sync.OnceValue and slices.Compact were both added in Go 1.21. cmp.Or was added in Go 1.22. Updates #8632 Updates #11058 Change-Id: I89ba4c404f40188e1f8a9566c8aaa049be377754 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>main
parent
5fdb4f83ad
commit
69b90742fe
@ -1,62 +0,0 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package uniq provides removal of adjacent duplicate elements in slices.
|
||||
// It is similar to the unix command uniq.
|
||||
package uniq |
||||
|
||||
// ModifySlice removes adjacent duplicate elements from the given slice. It
|
||||
// adjusts the length of the slice appropriately and zeros the tail.
|
||||
//
|
||||
// ModifySlice does O(len(*slice)) operations.
|
||||
func ModifySlice[E comparable](slice *[]E) { |
||||
// Remove duplicates
|
||||
dst := 0 |
||||
for i := 1; i < len(*slice); i++ { |
||||
if (*slice)[i] == (*slice)[dst] { |
||||
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] |
||||
} |
||||
} |
||||
|
||||
// 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] |
||||
} |
||||
} |
||||
@ -1,102 +0,0 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package uniq_test |
||||
|
||||
import ( |
||||
"reflect" |
||||
"strconv" |
||||
"testing" |
||||
|
||||
"tailscale.com/util/uniq" |
||||
) |
||||
|
||||
func runTests(t *testing.T, cb func(*[]uint32)) { |
||||
tests := []struct { |
||||
// Use uint32 to be different from an int-typed slice index
|
||||
in []uint32 |
||||
want []uint32 |
||||
}{ |
||||
{in: []uint32{0, 1, 2}, want: []uint32{0, 1, 2}}, |
||||
{in: []uint32{0, 1, 2, 2}, want: []uint32{0, 1, 2}}, |
||||
{in: []uint32{0, 0, 1, 2}, want: []uint32{0, 1, 2}}, |
||||
{in: []uint32{0, 1, 0, 2}, want: []uint32{0, 1, 0, 2}}, |
||||
{in: []uint32{0}, want: []uint32{0}}, |
||||
{in: []uint32{0, 0}, want: []uint32{0}}, |
||||
{in: []uint32{}, want: []uint32{}}, |
||||
} |
||||
|
||||
for _, test := range tests { |
||||
in := make([]uint32, len(test.in)) |
||||
copy(in, test.in) |
||||
cb(&test.in) |
||||
if !reflect.DeepEqual(test.in, test.want) { |
||||
t.Errorf("uniq.Slice(%v) = %v, want %v", in, test.in, test.want) |
||||
} |
||||
start := len(test.in) |
||||
test.in = test.in[:cap(test.in)] |
||||
for i := start; i < len(in); i++ { |
||||
if test.in[i] != 0 { |
||||
t.Errorf("uniq.Slice(%v): non-0 in tail of %v at index %v", in, test.in, i) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestModifySlice(t *testing.T) { |
||||
runTests(t, func(slice *[]uint32) { |
||||
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) { |
||||
benches := []struct { |
||||
name string |
||||
reset func(s []byte) |
||||
}{ |
||||
{name: "AllDups", |
||||
reset: func(s []byte) { |
||||
for i := range s { |
||||
s[i] = '*' |
||||
} |
||||
}, |
||||
}, |
||||
{name: "NoDups", |
||||
reset: func(s []byte) { |
||||
for i := range s { |
||||
s[i] = byte(i) |
||||
} |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
for _, bb := range benches { |
||||
b.Run(bb.name, func(b *testing.B) { |
||||
for size := 1; size <= 4096; size *= 16 { |
||||
b.Run(strconv.Itoa(size), func(b *testing.B) { |
||||
benchmark(b, 64, bb.reset) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func benchmark(b *testing.B, size int64, reset func(s []byte)) { |
||||
b.ReportAllocs() |
||||
b.SetBytes(size) |
||||
s := make([]byte, size) |
||||
b.ResetTimer() |
||||
for range b.N { |
||||
s = s[:size] |
||||
reset(s) |
||||
uniq.ModifySlice(&s) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue