github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/memhash_tsip.go (about) 1 //go:build runtime_memhash_tsip 2 3 // This is the tsip hash developed by Damian Gryski, based on ideas from SipHash. 4 // It is slower than leveldb's hash, but should be "stronger". 5 6 // https://en.wikipedia.org/wiki/SipHash 7 // https://github.com/veorq/SipHash 8 // https://github.com/dgryski/tsip 9 10 package runtime 11 12 import ( 13 "encoding/binary" 14 "math/bits" 15 "unsafe" 16 ) 17 18 func ptrToSlice(ptr unsafe.Pointer, n uintptr) []byte { 19 var p []byte 20 21 type _bslice struct { 22 ptr *byte 23 len uintptr 24 cap uintptr 25 } 26 27 pslice := (*_bslice)(unsafe.Pointer(&p)) 28 pslice.ptr = (*byte)(ptr) 29 pslice.cap = n 30 pslice.len = n 31 32 return p 33 } 34 35 type sip struct { 36 v0, v1 uint64 37 } 38 39 func (s *sip) round() { 40 s.v0 += s.v1 41 s.v1 = bits.RotateLeft64(s.v1, 13) ^ s.v0 42 s.v0 = bits.RotateLeft64(s.v0, 35) + s.v1 43 s.v1 = bits.RotateLeft64(s.v1, 17) ^ s.v0 44 s.v0 = bits.RotateLeft64(s.v0, 21) 45 } 46 47 func hash64(ptr unsafe.Pointer, n uintptr, seed uintptr) uint64 { 48 49 p := ptrToSlice(ptr, n) 50 51 k0 := uint64(seed) 52 k1 := uint64(0) 53 54 s := sip{ 55 v0: k0 ^ 0x736f6d6570736575, 56 v1: k1 ^ 0x646f72616e646f6d, 57 } 58 59 b := uint64(len(p)) << 56 60 61 for len(p) >= 8 { 62 m := binary.LittleEndian.Uint64(p[:8]) 63 s.v1 ^= m 64 s.round() 65 s.v0 ^= m 66 p = p[8:] 67 } 68 69 switch len(p) { 70 case 7: 71 b |= uint64(p[6]) << 48 72 fallthrough 73 case 6: 74 b |= uint64(p[5]) << 40 75 fallthrough 76 case 5: 77 b |= uint64(p[4]) << 32 78 fallthrough 79 case 4: 80 b |= uint64(p[3]) << 24 81 fallthrough 82 case 3: 83 b |= uint64(p[2]) << 16 84 fallthrough 85 case 2: 86 b |= uint64(p[1]) << 8 87 fallthrough 88 case 1: 89 b |= uint64(p[0]) 90 } 91 92 // last block 93 s.v1 ^= b 94 s.round() 95 s.v0 ^= b 96 97 // finalization 98 s.v1 ^= 0xff 99 s.round() 100 s.v1 = bits.RotateLeft64(s.v1, 32) 101 s.round() 102 s.v1 = bits.RotateLeft64(s.v1, 32) 103 104 return s.v0 ^ s.v1 105 } 106 107 type sip32 struct { 108 v0, v1 uint32 109 } 110 111 func (s *sip32) round() { 112 s.v0 += s.v1 113 s.v1 = bits.RotateLeft32(s.v1, 5) ^ s.v0 114 s.v0 = bits.RotateLeft32(s.v0, 8) + s.v1 115 s.v1 = bits.RotateLeft32(s.v1, 13) ^ s.v0 116 s.v0 = bits.RotateLeft32(s.v0, 7) 117 } 118 119 func hash32(ptr unsafe.Pointer, n uintptr, seed uintptr) uint32 { 120 // TODO(dgryski): replace this messiness with unsafe.Slice when we can use 1.17 features 121 122 p := ptrToSlice(ptr, n) 123 124 k0 := uint32(seed) 125 k1 := uint32(seed >> 32) 126 127 s := sip32{ 128 v0: k0 ^ 0x74656462, 129 v1: k1 ^ 0x6c796765, 130 } 131 b := uint32(len(p)) << 24 132 133 for len(p) >= 4 { 134 m := binary.LittleEndian.Uint32(p[:4]) 135 s.v1 ^= m 136 s.round() 137 s.v0 ^= m 138 p = p[4:] 139 } 140 141 switch len(p) { 142 case 3: 143 b |= uint32(p[2]) << 16 144 fallthrough 145 case 2: 146 b |= uint32(p[1]) << 8 147 fallthrough 148 case 1: 149 b |= uint32(p[0]) 150 } 151 152 // last block 153 s.v1 ^= b 154 s.round() 155 s.v0 ^= b 156 157 // finalization 158 s.v1 ^= 0xff 159 s.round() 160 s.v1 = bits.RotateLeft32(s.v1, 16) 161 s.round() 162 s.v1 = bits.RotateLeft32(s.v1, 16) 163 164 return s.v0 ^ s.v1 165 }