@@ -3,45 +3,35 @@ package cache
3
3
import (
4
4
"sync"
5
5
"time"
6
+
7
+ "github.com/Code-Hex/go-generics-cache/lfu"
8
+ "github.com/Code-Hex/go-generics-cache/lru"
9
+ "github.com/Code-Hex/go-generics-cache/simple"
6
10
)
7
11
8
- // Cache is a common-cache interface.
9
- type Cache [K comparable , V any ] interface {
12
+ // Interface is a common-cache interface.
13
+ type Interface [K comparable , V any ] interface {
10
14
Get (key K ) (value V , ok bool )
11
- Set (key K , val V , opts ... ItemOption )
15
+ Set (key K , val V )
12
16
Keys () []K
13
17
Delete (key K )
14
- Contains (key K ) bool
15
18
}
16
19
20
+ var (
21
+ _ Interface [any , any ] = (* simple.Cache [any , any ])(nil )
22
+ _ Interface [any , any ] = (* lru.Cache [any , any ])(nil )
23
+ _ Interface [any , any ] = (* lfu.Cache [any , any ])(nil )
24
+ )
25
+
17
26
// Item is an item
18
27
type Item [K comparable , V any ] struct {
19
- Key K
20
- Value V
21
- ReferenceCount int
22
- Expiration time.Duration
23
- CreatedAt time.Time
24
- ReferencedAt time.Time
25
- }
26
-
27
- // Referenced increments a reference counter and updates `ReferencedAt`
28
- // to current time.
29
- func (i * Item [K , V ]) Referenced () {
30
- i .ReferenceCount ++
31
- i .ReferencedAt = nowFunc ()
28
+ Key K
29
+ Value V
30
+ Expiration time.Duration
32
31
}
33
32
34
33
var nowFunc = time .Now
35
34
36
- // HasExpired returns true if the item has expired.
37
- // If the item's expiration is zero value, returns false.
38
- func (i Item [K , T ]) HasExpired () bool {
39
- if i .Expiration <= 0 {
40
- return false
41
- }
42
- return i .CreatedAt .Add (i .Expiration ).Before (nowFunc ())
43
- }
44
-
45
35
// ItemOption is an option for cache item.
46
36
type ItemOption func (* itemOptions )
47
37
@@ -50,42 +40,155 @@ type itemOptions struct {
50
40
}
51
41
52
42
// WithExpiration is an option to set expiration time for any items.
43
+ // If the expiration is zero or negative value, it treats as w/o expiration.
53
44
func WithExpiration (exp time.Duration ) ItemOption {
54
45
return func (o * itemOptions ) {
55
46
o .expiration = exp
56
47
}
57
48
}
58
49
59
- // NewItem creates a new item with specified any options.
60
- func NewItem [K comparable , V any ](key K , val V , opts ... ItemOption ) * Item [K , V ] {
50
+ // newItem creates a new item with specified any options.
51
+ func newItem [K comparable , V any ](key K , val V , opts ... ItemOption ) * Item [K , V ] {
61
52
o := new (itemOptions )
62
53
for _ , optFunc := range opts {
63
54
optFunc (o )
64
55
}
65
- now := nowFunc ()
66
56
return & Item [K , V ]{
67
- Key : key ,
68
- Value : val ,
69
- ReferenceCount : 1 ,
70
- Expiration : o .expiration ,
71
- CreatedAt : now ,
72
- ReferencedAt : now ,
57
+ Key : key ,
58
+ Value : val ,
59
+ Expiration : o .expiration ,
60
+ }
61
+ }
62
+
63
+ // Cache is a cache.
64
+ type Cache [K comparable , V any ] struct {
65
+ cache Interface [K , * Item [K , V ]]
66
+ expirations map [K ]chan struct {}
67
+ // mu is used to do lock in some method process.
68
+ mu sync.RWMutex
69
+ }
70
+
71
+ // Option is an option for cache.
72
+ type Option [K comparable , V any ] func (* options [K , V ])
73
+
74
+ type options [K comparable , V any ] struct {
75
+ cache Interface [K , * Item [K , V ]]
76
+ }
77
+
78
+ func newOptions [K comparable , V any ]() * options [K , V ] {
79
+ return & options [K , V ]{
80
+ cache : simple .NewCache [K , * Item [K , V ]](),
81
+ }
82
+ }
83
+
84
+ // AsLRU is an option to make a new Cache as LRU algorithm.
85
+ func AsLRU [K comparable , V any ](opts ... lru.Option ) Option [K , V ] {
86
+ return func (o * options [K , V ]) {
87
+ o .cache = lru .NewCache [K , * Item [K , V ]](opts ... )
88
+ }
89
+ }
90
+
91
+ // AsLFU is an option to make a new Cache as LFU algorithm.
92
+ func AsLFU [K comparable , V any ](opts ... lfu.Option ) Option [K , V ] {
93
+ return func (o * options [K , V ]) {
94
+ o .cache = lfu .NewCache [K , * Item [K , V ]](opts ... )
95
+ }
96
+ }
97
+
98
+ // New creates a new Cache.
99
+ func New [K comparable , V any ](opts ... Option [K , V ]) * Cache [K , V ] {
100
+ o := newOptions [K , V ]()
101
+ for _ , optFunc := range opts {
102
+ optFunc (o )
103
+ }
104
+ return & Cache [K , V ]{
105
+ cache : o .cache ,
106
+ expirations : make (map [K ]chan struct {}, 0 ),
107
+ }
108
+ }
109
+
110
+ // Get looks up a key's value from the cache.
111
+ func (c * Cache [K , V ]) Get (key K ) (value V , ok bool ) {
112
+ c .mu .RLock ()
113
+ defer c .mu .RUnlock ()
114
+ item , ok := c .cache .Get (key )
115
+ if ! ok {
116
+ return
117
+ }
118
+ return item .Value , true
119
+ }
120
+
121
+ // Set sets a value to the cache with key. replacing any existing value.
122
+ func (c * Cache [K , V ]) Set (key K , val V , opts ... ItemOption ) {
123
+ c .mu .Lock ()
124
+ defer c .mu .Unlock ()
125
+ item := newItem (key , val , opts ... )
126
+ if item .Expiration <= 0 {
127
+ c .cache .Set (key , item )
128
+ return
129
+ }
130
+
131
+ if _ , ok := c .cache .Get (key ); ok {
132
+ c .doneWatchExpiration (key )
73
133
}
134
+
135
+ c .cache .Set (key , item )
136
+ c .installExpirationWatcher (item .Key , item .Expiration )
137
+ }
138
+
139
+ func (c * Cache [K , V ]) installExpirationWatcher (key K , exp time.Duration ) {
140
+ done := make (chan struct {})
141
+ c .expirations [key ] = done
142
+ go func () {
143
+ select {
144
+ case <- time .After (exp ):
145
+ c .Delete (key )
146
+ case <- done :
147
+ }
148
+ }()
149
+ }
150
+
151
+ func (c * Cache [K , V ]) doneWatchExpiration (key K ) {
152
+ if ch , ok := c .expirations [key ]; ok {
153
+ close (ch )
154
+ }
155
+ }
156
+
157
+ // Keys returns the keys of the cache. the order is relied on algorithms.
158
+ func (c * Cache [K , V ]) Keys () []K {
159
+ c .mu .RLock ()
160
+ defer c .mu .RUnlock ()
161
+ return c .cache .Keys ()
162
+ }
163
+
164
+ // Delete deletes the item with provided key from the cache.
165
+ func (c * Cache [K , V ]) Delete (key K ) {
166
+ c .mu .Lock ()
167
+ defer c .mu .Unlock ()
168
+ c .cache .Delete (key )
169
+ }
170
+
171
+ // Contains reports whether key is within cache.
172
+ func (c * Cache [K , V ]) Contains (key K ) bool {
173
+ c .mu .RLock ()
174
+ defer c .mu .RUnlock ()
175
+ _ , ok := c .cache .Get (key )
176
+ return ok
74
177
}
75
178
76
179
// NumberCache is a in-memory cache which is able to store only Number constraint.
77
180
type NumberCache [K comparable , V Number ] struct {
78
- Cache [K , V ]
181
+ * Cache [K , V ]
79
182
// nmu is used to do lock in Increment/Decrement process.
80
- // Note that this must be here as a separate mutex because mu in Cache struct is Locked in GetItem ,
183
+ // Note that this must be here as a separate mutex because mu in Cache struct is Locked in Get ,
81
184
// and if we call mu.Lock in Increment/Decrement, it will cause deadlock.
82
185
nmu sync.Mutex
83
186
}
84
187
85
188
// NewNumber creates a new cache for Number constraint.
86
- func NewNumber [K comparable , V Number ](baseCache Cache [K , V ]) * NumberCache [K , V ] {
189
+ func NewNumber [K comparable , V Number ](opts ... Option [K , V ]) * NumberCache [K , V ] {
87
190
return & NumberCache [K , V ]{
88
- Cache : baseCache ,
191
+ Cache : New ( opts ... ) ,
89
192
}
90
193
}
91
194
0 commit comments