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