Last active
December 30, 2015 04:39
-
-
Save pingbird/7777388 to your computer and use it in GitHub Desktop.
improved serialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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