istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/util/smallset/smallset.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package smallset 16 17 import ( 18 "fmt" 19 20 "golang.org/x/exp/constraints" 21 22 "istio.io/istio/pkg/slices" 23 ) 24 25 // Set is an immutable set optimized for *small number of items*. For general purposes, Sets is likely better 26 // 27 // *Set construction*: sets is roughly 1kb allocations per 250 items. smallsets is 0. 28 // *Contains* sets is O(1). smallsets is O(logn). smallsets is typically faster up to about 5 elements. 29 // 30 // At 1000 items, it is roughly 5x slower (30ns vs 5ns). 31 type Set[T constraints.Ordered] struct { 32 items []T 33 } 34 35 // NewPresorted creates a new Set with the given items. 36 // If items is not sorted or contains duplicates, this gives undefined behavior; use New instead. 37 func NewPresorted[T constraints.Ordered](items ...T) Set[T] { 38 return Set[T]{items: items} 39 } 40 41 // New creates a new Set with the given items. 42 // Duplicates are removed 43 func New[T constraints.Ordered](items ...T) Set[T] { 44 if len(items) == 1 { 45 return Set[T]{items: items} 46 } 47 slices.Sort(items) 48 items = slices.FilterDuplicatesPresorted(items) 49 return Set[T]{items: items} 50 } 51 52 // CopyAndInsert builds a *new* with all the current items plus new items 53 func (s Set[T]) CopyAndInsert(items ...T) Set[T] { 54 slices.Sort(items) 55 // This is basically the 'merge' part of merge sort. 56 a := s.items 57 b := items 58 nl := make([]T, 0, len(a)+len(b)) 59 i, j := 0, 0 60 appendIfUnique := func(t T) []T { 61 l := len(nl) 62 if l == 0 { 63 nl = append(nl, t) 64 } else { 65 last := nl[l-1] 66 if last != t { 67 nl = append(nl, t) 68 } 69 } 70 return nl 71 } 72 for i < len(a) && j < len(b) { 73 if a[i] < b[j] { 74 nl = appendIfUnique(a[i]) 75 i++ 76 } else { 77 nl = appendIfUnique(b[j]) 78 j++ 79 } 80 } 81 for ; i < len(a); i++ { 82 nl = appendIfUnique(a[i]) 83 } 84 for ; j < len(b); j++ { 85 nl = appendIfUnique(b[j]) 86 } 87 // we already know they are sorted+unique 88 return Set[T]{items: nl} 89 } 90 91 // List returns the underlying slice. Must not be modified 92 func (s Set[T]) List() []T { 93 return s.items 94 } 95 96 // Contains returns whether the given item is in the set. 97 func (s Set[T]) Contains(item T) bool { 98 _, f := slices.BinarySearch(s.items, item) 99 return f 100 } 101 102 // Len returns the number of elements in this Set. 103 func (s Set[T]) Len() int { 104 return len(s.items) 105 } 106 107 // IsEmpty indicates whether the set is the empty set. 108 func (s Set[T]) IsEmpty() bool { 109 return len(s.items) == 0 110 } 111 112 // IsNil indicates whether the set is nil. This is different from an empty set. 113 // 'var smallset.Set': nil 114 // smallset.New(): nil 115 // smallset.New(emptyList...): not nil 116 func (s Set[T]) IsNil() bool { 117 return s.items == nil 118 } 119 120 // String returns a string representation of the set. 121 // Use it only for debugging and logging. 122 func (s Set[T]) String() string { 123 return fmt.Sprintf("%v", s.items) 124 }