136 lines
2.8 KiB
Go
136 lines
2.8 KiB
Go
// Copyright (c) 2023 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package exsync
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
type empty struct{}
|
|
|
|
var emptyVal = empty{}
|
|
|
|
// Set is a wrapper around a map[T]struct{} with a built-in mutex.
|
|
type Set[T comparable] struct {
|
|
m map[T]empty
|
|
l sync.RWMutex
|
|
}
|
|
|
|
// NewSet constructs a Set with an empty map.
|
|
func NewSet[T comparable]() *Set[T] {
|
|
return NewSetWithMap[T](make(map[T]empty))
|
|
}
|
|
|
|
// NewSetWithSize constructs a Set with a map that has been allocated the given amount of space.
|
|
func NewSetWithSize[T comparable](size int) *Set[T] {
|
|
return NewSetWithMap[T](make(map[T]empty, size))
|
|
}
|
|
|
|
// NewSetWithMap constructs a Set with the given map. Accessing the map directly after passing it here is not safe.
|
|
func NewSetWithMap[T comparable](m map[T]empty) *Set[T] {
|
|
return &Set[T]{m: m}
|
|
}
|
|
|
|
// NewSetWithItems constructs a Set with items from the given slice pre-filled.
|
|
// The slice is not modified or used after the function returns, so using it after this is safe.
|
|
func NewSetWithItems[T comparable](items []T) *Set[T] {
|
|
s := NewSetWithSize[T](len(items))
|
|
for _, item := range items {
|
|
s.m[item] = emptyVal
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Add adds an item to the set. The return value is true if the item was added to the set, or false otherwise.
|
|
func (s *Set[T]) Add(item T) bool {
|
|
if s == nil {
|
|
return false
|
|
}
|
|
s.l.Lock()
|
|
_, exists := s.m[item]
|
|
if exists {
|
|
s.l.Unlock()
|
|
return false
|
|
}
|
|
s.m[item] = emptyVal
|
|
s.l.Unlock()
|
|
return true
|
|
}
|
|
|
|
// Has checks if the given item is in the set.
|
|
func (s *Set[T]) Has(item T) bool {
|
|
if s == nil {
|
|
return false
|
|
}
|
|
s.l.RLock()
|
|
_, exists := s.m[item]
|
|
s.l.RUnlock()
|
|
return exists
|
|
}
|
|
|
|
// Pop removes the given item from the set. The return value is true if the item was in the set, or false otherwise.
|
|
func (s *Set[T]) Pop(item T) bool {
|
|
if s == nil {
|
|
return false
|
|
}
|
|
s.l.Lock()
|
|
_, exists := s.m[item]
|
|
if exists {
|
|
delete(s.m, item)
|
|
}
|
|
s.l.Unlock()
|
|
return exists
|
|
}
|
|
|
|
// Remove removes the given item from the set.
|
|
func (s *Set[T]) Remove(item T) {
|
|
if s == nil {
|
|
return
|
|
}
|
|
s.l.Lock()
|
|
delete(s.m, item)
|
|
s.l.Unlock()
|
|
}
|
|
|
|
// ReplaceAll replaces this set with the given set. If the given set is nil, the set is cleared.
|
|
func (s *Set[T]) ReplaceAll(newSet *Set[T]) {
|
|
if s == nil {
|
|
return
|
|
}
|
|
s.l.Lock()
|
|
if newSet == nil {
|
|
s.m = make(map[T]empty)
|
|
} else {
|
|
s.m = newSet.m
|
|
}
|
|
s.l.Unlock()
|
|
}
|
|
|
|
func (s *Set[T]) Size() int {
|
|
if s == nil {
|
|
return 0
|
|
}
|
|
s.l.RLock()
|
|
size := len(s.m)
|
|
s.l.RUnlock()
|
|
return size
|
|
}
|
|
|
|
func (s *Set[T]) AsList() []T {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
s.l.RLock()
|
|
list := make([]T, len(s.m))
|
|
i := 0
|
|
for item := range s.m {
|
|
list[i] = item
|
|
i++
|
|
}
|
|
s.l.RUnlock()
|
|
return list
|
|
}
|