util/deephash: fix map hashing when key & element have the same type

Regression from 09afb8e35b, in which the
same reflect.Value scratch value was being used as the map iterator
copy destination.

Also: make nil and empty maps hash differently, add test.

Fixes #4871

Co-authored-by: Josh Bleecher Snyder <josharian@gmail.com>
Change-Id: I67f42524bc81f694c1b7259d6682200125ea4a66
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-06-16 13:21:32 -07:00
committed by Brad Fitzpatrick
parent 22c544bca7
commit 757ecf7e80
2 changed files with 52 additions and 10 deletions
+16 -10
View File
@@ -98,10 +98,14 @@ func (s Sum) String() string {
}
var (
once sync.Once
seed uint64
seedOnce sync.Once
seed uint64
)
func initSeed() {
seed = uint64(time.Now().UnixNano())
}
func (h *hasher) sum() (s Sum) {
h.bw.Flush()
// Sum into scratch & copy out, as hash.Hash is an interface
@@ -121,9 +125,7 @@ func Hash(v any) (s Sum) {
h := hasherPool.Get().(*hasher)
defer hasherPool.Put(h)
h.reset()
once.Do(func() {
seed = uint64(time.Now().UnixNano())
})
seedOnce.Do(initSeed)
h.hashUint64(seed)
h.hashValue(reflect.ValueOf(v), false)
return h.sum()
@@ -425,9 +427,9 @@ func (h *hasher) hashValueWithType(v reflect.Value, ti *typeInfo, forceCycleChec
}
type mapHasher struct {
h hasher
val valueCache // re-usable values for map iteration
iter reflect.MapIter // re-usable map iterator
h hasher
valKey, valElem valueCache // re-usable values for map iteration
iter reflect.MapIter // re-usable map iterator
}
var mapHasherPool = &sync.Pool{
@@ -461,8 +463,12 @@ func (h *hasher) hashMap(v reflect.Value, ti *typeInfo, checkCycles bool) {
defer iter.Reset(reflect.Value{}) // avoid pinning v from mh.iter when we return
var sum Sum
k := mh.val.get(v.Type().Key())
e := mh.val.get(v.Type().Elem())
if v.IsNil() {
sum.sum[0] = 1 // something non-zero
}
k := mh.valKey.get(v.Type().Key())
e := mh.valElem.get(v.Type().Elem())
mh.h.visitStack = h.visitStack // always use the parent's visit stack to avoid cycles
for iter.Next() {
k.SetIterKey(iter)