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  }