-- serialization lib borrowed from OpenOS local serialization = {} -- delay loaded tables fail to deserialize cross [C] boundaries (such as when having to read files that cause yields) local local_pairs = function(tbl) local mt = getmetatable(tbl) return (mt and mt.__pairs or pairs)(tbl) end -- Important: pretty formatting will allow presenting non-serializable values -- but may generate output that cannot be unserialized back. function serialization.serialize(value, pretty) 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, "}") 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 end function serialization.unserialize(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 end return serialization