util/deephash: rely on direct memory hashing for primitive kinds ()

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 <joetsai@digital-static.net>
This commit is contained in:
Joe Tsai 2022-08-26 20:50:56 -07:00 committed by GitHub
parent f1c9812188
commit 70f9fc8c7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 97 deletions

@ -24,7 +24,6 @@ import (
"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()

@ -11,6 +11,7 @@ import (
"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",