diff --git a/src/hash/maphash/maphash.go b/src/hash/maphash/maphash.go index 10f5beff48..76127a1894 100644 --- a/src/hash/maphash/maphash.go +++ b/src/hash/maphash/maphash.go @@ -288,40 +288,31 @@ func (h *Hash) BlockSize() int { return len(h.buf) } // such that Comparable(s, v1) == Comparable(s, v2) if v1 == v2. // If v != v, then the resulting hash is randomly distributed. func Comparable[T comparable](seed Seed, v T) uint64 { - ret := comparableReady(seed, v) - if ret != 0 { - return ret - } + comparableReady(v) var h Hash h.SetSeed(seed) comparableF(&h, v) return h.Sum64() } -func comparableReady[T comparable](seed Seed, v T) uint64 { +func comparableReady[T comparable](v T) { // 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 seed.s - } - return 0 } // WriteComparable adds x to the data hashed by h. func WriteComparable[T comparable](h *Hash, x T) { - ret := comparableReady(h.seed, x) - if ret != 0 { - return - } + comparableReady(x) comparableF(h, x) } +// appendT hash a value, +// when the value cannot be directly hash raw memory, +// or when purego is used. func appendT(h *Hash, v reflect.Value) { switch v.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: @@ -350,10 +341,6 @@ func appendT(h *Hash, v reflect.Value) { case reflect.Struct: for i := range v.NumField() { f := v.Field(i) - // do not want to hash to the same value, - // type T1 struct { x int }{1} - // type T2 struct { x int; s string }{1,""}. - h.WriteString(f.Type().String()) appendT(h, f) } return @@ -370,6 +357,9 @@ func appendT(h *Hash, v reflect.Value) { return case reflect.UnsafePointer, reflect.Pointer: var buf [8]byte + // because pointing to the abi.Escape call in comparableReady, + // So this is ok to hash pointer, + // this way because we know their target won't be moved. byteorder.LePutUint64(buf[:], uint64(v.Pointer())) h.Write(buf[:]) return @@ -393,7 +383,6 @@ func (h *Hash) float64(f float64) { } byteorder.LePutUint64(buf[:], math.Float64bits(f)) h.Write(buf[:]) - return } func btoi(b bool) byte { diff --git a/src/hash/maphash/maphash_runtime.go b/src/hash/maphash/maphash_runtime.go index ad65dba6fd..1570e7dea4 100644 --- a/src/hash/maphash/maphash_runtime.go +++ b/src/hash/maphash/maphash_runtime.go @@ -46,17 +46,16 @@ func randUint64() uint64 { func comparableF[T comparable](h *Hash, v T) { t := abi.TypeFor[T]() - ptr := unsafe.Pointer(&v) - l := t.Size() + // We can only use the raw memory contents for the hash, + // if the raw memory contents are used for computing equality. + // That works for some types (int), + // but not others (float, string, structs with padding, etc.) if t.TFlag&abi.TFlagRegularMemory != 0 { + ptr := unsafe.Pointer(&v) + l := t.Size() h.Write(unsafe.Slice((*byte)(ptr), l)) return } - // Note: if T like struct {s string} - // str value equal but ptr not equal, - // if think of it as a contiguous piece of memory, - // hash it, that happen v1 == v2 - // Comparable(s, v1) != Comparable(s, v2). vv := reflect.ValueOf(v) appendT(h, vv) } diff --git a/src/hash/maphash/maphash_test.go b/src/hash/maphash/maphash_test.go index 72df7a89c9..9173f0388d 100644 --- a/src/hash/maphash/maphash_test.go +++ b/src/hash/maphash/maphash_test.go @@ -257,10 +257,6 @@ func TestComparable(t *testing.T) { testComparable(t, float64(0), negativeZero[float64]()) testComparableNoEqual(t, math.NaN(), math.NaN()) testComparableNoEqual(t, [2]string{"a", ""}, [2]string{"", "a"}) - testComparableNoEqual(t, any(struct{ x int }{x: 1}), any(struct { - x int - s string - }{x: 1})) } func testComparableNoEqual[T comparable](t *testing.T, v1, v2 T) { @@ -357,10 +353,6 @@ func TestWriteComparable(t *testing.T) { testWriteComparable(t, float64(0), negativeZero[float64]()) testWriteComparableNoEqual(t, math.NaN(), math.NaN()) testWriteComparableNoEqual(t, [2]string{"a", ""}, [2]string{"", "a"}) - testWriteComparableNoEqual(t, any(struct{ x int }{x: 1}), any(struct { - x int - s string - }{x: 1})) } func testWriteComparableNoEqual[T comparable](t *testing.T, v1, v2 T) {