Skip to content

Commit ef76f6a

Browse files
authored
Add threadsafe string set (#1522)
1 parent 7a70b16 commit ef76f6a

File tree

3 files changed

+761
-4
lines changed

3 files changed

+761
-4
lines changed

pkg/lib/sets/strset/strset.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ import (
2323
"strings"
2424
)
2525

26-
// string
27-
28-
// Set functionality adapted from github.com/scylladb/go-set
2926
type Set map[string]struct{}
3027

3128
var _keyExists = struct{}{}
@@ -266,7 +263,7 @@ func Union(sets ...Set) Set {
266263
return u
267264
}
268265

269-
// Difference returns a new set which contains items which are in in the first
266+
// Difference returns a new set which contains items which are in the first
270267
// set but not in the others.
271268
func Difference(set1 Set, sets ...Set) Set {
272269
s := set1.Copy()
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
/*
2+
Copyright 2020 Cortex Labs, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package threadsafe
18+
19+
import (
20+
"sync"
21+
22+
"github.com/cortexlabs/cortex/pkg/lib/sets/strset"
23+
)
24+
25+
// New creates and initializes a new Set.
26+
type Set struct {
27+
sync.RWMutex
28+
s strset.Set
29+
}
30+
31+
func New(ts ...string) *Set {
32+
set := Set{}
33+
set.s = strset.New(ts...)
34+
return &set
35+
}
36+
37+
func FromSlice(items []string) *Set {
38+
return New(items...)
39+
}
40+
41+
// NewWithSize creates a new Set and gives make map a size hint.
42+
func NewWithSize(size int) *Set {
43+
set := Set{}
44+
set.s = strset.NewWithSize(size)
45+
return &set
46+
}
47+
48+
func (s *Set) Len() int {
49+
s.RLock()
50+
defer s.RUnlock()
51+
return len(s.s)
52+
}
53+
54+
func (s *Set) ToStrset() strset.Set {
55+
s.RLock()
56+
defer s.RUnlock()
57+
return s.s.Copy()
58+
}
59+
60+
// Add includes the specified items (one or more) to the Set. The underlying
61+
// Set s is modified. If passed nothing it silently returns.
62+
func (s *Set) Add(items ...string) {
63+
s.Lock()
64+
defer s.Unlock()
65+
s.s.Add(items...)
66+
}
67+
68+
// Remove deletes the specified items from the Set. The underlying Set s is
69+
// modified. If passed nothing it silently returns.
70+
func (s *Set) Remove(items ...string) {
71+
s.Lock()
72+
defer s.Unlock()
73+
s.s.Remove(items...)
74+
}
75+
76+
// GetOne returns an item from the set or "" if the set is empty.
77+
func (s *Set) GetOne() string {
78+
s.RLock()
79+
defer s.RUnlock()
80+
return s.s.GetOne()
81+
}
82+
83+
// GetOne2 returns an item from the set. The second value is a bool that is
84+
// true if an item exists in the set, or false if the set is empty.
85+
func (s *Set) GetOne2() (string, bool) {
86+
s.RLock()
87+
defer s.RUnlock()
88+
return s.s.GetOne2()
89+
}
90+
91+
// Pop deletes and returns an item from the Set. The underlying Set s is
92+
// modified. If Set is empty, the zero value is returned.
93+
func (s *Set) Pop() string {
94+
s.Lock()
95+
defer s.Unlock()
96+
return s.s.Pop()
97+
}
98+
99+
// Pop2 tries to delete and return an item from the Set. The underlying Set s
100+
// is modified. The second value is a bool that is true if the item existed in
101+
// the set, and false if not. If Set is empty, the zero value and false are
102+
// returned.
103+
func (s *Set) Pop2() (string, bool) {
104+
s.Lock()
105+
defer s.Unlock()
106+
return s.s.Pop2()
107+
}
108+
109+
// Has looks for the existence of items passed. It returns false if nothing is
110+
// passed. For multiple items it returns true only if all of the items exist.
111+
func (s *Set) Has(items ...string) bool {
112+
s.RLock()
113+
defer s.RUnlock()
114+
return s.s.Has(items...)
115+
}
116+
117+
// HasAny looks for the existence of any of the items passed.
118+
// It returns false if nothing is passed.
119+
// For multiple items it returns true if any of the items exist.
120+
func (s *Set) HasAny(items ...string) bool {
121+
s.RLock()
122+
defer s.RUnlock()
123+
return s.s.HasAny(items...)
124+
}
125+
126+
// Clear removes all items from the Set.
127+
func (s *Set) Clear() {
128+
s.Lock()
129+
defer s.Unlock()
130+
s.s.Clear()
131+
}
132+
133+
// IsEqual test whether s and t are the same in size and have the same items.
134+
func (s *Set) IsEqual(t strset.Set) bool {
135+
s.RLock()
136+
defer s.RUnlock()
137+
return s.s.IsEqual(t)
138+
}
139+
140+
// IsEqualThreadsafe test whether s and t are the same in size and have the same items.
141+
func (s *Set) IsEqualThreadsafe(t *Set) bool {
142+
s.RLock()
143+
defer s.RUnlock()
144+
t.RLock()
145+
defer t.RUnlock()
146+
return s.s.IsEqual(t.s)
147+
}
148+
149+
// IsSubset tests whether t is a subset of s.
150+
func (s *Set) IsSubset(t strset.Set) bool {
151+
s.RLock()
152+
defer s.RUnlock()
153+
return s.s.IsSubset(t)
154+
}
155+
156+
// IsSubsetThreadsafe tests whether t is a subset of s.
157+
func (s *Set) IsSubsetThreadsafe(t *Set) bool {
158+
s.RLock()
159+
defer s.RUnlock()
160+
t.RLock()
161+
defer t.RUnlock()
162+
return s.s.IsSubset(t.s)
163+
}
164+
165+
// IsSuperset tests whether t is a superset of s.
166+
func (s *Set) IsSuperset(t strset.Set) bool {
167+
s.RLock()
168+
defer s.RUnlock()
169+
return s.s.IsSuperset(t)
170+
}
171+
172+
// IsSupersetThreadsafe tests whether t is a superset of s.
173+
func (s *Set) IsSupersetThreadsafe(t *Set) bool {
174+
s.RLock()
175+
defer s.RUnlock()
176+
t.RLock()
177+
defer t.RUnlock()
178+
return s.s.IsSuperset(t.s)
179+
}
180+
181+
// Copy returns a new Set with a copy of s.
182+
func (s *Set) Copy() strset.Set {
183+
s.RLock()
184+
defer s.RUnlock()
185+
return s.s.Copy()
186+
}
187+
188+
// CopyToThreadsafe returns a new Set with a copy of s.
189+
func (s *Set) CopyToThreadsafe() *Set {
190+
s.RLock()
191+
defer s.RUnlock()
192+
193+
newSet := Set{}
194+
newSet.s = s.s.Copy()
195+
return &newSet
196+
}
197+
198+
// String returns a string representation of s
199+
func (s *Set) String() string {
200+
s.RLock()
201+
defer s.RUnlock()
202+
return s.s.String()
203+
}
204+
205+
// List returns a slice of all items.
206+
func (s *Set) Slice() []string {
207+
s.RLock()
208+
defer s.RUnlock()
209+
return s.s.Slice()
210+
}
211+
212+
// List returns a sorted slice of all items.
213+
func (s *Set) SliceSorted() []string {
214+
s.RLock()
215+
defer s.RUnlock()
216+
return s.s.SliceSorted()
217+
}
218+
219+
// Merge is like Union, however it modifies the current Set it's applied on
220+
// with the given t Set.
221+
func (s *Set) Merge(sets ...strset.Set) {
222+
s.Lock()
223+
defer s.Unlock()
224+
s.s.Merge(sets...)
225+
}
226+
227+
// MergeThreadsafe is like UnionThreadsafe, however it modifies the current Set it's applied on
228+
// with the given t Set.
229+
func (s *Set) MergeThreadsafe(sets ...*Set) {
230+
s.Lock()
231+
defer s.Unlock()
232+
233+
for _, set := range sets {
234+
set.RLock()
235+
s.s.Merge(set.s)
236+
set.RUnlock()
237+
}
238+
}
239+
240+
// Subtract removes the Set items contained in sets from Set s
241+
func (s *Set) Subtract(sets ...strset.Set) {
242+
s.Lock()
243+
defer s.Unlock()
244+
s.s.Subtract(sets...)
245+
}
246+
247+
// SubtractThreadsafe removes the Set items contained in sets from Set s
248+
func (s *Set) SubtractThreadsafe(sets ...*Set) {
249+
s.Lock()
250+
defer s.Unlock()
251+
252+
for _, set := range sets {
253+
set.RLock()
254+
s.s.Subtract(set.s)
255+
set.RUnlock()
256+
}
257+
}
258+
259+
// Remove items until len(s) <= targetLen
260+
func (s *Set) Shrink(targetLen int) {
261+
s.Lock()
262+
defer s.Unlock()
263+
s.s.Shrink(targetLen)
264+
}
265+
266+
// remove items alphabetically until len(s) <= targetLen
267+
func (s *Set) ShrinkSorted(targetLen int) {
268+
s.Lock()
269+
defer s.Unlock()
270+
s.s.ShrinkSorted(targetLen)
271+
}
272+
273+
// Union is the merger of multiple sets. It returns a new set with all the
274+
// elements present in all the sets that are passed.
275+
func Union(set1 *Set, sets ...strset.Set) *Set {
276+
finalSet := set1.CopyToThreadsafe()
277+
for _, set := range sets {
278+
finalSet.s.Merge(set)
279+
}
280+
return finalSet
281+
}
282+
283+
// UnionThreadsafe is the merger of multiple sets. It returns a new set with all the
284+
// elements present in all the sets that are passed.
285+
func UnionThreadsafe(sets ...*Set) *Set {
286+
finalSet := New()
287+
for _, set := range sets {
288+
set.RLock()
289+
finalSet.s.Merge(set.s)
290+
set.RUnlock()
291+
}
292+
return finalSet
293+
}
294+
295+
// Difference returns a new set which contains items which are in the first
296+
// set but not in the others.
297+
func Difference(set1 *Set, sets ...strset.Set) *Set {
298+
s := set1.CopyToThreadsafe()
299+
for _, set := range sets {
300+
s.s.Subtract(set)
301+
}
302+
return s
303+
}
304+
305+
// DifferenceThreadsafe returns a new set which contains items which are in in the first
306+
// set but not in the others.
307+
func DifferenceThreadsafe(set1 *Set, sets ...*Set) *Set {
308+
s := set1.CopyToThreadsafe()
309+
for _, set := range sets {
310+
set.RLock()
311+
s.s.Subtract(set.s)
312+
set.RUnlock()
313+
}
314+
return s
315+
}
316+
317+
// Intersection returns a new set which contains items that only exist in all
318+
// given sets.
319+
func Intersection(set1 *Set, sets ...strset.Set) *Set {
320+
t := set1.CopyToThreadsafe()
321+
for _, set := range sets {
322+
for item := range t.s {
323+
if _, has := set[item]; !has {
324+
delete(t.s, item)
325+
}
326+
}
327+
}
328+
return t
329+
}
330+
331+
// IntersectionThreadsafe returns a new set which contains items that only exist in all
332+
// given sets.
333+
func IntersectionThreadsafe(set1 *Set, sets ...*Set) *Set {
334+
t := set1.CopyToThreadsafe()
335+
for _, set := range sets {
336+
set.RLock()
337+
for item := range t.s {
338+
if _, has := set.s[item]; !has {
339+
delete(t.s, item)
340+
}
341+
}
342+
set.RUnlock()
343+
}
344+
return t
345+
}
346+
347+
// SymmetricDifferenceThreadsafe returns a new set which s is the difference of items
348+
// which are in one of either, but not in both.
349+
func SymmetricDifferenceThreadsafe(s *Set, t *Set) *Set {
350+
u := DifferenceThreadsafe(s, t)
351+
v := DifferenceThreadsafe(t, s)
352+
return UnionThreadsafe(u, v)
353+
}

0 commit comments

Comments
 (0)