Skip to content

Commit 70ed22c

Browse files
authored
util/uniq: add ModifySliceFunc (tailscale#5504)
Follow-up to tailscale#5491. This was used in control... oops! Signed-off-by: Andrew Dunham <andrew@tailscale.com>
1 parent 7ca17b6 commit 70ed22c

File tree

2 files changed

+49
-12
lines changed

2 files changed

+49
-12
lines changed

util/uniq/slice.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,31 @@ func ModifySlice[E comparable](slice *[]E) {
3333
*slice = (*slice)[:end]
3434
}
3535
}
36+
37+
// ModifySliceFunc is the same as ModifySlice except that it allows using a
38+
// custom comparison function.
39+
//
40+
// eq should report whether the two provided elements are equal.
41+
func ModifySliceFunc[E any](slice *[]E, eq func(i, j E) bool) {
42+
// Remove duplicates
43+
dst := 0
44+
for i := 1; i < len(*slice); i++ {
45+
if eq((*slice)[dst], (*slice)[i]) {
46+
continue
47+
}
48+
dst++
49+
(*slice)[dst] = (*slice)[i]
50+
}
51+
52+
// Zero out the elements we removed at the end of the slice
53+
end := dst + 1
54+
var zero E
55+
for i := end; i < len(*slice); i++ {
56+
(*slice)[i] = zero
57+
}
58+
59+
// Truncate the slice
60+
if end < len(*slice) {
61+
*slice = (*slice)[:end]
62+
}
63+
}

util/uniq/slice_test.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,23 @@ import (
1212
"tailscale.com/util/uniq"
1313
)
1414

15-
func runTests(t *testing.T, cb func(*[]int)) {
15+
func runTests(t *testing.T, cb func(*[]uint32)) {
1616
tests := []struct {
17-
in []int
18-
want []int
17+
// Use uint32 to be different from an int-typed slice index
18+
in []uint32
19+
want []uint32
1920
}{
20-
{in: []int{0, 1, 2}, want: []int{0, 1, 2}},
21-
{in: []int{0, 1, 2, 2}, want: []int{0, 1, 2}},
22-
{in: []int{0, 0, 1, 2}, want: []int{0, 1, 2}},
23-
{in: []int{0, 1, 0, 2}, want: []int{0, 1, 0, 2}},
24-
{in: []int{0}, want: []int{0}},
25-
{in: []int{0, 0}, want: []int{0}},
26-
{in: []int{}, want: []int{}},
21+
{in: []uint32{0, 1, 2}, want: []uint32{0, 1, 2}},
22+
{in: []uint32{0, 1, 2, 2}, want: []uint32{0, 1, 2}},
23+
{in: []uint32{0, 0, 1, 2}, want: []uint32{0, 1, 2}},
24+
{in: []uint32{0, 1, 0, 2}, want: []uint32{0, 1, 0, 2}},
25+
{in: []uint32{0}, want: []uint32{0}},
26+
{in: []uint32{0, 0}, want: []uint32{0}},
27+
{in: []uint32{}, want: []uint32{}},
2728
}
2829

2930
for _, test := range tests {
30-
in := make([]int, len(test.in))
31+
in := make([]uint32, len(test.in))
3132
copy(in, test.in)
3233
cb(&test.in)
3334
if !reflect.DeepEqual(test.in, test.want) {
@@ -44,11 +45,19 @@ func runTests(t *testing.T, cb func(*[]int)) {
4445
}
4546

4647
func TestModifySlice(t *testing.T) {
47-
runTests(t, func(slice *[]int) {
48+
runTests(t, func(slice *[]uint32) {
4849
uniq.ModifySlice(slice)
4950
})
5051
}
5152

53+
func TestModifySliceFunc(t *testing.T) {
54+
runTests(t, func(slice *[]uint32) {
55+
uniq.ModifySliceFunc(slice, func(i, j uint32) bool {
56+
return i == j
57+
})
58+
})
59+
}
60+
5261
func Benchmark(b *testing.B) {
5362
benches := []struct {
5463
name string

0 commit comments

Comments
 (0)