local function timeTest(str, f, ...) local clock = os.clock() local r = f(...) print(str, os.clock()-clock) return r end function main() local obj = {} for i=1,1000 do obj[i] = {} for j=1,100,3 do obj[i][j] = math.random() obj[i][j+1] = "n:"..math.random() obj[i][j+2] = (math.random()>0.5 and true or false) end end local s = "" local _obj print("Testing:") --s = timeTest("serialize():", serialize, obj) --_obj = timeTest("unserialize():", unserialize, s) --print() --s = timeTest("serialization.serialize():", serialization.serialize, obj) --_obj = timeTest("serialization.unserialize():", serialization.unserialize, s) --print() s = timeTest("table.toString():", table.toString, obj) _obj = timeTest("table.fromString():", table.fromString, s) end -- ********************************************************************************** -- -- ** Serialize table to string ** -- -- ** ** -- -- ** Modified version of http://l...content-available-to-author-only...s.org/wiki/SaveTableToFile ** -- -- ** ** -- -- ********************************************************************************** -- -- declare local variables --// exportstring( string ) --// returns a "Lua" portable version of the string local function exportstring( s ) return string.format("%q", s) end local insert = table.insert local tostring= tostring local ipairs = ipairs local pairs = pairs local type = type --// The Save Function function table.toString(tbl) if type(tbl) ~= 'table' then return "" end -- Argument not a table local charS,charE = " ","\n" local s_tbl = {} -- initiate variables for save procedure local tables,lookup = { tbl },{ [tbl] = 1 } insert(s_tbl, "return {"..charE ) for idx,t in ipairs( tables ) do insert(s_tbl, "-- Table: {"..idx.."}"..charE ) insert(s_tbl, "{"..charE ) local thandled = {} for i,v in ipairs( t ) do thandled[i] = true local stype = type( v ) -- only handle value if stype == "table" then if not lookup[v] then insert( tables, v ) lookup[v] = #tables end insert(s_tbl, charS.."{"..lookup[v].."},"..charE ) elseif stype == "string" then insert(s_tbl, charS..exportstring( v )..","..charE ) elseif stype == "number" or stype == "boolean" then insert(s_tbl, charS..tostring( v )..","..charE ) end end for i,v in pairs( t ) do -- escape handled values if (not thandled[i]) then local str = "" local stype = type( i ) -- handle index if stype == "table" then if not lookup[i] then insert( tables,i ) lookup[i] = #tables end str = charS.."[{"..lookup[i].."}]=" elseif stype == "string" then str = charS.."["..exportstring( i ).."]=" elseif stype == "number" or stype == "boolean" then str = charS.."["..tostring( i ).."]=" end if str ~= "" then stype = type( v ) -- handle value if stype == "table" then if not lookup[v] then insert( tables,v ) lookup[v] = #tables end insert(s_tbl, str.."{"..lookup[v].."},"..charE ) elseif stype == "string" then insert(s_tbl, str..exportstring( v )..","..charE ) elseif stype == "number" or stype == "boolean" then insert(s_tbl, str..tostring( v )..","..charE ) end end end end insert(s_tbl, "},"..charE ) end insert(s_tbl, "}" ) return table.concat(s_tbl) end --// The Load Function function table.fromString(s) if not s then return end -- Argument not string local ftables = loadstring(s) if not ftables then return end -- String cant be parsed into function local tables = ftables() for idx = 1,#tables do local tolinki = {} for i,v in pairs( tables[idx] ) do if type( v ) == "table" then tables[idx][i] = tables[v[1]] end if type( i ) == "table" and tables[i[1]] then insert( tolinki,{ i,tables[i[1]] } ) end end -- link indices for _,v in ipairs( tolinki ) do tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil end end return tables[1] end local function serializeImpl( t, tTracking ) local sType = type(t) if sType == "table" then if tTracking[t] ~= nil then error( "Cannot serialize table with recursive entries" ) end tTracking[t] = true local result = "{" for k,v in pairs(t) do result = result..("["..serializeImpl(k, tTracking).."]="..serializeImpl(v, tTracking)..",") end result = result.."}" return result elseif sType == "string" then return string.format( "%q", t ) elseif sType == "number" or sType == "boolean" or sType == "nil" then return tostring(t) else error( "Cannot serialize type "..sType ) end end function serialize( t ) local tTracking = {} return serializeImpl( t, tTracking ) end function unserialize( s ) local func, e = loadstring( "return "..s, "serialize" ) if not func then return s else --setfenv( func, {} ) return func() end end serialization = {} -- Important: pretty formatting will allow presenting non-serializable values -- but may generate output that cannot be unserialized back. function serialization.serialize(value, pretty) 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 function s(v, l) local t = type(v) if t == "nil" then return "nil" elseif t == "boolean" then return v and "true" or "false" elseif t == "number" then if v ~= v then return "0/0" elseif v == math.huge then return "math.huge" elseif v == -math.huge then return "-math.huge" else return tostring(v) end elseif t == "string" then return string.format("%q", v):gsub("\\\n","\\n") elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then return tostring(v) elseif t == "table" then if ts[v] then if pretty then return "recursion" else error("tables with cycles are not supported") end end ts[v] = true local i, r = 1, nil local f if pretty then local ks, sks, oks = {}, {}, {} for k in pairs(v) 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, v[k] else return nil end end) else f = table.pack(pairs(v)) end for k, v in table.unpack(f) do if r then r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "") else r = "{" end local tk = type(k) if tk == "number" and k == i then i = i + 1 r = r .. s(v, l + 1) else if tk == "string" and not kw[k] and string.match(k, id) then r = r .. k else r = r .. "[" .. s(k, l + 1) .. "]" end r = r .. "=" .. s(v, l + 1) end end ts[v] = nil -- allow writing same table more than once return (r or "{") .. "}" else if pretty then return tostring(t) else error("unsupported type: " .. t) end end end local result = s(value, 1) local limit = type(pretty) == "number" and pretty or 10 if pretty then 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) --checkArg(1, data, "string") local result, reason = load("return " .. data, "=data", _, {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 main()