diff --git a/src/hash/maphash/maphash.go b/src/hash/maphash/maphash.go index 2ea39dacd9..10f5beff48 100644 --- a/src/hash/maphash/maphash.go +++ b/src/hash/maphash/maphash.go @@ -359,26 +359,11 @@ func appendT(h *Hash, v reflect.Value) { return case reflect.Complex64, reflect.Complex128: c := v.Complex() - var buf [8]byte - byteorder.LePutUint64(buf[:], math.Float64bits(real(c))) - h.Write(buf[:]) - byteorder.LePutUint64(buf[:], math.Float64bits(imag(c))) - h.Write(buf[:]) + h.float64(real(c)) + h.float64(imag(c)) return case reflect.Float32, reflect.Float64: - f := v.Float() - if f == 0 { - h.WriteByte(0) - return - } - var buf [8]byte - if f != f { - byteorder.LePutUint64(buf[:], randUint64()) - h.Write(buf[:]) - return - } - byteorder.LePutUint64(buf[:], math.Float64bits(f)) - h.Write(buf[:]) + h.float64(v.Float()) return case reflect.Bool: h.WriteByte(btoi(v.Bool())) @@ -395,6 +380,22 @@ func appendT(h *Hash, v reflect.Value) { panic(fmt.Errorf("hash/maphash: %s not comparable", v.Type().String())) } +func (h *Hash) float64(f float64) { + if f == 0 { + h.WriteByte(0) + return + } + var buf [8]byte + if f != f { + byteorder.LePutUint64(buf[:], randUint64()) + h.Write(buf[:]) + return + } + byteorder.LePutUint64(buf[:], math.Float64bits(f)) + h.Write(buf[:]) + return +} + func btoi(b bool) byte { if b { return 1 diff --git a/src/hash/maphash/maphash_purego.go b/src/hash/maphash/maphash_purego.go index 9f348ed6ae..be7ac52f23 100644 --- a/src/hash/maphash/maphash_purego.go +++ b/src/hash/maphash/maphash_purego.go @@ -94,8 +94,6 @@ func mix(a, b uint64) uint64 { return hi ^ lo } -var strTyp = reflect.TypeFor[string]() - func comparableF[T comparable](h *Hash, v T) { vv := reflect.ValueOf(v) appendT(h, vv) diff --git a/src/hash/maphash/maphash_runtime.go b/src/hash/maphash/maphash_runtime.go index be5f1c3697..ad65dba6fd 100644 --- a/src/hash/maphash/maphash_runtime.go +++ b/src/hash/maphash/maphash_runtime.go @@ -48,21 +48,15 @@ func comparableF[T comparable](h *Hash, v T) { t := abi.TypeFor[T]() ptr := unsafe.Pointer(&v) l := t.Size() - k := t.Kind() - if k == abi.String { - s := ((*string)(ptr)) - h.WriteString(*s) + if t.TFlag&abi.TFlagRegularMemory != 0 { + h.Write(unsafe.Slice((*byte)(ptr), l)) return } - 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, - // hash it, that happen v1 == v2 - // Comparable(s, v1) != Comparable(s, v2). - vv := reflect.ValueOf(v) - appendT(h, vv) - return - } - h.Write(unsafe.Slice((*byte)(ptr), l)) + // 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 9e1cdca1e0..72df7a89c9 100644 --- a/src/hash/maphash/maphash_test.go +++ b/src/hash/maphash/maphash_test.go @@ -227,13 +227,19 @@ func TestComparable(t *testing.T) { testComparable(t, uintptr(12)) testComparable(t, any("s")) testComparable(t, "s") + testComparable(t, true) testComparable(t, new(float64)) testComparable(t, float64(9)) testComparable(t, complex128(9i+1)) + testComparable(t, struct{}{}) testComparable(t, struct { i int + u uint + b bool f float64 - }{i: 9, f: 9.9}) + p *int + a any + }{i: 9, u: 1, b: true, f: 9.9, p: new(int), a: 1}) type S struct { s string } @@ -246,11 +252,21 @@ func TestComparable(t *testing.T) { t.Fatalf("unexpected two heapStr value not equal") } testComparable(t, s1, s2) + testComparable(t, s1.s, s2.s) testComparable(t, float32(0), negativeZero[float32]()) 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) { seed := MakeSeed() - if Comparable(seed, math.NaN()) == Comparable(seed, math.NaN()) { - t.Fatalf("Comparable(seed, NaN) == Comparable(seed, NaN)") + if Comparable(seed, v1) == Comparable(seed, v2) { + t.Fatalf("Comparable(seed, %v) == Comparable(seed, %v)", v1, v2) } } @@ -291,13 +307,17 @@ func testComparable[T comparable](t *testing.T, v T, v2 ...T) { }) } +var use byte + //go:noinline func stackGrow(dep int) { if dep == 0 { return } var local [1024]byte - _ = local + // make sure local is allocated on the stack. + local[randUint64()%1024] = byte(randUint64()) + use = local[randUint64()%1024] stackGrow(dep - 1) } @@ -307,13 +327,19 @@ func TestWriteComparable(t *testing.T) { testWriteComparable(t, uintptr(12)) testWriteComparable(t, any("s")) testWriteComparable(t, "s") + testComparable(t, true) testWriteComparable(t, new(float64)) testWriteComparable(t, float64(9)) testWriteComparable(t, complex128(9i+1)) + testWriteComparable(t, struct{}{}) testWriteComparable(t, struct { i int + u uint + b bool f float64 - }{i: 9, f: 9.9}) + p *int + a any + }{i: 9, u: 1, b: true, f: 9.9, p: new(int), a: 1}) type S struct { s string } @@ -326,17 +352,28 @@ func TestWriteComparable(t *testing.T) { t.Fatalf("unexpected two heapStr value not equal") } testWriteComparable(t, s1, s2) + testWriteComparable(t, s1.s, s2.s) testWriteComparable(t, float32(0), negativeZero[float32]()) 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) { seed := MakeSeed() h1 := Hash{} h2 := Hash{} h1.seed, h2.seed = seed, seed - WriteComparable(&h1, math.NaN()) - WriteComparable(&h2, math.NaN()) + WriteComparable(&h1, v1) + WriteComparable(&h2, v2) if h1.Sum64() == h2.Sum64() { - t.Fatalf("WriteComparable(seed, NaN) == WriteComparable(seed, NaN)") + t.Fatalf("WriteComparable(seed, %v) == WriteComparable(seed, %v)", v1, v2) } + } func testWriteComparable[T comparable](t *testing.T, v T, v2 ...T) {