go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/murmur3/murmur128.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package murmur3 9 10 import ( 11 "hash" 12 "math/bits" 13 "unsafe" 14 ) 15 16 const ( 17 c1_128 = 0x87c37b91114253d5 18 c2_128 = 0x4cf5ad432745937f 19 ) 20 21 // Make sure interfaces are correctly implemented. 22 var ( 23 _ hash.Hash = new(digest128) 24 _ Hash128 = new(digest128) 25 _ bmixer = new(digest128) 26 ) 27 28 // Hash128 represents a 128-bit hasher 29 // Hack: the standard api doesn't define any Hash128 interface. 30 type Hash128 interface { 31 hash.Hash 32 Sum128() (uint64, uint64) 33 } 34 35 // digest128 represents a partial evaluation of a 128 bites hash. 36 type digest128 struct { 37 digest 38 h1 uint64 // Unfinalized running hash part 1. 39 h2 uint64 // Unfinalized running hash part 2. 40 } 41 42 // New128 returns a 128-bit hasher 43 func New128() Hash128 { return New128WithSeed(0) } 44 45 // New128WithSeed returns a 128-bit hasher set with explicit seed value 46 func New128WithSeed(seed uint32) Hash128 { 47 d := new(digest128) 48 d.seed = seed 49 d.bmixer = d 50 d.Reset() 51 return d 52 } 53 54 func (d *digest128) Size() int { return 16 } 55 56 func (d *digest128) reset() { d.h1, d.h2 = uint64(d.seed), uint64(d.seed) } 57 58 func (d *digest128) Sum(b []byte) []byte { 59 h1, h2 := d.Sum128() 60 return append(b, 61 byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32), 62 byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), 63 64 byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32), 65 byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2), 66 ) 67 } 68 69 func (d *digest128) bmix(p []byte) (tail []byte) { 70 h1, h2 := d.h1, d.h2 71 72 nblocks := len(p) / 16 73 for i := 0; i < nblocks; i++ { 74 t := (*[2]uint64)(unsafe.Pointer(&p[i*16])) 75 k1, k2 := t[0], t[1] 76 77 k1 *= c1_128 78 k1 = bits.RotateLeft64(k1, 31) 79 k1 *= c2_128 80 h1 ^= k1 81 82 h1 = bits.RotateLeft64(h1, 27) 83 h1 += h2 84 h1 = h1*5 + 0x52dce729 85 86 k2 *= c2_128 87 k2 = bits.RotateLeft64(k2, 33) 88 k2 *= c1_128 89 h2 ^= k2 90 91 h2 = bits.RotateLeft64(h2, 31) 92 h2 += h1 93 h2 = h2*5 + 0x38495ab5 94 } 95 d.h1, d.h2 = h1, h2 96 return p[nblocks*d.Size():] 97 } 98 99 func (d *digest128) Sum128() (h1, h2 uint64) { 100 101 h1, h2 = d.h1, d.h2 102 103 var k1, k2 uint64 104 switch len(d.tail) & 15 { 105 case 15: 106 k2 ^= uint64(d.tail[14]) << 48 107 fallthrough 108 case 14: 109 k2 ^= uint64(d.tail[13]) << 40 110 fallthrough 111 case 13: 112 k2 ^= uint64(d.tail[12]) << 32 113 fallthrough 114 case 12: 115 k2 ^= uint64(d.tail[11]) << 24 116 fallthrough 117 case 11: 118 k2 ^= uint64(d.tail[10]) << 16 119 fallthrough 120 case 10: 121 k2 ^= uint64(d.tail[9]) << 8 122 fallthrough 123 case 9: 124 k2 ^= uint64(d.tail[8]) << 0 125 126 k2 *= c2_128 127 k2 = bits.RotateLeft64(k2, 33) 128 k2 *= c1_128 129 h2 ^= k2 130 131 fallthrough 132 133 case 8: 134 k1 ^= uint64(d.tail[7]) << 56 135 fallthrough 136 case 7: 137 k1 ^= uint64(d.tail[6]) << 48 138 fallthrough 139 case 6: 140 k1 ^= uint64(d.tail[5]) << 40 141 fallthrough 142 case 5: 143 k1 ^= uint64(d.tail[4]) << 32 144 fallthrough 145 case 4: 146 k1 ^= uint64(d.tail[3]) << 24 147 fallthrough 148 case 3: 149 k1 ^= uint64(d.tail[2]) << 16 150 fallthrough 151 case 2: 152 k1 ^= uint64(d.tail[1]) << 8 153 fallthrough 154 case 1: 155 k1 ^= uint64(d.tail[0]) << 0 156 k1 *= c1_128 157 k1 = bits.RotateLeft64(k1, 31) 158 k1 *= c2_128 159 h1 ^= k1 160 } 161 162 h1 ^= uint64(d.clen) 163 h2 ^= uint64(d.clen) 164 165 h1 += h2 166 h2 += h1 167 168 h1 = fmix64(h1) 169 h2 = fmix64(h2) 170 171 h1 += h2 172 h2 += h1 173 174 return h1, h2 175 } 176 177 func fmix64(k uint64) uint64 { 178 k ^= k >> 33 179 k *= 0xff51afd7ed558ccd 180 k ^= k >> 33 181 k *= 0xc4ceb9fe1a85ec53 182 k ^= k >> 33 183 return k 184 } 185 186 // Sum128 returns the MurmurHash3 sum of data. It is equivalent to the 187 // following sequence (without the extra burden and the extra allocation): 188 // 189 // hasher := New128() 190 // hasher.Write(data) 191 // return hasher.Sum128() 192 func Sum128(data []byte) (h1 uint64, h2 uint64) { return Sum128WithSeed(data, 0) } 193 194 // Sum128WithSeed returns the MurmurHash3 sum of data. It is equivalent to the 195 // following sequence (without the extra burden and the extra allocation): 196 // 197 // hasher := New128WithSeed(seed) 198 // hasher.Write(data) 199 // return hasher.Sum128() 200 func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) { 201 d := digest128{h1: uint64(seed), h2: uint64(seed)} 202 d.seed = seed 203 d.tail = d.bmix(data) 204 d.clen = len(data) 205 return d.Sum128() 206 }