mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-30 04:31:04 +00:00
types/views: fix SliceEqualAnyOrderFunc short optimization
This was flagged by @tkhattra on the merge commit; thanks! Updates tailscale/corp#25479 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ia8045640f02bd4dcc0fe7433249fd72ac6b9cf52
This commit is contained in:
parent
0aa54151f2
commit
eb299302ba
@ -386,14 +386,32 @@ func SliceEqualAnyOrderFunc[T any, V comparable](a, b Slice[T], cmp func(T) V) b
|
|||||||
// do the quadratic thing. We can also only check the items between
|
// do the quadratic thing. We can also only check the items between
|
||||||
// diffStart and the end.
|
// diffStart and the end.
|
||||||
nRemain := a.Len() - diffStart
|
nRemain := a.Len() - diffStart
|
||||||
if nRemain <= 5 {
|
const shortOptLen = 5
|
||||||
maxLen := a.Len() // same as b.Len()
|
if nRemain <= shortOptLen {
|
||||||
for i := diffStart; i < maxLen; i++ {
|
// These track which elements in a and b have been matched, so
|
||||||
av := cmp(a.At(i))
|
// that we don't treat arrays with differing number of
|
||||||
|
// duplicate elements as equal (e.g. [1, 1, 2] and [1, 2, 2]).
|
||||||
|
var aMatched, bMatched [shortOptLen]bool
|
||||||
|
|
||||||
|
// Compare each element in a to each element in b
|
||||||
|
for i := range nRemain {
|
||||||
|
av := cmp(a.At(i + diffStart))
|
||||||
found := false
|
found := false
|
||||||
for j := diffStart; j < maxLen; j++ {
|
for j := range nRemain {
|
||||||
bv := cmp(b.At(j))
|
// Skip elements in b that have already been
|
||||||
|
// used to match an item in a.
|
||||||
|
if bMatched[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bv := cmp(b.At(j + diffStart))
|
||||||
if av == bv {
|
if av == bv {
|
||||||
|
// Mark these elements as already
|
||||||
|
// matched, so that a future loop
|
||||||
|
// iteration (of a duplicate element)
|
||||||
|
// doesn't match it again.
|
||||||
|
aMatched[i] = true
|
||||||
|
bMatched[j] = true
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -402,6 +420,14 @@ func SliceEqualAnyOrderFunc[T any, V comparable](a, b Slice[T], cmp func(T) V) b
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify all elements were matched exactly once.
|
||||||
|
for i := range nRemain {
|
||||||
|
if !aMatched[i] || !bMatched[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +197,38 @@ func TestSliceEqualAnyOrderFunc(t *testing.T) {
|
|||||||
// Long difference; past the quadratic limit
|
// Long difference; past the quadratic limit
|
||||||
longDiff := ncFrom("b", "a", "c", "d", "e", "f", "g", "h", "i", "k") // differs at end
|
longDiff := ncFrom("b", "a", "c", "d", "e", "f", "g", "h", "i", "k") // differs at end
|
||||||
c.Check(SliceEqualAnyOrderFunc(longSlice, longDiff, cmp), qt.Equals, false)
|
c.Check(SliceEqualAnyOrderFunc(longSlice, longDiff, cmp), qt.Equals, false)
|
||||||
|
|
||||||
|
// The short slice optimization had a bug where it wouldn't handle
|
||||||
|
// duplicate elements; test various cases here driven by code coverage.
|
||||||
|
shortTestCases := []struct {
|
||||||
|
name string
|
||||||
|
s1, s2 Slice[nc]
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "duplicates_same_length",
|
||||||
|
s1: ncFrom("a", "a", "b"),
|
||||||
|
s2: ncFrom("a", "b", "b"),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicates_different_matched",
|
||||||
|
s1: ncFrom("x", "y", "a", "a", "b"),
|
||||||
|
s2: ncFrom("x", "y", "b", "a", "a"),
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "item_in_a_not_b",
|
||||||
|
s1: ncFrom("x", "y", "a", "b", "c"),
|
||||||
|
s2: ncFrom("x", "y", "b", "c", "q"),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range shortTestCases {
|
||||||
|
t.Run("short_"+tc.name, func(t *testing.T) {
|
||||||
|
c.Check(SliceEqualAnyOrderFunc(tc.s1, tc.s2, cmp), qt.Equals, tc.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSliceEqual(t *testing.T) {
|
func TestSliceEqual(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user