equal.go 3.46 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
package ipld

// DeepEqual reports whether x and y are "deeply equal" as IPLD nodes.
// This is similar to reflect.DeepEqual, but based around the Node interface.
//
// Two nodes must have the same kind to be deeply equal.
// If either node has the invalid kind, the nodes are not deeply equal.
//
// Two nodes of scalar kinds (null, bool, int, float, string, bytes, link)
// are deeply equal if their Go values, as returned by AsKind methods, are equal as
// per Go's == comparison operator.
//
// Note that Links are compared in a shallow way, without being followed.
// This will generally be enough, as it's rare to have two different links to the
// same IPLD data by using a different codec or multihash type.
//
// Two nodes of recursive kinds (map, list)
// must have the same length to be deeply equal.
// Their elements, as reported by iterators, must be deeply equal.
// The elements are compared in the iterator's order,
// meaning two maps sorting the same keys differently might not be equal.
//
// Note that this function panics if either Node returns an error.
// We only call valid methods for each Kind,
// so an error should only happen if a Node implementation breaks that contract.
// It is generally not recommended to call DeepEqual on ADL nodes.
func DeepEqual(x, y Node) bool {
	xk, yk := x.Kind(), y.Kind()
	if xk != yk {
		return false
	}

	switch xk {

	// Scalar kinds.
	case Kind_Null:
		return x.IsNull() == y.IsNull()
	case Kind_Bool:
		xv, err := x.AsBool()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsBool()
		if err != nil {
			panic(err)
		}
		return xv == yv
	case Kind_Int:
		xv, err := x.AsInt()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsInt()
		if err != nil {
			panic(err)
		}
		return xv == yv
	case Kind_Float:
		xv, err := x.AsFloat()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsFloat()
		if err != nil {
			panic(err)
		}
		return xv == yv
	case Kind_String:
		xv, err := x.AsString()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsString()
		if err != nil {
			panic(err)
		}
		return xv == yv
	case Kind_Bytes:
		xv, err := x.AsBytes()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsBytes()
		if err != nil {
			panic(err)
		}
		return string(xv) == string(yv)
	case Kind_Link:
		xv, err := x.AsLink()
		if err != nil {
			panic(err)
		}
		yv, err := y.AsLink()
		if err != nil {
			panic(err)
		}
		// Links are just compared via ==.
		// This requires the types to exactly match,
		// and the values to be equal as per == too.
		// This will generally work,
		// as ipld-prime assumes link types to be consistent.
		return xv == yv

	// Recursive kinds.
	case Kind_Map:
		if x.Length() != y.Length() {
			return false
		}
		xitr := x.MapIterator()
		yitr := y.MapIterator()
		for !xitr.Done() && !yitr.Done() {
			xkey, xval, err := xitr.Next()
			if err != nil {
				panic(err)
			}
			ykey, yval, err := yitr.Next()
			if err != nil {
				panic(err)
			}
			if !DeepEqual(xkey, ykey) {
				return false
			}
			if !DeepEqual(xval, yval) {
				return false
			}
		}
		return true
	case Kind_List:
		if x.Length() != y.Length() {
			return false
		}
		xitr := x.ListIterator()
		yitr := y.ListIterator()
		for !xitr.Done() && !yitr.Done() {
			_, xval, err := xitr.Next()
			if err != nil {
				panic(err)
			}
			_, yval, err := yitr.Next()
			if err != nil {
				panic(err)
			}
			if !DeepEqual(xval, yval) {
				return false
			}
		}
		return true

	// As per the docs, other kinds such as Invalid are not deeply equal.
	default:
		return false
	}
}