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  }