Skip to content
Merged
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
9 changes: 8 additions & 1 deletion lib/src/helpers/fast_hash.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ abstract final class FastHash {
// Offset basis 0xcbf29ce484222325, split because a >2^53 literal is a
// compile error on JS targets even when the function is never called.
var hash = (0xcbf29ce4 << 32) | 0x84222325;
for (final codeUnit in s.codeUnits) {
// Indexed codeUnitAt, not `for (... in s.codeUnits)`: same output, same
// speed on JIT, but on AOT the for-in pays a fixed ~2ns/hash ListIterator
// setup plus a per-step length check on top of codeUnitAt's own bounds
// check: ~20-30% on 5-char keys, nil by 64 chars (measured on isolated
// AOT binaries, inlined and never-inline). Do not flip back (see #12,
// #21, #28).
for (var i = 0; i < s.length; i++) {
final codeUnit = s.codeUnitAt(i);
hash ^= codeUnit >> 8;
hash *= fnvPrime;
hash ^= codeUnit & 0xFF;
Expand Down
10 changes: 10 additions & 0 deletions test/fast_hash_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ void main() {
check(FastHash.fnv1a('')).equals((0xcbf29ce4 << 32) | 0x84222325);
});

test('known vectors are stable', () {
// Hashes UTF-16 code units as hi/lo byte pairs, so standard FNV
// byte vectors do not apply; pinned from the released algorithm.
// Split literals: a >2^53 literal is a dart2js compile error.
check(FastHash.fnv1a('a')).equals((0x08326707 << 32) | 0xb4eb37da);
check(
FastHash.fnv1a('hello'),
).equals((0x4ce155d4 << 32) | 0x4072f8ef);
});

test('handles unicode code units', () {
check(
FastHash.fnv1a('🚀'),
Expand Down