navigator.lua/lua/navigator/lru.lua
2021-08-29 17:35:46 +10:00

151 lines
3.4 KiB
Lua

-- lua-lru, LRU cache in Lua
-- Copyright (c) 2015 Boris Nagaev
-- See the LICENSE file for terms of use.
local lru = {}
function lru.new(max_size, max_bytes)
assert(max_size >= 1, "max_size must be >= 1")
assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1")
-- current size
local size = 0
local bytes_used = 0
-- map is a hash map from keys to tuples
-- tuple: value, prev, next, key
-- prev and next are pointers to tuples
local map = {}
-- indices of tuple
local VALUE = 1
local PREV = 2
local NEXT = 3
local KEY = 4
local BYTES = 5
-- newest and oldest are ends of double-linked list
local newest = nil -- first
local oldest = nil -- last
local removed_tuple -- created in del(), removed in set()
-- remove a tuple from linked list
local function cut(tuple)
local tuple_prev = tuple[PREV]
local tuple_next = tuple[NEXT]
tuple[PREV] = nil
tuple[NEXT] = nil
if tuple_prev and tuple_next then
tuple_prev[NEXT] = tuple_next
tuple_next[PREV] = tuple_prev
elseif tuple_prev then
-- tuple is the oldest element
tuple_prev[NEXT] = nil
oldest = tuple_prev
elseif tuple_next then
-- tuple is the newest element
tuple_next[PREV] = nil
newest = tuple_next
else
-- tuple is the only element
newest = nil
oldest = nil
end
end
-- insert a tuple to the newest end
local function setNewest(tuple)
if not newest then
newest = tuple
oldest = tuple
else
tuple[NEXT] = newest
newest[PREV] = tuple
newest = tuple
end
end
local function del(key, tuple)
map[key] = nil
cut(tuple)
size = size - 1
bytes_used = bytes_used - (tuple[BYTES] or 0)
removed_tuple = tuple
end
-- removes elemenets to provide enough memory
-- returns last removed element or nil
local function makeFreeSpace(bytes)
while size + 1 > max_size or (max_bytes and bytes_used + bytes > max_bytes) do
assert(oldest, "not enough storage for cache")
del(oldest[KEY], oldest)
end
end
local function get(_, key)
local tuple = map[key]
if not tuple then
return nil
end
cut(tuple)
setNewest(tuple)
return tuple[VALUE]
end
local function set(_, key, value, bytes)
local tuple = map[key]
if tuple then
del(key, tuple)
end
if value ~= nil then
-- the value is not removed
bytes = max_bytes and (bytes or #value) or 0
makeFreeSpace(bytes)
local tuple1 = removed_tuple or {}
map[key] = tuple1
tuple1[VALUE] = value
tuple1[KEY] = key
tuple1[BYTES] = max_bytes and bytes
size = size + 1
bytes_used = bytes_used + bytes
setNewest(tuple1)
else
assert(key ~= nil, "Key may not be nil")
end
removed_tuple = nil
end
local function delete(_, key)
return set(_, key, nil)
end
local function mynext(_, prev_key)
local tuple
if prev_key then
tuple = map[prev_key][NEXT]
else
tuple = newest
end
if tuple then
return tuple[KEY], tuple[VALUE]
else
return nil
end
end
-- returns iterator for keys and values
local function lru_pairs()
return mynext, nil, nil
end
local mt = {
__index = {get = get, set = set, delete = delete, pairs = lru_pairs},
__pairs = lru_pairs
}
return setmetatable({}, mt)
end
return lru