Skip to content

Cherrypick #126 to 1.0 - Implement equality for Values #128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions value/less_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ func TestValueLess(t *testing.T) {
t.Run(table[i].name, func(t *testing.T) {
tt := table[i]
if tt.eq {
if !tt.a.Equals(tt.b) {
t.Errorf("oops, a != b: %#v, %#v", tt.a, tt.b)
}
if tt.a.Less(tt.b) {
t.Errorf("oops, a < b: %#v, %#v", tt.a, tt.b)
}
Expand All @@ -307,6 +310,19 @@ func TestValueLess(t *testing.T) {
if tt.b.Less(tt.b) {
t.Errorf("oops, b < a: %#v, %#v", tt.b, tt.a)
}

if tt.eq {
if tt.a.Compare(tt.b) != 0 || tt.b.Compare(tt.b) != 0 {
t.Errorf("oops, a != b: %#v, %#v", tt.a, tt.b)
}
} else {
if !(tt.a.Compare(tt.b) < 0) {
t.Errorf("oops, a is not less than b: %#v, %#v", tt.a, tt.b)
}
if !(tt.b.Compare(tt.a) > 0) {
t.Errorf("oops, b is not more than a: %#v, %#v", tt.a, tt.b)
}
}
})
}

Expand Down
226 changes: 175 additions & 51 deletions value/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,93 +36,186 @@ type Value struct {

// Equals returns true iff the two values are equal.
func (v Value) Equals(rhs Value) bool {
return !v.Less(rhs) && !rhs.Less(v)
if v.FloatValue != nil || rhs.FloatValue != nil {
var lf float64
if v.FloatValue != nil {
lf = float64(*v.FloatValue)
} else if v.IntValue != nil {
lf = float64(*v.IntValue)
} else {
return false
}
var rf float64
if rhs.FloatValue != nil {
rf = float64(*rhs.FloatValue)
} else if rhs.IntValue != nil {
rf = float64(*rhs.IntValue)
} else {
return false
}
return lf == rf
}
if v.IntValue != nil {
if rhs.IntValue != nil {
return *v.IntValue == *rhs.IntValue
}
return false
}
if v.StringValue != nil {
if rhs.StringValue != nil {
return *v.StringValue == *rhs.StringValue
}
return false
}
if v.BooleanValue != nil {
if rhs.BooleanValue != nil {
return *v.BooleanValue == *rhs.BooleanValue
}
return false
}
if v.ListValue != nil {
if rhs.ListValue != nil {
return v.ListValue.Equals(rhs.ListValue)
}
return false
}
if v.MapValue != nil {
if rhs.MapValue != nil {
return v.MapValue.Equals(rhs.MapValue)
}
return false
}
if v.Null {
if rhs.Null {
return true
}
return false
}
// No field is set, on either objects.
return true
}

// Less provides a total ordering for Value (so that they can be sorted, even
// if they are of different types).
func (v Value) Less(rhs Value) bool {
return v.Compare(rhs) == -1
}

// Compare provides a total ordering for Value (so that they can be
// sorted, even if they are of different types). The result will be 0 if
// v==rhs, -1 if v < rhs, and +1 if v > rhs.
func (v Value) Compare(rhs Value) int {
if v.FloatValue != nil {
if rhs.FloatValue == nil {
// Extra: compare floats and ints numerically.
if rhs.IntValue != nil {
return float64(*v.FloatValue) < float64(*rhs.IntValue)
return v.FloatValue.Compare(Float(*rhs.IntValue))
}
return true
return -1
}
return *v.FloatValue < *rhs.FloatValue
return v.FloatValue.Compare(*rhs.FloatValue)
} else if rhs.FloatValue != nil {
// Extra: compare floats and ints numerically.
if v.IntValue != nil {
return float64(*v.IntValue) < float64(*rhs.FloatValue)
return Float(*v.IntValue).Compare(*rhs.FloatValue)
}
return false
return 1
}

if v.IntValue != nil {
if rhs.IntValue == nil {
return true
return -1
}
return *v.IntValue < *rhs.IntValue
return v.IntValue.Compare(*rhs.IntValue)
} else if rhs.IntValue != nil {
return false
return 1
}

if v.StringValue != nil {
if rhs.StringValue == nil {
return true
return -1
}
return *v.StringValue < *rhs.StringValue
return strings.Compare(string(*v.StringValue), string(*rhs.StringValue))
} else if rhs.StringValue != nil {
return false
return 1
}

if v.BooleanValue != nil {
if rhs.BooleanValue == nil {
return true
return -1
}
if *v.BooleanValue == *rhs.BooleanValue {
return false
}
return *v.BooleanValue == false
return v.BooleanValue.Compare(*rhs.BooleanValue)
} else if rhs.BooleanValue != nil {
return false
return 1
}

if v.ListValue != nil {
if rhs.ListValue == nil {
return true
return -1
}
return v.ListValue.Less(rhs.ListValue)
return v.ListValue.Compare(rhs.ListValue)
} else if rhs.ListValue != nil {
return false
return 1
}
if v.MapValue != nil {
if rhs.MapValue == nil {
return true
return -1
}
return v.MapValue.Less(rhs.MapValue)
return v.MapValue.Compare(rhs.MapValue)
} else if rhs.MapValue != nil {
return false
return 1
}
if v.Null {
if !rhs.Null {
return true
return -1
}
return false
return 0
} else if rhs.Null {
return false
return 1
}

// Invalid Value-- nothing is set.
return false
return 0
}

type Int int64
type Float float64
type String string
type Boolean bool

// Compare compares integers. The result will be 0 if i==rhs, -1 if i <
// rhs, and +1 if i > rhs.
func (i Int) Compare(rhs Int) int {
if i > rhs {
return 1
} else if i < rhs {
return -1
}
return 0
}

// Compare compares floats. The result will be 0 if f==rhs, -1 if f <
// rhs, and +1 if f > rhs.
func (f Float) Compare(rhs Float) int {
if f > rhs {
return 1
} else if f < rhs {
return -1
}
return 0
}

// Compare compares booleans. The result will be 0 if b==rhs, -1 if b <
// rhs, and +1 if b > rhs.
func (b Boolean) Compare(rhs Boolean) int {
if b == rhs {
return 0
} else if b == false {
return -1
}
return 1
}

// Field is an individual key-value pair.
type Field struct {
Name string
Expand All @@ -134,29 +227,44 @@ type List struct {
Items []Value
}

// Equals compares two lists lexically.
func (l *List) Equals(rhs *List) bool {
if len(l.Items) != len(rhs.Items) {
return false
}

for i, lv := range l.Items {
if !lv.Equals(rhs.Items[i]) {
return false
}
}
return true
}

// Less compares two lists lexically.
func (l *List) Less(rhs *List) bool {
return l.Compare(rhs) == -1
}

// Compare compares two lists lexically. The result will be 0 if l==rhs, -1
// if l < rhs, and +1 if l > rhs.
func (l *List) Compare(rhs *List) int {
i := 0
for {
if i >= len(l.Items) && i >= len(rhs.Items) {
// Lists are the same length and all items are equal.
return false
return 0
}
if i >= len(l.Items) {
// LHS is shorter.
return true
return -1
}
if i >= len(rhs.Items) {
// RHS is shorter.
return false
return 1
}
if l.Items[i].Less(rhs.Items[i]) {
// LHS is less; return
return true
}
if rhs.Items[i].Less(l.Items[i]) {
// RHS is less; return
return false
if c := l.Items[i].Compare(rhs.Items[i]); c != 0 {
return c
}
// The items are equal; continue.
i++
Expand Down Expand Up @@ -191,8 +299,30 @@ func (m *Map) computeOrder() []int {
return m.order
}

// Equals compares two maps lexically.
func (m *Map) Equals(rhs *Map) bool {
if len(m.Items) != len(rhs.Items) {
return false
}
for _, lfield := range m.Items {
rfield, ok := rhs.Get(lfield.Name)
if !ok {
return false
}
if !lfield.Value.Equals(rfield.Value) {
return false
}
}
return true
}

// Less compares two maps lexically.
func (m *Map) Less(rhs *Map) bool {
return m.Compare(rhs) == -1
}

// Compare compares two maps lexically.
func (m *Map) Compare(rhs *Map) int {
var noAllocL, noAllocR [2]int
var morder, rorder []int

Expand Down Expand Up @@ -238,28 +368,22 @@ func (m *Map) Less(rhs *Map) bool {
for {
if i >= len(morder) && i >= len(rorder) {
// Maps are the same length and all items are equal.
return false
return 0
}
if i >= len(morder) {
// LHS is shorter.
return true
return -1
}
if i >= len(rorder) {
// RHS is shorter.
return false
return 1
}
fa, fb := &m.Items[morder[i]], &rhs.Items[rorder[i]]
if fa.Name != fb.Name {
// the map having the field name that sorts lexically less is "less"
return fa.Name < fb.Name
if c := strings.Compare(fa.Name, fb.Name); c != 0 {
return c
}
if fa.Value.Less(fb.Value) {
// LHS is less; return
return true
}
if fb.Value.Less(fa.Value) {
// RHS is less; return
return false
if c := fa.Value.Compare(fb.Value); c != 0 {
return c
}
// The items are equal; continue.
i++
Expand Down