-
Notifications
You must be signed in to change notification settings - Fork 0
/
limiter.go
73 lines (56 loc) · 1.4 KB
/
limiter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package limiter
import (
tb "github.com/sadfun/limiter/bucket"
"github.com/sadfun/limiter/storage"
"slices"
"time"
)
type Limiter[K comparable] struct {
storage storage.Storage[K]
limit int
t time.Duration
}
type Config[K comparable] struct {
Limit int
Duration time.Duration
Storage storage.Storage[K]
}
func fillDefaultConfig[K comparable](config *Config[K]) {
if config.Storage == nil {
config.Storage = storage.NewMapStorage[K]()
}
}
func NewLimiter[K comparable](config *Config[K]) *Limiter[K] {
fillDefaultConfig(config)
return &Limiter[K]{
storage: config.Storage,
limit: config.Limit,
t: config.Duration,
}
}
func (limiter *Limiter[K]) Use(key K) (ok bool) {
return limiter.UseN(key, 1)
}
func (limiter *Limiter[K]) UseN(key K, n int) (ok bool) {
if limiter.limit < n {
return false
}
now := time.Now()
limiter.storage.Update(key, func(bucket tb.Bucket) (newBucket tb.Bucket) {
limiter.dropExpiredTokens(now, &bucket)
if (len(bucket.Tokens) + n) > limiter.limit {
return bucket
}
slices.Grow(bucket.Tokens, limiter.limit-len(bucket.Tokens))
for i := 0; i < n; i++ {
bucket.Tokens = append(bucket.Tokens, now.UnixNano())
}
ok = true
return bucket
})
return ok
}
func (limiter *Limiter[K]) dropExpiredTokens(now time.Time, tb *tb.Bucket) {
i, _ := slices.BinarySearch(tb.Tokens, now.Add(-limiter.t).UnixNano())
tb.Tokens = tb.Tokens[i:]
}