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