serialize.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. --- Lua module to serialize values as Lua code.
  2. -- From: https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua
  3. -- License: MIT
  4. -- @copyright 2006-2997 Fabien Fleutot <metalua@gmail.com>
  5. -- @author Fabien Fleutot <metalua@gmail.com>
  6. -- @author ShadowNinja <shadowninja@minetest.net>
  7. --------------------------------------------------------------------------------
  8. --- Serialize an object into a source code string. This string, when passed as
  9. -- an argument to deserialize(), returns an object structurally identical to
  10. -- the original one. The following are currently supported:
  11. -- * Booleans, numbers, strings, and nil.
  12. -- * Functions; uses interpreter-dependent (and sometimes platform-dependent) bytecode!
  13. -- * Tables; they can cantain multiple references and can be recursive, but metatables aren't saved.
  14. -- This works in two phases:
  15. -- 1. Recursively find and record multiple references and recursion.
  16. -- 2. Recursively dump the value into a string.
  17. -- @param x Value to serialize (nil is allowed).
  18. -- @return load()able string containing the value.
  19. function core.serialize(x)
  20. local local_index = 1 -- Top index of the "_" local table in the dump
  21. -- table->nil/1/2 set of tables seen.
  22. -- nil = not seen, 1 = seen once, 2 = seen multiple times.
  23. local seen = {}
  24. -- nest_points are places where a table appears within itself, directly
  25. -- or not. For instance, all of these chunks create nest points in
  26. -- table x: "x = {}; x[x] = 1", "x = {}; x[1] = x",
  27. -- "x = {}; x[1] = {y = {x}}".
  28. -- To handle those, two tables are used by mark_nest_point:
  29. -- * nested - Transient set of tables being currently traversed.
  30. -- Used for detecting nested tables.
  31. -- * nest_points - parent->{key=value, ...} table cantaining the nested
  32. -- keys and values in the parent. They're all dumped after all the
  33. -- other table operations have been performed.
  34. --
  35. -- mark_nest_point(p, k, v) fills nest_points with information required
  36. -- to remember that key/value (k, v) creates a nest point in table
  37. -- parent. It also marks "parent" and the nested item(s) as occuring
  38. -- multiple times, since several references to it will be required in
  39. -- order to patch the nest points.
  40. local nest_points = {}
  41. local nested = {}
  42. local function mark_nest_point(parent, k, v)
  43. local nk, nv = nested[k], nested[v]
  44. local np = nest_points[parent]
  45. if not np then
  46. np = {}
  47. nest_points[parent] = np
  48. end
  49. np[k] = v
  50. seen[parent] = 2
  51. if nk then seen[k] = 2 end
  52. if nv then seen[v] = 2 end
  53. end
  54. -- First phase, list the tables and functions which appear more than
  55. -- once in x.
  56. local function mark_multiple_occurences(x)
  57. local tp = type(x)
  58. if tp ~= "table" and tp ~= "function" then
  59. -- No identity (comparison is done by value, not by instance)
  60. return
  61. end
  62. if seen[x] == 1 then
  63. seen[x] = 2
  64. elseif seen[x] ~= 2 then
  65. seen[x] = 1
  66. end
  67. if tp == "table" then
  68. nested[x] = true
  69. for k, v in pairs(x) do
  70. if nested[k] or nested[v] then
  71. mark_nest_point(x, k, v)
  72. else
  73. mark_multiple_occurences(k)
  74. mark_multiple_occurences(v)
  75. end
  76. end
  77. nested[x] = nil
  78. end
  79. end
  80. local dumped = {} -- object->varname set
  81. local local_defs = {} -- Dumped local definitions as source code lines
  82. -- Mutually recursive local functions:
  83. local dump_val, dump_or_ref_val
  84. -- If x occurs multiple times, dump the local variable rather than
  85. -- the value. If it's the first time it's dumped, also dump the
  86. -- content in local_defs.
  87. function dump_or_ref_val(x)
  88. if seen[x] ~= 2 then
  89. return dump_val(x)
  90. end
  91. local var = dumped[x]
  92. if var then -- Already referenced
  93. return var
  94. end
  95. -- First occurence, create and register reference
  96. local val = dump_val(x)
  97. local i = local_index
  98. local_index = local_index + 1
  99. var = "_["..i.."]"
  100. local_defs[#local_defs + 1] = var.." = "..val
  101. dumped[x] = var
  102. return var
  103. end
  104. -- Second phase. Dump the object; subparts occuring multiple times
  105. -- are dumped in local variables which can be referenced multiple
  106. -- times. Care is taken to dump local vars in a sensible order.
  107. function dump_val(x)
  108. local tp = type(x)
  109. if x == nil then return "nil"
  110. elseif tp == "string" then return string.format("%q", x)
  111. elseif tp == "boolean" then return x and "true" or "false"
  112. elseif tp == "function" then
  113. return string.format("loadstring(%q)", string.dump(x))
  114. elseif tp == "number" then
  115. -- Serialize numbers reversibly with string.format
  116. return string.format("%.17g", x)
  117. elseif tp == "table" then
  118. local vals = {}
  119. local idx_dumped = {}
  120. local np = nest_points[x]
  121. for i, v in ipairs(x) do
  122. if not np or not np[i] then
  123. vals[#vals + 1] = dump_or_ref_val(v)
  124. end
  125. idx_dumped[i] = true
  126. end
  127. for k, v in pairs(x) do
  128. if (not np or not np[k]) and
  129. not idx_dumped[k] then
  130. vals[#vals + 1] = "["..dump_or_ref_val(k).."] = "
  131. ..dump_or_ref_val(v)
  132. end
  133. end
  134. return "{"..table.concat(vals, ", ").."}"
  135. else
  136. error("Can't serialize data of type "..tp)
  137. end
  138. end
  139. local function dump_nest_points()
  140. for parent, vals in pairs(nest_points) do
  141. for k, v in pairs(vals) do
  142. local_defs[#local_defs + 1] = dump_or_ref_val(parent)
  143. .."["..dump_or_ref_val(k).."] = "
  144. ..dump_or_ref_val(v)
  145. end
  146. end
  147. end
  148. mark_multiple_occurences(x)
  149. local top_level = dump_or_ref_val(x)
  150. dump_nest_points()
  151. if next(local_defs) then
  152. return "local _ = {}\n"
  153. ..table.concat(local_defs, "\n")
  154. .."\nreturn "..top_level
  155. else
  156. return "return "..top_level
  157. end
  158. end
  159. -- Deserialization
  160. local function safe_loadstring(...)
  161. local func, err = loadstring(...)
  162. if func then
  163. setfenv(func, {})
  164. return func
  165. end
  166. return nil, err
  167. end
  168. local function dummy_func() end
  169. function core.deserialize(str, safe)
  170. if type(str) ~= "string" then
  171. return nil, "Cannot deserialize type '"..type(str)
  172. .."'. Argument must be a string."
  173. end
  174. if str:byte(1) == 0x1B then
  175. return nil, "Bytecode prohibited"
  176. end
  177. local f, err = loadstring(str)
  178. if not f then return nil, err end
  179. -- The environment is recreated every time so deseralized code cannot
  180. -- pollute it with permanent references.
  181. setfenv(f, {loadstring = safe and dummy_func or safe_loadstring})
  182. local good, data = pcall(f)
  183. if good then
  184. return data
  185. else
  186. return nil, data
  187. end
  188. end