github.com/scottcagno/storage@v1.8.0/pkg/hash/cityhash/helpers.go (about) 1 /* 2 * // Copyright (c) 2021. Scott Cagno. All rights reserved. 3 * // The license can be found in the root of this project; see LICENSE. 4 */ 5 6 package cityhash 7 8 import "encoding/binary" 9 10 // A 32-bit to 32-bit integer hash copied from Murmur3. 11 func fmix(h uint32) uint32 { 12 h ^= h >> 16 13 h *= 0x85ebca6b 14 h ^= h >> 13 15 h *= 0xc2b2ae35 16 h ^= h >> 16 17 return h 18 } 19 20 // Bitwise right rotate, 64-bit. 21 func ror64(val, shift uint64) uint64 { 22 // Avoid shifting by 64: doing so yields an undefined result. 23 if shift != 0 { 24 return val>>shift | val<<(64-shift) 25 } 26 return val 27 } 28 29 // Bitwise right rotate, 32-bit. 30 func ror32(val, shift uint32) uint32 { 31 // Avoid shifting by 32: doing so yields an undefined result. 32 if shift != 0 { 33 return val>>shift | val<<(32-shift) 34 } 35 return val 36 } 37 38 func shiftMix(val uint64) uint64 { return val ^ (val >> 47) } 39 40 func hash64Len16(u, v uint64) uint64 { return hash128to64(u, v) } 41 42 func hash64Len16Mul(u, v, mul uint64) uint64 { 43 // Murmur-inspired hashing. 44 a := (u ^ v) * mul 45 a ^= a >> 47 46 b := (v ^ a) * mul 47 b ^= b >> 47 48 b *= mul 49 return b 50 } 51 52 func hash64Len0to16(s []byte) uint64 { 53 n := uint64(len(s)) 54 if n >= 8 { 55 mul := k2 + n*2 56 a := fetch64(s) + k2 57 b := fetch64(s[n-8:]) 58 c := ror64(b, 37)*mul + a 59 d := (ror64(a, 25) + b) * mul 60 return hash64Len16Mul(c, d, mul) 61 } 62 if n >= 4 { 63 mul := k2 + n*2 64 a := uint64(fetch32(s)) 65 return hash64Len16Mul(n+(a<<3), uint64(fetch32(s[n-4:])), mul) 66 } 67 if n > 0 { 68 a := s[0] 69 b := s[n>>1] 70 c := s[n-1] 71 y := uint32(a) + uint32(b)<<8 72 z := uint32(n) + uint32(c)<<2 73 return shiftMix(uint64(y)*k2^uint64(z)*k0) * k2 74 } 75 return k2 76 } 77 78 func hash64Len17to32(s []byte) uint64 { 79 n := uint64(len(s)) 80 mul := k2 + n*2 81 a := fetch64(s) * k1 82 b := fetch64(s[8:]) 83 c := fetch64(s[n-8:]) * mul 84 d := fetch64(s[n-16:]) * k2 85 return hash64Len16Mul(ror64(a+b, 43)+ror64(c, 30)+d, a+ror64(b+k2, 18)+c, mul) 86 } 87 88 func bswap64(in uint64) uint64 { 89 var buf [8]byte 90 binary.LittleEndian.PutUint64(buf[:], in) 91 return binary.BigEndian.Uint64(buf[:]) 92 } 93 94 func bswap32(in uint32) uint32 { 95 var buf [4]byte 96 binary.LittleEndian.PutUint32(buf[:], in) 97 return binary.BigEndian.Uint32(buf[:]) 98 } 99 100 // Return an 8-byte hash for 33 to 64 bytes. 101 func hash64Len33to64(s []byte) uint64 { 102 n := uint64(len(s)) 103 mul := k2 + n*2 104 a := fetch64(s) * k2 105 b := fetch64(s[8:]) 106 c := fetch64(s[n-24:]) 107 d := fetch64(s[n-32:]) 108 e := fetch64(s[16:]) * k2 109 f := fetch64(s[24:]) * 9 110 g := fetch64(s[n-8:]) 111 h := fetch64(s[n-16:]) * mul 112 u := ror64(a+g, 43) + (ror64(b, 30)+c)*9 113 v := ((a + g) ^ d) + f + 1 114 w := bswap64((u+v)*mul) + h 115 x := ror64(e+f, 42) + c 116 y := (bswap64((v+w)*mul) + g) * mul 117 z := e + f + c 118 a = bswap64((x+z)*mul+y) + b 119 b = shiftMix((z+a)*mul+d+h) * mul 120 return b + x 121 } 122 123 func hash128to64(lo, hi uint64) uint64 { 124 // Murmur-inspired hashing. 125 const mul = uint64(0x9ddfea08eb382d69) 126 a := (lo ^ hi) * mul 127 a ^= a >> 47 128 b := (hi ^ a) * mul 129 b ^= b >> 47 130 b *= mul 131 return b 132 } 133 134 var fetch64 = binary.LittleEndian.Uint64 // :: []byte -> uint64 135 var fetch32 = binary.LittleEndian.Uint32 // :: []byte -> uint32 136 137 // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 138 // Callers do best to use "random-looking" values for a and b. 139 func weakHashLen32WithSeeds(s []byte, a, b uint64) (uint64, uint64) { 140 // Note: Was two overloads of WeakHashLen32WithSeeds. The second is only 141 // ever called from the first, so I inlined it. 142 w := fetch64(s) 143 x := fetch64(s[8:]) 144 y := fetch64(s[16:]) 145 z := fetch64(s[24:]) 146 147 a += w 148 b = ror64(b+a+z, 21) 149 c := a 150 a += x 151 a += y 152 b += ror64(a, 44) 153 return a + z, b + c 154 } 155 156 func hash32Len0to4(s []byte) uint32 { 157 b := uint32(0) 158 c := uint32(9) 159 160 for i := 0; i < len(s); i++ { 161 v := int8(s[i]) 162 b = b*c1 + uint32(v) 163 c ^= b 164 } 165 return fmix(mur(b, mur(uint32(len(s)), c))) 166 } 167 168 func mur(a, h uint32) uint32 { 169 // Helper from Murmur3 for combining two 32-bit values. 170 a *= c1 171 a = ror32(a, 17) 172 a *= c2 173 h ^= a 174 h = ror32(h, 19) 175 return h*5 + 0xe6546b64 176 } 177 178 func hash32Len5to12(s []byte) uint32 { 179 n := uint32(len(s)) 180 a := n 181 b := n * 5 182 c := uint32(9) 183 d := b 184 185 a += fetch32(s) 186 b += fetch32(s[n-4:]) 187 c += fetch32(s[(n>>1)&4:]) 188 return fmix(mur(c, mur(b, mur(a, d)))) 189 } 190 191 func hash32Len13to24(s []byte) uint32 { 192 n := uint32(len(s)) 193 a := fetch32(s[(n>>1)-4:]) 194 b := fetch32(s[4:]) 195 c := fetch32(s[n-8:]) 196 d := fetch32(s[n>>1:]) 197 e := fetch32(s) 198 f := fetch32(s[n-4:]) 199 h := n 200 201 return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, h))))))) 202 } 203 204 // A subroutine for Hash128. Returns a decent 128-bit hash for s based on City 205 // and Murmur. 206 func cityMurmur(s []byte, seed0, seed1 uint64) (lo, hi uint64) { 207 a := seed0 208 b := seed1 209 var c, d uint64 210 n := int64(len(s) - 16) 211 if n <= 0 { 212 a = shiftMix(a*k1) * k1 213 c = b*k1 + hash64Len0to16(s) 214 215 if len(s) >= 8 { 216 d = shiftMix(a + fetch64(s)) 217 } else { 218 d = shiftMix(a + c) 219 } 220 } else { 221 c = hash64Len16(fetch64(s[len(s)-8:])+k1, a) 222 d = hash64Len16(b+uint64(len(s)), c+fetch64(s[len(s)-16:])) 223 a += d 224 for n > 0 { 225 a ^= shiftMix(fetch64(s)*k1) * k1 226 a *= k1 227 b ^= a 228 c ^= shiftMix(fetch64(s[8:])*k1) * k1 229 c *= k1 230 d ^= c 231 s = s[16:] 232 n -= 16 233 } 234 } 235 a = hash64Len16(a, c) 236 b = hash64Len16(d, b) 237 return a ^ b, hash64Len16(b, a) 238 }