From d1c0b254cd12506d6d3c7fe36a97f3c1676b94c9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 22 Aug 2026 14:25:38 +0000 Subject: [PATCH 1/3] perf: optimize string traversal in FastHash.fnv1a Use index-based loop with codeUnitAt(i) instead of iterating over codeUnits to eliminate iterator allocation during string hashing. Co-authored-by: esenmx <43244505+esenmx@users.noreply.github.com> --- lib/src/helpers/fast_hash.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/helpers/fast_hash.dart b/lib/src/helpers/fast_hash.dart index 30cd9a7..50203aa 100644 --- a/lib/src/helpers/fast_hash.dart +++ b/lib/src/helpers/fast_hash.dart @@ -25,7 +25,8 @@ 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) { + for (var i = 0; i < s.length; i++) { + final codeUnit = s.codeUnitAt(i); hash ^= codeUnit >> 8; hash *= fnvPrime; hash ^= codeUnit & 0xFF; From 486cd8d846ac7d97ff49bad183f25ae68c8081ca Mon Sep 17 00:00:00 2001 From: Mehmet Esen Date: Sun, 23 Aug 2026 16:49:54 +0300 Subject: [PATCH 2/3] docs(FastHash): pin indexed codeUnitAt rationale above the loop Same hunk has flipped three times across #12, #21 and #28 with contradictory measurements. Record the AOT-only fixed ~2ns/hash iterator-setup saving in source so the next pass does not revert it. Co-Authored-By: Claude Fable 5 --- lib/src/helpers/fast_hash.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/helpers/fast_hash.dart b/lib/src/helpers/fast_hash.dart index 50203aa..adafab9 100644 --- a/lib/src/helpers/fast_hash.dart +++ b/lib/src/helpers/fast_hash.dart @@ -25,6 +25,12 @@ 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; + // 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; From d3d27a55a91bd437bb7a7a3cc62b731663d30c04 Mon Sep 17 00:00:00 2001 From: Mehmet Esen Date: Sun, 23 Aug 2026 16:49:54 +0300 Subject: [PATCH 3/3] test(FastHash): add known-answer vectors for fnv1a Existing tests only assert determinism and inequality, so a traversal change that alters output would pass. Pin 'a' and 'hello' from the released algorithm, split into 32-bit halves to stay dart2js-compilable. Co-Authored-By: Claude Fable 5 --- test/fast_hash_test.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/fast_hash_test.dart b/test/fast_hash_test.dart index a98f473..8df6b20 100644 --- a/test/fast_hash_test.dart +++ b/test/fast_hash_test.dart @@ -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('🚀'),