|
|
|
@ -169,6 +169,28 @@ func (h *hasher) print(v reflect.Value) (acyclic bool) {
|
|
|
|
|
case reflect.Interface:
|
|
|
|
|
return h.print(v.Elem())
|
|
|
|
|
case reflect.Map:
|
|
|
|
|
// TODO(bradfitz): ideally we'd avoid these map
|
|
|
|
|
// operations to detect cycles if we knew from the map
|
|
|
|
|
// element type that there no way to form a cycle,
|
|
|
|
|
// which is the common case. Notably, we don't care
|
|
|
|
|
// about hashing the same map+contents twice in
|
|
|
|
|
// different parts of the tree. In fact, we should
|
|
|
|
|
// ideally. (And this prevents it) We should only stop
|
|
|
|
|
// hashing when there's a cycle. What we should
|
|
|
|
|
// probably do is make sure we enumerate the data
|
|
|
|
|
// structure tree is a fixed order and then give each
|
|
|
|
|
// pointer an increasing number, and when we hit a
|
|
|
|
|
// dup, rather than emitting nothing, we should emit a
|
|
|
|
|
// "value #12" reference. Which implies that all things
|
|
|
|
|
// emit to the bufio.Writer should be type-tagged so
|
|
|
|
|
// we can distinguish loop references without risk of
|
|
|
|
|
// collisions.
|
|
|
|
|
ptr := v.Pointer()
|
|
|
|
|
if visited[ptr] {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
visited[ptr] = true
|
|
|
|
|
|
|
|
|
|
if h.hashMapAcyclic(v) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|