From 70f9fc8c7a0b23ab2732e917601cc12ac39b1493 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Fri, 26 Aug 2022 20:50:56 -0700 Subject: [PATCH] util/deephash: rely on direct memory hashing for primitive kinds (#5457) Rather than separate functions to hash each kind, just rely on the fact that these are direct memory hashable, thus simplifying the code. Signed-off-by: Joe Tsai --- util/deephash/deephash.go | 116 ++++++--------------------------- util/deephash/deephash_test.go | 12 +++- 2 files changed, 31 insertions(+), 97 deletions(-) diff --git a/util/deephash/deephash.go b/util/deephash/deephash.go index f194c87bd..228473028 100644 --- a/util/deephash/deephash.go +++ b/util/deephash/deephash.go @@ -24,7 +24,6 @@ "crypto/sha256" "encoding/binary" "encoding/hex" - "math" "net/netip" "reflect" "sync" @@ -238,46 +237,6 @@ func (ti *typeInfo) buildHashFuncOnce() { ti.hashFuncLazy = genTypeHasher(ti) } -func (h *hasher) hashBoolv(v addressableValue) { - var b byte - if v.Bool() { - b = 1 - } - h.HashUint8(b) -} - -func (h *hasher) hashUint8v(v addressableValue) { - h.HashUint8(uint8(v.Uint())) -} - -func (h *hasher) hashInt8v(v addressableValue) { - h.HashUint8(uint8(v.Int())) -} - -func (h *hasher) hashUint16v(v addressableValue) { - h.HashUint16(uint16(v.Uint())) -} - -func (h *hasher) hashInt16v(v addressableValue) { - h.HashUint16(uint16(v.Int())) -} - -func (h *hasher) hashUint32v(v addressableValue) { - h.HashUint32(uint32(v.Uint())) -} - -func (h *hasher) hashInt32v(v addressableValue) { - h.HashUint32(uint32(v.Int())) -} - -func (h *hasher) hashUint64v(v addressableValue) { - h.HashUint64(v.Uint()) -} - -func (h *hasher) hashInt64v(v addressableValue) { - h.HashUint64(uint64(v.Int())) -} - // fieldInfo describes a struct field. type fieldInfo struct { index int // index of field for reflect.Value.Field(n); -1 if invalid @@ -358,33 +317,21 @@ func genHashPtrToMemoryRange(eleType reflect.Type) typeHasherFunc { func genTypeHasher(ti *typeInfo) typeHasherFunc { t := ti.rtype + + // Types with specific hashing. + switch t { + case timeTimeType: + return (*hasher).hashTimev + case netipAddrType: + return (*hasher).hashAddrv + } + + // Types that can have their memory representation directly hashed. + if typeIsMemHashable(t) { + return makeMemHasher(t.Size()) + } + switch t.Kind() { - case reflect.Bool: - return (*hasher).hashBoolv - case reflect.Int8: - return (*hasher).hashInt8v - case reflect.Int16: - return (*hasher).hashInt16v - case reflect.Int32: - return (*hasher).hashInt32v - case reflect.Int, reflect.Int64: - return (*hasher).hashInt64v - case reflect.Uint8: - return (*hasher).hashUint8v - case reflect.Uint16: - return (*hasher).hashUint16v - case reflect.Uint32: - return (*hasher).hashUint32v - case reflect.Uint, reflect.Uintptr, reflect.Uint64: - return (*hasher).hashUint64v - case reflect.Float32: - return (*hasher).hashFloat32v - case reflect.Float64: - return (*hasher).hashFloat64v - case reflect.Complex64: - return (*hasher).hashComplex64v - case reflect.Complex128: - return (*hasher).hashComplex128v case reflect.String: return (*hasher).hashString case reflect.Slice: @@ -399,14 +346,7 @@ func genTypeHasher(ti *typeInfo) typeHasherFunc { eti := getTypeInfo(et) return genHashArray(t, eti) case reflect.Struct: - switch t { - case timeTimeType: - return (*hasher).hashTimev - case netipAddrType: - return (*hasher).hashAddrv - default: - return genHashStructFields(t) - } + return genHashStructFields(t) case reflect.Map: return func(h *hasher, v addressableValue) { if v.IsNil() { @@ -476,26 +416,6 @@ func (h *hasher) hashString(v addressableValue) { h.HashString(s) } -func (h *hasher) hashFloat32v(v addressableValue) { - h.HashUint32(math.Float32bits(float32(v.Float()))) -} - -func (h *hasher) hashFloat64v(v addressableValue) { - h.HashUint64(math.Float64bits(v.Float())) -} - -func (h *hasher) hashComplex64v(v addressableValue) { - c := complex64(v.Complex()) - h.HashUint32(math.Float32bits(real(c))) - h.HashUint32(math.Float32bits(imag(c))) -} - -func (h *hasher) hashComplex128v(v addressableValue) { - c := v.Complex() - h.HashUint64(math.Float64bits(real(c))) - h.HashUint64(math.Float64bits(imag(c))) -} - // hashTimev hashes v, of kind time.Time. func (h *hasher) hashTimev(v addressableValue) { // Include the zone offset (but not the name) to keep @@ -531,6 +451,12 @@ func (h *hasher) hashAddrv(v addressableValue) { } } +func makeMemHasher(n uintptr) typeHasherFunc { + return func(h *hasher, v addressableValue) { + h.HashBytes(unsafe.Slice((*byte)(v.Addr().UnsafePointer()), n)) + } +} + // hashSliceMem hashes v, of kind Slice, with a memhash-able element type. func (h *hasher) hashSliceMem(v addressableValue) { vLen := v.Len() diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index 35abf1a97..a2b2e0492 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -11,6 +11,7 @@ "fmt" "hash" "math" + "math/bits" "math/rand" "net/netip" "reflect" @@ -342,6 +343,13 @@ func u8(n uint8) string { return string([]byte{n}) } func u16(n uint16) string { return string(binary.LittleEndian.AppendUint16(nil, n)) } func u32(n uint32) string { return string(binary.LittleEndian.AppendUint32(nil, n)) } func u64(n uint64) string { return string(binary.LittleEndian.AppendUint64(nil, n)) } +func ux(n uint) string { + if bits.UintSize == 32 { + return u32(uint32(n)) + } else { + return u64(uint64(n)) + } +} func TestGetTypeHasher(t *testing.T) { switch runtime.GOARCH { @@ -367,12 +375,12 @@ func TestGetTypeHasher(t *testing.T) { { name: "int", val: int(1), - out: "\x01\x00\x00\x00\x00\x00\x00\x00", + out: ux(1), }, { name: "int_negative", val: int(-1), - out: "\xff\xff\xff\xff\xff\xff\xff\xff", + out: ux(math.MaxUint), }, { name: "int8",