github.com/fufuok/freelru@v0.13.3/hasher.go (about) 1 package freelru 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 // MakeHasher creates a fast hash function for the given comparable type. 9 // The only limitation is that the type should not contain interfaces inside 10 // based on runtime.typehash. 11 // Thanks to @puzpuzpuz for the idea in xsync. 12 func MakeHasher[T comparable]() func(T) uint32 { 13 var zero T 14 seed := makeSeed() 15 if reflect.TypeOf(&zero).Elem().Kind() == reflect.Interface { 16 return func(value T) uint32 { 17 iValue := any(value) 18 i := (*iface)(unsafe.Pointer(&iValue)) 19 return runtimeTypehash32(i.typ, i.word, seed) 20 } 21 } 22 var iZero any = zero 23 i := (*iface)(unsafe.Pointer(&iZero)) 24 return func(value T) uint32 { 25 return runtimeTypehash32(i.typ, unsafe.Pointer(&value), seed) 26 } 27 } 28 29 // makeSeed creates a random seed. 30 func makeSeed() uint32 { 31 var s1 uint32 32 for { 33 s1 = runtimeFastrand() 34 // We use seed 0 to indicate an uninitialized seed/hash, 35 // so keep trying until we get a non-zero seed. 36 if s1 != 0 { 37 break 38 } 39 } 40 return s1 41 } 42 43 // how interface is represented in memory 44 type iface struct { 45 typ uintptr 46 word unsafe.Pointer 47 } 48 49 // same as runtimeTypehash, but always returns a uint64 50 // see: maphash.rthash function for details 51 func runtimeTypehash32(t uintptr, p unsafe.Pointer, seed uint32) uint32 { 52 return uint32(runtimeTypehash(t, p, uintptr(seed))) 53 } 54 55 //go:noescape 56 //go:linkname runtimeTypehash runtime.typehash 57 func runtimeTypehash(t uintptr, p unsafe.Pointer, h uintptr) uintptr 58 59 //go:noescape 60 //go:linkname runtimeFastrand runtime.fastrand 61 func runtimeFastrand() uint32