]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/lua: added basic bindings for LRU
authorMarek Vavruša <mvavrusa@cloudflare.com>
Tue, 24 Apr 2018 02:05:30 +0000 (19:05 -0700)
committerPetr Špaček <petr.spacek@nic.cz>
Wed, 9 May 2018 14:49:30 +0000 (16:49 +0200)
Only get-set operations are added, added some tests.

daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/lua/kres.lua
tests/config/lru.test.lua [new file with mode: 0644]

index 885076360c11717ce87098cc0bd4da5f2eb0a6c9..35d5bc640207849f7882b53d1ae09de57d254e92 100644 (file)
@@ -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 *);
index 5752f2e03e34576f3437ca1e03147c0c1d27168d..117f3e9090a0340f6cadd0d75decaf2710c31919 100755 (executable)
@@ -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
index 65ecad9e49097097e2f28d4985d116ba42e06f93..d781d84c8b1675361bdd1d93dc1986030faa8d8b 100644 (file)
@@ -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 (file)
index 0000000..0dbf1ce
--- /dev/null
@@ -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