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  }