solana/programs/native/lua_loader/serialize.lua

175 lines
3.9 KiB
Lua

----------------------------------------------------------------
-- serialize.lua
--
-- Exports:
--
-- orderedPairs : deterministically ordered version of pairs()
--
-- serialize : convert Lua value to string in Lua syntax
--
----------------------------------------------------------------
-- orderedPairs: iterate over table elements in deterministic order. First,
-- array elements are returned, then remaining elements sorted by the key's
-- type and value.
-- compare any two Lua values, establishing a complete ordering
local function ltAny(a,b)
local ta, tb = type(a), type(b)
if ta ~= tb then
return ta < tb
end
if ta == "string" or ta == "number" then
return a < b
end
return tostring(a) < tostring(b)
end
local inext = ipairs{}
local function orderedPairs(t)
local keys = {}
local keyIndex = 1
local counting = true
local function _next(seen, s)
local v
if counting then
-- return next array index
s, v = inext(t, s)
if s ~= nil then
seen[s] = true
return s,v
end
counting = false
-- construct sorted unseen keys
for k,v in pairs(t) do
if not seen[k] then
table.insert(keys, k)
end
end
table.sort(keys, ltAny)
end
-- return next unseen table element
s = keys[keyIndex]
if s ~= nil then
keyIndex = keyIndex + 1
v = t[s]
end
return s, v
end
return _next, {}, 0
end
-- avoid 'nan', 'inf', and '-inf'
local numtostring = {
[tostring(-1/0)] = "-1/0",
[tostring(1/0)] = "1/0",
[tostring(0/0)] = "0/0"
}
setmetatable(numtostring, { __index = function (t, k) return k end })
-- serialize: Serialize a Lua data structure
--
-- x = value to serialize
-- out = function to be called repeatedly with strings, or
-- table into which strings should be inserted, or
-- nil => return a string
-- iter = function to iterate over table elements, or
-- "s" to sort elements by key, or
-- nil for default (fastest)
--
-- Notes:
-- * Does not support self-referential data structures.
-- * Does not optimize for repeated sub-expressions.
-- * Does not preserve topology; only values.
-- * Does not handle types other than nil, number, boolean, string, table
--
local function serialize(x, out, iter)
local visited = {}
local iter = iter=="s" and orderedPairs or iter or pairs
assert(type(iter) == "function")
local function _serialize(x)
if type(x) == "string" then
out(string.format("%q", x))
elseif type(x) == "number" then
out(numtostring[tostring(x)])
elseif type(x) == "boolean" or
type(x) == "nil" then
out(tostring(x))
elseif type(x) == "table" then
if visited[x] then
error("serialize: recursive structure")
end
visited[x] = true
local first, nextIndex = true, 1
out "{"
for k,v in iter(x) do
if first then
first = false
else
out ","
end
if k == nextIndex then
nextIndex = nextIndex + 1
else
if type(k) == "string" and k:match("^[%a_][%w_]*$") then
out(k.."=")
else
out "["
_serialize(k)
out "]="
end
end
_serialize(v)
end
out "}"
visited[x] = false
else
error("serialize: unsupported type")
end
end
local result
if not out then
result = {}
out = result
end
if type(out) == "table" then
local t = out
function out(s)
table.insert(t,s)
end
end
_serialize(x)
if result then
return table.concat(result)
end
end
return {
orderedPairs = orderedPairs,
serialize = serialize
}