+++ /dev/null
-package testutil
-
-import (
- "reflect"
- "unsafe"
-)
-
-type visit struct {
- a1, a2 unsafe.Pointer
- typ reflect.Type
-}
-
-// DeepEqual is similar to reflect.DeepEqual, but treats nil as equal
-// to empty maps and slices. Some of the implementation is cribbed
-// from Go's reflect package.
-func DeepEqual(x, y interface{}) bool {
- vx := reflect.ValueOf(x)
- vy := reflect.ValueOf(y)
- return deepValueEqual(vx, vy, make(map[visit]bool))
-}
-
-func deepValueEqual(x, y reflect.Value, visited map[visit]bool) bool {
- if isEmpty(x) && isEmpty(y) {
- return true
- }
- if !x.IsValid() {
- return !y.IsValid()
- }
- if !y.IsValid() {
- return false
- }
-
- tx := x.Type()
- ty := y.Type()
- if tx != ty {
- return false
- }
-
- switch tx.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
- if x.CanAddr() && y.CanAddr() {
- a1 := unsafe.Pointer(x.UnsafeAddr())
- a2 := unsafe.Pointer(y.UnsafeAddr())
- if uintptr(a1) > uintptr(a2) {
- // Canonicalize order to reduce number of entries in visited.
- // Assumes non-moving garbage collector.
- a1, a2 = a2, a1
- }
- v := visit{a1, a2, tx}
- if visited[v] {
- return true
- }
- visited[v] = true
- }
- }
-
- switch tx.Kind() {
- case reflect.Bool:
- return x.Bool() == y.Bool()
-
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return x.Int() == y.Int()
-
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return x.Uint() == y.Uint()
-
- case reflect.Float32, reflect.Float64:
- return x.Float() == y.Float()
-
- case reflect.Complex64, reflect.Complex128:
- return x.Complex() == y.Complex()
-
- case reflect.String:
- return x.String() == y.String()
-
- case reflect.Array:
- for i := 0; i < tx.Len(); i++ {
- if !deepValueEqual(x.Index(i), y.Index(i), visited) {
- return false
- }
- }
- return true
-
- case reflect.Slice:
- ttx := tx.Elem()
- tty := ty.Elem()
- if ttx != tty {
- return false
- }
- if x.Len() != y.Len() {
- return false
- }
- for i := 0; i < x.Len(); i++ {
- if !deepValueEqual(x.Index(i), y.Index(i), visited) {
- return false
- }
- }
- return true
-
- case reflect.Interface:
- if x.IsNil() {
- return y.IsNil()
- }
- if y.IsNil() {
- return false
- }
- return deepValueEqual(x.Elem(), y.Elem(), visited)
-
- case reflect.Ptr:
- if x.Pointer() == y.Pointer() {
- return true
- }
- return deepValueEqual(x.Elem(), y.Elem(), visited)
-
- case reflect.Struct:
- for i := 0; i < tx.NumField(); i++ {
- if !deepValueEqual(x.Field(i), y.Field(i), visited) {
- return false
- }
- }
- return true
-
- case reflect.Map:
- if x.Pointer() == y.Pointer() {
- return true
- }
- if x.Len() != y.Len() {
- return false
- }
- for _, k := range x.MapKeys() {
- if !deepValueEqual(x.MapIndex(k), y.MapIndex(k), visited) {
- return false
- }
- }
- return true
-
- case reflect.Func:
- return x.IsNil() && y.IsNil()
- }
- return false
-}
-
-func isEmpty(v reflect.Value) bool {
- if !v.IsValid() {
- return true
- }
- switch v.Type().Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr:
- return v.IsNil()
-
- case reflect.Slice, reflect.Map:
- return v.IsNil() || v.Len() == 0
- }
- return false
-}