mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-03 09:20:10 +00:00
218 lines
5.6 KiB
Lua
218 lines
5.6 KiB
Lua
-- Adapted from http://lua-users.org/wiki/TablePersistence to preserve order and
|
|
-- put single values at the end of the stream
|
|
|
|
local write, writeIndent, writers, refCount;
|
|
|
|
function pairsByKeyVals(t, f)
|
|
local a = {}
|
|
|
|
-- io.write(debug.traceback().."\n")
|
|
for n in pairs(t) do table.insert(a, n) end
|
|
table.sort(a, function(x, y) return f(x, y, t[x], t[y]) end)
|
|
local i = 0 -- iterator variable
|
|
local iter = function () -- iterator function
|
|
i = i + 1
|
|
if a[i] == nil then return nil
|
|
else return a[i], t[a[i]]
|
|
end
|
|
end
|
|
return iter
|
|
end
|
|
function asc_table_after(a,b,t_a,t_b)
|
|
local a_is_table = (type(t_a) == "table")
|
|
local b_is_table = (type(t_b) == "table")
|
|
|
|
if a_is_table and not b_is_table then
|
|
return false
|
|
elseif not a_is_table and b_is_table then
|
|
return true
|
|
else
|
|
return (a < b)
|
|
end
|
|
end
|
|
|
|
persistence =
|
|
{
|
|
store = function (path, ...)
|
|
local file, e = io.open(path, "w");
|
|
if not file then
|
|
return error(e);
|
|
end
|
|
local n = select("#", ...);
|
|
-- Count references
|
|
local objRefCount = {}; -- Stores reference that will be exported
|
|
for i = 1, n do
|
|
refCount(objRefCount, (select(i,...)));
|
|
end;
|
|
-- Export Objects with more than one ref and assign name
|
|
-- First, create empty tables for each
|
|
local objRefNames = {};
|
|
local objRefIdx = 0;
|
|
|
|
local has_objRefCount = false
|
|
for obj, count in pairs(objRefCount) do
|
|
if count > 1 then
|
|
has_objRefCount = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if has_objRefCount then
|
|
file:write("-- Persistent Data\n");
|
|
file:write("local multiRefObjects = {\n");
|
|
for obj, count in pairs(objRefCount) do
|
|
if count > 1 then
|
|
objRefIdx = objRefIdx + 1;
|
|
objRefNames[obj] = objRefIdx;
|
|
file:write("{};"); -- table objRefIdx
|
|
end;
|
|
end;
|
|
file:write("\n} -- multiRefObjects\n");
|
|
|
|
-- Then fill them (this requires all empty multiRefObjects to exist)
|
|
for obj, idx in pairs(objRefNames) do
|
|
for k, v in pairs(obj) do
|
|
file:write("multiRefObjects["..idx.."][");
|
|
write(file, k, 0, objRefNames);
|
|
file:write("] = ");
|
|
write(file, v, 0, objRefNames);
|
|
file:write("\n");
|
|
end;
|
|
end;
|
|
end
|
|
-- Create the remaining objects
|
|
for i = 1, n do
|
|
local suffix = ""
|
|
if i > 1 then
|
|
suffix = (i-1) .. ""
|
|
end
|
|
file:write("local ".."lang"..suffix.." = ");
|
|
write(file, (select(i,...)), 0, objRefNames);
|
|
file:write("\n");
|
|
end
|
|
-- Return them
|
|
if n > 0 then
|
|
file:write("\nreturn lang");
|
|
for i = 2, n do
|
|
file:write(" ,lang"..(i-1));
|
|
end;
|
|
file:write("\n");
|
|
else
|
|
file:write("return\n");
|
|
end;
|
|
if type(path) == "string" then
|
|
file:close();
|
|
end;
|
|
end;
|
|
|
|
load = function (path)
|
|
local f, e;
|
|
if type(path) == "string" then
|
|
f, e = loadfile(path);
|
|
else
|
|
f, e = path:read('*a')
|
|
end
|
|
if f then
|
|
return f();
|
|
else
|
|
return nil, e;
|
|
end;
|
|
end;
|
|
}
|
|
|
|
-- Private methods
|
|
|
|
-- write thing (dispatcher)
|
|
write = function (file, item, level, objRefNames)
|
|
writers[type(item)](file, item, level, objRefNames);
|
|
end;
|
|
|
|
-- write indent
|
|
writeIndent = function (file, level)
|
|
for i = 1, level do
|
|
file:write(" ");
|
|
end;
|
|
end;
|
|
|
|
-- recursively count references
|
|
refCount = function (objRefCount, item)
|
|
-- only count reference types (tables)
|
|
if type(item) == "table" then
|
|
-- Increase ref count
|
|
if objRefCount[item] then
|
|
objRefCount[item] = objRefCount[item] + 1;
|
|
else
|
|
objRefCount[item] = 1;
|
|
-- If first encounter, traverse
|
|
for k, v in pairs(item) do
|
|
refCount(objRefCount, k);
|
|
refCount(objRefCount, v);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
-- Format items for the purpose of restoring
|
|
writers = {
|
|
["nil"] = function (file, item)
|
|
file:write("nil");
|
|
end;
|
|
["number"] = function (file, item)
|
|
file:write(tostring(item));
|
|
end;
|
|
["string"] = function (file, item)
|
|
file:write(string.format("%q", item));
|
|
end;
|
|
["boolean"] = function (file, item)
|
|
if item then
|
|
file:write("true");
|
|
else
|
|
file:write("false");
|
|
end
|
|
end;
|
|
["table"] = function (file, item, level, objRefNames)
|
|
local refIdx = objRefNames[item];
|
|
if refIdx then
|
|
-- Table with multiple references
|
|
file:write("multiRefObjects["..refIdx.."]");
|
|
else
|
|
-- Single use table
|
|
file:write("{\n");
|
|
for k, v in pairsByKeyVals(item, asc_table_after) do
|
|
writeIndent(file, level+1);
|
|
file:write("[");
|
|
write(file, k, level+1, objRefNames);
|
|
file:write("] = ");
|
|
write(file, v, level+1, objRefNames);
|
|
file:write(",\n");
|
|
end
|
|
writeIndent(file, level);
|
|
file:write("}");
|
|
end;
|
|
end;
|
|
["function"] = function (file, item)
|
|
-- Does only work for "normal" functions, not those
|
|
-- with upvalues or c functions
|
|
local dInfo = debug.getinfo(item, "uS");
|
|
if dInfo.nups > 0 then
|
|
file:write("nil --[[functions with upvalue not supported]]");
|
|
elseif dInfo.what ~= "Lua" then
|
|
file:write("nil --[[non-lua function not supported]]");
|
|
else
|
|
local r, s = pcall(string.dump,item);
|
|
if r then
|
|
file:write(string.format("loadstring(%q)", s));
|
|
else
|
|
file:write("nil --[[function could not be dumped]]");
|
|
end
|
|
end
|
|
end;
|
|
["thread"] = function (file, item)
|
|
file:write("nil --[[thread]]\n");
|
|
end;
|
|
["userdata"] = function (file, item)
|
|
file:write("nil --[[userdata]]\n");
|
|
end;
|
|
}
|
|
|
|
return persistence
|