cache.go

v1.0.1
Doc Versions Source
1
package sumdb
2
3
import "sync"
4
5
// lruCache is a simple fixed-size LRU cache safe for concurrent use.
6
// Keys are int64 (record/hash IDs), values are []byte.
7
type lruCache struct {
8
	mu      sync.Mutex
9
	cap     int
10
	items   map[int64]*lruEntry
11
	head    *lruEntry // most recent
12
	tail    *lruEntry // least recent
13
}
14
15
type lruEntry struct {
16
	key        int64
17
	value      []byte
18
	prev, next *lruEntry
19
}
20
21
func newLRUCache(cap int) *lruCache {
22
	return &lruCache{
23
		cap:   cap,
24
		items: make(map[int64]*lruEntry, cap),
25
	}
26
}
27
28
func (c *lruCache) get(key int64) ([]byte, bool) {
29
	c.mu.Lock()
30
	defer c.mu.Unlock()
31
32
	e, ok := c.items[key]
33
	if !ok {
34
		return nil, false
35
	}
36
	c.moveToFront(e)
37
	return e.value, true
38
}
39
40
func (c *lruCache) put(key int64, value []byte) {
41
	c.mu.Lock()
42
	defer c.mu.Unlock()
43
44
	if e, ok := c.items[key]; ok {
45
		e.value = value
46
		c.moveToFront(e)
47
		return
48
	}
49
50
	e := &lruEntry{key: key, value: value}
51
	c.items[key] = e
52
	c.pushFront(e)
53
54
	if len(c.items) > c.cap {
55
		c.removeTail()
56
	}
57
}
58
59
func (c *lruCache) moveToFront(e *lruEntry) {
60
	if c.head == e {
61
		return
62
	}
63
	c.remove(e)
64
	c.pushFront(e)
65
}
66
67
func (c *lruCache) pushFront(e *lruEntry) {
68
	e.prev = nil
69
	e.next = c.head
70
	if c.head != nil {
71
		c.head.prev = e
72
	}
73
	c.head = e
74
	if c.tail == nil {
75
		c.tail = e
76
	}
77
}
78
79
func (c *lruCache) remove(e *lruEntry) {
80
	if e.prev != nil {
81
		e.prev.next = e.next
82
	} else {
83
		c.head = e.next
84
	}
85
	if e.next != nil {
86
		e.next.prev = e.prev
87
	} else {
88
		c.tail = e.prev
89
	}
90
}
91
92
func (c *lruCache) removeTail() {
93
	if c.tail == nil {
94
		return
95
	}
96
	e := c.tail
97
	c.remove(e)
98
	delete(c.items, e.key)
99
}
100

Source Files