fork download
  1. local function timeTest(str, f, ...)
  2. local clock = os.clock()
  3. local r = f(...)
  4. print(str, os.clock()-clock)
  5. return r
  6. end
  7.  
  8. function main()
  9. local obj = {}
  10. for i=1,500 do
  11. obj[i] = {}
  12. for j=1,100,3 do
  13. obj[i][j] = math.random()
  14. obj[i][j+1] = "n:"..math.random()
  15. obj[i][j+2] = (math.random()>0.5 and true or false)
  16. end
  17. end
  18.  
  19. local s = ""
  20. local _obj
  21. print("Testing:")
  22. s = timeTest("serialize():", serialize, obj)
  23. _obj = timeTest("unserialize():", unserialize, s)
  24.  
  25. print()
  26. s = timeTest("serialization.serialize():", serialization.serialize, obj)
  27. _obj = timeTest("serialization.unserialize():", serialization.unserialize, s)
  28.  
  29. print()
  30. s = timeTest("table.toString():", table.toString, obj)
  31. _obj = timeTest("table.fromString():", table.fromString, s)
  32. end
  33.  
  34. -- ********************************************************************************** --
  35. -- ** Serialize table to string ** --
  36. -- ** ** --
  37. -- ** Modified version of http://l...content-available-to-author-only...s.org/wiki/SaveTableToFile ** --
  38. -- ** ** --
  39. -- ********************************************************************************** --
  40.  
  41. -- declare local variables
  42. --// exportstring( string )
  43. --// returns a "Lua" portable version of the string
  44. local function exportstring( s )
  45. return string.format("%q", s)
  46. end
  47.  
  48. local insert = table.insert
  49. local tostring= tostring
  50. local ipairs = ipairs
  51. local pairs = pairs
  52. local type = type
  53.  
  54. --// The Save Function
  55. function table.toString(tbl)
  56. if type(tbl) ~= 'table' then return "" end -- Argument not a table
  57.  
  58. local charS,charE = " ","\n"
  59. local s_tbl = {}
  60.  
  61. -- initiate variables for save procedure
  62. local tables,lookup = { tbl },{ [tbl] = 1 }
  63. insert(s_tbl, "return {"..charE )
  64.  
  65. for idx,t in ipairs( tables ) do
  66. insert(s_tbl, "-- Table: {"..idx.."}"..charE )
  67. insert(s_tbl, "{"..charE )
  68. local thandled = {}
  69.  
  70. for i,v in ipairs( t ) do
  71. thandled[i] = true
  72. local stype = type( v )
  73. -- only handle value
  74. if stype == "table" then
  75. if not lookup[v] then
  76. insert( tables, v )
  77. lookup[v] = #tables
  78. end
  79. insert(s_tbl, charS.."{"..lookup[v].."},"..charE )
  80. elseif stype == "string" then
  81. insert(s_tbl, charS..exportstring( v )..","..charE )
  82. elseif stype == "number" or stype == "boolean" then
  83. insert(s_tbl, charS..tostring( v )..","..charE )
  84. end
  85. end
  86.  
  87. for i,v in pairs( t ) do
  88. -- escape handled values
  89. if (not thandled[i]) then
  90.  
  91. local str = ""
  92. local stype = type( i )
  93. -- handle index
  94. if stype == "table" then
  95. if not lookup[i] then
  96. insert( tables,i )
  97. lookup[i] = #tables
  98. end
  99. str = charS.."[{"..lookup[i].."}]="
  100. elseif stype == "string" then
  101. str = charS.."["..exportstring( i ).."]="
  102. elseif stype == "number" or stype == "boolean" then
  103. str = charS.."["..tostring( i ).."]="
  104. end
  105.  
  106. if str ~= "" then
  107. stype = type( v )
  108. -- handle value
  109. if stype == "table" then
  110. if not lookup[v] then
  111. insert( tables,v )
  112. lookup[v] = #tables
  113. end
  114. insert(s_tbl, str.."{"..lookup[v].."},"..charE )
  115. elseif stype == "string" then
  116. insert(s_tbl, str..exportstring( v )..","..charE )
  117. elseif stype == "number" or stype == "boolean" then
  118. insert(s_tbl, str..tostring( v )..","..charE )
  119. end
  120. end
  121. end
  122. end
  123. insert(s_tbl, "},"..charE )
  124. end
  125. insert(s_tbl, "}" )
  126.  
  127. return table.concat(s_tbl)
  128. end
  129.  
  130. --// The Load Function
  131. function table.fromString(s)
  132. if not s then return end -- Argument not string
  133. local ftables = loadstring(s)
  134. if not ftables then return end -- String cant be parsed into function
  135. local tables = ftables()
  136. for idx = 1,#tables do
  137. local tolinki = {}
  138. for i,v in pairs( tables[idx] ) do
  139. if type( v ) == "table" then
  140. tables[idx][i] = tables[v[1]]
  141. end
  142. if type( i ) == "table" and tables[i[1]] then
  143. insert( tolinki,{ i,tables[i[1]] } )
  144. end
  145. end
  146. -- link indices
  147. for _,v in ipairs( tolinki ) do
  148. tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil
  149. end
  150. end
  151. return tables[1]
  152. end
  153.  
  154.  
  155.  
  156. local function serializeImpl( t, tTracking )
  157. local sType = type(t)
  158. if sType == "table" then
  159. if tTracking[t] ~= nil then
  160. error( "Cannot serialize table with recursive entries" )
  161. end
  162. tTracking[t] = true
  163.  
  164. local result = "{"
  165. for k,v in pairs(t) do
  166. result = result..("["..serializeImpl(k, tTracking).."]="..serializeImpl(v, tTracking)..",")
  167. end
  168. result = result.."}"
  169. return result
  170.  
  171. elseif sType == "string" then
  172. return string.format( "%q", t )
  173.  
  174. elseif sType == "number" or sType == "boolean" or sType == "nil" then
  175. return tostring(t)
  176.  
  177. else
  178. error( "Cannot serialize type "..sType )
  179.  
  180. end
  181. end
  182.  
  183. function serialize( t )
  184. local tTracking = {}
  185. return serializeImpl( t, tTracking )
  186. end
  187.  
  188. function unserialize( s )
  189. local func, e = loadstring( "return "..s, "serialize" )
  190. if not func then
  191. return s
  192. else
  193. --setfenv( func, {} )
  194. return func()
  195. end
  196. end
  197.  
  198.  
  199. serialization = {}
  200.  
  201. -- Important: pretty formatting will allow presenting non-serializable values
  202. -- but may generate output that cannot be unserialized back.
  203. function serialization.serialize(value, pretty)
  204. local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
  205. ["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
  206. ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
  207. ["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
  208. ["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
  209. ["until"]=true, ["while"]=true}
  210. local id = "^[%a_][%w_]*$"
  211. local ts = {}
  212. local function s(v, l)
  213. local t = type(v)
  214. if t == "nil" then
  215. return "nil"
  216. elseif t == "boolean" then
  217. return v and "true" or "false"
  218. elseif t == "number" then
  219. if v ~= v then
  220. return "0/0"
  221. elseif v == math.huge then
  222. return "math.huge"
  223. elseif v == -math.huge then
  224. return "-math.huge"
  225. else
  226. return tostring(v)
  227. end
  228. elseif t == "string" then
  229. return string.format("%q", v):gsub("\\\n","\\n")
  230. elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
  231. return tostring(v)
  232. elseif t == "table" then
  233. if ts[v] then
  234. if pretty then
  235. return "recursion"
  236. else
  237. error("tables with cycles are not supported")
  238. end
  239. end
  240. ts[v] = true
  241. local i, r = 1, nil
  242. local f
  243. if pretty then
  244. local ks, sks, oks = {}, {}, {}
  245. for k in pairs(v) do
  246. if type(k) == "number" then
  247. table.insert(ks, k)
  248. elseif type(k) == "string" then
  249. table.insert(sks, k)
  250. else
  251. table.insert(oks, k)
  252. end
  253. end
  254. table.sort(ks)
  255. table.sort(sks)
  256. for _, k in ipairs(sks) do
  257. table.insert(ks, k)
  258. end
  259. for _, k in ipairs(oks) do
  260. table.insert(ks, k)
  261. end
  262. local n = 0
  263. f = table.pack(function()
  264. n = n + 1
  265. local k = ks[n]
  266. if k ~= nil then
  267. return k, v[k]
  268. else
  269. return nil
  270. end
  271. end)
  272. else
  273. f = table.pack(pairs(v))
  274. end
  275. for k, v in table.unpack(f) do
  276. if r then
  277. r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
  278. else
  279. r = "{"
  280. end
  281. local tk = type(k)
  282. if tk == "number" and k == i then
  283. i = i + 1
  284. r = r .. s(v, l + 1)
  285. else
  286. if tk == "string" and not kw[k] and string.match(k, id) then
  287. r = r .. k
  288. else
  289. r = r .. "[" .. s(k, l + 1) .. "]"
  290. end
  291. r = r .. "=" .. s(v, l + 1)
  292. end
  293. end
  294. ts[v] = nil -- allow writing same table more than once
  295. return (r or "{") .. "}"
  296. else
  297. if pretty then
  298. return tostring(t)
  299. else
  300. error("unsupported type: " .. t)
  301. end
  302. end
  303. end
  304. local result = s(value, 1)
  305. local limit = type(pretty) == "number" and pretty or 10
  306. if pretty then
  307. local truncate = 0
  308. while limit > 0 and truncate do
  309. truncate = string.find(result, "\n", truncate + 1, true)
  310. limit = limit - 1
  311. end
  312. if truncate then
  313. return result:sub(1, truncate) .. "..."
  314. end
  315. end
  316. return result
  317. end
  318.  
  319. function serialization.unserialize(data)
  320. --checkArg(1, data, "string")
  321. local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
  322. if not result then
  323. return nil, reason
  324. end
  325. local ok, output = pcall(result)
  326. if not ok then
  327. return nil, output
  328. end
  329. return output
  330. end
  331.  
  332.  
  333. main()
Success #stdin #stdout 2.34s 2832KB
stdin
Standard input is empty
stdout
Testing:
serialize():	0.947887
unserialize():	0.104104

serialization.serialize():	0.92177
serialization.unserialize():	0.059995

table.toString():	0.159426
table.fromString():	0.089499