201 lines
5.4 KiB
Lua
201 lines
5.4 KiB
Lua
-- Internal persistence library
|
|
|
|
--[[ Provides ]]
|
|
-- persistence.store(path, ...): Stores arbitrary items to the file at the given path
|
|
-- persistence.load(path): Loads files that were previously stored with store and returns them
|
|
|
|
--[[ Limitations ]]
|
|
-- Does not export userdata, threads or most function values
|
|
-- Function export is not portable
|
|
|
|
--[[ License: MIT (see bottom) ]]
|
|
|
|
-- Private methods
|
|
local write, writeIndent, writers, refCount;
|
|
|
|
-- 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("\t");
|
|
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 pairs(item) 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 function (path, ...)
|
|
local file, e;
|
|
if type(path) == "string" then
|
|
-- Path, open a file
|
|
file, e = io.open(path, "w");
|
|
if not file then
|
|
return error(e);
|
|
end
|
|
else
|
|
-- Just treat it as file
|
|
file = path;
|
|
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;
|
|
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;
|
|
-- Create the remaining objects
|
|
for i = 1, n do
|
|
file:write("local ".."obj"..i.." = ");
|
|
write(file, (select(i,...)), 0, objRefNames);
|
|
file:write("\n");
|
|
end
|
|
-- Return them
|
|
if n > 0 then
|
|
file:write("return obj1");
|
|
for i = 2, n do
|
|
file:write(" ,obj"..i);
|
|
end;
|
|
file:write("\n");
|
|
else
|
|
file:write("return\n");
|
|
end;
|
|
file:close();
|
|
end, function (path)
|
|
local f, e = loadfile(path);
|
|
if f then
|
|
return f();
|
|
else
|
|
return nil, e;
|
|
end;
|
|
end
|
|
|
|
--[[
|
|
Copyright (c) 2010 Gerhard Roethlin
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use,
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following
|
|
conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
]]
|