|
- -- 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) -- 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, "}")
- 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) -- 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
- end
-
- return serialization
|