Advertisement
ZorbaTHut

Addon persistence code, public

May 14th, 2014
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.03 KB | None | 0 0
  1. --[[
  2. Copyright (c) 2010 The Color Black
  3. Copyright (c) 2011-2014 Trion Worlds, Inc.
  4.  
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11.  
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14.  
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. THE SOFTWARE.
  22. ]]
  23.  
  24. local type = _G.type
  25. local tostring = _G.tostring
  26. local rawget = _G.rawget
  27. local sformat = _G.string.format
  28. local tinsert = _G.table.insert
  29. local tremove = _G.table.remove
  30. local tconcat = _G.table.concat
  31. local tsort = _G.table.sort
  32. local pairs = _G.pairs
  33. local ipairs = _G.ipairs
  34. local print = _G.print
  35. local select = _G.select
  36. local unpack = _G.unpack
  37. local loadstring = _G.loadstring
  38. local max = _G.math.max
  39. local time = _G.os.time
  40.  
  41. local function sorter(a, b)
  42. if type(a) ~= type(b) then
  43. return type(a) < type(b)
  44. end
  45.  
  46. if type(a) == "number" or type(a) == "string" then
  47. return a < b
  48. elseif type(a) == "boolean" then
  49. -- true goes before false
  50. if a == b then
  51. return false
  52. else
  53. return a
  54. end
  55. else
  56. -- welp
  57. return tostring(a) < tostring(b)
  58. end
  59. end
  60.  
  61. local luaKeywords = {}
  62. for _, v in ipairs{"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"} do
  63. luaKeywords[v] = true
  64. end
  65.  
  66. local function isSafeString(str)
  67. return type(str) == "string" and StringIdentifierSafe(str) and not luaKeywords[str]
  68. end
  69.  
  70. local write
  71. local function writerTable(f, item, level, refs, inline)
  72. if inline and inline[item] then
  73. return true
  74. end
  75. if inline then
  76. inline[item] = true
  77. end
  78.  
  79. if refs[item] then
  80. -- Table with multiple references
  81. f("ref["..refs[item].."]")
  82. else
  83. -- Single use table
  84. f("{")
  85.  
  86. -- We do our best to make things pretty
  87. local needcomma = false
  88.  
  89. -- First, we put in consecutive values
  90. local len = 0
  91. if type(item) == "table" then
  92. len = #item
  93. end -- userdatas will be ignored, so we'll jsut go straight to pairs
  94.  
  95. for i = 1, len do
  96. local v = rawget(item, i)
  97. if needcomma then
  98. f(",")
  99. if inline then
  100. f(" ")
  101. end
  102. end
  103. needcomma = true
  104. if not inline then
  105. f("\n")
  106. f(("\t"):rep(level + 1))
  107. end
  108.  
  109. if write(f, v, level + 1, refs, inline) then return true end
  110. end
  111.  
  112. local order = {}
  113. for k, v in pairs(item) do
  114. if type(k) ~= "number" or k < 1 or k > len then
  115. tinsert(order, k)
  116. end
  117. end
  118. tsort(order, sorter)
  119.  
  120. for _, k in ipairs(order) do
  121. if needcomma then
  122. f(",")
  123. if inline then
  124. f(" ")
  125. end
  126. end
  127. needcomma = true
  128. if not inline then
  129. f("\n")
  130. f(("\t"):rep(level + 1))
  131. end
  132.  
  133. if isSafeString(k) then
  134. f(k)
  135. f(" = ")
  136. else
  137. f("[")
  138. if write(f, k, level + 1, refs, inline) then return true end
  139. f("] = ")
  140. end
  141.  
  142. local v = item[k]
  143. if write(f, v, level + 1, refs, inline) then return true end
  144. end
  145.  
  146. if not inline then
  147. f("\n")
  148. f(("\t"):rep(level))
  149. end
  150. f("}")
  151. end
  152. end
  153.  
  154. local writers = {
  155. ["nil"] = function (f, item)
  156. f(0)
  157. end,
  158. ["number"] = function (f, item)
  159. f(1, item)
  160. end,
  161. ["string"] = function (f, item)
  162. f(2, item)
  163. end,
  164. ["boolean"] = function (f, item)
  165. if item then
  166. f(3)
  167. else
  168. f(4)
  169. end
  170. end,
  171. ["table"] = writerTable,
  172. ["function"] = function (f, item)
  173. -- can, meet worms
  174. f("nil --[[" .. tostring(item) .. "]]");
  175. end,
  176. ["thread"] = function (f, item)
  177. f("nil --[[" .. tostring(item) .. "]]");
  178. end,
  179. ["userdata"] = writerTable, -- we pretend this is a table for the sake of serialization. we won't lose any useful data - it would have been nil otherwise - and we gain the ability to dump the Event.UI hierarchy.
  180. }
  181.  
  182. function write(f, item, level, refs, used)
  183. return writers[type(item)](f, item, level, refs, used)
  184. end
  185.  
  186. local function refCount(objRefCount, item)
  187. -- only count reference types (tables)
  188. if type(item) == "table" then
  189. local rv = false
  190. -- Mark as an overflow
  191. if objRefCount[item] then
  192. objRefCount[item] = objRefCount[item] + 1
  193. rv = true
  194. else
  195. objRefCount[item] = 1
  196. -- If first encounter, traverse
  197. for k, v in pairs(item) do
  198. if refCount(objRefCount, k) then rv = true end
  199. if refCount(objRefCount, v) then rv = true end
  200. end
  201. end
  202. return rv
  203. end
  204. end
  205.  
  206. -- At some point we should maybe expose this to the outside world.
  207. -- forceSplitMode - nil or 0, 1, 2
  208. function persist(input, actives, f_out, forceSplitMode)
  209. -- default
  210. forceSplitMode = forceSplitMode or 0
  211.  
  212. -- hacky solution to deal with serialization problems: just step up the mode as we go and make sure it parses.
  213. -- this should probably be dealt with better but I'm honestly not sure how
  214. local append = Append()
  215.  
  216. -- Count references
  217. local objRefCount = {} -- Stores reference that will be exported
  218. local refsNeeded = false
  219. local items = {}
  220. for k, v in pairs(input) do
  221. tinsert(items, k)
  222. if refCount(objRefCount, k) then refsNeeded = true end
  223. if refCount(objRefCount, v) then refsNeeded = true end
  224. end
  225. for k in pairs(actives) do
  226. if input[k] == nil then
  227. tinsert(items, k)
  228. if refCount(objRefCount, k) then refsNeeded = true end
  229. end
  230. end
  231.  
  232. -- luajit has a limitation where more than 65k constants in one table causes parse errors.
  233. -- in that case, we pretend *everything* is a reference, so we can still save it properly.
  234. local totalobjs = 0
  235. local functionsplit = false
  236. for _ in pairs(objRefCount) do
  237. totalobjs = totalobjs + 1
  238. end
  239. if totalobjs > 65000 then
  240. forceSplitMode = max(forceSplitMode, 2)
  241. end
  242.  
  243. if forceSplitMode >= 1 then
  244. refsNeeded = true
  245. for k in pairs(objRefCount) do
  246. objRefCount[k] = objRefCount[k] + 1 -- "now it's more than one"
  247. end
  248. end
  249.  
  250. if forceSplitMode >= 2 then
  251. functionsplit = true
  252. end
  253.  
  254. -- Export Objects with more than one ref and assign name
  255. local objRefNames = {}
  256. if refsNeeded then
  257. -- First, create empty tables for each
  258. local objRefIdx = 0
  259. for obj, count in pairs(objRefCount) do
  260. if count > 1 then
  261. objRefIdx = objRefIdx + 1
  262. objRefNames[obj] = objRefIdx
  263. end
  264. end
  265. append("local ref = {}\n")
  266. append(sformat("for k=1,%d do ref[k] = {} end\n", objRefIdx))
  267.  
  268. -- Then fill them (this requires all empty multiRefObjects to exist)
  269. -- Unlike the early segment, we're putting limited effort into making this pretty
  270. -- if you're doing something that relies on this it's going to be ugly anyway.
  271. if functionsplit then
  272. append(";(function () -- function to split up constants in order to avoid luajit design limits\n")
  273. end
  274. local fct = 0
  275. for obj, idx in pairs(objRefNames) do
  276. for k, v in pairs(obj) do
  277. fct = fct + 2
  278. append("ref["..idx.."]")
  279. if isSafeString(k) then
  280. append(".")
  281. append(k)
  282. else
  283. append("[")
  284. write(append, k, 0, objRefNames)
  285. append("]")
  286. end
  287. append(" = ")
  288. write(append, v, 0, objRefNames)
  289. append("\n")
  290. if functionsplit and fct > 65500 then
  291. fct = 0
  292. append("end)()\n")
  293. append(";(function () -- function to split up constants in order to avoid luajit design limits\n")
  294. end
  295. end
  296. end
  297. if functionsplit then
  298. append("end)()\n")
  299. end
  300. end
  301.  
  302. tsort(items, sorter)
  303. for _, k in ipairs(items) do
  304. if isSafeString(k) then
  305. append(k)
  306. append(" = ")
  307. else
  308. append("_G[")
  309. write(append, k, 0, objRefNames)
  310. append("] = ")
  311. end
  312.  
  313. write(append, rawget(input, k), 0, objRefNames)
  314.  
  315. append("\n")
  316. end
  317.  
  318. local result = append(nil) -- done!
  319. append = nil
  320.  
  321. if not loadstring(result) then
  322. if forceSplitMode >= 2 then
  323. error("Cannot persist properly, please report this")
  324. else
  325. persist(input, actives, f_out, forceSplitMode + 1)
  326. end
  327. else
  328. f_out(result)
  329. end
  330. end
  331.  
  332. function serializeFull(elements, exists)
  333. local append = Append()
  334.  
  335. persist(elements, exists or elements, append)
  336.  
  337. return append(nil)
  338. end
  339.  
  340. function serializeInline(element)
  341. local append = Append()
  342.  
  343. if write(append, element, 0, {}, {}) then
  344. return nil
  345. else
  346. return append(nil)
  347. end
  348. end
  349.  
  350. function _G.dump(...)
  351. local stringized = {}
  352. local elements = select("#", ...)
  353. for k = 1, elements do
  354. local element = select(k, ...)
  355. if type(element) == "table" then
  356. stringized[k] = serializeInline(element) or "(serialization failed)"
  357. else
  358. stringized[k] = element
  359. end
  360. end
  361. -- intentionally using _G.print here
  362. _G.print(unpack(stringized, 1, elements))
  363. end
  364.  
  365. --[[function persistToString(input)
  366. local store = ""
  367. -- AMAZINGLY INEFFICIENT, IMPROVE
  368. f = function (str)
  369. store = store .. str
  370. end
  371.  
  372. persist(input, f)
  373.  
  374. return store
  375. end]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement