1package set
2
3import "sync"
4
5// Set defines a thread safe set data structure.
6type Set struct {
7	set
8	l sync.RWMutex // we name it because we don't want to expose it
9}
10
11// New creates and initialize a new Set. It's accept a variable number of
12// arguments to populate the initial set. If nothing passed a Set with zero
13// size is created.
14func newTS() *Set {
15	s := &Set{}
16	s.m = make(map[interface{}]struct{})
17
18	// Ensure interface compliance
19	var _ Interface = s
20
21	return s
22}
23
24// Add includes the specified items (one or more) to the set. The underlying
25// Set s is modified. If passed nothing it silently returns.
26func (s *Set) Add(items ...interface{}) {
27	if len(items) == 0 {
28		return
29	}
30
31	s.l.Lock()
32	defer s.l.Unlock()
33
34	for _, item := range items {
35		s.m[item] = keyExists
36	}
37}
38
39// Remove deletes the specified items from the set.  The underlying Set s is
40// modified. If passed nothing it silently returns.
41func (s *Set) Remove(items ...interface{}) {
42	if len(items) == 0 {
43		return
44	}
45
46	s.l.Lock()
47	defer s.l.Unlock()
48
49	for _, item := range items {
50		delete(s.m, item)
51	}
52}
53
54// Pop  deletes and return an item from the set. The underlying Set s is
55// modified. If set is empty, nil is returned.
56func (s *Set) Pop() interface{} {
57	s.l.RLock()
58	for item := range s.m {
59		s.l.RUnlock()
60		s.l.Lock()
61		delete(s.m, item)
62		s.l.Unlock()
63		return item
64	}
65	s.l.RUnlock()
66	return nil
67}
68
69// Has looks for the existence of items passed. It returns false if nothing is
70// passed. For multiple items it returns true only if all of  the items exist.
71func (s *Set) Has(items ...interface{}) bool {
72	// assume checked for empty item, which not exist
73	if len(items) == 0 {
74		return false
75	}
76
77	s.l.RLock()
78	defer s.l.RUnlock()
79
80	has := true
81	for _, item := range items {
82		if _, has = s.m[item]; !has {
83			break
84		}
85	}
86	return has
87}
88
89// Size returns the number of items in a set.
90func (s *Set) Size() int {
91	s.l.RLock()
92	defer s.l.RUnlock()
93
94	l := len(s.m)
95	return l
96}
97
98// Clear removes all items from the set.
99func (s *Set) Clear() {
100	s.l.Lock()
101	defer s.l.Unlock()
102
103	s.m = make(map[interface{}]struct{})
104}
105
106// IsEqual test whether s and t are the same in size and have the same items.
107func (s *Set) IsEqual(t Interface) bool {
108	s.l.RLock()
109	defer s.l.RUnlock()
110
111	// Force locking only if given set is threadsafe.
112	if conv, ok := t.(*Set); ok {
113		conv.l.RLock()
114		defer conv.l.RUnlock()
115	}
116
117	// return false if they are no the same size
118	if sameSize := len(s.m) == t.Size(); !sameSize {
119		return false
120	}
121
122	equal := true
123	t.Each(func(item interface{}) bool {
124		_, equal = s.m[item]
125		return equal // if false, Each() will end
126	})
127
128	return equal
129}
130
131// IsSubset tests whether t is a subset of s.
132func (s *Set) IsSubset(t Interface) (subset bool) {
133	s.l.RLock()
134	defer s.l.RUnlock()
135
136	subset = true
137
138	t.Each(func(item interface{}) bool {
139		_, subset = s.m[item]
140		return subset
141	})
142
143	return
144}
145
146// Each traverses the items in the Set, calling the provided function for each
147// set member. Traversal will continue until all items in the Set have been
148// visited, or if the closure returns false.
149func (s *Set) Each(f func(item interface{}) bool) {
150	s.l.RLock()
151	defer s.l.RUnlock()
152
153	for item := range s.m {
154		if !f(item) {
155			break
156		}
157	}
158}
159
160// List returns a slice of all items. There is also StringSlice() and
161// IntSlice() methods for returning slices of type string or int.
162func (s *Set) List() []interface{} {
163	s.l.RLock()
164	defer s.l.RUnlock()
165
166	list := make([]interface{}, 0, len(s.m))
167
168	for item := range s.m {
169		list = append(list, item)
170	}
171
172	return list
173}
174
175// Copy returns a new Set with a copy of s.
176func (s *Set) Copy() Interface {
177	u := newTS()
178	for item := range s.m {
179		u.Add(item)
180	}
181	return u
182}
183
184// Merge is like Union, however it modifies the current set it's applied on
185// with the given t set.
186func (s *Set) Merge(t Interface) {
187	s.l.Lock()
188	defer s.l.Unlock()
189
190	t.Each(func(item interface{}) bool {
191		s.m[item] = keyExists
192		return true
193	})
194}
195