mirror of https://github.com/golang/go.git
parent
6a2db9524f
commit
c93aab36ac
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue