github.com/m3db/stackmurmur3@v1.0.1/murmur128.go (about) 1 package murmur3 2 3 import ( 4 "fmt" 5 "unsafe" 6 ) 7 8 const ( 9 c1_128 = 0x87c37b91114253d5 10 c2_128 = 0x4cf5ad432745937f 11 ) 12 13 // Digest128 is a murmur3 128 bit digest that can be written to 14 // as many consequent times as necessary without heap allocations. 15 type Digest128 struct { 16 digest 17 h1 uint64 // Unfinalized running hash part 1. 18 h2 uint64 // Unfinalized running hash part 2. 19 } 20 21 // New128 returns a new 128 bit digest. 22 func New128() Digest128 { 23 return New128WithSeed(0) 24 } 25 26 // New128WithSeed returns a new 128 bit digest with a seed. 27 func New128WithSeed(seed uint32) Digest128 { 28 s := uint64(seed) 29 return Digest128{digest: digest{seed: seed}, h1: s, h2: s} 30 } 31 32 // Size returns the number of bytes Sum will return. 33 func (d Digest128) Size() int { 34 return 16 35 } 36 37 // Sum returns the current binary hash appended to input byte ref. 38 func (d Digest128) Sum(b []byte) []byte { 39 h1, h2 := d.Sum128() 40 return append(b, 41 byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32), 42 byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), 43 44 byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32), 45 byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2), 46 ) 47 } 48 49 func (d Digest128) bmix(p []byte) (Digest128, []byte) { 50 h1, h2 := d.h1, d.h2 51 52 nblocks := len(p) / 16 53 for i := 0; i < nblocks; i++ { 54 t := (*[2]uint64)(unsafe.Pointer(&p[i*16])) 55 k1, k2 := t[0], t[1] 56 57 k1 *= c1_128 58 k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31) 59 k1 *= c2_128 60 h1 ^= k1 61 62 h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27) 63 h1 += h2 64 h1 = h1*5 + 0x52dce729 65 66 k2 *= c2_128 67 k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33) 68 k2 *= c1_128 69 h2 ^= k2 70 71 h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31) 72 h2 += h1 73 h2 = h2*5 + 0x38495ab5 74 } 75 d.h1, d.h2 = h1, h2 76 return d, p[nblocks*d.Size():] 77 } 78 79 func (d Digest128) bmixbuf() Digest128 { 80 h1, h2 := d.h1, d.h2 81 82 if d.tail != d.Size() { 83 panic(fmt.Errorf("expected full block")) 84 } 85 86 k1, k2 := d.loadUint64(0), d.loadUint64(1) 87 88 k1 *= c1_128 89 k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31) 90 k1 *= c2_128 91 h1 ^= k1 92 93 h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27) 94 h1 += h2 95 h1 = h1*5 + 0x52dce729 96 97 k2 *= c2_128 98 k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33) 99 k2 *= c1_128 100 h2 ^= k2 101 102 h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31) 103 h2 += h1 104 h2 = h2*5 + 0x38495ab5 105 106 d.h1, d.h2 = h1, h2 107 d.tail = 0 108 return d 109 } 110 111 // Sum128 returns the 128 bit hash of the digest. 112 func (d Digest128) Sum128() (h1, h2 uint64) { 113 h1, h2 = d.h1, d.h2 114 115 var k1, k2 uint64 116 switch d.tail & 15 { 117 case 15: 118 k2 ^= uint64(d.buf[14]) << 48 119 fallthrough 120 case 14: 121 k2 ^= uint64(d.buf[13]) << 40 122 fallthrough 123 case 13: 124 k2 ^= uint64(d.buf[12]) << 32 125 fallthrough 126 case 12: 127 k2 ^= uint64(d.buf[11]) << 24 128 fallthrough 129 case 11: 130 k2 ^= uint64(d.buf[10]) << 16 131 fallthrough 132 case 10: 133 k2 ^= uint64(d.buf[9]) << 8 134 fallthrough 135 case 9: 136 k2 ^= uint64(d.buf[8]) << 0 137 138 k2 *= c2_128 139 k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33) 140 k2 *= c1_128 141 h2 ^= k2 142 143 fallthrough 144 145 case 8: 146 k1 ^= uint64(d.buf[7]) << 56 147 fallthrough 148 case 7: 149 k1 ^= uint64(d.buf[6]) << 48 150 fallthrough 151 case 6: 152 k1 ^= uint64(d.buf[5]) << 40 153 fallthrough 154 case 5: 155 k1 ^= uint64(d.buf[4]) << 32 156 fallthrough 157 case 4: 158 k1 ^= uint64(d.buf[3]) << 24 159 fallthrough 160 case 3: 161 k1 ^= uint64(d.buf[2]) << 16 162 fallthrough 163 case 2: 164 k1 ^= uint64(d.buf[1]) << 8 165 fallthrough 166 case 1: 167 k1 ^= uint64(d.buf[0]) << 0 168 k1 *= c1_128 169 k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31) 170 k1 *= c2_128 171 h1 ^= k1 172 } 173 174 h1 ^= uint64(d.clen) 175 h2 ^= uint64(d.clen) 176 177 h1 += h2 178 h2 += h1 179 180 h1 = fmix64(h1) 181 h2 = fmix64(h2) 182 183 h1 += h2 184 h2 += h1 185 186 return h1, h2 187 } 188 189 // Write will write bytes to the digest and return a new digest 190 // representing the derived digest. 191 func (d Digest128) Write(p []byte) Digest128 { 192 n := len(p) 193 d.clen += n 194 195 if d.tail > 0 { 196 // Stick back pending bytes. 197 nfree := d.Size() - d.tail // nfree ∈ [1, d.Size()-1]. 198 if len(p) <= nfree { 199 // Everything can fit in buf 200 for i := 0; i < len(p); i++ { 201 d.buf[d.tail] = p[i] 202 d.tail++ 203 } 204 if len(p) == int(nfree) { 205 d = d.bmixbuf() 206 } 207 return d 208 } 209 210 // One full block can be formed. 211 add := p[:nfree] 212 for i := 0; i < len(add); i++ { 213 d.buf[d.tail] = add[i] 214 d.tail++ 215 } 216 217 // Process the full block 218 d = d.bmixbuf() 219 220 p = p[nfree:] 221 } 222 223 d, tail := d.bmix(p) 224 225 // Keep own copy of the 0 to Size()-1 pending bytes. 226 d.tail = len(tail) 227 for i := 0; i < d.tail; i++ { 228 d.buf[i] = tail[i] 229 } 230 231 return d 232 } 233 234 func fmix64(k uint64) uint64 { 235 k ^= k >> 33 236 k *= 0xff51afd7ed558ccd 237 k ^= k >> 33 238 k *= 0xc4ceb9fe1a85ec53 239 k ^= k >> 33 240 return k 241 } 242 243 // Sum128 returns the MurmurHash3 sum of data without any heap allocations. 244 func Sum128(data []byte) (h1 uint64, h2 uint64) { 245 return Sum128WithSeed(data, 0) 246 } 247 248 // Sum128WithSeed returns the MurmurHash3 sum of data given a seed 249 // without any heap allocations. 250 func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) { 251 return New128WithSeed(seed).Write(data).Sum128() 252 }