stableHash function

String stableHash(
  1. Object? value
)

Returns the FNV-1a 64-bit hash of canonicalString of value as a lowercase hex string.

Determinism: on a given platform, equal inputs always yield equal hashes within a run and across runs. (Across the VM/web boundary the digests differ — see the library-level platform note.) Map key order does NOT change the hash (keys are sorted first); list order DOES. null and the string "null" differ because strings are quoted in the canonical form.

Example:

stableHash(<String, int>{'a': 1, 'b': 2}) ==
    stableHash(<String, int>{'b': 2, 'a': 1}); // true

Audited: 2026-06-12 11:26 EDT

Implementation

String stableHash(Object? value) {
  final String text = canonicalString(value);
  // FNV-1a over UTF-16 code units, held as two 32-bit limbs (hi:lo) so the
  // mod-2^64 multiply is computed identically on the VM and the web. XOR the
  // datum into the low limb (FNV-1a), then multiply the full 64-bit value by the
  // prime via _mulMod64. This reproduces the exact result the native
  // `(hash ^ unit) * prime` produced on the VM.
  int hi = _fnvOffsetHi;
  int lo = _fnvOffsetLo;
  for (final int unit in text.codeUnits) {
    final int xored = (lo ^ unit) & _mask32;
    final (int nextHi, int nextLo) = _mulMod64(hi, xored, _fnvPrimeHi, _fnvPrimeLo);
    hi = nextHi;
    lo = nextLo;
  }
  // Both limbs are already unsigned 32-bit, so each renders as a sign-free
  // 8-digit hex word; concatenated they are the stable 16-digit digest.
  return hi.toRadixString(16).padLeft(8, '0') + lo.toRadixString(16).padLeft(8, '0');
}