}
})
+-- Parametrized LRU table
+local typed_lru_t = 'struct { $ value_type[1]; struct lru * lru; }'
+
+-- Metatype for LRU
+local lru_metatype = {
+ -- Create a new LRU with given value type
+ -- By default the LRU will have a capacity of 65536 elements
+ -- Note: At the point the parametrized type must be finalized
+ __new = function (ct, max_slots)
+ -- {0} will make sure that the value is coercible to a number
+ local o = ffi.new(ct, {0}, C.lru_create_impl(max_slots or 65536, nil, nil))
+ if o.lru == nil then
+ return
+ end
+ return o
+ end,
+ -- Destructor to clean allocated memory
+ __gc = function (self)
+ assert(self.lru ~= nil)
+ C.lru_free_items_impl(self.lru)
+ C.free(self.lru)
+ self.lru = nil
+ end,
+ __index = {
+ -- Look up key and return reference to current
+ -- Note: The key will be inserted if it doesn't exist
+ get_ref = function (self, key, key_len, allow_insert)
+ local insert = allow_insert and true or false
+ local ptr = C.lru_get_impl(self.lru, key, key_len or #key, ffi.sizeof(self.value_type[0]), insert, nil)
+ if ptr ~= nil then
+ return ffi.cast(self.value_type, ptr)
+ end
+ end,
+ -- Look up key and return current value
+ get = function (self, key, key_len)
+ local ref = self:get_ref(key, key_len, false)
+ if ref then
+ return ref[0]
+ end
+ end,
+ -- Set value for key to given value
+ set = function (self, key, value, key_len)
+ local ref = self:get_ref(key, key_len, true)
+ if ref then
+ ref[0] = value
+ return true
+ end
+ end,
+ },
+}
+
-- Pretty print for domain name
local function dname2str(dname)
if dname == nil then return end
-- Export types
rrset = knot_rrset_t,
packet = knot_pkt_t,
+ lru = function (max_size, value_type)
+ local ct = ffi.typeof(typed_lru_t, value_type or ffi.typeof('uint64_t'))
+ return ffi.metatype(ct, lru_metatype)(max_size)
+ end,
-- Metatypes. Beware that any pointer will be cast silently...
pkt_t = function (udata) return ffi.cast('knot_pkt_t *', udata) end,
--- /dev/null
+local ffi = require('ffi')
+
+-- Test LRU interface
+local function test_lru()
+ local capacity = 1024
+ local lru = kres.lru(capacity)
+ local dict = {
+ "catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
+ "alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
+ "seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
+ "physiologically", "economizer", "forcepslike", "betrumpet",
+ "Danization", "broadthroat", "randir", "usherette", "nephropyosis",
+ "hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
+ "siphonognathous", "indoor", "featheriness", "forwardation",
+ "archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
+ "unpragmatical", "childless", "transumpt", "productive",
+ "thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
+ "pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
+ "smoothbored", "widower", "sivathere", "pendle", "saltation",
+ "autopelagic", "campfight", "unexplained", "Macrorhamphosus",
+ "absconsa", "counterflory", "interdependent", "triact", "reconcentration",
+ "oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
+ "pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
+ "Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
+ "cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
+ "bacillogenic", "hamartite", "polytheistical", "unescapableness",
+ "Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
+ "perishless", "cupidity", "semilichen", "gadge", "detrimental",
+ "misencourage", "toparchia", "lurchingly", "apocatastasis"
+ }
+
+ -- Check that key insertion works
+ local inserted = 0
+ for i, word in ipairs(dict) do
+ if lru:set(word, i) then
+ if lru:get(word) == i then
+ inserted = inserted + 1
+ end
+ end
+ end
+
+ is(inserted, #dict, 'all inserted keys can be retrieved')
+
+ -- Check that using binary data as keys works
+ local badinserts = 0
+ for i, word in ipairs(dict) do
+ local word_len = #word
+ word = ffi.cast('char *', word)
+ if lru:set(word, i, word_len) then
+ if lru:get(word, word_len) ~= i then
+ badinserts = badinserts + 1
+ end
+ end
+ end
+
+ is(badinserts, 0, 'insertion works for binary keys')
+
+ -- Sanity check that non-existent keys cannot be retrieved
+ local missing = "not in lru"
+ is(lru:get(missing, #missing, false), nil, 'key that wasnt inserted cannot be retrieved')
+
+ -- Test whether key eviction works and LRU is able to insert past the capacity
+ badinserts = 0
+ for i = 0, capacity do
+ local word = dict[1] .. tostring(i)
+ if lru:set(word, i) then
+ if lru:get(word) ~= i then
+ badinserts = badinserts + 1
+ end
+ end
+ end
+
+ is(badinserts, 0, 'insertion works for more keys than LRU capacity')
+
+ -- Delete and GC
+ lru = nil -- luacheck: ignore 311
+ collectgarbage()
+ collectgarbage()
+end
+
+return {
+ test_lru,
+}
\ No newline at end of file