From: Marek VavrusÌŒa Date: Tue, 24 Apr 2018 02:05:30 +0000 (-0700) Subject: daemon/lua: added basic bindings for LRU X-Git-Tag: v2.4.0~48^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e2ed9ec29be56e4ee08d8bae8bf3ba978bbbf86;p=thirdparty%2Fknot-resolver.git daemon/lua: added basic bindings for LRU Only get-set operations are added, added some tests. --- diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 885076360..35d5bc640 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -297,6 +297,9 @@ _Bool kr_zonecut_is_empty(struct kr_zonecut *); void kr_zonecut_set(struct kr_zonecut *, const knot_dname_t *); const knot_dname_t *kr_zonecut_find_nsname(struct kr_zonecut *); uint64_t kr_now(); +void lru_free_items_impl(struct lru *); +struct lru *lru_create_impl(unsigned int, knot_mm_t *, knot_mm_t *); +void *lru_get_impl(struct lru *, const char *, unsigned int, unsigned int, bool, bool *); knot_rrset_t *kr_ta_get(map_t *, const knot_dname_t *); int kr_ta_add(map_t *, const knot_dname_t *, uint16_t, uint32_t, const uint8_t *, uint16_t); int kr_ta_del(map_t *, const knot_dname_t *); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 5752f2e03..117f3e909 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -24,6 +24,7 @@ typedef struct knot_dump_style knot_dump_style_t; extern const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT; typedef void knot_db_t; struct kr_cdb_api {}; +struct lru {}; " # The generator doesn't work well with typedefs of functions. @@ -165,6 +166,9 @@ EOF kr_zonecut_set kr_zonecut_find_nsname kr_now + lru_free_items_impl + lru_create_impl + lru_get_impl # Trust anchors kr_ta_get kr_ta_add diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index 65ecad9e4..d781d84c8 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -249,6 +249,57 @@ ffi.metatype( sockaddr_t, { } }) +-- 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 @@ -799,6 +850,10 @@ kres = { -- 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, diff --git a/tests/config/lru.test.lua b/tests/config/lru.test.lua new file mode 100644 index 000000000..0dbf1cedc --- /dev/null +++ b/tests/config/lru.test.lua @@ -0,0 +1,83 @@ +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