Source File
deepequal.go
Belonging Package
reflect
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Deep equality test via reflection
package reflect
import (
)
// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
a1 unsafe.Pointer
a2 unsafe.Pointer
typ Type
}
// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func (, Value, map[visit]bool) bool {
if !.IsValid() || !.IsValid() {
return .IsValid() == .IsValid()
}
if .Type() != .Type() {
return false
}
// We want to avoid putting more in the visited map than we need to.
// For any possible reference cycle that might be encountered,
// 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.
:= func(, Value) bool {
switch .Kind() {
case Pointer:
if .typ().PtrBytes == 0 {
// not-in-heap pointers can't be cyclic.
// At least, all of our current uses of runtime/internal/sys.NotInHeap
// have that property. The runtime ones aren't cyclic (and we don't use
// DeepEqual on them anyway), and the cgo-generated ones are
// all empty structs.
return false
}
fallthrough
case Map, Slice, Interface:
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
return !.IsNil() && !.IsNil()
}
return false
}
if (, ) {
// For a Pointer or Map value, we need to check flagIndir,
// which we do by calling the pointer method.
// For Slice or Interface, flagIndir is always set,
// and using v.ptr suffices.
:= func( Value) unsafe.Pointer {
switch .Kind() {
case Pointer, Map:
return .pointer()
default:
return .ptr
}
}
:= ()
:= ()
if uintptr() > uintptr() {
// Canonicalize order to reduce number of entries in visited.
// Assumes non-moving garbage collector.
, = ,
}
// Short circuit if references are already seen.
:= .Type()
:= visit{, , }
if [] {
return true
}
// Remember for later.
[] = true
}
switch .Kind() {
case Array:
for := 0; < .Len(); ++ {
if !(.Index(), .Index(), ) {
return false
}
}
return true
case Slice:
if .IsNil() != .IsNil() {
return false
}
if .Len() != .Len() {
return false
}
if .UnsafePointer() == .UnsafePointer() {
return true
}
// Special case for []byte, which is common.
if .Type().Elem().Kind() == Uint8 {
return bytealg.Equal(.Bytes(), .Bytes())
}
for := 0; < .Len(); ++ {
if !(.Index(), .Index(), ) {
return false
}
}
return true
case Interface:
if .IsNil() || .IsNil() {
return .IsNil() == .IsNil()
}
return (.Elem(), .Elem(), )
case Pointer:
if .UnsafePointer() == .UnsafePointer() {
return true
}
return (.Elem(), .Elem(), )
case Struct:
for , := 0, .NumField(); < ; ++ {
if !(.Field(), .Field(), ) {
return false
}
}
return true
case Map:
if .IsNil() != .IsNil() {
return false
}
if .Len() != .Len() {
return false
}
if .UnsafePointer() == .UnsafePointer() {
return true
}
for , := range .MapKeys() {
:= .MapIndex()
:= .MapIndex()
if !.IsValid() || !.IsValid() || !(, , ) {
return false
}
}
return true
case Func:
if .IsNil() && .IsNil() {
return true
}
// Can't do better than this:
return false
case Int, Int8, Int16, Int32, Int64:
return .Int() == .Int()
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return .Uint() == .Uint()
case String:
return .String() == .String()
case Bool:
return .Bool() == .Bool()
case Float32, Float64:
return .Float() == .Float()
case Complex64, Complex128:
return .Complex() == .Complex()
default:
// Normal equality suffices
return valueInterface(, false) == valueInterface(, false)
}
}
// DeepEqual reports whether x and y are “deeply equal,” defined as follows.
// Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal.
//
// Array values are deeply equal when their corresponding elements are deeply equal.
//
// Struct values are deeply equal if their corresponding fields,
// both exported and unexported, are deeply equal.
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
//
// Interface values are deeply equal if they hold deeply equal concrete values.
//
// Map values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they are the same map object or their corresponding keys
// (matched using Go equality) map to deeply equal values.
//
// Pointer values are deeply equal if they are equal using Go's == operator
// or if they point to deeply equal values.
//
// Slice values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they point to the same initial entry of the same underlying array
// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.
// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil))
// are not deeply equal.
//
// Other values - numbers, bools, strings, and channels - are deeply equal
// if they are equal using Go's == operator.
//
// In general DeepEqual is a recursive relaxation of Go's == operator.
// However, this idea is impossible to implement without some inconsistency.
// Specifically, it is possible for a value to be unequal to itself,
// either because it is of func type (uncomparable in general)
// or because it is a floating-point NaN value (not equal to itself in floating-point comparison),
// or because it is an array, struct, or interface containing
// such a value.
// On the other hand, pointer values are always equal to themselves,
// even if they point at or contain such problematic values,
// because they compare equal using Go's == operator, and that
// is a sufficient condition to be deeply equal, regardless of content.
// DeepEqual has been defined so that the same short-cut applies
// to slices and maps: if x and y are the same slice or the same map,
// they are deeply equal regardless of content.
//
// As DeepEqual traverses the data values it may find a cycle. The
// second and subsequent times that DeepEqual compares two pointer
// values that have been compared before, it treats the values as
// equal rather than examining the values to which they point.
// This ensures that DeepEqual terminates.
func (, any) bool {
if == nil || == nil {
return ==
}
:= ValueOf()
:= ValueOf()
if .Type() != .Type() {
return false
}
return deepValueEqual(, , make(map[visit]bool))
}
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |