github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/internal/rthash/hash.go (about) 1 package rthash 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/jxskiss/gopkg/v2/internal/linkname" 8 "github.com/jxskiss/gopkg/v2/internal/unsafeheader" 9 ) 10 11 type HashFunc[K comparable] func(key K) uintptr 12 13 // NewHashFunc returns a new hash function, which exposes several 14 // hash functions in package [runtime]. 15 // 16 // Note that this function generates a random seed, each calling of this 17 // function returns DIFFERENT hash function, different hash functions 18 // generate different result for same input. 19 // 20 // The returned function is safe for concurrent use by multiple goroutines. 21 func NewHashFunc[K comparable]() HashFunc[K] { 22 var seed uintptr 23 for seed == 0 { 24 seed = uintptr(linkname.Runtime_fastrand64()) 25 } 26 27 var zero K 28 typ := reflect.TypeOf(zero) 29 if typ == nil { 30 return func(key K) uintptr { 31 return linkname.Runtime_efaceHash(key, seed) 32 } 33 } 34 35 switch typ.Kind() { 36 case reflect.Int32, reflect.Int64, reflect.Int, 37 reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr: 38 size := unsafe.Sizeof(zero) 39 if size == 4 { 40 return func(key K) uintptr { 41 return linkname.Runtime_memhash32(noescape(unsafe.Pointer(&key)), seed) 42 } 43 } 44 return func(key K) uintptr { 45 return linkname.Runtime_memhash64(noescape(unsafe.Pointer(&key)), seed) 46 } 47 case reflect.String: 48 return func(key K) uintptr { 49 return linkname.Runtime_stringHash(*(*string)(unsafe.Pointer(&key)), seed) 50 } 51 default: 52 rtype := unsafeheader.ToRType(typ) 53 return func(key K) uintptr { 54 return linkname.Runtime_typehash(rtype, unsafe.Pointer(&key), seed) 55 } 56 } 57 } 58 59 // NewBytesHash returns a new hash function to hash byte slice. 60 // 61 // Note that this function generates a random seed, each calling of this 62 // function returns DIFFERENT hash function, different hash functions 63 // generate different hash result for same input. 64 // 65 // The returned function is safe for concurrent use by multiple goroutines. 66 func NewBytesHash() func(b []byte) uintptr { 67 var seed uintptr 68 for seed == 0 { 69 seed = uintptr(linkname.Runtime_fastrand64()) 70 } 71 return func(b []byte) uintptr { 72 return linkname.Runtime_bytesHash(b, seed) 73 } 74 } 75 76 // noescape hides a pointer from escape analysis. noescape is 77 // the identity function but escape analysis doesn't think the 78 // output depends on the input. noescape is inlined and currently 79 // compiles down to zero instructions. 80 // USE CAREFULLY! 81 // 82 //go:nosplit 83 func noescape(p unsafe.Pointer) unsafe.Pointer { 84 x := uintptr(p) 85 return unsafe.Pointer(x ^ 0) //nolint:staticcheck 86 }