Change-Id: I0460ecbbe8a7b564b72a36132a32b60d68aed7f4
This commit is contained in:
qiulaidongfeng 2024-09-04 20:57:06 +08:00
parent 1ce6954117
commit 10f077890e
3 changed files with 47 additions and 24 deletions

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package maphash provides hash functions on byte sequences.
// Package maphash provides hash functions on byte sequences or comparable value.
// These hash functions are intended to be used to implement hash tables or
// other data structures that need to map arbitrary strings or byte
// sequences to a uniform distribution on unsigned 64-bit integers.
@ -16,7 +16,9 @@ import (
"fmt"
"internal/abi"
"internal/byteorder"
"math"
"reflect"
"unsafe"
)
// A Seed is a random value that selects the specific hash function
@ -285,13 +287,11 @@ func (h *Hash) BlockSize() int { return len(h.buf) }
// Comparable returns the hash of comparable value v with the given seed
// such that Comparable(s, v1) == Comparable(s, v2) if v1 == v2.
// If v contains a floating-point NaN, then the hash is non-deterministically random.
// If v != v, then the resulting hash is randomly distributed.
func Comparable[T comparable](seed Seed, v T) uint64 {
abi.Escape(v)
t := abi.TypeFor[T]()
len := t.Size()
if len == 0 {
return seed.s
t, ret := comparableReady(seed, v)
if ret != 0 {
return ret
}
var h Hash
h.SetSeed(seed)
@ -299,12 +299,35 @@ func Comparable[T comparable](seed Seed, v T) uint64 {
return h.Sum64()
}
func comparableReady[T comparable](seed Seed, v T) (*abi.Type, uint64) {
// Let v be on the heap,
// make sure that if v is a pointer to a variable inside the function,
// if v and the value it points to do not change,
// Comparable(seed,v) before goroutine stack growth
// is equal to Comparable(seed,v) after goroutine stack growth.
abi.Escape(v)
t := abi.TypeFor[T]()
len := t.Size()
if len == 0 {
return t, seed.s
}
return t, 0
}
// WriteComparable adds x to the data hashed by h.
func WriteComparable[T comparable](h *Hash, x T) {
comparableF(h, x, abi.TypeFor[T]())
t, ret := comparableReady(h.seed, x)
if ret != 0 {
return
}
comparableF(h, x, t)
}
func appendT(h *Hash, v reflect.Value) {
var buf [8]byte
byteorder.LePutUint64(buf[:], uint64(uintptr(unsafe.Pointer(abi.TypeOf(v.Type())))))
h.Write(buf[:])
switch v.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
var buf [8]byte
@ -317,8 +340,11 @@ func appendT(h *Hash, v reflect.Value) {
h.Write(buf[:])
return
case reflect.Array:
for i := range v.Len() {
appendT(h, v.Index(i))
var buf [8]byte
for i := range uint64(v.Len()) {
byteorder.LePutUint64(buf[:], i)
h.Write(buf[:])
appendT(h, v.Index(int(i)))
}
return
case reflect.String:
@ -326,26 +352,26 @@ func appendT(h *Hash, v reflect.Value) {
return
case reflect.Struct:
for i := range v.NumField() {
appendT(h, v.Field(i))
f := v.Field(i)
h.WriteString(f.Type().String())
appendT(h, f)
}
return
case reflect.Complex64, reflect.Complex128:
c := v.Complex()
var buf [8]byte
byteorder.LePutUint64(buf[:], uint64(real(c)))
byteorder.LePutUint64(buf[:], math.Float64bits(real(c)))
h.Write(buf[:])
byteorder.LePutUint64(buf[:], uint64(imag(c)))
byteorder.LePutUint64(buf[:], math.Float64bits(imag(c)))
h.Write(buf[:])
return
case reflect.Float32, reflect.Float64:
var buf [8]byte
byteorder.LePutUint64(buf[:], uint64(v.Float()))
byteorder.LePutUint64(buf[:], math.Float64bits(v.Float()))
h.Write(buf[:])
return
case reflect.Bool:
var buf [8]byte
byteorder.LePutUint16(buf[:], btoi(v.Bool()))
h.Write(buf[:])
h.WriteByte(btoi(v.Bool()))
return
case reflect.UnsafePointer, reflect.Pointer:
var buf [8]byte
@ -359,7 +385,7 @@ func appendT(h *Hash, v reflect.Value) {
panic(fmt.Errorf("hash/maphash: %s not comparable", v.Type().String()))
}
func btoi(b bool) uint16 {
func btoi(b bool) byte {
if b {
return 1
}

View File

@ -100,9 +100,5 @@ var strTyp = reflect.TypeFor[string]()
func comparableF[T comparable](h *Hash, v T, t *abi.Type) {
vv := reflect.ValueOf(v)
typ := vv.Type()
if typ == strTyp {
h.WriteString(vv.String())
return
}
appendT(h, vv)
}

View File

@ -49,10 +49,11 @@ func comparableF[T comparable](h *Hash, v T, t *abi.Type) {
l := t.Size()
k := t.Kind()
if k == abi.String {
s := ((*string)(unsafe.Pointer(&v)))
s := ((*string)(ptr))
h.WriteString(*s)
return
} else if t.TFlag&abi.TFlagRegularMemory == 0 {
}
if t.TFlag&abi.TFlagRegularMemory == 0 {
// Note: if T like struct {s string}
// str value equal but ptr not equal,
// if think of it as a contiguous piece of memory,