Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@
- Added FNV-1 64bit hash function.
- Added FNV-1a 64bit hash function.

## 1.0.2

- Added a FNV-1 64-bit hash function that returns a BigInt.
- Added a FNV-1a 64-bit hash function that returns a BigInt
- [BREAKING CHANGE] fnv_constants.dart is no longer exported
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ A simple usage example:
import 'package:fnv/fnv.dart';

void main() {
// Returns int, 32-bit precision, full platform support
print(fnv1a_32_s('foo'));

// Returns int, 64-bit precision, but does not support web platforms
print(fnv1a_64_s('foo'));

// Returns BigInt, 64-bit precision, full platform support
print(fnv1a_64_s_bigint('foo'));
}
```

Expand Down
7 changes: 7 additions & 0 deletions example/fnv_example.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import 'package:fnv/fnv.dart';

void main() {
// Returns int, 32-bit precision, full platform support
print(fnv1a_32_s('foo'));

// Returns int, 64-bit precision, but does not support web platforms
print(fnv1a_64_s('foo'));

// Returns BigInt, 64-bit precision, full platform support
print(fnv1a_64_s_bigint('foo'));
}
1 change: 0 additions & 1 deletion lib/fnv.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
library fnv;

export 'src/fnv.dart';
export 'src/fnv_constants.dart';
102 changes: 64 additions & 38 deletions lib/src/fnv.dart
Original file line number Diff line number Diff line change
@@ -1,70 +1,96 @@
import 'dart:convert';
import 'dart:math';

import 'fnv_algorithm.dart' as adaptive;
import 'fnv_algorithm_full_precision.dart' as full_precision;
import 'fnv_constants.dart';

/// FNV hash algorithm
int _fnv(List<int> bytes, int init, int fnv_prime, int mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash *= fnv_prime;
hash ^= bytes[i++];
}

return hash & mask;
}

/// FNVA hash algorithm
int _fnva(List<int> bytes, int init, int fnv_prime, int mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash ^= bytes[i++];
hash *= fnv_prime;
}

return hash & mask;
}
const bool _kIsWeb = bool.fromEnvironment('dart.library.js_util');

/// FNV-1 32bit hash algorithm
int fnv1_32(List<int> bytes, {int init = FNV1_32_INIT}) {
return _fnv(bytes, init, FNV_32_PRIME, UINT32_MASK);
int fnv1_32(List<int> bytes, {int? init}) {
final initUsed = init != null ? BigInt.from(init) : FNV1_32_INIT;
return full_precision
.fnv_bigint(bytes, initUsed, FNV_32_PRIME, UINT32_MASK)
.toInt();
}

/// FNV-1 32bit hash algorithm
int fnv1_32_s(String str, {int init = FNV1_32_INIT}) {
int fnv1_32_s(String str, {int? init}) {
return fnv1_32(utf8.encode(str), init: init);
}

/// FNV-1a 32bit hash algorithm
int fnv1a_32(List<int> bytes, {int init = FNV1A_32_INIT}) {
return _fnva(bytes, init, FNV_32_PRIME, UINT32_MASK);
int fnv1a_32(List<int> bytes, {int? init}) {
final initUsed = init != null ? BigInt.from(init) : FNV1A_32_INIT;
return full_precision
.fnva_bigint(bytes, initUsed, FNV_32_PRIME, UINT32_MASK)
.toInt();
}

/// FNV-1a 32bit hash algorithm
int fnv1a_32_s(String str, {int init = FNV1_32_INIT}) {
int fnv1a_32_s(String str, {int? init}) {
return fnv1a_32(utf8.encode(str), init: init);
}

void _checkUnsupportedWeb() {
if (_kIsWeb) {
throw UnsupportedError(
'If you need to support the web platform, please use the _bigint method.');
}
}

/// FNV-1 64bit hash algorithm
int fnv1_64(List<int> bytes, {int init = FNV1_64_INIT}) {
return _fnv(bytes, init, FNV_64_PRIME, UINT64_MASK);
int fnv1_64(List<int> bytes, {int? init}) {
_checkUnsupportedWeb();
final initUsed = init != null ? BigInt.from(init) : FNV1_64_INIT;
return full_precision
.fnv_bigint(bytes, initUsed, FNV_64_PRIME, UINT64_MASK)
.toInt();
}

/// FNV-1 64bit hash algorithm
int fnv1_64_s(String str, {int init = FNV1_64_INIT}) {
int fnv1_64_s(String str, {int? init}) {
return fnv1_64(utf8.encode(str), init: init);
}

/// FNV-1a 64bit hash algorithm
int fnv1a_64(List<int> bytes, {int init = FNV1A_64_INIT}) {
return _fnva(bytes, init, FNV_64_PRIME, UINT64_MASK);
int fnv1a_64(List<int> bytes, {int? init}) {
_checkUnsupportedWeb();
final initUsed = init != null ? BigInt.from(init) : FNV1A_64_INIT;
return full_precision
.fnva_bigint(bytes, initUsed, FNV_64_PRIME, UINT64_MASK)
.toInt();
}

/// FNV-1a 64bit hash algorithm
int fnv1a_64_s(String str, {int init = FNV1_64_INIT}) {
int fnv1a_64_s(String str, {int? init}) {
return fnv1a_64(utf8.encode(str), init: init);
}

/// FNV-1 64bit hash algorithm
/// Support for web platforms
BigInt fnv1_64_bigint(List<int> bytes, {BigInt? init}) {
final initUsed = init ?? FNV1_64_INIT;
assert(initUsed.bitLength <= 64);
return adaptive.fnv_bigint(bytes, initUsed, FNV_64_PRIME, UINT64_MASK);
}

/// FNV-1 64bit hash algorithm
/// Support for web platforms
BigInt fnv1_64_s_bigint(String str, {BigInt? init}) {
return fnv1_64_bigint(utf8.encode(str), init: init);
}

/// FNV-1a 64bit hash algorithm
/// Support for web platforms
BigInt fnv1a_64_bigint(List<int> bytes, {BigInt? init}) {
final initUsed = init ?? FNV1A_64_INIT;
assert(initUsed.bitLength <= 64);
return adaptive.fnva_bigint(bytes, initUsed, FNV_64_PRIME, UINT64_MASK);
}

/// FNV-1a 64bit hash algorithm
/// Support for web platforms
BigInt fnv1a_64_s_bigint(String str, {BigInt? init}) {
return fnv1a_64_bigint(utf8.encode(str), init: init);
}
2 changes: 2 additions & 0 deletions lib/src/fnv_algorithm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'fnv_algorithm_full_precision.dart'
if (dart.library.js) 'fnv_algorithm_web.dart';
33 changes: 33 additions & 0 deletions lib/src/fnv_algorithm_full_precision.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/// FNV hash algorithm
int _fnv(List<int> bytes, int init, int fnv_prime, int mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash *= fnv_prime;
hash ^= bytes[i++];
}

return hash & mask;
}

/// FNVA hash algorithm
int _fnva(List<int> bytes, int init, int fnv_prime, int mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash ^= bytes[i++];
hash *= fnv_prime;
}

return hash & mask;
}

BigInt fnv_bigint(
List<int> bytes, BigInt init, BigInt fnv_prime, BigInt mask) =>
BigInt.from(_fnv(bytes, init.toInt(), fnv_prime.toInt(), mask.toInt()));

BigInt fnva_bigint(
List<int> bytes, BigInt init, BigInt fnv_prime, BigInt mask) =>
BigInt.from(_fnva(bytes, init.toInt(), fnv_prime.toInt(), mask.toInt()));
30 changes: 30 additions & 0 deletions lib/src/fnv_algorithm_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/// FNV hash algorithm, Use BigInt
BigInt fnv_bigint(List<int> bytes, BigInt init, BigInt fnv_prime, BigInt mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash *= fnv_prime;
hash = hash.toSigned(64);
hash ^= BigInt.from(bytes[i++]);
hash = hash.toSigned(64);
}

return hash & mask;
}

/// FNVA hash algorithm, Use BigInt
BigInt fnva_bigint(
List<int> bytes, BigInt init, BigInt fnv_prime, BigInt mask) {
var hash = init;

var i = 0;
while (i < bytes.length) {
hash ^= BigInt.from(bytes[i++]);
hash = hash.toSigned(64);
hash *= fnv_prime;
hash = hash.toSigned(64);
}

return hash & mask;
}
16 changes: 8 additions & 8 deletions lib/src/fnv_constants.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const int FNV1_32_INIT = 0x811c9dc5;
const int FNV1A_32_INIT = FNV1_32_INIT;
const int FNV1_64_INIT = 0xcbf29ce484222325;
const int FNV1A_64_INIT = FNV1_64_INIT;
final BigInt FNV1_32_INIT = BigInt.from(0x811c9dc5);
final BigInt FNV1A_32_INIT = FNV1_32_INIT;
final BigInt FNV1_64_INIT = BigInt.parse('0xcbf29ce484222325').toSigned(64);
final BigInt FNV1A_64_INIT = FNV1_64_INIT;

const int FNV_32_PRIME = 0x01000193;
const int FNV_64_PRIME = 0x100000001b3;
final BigInt FNV_32_PRIME = BigInt.from(0x01000193);
final BigInt FNV_64_PRIME = BigInt.from(0x100000001b3);

const int UINT32_MASK = 0xffffffff;
const int UINT64_MASK = 0xffffffffffffffff;
final BigInt UINT32_MASK = BigInt.from(0xffffffff);
final BigInt UINT64_MASK = BigInt.parse('0xffffffffffffffff').toSigned(64);
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ environment:
sdk: '>=2.13.4 <3.0.0'

dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.10.0
test: ^1.16.0
35 changes: 35 additions & 0 deletions test/fnv_bigint_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'dart:convert';

import 'package:fnv/fnv.dart';
import 'package:test/test.dart';

// use ```flutter test --platform chrome .\test\fnv_bigint_test.dart```
void main() {
group('A group of tests for bigint', () {
test('normal method unsupported web platform', () {
expect(() => fnv1_64_s(''), throwsA(isA<UnsupportedError>()));
expect(() => fnv1a_64_s(''), throwsA(isA<UnsupportedError>()));
});

test('fnv1_64', () {
expect(fnv1_64_bigint(utf8.encode('test')),
BigInt.parse('0x8c093f7e9fccbf69').toSigned(64));
expect(fnv1_64_s_bigint('test'),
BigInt.parse('0x8c093f7e9fccbf69').toSigned(64));
});

test('fnv1a_64', () {
expect(fnv1a_64_bigint(utf8.encode('test')),
BigInt.parse('0xf9e6e6ef197c2b25').toSigned(64));
expect(fnv1a_64_s_bigint('test'),
BigInt.parse('0xf9e6e6ef197c2b25').toSigned(64));
});

test('error init hash', () {
expect(
() =>
fnv1a_64_s_bigint('', init: BigInt.parse('0x1ffffffffffffffff')),
throwsA(isA<AssertionError>()));
});
});
}