persistence.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. -- Internal persistence library
  2. --[[ Provides ]]
  3. -- persistence.store(path, ...): Stores arbitrary items to the file at the given path
  4. -- persistence.load(path): Loads files that were previously stored with store and returns them
  5. --[[ Limitations ]]
  6. -- Does not export userdata, threads or most function values
  7. -- Function export is not portable
  8. --[[ License: MIT (see bottom) ]]
  9. -- Private methods
  10. local write, writeIndent, writers, refCount;
  11. -- write thing (dispatcher)
  12. write = function (file, item, level, objRefNames)
  13. writers[type(item)](file, item, level, objRefNames);
  14. end;
  15. -- write indent
  16. writeIndent = function (file, level)
  17. for i = 1, level do
  18. file:write("\t");
  19. end;
  20. end;
  21. -- recursively count references
  22. refCount = function (objRefCount, item)
  23. -- only count reference types (tables)
  24. if type(item) == "table" then
  25. -- Increase ref count
  26. if objRefCount[item] then
  27. objRefCount[item] = objRefCount[item] + 1;
  28. else
  29. objRefCount[item] = 1;
  30. -- If first encounter, traverse
  31. for k, v in pairs(item) do
  32. refCount(objRefCount, k);
  33. refCount(objRefCount, v);
  34. end;
  35. end;
  36. end;
  37. end;
  38. -- Format items for the purpose of restoring
  39. writers = {
  40. ["nil"] = function (file, item)
  41. file:write("nil");
  42. end;
  43. ["number"] = function (file, item)
  44. file:write(tostring(item));
  45. end;
  46. ["string"] = function (file, item)
  47. file:write(string.format("%q", item));
  48. end;
  49. ["boolean"] = function (file, item)
  50. if item then
  51. file:write("true");
  52. else
  53. file:write("false");
  54. end
  55. end;
  56. ["table"] = function (file, item, level, objRefNames)
  57. local refIdx = objRefNames[item];
  58. if refIdx then
  59. -- Table with multiple references
  60. file:write("multiRefObjects["..refIdx.."]");
  61. else
  62. -- Single use table
  63. file:write("{\n");
  64. for k, v in pairs(item) do
  65. writeIndent(file, level+1);
  66. file:write("[");
  67. write(file, k, level+1, objRefNames);
  68. file:write("] = ");
  69. write(file, v, level+1, objRefNames);
  70. file:write(";\n");
  71. end
  72. writeIndent(file, level);
  73. file:write("}");
  74. end;
  75. end;
  76. ["function"] = function (file, item)
  77. -- Does only work for "normal" functions, not those
  78. -- with upvalues or c functions
  79. local dInfo = debug.getinfo(item, "uS");
  80. if dInfo.nups > 0 then
  81. file:write("nil --[[functions with upvalue not supported]]");
  82. elseif dInfo.what ~= "Lua" then
  83. file:write("nil --[[non-lua function not supported]]");
  84. else
  85. local r, s = pcall(string.dump,item);
  86. if r then
  87. file:write(string.format("loadstring(%q)", s));
  88. else
  89. file:write("nil --[[function could not be dumped]]");
  90. end
  91. end
  92. end;
  93. ["thread"] = function (file, item)
  94. file:write("nil --[[thread]]\n");
  95. end;
  96. ["userdata"] = function (file, item)
  97. file:write("nil --[[userdata]]\n");
  98. end;
  99. }
  100. return function (path, ...)
  101. local file, e;
  102. if type(path) == "string" then
  103. -- Path, open a file
  104. file, e = io.open(path, "w");
  105. if not file then
  106. return error(e);
  107. end
  108. else
  109. -- Just treat it as file
  110. file = path;
  111. end
  112. local n = select("#", ...);
  113. -- Count references
  114. local objRefCount = {}; -- Stores reference that will be exported
  115. for i = 1, n do
  116. refCount(objRefCount, (select(i,...)));
  117. end;
  118. -- Export Objects with more than one ref and assign name
  119. -- First, create empty tables for each
  120. local objRefNames = {};
  121. local objRefIdx = 0;
  122. file:write("-- Persistent Data\n");
  123. file:write("local multiRefObjects = {\n");
  124. for obj, count in pairs(objRefCount) do
  125. if count > 1 then
  126. objRefIdx = objRefIdx + 1;
  127. objRefNames[obj] = objRefIdx;
  128. file:write("{};"); -- table objRefIdx
  129. end;
  130. end;
  131. file:write("\n} -- multiRefObjects\n");
  132. -- Then fill them (this requires all empty multiRefObjects to exist)
  133. for obj, idx in pairs(objRefNames) do
  134. for k, v in pairs(obj) do
  135. file:write("multiRefObjects["..idx.."][");
  136. write(file, k, 0, objRefNames);
  137. file:write("] = ");
  138. write(file, v, 0, objRefNames);
  139. file:write(";\n");
  140. end;
  141. end;
  142. -- Create the remaining objects
  143. for i = 1, n do
  144. file:write("local ".."obj"..i.." = ");
  145. write(file, (select(i,...)), 0, objRefNames);
  146. file:write("\n");
  147. end
  148. -- Return them
  149. if n > 0 then
  150. file:write("return obj1");
  151. for i = 2, n do
  152. file:write(" ,obj"..i);
  153. end;
  154. file:write("\n");
  155. else
  156. file:write("return\n");
  157. end;
  158. file:close();
  159. end, function (path)
  160. local f, e = loadfile(path);
  161. if f then
  162. return f();
  163. else
  164. return nil, e;
  165. end;
  166. end
  167. --[[
  168. Copyright (c) 2010 Gerhard Roethlin
  169. Permission is hereby granted, free of charge, to any person
  170. obtaining a copy of this software and associated documentation
  171. files (the "Software"), to deal in the Software without
  172. restriction, including without limitation the rights to use,
  173. copy, modify, merge, publish, distribute, sublicense, and/or sell
  174. copies of the Software, and to permit persons to whom the
  175. Software is furnished to do so, subject to the following
  176. conditions:
  177. The above copyright notice and this permission notice shall be
  178. included in all copies or substantial portions of the Software.
  179. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  180. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  181. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  182. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  183. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  184. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  185. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  186. OTHER DEALINGS IN THE SOFTWARE.
  187. ]]