util/uniq: add ModifySliceFunc (#5504)

Follow-up to #5491. This was used in control... oops!

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
pull/5506/head
Andrew Dunham 2 years ago committed by GitHub
parent 7ca17b6bdb
commit 70ed22ccf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,3 +33,31 @@ func ModifySlice[E comparable](slice *[]E) {
*slice = (*slice)[:end] *slice = (*slice)[:end]
} }
} }
// ModifySliceFunc is the same as ModifySlice except that it allows using a
// custom comparison function.
//
// eq should report whether the two provided elements are equal.
func ModifySliceFunc[E any](slice *[]E, eq func(i, j E) bool) {
// Remove duplicates
dst := 0
for i := 1; i < len(*slice); i++ {
if eq((*slice)[dst], (*slice)[i]) {
continue
}
dst++
(*slice)[dst] = (*slice)[i]
}
// Zero out the elements we removed at the end of the slice
end := dst + 1
var zero E
for i := end; i < len(*slice); i++ {
(*slice)[i] = zero
}
// Truncate the slice
if end < len(*slice) {
*slice = (*slice)[:end]
}
}

@ -12,22 +12,23 @@ import (
"tailscale.com/util/uniq" "tailscale.com/util/uniq"
) )
func runTests(t *testing.T, cb func(*[]int)) { func runTests(t *testing.T, cb func(*[]uint32)) {
tests := []struct { tests := []struct {
in []int // Use uint32 to be different from an int-typed slice index
want []int in []uint32
want []uint32
}{ }{
{in: []int{0, 1, 2}, want: []int{0, 1, 2}}, {in: []uint32{0, 1, 2}, want: []uint32{0, 1, 2}},
{in: []int{0, 1, 2, 2}, want: []int{0, 1, 2}}, {in: []uint32{0, 1, 2, 2}, want: []uint32{0, 1, 2}},
{in: []int{0, 0, 1, 2}, want: []int{0, 1, 2}}, {in: []uint32{0, 0, 1, 2}, want: []uint32{0, 1, 2}},
{in: []int{0, 1, 0, 2}, want: []int{0, 1, 0, 2}}, {in: []uint32{0, 1, 0, 2}, want: []uint32{0, 1, 0, 2}},
{in: []int{0}, want: []int{0}}, {in: []uint32{0}, want: []uint32{0}},
{in: []int{0, 0}, want: []int{0}}, {in: []uint32{0, 0}, want: []uint32{0}},
{in: []int{}, want: []int{}}, {in: []uint32{}, want: []uint32{}},
} }
for _, test := range tests { for _, test := range tests {
in := make([]int, len(test.in)) in := make([]uint32, len(test.in))
copy(in, test.in) copy(in, test.in)
cb(&test.in) cb(&test.in)
if !reflect.DeepEqual(test.in, test.want) { if !reflect.DeepEqual(test.in, test.want) {
@ -44,11 +45,19 @@ func runTests(t *testing.T, cb func(*[]int)) {
} }
func TestModifySlice(t *testing.T) { func TestModifySlice(t *testing.T) {
runTests(t, func(slice *[]int) { runTests(t, func(slice *[]uint32) {
uniq.ModifySlice(slice) uniq.ModifySlice(slice)
}) })
} }
func TestModifySliceFunc(t *testing.T) {
runTests(t, func(slice *[]uint32) {
uniq.ModifySliceFunc(slice, func(i, j uint32) bool {
return i == j
})
})
}
func Benchmark(b *testing.B) { func Benchmark(b *testing.B) {
benches := []struct { benches := []struct {
name string name string

Loading…
Cancel
Save