github.com/arr-ai/hash@v0.8.0/alg.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package hash 6 7 import ( 8 "crypto/rand" 9 "fmt" 10 "runtime" 11 "unsafe" 12 13 "golang.org/x/sys/cpu" 14 ) 15 16 const ( 17 c0 = (8-unsafe.Sizeof(uintptr(0)))/4*2860486313 + 18 (unsafe.Sizeof(uintptr(0))-4)/4*(33054211828000289&(1<<unsafe.Sizeof(uintptr(0))-1)) 19 c1 = (8-unsafe.Sizeof(uintptr(0)))/4*3267000013 + 20 (unsafe.Sizeof(uintptr(0))-4)/4*(23344194077549503&(1<<unsafe.Sizeof(uintptr(0))-1)) 21 ) 22 23 // type algorithms - known to compiler 24 const ( 25 algNOEQ = iota 26 algMEM0 27 algMEM8 28 algMEM16 29 algMEM32 30 algMEM64 31 algMEM128 32 algSTRING 33 algFLOAT32 34 algFLOAT64 35 algCPLX64 36 algCPLX128 37 algMAX 38 algPTR = algMEM32 + (algMEM64-algMEM32)*(unsafe.Sizeof(uintptr(0))/8) 39 algINT = algMEM32 + (algMEM64-algMEM32)*(unsafe.Sizeof(0)/8) 40 algUINT = algMEM32 + (algMEM64-algMEM32)*(unsafe.Sizeof(uint(0))/8) 41 ) 42 43 type hashFunc func(unsafe.Pointer, uintptr) uintptr 44 45 func memhash0(p unsafe.Pointer, h uintptr) uintptr { 46 return h 47 } 48 49 func memhash8(p unsafe.Pointer, h uintptr) uintptr { 50 return memhash(p, h, 1) 51 } 52 53 func memhash16(p unsafe.Pointer, h uintptr) uintptr { 54 return memhash(p, h, 2) 55 } 56 57 func memhash128(p unsafe.Pointer, h uintptr) uintptr { 58 return memhash(p, h, 16) 59 } 60 61 var algarray = [algMAX]hashFunc{ 62 algNOEQ: nil, 63 algMEM0: memhash0, 64 algMEM8: memhash8, 65 algMEM16: memhash16, 66 algMEM32: memhash32, 67 algMEM64: memhash64, 68 algMEM128: memhash128, 69 algSTRING: strhash, 70 algFLOAT32: f32hash, 71 algFLOAT64: f64hash, 72 algCPLX64: c64hash, 73 algCPLX128: c128hash, 74 } 75 76 var useAeshash bool 77 78 // in asm_*.s 79 func aeshash(p unsafe.Pointer, h, s uintptr) uintptr 80 func aeshash32(p unsafe.Pointer, h uintptr) uintptr 81 func aeshash64(p unsafe.Pointer, h uintptr) uintptr 82 func aeshashstr(p unsafe.Pointer, h uintptr) uintptr 83 84 type stringStruct struct { 85 str unsafe.Pointer 86 len int 87 } 88 89 func strhash(a unsafe.Pointer, h uintptr) uintptr { 90 x := (*stringStruct)(a) 91 return memhash(x.str, h, uintptr(x.len)) 92 } 93 94 // NOTE: Because NaN != NaN, a map can contain any 95 // number of (mostly useless) entries keyed with NaNs. 96 // To avoid long hash chains, we assign a random number 97 // as the hash value for a NaN. 98 99 func f32hash(p unsafe.Pointer, h uintptr) uintptr { 100 f := *(*float32)(p) 101 switch { 102 case f == 0: 103 return c1 * (c0 ^ h) // +0, -0 104 case f != f: 105 return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN 106 default: 107 return memhash(p, h, 4) 108 } 109 } 110 111 func f64hash(p unsafe.Pointer, h uintptr) uintptr { 112 f := *(*float64)(p) 113 switch { 114 case f == 0: 115 return c1 * (c0 ^ h) // +0, -0 116 case f != f: 117 return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN 118 default: 119 return memhash(p, h, 8) 120 } 121 } 122 123 func c64hash(p unsafe.Pointer, h uintptr) uintptr { 124 x := (*[2]float32)(p) 125 return f32hash(unsafe.Pointer(&x[1]), f32hash(unsafe.Pointer(&x[0]), h)) 126 } 127 128 func c128hash(p unsafe.Pointer, h uintptr) uintptr { 129 x := (*[2]float64)(p) 130 return f64hash(unsafe.Pointer(&x[1]), f64hash(unsafe.Pointer(&x[0]), h)) 131 } 132 133 const hashRandomBytes = unsafe.Sizeof(uintptr(0)) / 4 * 64 134 135 //go:nosplit 136 func getRandomData(r []byte) { 137 if _, err := rand.Read(r); err != nil { 138 panic(err) 139 } 140 } 141 142 // used in asm_{386,amd64,arm64}.s to seed the hash function 143 var aeskeysched [hashRandomBytes]byte 144 145 // used in hash{32,64}.go to seed the hash function 146 var hashkey [4]uintptr 147 148 // GetSeeds returns the internal seeds used for computing hashes. 149 func GetSeeds() (a []byte, h []uintptr) { 150 if useAeshash { 151 return aeskeysched[:], nil 152 } 153 return nil, hashkey[:] 154 } 155 156 // SetSeeds sets the internal seeds used for computing hashes. 157 func SetSeeds(a []byte, h []uintptr) error { 158 if useAeshash { 159 if len(a) != len(aeskeysched) { 160 return fmt.Errorf("SetSeeds: aeskeysched: %d != %d", len(a), len(aeskeysched)) //nolint:golint 161 } 162 copy(aeskeysched[:], a) 163 return nil 164 } 165 if len(h) != len(hashkey) { 166 return fmt.Errorf("SetSeeds: hashkey: %d != %d", len(h), len(hashkey)) //nolint:golint 167 } 168 copy(hashkey[:], h) 169 return nil 170 } 171 172 var _ = func() (_ struct{}) { 173 // Install AES hash algorithms if the instructions needed are present. 174 if (runtime.GOARCH == "386" || runtime.GOARCH == "amd64") && runtime.GOOS != "nacl" && 175 cpu.X86.HasAES && // AESENC 176 cpu.X86.HasSSSE3 && // PSHUFB 177 cpu.X86.HasSSE41 { // PINSR{D,Q} 178 initAlgAES() 179 } else if runtime.GOARCH == "arm64" && cpu.ARM64.HasAES { 180 initAlgAES() 181 } else { 182 getRandomData((*[len(hashkey) * int(unsafe.Sizeof(uintptr(0)))]byte)(unsafe.Pointer(&hashkey))[:]) 183 hashkey[0] |= 1 // make sure these numbers are odd 184 hashkey[1] |= 1 185 hashkey[2] |= 1 186 hashkey[3] |= 1 187 } 188 return 189 }() 190 191 func initAlgAES() { 192 if runtime.GOOS == "aix" { 193 // runtime.algarray is immutable on AIX: see cmd/link/internal/ld/xcoff.go 194 return 195 } 196 useAeshash = true 197 algarray[algMEM32] = aeshash32 198 algarray[algMEM64] = aeshash64 199 algarray[algSTRING] = aeshashstr 200 // Initialize with random data so hash collisions will be hard to engineer. 201 getRandomData(aeskeysched[:]) 202 } 203 204 // Note: These routines perform the read with an native endianness. 205 func readUnaligned32(p unsafe.Pointer) uint32 { 206 q := (*[4]byte)(p) 207 if bigEndian { 208 return uint32(q[3]) | uint32(q[2])<<8 | uint32(q[1])<<16 | uint32(q[0])<<24 209 } 210 return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24 211 } 212 213 func readUnaligned64(p unsafe.Pointer) uint64 { 214 q := (*[8]byte)(p) 215 if bigEndian { 216 return uint64(q[7]) | uint64(q[6])<<8 | uint64(q[5])<<16 | uint64(q[4])<<24 | 217 uint64(q[3])<<32 | uint64(q[2])<<40 | uint64(q[1])<<48 | uint64(q[0])<<56 218 } 219 return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | 220 uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56 221 }