/// <summary> /// Computes the 128-bit city hash using a specific <paramref name="seed" />. /// This algorithm is tuned for strings of at least a few hundred bytes. /// </summary> /// <param name="value">The string value.</param> /// <param name="seed">Specifies the seed for the CityHash algorithm.</param> /// <returns>The 128-bit city hash.</returns> /// <exception cref="ArgumentNullException">value</exception> /// <remarks>This function encodes the string using the unicode block (ISO/IEC 8859-1).</remarks> public static uint128 GetCityHash128(this string value, uint128 seed) { if (value == null) { throw new ArgumentNullException("value"); } return(CityHash.CityHash128(value, seed)); }
private static ulong Hash128to64(uint128 x) { const ulong kMul = 0x9ddfea08eb382d69UL; var a = (x.Low ^ x.High) * kMul; a ^= (a >> 47); var b = (x.High ^ a) * kMul; b ^= (b >> 47); b *= kMul; return(b); }
protected static uint128 CityMurmur(byte[] value, uint128 seed, int offset) { var a = seed.Low; var b = seed.High; ulong c; ulong d; var len = value.Length - offset; var l = len - 16; if (l <= 0) // len <= 16 { a = ShiftMix(a * k1) * k1; c = b * k1 + HashLen0to16(value, offset); d = ShiftMix(a + (len >= 8 ? Fetch64(value, offset) : c)); } else // len > 16 { c = HashLen16(Fetch64(value, offset + len - 8) + k1, a); d = HashLen16(b + (ulong)len, c + Fetch64(value, offset + len - 16)); a += d; var p = offset; do { a ^= ShiftMix(Fetch64(value, p) * k1) * k1; a *= k1; b ^= a; c ^= ShiftMix(Fetch64(value, p + 8) * k1) * k1; c *= k1; d ^= c; p += 16; l -= 16; } while (l > 0); } a = HashLen16(a, c); b = HashLen16(d, b); return(new uint128(a ^ b, HashLen16(b, a))); }
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) }); }
public static uint128 CityHash128(string value, uint128 seed) { return(CityHash128(Encoding.GetEncoding("ISO-8859-1").GetBytes(value), seed, 0)); }