v0.0.543
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m7s
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m7s
This commit is contained in:
parent
8a92a6cc52
commit
d9517fe73c
@ -26,6 +26,20 @@ func (rb *RingBuffer[T]) Push(item T) {
|
||||
rb.head = (rb.head + 1) % rb.capacity
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[T]) PushPop(item T) *T {
|
||||
if rb.size < rb.capacity {
|
||||
rb.size++
|
||||
rb.items[rb.head] = item
|
||||
rb.head = (rb.head + 1) % rb.capacity
|
||||
return nil
|
||||
} else {
|
||||
prev := rb.items[rb.head]
|
||||
rb.items[rb.head] = item
|
||||
rb.head = (rb.head + 1) % rb.capacity
|
||||
return &prev
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[T]) Peek() (T, bool) {
|
||||
if rb.size == 0 {
|
||||
return *new(T), false
|
||||
@ -96,3 +110,35 @@ func (rb *RingBuffer[T]) Iter2() iter.Seq2[int, T] {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *RingBuffer[T]) Remove(fnEqual func(v T) bool) int {
|
||||
// Mike [2024-11-13]: I *really* tried to write an in-place algorithm to remove elements
|
||||
// But after carful consideration, I left that as an exercise for future readers
|
||||
// It is, suprisingly, non-trivial, especially because the head-ptr must be weirdly updated
|
||||
// And out At() method does not work correctly with {head<>0 && size<capacity}
|
||||
|
||||
dc := 0
|
||||
b := make([]T, rb.capacity)
|
||||
bsize := 0
|
||||
|
||||
for i := 0; i < rb.size; i++ {
|
||||
comp := rb.At(i)
|
||||
if fnEqual(comp) {
|
||||
dc++
|
||||
} else {
|
||||
b[bsize] = comp
|
||||
bsize++
|
||||
}
|
||||
}
|
||||
|
||||
if dc == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
rb.items = b
|
||||
rb.size = bsize
|
||||
rb.head = bsize % rb.capacity
|
||||
|
||||
return dc
|
||||
|
||||
}
|
||||
|
447
dataext/ringBuffer_test.go
Normal file
447
dataext/ringBuffer_test.go
Normal file
@ -0,0 +1,447 @@
|
||||
package dataext
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRingBufferPushAddsItem(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
if rb.Size() != 1 {
|
||||
t.Errorf("Expected size 1, got %d", rb.Size())
|
||||
}
|
||||
if item, _ := rb.Peek(); item != 1 {
|
||||
t.Errorf("Expected item 1, got %d", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferPushPopReturnsOldestItem(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
if item := rb.PushPop(4); item == nil || *item != 1 {
|
||||
t.Errorf("Expected item 1, got %v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferPeekReturnsLastPushedItem(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
if item, _ := rb.Peek(); item != 2 {
|
||||
t.Errorf("Expected item 2, got %d", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferOverflow1(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1) // overriden
|
||||
rb.Push(2) // overriden
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
rb.Push(5)
|
||||
rb.Push(7)
|
||||
if rb.Size() != 5 {
|
||||
t.Errorf("Expected size 4, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{3, 9, 4, 5, 7}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferItemsReturnsAllItems(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
items := rb.Items()
|
||||
expected := []int{1, 2, 3}
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferClearEmptiesBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Clear()
|
||||
if rb.Size() != 0 {
|
||||
t.Errorf("Expected size 0, got %d", rb.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferIsFullReturnsTrueWhenFull(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
if !rb.IsFull() {
|
||||
t.Errorf("Expected buffer to be full")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferAtReturnsCorrectItem(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
if item := rb.At(1); item != 2 {
|
||||
t.Errorf("Expected item 2, got %d", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferGetReturnsCorrectItem(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
if item, ok := rb.Get(1); !ok || item != 2 {
|
||||
t.Errorf("Expected item 2, got %d", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
rb.Push(2)
|
||||
rb.Push(4)
|
||||
removed := rb.Remove(func(v int) bool { return v == 2 })
|
||||
if removed != 2 {
|
||||
t.Errorf("Expected 2 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 3 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 3, 4}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems2(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
rb.Push(2)
|
||||
rb.Push(4)
|
||||
removed := rb.Remove(func(v int) bool { return v == 3 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 2 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 4 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 2, 2, 4}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems3(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
removed := rb.Remove(func(v int) bool { return v == 3 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 2 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 4 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 2, 9, 4}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems4(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1) // overriden
|
||||
rb.Push(2) // overriden
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
rb.Push(5)
|
||||
rb.Push(7)
|
||||
removed := rb.Remove(func(v int) bool { return v == 7 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 1 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 4 {
|
||||
t.Errorf("Expected size 4, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{3, 9, 4, 5}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems5(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1) // overriden
|
||||
rb.Push(2) // overriden
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
rb.Push(5)
|
||||
rb.Push(7)
|
||||
removed := rb.Remove(func(v int) bool { return v == 3 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 1 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 4 {
|
||||
t.Errorf("Expected size 4, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{9, 4, 5, 7}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems6(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1) // overriden
|
||||
rb.Push(2) // overriden
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
rb.Push(5)
|
||||
rb.Push(7)
|
||||
removed := rb.Remove(func(v int) bool { return v == 1 })
|
||||
if removed != 0 {
|
||||
t.Errorf("Expected 0 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 5 {
|
||||
t.Errorf("Expected size 5, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{3, 9, 4, 5, 7}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
if !rb.IsFull() {
|
||||
t.Errorf("Expected buffer to not be full")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveDeletesMatchingItems7(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1) // overriden
|
||||
rb.Push(2) // overriden
|
||||
rb.Push(3)
|
||||
rb.Push(9)
|
||||
rb.Push(4)
|
||||
rb.Push(5)
|
||||
rb.Push(7)
|
||||
removed := rb.Remove(func(v int) bool { return v == 9 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 1 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 4 {
|
||||
t.Errorf("Expected size 4, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{3, 4, 5, 7}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
if rb.IsFull() {
|
||||
t.Errorf("Expected buffer to not be full")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferAddItemsToFullRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
rb.Push(4)
|
||||
if rb.Size() != 3 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{2, 3, 4}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferAddItemsToNonFullRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
if rb.Size() != 2 {
|
||||
t.Errorf("Expected size 2, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 2}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveItemsFromNonFullRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
removed := rb.Remove(func(v int) bool { return v == 1 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 1 item removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 1 {
|
||||
t.Errorf("Expected size 1, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{2}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveItemsFromFullRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
removed := rb.Remove(func(v int) bool { return v == 2 })
|
||||
if removed != 1 {
|
||||
t.Errorf("Expected 1 item removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 2 {
|
||||
t.Errorf("Expected size 2, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 3}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveMultipleItemsFromRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](5)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
rb.Push(2)
|
||||
rb.Push(4)
|
||||
removed := rb.Remove(func(v int) bool { return v == 2 })
|
||||
if removed != 2 {
|
||||
t.Errorf("Expected 2 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 3 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
expected := []int{1, 3, 4}
|
||||
items := rb.Items()
|
||||
for i, item := range items {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveAllItemsFromRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
removed := rb.Remove(func(v int) bool { return true })
|
||||
if removed != 3 {
|
||||
t.Errorf("Expected 3 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 0 {
|
||||
t.Errorf("Expected size 0, got %d", rb.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferRemoveNoItemsFromRingBuffer(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
removed := rb.Remove(func(v int) bool { return false })
|
||||
if removed != 0 {
|
||||
t.Errorf("Expected 0 items removed, got %d", removed)
|
||||
}
|
||||
if rb.Size() != 3 {
|
||||
t.Errorf("Expected size 3, got %d", rb.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferIteratesOverAllItems(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
expected := []int{1, 2, 3}
|
||||
i := 0
|
||||
for item := range rb.Iter() {
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != len(expected) {
|
||||
t.Errorf("Expected to iterate over %d items, but iterated over %d", len(expected), i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRingBufferIter2IteratesOverAllItemsWithIndices(t *testing.T) {
|
||||
rb := NewRingBuffer[int](3)
|
||||
rb.Push(1)
|
||||
rb.Push(2)
|
||||
rb.Push(3)
|
||||
expected := []int{1, 2, 3}
|
||||
i := 0
|
||||
for index, item := range rb.Iter2() {
|
||||
if index != i {
|
||||
t.Errorf("Expected index %d, got %d", i, index)
|
||||
}
|
||||
if item != expected[i] {
|
||||
t.Errorf("Expected item %d, got %d", expected[i], item)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != len(expected) {
|
||||
t.Errorf("Expected to iterate over %d items, but iterated over %d", len(expected), i)
|
||||
}
|
||||
}
|
@ -7,6 +7,10 @@ type SyncMap[TKey comparable, TData any] struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewSyncMap[TKey comparable, TData any]() *SyncMap[TKey, TData] {
|
||||
return &SyncMap[TKey, TData]{data: make(map[TKey]TData), lock: sync.Mutex{}}
|
||||
}
|
||||
|
||||
func (s *SyncMap[TKey, TData]) Set(key TKey, data TData) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
143
dataext/syncRingSet.go
Normal file
143
dataext/syncRingSet.go
Normal file
@ -0,0 +1,143 @@
|
||||
package dataext
|
||||
|
||||
import "sync"
|
||||
|
||||
type SyncRingSet[TData comparable] struct {
|
||||
data map[TData]bool
|
||||
lock sync.Mutex
|
||||
ring *RingBuffer[TData]
|
||||
}
|
||||
|
||||
func NewSyncRingSet[TData comparable](capacity int) *SyncRingSet[TData] {
|
||||
return &SyncRingSet[TData]{
|
||||
data: make(map[TData]bool, capacity+1),
|
||||
lock: sync.Mutex{},
|
||||
ring: NewRingBuffer[TData](capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds `value` to the set
|
||||
// returns true if the value was actually inserted (value did not exist beforehand)
|
||||
// returns false if the value already existed
|
||||
func (s *SyncRingSet[TData]) Add(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
_, existsInPreState := s.data[value]
|
||||
if existsInPreState {
|
||||
return false
|
||||
}
|
||||
|
||||
prev := s.ring.PushPop(value)
|
||||
|
||||
s.data[value] = true
|
||||
if prev != nil {
|
||||
delete(s.data, *prev)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SyncRingSet[TData]) AddAll(values []TData) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
_, existsInPreState := s.data[value]
|
||||
if existsInPreState {
|
||||
continue
|
||||
}
|
||||
|
||||
prev := s.ring.PushPop(value)
|
||||
|
||||
s.data[value] = true
|
||||
if prev != nil {
|
||||
delete(s.data, *prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SyncRingSet[TData]) Remove(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
_, existsInPreState := s.data[value]
|
||||
if !existsInPreState {
|
||||
return false
|
||||
}
|
||||
|
||||
delete(s.data, value)
|
||||
s.ring.Remove(func(v TData) bool { return value == v })
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SyncRingSet[TData]) RemoveAll(values []TData) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
delete(s.data, value)
|
||||
s.ring.Remove(func(v TData) bool { return value == v })
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SyncRingSet[TData]) Contains(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
_, ok := s.data[value]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *SyncRingSet[TData]) Get() []TData {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
r := make([]TData, 0, len(s.data))
|
||||
|
||||
for k := range s.data {
|
||||
r = append(r, k)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// AddIfNotContains
|
||||
// returns true if the value was actually added (value did not exist beforehand)
|
||||
// returns false if the value already existed
|
||||
func (s *SyncRingSet[TData]) AddIfNotContains(key TData) bool {
|
||||
return s.Add(key)
|
||||
}
|
||||
|
||||
// RemoveIfContains
|
||||
// returns true if the value was actually removed (value did exist beforehand)
|
||||
// returns false if the value did not exist in the set
|
||||
func (s *SyncRingSet[TData]) RemoveIfContains(key TData) bool {
|
||||
return s.Remove(key)
|
||||
}
|
@ -7,8 +7,12 @@ type SyncSet[TData comparable] struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewSyncSet[TData comparable]() *SyncSet[TData] {
|
||||
return &SyncSet[TData]{data: make(map[TData]bool), lock: sync.Mutex{}}
|
||||
}
|
||||
|
||||
// Add adds `value` to the set
|
||||
// returns true if the value was actually inserted
|
||||
// returns true if the value was actually inserted (value did not exist beforehand)
|
||||
// returns false if the value already existed
|
||||
func (s *SyncSet[TData]) Add(value TData) bool {
|
||||
s.lock.Lock()
|
||||
@ -19,9 +23,12 @@ func (s *SyncSet[TData]) Add(value TData) bool {
|
||||
}
|
||||
|
||||
_, existsInPreState := s.data[value]
|
||||
s.data[value] = true
|
||||
if existsInPreState {
|
||||
return false
|
||||
}
|
||||
|
||||
return !existsInPreState
|
||||
s.data[value] = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SyncSet[TData]) AddAll(values []TData) {
|
||||
@ -37,6 +44,36 @@ func (s *SyncSet[TData]) AddAll(values []TData) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SyncSet[TData]) Remove(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
_, existsInPreState := s.data[value]
|
||||
if !existsInPreState {
|
||||
return false
|
||||
}
|
||||
|
||||
delete(s.data, value)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SyncSet[TData]) RemoveAll(values []TData) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.data == nil {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
delete(s.data, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SyncSet[TData]) Contains(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
@ -66,3 +103,17 @@ func (s *SyncSet[TData]) Get() []TData {
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// AddIfNotContains
|
||||
// returns true if the value was actually added (value did not exist beforehand)
|
||||
// returns false if the value already existed
|
||||
func (s *SyncSet[TData]) AddIfNotContains(key TData) bool {
|
||||
return s.Add(key)
|
||||
}
|
||||
|
||||
// RemoveIfContains
|
||||
// returns true if the value was actually removed (value did exist beforehand)
|
||||
// returns false if the value did not exist in the set
|
||||
func (s *SyncSet[TData]) RemoveIfContains(key TData) bool {
|
||||
return s.Remove(key)
|
||||
}
|
||||
|
18
go.mod
18
go.mod
@ -9,15 +9,15 @@ require (
|
||||
github.com/rs/xid v1.6.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
go.mongodb.org/mongo-driver v1.17.1
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/term v0.25.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.org/x/term v0.26.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/jung-kurt/gofpdf v1.16.2
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sync v0.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -36,7 +36,7 @@ require (
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
@ -51,10 +51,10 @@ require (
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
golang.org/x/arch v0.11.0 // indirect
|
||||
golang.org/x/image v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/arch v0.12.0 // indirect
|
||||
golang.org/x/image v0.22.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.37.6 // indirect
|
||||
|
18
go.sum
18
go.sum
@ -68,6 +68,8 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
@ -131,24 +133,34 @@ go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHy
|
||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
|
||||
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -160,16 +172,22 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
@ -1,5 +1,5 @@
|
||||
package goext
|
||||
|
||||
const GoextVersion = "0.0.542"
|
||||
const GoextVersion = "0.0.543"
|
||||
|
||||
const GoextVersionTimestamp = "2024-11-07T13:13:12+0100"
|
||||
const GoextVersionTimestamp = "2024-11-13T15:03:51+0100"
|
||||
|
Loading…
Reference in New Issue
Block a user