var loop1, loop2 Loop
var loopy1, loopy2 Loopy
+var cycleMap1, cycleMap2, cycleMap3 map[string]interface{}
func init() {
loop1 = &loop2
loopy1 = &loopy2
loopy2 = &loopy1
+
+ cycleMap1 = map[string]interface{}{}
+ cycleMap1["cycle"] = cycleMap1
+ cycleMap2 = map[string]interface{}{}
+ cycleMap2["cycle"] = cycleMap2
+ cycleMap3 = map[string]interface{}{}
+ cycleMap3["different"] = cycleMap3
}
var deepEqualTests = []DeepEqualTest{
{&loop1, &loop2, true},
{&loopy1, &loopy1, true},
{&loopy1, &loopy2, true},
+ {&cycleMap1, &cycleMap2, true},
+ {&cycleMap1, &cycleMap3, false},
}
func TestDeepEqual(t *testing.T) {
test.b = test.a
}
if r := DeepEqual(test.a, test.b); r != test.eq {
- t.Errorf("DeepEqual(%v, %v) = %v, want %v", test.a, test.b, r, test.eq)
+ t.Errorf("DeepEqual(%#v, %#v) = %v, want %v", test.a, test.b, r, test.eq)
}
}
}
// We want to avoid putting more in the visited map than we need to.
// For any possible reference cycle that might be encountered,
- // hard(t) needs to return true for at least one of the types in the cycle.
- hard := func(k Kind) bool {
- switch k {
+ // hard(v1, v2) needs to return true for at least one of the types in the cycle,
+ // and it's safe and valid to get Value's internal pointer.
+ hard := func(v1, v2 Value) bool {
+ switch v1.Kind() {
case Map, Slice, Ptr, Interface:
- return true
+ // Nil pointers cannot be cyclic. Avoid putting them in the visited map.
+ return !v1.IsNil() && !v2.IsNil()
}
return false
}
- if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
- addr1 := unsafe.Pointer(v1.UnsafeAddr())
- addr2 := unsafe.Pointer(v2.UnsafeAddr())
+ if hard(v1, v2) {
+ addr1 := v1.ptr
+ addr2 := v2.ptr
if uintptr(addr1) > uintptr(addr2) {
// Canonicalize order to reduce number of entries in visited.
// Assumes non-moving garbage collector.