mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
types/views: add SliceMapKey[T]
views.Slice are meant to be immutable, and if used as such it is at times desirable to use them as a key in a map. For non-viewed slices it was kinda doable by creating a custom key struct but views.Slice didn't allow for the same so add a method to create that struct here. Updates tailscale/corp#17122 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
5595b61b96
commit
b752bde280
@ -39,6 +39,9 @@ func ByteSliceOf[T ~[]byte](x T) ByteSlice[T] {
|
||||
return ByteSlice[T]{x}
|
||||
}
|
||||
|
||||
// MapKey returns a unique key for a slice, based on its address and length.
|
||||
func (v ByteSlice[T]) MapKey() SliceMapKey[byte] { return mapKey(v.ж) }
|
||||
|
||||
// Len returns the length of the slice.
|
||||
func (v ByteSlice[T]) Len() int {
|
||||
return len(v.ж)
|
||||
@ -168,6 +171,22 @@ func (v SliceView[T, V]) SliceTo(i int) SliceView[T, V] { return SliceView[T, V]
|
||||
// Slice returns v[i:j]
|
||||
func (v SliceView[T, V]) Slice(i, j int) SliceView[T, V] { return SliceView[T, V]{v.ж[i:j]} }
|
||||
|
||||
// SliceMapKey represents a comparable unique key for a slice, based on its
|
||||
// address and length. It can be used to key maps by slices but should only be
|
||||
// used when the underlying slice is immutable.
|
||||
//
|
||||
// Empty and nil slices have different keys.
|
||||
type SliceMapKey[T any] struct {
|
||||
// t is the address of the first element, or nil if the slice is nil or
|
||||
// empty.
|
||||
t *T
|
||||
// n is the length of the slice, or -1 if the slice is nil.
|
||||
n int
|
||||
}
|
||||
|
||||
// MapKey returns a unique key for a slice, based on its address and length.
|
||||
func (v SliceView[T, V]) MapKey() SliceMapKey[T] { return mapKey(v.ж) }
|
||||
|
||||
// AppendTo appends the underlying slice values to dst.
|
||||
func (v SliceView[T, V]) AppendTo(dst []V) []V {
|
||||
for _, x := range v.ж {
|
||||
@ -190,6 +209,20 @@ type Slice[T any] struct {
|
||||
ж []T
|
||||
}
|
||||
|
||||
// MapKey returns a unique key for a slice, based on its address and length.
|
||||
func (v Slice[T]) MapKey() SliceMapKey[T] { return mapKey(v.ж) }
|
||||
|
||||
// mapKey returns a unique key for a slice, based on its address and length.
|
||||
func mapKey[T any](x []T) SliceMapKey[T] {
|
||||
if x == nil {
|
||||
return SliceMapKey[T]{nil, -1}
|
||||
}
|
||||
if len(x) == 0 {
|
||||
return SliceMapKey[T]{nil, 0}
|
||||
}
|
||||
return SliceMapKey[T]{&x[0], len(x)}
|
||||
}
|
||||
|
||||
// SliceOf returns a Slice for the provided slice for immutable values.
|
||||
// It is the caller's responsibility to make sure V is immutable.
|
||||
func SliceOf[T any](x []T) Slice[T] {
|
||||
|
@ -166,3 +166,40 @@ func TestSliceEqual(t *testing.T) {
|
||||
t.Error("got a[:2] == a[:1]")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSliceMapKey tests that the MapKey method returns the same key for slices
|
||||
// with the same underlying slice and different keys for different slices or
|
||||
// with same underlying slice but different bounds.
|
||||
func TestSliceMapKey(t *testing.T) {
|
||||
underlying := []string{"foo", "bar"}
|
||||
nilSlice := SliceOf[string](nil)
|
||||
empty := SliceOf([]string{})
|
||||
u1 := SliceOf(underlying)
|
||||
u2 := SliceOf(underlying)
|
||||
u3 := SliceOf([]string{"foo", "bar"}) // different underlying slice
|
||||
|
||||
sub1 := u1.Slice(0, 1)
|
||||
sub2 := u1.Slice(1, 2)
|
||||
sub3 := u1.Slice(0, 2)
|
||||
|
||||
wantSame := []Slice[string]{u1, u2, sub3}
|
||||
for i := 1; i < len(wantSame); i++ {
|
||||
s0, si := wantSame[0], wantSame[i]
|
||||
k0 := s0.MapKey()
|
||||
ki := si.MapKey()
|
||||
if ki != k0 {
|
||||
t.Fatalf("wantSame[%d, %+v, %q) != wantSame[0, %+v, %q)", i, ki, si.AsSlice(), k0, s0.AsSlice())
|
||||
}
|
||||
}
|
||||
|
||||
wantDiff := []Slice[string]{nilSlice, empty, sub1, sub2, sub3, u3}
|
||||
for i := 0; i < len(wantDiff); i++ {
|
||||
for j := i + 1; j < len(wantDiff); j++ {
|
||||
si, sj := wantDiff[i], wantDiff[j]
|
||||
ki, kj := wantDiff[i].MapKey(), wantDiff[j].MapKey()
|
||||
if ki == kj {
|
||||
t.Fatalf("wantDiff[%d, %+v, %q] == wantDiff[%d, %+v, %q] ", i, ki, si.AsSlice(), j, kj, sj.AsSlice())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user