util/deephash: handle slice edge-cases (#5471)
It is unclear whether the lack of checking nil-ness of slices was an oversight or a deliberate feature. Lacking a comment, the assumption is that this was an oversight. Also, expand the logic to perform cycle detection for recursive slices. We do this on a per-element basis since a slice is semantically equivalent to a list of pointers. Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
@@ -294,6 +294,11 @@ func makeSliceHasher(t reflect.Type) typeHasherFunc {
|
||||
if typeIsMemHashable(t.Elem()) {
|
||||
return func(h *hasher, p pointer) {
|
||||
pa := p.sliceArray()
|
||||
if pa.isNil() {
|
||||
h.HashUint8(0) // indicates nil
|
||||
return
|
||||
}
|
||||
h.HashUint8(1) // indicates visiting slice
|
||||
n := p.sliceLen()
|
||||
b := pa.asMemory(uintptr(n) * nb)
|
||||
h.HashUint64(uint64(n))
|
||||
@@ -305,11 +310,30 @@ func makeSliceHasher(t reflect.Type) typeHasherFunc {
|
||||
var hashElem typeHasherFunc
|
||||
init := func() {
|
||||
hashElem = lookupTypeHasher(t.Elem())
|
||||
if typeIsRecursive(t) {
|
||||
hashElemDefault := hashElem
|
||||
hashElem = func(h *hasher, p pointer) {
|
||||
if idx, ok := h.visitStack.seen(p.p); ok {
|
||||
h.HashUint8(2) // indicates cycle
|
||||
h.HashUint64(uint64(idx))
|
||||
return
|
||||
}
|
||||
h.HashUint8(1) // indicates visiting slice element
|
||||
h.visitStack.push(p.p)
|
||||
defer h.visitStack.pop(p.p)
|
||||
hashElemDefault(h, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return func(h *hasher, p pointer) {
|
||||
pa := p.sliceArray()
|
||||
if pa.isNil() {
|
||||
h.HashUint8(0) // indicates nil
|
||||
return
|
||||
}
|
||||
once.Do(init)
|
||||
h.HashUint8(1) // indicates visiting slice
|
||||
n := p.sliceLen()
|
||||
h.HashUint64(uint64(n))
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
Reference in New Issue
Block a user