-
Notifications
You must be signed in to change notification settings - Fork 1
/
cmap.go
102 lines (83 loc) · 2.14 KB
/
cmap.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
package cmap
import (
"sync"
)
// Container is a wrapper around Go's built-in map type that will only carry up
// to cap key-value pairs. An attempt to add a key/value pair to a cmap
// container that is already at capacity will succeed but will evict an existing
// key-value pair from the Container according to the LRU (least-recently used)
// policy.
type Container struct {
len, cap int
vals map[interface{}]*node
fakeHead, fakeTail *node
rwm *sync.RWMutex
}
// New returns a cmap container that can carry up to cap key/value pairs. The
// parameter cap should be a positive integer.
func New(cap int) (*Container, error) {
if cap < 1 {
return nil, ErrInvalidCapacity
}
fakeHead, fakeTail := new(node), new(node)
fakeHead.next, fakeTail.prev = fakeTail, fakeHead
cm := &Container{
cap: cap,
vals: make(map[interface{}]*node),
fakeHead: fakeHead,
fakeTail: fakeTail,
rwm: new(sync.RWMutex),
}
return cm, nil
}
// Get returns the value that corresponds to the given key, if it exists.
func (cm *Container) Get(key interface{}) (interface{}, bool) {
cm.rwm.RLock()
defer cm.rwm.RUnlock()
v, ok := cm.vals[key]
if !ok {
return nil, ok
}
moveToTail(cm.fakeTail, v)
return v.val, ok
}
// Put adds the given key/value pair to the cmap container.
func (cm *Container) Put(key, val interface{}) {
newNode := &node{
key: key,
val: val,
}
cm.rwm.Lock()
defer cm.rwm.Unlock()
oldNode, ok := cm.vals[key]
// Identify the cases where we need to remove elements first.
if ok {
delete(cm.vals, oldNode.key)
removeNode(oldNode)
} else {
if cm.len == cm.cap {
excessNode := cm.fakeHead.next
delete(cm.vals, excessNode.key)
removeNode(excessNode)
cm.len--
}
}
// The key does NOT exist in the map and we're below capacity.
cm.vals[key] = newNode
addToTail(cm.fakeTail, newNode)
cm.len++
return
}
// Delete removes the given key from the cmap container, if it exists.
func (cm *Container) Delete(key interface{}) bool {
cm.rwm.Lock()
defer cm.rwm.Unlock()
v, ok := cm.vals[key]
if !ok {
return false
}
delete(cm.vals, v.key)
removeNode(v)
cm.len--
return true
}