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