forked from LyricTian/captcha
/
memory.go
120 lines (103 loc) · 1.88 KB
/
memory.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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package store
import (
"container/list"
"sync"
"time"
)
// NewMemoryStore An internal store for captcha ids and their values.
func NewMemoryStore(gcInterval, expiration time.Duration) Store {
mstore := &memoryStore{
data: make(map[string]*dataItem),
list: list.New(),
ticker: time.NewTicker(gcInterval),
expiration: expiration,
}
go mstore.gc()
return mstore
}
type dataItem struct {
id string
value []byte
expiredAt time.Time
}
// memoryStore memory store
type memoryStore struct {
sync.RWMutex
data map[string]*dataItem
list *list.List
ticker *time.Ticker
expiration time.Duration
}
func (s *memoryStore) gc() {
for range s.ticker.C {
s.RLock()
e := s.list.Front()
s.RUnlock()
for e != nil {
item := e.Value.(*dataItem)
if item.expiredAt.Before(time.Now()) {
s.Lock()
s.list.Remove(e)
delete(s.data, item.id)
e = e.Next()
s.Unlock()
} else {
break
}
}
}
}
func (s *memoryStore) Set(id string, digits []byte) {
s.Lock()
defer s.Unlock()
expiredAt := time.Now().Add(s.expiration)
if _, ok := s.data[id]; ok {
e := s.list.Front()
for e != nil {
item := e.Value.(*dataItem)
if item.id == id {
item.value = digits
item.expiredAt = expiredAt
s.list.MoveToBack(e)
break
}
e = e.Next()
}
return
}
item := &dataItem{
id: id,
value: digits,
expiredAt: expiredAt,
}
s.data[id] = item
s.list.PushBack(item)
}
func (s *memoryStore) Get(id string, clear bool) []byte {
s.RLock()
item, ok := s.data[id]
s.RUnlock()
if !ok {
return nil
}
if clear {
s.remove(id)
}
return item.value
}
func (s *memoryStore) remove(id string) {
s.RLock()
e := s.list.Front()
for e != nil {
item := e.Value.(*dataItem)
if item.id == id {
break
}
e = e.Next()
}
s.RUnlock()
s.Lock()
s.list.Remove(e)
delete(s.data, id)
s.Unlock()
}