From d0b4d44963d5ea6ce2f8bd312de749dd2e5afa7d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 27 Jul 2026 20:50:35 +0000 Subject: [PATCH] util/set: add OfSliceView constructor and Set.AddSliceView Building a Set from a views.Slice previously required set.Of(v.AsSlice()...), which allocates an intermediate slice copy before allocating the set. Add OfSliceView and AddSliceView to populate a set directly from the view, mirroring the existing AddSlice/AddSeq/AddSet family. Updates tailscale/corp#45499 Signed-off-by: Brad Fitzpatrick Change-Id: I3f2a9d417c60be8e5f1acd42708e2f9a4d6c1b7e --- util/set/set.go | 16 ++++++++++++++++ util/set/set_test.go | 14 ++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/util/set/set.go b/util/set/set.go index 38bf676f8..c6db4f3f2 100644 --- a/util/set/set.go +++ b/util/set/set.go @@ -10,6 +10,8 @@ "maps" "reflect" "sort" + + "tailscale.com/types/views" ) // Set is a set of T. @@ -27,6 +29,13 @@ func Of[T comparable](slice ...T) Set[T] { return s } +// OfSliceView returns a new set constructed from the elements in v. +func OfSliceView[T comparable](v views.Slice[T]) Set[T] { + s := make(Set[T], v.Len()) + s.AddSliceView(v) + return s +} + // Clone returns a new set cloned from the elements in s. func (s Set[T]) Clone() Set[T] { return maps.Clone(s) @@ -56,6 +65,13 @@ func (s Set[T]) AddSlice(es []T) { } } +// AddSliceView adds each element of v to s. +func (s Set[T]) AddSliceView(v views.Slice[T]) { + for _, e := range v.All() { + s.Add(e) + } +} + // AddSet adds each element of es to s. func (s Set[T]) AddSet(es Set[T]) { for e := range es { diff --git a/util/set/set_test.go b/util/set/set_test.go index d3d118bee..1c7dee287 100644 --- a/util/set/set_test.go +++ b/util/set/set_test.go @@ -7,6 +7,8 @@ "encoding/json" "slices" "testing" + + "tailscale.com/types/views" ) func TestSet(t *testing.T) { @@ -98,6 +100,18 @@ func TestSetOf(t *testing.T) { } } +func TestOfSliceView(t *testing.T) { + s := OfSliceView(views.SliceOf([]int{1, 2, 3, 4, 4, 1})) + if s.Len() != 4 { + t.Errorf("wrong len %d; want 4", s.Len()) + } + for _, n := range []int{1, 2, 3, 4} { + if !s.Contains(n) { + t.Errorf("should contain %d", n) + } + } +} + func TestEqual(t *testing.T) { type test struct { name string