github.com/puzpuzpuz/xsync/v3@v3.1.1-0.20240225193106-cbe4ec1e954f/util_hash.go (about) 1 package xsync 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 // makeSeed creates a random seed. 9 func makeSeed() uint64 { 10 var s1 uint32 11 for { 12 s1 = runtime_fastrand() 13 // We use seed 0 to indicate an uninitialized seed/hash, 14 // so keep trying until we get a non-zero seed. 15 if s1 != 0 { 16 break 17 } 18 } 19 s2 := runtime_fastrand() 20 return uint64(s1)<<32 | uint64(s2) 21 } 22 23 // hashString calculates a hash of s with the given seed. 24 func hashString(s string, seed uint64) uint64 { 25 if s == "" { 26 return seed 27 } 28 strh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 29 return uint64(runtime_memhash(unsafe.Pointer(strh.Data), uintptr(seed), uintptr(strh.Len))) 30 } 31 32 //go:noescape 33 //go:linkname runtime_memhash runtime.memhash 34 func runtime_memhash(p unsafe.Pointer, h, s uintptr) uintptr 35 36 // makeHasher creates a fast hash function for the given comparable type. 37 // The only limitation is that the type should not contain interfaces inside 38 // based on runtime.typehash. 39 func makeHasher[T comparable]() func(T, uint64) uint64 { 40 var zero T 41 42 if reflect.TypeOf(&zero).Elem().Kind() == reflect.Interface { 43 return func(value T, seed uint64) uint64 { 44 iValue := any(value) 45 i := (*iface)(unsafe.Pointer(&iValue)) 46 return runtime_typehash64(i.typ, i.word, seed) 47 } 48 } else { 49 var iZero any = zero 50 i := (*iface)(unsafe.Pointer(&iZero)) 51 return func(value T, seed uint64) uint64 { 52 return runtime_typehash64(i.typ, unsafe.Pointer(&value), seed) 53 } 54 } 55 } 56 57 // how interface is represented in memory 58 type iface struct { 59 typ uintptr 60 word unsafe.Pointer 61 } 62 63 // same as runtime_typehash, but always returns a uint64 64 // see: maphash.rthash function for details 65 func runtime_typehash64(t uintptr, p unsafe.Pointer, seed uint64) uint64 { 66 if unsafe.Sizeof(uintptr(0)) == 8 { 67 return uint64(runtime_typehash(t, p, uintptr(seed))) 68 } 69 70 lo := runtime_typehash(t, p, uintptr(seed)) 71 hi := runtime_typehash(t, p, uintptr(seed>>32)) 72 return uint64(hi)<<32 | uint64(lo) 73 } 74 75 //go:noescape 76 //go:linkname runtime_typehash runtime.typehash 77 func runtime_typehash(t uintptr, p unsafe.Pointer, h uintptr) uintptr