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 }