Software / code / prosody
Comparison
util/cache.lua @ 6945:d779c55058c6
util.cache: Small update to prefix private fields with an underscore, add a :count() method (same as util.queue) and add an optional on_evict callback
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Thu, 26 Nov 2015 00:07:48 +0000 |
| parent | 6943:0a31ec3193f0 |
| child | 7016:e0a0af42b09f |
comparison
equal
deleted
inserted
replaced
| 6944:62b6f6d230f1 | 6945:d779c55058c6 |
|---|---|
| 1 local cache_methods = {}; | |
| 2 local cache_mt = { __index = cache_methods }; | |
| 3 | |
| 4 local function new(size) | |
| 5 size = assert(tonumber(size), "cache size must be a number"); | |
| 6 size = math.floor(size); | |
| 7 assert(size > 0, "cache size must be greater than zero"); | |
| 8 local data = {}; | |
| 9 return setmetatable({ data = data, count = 0, size = size, head = nil, tail = nil }, cache_mt); | |
| 10 end | |
| 11 | 1 |
| 12 local function _remove(list, m) | 2 local function _remove(list, m) |
| 13 if m.prev then | 3 if m.prev then |
| 14 m.prev.next = m.next; | 4 m.prev.next = m.next; |
| 15 end | 5 end |
| 16 if m.next then | 6 if m.next then |
| 17 m.next.prev = m.prev; | 7 m.next.prev = m.prev; |
| 18 end | 8 end |
| 19 if list.tail == m then | 9 if list._tail == m then |
| 20 list.tail = m.prev; | 10 list._tail = m.prev; |
| 21 end | 11 end |
| 22 if list.head == m then | 12 if list._head == m then |
| 23 list.head = m.next; | 13 list._head = m.next; |
| 24 end | 14 end |
| 25 list.count = list.count - 1; | 15 list._count = list._count - 1; |
| 26 end | 16 end |
| 27 | 17 |
| 28 local function _insert(list, m) | 18 local function _insert(list, m) |
| 29 if list.head then | 19 if list._head then |
| 30 list.head.prev = m; | 20 list._head.prev = m; |
| 31 end | 21 end |
| 32 m.prev, m.next = nil, list.head; | 22 m.prev, m.next = nil, list._head; |
| 33 list.head = m; | 23 list._head = m; |
| 34 if not list.tail then | 24 if not list._tail then |
| 35 list.tail = m; | 25 list._tail = m; |
| 36 end | 26 end |
| 37 list.count = list.count + 1; | 27 list._count = list._count + 1; |
| 38 end | 28 end |
| 39 | 29 |
| 30 local cache_methods = {}; | |
| 31 local cache_mt = { __index = cache_methods }; | |
| 32 | |
| 40 function cache_methods:set(k, v) | 33 function cache_methods:set(k, v) |
| 41 local m = self.data[k]; | 34 local m = self._data[k]; |
| 42 if m then | 35 if m then |
| 43 -- Key already exists | 36 -- Key already exists |
| 44 if v ~= nil then | 37 if v ~= nil then |
| 45 -- Bump to head of list | 38 -- Bump to head of list |
| 46 _remove(self, m); | 39 _remove(self, m); |
| 47 _insert(self, m); | 40 _insert(self, m); |
| 48 m.value = v; | 41 m.value = v; |
| 49 else | 42 else |
| 50 -- Remove from list | 43 -- Remove from list |
| 51 _remove(self, m); | 44 _remove(self, m); |
| 52 self.data[k] = nil; | 45 self._data[k] = nil; |
| 53 end | 46 end |
| 54 return; | 47 return true; |
| 55 end | 48 end |
| 56 -- New key | 49 -- New key |
| 57 if v == nil then | 50 if v == nil then |
| 58 return; | 51 return true; |
| 59 end | 52 end |
| 60 -- Check whether we need to remove oldest k/v | 53 -- Check whether we need to remove oldest k/v |
| 61 if self.count == self.size then | 54 if self._count == self.size then |
| 62 self.data[self.tail.key] = nil; | 55 local tail = self._tail; |
| 63 _remove(self, self.tail); | 56 local on_evict = self._on_evict; |
| 57 if on_evict then | |
| 58 on_evict(tail.key, tail.value); | |
| 59 end | |
| 60 _remove(self, tail); | |
| 61 self._data[tail.key] = nil; | |
| 64 end | 62 end |
| 65 | 63 |
| 66 m = { key = k, value = v, prev = nil, next = nil }; | 64 m = { key = k, value = v, prev = nil, next = nil }; |
| 67 self.data[k] = m; | 65 self._data[k] = m; |
| 68 _insert(self, m); | 66 _insert(self, m); |
| 67 return true; | |
| 69 end | 68 end |
| 70 | 69 |
| 71 function cache_methods:get(k) | 70 function cache_methods:get(k) |
| 72 local m = self.data[k]; | 71 local m = self._data[k]; |
| 73 if m then | 72 if m then |
| 74 return m.value; | 73 return m.value; |
| 75 end | 74 end |
| 76 return nil; | 75 return nil; |
| 77 end | 76 end |
| 78 | 77 |
| 79 function cache_methods:items() | 78 function cache_methods:items() |
| 80 local m = self.head; | 79 local m = self._head; |
| 81 return function () | 80 return function () |
| 82 if not m then | 81 if not m then |
| 83 return; | 82 return; |
| 84 end | 83 end |
| 85 local k, v = m.key, m.value; | 84 local k, v = m.key, m.value; |
| 86 m = m.next; | 85 m = m.next; |
| 87 return k, v; | 86 return k, v; |
| 88 end | 87 end |
| 89 end | 88 end |
| 90 | 89 |
| 90 function cache_methods:count() | |
| 91 return self._count; | |
| 92 end | |
| 93 | |
| 94 local function new(size, on_evict) | |
| 95 size = assert(tonumber(size), "cache size must be a number"); | |
| 96 size = math.floor(size); | |
| 97 assert(size > 0, "cache size must be greater than zero"); | |
| 98 local data = {}; | |
| 99 return setmetatable({ _data = data, _count = 0, size = size, _head = nil, _tail = nil, _on_evict = on_evict }, cache_mt); | |
| 100 end | |
| 101 | |
| 91 return { | 102 return { |
| 92 new = new; | 103 new = new; |
| 93 } | 104 } |