github.com/cilium/cilium@v1.16.2/pkg/slices/slices.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package slices
     5  
     6  import (
     7  	"slices"
     8  	"sort"
     9  
    10  	"golang.org/x/exp/constraints"
    11  )
    12  
    13  // Unique deduplicates the elements in the input slice, preserving their ordering and
    14  // modifying the slice in place.
    15  // Unique relies on a map to find multiple occurrences of the same elements.
    16  // For slices with a size less than 192 elements, a simpler O(N^2) search algorithm
    17  // that does not allocate memory is used instead.
    18  // Limit of 192 has been experimentally derived (look at BenchmarkUnique for more information).
    19  func Unique[S ~[]T, T comparable](s S) S {
    20  	if len(s) < 2 {
    21  		return s
    22  	}
    23  
    24  	last := 0
    25  
    26  	if len(s) < 192 {
    27  	Loop:
    28  		for i := 0; i < len(s); i++ {
    29  			for j := 0; j < last; j++ {
    30  				if s[i] == s[j] {
    31  					continue Loop
    32  				}
    33  			}
    34  			s[last] = s[i]
    35  			last++
    36  		}
    37  	} else {
    38  		set := make(map[T]struct{}, len(s))
    39  		for i := 0; i < len(s); i++ {
    40  			if _, ok := set[s[i]]; ok {
    41  				continue
    42  			}
    43  			set[s[i]] = struct{}{}
    44  			s[last] = s[i]
    45  			last++
    46  		}
    47  	}
    48  
    49  	return s[:last]
    50  }
    51  
    52  // UniqueFunc deduplicates the elements in the input slice like Unique, but takes a
    53  // function to extract the comparable "key" to compare T. This is slower than Unique,
    54  // but can be used with non-comparable elements.
    55  func UniqueFunc[S ~[]T, T any, K comparable](s S, key func(i int) K) S {
    56  	if len(s) < 2 {
    57  		return s
    58  	}
    59  
    60  	last := 0
    61  
    62  	set := make(map[K]struct{}, len(s))
    63  	for i := 0; i < len(s); i++ {
    64  		if _, ok := set[key(i)]; ok {
    65  			continue
    66  		}
    67  		set[key(i)] = struct{}{}
    68  		s[last] = s[i]
    69  		last++
    70  	}
    71  
    72  	return s[:last]
    73  }
    74  
    75  // SortedUnique sorts and dedup the input slice in place.
    76  // It uses the < operator to compare the elements in the slice and thus requires
    77  // the elements to satisfies contraints.Ordered.
    78  func SortedUnique[S ~[]T, T constraints.Ordered](s S) S {
    79  	if len(s) < 2 {
    80  		return s
    81  	}
    82  
    83  	sort.Slice(s, func(i, j int) bool {
    84  		return s[i] < s[j]
    85  	})
    86  	return slices.Compact(s)
    87  }
    88  
    89  // SortedUniqueFunc is like SortedUnique but allows the user to specify custom functions
    90  // for ordering (less function) and comparing (eq function) the elements in the slice.
    91  // This is useful in all the cases where SortedUnique cannot be used:
    92  // - for types that do not satisfy constraints.Ordered (e.g: composite types)
    93  // - when the user wants to customize how elements are compared (e.g: user wants to enforce reverse ordering)
    94  func SortedUniqueFunc[S ~[]T, T any](
    95  	s S,
    96  	less func(i, j int) bool,
    97  	eq func(a, b T) bool,
    98  ) S {
    99  	if len(s) < 2 {
   100  		return s
   101  	}
   102  
   103  	sort.Slice(s, less)
   104  	return slices.CompactFunc(s, eq)
   105  }
   106  
   107  // Diff returns a slice of elements which is the difference of a and b.
   108  // The returned slice keeps the elements in the same order found in the "a" slice.
   109  // Both input slices are considered as sets, that is, all elements are considered as
   110  // unique when computing the difference.
   111  func Diff[S ~[]T, T comparable](a, b S) []T {
   112  	if len(a) == 0 {
   113  		return nil
   114  	}
   115  	if len(b) == 0 {
   116  		return a
   117  	}
   118  
   119  	var diff []T
   120  
   121  	setB := make(map[T]struct{}, len(b))
   122  	for _, v := range b {
   123  		setB[v] = struct{}{}
   124  	}
   125  
   126  	setA := make(map[T]struct{}, len(a))
   127  	for _, v := range a {
   128  		// v is in b, too
   129  		if _, ok := setB[v]; ok {
   130  			continue
   131  		}
   132  		// v has been already added to diff
   133  		if _, ok := setA[v]; ok {
   134  			continue
   135  		}
   136  		diff = append(diff, v)
   137  		setA[v] = struct{}{}
   138  	}
   139  	return diff
   140  }
   141  
   142  // SubsetOf returns a boolean that indicates if slice a is a subset of slice b.
   143  // In case it is not, the returned slice contains all the unique elements that are in a but not in b.
   144  func SubsetOf[S ~[]T, T comparable](a, b S) (bool, []T) {
   145  	d := Diff(a, b)
   146  	return len(d) == 0, d
   147  }
   148  
   149  // XorNil returns true if one of the two slices is nil while the other is not.
   150  func XorNil[T any](s1, s2 []T) bool {
   151  	return s1 == nil && s2 != nil ||
   152  		s1 != nil && s2 == nil
   153  }