forked from Archives/navigator.lua
Feature: LRU cache for search result
parent
d39cf20903
commit
940349759b
@ -0,0 +1,150 @@
|
||||
-- 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
|
Loading…
Reference in New Issue