Serializer: Sauberer Umgang mit numerischen und zyklischen Tabellen

This commit is contained in:
Nero 2023-07-20 17:09:37 +00:00
parent 65c39c6cf7
commit 769cd77195

View File

@ -1,34 +1,54 @@
-- Tabelle in einen String umwandeln, rekursiv -- Tabelle in einen String umwandeln, rekursiv
-- Funktionen als Lua-Werte werden ignoriert -- Funktionen als Lua-Werte werden ignoriert
function __tostring(tab) -- forbidden ist ein set von elterntabellen zum erkennen von kreisreferenzen
local t=type(tab) function serialize(value, forbidden)
forbidden = forbidden or {}
local t=type(value)
if t=="table" then if t=="table" then
-- Kreisreferenz -> als nil serialisieren
if forbidden[value] then return "nil" end
forbidden[value]=true
local r="" local r=""
-- Tabellen-Keys sammeln und sortieren local numeric_indexes={}
-- Schnellversion fuer numerische Tabellen
for k,v in ipairs(value) do
numeric_indexes[k]=true
r=r..(r=="" and "" or ",")..serialize(v, forbidden)
end
-- Tabellen-Keys fuer assoziative Tabellen sammeln
local tkeys={} local tkeys={}
for k in pairs(tab) do table.insert(tkeys, k) end for k in pairs(value) do
-- Nur Keys, die nicht von ipairs() weiter oben schon verarbeitet wurden
if numeric_indexes[k]==nil then
table.insert(tkeys, k)
end
end
-- Tabellen-Keys sortieren
table.sort(tkeys, function(a,b) return tostring(a)<tostring(b) end) table.sort(tkeys, function(a,b) return tostring(a)<tostring(b) end)
-- Durch die Tabelle gehen -- Durch die Tabelle gehen
for _,k in ipairs(tkeys) do for _,k in ipairs(tkeys) do
-- Uns selbst fuer Tabellen-Value aufrufen local v=serialize(value[k], forbidden)
local v = __tostring(tab[k]) if v ~= "nil" then
-- ignore nil values, they unset the key anyways r=r
if v~= nil and v ~= "nil" then -- Komma, um von eventuellen vorherigen Daten abzugrenzen
if r~="" then r=r.."," end ..(r=="" and "" or ",")
-- short format: foo="bar", saves bytes -- Schluessel entweder direkt als string oder serialisiert in eckigen Klammern
if type(k)=="string" and k:match("^[%l%u_][%w_]*$") then ..((type(k)=="string" and k:match("^[%l%u_][%w_]*$")) and k or "["..serialize(k).."]")
r=r..k.."="..v -- Die Zuweisung
-- long format: ["foo"]="bar", allows weird keys .."="..v
else
r=r.."["..__tostring(k).."]="..v
end
end end
end end
forbidden[value]=false
return "{"..r.."}" return "{"..r.."}"
elseif t=="number" or t=="boolean" then elseif t=="number" or t=="boolean" then
return tostring(tab) return tostring(value)
elseif t=="string" then elseif t=="string" then
return ("%q"):format(tab) return string.format("%q",value)
else else
return "nil" return "nil"
end end
@ -39,7 +59,7 @@ function dump(...)
local args={...} local args={...}
for i=1,#args do for i=1,#args do
if type(args[i]) == "table" then if type(args[i]) == "table" then
args[i]=__tostring(args[i]) args[i]=serialize(args[i])
end end
end end
print(table.unpack(args)) print(table.unpack(args))
@ -50,7 +70,7 @@ end
-- Formatierungszeichen werden URL-encoded, damit EEP mit ihren zurecht kommt -- Formatierungszeichen werden URL-encoded, damit EEP mit ihren zurecht kommt
-- Speziell Hochkommas haben mir immer meine Daten abgeschnitten... -- Speziell Hochkommas haben mir immer meine Daten abgeschnitten...
function speicherTabelle(Slotnummer, Tabelle) function speicherTabelle(Slotnummer, Tabelle)
local s=__tostring(Tabelle):gsub("([%c%%\"])", function(c) local s=serialize(Tabelle):gsub("([%c%%\"])", function(c)
return string.format("%%%02X", string.byte(c)) return string.format("%%%02X", string.byte(c))
end) end)
EEPSaveData(Slotnummer, s) EEPSaveData(Slotnummer, s)