serve/util/cache.lua

changeset 138
561f0af6c9dc
equal deleted inserted replaced
137:091212cef52a 138:561f0af6c9dc
1
2 local function _remove(list, m)
3 if m.prev then
4 m.prev.next = m.next;
5 end
6 if m.next then
7 m.next.prev = m.prev;
8 end
9 if list._tail == m then
10 list._tail = m.prev;
11 end
12 if list._head == m then
13 list._head = m.next;
14 end
15 list._count = list._count - 1;
16 end
17
18 local function _insert(list, m)
19 if list._head then
20 list._head.prev = m;
21 end
22 m.prev, m.next = nil, list._head;
23 list._head = m;
24 if not list._tail then
25 list._tail = m;
26 end
27 list._count = list._count + 1;
28 end
29
30 local cache_methods = {};
31 local cache_mt = { __index = cache_methods };
32
33 function cache_methods:set(k, v)
34 local m = self._data[k];
35 if m then
36 -- Key already exists
37 if v ~= nil then
38 -- Bump to head of list
39 _remove(self, m);
40 _insert(self, m);
41 m.value = v;
42 else
43 -- Remove from list
44 _remove(self, m);
45 self._data[k] = nil;
46 end
47 return true;
48 end
49 -- New key
50 if v == nil then
51 return true;
52 end
53 -- Check whether we need to remove oldest k/v
54 if self._count == self.size then
55 local tail = self._tail;
56 local on_evict, evicted_key, evicted_value = self._on_evict, tail.key, tail.value;
57 if on_evict ~= nil and (on_evict == false or on_evict(evicted_key, evicted_value) == false) then
58 -- Cache is full, and we're not allowed to evict
59 return false;
60 end
61 _remove(self, tail);
62 self._data[evicted_key] = nil;
63 end
64
65 m = { key = k, value = v, prev = nil, next = nil };
66 self._data[k] = m;
67 _insert(self, m);
68 return true;
69 end
70
71 function cache_methods:get(k)
72 local m = self._data[k];
73 if m then
74 return m.value;
75 end
76 return nil;
77 end
78
79 function cache_methods:items()
80 local m = self._head;
81 return function ()
82 if not m then
83 return;
84 end
85 local k, v = m.key, m.value;
86 m = m.next;
87 return k, v;
88 end
89 end
90
91 function cache_methods:values()
92 local m = self._head;
93 return function ()
94 if not m then
95 return;
96 end
97 local v = m.value;
98 m = m.next;
99 return v;
100 end
101 end
102
103 function cache_methods:count()
104 return self._count;
105 end
106
107 function cache_methods:head()
108 local head = self._head;
109 if not head then return nil, nil; end
110 return head.key, head.value;
111 end
112
113 function cache_methods:tail()
114 local tail = self._tail;
115 if not tail then return nil, nil; end
116 return tail.key, tail.value;
117 end
118
119 function cache_methods:resize(new_size)
120 new_size = assert(tonumber(new_size), "cache size must be a number");
121 new_size = math.floor(new_size);
122 assert(new_size > 0, "cache size must be greater than zero");
123 local on_evict = self._on_evict;
124 while self._count > new_size do
125 local tail = self._tail;
126 local evicted_key, evicted_value = tail.key, tail.value;
127 if on_evict ~= nil and (on_evict == false or on_evict(evicted_key, evicted_value) == false) then
128 -- Cache is full, and we're not allowed to evict
129 return false;
130 end
131 _remove(self, tail);
132 self._data[evicted_key] = nil;
133 end
134 self.size = new_size;
135 return true;
136 end
137
138 function cache_methods:table()
139 --luacheck: ignore 212/t
140 if not self.proxy_table then
141 self.proxy_table = setmetatable({}, {
142 __index = function (t, k)
143 return self:get(k);
144 end;
145 __newindex = function (t, k, v)
146 if not self:set(k, v) then
147 error("failed to insert key into cache - full");
148 end
149 end;
150 __pairs = function (t)
151 return self:items();
152 end;
153 __len = function (t)
154 return self:count();
155 end;
156 });
157 end
158 return self.proxy_table;
159 end
160
161 function cache_methods:clear()
162 self._data = {};
163 self._count = 0;
164 self._head = nil;
165 self._tail = nil;
166 end
167
168 local function new(size, on_evict)
169 size = assert(tonumber(size), "cache size must be a number");
170 size = math.floor(size);
171 assert(size > 0, "cache size must be greater than zero");
172 local data = {};
173 return setmetatable({ _data = data, _count = 0, size = size, _head = nil, _tail = nil, _on_evict = on_evict }, cache_mt);
174 end
175
176 return {
177 new = new;
178 }

mercurial