package htmlgame
import (
"sync"
"time"
)
// Limiter allows at most Per requests per key within any Window (a sliding
// window). It is in-memory: a restart forgets it, which only resets the
// window. Keys with no request in the last Window are pruned as it runs.
type Limiter struct {
Per int
Window time.Duration
mu sync.Mutex
hits map[int64][]time.Time
pruned time.Time
}
// Allow records a request for key at now and reports whether it is within
// the limit. A refused request is not recorded.
func (l *Limiter) Allow(key int64, now time.Time) bool {
l.mu.Lock()
defer l.mu.Unlock()
if l.hits == nil {
l.hits = map[int64][]time.Time{}
}
cutoff := now.Add(-l.Window)
if now.Sub(l.pruned) >= l.Window {
for k, ts := range l.hits {
if len(ts) == 0 || !ts[len(ts)-1].After(cutoff) {
delete(l.hits, k)
}
}
l.pruned = now
}
recent := l.hits[key]
i := 0
for i < len(recent) && !recent[i].After(cutoff) {
i++
}
recent = recent[i:]
if len(recent) >= l.Per {
l.hits[key] = recent
return false
}
l.hits[key] = append(recent, now)
return true
}