GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Setup Failed
Push — master ( 7774a3...638b76 )
by Amir
01:25
created

lfucache.*lfuCache.Get   A

Complexity

Conditions 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 1
dl 0
loc 13
rs 9.9
c 0
b 0
f 0
1
// This package implement LFU (Least Frequently Used) cache
0 ignored issues
show
introduced by
package comment should be of the form "Package lfucache ..."
Loading history...
2
package lfucache
3
4
import (
5
	"github.com/arazmj/gerdu/dlinklist"
6
	"github.com/arazmj/gerdu/metrics"
7
	"github.com/inhies/go-bytesize"
8
	"sync"
9
)
10
11
// lfuCache data structure
12
type lfuCache struct {
13
	sync.Mutex
14
	size     bytesize.ByteSize
15
	capacity bytesize.ByteSize
16
	node     map[string]*dlinklist.Node
17
	freq     map[int]*dlinklist.DLinkedList
18
	minFreq  int
19
}
20
21
// NewCache lfuCache constructor
22
func NewCache(capacity bytesize.ByteSize) *lfuCache {
0 ignored issues
show
introduced by
exported func NewCache returns unexported type *lfucache.lfuCache, which can be annoying to use
Loading history...
23
	return &lfuCache{
24
		size:     0,
25
		capacity: capacity,
26
		node:     map[string]*dlinklist.Node{},
27
		freq:     map[int]*dlinklist.DLinkedList{},
28
		minFreq:  0,
29
	}
30
}
31
32
// This is a helper function that used in the following two cases:
33
//
34
// 1. when Get(key)` is called; and
35
// 2. when Put(key, value)` is called and the key exists.
36
//
37
// The common point of these two cases is that:
38
//
39
// 1. no new node comes in, and
40
// 2. the node is visited one more times -> node.freq changed ->
41
// thus the place of this node will change
42
//
43
// The logic of this function is:
44
//
45
// 1. pop the node from the old DLinkedList (with freq `f`)
46
// 2. append the node to new DLinkedList (with freq `f+1`)
47
// 3. if old DLinkedList has size 0 and minFreq is `f`,
48
// update minFreq to `f+1`
49
//
50
// All of the above operations took O(1) time.
51
func (c *lfuCache) update(node *dlinklist.Node) {
52
	freq := node.Freq
53
54
	c.freq[freq].RemoveNode(node)
55
	if v, _ := c.freq[freq]; c.minFreq == freq && v.Size() == 0 {
56
		delete(c.freq, freq)
57
		c.minFreq++
58
	}
59
60
	node.Freq++
61
	freq = node.Freq
62
	if _, ok := c.freq[freq]; !ok {
63
		c.freq[freq] = dlinklist.NewLinkedList()
64
	}
65
	c.freq[freq].AddNode(node)
66
}
67
68
// Get through checking node[key], we can get the node in O(1) time.
69
// Just performs update, then we can return the value of node.
70
func (c *lfuCache) Get(key string) (value string, ok bool) {
71
	defer c.Unlock()
72
	c.Lock()
73
74
	if _, ok := c.node[key]; !ok {
75
		metrics.Miss.Inc()
76
		return "", false
77
	}
78
79
	metrics.Hits.Inc()
80
	node := c.node[key]
81
	c.update(node)
82
	return node.Value, true
83
}
84
85
// Put If `key` already exists in self._node, we do the same operations as `get`, except
86
// updating the node.val to new value.	Otherwise
87
// 1. if the cache reaches its capacity, pop the least frequently used item. (*)
88
// 2. add new node to self._node
89
// 3. add new node to the DLinkedList with frequency 1
90
// 4. reset minFreq to 1
91
//
92
// (*) How to pop the least frequently used item? Two facts:
93
// 1. we maintain the minFreq, the minimum possible frequency in cache.
94
// 2. All cache with the same frequency are stored as a DLinkedList, with
95
// recently used order (Always append at head)
96
// 3. The tail of the DLinkedList with minFreq is the least
97
//recently used one, pop it.
98
func (c *lfuCache) Put(key, value string) (created bool) {
99
	defer c.Unlock()
100
	c.Lock()
101
	if c.capacity == 0 {
102
		return
103
	}
104
	if _, ok := c.node[key]; ok {
105
		metrics.Hits.Inc()
106
		node := c.node[key]
107
		c.update(node)
108
		node.Value = value
109
		created = false
110
	} else {
111
		c.size += bytesize.ByteSize(len(value))
112
		for c.size > c.capacity {
113
			minList, ok := c.freq[c.minFreq]
114
			metrics.Deletes.Inc()
115
			if !ok || minList.Size() == 0 {
116
				delete(c.freq, c.minFreq)
117
				c.minFreq++
118
			} else {
119
				node := minList.PopTail()
120
				freq := node.Freq
121
				if v, _ := c.freq[c.minFreq]; c.minFreq == freq && v.Size() == 0 {
122
					delete(c.freq, freq)
123
					c.minFreq++
124
				}
125
				c.size -= bytesize.ByteSize(len(node.Value))
126
				delete(c.node, node.Key)
127
			}
128
		}
129
		metrics.Adds.Inc()
130
		node := &dlinklist.Node{
131
			Key:   key,
132
			Value: value,
133
			Freq:  1,
134
		}
135
		c.node[key] = node
136
		if _, ok := c.freq[1]; !ok {
137
			c.freq[1] = dlinklist.NewLinkedList()
138
		}
139
140
		c.freq[1].AddNode(node)
141
		c.minFreq = 1
142
		created = true
143
	}
144
	return created
145
}
146
147
func (c *lfuCache) Delete(key string) (ok bool) {
148
	node, ok := c.node[key]
149
	if !ok {
150
		return false
151
	}
152
	freq := node.Freq
153
	if v, _ := c.freq[c.minFreq]; c.minFreq == freq && v.Size() == 0 {
154
		metrics.Deletes.Inc()
155
		delete(c.freq, freq)
156
		c.minFreq++
157
	}
158
	delete(c.node, key)
159
	return true
160
}
161