Files

93 lines
1.9 KiB
Go

package bloomfilter
import (
"sync"
"sync/atomic"
"time"
)
// Limiter limits the number of added items.
//
// It is safe using the Limiter from concurrent goroutines.
type Limiter struct {
maxItems int
v atomic.Pointer[limiter]
wg sync.WaitGroup
stopCh chan struct{}
}
// NewLimiter creates new Limiter, which can hold up to maxItems unique items during the given refreshInterval.
func NewLimiter(maxItems int, refreshInterval time.Duration) *Limiter {
l := &Limiter{
maxItems: maxItems,
stopCh: make(chan struct{}),
}
l.v.Store(newLimiter(maxItems))
l.wg.Go(func() {
t := time.NewTicker(refreshInterval)
defer t.Stop()
for {
select {
case <-t.C:
l.v.Store(newLimiter(maxItems))
case <-l.stopCh:
return
}
}
})
return l
}
// MustStop stops the given limiter.
// It is expected that nobody access the limiter at MustStop call.
func (l *Limiter) MustStop() {
close(l.stopCh)
l.wg.Wait()
}
// MaxItems returns the maxItems passed to NewLimiter.
func (l *Limiter) MaxItems() int {
return l.maxItems
}
// CurrentItems return the current number of items registered in l.
func (l *Limiter) CurrentItems() int {
lm := l.v.Load()
n := lm.currentItems.Load()
return int(n)
}
// Add adds h to the limiter.
//
// It is safe calling Add from concurrent goroutines.
//
// True is returned if h is added or already exists in l.
// False is returned if h cannot be added to l, since it already has maxItems unique items.
func (l *Limiter) Add(h uint64) bool {
lm := l.v.Load()
return lm.Add(h)
}
type limiter struct {
currentItems atomic.Uint64
f *filter
}
func newLimiter(maxItems int) *limiter {
return &limiter{
f: newFilter(maxItems),
}
}
func (l *limiter) Add(h uint64) bool {
currentItems := l.currentItems.Load()
if currentItems >= uint64(l.f.maxItems) {
return l.f.Has(h)
}
if l.f.Add(h) {
l.currentItems.Add(1)
}
return true
}