github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/kv/hasher.go (about)

     1  //go:build go1.22
     2  // +build go1.22
     3  
     4  package kv
     5  
     6  import (
     7  	randv2 "math/rand/v2"
     8  	"unsafe"
     9  )
    10  
    11  type hashFn func(unsafe.Pointer, uintptr) uintptr
    12  
    13  // Copy from go1.22.1
    14  // go/src/internal/abi/type.go
    15  type _mapType struct {
    16  	_      [9]uint64                             // go/src/internal/abi/type.go Type: size 48, 6 bytes; key, elem, bucket: size 8 * 3, 3 bytes
    17  	hasher func(unsafe.Pointer, uintptr) uintptr // function for hashing keys (ptr to key, seed) -> hash
    18  	_      uint64                                // key size, value size, bucket size, flags
    19  }
    20  
    21  type _mapIface struct {
    22  	typ *_mapType
    23  	_   uint64 // go/src/runtime/map.go, hmap pointer, size 8, 1 byte
    24  }
    25  
    26  //go:nosplit
    27  //go:nocheckptr
    28  func noescape(p unsafe.Pointer) unsafe.Pointer {
    29  	x := uintptr(p)
    30  	return unsafe.Pointer(x ^ 0)
    31  }
    32  
    33  func newHashSeed() uintptr {
    34  	return uintptr(randv2.Int())
    35  }
    36  
    37  type Hasher[K comparable] struct {
    38  	hash hashFn
    39  	seed uintptr
    40  }
    41  
    42  func (h Hasher[K]) Hash(key K) uint64 {
    43  	// Promise the key no escapes to the heap.
    44  	p := noescape(unsafe.Pointer(&key))
    45  	return uint64(h.hash(p, h.seed))
    46  }
    47  
    48  func getRuntimeHasher[K comparable]() (fn hashFn) {
    49  	i := (any)(make(map[K]struct{}))
    50  	iface := (*_mapIface)(unsafe.Pointer(&i))
    51  	fn = iface.typ.hasher
    52  	return
    53  }
    54  
    55  func newHasher[K comparable]() Hasher[K] {
    56  	return Hasher[K]{
    57  		hash: getRuntimeHasher[K](),
    58  		seed: newHashSeed(),
    59  	}
    60  }
    61  
    62  func newSeedHasher[K comparable](hasher Hasher[K]) Hasher[K] {
    63  	return Hasher[K]{
    64  		hash: hasher.hash,
    65  		seed: newHashSeed(),
    66  	}
    67  }