Skip to content

Instantly share code, notes, and snippets.

@pingbird
Last active December 30, 2015 04:39
Show Gist options
  • Save pingbird/7777388 to your computer and use it in GitHub Desktop.
Save pingbird/7777388 to your computer and use it in GitHub Desktop.
improved serialization
local _tob64={
[0]="A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"0","1","2","3","4","5","6","7","8","9","+","/"
}
function tob64(stxt)
local txt=tostring(stxt)
if not txt then
error("string expected, got "..type(stxt),2)
end
local d,o,d1,d2,d3={string.byte(txt,1,#txt)},""
for l1=1,#txt-2,3 do
d1,d2,d3=d[l1],d[l1+1],d[l1+2]
o=o.._tob64[math.floor(d1/4)].._tob64[((d1%4)*16)+math.floor(d2/16)].._tob64[((d2%16)*4)+math.floor(d3/64)].._tob64[d3%64]
end
local m=#txt%3
if m==1 then
o=o.._tob64[math.floor(d[#txt]/4)].._tob64[((d[#txt]%4)*16)].."=="
elseif m==2 then
o=o.._tob64[math.floor(d[#txt-1]/4)].._tob64[((d[#txt-1]%4)*16)+math.floor(d[#txt]/16)].._tob64[(d[#txt]%16)*4].."="
end
return o
end
local _unb64={
["A"]=0,["B"]=1,["C"]=2,["D"]=3,["E"]=4,["F"]=5,["G"]=6,["H"]=7,["I"]=8,["J"]=9,["K"]=10,["L"]=11,["M"]=12,["N"]=13,
["O"]=14,["P"]=15,["Q"]=16,["R"]=17,["S"]=18,["T"]=19,["U"]=20,["V"]=21,["W"]=22,["X"]=23,["Y"]=24,["Z"]=25,
["a"]=26,["b"]=27,["c"]=28,["d"]=29,["e"]=30,["f"]=31,["g"]=32,["h"]=33,["i"]=34,["j"]=35,["k"]=36,["l"]=37,["m"]=38,
["n"]=39,["o"]=40,["p"]=41,["q"]=42,["r"]=43,["s"]=44,["t"]=45,["u"]=46,["v"]=47,["w"]=48,["x"]=49,["y"]=50,["z"]=51,
["0"]=52,["1"]=53,["2"]=54,["3"]=55,["4"]=56,["5"]=57,["6"]=58,["7"]=59,["8"]=60,["9"]=61,["+"]=62,["/"]=63,
}
function unb64(stxt)
local txt=tostring(stxt)
if not txt then
error("string expected, got "..type(stxt),2)
end
txt=txt:gsub("[^%a%d/%+]","")
local m=#txt%4
if m==1 then
error("invalid b64",2)
end
local o,d1,d2=""
for l1=1,#txt-3,4 do
d1,d2=_unb64[txt:sub(l1+1,l1+1)],_unb64[txt:sub(l1+2,l1+2)]
o=o..string.char((_unb64[txt:sub(l1,l1)]*4)+math.floor(d1/16),((d1%16)*16)+math.floor(d2/4),((d2%4)*64)+_unb64[txt:sub(l1+3,l1+3)])
end
if m==2 then
o=o..string.char((_unb64[txt:sub(-2,-2)]*4)+math.floor(_unb64[txt:sub(-1,-1)]/16))
elseif m==3 then
d1=_unb64[txt:sub(-2,-2)]
o=o..string.char((_unb64[txt:sub(-3,-3)]*4)+math.floor(d1/16),((d1%16)*16)+math.floor(_unb64[txt:sub(-1,-1)]/4))
end
return o
end
function serialize(dat,options)
options=options or {}
local out=""
local queue={{dat}}
local cv=0
local keydat
local ptbl={}
while queue[1] do
local cu=queue[1]
table.remove(queue,1)
local typ=type(cu[1])
local ot
if typ=="string" then
ot=string.gsub(string.format("%q",cu[1]),"\\\n","\\n")
elseif typ=="number" or typ=="boolean" or typ=="nil" then
ot=tostring(cu[1])
elseif typ=="table" then
local empty=true
ot="{"
local st=0
ptbl[#ptbl+1]=cu[1]
for k,v in pairs(cu[1]) do
empty=false
st=st+1
table.insert(queue,st,{k,"key"})
st=st+1
local val=v
if type(v)=="table" then
for n,l in pairs(ptbl) do
if l==v then
if options.nofalse then
val="recursive"
elseif options.noerror then
return false
else
error("Cannot handle recursive tables.",2)
end
end
end
end
table.insert(queue,st,{val,"value",nil,st/2})
end
if empty then
ot=ot.."}"
ptbl[#ptbl]=nil
typ="emptytable"
else
cv=cv+1
if cu[3] then
queue[st][3]=cu[3]
cu[3]=nil
end
queue[st][3]=(queue[st][3] or 0)+1
end
elseif typ=="function" then
if options.nofunc then
ot="function"
else
local e,r,er=pcall(string.dump,cu[1])
if e and r then
ot="f(\""..tob64(r).."\")"
else
if options.nofalse then
ot="invalid function"
elseif options.noerror then
return false
else
error(r or er,2)
end
end
end
end
if cu[2]=="key" then
if type(ot)=="string" then
local nt=ot:sub(2,-2)
local e,r=loadstring("return {"..nt.."=true}")
if options.noshortkey or not e then
ot="["..ot.."]="
else
ot=nt.."="
end
else
ot=ot.."="
end
keydat={cu[1],ot}
ot=""
elseif cu[2]=="value" then
if keydat[1]~=cu[4] then
ot=keydat[2]..ot
end
if cu[3] then
ot=ot..("}"):rep(cu[3])
for l1=1,cu[3] do
ptbl[#ptbl]=nil
end
cv=cv-cu[3]
if cv~=0 then
ot=ot..","
end
elseif typ~="table" then
ot=ot..","
end
end
out=out..ot
end
return out
end
function serializeRecursive(dat,nofunc,rc)
local tpe=type(dat)
if tpe=="table" then
local result="{"
local aset=1
local comma=false
for k,v in pairs(dat) do
comma=true
if k==aset then
result=result..serializeRecursive(v,nofunc)..","
aset=aset+1
else
local tmp=serializeImpl(k,nofunc)
local tmp2=serializeImpl(v,nofunc)
if type(k)=="string" then
local r=loadstring("return {"..k.."="..(nofunc and "true" or tmp2).."}")
if r and not string.find(k,",") then
result=result..k.."="..tmp2..","
else
result=result.."["..tmp.."]="..tmp2..","
end
else
result=result.."["..tmp.."]="..tmp2..","
end
end
end
if comma then
result=string.sub(result,1,-2)
end
result=result.."}"
return result
elseif sType == "string" then
local s=string.gsub(string.format("%q",dat),"\\\n","\\n")
return s
elseif sType == "number" or sType == "boolean" or sType == "nil" then
return tostring(dat)
elseif sType == "function" then -- function i added
if nofunc then
return "function"
else
local status,data=pcall(string.dump,dat) -- convert the function into a string
if status then
return "f("..tob64(data)..")" -- format it so it dosent screw up syntax
else
return "badfunc"
end
end
else
error("unknown type: "..tpe)
end
end
function unserialize(s,tf) -- converts a string back into its original form
do -- check to make sure it isnt attempting to call anything
local strtype=0
local esc=false
local bc=false
for c in s:gmatch(".") do
if strtype>0 then
if esc then
esc=false
elseif c=="\\" then
esc=true
elseif (strtype==1 and c=="\"") or (strtype==2 and c=="'") then
strtype=0
end
elseif c=="\"" then
strtype=1
elseif c=="'" then
strtype=2
end
if bc then
if c==")" then
error("Function call detected.",2)
else
bc=false
end
elseif c=="(" and strtype==0 then
bc=true
end
end
end
if type(s)~="string" then
error("String exepcted. got "..type(s),2)
end
local func,e=loadstring("return "..s,"unserialize")
if not func then
error("Invalid string.",2)
end
return setfenv(func,{f=function(s) return loadstring(unb64(s)) end})()
end
function c(...)
local r=serialize({...},{noerror=true})
if r then
r=r:sub(2,-2)
end
return r
end
function d(S)
if not S then
return false,"nil"
end
local E,D,C=pcall(unserialize,"{"..S.."}")
if C or not E then
return false,D
else
return type(D)=="table" and D
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment