util.cache: Add support for creating a proxy table to a cache, that looks and acts...
[prosody.git] / util / cache.lua
index 72b74351a37733e2374abb6b0a5b602043ff3f71..44bbfe3098427ae9c9fef7ba9b6ce8d9a4830ddf 100644 (file)
@@ -53,12 +53,13 @@ function cache_methods:set(k, v)
        -- Check whether we need to remove oldest k/v
        if self._count == self.size then
                local tail = self._tail;
-               local on_evict = self._on_evict;
-               if on_evict then
-                       on_evict(tail.key, tail.value);
+               local on_evict, evicted_key, evicted_value = self._on_evict, tail.key, tail.value;
+               if on_evict ~= nil and (on_evict == false or on_evict(evicted_key, evicted_value) == false) then
+                       -- Cache is full, and we're not allowed to evict
+                       return false;
                end
                _remove(self, tail);
-               self._data[tail.key] = nil;
+               self._data[evicted_key] = nil;
        end
 
        m = { key = k, value = v, prev = nil, next = nil };
@@ -87,10 +88,56 @@ function cache_methods:items()
        end
 end
 
+function cache_methods:values()
+       local m = self._head;
+       return function ()
+               if not m then
+                       return;
+               end
+               local v = m.value;
+               m = m.next;
+               return v;
+       end
+end
+
 function cache_methods:count()
        return self._count;
 end
 
+function cache_methods:head()
+       local head = self._head;
+       if not head then return nil, nil; end
+       return head.key, head.value;
+end
+
+function cache_methods:tail()
+       local tail = self._tail;
+       if not tail then return nil, nil; end
+       return tail.key, tail.value;
+end
+
+function cache_methods:table()
+       if not self.proxy_table then
+               self.proxy_table = setmetatable({}, {
+                       __index = function (t, k)
+                               return self:get(k);
+                       end;
+                       __newindex = function (t, k, v)
+                               if not self:set(k, v) then
+                                       error("failed to insert key into cache - full");
+                               end
+                       end;
+                       __pairs = function (t)
+                               return self:items();
+                       end;
+                       __len = function (t)
+                               return self:count();
+                       end;
+               });
+       end
+       return self.proxy_table;
+end
+
 local function new(size, on_evict)
        size = assert(tonumber(size), "cache size must be a number");
        size = math.floor(size);