...
1 package ridata
2
3 import (
4 "errors"
5 "fmt"
6
7 "github.com/dsoprea/go-logging"
8 )
9
10 var (
11
12 ErrLruEmpty = errors.New("lru is empty")
13 )
14
15
16 type LruKey interface{}
17
18
19 type LruItem interface {
20 Id() LruKey
21 }
22
23 type lruNode struct {
24 before *lruNode
25 after *lruNode
26 item LruItem
27 }
28
29
30 func (ln *lruNode) String() string {
31 var beforePhrase string
32 if ln.before != nil {
33 beforePhrase = fmt.Sprintf("%v", ln.before.item.Id())
34 } else {
35 beforePhrase = "<NULL>"
36 }
37
38 var afterPhrase string
39 if ln.after != nil {
40 afterPhrase = fmt.Sprintf("%v", ln.after.item.Id())
41 } else {
42 afterPhrase = "<NULL>"
43 }
44
45 return fmt.Sprintf("[%v] BEFORE=[%s] AFTER=[%s]", ln.item.Id(), beforePhrase, afterPhrase)
46 }
47
48 type lruEventFunc func(id LruKey) (err error)
49
50
51 type Lru struct {
52 top *lruNode
53 bottom *lruNode
54 lookup map[LruKey]*lruNode
55 maxSize int
56 dropCb lruEventFunc
57 }
58
59
60 func NewLru(maxSize int) *Lru {
61 return &Lru{
62 lookup: make(map[LruKey]*lruNode),
63 maxSize: maxSize,
64 }
65 }
66
67
68
69 func (lru *Lru) SetDropCb(cb lruEventFunc) {
70 lru.dropCb = cb
71 }
72
73
74 func (lru *Lru) Count() int {
75 return len(lru.lookup)
76 }
77
78
79 func (lru *Lru) MaxCount() int {
80 return lru.maxSize
81 }
82
83
84 func (lru *Lru) IsFull() bool {
85 return lru.Count() == lru.maxSize
86 }
87
88
89 func (lru *Lru) Exists(id LruKey) bool {
90 _, found := lru.lookup[id]
91 return found
92 }
93
94
95
96
97
98 func (lru *Lru) FindPosition(id LruKey) int {
99 node, found := lru.lookup[id]
100 if found == false {
101 return -1
102 }
103
104 position := 0
105 for ; node.before != nil; node = node.before {
106 position++
107 }
108
109 return position
110 }
111
112
113 func (lru *Lru) Get(id LruKey) (found bool, item LruItem, err error) {
114 defer func() {
115 if state := recover(); state != nil {
116 err = log.Wrap(state.(error))
117 }
118 }()
119
120 if node, found := lru.lookup[id]; found == true {
121 _, _, err := lru.Set(node.item)
122 log.PanicIf(err)
123
124 return true, node.item, nil
125 }
126
127 return false, nil, nil
128 }
129
130
131
132
133
134
135 func (lru *Lru) Set(item LruItem) (added bool, droppedItem LruItem, err error) {
136 defer func() {
137 if state := recover(); state != nil {
138 err = log.Wrap(state.(error))
139 }
140 }()
141
142
143
144 id := item.Id()
145
146 node, found := lru.lookup[id]
147
148 added = (found == false)
149
150 if found == true {
151
152 if node.before == nil {
153 return added, nil, nil
154 }
155
156
157
158 if lru.bottom == node {
159 lru.bottom = lru.bottom.before
160 }
161
162
163 if node.before != nil {
164 node.before.after = node.after
165 node.before = nil
166 }
167
168
169 node.after = lru.top
170
171
172 lru.top = node
173 } else {
174 node = &lruNode{
175 after: lru.top,
176 item: item,
177 }
178
179 lru.lookup[id] = node
180
181
182 lru.top = node
183 }
184
185
186 if node.after != nil {
187 node.after.before = node
188 }
189
190 if lru.bottom == nil {
191 lru.bottom = node
192 }
193
194 if len(lru.lookup) > lru.maxSize {
195 lastItemId := lru.Oldest()
196 lastNode := lru.lookup[lastItemId]
197
198 found, err := lru.Drop(lastItemId)
199 log.PanicIf(err)
200
201 if found == false {
202 log.Panicf("drop of old item was ineffectual")
203 }
204
205 droppedItem = lastNode.item
206 }
207
208 return added, droppedItem, nil
209 }
210
211
212 func (lru *Lru) Drop(id LruKey) (found bool, err error) {
213 defer func() {
214 if state := recover(); state != nil {
215 err = log.Wrap(state.(error))
216 }
217 }()
218
219 node, found := lru.lookup[id]
220 if found == false {
221 return false, nil
222 }
223
224
225 if node.before == nil {
226 lru.top = node.after
227 }
228
229
230 if node.after == nil {
231 lru.bottom = node.before
232 }
233
234
235 if node.before != nil {
236 node.before.after = node.after
237 }
238
239 delete(lru.lookup, id)
240
241 if lru.dropCb != nil {
242 err := lru.dropCb(id)
243 log.PanicIf(err)
244 }
245
246 return true, nil
247 }
248
249
250 func (lru *Lru) Newest() LruKey {
251 if lru.top != nil {
252 return lru.top.item.Id()
253 }
254
255 return nil
256 }
257
258
259 func (lru *Lru) Oldest() LruKey {
260 if lru.bottom != nil {
261 return lru.bottom.item.Id()
262 }
263
264 return nil
265 }
266
267
268 func (lru *Lru) All() []LruKey {
269 collected := make([]LruKey, len(lru.lookup))
270 i := 0
271 for value := range lru.lookup {
272 collected[i] = value
273 i++
274 }
275
276 return collected
277 }
278
279
280
281 func (lru *Lru) PopOldest() (item LruItem, err error) {
282 lk := lru.Oldest()
283 if lk == nil {
284 return nil, ErrLruEmpty
285 }
286
287 node := lru.lookup[lk]
288 if node == nil {
289 log.Panicf("something went wrong resolving the oldest item")
290 }
291
292 found, err := lru.Drop(lk)
293 log.PanicIf(err)
294
295 if found == false {
296 log.Panicf("something went wrong dropping the oldest item")
297 }
298
299 return node.item, nil
300 }
301
302
303 func (lru *Lru) Dump() {
304 fmt.Printf("Count: (%d)\n", len(lru.lookup))
305 fmt.Printf("\n")
306
307 fmt.Printf("Top: %v\n", lru.top)
308 fmt.Printf("Bottom: %v\n", lru.bottom)
309 fmt.Printf("\n")
310
311 i := 0
312 for ptr := lru.top; ptr != nil; ptr = ptr.after {
313 fmt.Printf("%03d: %s\n", i, ptr)
314 i++
315 }
316 }
317
View as plain text