util/uniq,types/lazy,*: delete code that's now in Go std

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>
This commit is contained in:
Brad Fitzpatrick
2025-01-12 19:14:04 -08:00
committed by Brad Fitzpatrick
parent 5fdb4f83ad
commit 69b90742fe
13 changed files with 18 additions and 271 deletions

View File

@@ -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]
}
}

View File

@@ -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)
}
}