private static unsafe ulong Hash64_na(byte *s, uint len) { const ulong seed = 81; // For strings over 64 bytes we loop. Internal state consists of // 56 bytes: v, w, x, y, and z. ulong x = seed; ulong y = unchecked (seed * k1 + 113); ulong z = ShiftMix(y * k2 + 113) * k2; uint128_t v = Uint128(0, 0); uint128_t w = Uint128(0, 0); x = x * k2 + Fetch64(s); ulong tmp; // Set end so that after the loop we have 1 to 64 bytes left to process. byte *end = s + (len - 1) / 64 * 64; byte *last64 = end + ((len - 1) & 63) - 63; do { x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * k1; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); tmp = z; z = x; x = tmp; s += 64; } while (s != end); ulong mul = k1 + ((z & 0xff) << 1); // Make s point to the last 64 bytes of input. s = last64; w.first += (len - 1) & 63; v.first += w.first; w.first += v.first; x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * mul; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * mul; x ^= w.second * 9; y += v.first * 9 + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * mul; v = WeakHashLen32WithSeeds(s, v.second * mul, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); tmp = z; z = x; x = tmp; return(HashLen16(HashLen16(v.first, w.first, mul) + ShiftMix(y) * k0 + z, HashLen16(v.second, w.second, mul) + x, mul)); }
private static ulong Hash128to64(uint128_t x) { // Taken from https://github.com/google/farmhash/blob/master/src/farmhash.h const ulong kMul = 0x9DDFEA08EB382D69; ulong a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; a ^= (a >> 47); ulong b = (Uint128High64(x) ^ a) * kMul; b ^= (b >> 47); b *= kMul; return(b); }
private static unsafe uint128_t CityMurmur(byte *s, uint len, uint128_t seed) { // Taken from https://github.com/google/farmhash/blob/master/src/farmhash.cc ulong a = Uint128Low64(seed); ulong b = Uint128High64(seed); ulong c = 0; ulong d = 0; long l = len - 16; if (l <= 0) { // len <= 16 a = ShiftMix(a * K1) * K1; c = b * K1 + HashLen0to16(s, len); d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); } else { // len > 16 c = HashLen16(Fetch64(s + len - 8) + K1, a); d = HashLen16(b + len, c + Fetch64(s + len - 16)); a += d; do { a ^= ShiftMix(Fetch64(s) * K1) * K1; a *= K1; b ^= a; c ^= ShiftMix(Fetch64(s + 8) * K1) * K1; c *= K1; d ^= c; s += 16; l -= 16; } while (l > 0); } a = HashLen16(a, c); b = HashLen16(d, b); return(Uint128(a ^ b, HashLen16(b, a))); }
private static unsafe uint128_t CityHash128WithSeed(byte *s, uint len, uint128_t seed) { // Taken from https://github.com/google/farmhash/blob/master/src/farmhash.cc if (len < 128) { return(CityMurmur(s, len, seed)); } ulong tmp_x; // We expect len >= 128 to be the common case. Keep 56 bytes of state: // v, w, x, y, and z. uint128_t v = new uint128_t(0, 0); uint128_t w = new uint128_t(0, 0); ulong x = Uint128Low64(seed); ulong y = Uint128High64(seed); ulong z = len * K1; v.first = Rotate64(y ^ K1, 49) * K1 + Fetch64(s); v.second = Rotate64(v.first, 42) * K1 + Fetch64(s + 8); w.first = Rotate64(y + z, 35) * K1 + x; w.second = Rotate64(x + Fetch64(s + 88), 53) * K1; // This is the same inner loop as CityHash64(), manually unrolled. do { x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * K1; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * K1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * K1; v = WeakHashLen32WithSeeds(s, v.second * K1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); tmp_x = x; x = z; z = tmp_x; s += 64; x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * K1; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * K1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * K1; v = WeakHashLen32WithSeeds(s, v.second * K1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); tmp_x = x; x = z; z = tmp_x; s += 64; len -= 128; } while (len >= 128); x += Rotate64(v.first + z, 49) * K0; y = y * K0 + Rotate64(w.second, 37); z = z * K0 + Rotate64(w.first, 27); w.first *= 9; v.first *= K0; // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. for (uint tail_done = 0; tail_done < len;) { tail_done += 32; y = Rotate64(x + y, 42) * K0 + v.second; w.first += Fetch64(s + len - tail_done + 16); x = x * K0 + w.first; z += w.second + Fetch64(s + len - tail_done); w.second += v.first; v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); v.first *= 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.first); y = HashLen16(y + z, w.first); return(Uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second))); }
public static unsafe byte[] Hash64(byte *s, uint len) { // Taken from https://github.com/google/farmhash/blob/master/src/farmhash.cc const ulong seed = 81; ulong tmp_x; if (0 <= len && len <= 16) { return(GetBytes64(HashLen0to16(s, len))); } else if (17 <= len && len <= 32) { return(GetBytes64(HashLen17to32(s, len))); } else if (33 <= len && len <= 64) { return(GetBytes64(HashLen33to64(s, len))); } ulong x = seed; ulong y = unchecked (seed * K1 + 113); ulong z = ShiftMix(y * K2 + 113) * K2; uint128_t v = new uint128_t(0, 0); uint128_t w = new uint128_t(0, 0); x = x * K2 + Fetch64(s); byte *end = s + (len - 1) / 64 * 64; byte *last64 = end + ((len - 1) & 63) - 63; do { x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * K1; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * K1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * K1; v = WeakHashLen32WithSeeds(s, v.second * K1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); // swap tmp_x = x; x = z; z = tmp_x; s += 64; } while (s != end); ulong mul = K1 + ((z & 0xff) << 1); // Make s point to the last 64 bytes of input. s = last64; w.first += ((len - 1) & 63); v.first += w.first; w.first += v.first; x = Rotate64(x + y + v.first + Fetch64(s + 8), 37) * mul; y = Rotate64(y + v.second + Fetch64(s + 48), 42) * mul; x ^= w.second * 9; y += v.first * 9 + Fetch64(s + 40); z = Rotate64(z + w.first, 33) * mul; v = WeakHashLen32WithSeeds(s, v.second * mul, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); // swap tmp_x = x; x = z; z = tmp_x; return(GetBytes64(HashLen16(HashLen16(v.first, w.first, mul) + ShiftMix(y) * K0 + z, HashLen16(v.second, w.second, mul) + x, mul))); }
private static ulong Uint128High64(uint128_t x) => x.second;
private static ulong Uint128Low64(uint128_t x) => x.first;
private static unsafe ulong Hash64_uo(byte *s, uint len) { const ulong seed0 = 81; const ulong seed1 = 0; // For strings over 64 bytes we loop. Internal state consists of // 64 bytes: u, v, w, x, y, and z. ulong x = seed0; ulong y = seed1 * k2 + 113; ulong z = ShiftMix(y * k2) * k2; // v and w used to be uint128_t(seed0, seed1), uint128_t(0, 0), but // using only primitives meant a 40% performance increase, hence the // deviation with original farmhash algorithm; see commit 380c059 ulong v_first = seed0; ulong v_second = seed1; ulong w_first = 0; ulong w_second = 0; ulong u = x - z; x *= k2; ulong mul = k2 + (u & 0x82); // Set end so that after the loop we have 1 to 64 bytes left to process. byte *end = s + (len - 1) / 64 * 64; byte *last64 = end + ((len - 1) & 63) - 63; do { ulong a0 = Fetch64(s); ulong a1 = Fetch64(s + 8); ulong a2 = Fetch64(s + 16); ulong a3 = Fetch64(s + 24); ulong a4 = Fetch64(s + 32); ulong a5 = Fetch64(s + 40); ulong a6 = Fetch64(s + 48); ulong a7 = Fetch64(s + 56); x += a0 + a1; y += a2; z += a3; v_first += a4; v_second += a5 + a1; w_first += a6; w_second += a7; x = Rotate64(x, 26); x *= 9; y = Rotate64(y, 29); z *= mul; v_first = Rotate64(v_first, 33); v_second = Rotate64(v_second, 30); w_first ^= x; w_first *= 9; z = Rotate64(z, 32); z += w_second; w_second += z; z *= 9; ulong tmp = u; u = y; y = tmp; z += a0 + a6; v_first += a2; v_second += a3; w_first += a4; w_second += a5 + a6; x += a1; y += a7; y += v_first; v_first += x - y; v_second += w_first; w_first += v_second; w_second += x - y; x += w_second; w_second = Rotate64(w_second, 34); tmp = u; u = z; z = tmp; s += 64; } while (s != end); // Make s point to the last 64 bytes of input. s = last64; u *= 9; v_second = Rotate64(v_second, 28); v_first = Rotate64(v_first, 20); w_first += (len - 1) & 63; u += y; y += u; x = Rotate64(y - x + v_first + Fetch64(s + 8), 37) * mul; y = Rotate64(y ^ v_second ^ Fetch64(s + 48), 42) * mul; x ^= w_second * 9; y += v_first + Fetch64(s + 40); z = Rotate64(z + w_first, 33) * mul; uint128_t v = WeakHashLen32WithSeeds(s, v_second * mul, x + w_first); uint128_t w = WeakHashLen32WithSeeds(s + 32, z + w_second, y + Fetch64(s + 16)); return(H(HashLen16(v.first + x, w.first ^ y, mul) + z - u, H(v.second + y, w.second + z, k2, 30) ^ x, k2, 31)); }
private static ulong Hash64_na(ReadOnlySpan <byte> s, uint len) { const ulong seed = 81; // For strings over 64 bytes we loop. Internal state consists of // 56 bytes: v, w, x, y, and z. ulong x = seed; ulong y = unchecked (seed * k1 + 113); ulong z = ShiftMix(y * k2 + 113) * k2; uint128_t v = UInt128(0, 0); uint128_t w = UInt128(0, 0); x = x * k2 + Fetch64(s); ulong tmp; // Set end so that after the loop we have 1 to 64 bytes left to process. uint end = (len - 1) / 64 * 64; uint last64 = end + ((len - 1) & 63) - 63; uint s_index = 0; do { x = Rotate64(x + y + v.first + Fetch64(s.Slice((int)(8 + s_index))), 37) * k1; y = Rotate64(y + v.second + Fetch64(s.Slice((int)(48 + s_index))), 42) * k1; x ^= w.second; y += v.first + Fetch64(s.Slice((int)(40 + s_index))); z = Rotate64(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s.Slice((int)(s_index)), v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s.Slice((int)(32 + s_index)), z + w.second, y + Fetch64(s.Slice((int)(16 + s_index)))); tmp = z; z = x; x = tmp; s_index += 64; } while (s_index != end); ulong mul = k1 + ((z & 0xff) << 1); // Make s point to the last 64 bytes of input. s_index = last64; w.first += (len - 1) & 63; v.first += w.first; w.first += v.first; x = Rotate64(x + y + v.first + Fetch64(s.Slice((int)(8 + s_index))), 37) * mul; y = Rotate64(y + v.second + Fetch64(s.Slice((int)(48 + s_index))), 42) * mul; x ^= w.second * 9; y += v.first * 9 + Fetch64(s.Slice((int)(40 + s_index))); z = Rotate64(z + w.first, 33) * mul; v = WeakHashLen32WithSeeds(s.Slice((int)(s_index)), v.second * mul, x + w.first); w = WeakHashLen32WithSeeds(s.Slice((int)(32 + s_index)), z + w.second, y + Fetch64(s.Slice((int)(16 + s_index)))); tmp = z; z = x; x = tmp; return(HashLen16(HashLen16(v.first, w.first, mul) + ShiftMix(y) * k0 + z, HashLen16(v.second, w.second, mul) + x, mul)); }