github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/testutil/deepequal.go (about) 1 package testutil 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 type visit struct { 9 a1, a2 unsafe.Pointer 10 typ reflect.Type 11 } 12 13 // DeepEqual is similar to reflect.DeepEqual, but treats nil as equal 14 // to empty maps and slices. Some of the implementation is cribbed 15 // from Go's reflect package. 16 func DeepEqual(x, y interface{}) bool { 17 vx := reflect.ValueOf(x) 18 vy := reflect.ValueOf(y) 19 return deepValueEqual(vx, vy, make(map[visit]bool)) 20 } 21 22 func deepValueEqual(x, y reflect.Value, visited map[visit]bool) bool { 23 if isEmpty(x) && isEmpty(y) { 24 return true 25 } 26 if !x.IsValid() { 27 return !y.IsValid() 28 } 29 if !y.IsValid() { 30 return false 31 } 32 33 tx := x.Type() 34 ty := y.Type() 35 if tx != ty { 36 return false 37 } 38 39 switch tx.Kind() { 40 case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: 41 if x.CanAddr() && y.CanAddr() { 42 a1 := unsafe.Pointer(x.UnsafeAddr()) 43 a2 := unsafe.Pointer(y.UnsafeAddr()) 44 if uintptr(a1) > uintptr(a2) { 45 // Canonicalize order to reduce number of entries in visited. 46 // Assumes non-moving garbage collector. 47 a1, a2 = a2, a1 48 } 49 v := visit{a1, a2, tx} 50 if visited[v] { 51 return true 52 } 53 visited[v] = true 54 } 55 } 56 57 switch tx.Kind() { 58 case reflect.Bool: 59 return x.Bool() == y.Bool() 60 61 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 62 return x.Int() == y.Int() 63 64 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 65 return x.Uint() == y.Uint() 66 67 case reflect.Float32, reflect.Float64: 68 return x.Float() == y.Float() 69 70 case reflect.Complex64, reflect.Complex128: 71 return x.Complex() == y.Complex() 72 73 case reflect.String: 74 return x.String() == y.String() 75 76 case reflect.Array: 77 for i := 0; i < tx.Len(); i++ { 78 if !deepValueEqual(x.Index(i), y.Index(i), visited) { 79 return false 80 } 81 } 82 return true 83 84 case reflect.Slice: 85 ttx := tx.Elem() 86 tty := ty.Elem() 87 if ttx != tty { 88 return false 89 } 90 if x.Len() != y.Len() { 91 return false 92 } 93 for i := 0; i < x.Len(); i++ { 94 if !deepValueEqual(x.Index(i), y.Index(i), visited) { 95 return false 96 } 97 } 98 return true 99 100 case reflect.Interface: 101 if x.IsNil() { 102 return y.IsNil() 103 } 104 if y.IsNil() { 105 return false 106 } 107 return deepValueEqual(x.Elem(), y.Elem(), visited) 108 109 case reflect.Ptr: 110 if x.Pointer() == y.Pointer() { 111 return true 112 } 113 return deepValueEqual(x.Elem(), y.Elem(), visited) 114 115 case reflect.Struct: 116 for i := 0; i < tx.NumField(); i++ { 117 if !deepValueEqual(x.Field(i), y.Field(i), visited) { 118 return false 119 } 120 } 121 return true 122 123 case reflect.Map: 124 if x.Pointer() == y.Pointer() { 125 return true 126 } 127 if x.Len() != y.Len() { 128 return false 129 } 130 for _, k := range x.MapKeys() { 131 if !deepValueEqual(x.MapIndex(k), y.MapIndex(k), visited) { 132 return false 133 } 134 } 135 return true 136 137 case reflect.Func: 138 return x.IsNil() && y.IsNil() 139 } 140 return false 141 } 142 143 func isEmpty(v reflect.Value) bool { 144 if !v.IsValid() { 145 return true 146 } 147 switch v.Type().Kind() { 148 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr: 149 return v.IsNil() 150 151 case reflect.Slice, reflect.Map: 152 return v.IsNil() || v.Len() == 0 153 } 154 return false 155 }