/// <summary> /// Computes the 128-bit city hash and are tuned for strings of at least a few hundred bytes using /// the specified <paramref name="seed"/> starting at a <paramref name="offset"/> position. /// </summary> /// <param name="value">The encoded string.</param> /// <param name="seed">The seed used by the hash alrorithm.</param> /// <param name="offset">The offset position in the byte array.</param> /// <returns>The 128-bit city hash.</returns> /// <remarks> /// The city hash is designed to compute hash for STRINGs only! /// The city hash "works" with other types of data, but keep in mind it was not built for it. /// </remarks> protected static UInt128 CityHash128(byte[] value, UInt128 seed, int offset) { if (value.Length - offset < 128) { return(CityMurmur(value, seed, offset)); } // We expect len >= 128 to be the common case. Keep 56 bytes of state: // v, w, x, y, and z. var len = value.Length - offset; var x = seed.Low; var y = seed.High; var z = (ulong)len * K1; var v = new UInt128 { Low = Rotate(seed.High ^ K1, 49) * K1 + Fetch64(value, offset) }; v.High = Rotate(v.Low, 42) * K1 + Fetch64(value, offset + 8); var w = new UInt128 { Low = Rotate(y + z, 35) * K1 + x, High = Rotate(seed.Low + Fetch64(value, offset + 88), 53) * K1 }; // This is the same inner loop as CityHash64(), manually unrolled. var s = offset; do { x = Rotate(x + y + v.Low + Fetch64(value, s + 8), 37) * K1; y = Rotate(y + v.High + Fetch64(value, s + 48), 42) * K1; x ^= w.High; y += v.Low + Fetch64(value, s + 40); z = Rotate(z + w.Low, 33) * K1; v = WeakHashLen32WithSeeds(value, s, v.High * K1, x + w.Low); w = WeakHashLen32WithSeeds(value, s + 32, z + w.High, y + Fetch64(value, s + 16)); Swap(ref z, ref x); s += 64; x = Rotate(x + y + v.Low + Fetch64(value, s + 8), 37) * K1; y = Rotate(y + v.High + Fetch64(value, s + 48), 42) * K1; x ^= w.High; y += v.Low + Fetch64(value, s + 40); z = Rotate(z + w.Low, 33) * K1; v = WeakHashLen32WithSeeds(value, s, v.High * K1, x + w.Low); w = WeakHashLen32WithSeeds(value, s + 32, z + w.High, y + Fetch64(value, s + 16)); Swap(ref z, ref x); s += 64; len -= 128; } while (len >= 128); x += Rotate(v.Low + z, 49) * K0; y = y * K0 + Rotate(w.High, 37); z = z * K0 + Rotate(w.Low, 27); w.Low *= 9; v.Low *= K0; // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. for (var tail = 0; tail < len;) { tail += 32; y = Rotate(x + y, 42) * K0 + v.High; w.Low += Fetch64(value, s + len - tail + 16); x = x * K0 + w.Low; z += w.High + Fetch64(value, s + len - tail); w.High += v.Low; v = WeakHashLen32WithSeeds(value, s + len - tail, v.Low + z, v.High); v.Low *= K0; } // At this point our 56 bytes of state should contain more than // enough information for a strong 128-bit hash. We use two // different 56-byte-to-8-byte hashes to get a 16-byte final result. x = HashLen16(x, v.Low); y = HashLen16(y + z, w.Low); return(new UInt128 { Low = HashLen16(x + v.High, w.High) + y, High = HashLen16(x + w.High, y + v.High) }); }
/// <summary> /// Computes the 128-bit city hash and are tuned for strings of at least a few hundred bytes using /// the specified <paramref name="seed"/>. /// </summary> /// <param name="value">The string value.</param> /// <param name="seed">The seed used by the city hash algorithm.</param> /// <returns>The 128-bit city hash.</returns> /// <remarks>This function encodes the string using the unicode block (ISO/IEC 8859-1).</remarks> public static UInt128 CityHash128(string value, UInt128 seed) { return(CityHash128(Encoding.GetEncoding("ISO-8859-1").GetBytes(value), seed, 0)); }