|
|
@@ -1,145 +1,49 @@ |
|
|
|
local serialization = {} |
|
|
|
|
|
|
|
local local_pairs = function(tbl) |
|
|
|
local mt = getmetatable(tbl) |
|
|
|
return (mt and mt.__pairs or pairs)(tbl) |
|
|
|
local serial = {} |
|
|
|
local local_pairs=function(tbl) |
|
|
|
local mt=getmetatable(tbl) |
|
|
|
return (mt and mt.__pairs or pairs)(tbl) |
|
|
|
end |
|
|
|
|
|
|
|
function serialization.serialize(value, pretty) -- serialize *value* into a string, optionally in a nicely formatted manner when *pretty* is set |
|
|
|
local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true, |
|
|
|
["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true, |
|
|
|
["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true, |
|
|
|
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true, |
|
|
|
["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true, |
|
|
|
["until"]=true, ["while"]=true} |
|
|
|
local id = "^[%a_][%w_]*$" |
|
|
|
local ts = {} |
|
|
|
local result_pack = {} |
|
|
|
local function recurse(current_value, depth) |
|
|
|
local t = type(current_value) |
|
|
|
if t == "number" then |
|
|
|
if current_value ~= current_value then |
|
|
|
table.insert(result_pack, "0/0") |
|
|
|
elseif current_value == math.huge then |
|
|
|
table.insert(result_pack, "math.huge") |
|
|
|
elseif current_value == -math.huge then |
|
|
|
table.insert(result_pack, "-math.huge") |
|
|
|
else |
|
|
|
table.insert(result_pack, tostring(current_value)) |
|
|
|
end |
|
|
|
elseif t == "string" then |
|
|
|
table.insert(result_pack, (string.format("%q", current_value):gsub("\\\n","\\n"))) |
|
|
|
elseif |
|
|
|
t == "nil" or |
|
|
|
t == "boolean" or |
|
|
|
pretty and (t ~= "table" or (getmetatable(current_value) or {}).__tostring) then |
|
|
|
table.insert(result_pack, tostring(current_value)) |
|
|
|
elseif t == "table" then |
|
|
|
if ts[current_value] then |
|
|
|
if pretty then |
|
|
|
table.insert(result_pack, "recursion") |
|
|
|
return |
|
|
|
else |
|
|
|
error("tables with cycles are not supported") |
|
|
|
end |
|
|
|
end |
|
|
|
ts[current_value] = true |
|
|
|
local f |
|
|
|
if pretty then |
|
|
|
local ks, sks, oks = {}, {}, {} |
|
|
|
for k in local_pairs(current_value) do |
|
|
|
if type(k) == "number" then |
|
|
|
table.insert(ks, k) |
|
|
|
elseif type(k) == "string" then |
|
|
|
table.insert(sks, k) |
|
|
|
else |
|
|
|
table.insert(oks, k) |
|
|
|
end |
|
|
|
end |
|
|
|
table.sort(ks) |
|
|
|
table.sort(sks) |
|
|
|
for _, k in ipairs(sks) do |
|
|
|
table.insert(ks, k) |
|
|
|
end |
|
|
|
for _, k in ipairs(oks) do |
|
|
|
table.insert(ks, k) |
|
|
|
end |
|
|
|
local n = 0 |
|
|
|
f = table.pack(function() |
|
|
|
n = n + 1 |
|
|
|
local k = ks[n] |
|
|
|
if k ~= nil then |
|
|
|
return k, current_value[k] |
|
|
|
else |
|
|
|
return nil |
|
|
|
end |
|
|
|
end) |
|
|
|
else |
|
|
|
f = table.pack(local_pairs(current_value)) |
|
|
|
end |
|
|
|
local i = 1 |
|
|
|
local first = true |
|
|
|
table.insert(result_pack, "{") |
|
|
|
for k, v in table.unpack(f) do |
|
|
|
if not first then |
|
|
|
table.insert(result_pack, ",") |
|
|
|
if pretty then |
|
|
|
table.insert(result_pack, "\n" .. string.rep(" ", depth)) |
|
|
|
end |
|
|
|
end |
|
|
|
first = nil |
|
|
|
local tk = type(k) |
|
|
|
if tk == "number" and k == i then |
|
|
|
i = i + 1 |
|
|
|
recurse(v, depth + 1) |
|
|
|
else |
|
|
|
if tk == "string" and not kw[k] and string.match(k, id) then |
|
|
|
table.insert(result_pack, k) |
|
|
|
else |
|
|
|
table.insert(result_pack, "[") |
|
|
|
recurse(k, depth + 1) |
|
|
|
table.insert(result_pack, "]") |
|
|
|
end |
|
|
|
table.insert(result_pack, "=") |
|
|
|
recurse(v, depth + 1) |
|
|
|
end |
|
|
|
end |
|
|
|
ts[current_value] = nil -- allow writing same table more than once |
|
|
|
table.insert(result_pack, "}") |
|
|
|
function serial.serialize(value) |
|
|
|
local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true} |
|
|
|
local id="^[%a_][%w_]*$" |
|
|
|
local ts={} |
|
|
|
local function s(v,l) |
|
|
|
local t=type(v) |
|
|
|
if t=="nil" then return "nil" |
|
|
|
elseif t=="boolean" then return v and "true" or "false" |
|
|
|
elseif t=="number" then |
|
|
|
if v~=v then return "0/0" |
|
|
|
elseif v==math.huge then return "math.huge" |
|
|
|
elseif v==-math.huge then return "-math.huge" |
|
|
|
else return tostring(v) end |
|
|
|
elseif t=="string" then return string.format("%q",v):gsub("\\\n","\\n") |
|
|
|
elseif t=="table" then |
|
|
|
if ts[v] then error("tcyc") end |
|
|
|
ts[v]=true |
|
|
|
local i,r=1, nil |
|
|
|
local f=table.pack(local_pairs(v)) |
|
|
|
for k,v in table.unpack(f) do |
|
|
|
if r then r=r..","..(("\n"..string.rep(" ",l)) or "") |
|
|
|
else r="{" end |
|
|
|
local tk=type(k) |
|
|
|
if tk=="number" and k==i then |
|
|
|
i=i+1 |
|
|
|
r=r..s(v,l+1) |
|
|
|
else |
|
|
|
error("unsupported type: " .. t) |
|
|
|
end |
|
|
|
end |
|
|
|
recurse(value, 1) |
|
|
|
local result = table.concat(result_pack) |
|
|
|
if pretty then |
|
|
|
local limit = type(pretty) == "number" and pretty or 10 |
|
|
|
local truncate = 0 |
|
|
|
while limit > 0 and truncate do |
|
|
|
truncate = string.find(result, "\n", truncate + 1, true) |
|
|
|
limit = limit - 1 |
|
|
|
end |
|
|
|
if truncate then |
|
|
|
return result:sub(1, truncate) .. "..." |
|
|
|
end |
|
|
|
end |
|
|
|
return result |
|
|
|
if tk == "string" and not kw[k] and string.match(k,id) then r=r..k |
|
|
|
else r=r.."["..s(k,l+1).."]" end |
|
|
|
r=r.."="..s(v,l+1) end end |
|
|
|
ts[v]=nil |
|
|
|
return (r or "{").."}" |
|
|
|
else error("ut "..t) end end |
|
|
|
return s(value, 1) |
|
|
|
end |
|
|
|
|
|
|
|
function serialization.unserialize(data) -- returns the data contained in serialized string *data* |
|
|
|
local result, reason = load("return " .. data, "=data", nil, {math={huge=math.huge}}) |
|
|
|
if not result then |
|
|
|
return nil, reason |
|
|
|
end |
|
|
|
local ok, output = pcall(result) |
|
|
|
if not ok then |
|
|
|
return nil, output |
|
|
|
end |
|
|
|
return output |
|
|
|
function serial.unserialize(data) |
|
|
|
checkArg(1, data, "string") |
|
|
|
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}}) |
|
|
|
if not result then return nil, reason end |
|
|
|
local ok, output = pcall(result) |
|
|
|
if not ok then return nil, output end |
|
|
|
return output |
|
|
|
end |
|
|
|
|
|
|
|
return serialization |
|
|
|
return serial |