123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- -- 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.
- ]]
|