package set import ( "sync" ) // Set defines a thread safe set data structure. type Set struct { set l sync.RWMutex // we name it because we don't want to expose it } // New creates and initialize a new Set. It's accept a variable number of // arguments to populate the initial set. If nothing passed a Set with zero // size is created. func New(items ...interface{}) *Set { s := &Set{} s.m = make(map[interface{}]struct{}) // Ensure interface compliance var _ Interface = s s.Add(items...) return s } // New creates and initalizes a new Set interface. It accepts a variable // number of arguments to populate the initial set. If nothing is passed a // zero size Set based on the struct is created. func (s *Set) New(items ...interface{}) Interface { return New(items...) } // Add includes the specified items (one or more) to the set. The underlying // Set s is modified. If passed nothing it silently returns. func (s *Set) Add(items ...interface{}) { if len(items) == 0 { return } s.l.Lock() defer s.l.Unlock() for _, item := range items { s.m[item] = keyExists } } // Remove deletes the specified items from the set. The underlying Set s is // modified. If passed nothing it silently returns. func (s *Set) Remove(items ...interface{}) { if len(items) == 0 { return } s.l.Lock() defer s.l.Unlock() for _, item := range items { delete(s.m, item) } } // Pop deletes and return an item from the set. The underlying Set s is // modified. If set is empty, nil is returned. func (s *Set) Pop() interface{} { s.l.RLock() for item := range s.m { s.l.RUnlock() s.l.Lock() delete(s.m, item) s.l.Unlock() return item } s.l.RUnlock() return nil } // Has looks for the existence of items passed. It returns false if nothing is // passed. For multiple items it returns true only if all of the items exist. func (s *Set) Has(items ...interface{}) bool { // assume checked for empty item, which not exist if len(items) == 0 { return false } s.l.RLock() defer s.l.RUnlock() has := true for _, item := range items { if _, has = s.m[item]; !has { break } } return has } // Size returns the number of items in a set. func (s *Set) Size() int { s.l.RLock() defer s.l.RUnlock() l := len(s.m) return l } // Clear removes all items from the set. func (s *Set) Clear() { s.l.Lock() defer s.l.Unlock() s.m = make(map[interface{}]struct{}) } // IsEqual test whether s and t are the same in size and have the same items. func (s *Set) IsEqual(t Interface) bool { s.l.RLock() defer s.l.RUnlock() // Force locking only if given set is threadsafe. if conv, ok := t.(*Set); ok { conv.l.RLock() defer conv.l.RUnlock() } equal := true if equal = len(s.m) == t.Size(); equal { t.Each(func(item interface{}) (equal bool) { _, equal = s.m[item] return }) } return equal } // IsSubset tests whether t is a subset of s. func (s *Set) IsSubset(t Interface) (subset bool) { s.l.RLock() defer s.l.RUnlock() subset = true t.Each(func(item interface{}) bool { _, subset = s.m[item] return subset }) return } // Each traverses the items in the Set, calling the provided function for each // set member. Traversal will continue until all items in the Set have been // visited, or if the closure returns false. func (s *Set) Each(f func(item interface{}) bool) { s.l.RLock() defer s.l.RUnlock() for item := range s.m { if !f(item) { break } } } // List returns a slice of all items. There is also StringSlice() and // IntSlice() methods for returning slices of type string or int. func (s *Set) List() []interface{} { s.l.RLock() defer s.l.RUnlock() list := make([]interface{}, 0, len(s.m)) for item := range s.m { list = append(list, item) } return list } // Copy returns a new Set with a copy of s. func (s *Set) Copy() Interface { return New(s.List()...) } // Merge is like Union, however it modifies the current set it's applied on // with the given t set. func (s *Set) Merge(t Interface) { s.l.Lock() defer s.l.Unlock() t.Each(func(item interface{}) bool { s.m[item] = keyExists return true }) }