From c93aab36ac8173c6b6e779b2c8832651fc15b481 Mon Sep 17 00:00:00 2001 From: qiulaidongfeng <2645477756@qq.com> Date: Sun, 8 Sep 2024 09:59:47 +0800 Subject: [PATCH] new Change-Id: Id9595ce22239a2e823f3505eeb5027b62c3d53e6 --- src/hash/maphash/maphash.go | 38 ++++++++++++++++++----------- src/hash/maphash/maphash_purego.go | 3 +-- src/hash/maphash/maphash_runtime.go | 3 ++- src/hash/maphash/maphash_test.go | 26 +++++++++++++++++++- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/hash/maphash/maphash.go b/src/hash/maphash/maphash.go index 699cc57501..2ea39dacd9 100644 --- a/src/hash/maphash/maphash.go +++ b/src/hash/maphash/maphash.go @@ -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 or comparable value. +// Package maphash provides hash functions on byte sequences and comparable values. // 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. @@ -18,7 +18,6 @@ import ( "internal/byteorder" "math" "reflect" - "unsafe" ) // A Seed is a random value that selects the specific hash function @@ -289,17 +288,17 @@ 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 { - t, ret := comparableReady(seed, v) + ret := comparableReady(seed, v) if ret != 0 { return ret } var h Hash h.SetSeed(seed) - comparableF(&h, v, t) + comparableF(&h, v) return h.Sum64() } -func comparableReady[T comparable](seed Seed, v T) (*abi.Type, uint64) { +func comparableReady[T comparable](seed Seed, v T) 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, @@ -309,25 +308,21 @@ func comparableReady[T comparable](seed Seed, v T) (*abi.Type, uint64) { t := abi.TypeFor[T]() len := t.Size() if len == 0 { - return t, seed.s + return seed.s } - return t, 0 + return 0 } // WriteComparable adds x to the data hashed by h. func WriteComparable[T comparable](h *Hash, x T) { - t, ret := comparableReady(h.seed, x) + ret := comparableReady(h.seed, x) if ret != 0 { return } - comparableF(h, x, t) + comparableF(h, x) } 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 @@ -343,6 +338,8 @@ func appendT(h *Hash, v reflect.Value) { var buf [8]byte for i := range uint64(v.Len()) { byteorder.LePutUint64(buf[:], i) + // do not want to hash to the same value, + // [2]string{"foo", ""} and [2]string{"", "foo"}. h.Write(buf[:]) appendT(h, v.Index(int(i))) } @@ -353,6 +350,9 @@ 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) } @@ -366,8 +366,18 @@ func appendT(h *Hash, v reflect.Value) { h.Write(buf[:]) return case reflect.Float32, reflect.Float64: + f := v.Float() + if f == 0 { + h.WriteByte(0) + return + } var buf [8]byte - byteorder.LePutUint64(buf[:], math.Float64bits(v.Float())) + if f != f { + byteorder.LePutUint64(buf[:], randUint64()) + h.Write(buf[:]) + return + } + byteorder.LePutUint64(buf[:], math.Float64bits(f)) h.Write(buf[:]) return case reflect.Bool: diff --git a/src/hash/maphash/maphash_purego.go b/src/hash/maphash/maphash_purego.go index c1899e47e7..9f348ed6ae 100644 --- a/src/hash/maphash/maphash_purego.go +++ b/src/hash/maphash/maphash_purego.go @@ -8,7 +8,6 @@ package maphash import ( "crypto/rand" - "internal/abi" "internal/byteorder" "math/bits" "reflect" @@ -97,7 +96,7 @@ func mix(a, b uint64) uint64 { var strTyp = reflect.TypeFor[string]() -func comparableF[T comparable](h *Hash, v T, t *abi.Type) { +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 f055cf698c..be5f1c3697 100644 --- a/src/hash/maphash/maphash_runtime.go +++ b/src/hash/maphash/maphash_runtime.go @@ -44,7 +44,8 @@ func randUint64() uint64 { return runtime_rand() } -func comparableF[T comparable](h *Hash, v T, t *abi.Type) { +func comparableF[T comparable](h *Hash, v T) { + t := abi.TypeFor[T]() ptr := unsafe.Pointer(&v) l := t.Size() k := t.Kind() diff --git a/src/hash/maphash/maphash_test.go b/src/hash/maphash/maphash_test.go index 29afe23ed4..9e1cdca1e0 100644 --- a/src/hash/maphash/maphash_test.go +++ b/src/hash/maphash/maphash_test.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "fmt" "hash" + "math" "reflect" "strings" "testing" @@ -214,6 +215,12 @@ func TestSeedFromReset(t *testing.T) { } } +func negativeZero[T float32 | float64]() T { + var f T + f = -f + return f +} + func TestComparable(t *testing.T) { testComparable(t, int64(2)) testComparable(t, uint64(8)) @@ -239,6 +246,12 @@ func TestComparable(t *testing.T) { t.Fatalf("unexpected two heapStr value not equal") } testComparable(t, s1, s2) + testComparable(t, float32(0), negativeZero[float32]()) + testComparable(t, float64(0), negativeZero[float64]()) + seed := MakeSeed() + if Comparable(seed, math.NaN()) == Comparable(seed, math.NaN()) { + t.Fatalf("Comparable(seed, NaN) == Comparable(seed, NaN)") + } } var heapStrValue []byte @@ -313,6 +326,17 @@ func TestWriteComparable(t *testing.T) { t.Fatalf("unexpected two heapStr value not equal") } testWriteComparable(t, s1, s2) + testWriteComparable(t, float32(0), negativeZero[float32]()) + testWriteComparable(t, float64(0), negativeZero[float64]()) + seed := MakeSeed() + h1 := Hash{} + h2 := Hash{} + h1.seed, h2.seed = seed, seed + WriteComparable(&h1, math.NaN()) + WriteComparable(&h2, math.NaN()) + if h1.Sum64() == h2.Sum64() { + t.Fatalf("WriteComparable(seed, NaN) == WriteComparable(seed, NaN)") + } } func testWriteComparable[T comparable](t *testing.T, v T, v2 ...T) { @@ -328,7 +352,7 @@ func testWriteComparable[T comparable](t *testing.T, v T, v2 ...T) { h2.seed = h1.seed WriteComparable(&h1, a) WriteComparable(&h2, b) - if h1.Sum64() != h1.Sum64() { + if h1.Sum64() != h2.Sum64() { t.Fatalf("WriteComparable(h, %v) != WriteComparable(h, %v)", a, b) } WriteComparable(&h1, pa)