Advertisement
osmarks

ser+

Aug 18th, 2018
8,361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.78 KB | None | 0 0
  1.     --[[
  2.     BLODS - Binary Lua Object (De)Serialization
  3. ]]
  4.  
  5. --[[
  6.     Save on table access.
  7. ]]
  8. local pairs       = pairs
  9. local type        = type
  10. local loadstring  = loadstring
  11. local mathabs     = math.abs
  12. local mathfloor   = math.floor
  13. local mathfrexp   = math.frexp
  14. local mathmodf    = math.modf
  15. local mathpow     = math.pow
  16. local stringbyte  = string.byte
  17. local stringchar  = string.char
  18. local stringdump  = string.dump
  19. local stringsub   = string.sub
  20. local tableconcat = table.concat
  21.  
  22. --[[
  23.     Float conversions. Modified from http://snippets.luacode.org/snippets/IEEE_float_conversion_144.
  24. ]]
  25. local function double2str(value)
  26.     local s=value<0 and 1 or 0
  27.     if mathabs(value)==1/0 then
  28.         return (s==1 and "\0\0\0\0\0\0\240\255" or "\0\0\0\0\0\0\240\127")
  29.     end
  30.     if value~=value then
  31.         return "\170\170\170\170\170\170\250\255"
  32.     end
  33.     local fr,exp=mathfrexp(mathabs(value))
  34.     fr,exp=fr*2,exp-1
  35.     exp=exp+1023
  36.     return tableconcat({stringchar(mathfloor(fr*2^52)%256),
  37.     stringchar(mathfloor(fr*2^44)%256),
  38.     stringchar(mathfloor(fr*2^36)%256),
  39.     stringchar(mathfloor(fr*2^28)%256),
  40.     stringchar(mathfloor(fr*2^20)%256),
  41.     stringchar(mathfloor(fr*2^12)%256),
  42.     stringchar(mathfloor(fr*2^4)%16+mathfloor(exp)%16*16),
  43.     stringchar(mathfloor(exp/2^4)%128+128*s)})
  44. end
  45.  
  46. local function str2double(str)
  47.     local fr=stringbyte(str, 1)/2^52+stringbyte(str, 2)/2^44+stringbyte(str, 3)/2^36+stringbyte(str, 4)/2^28+stringbyte(str, 5)/2^20+stringbyte(str, 6)/2^12+(stringbyte(str, 7)%16)/2^4+1
  48.     local exp=(stringbyte(str, 8)%128)*16+mathfloor(str:byte(7)/16)-1023
  49.     local s=mathfloor(stringbyte(str, 8)/128)
  50.     if exp==1024 then
  51.         return fr==1 and (1-2*s)/0 or 0/0
  52.     end
  53.     return (1-2*s)*fr*2^exp
  54. end
  55.  
  56. --[[
  57.     Integer conversions. Taken from http://lua-users.org/wiki/ReadWriteFormat.
  58.     Modified to support signed ints.
  59. ]]
  60.  
  61. local function signedstringtonumber(str)
  62.   local function _b2n(exp, num, digit, ...)
  63.     if not digit then return num end
  64.     return _b2n(exp*256, num + digit*exp, ...)
  65.   end
  66.   return _b2n(256, stringbyte(str, 1, -1)) - mathpow(2, #str * 8 - 1)
  67. end
  68.  
  69. local function stringtonumber(str)
  70.     local function _b2n(exp, num, digit, ...)
  71.       if not digit then return num end
  72.       return _b2n(exp*256, num + digit*exp, ...)
  73.     end
  74.     return _b2n(256, stringbyte(str, 1, -1))
  75. end
  76.  
  77. local function numbertobytes(num, width)
  78.     local function _n2b(width, num, rem)
  79.         rem = rem * 256
  80.         if width == 0 then return rem end
  81.         return rem, _n2b(width-1, mathmodf(num/256))
  82.     end
  83.     return stringchar(_n2b(width-1, mathmodf((num)/256)))
  84. end
  85.  
  86. local function log2(x)
  87.     return math.log10(x) / math.log10(2)
  88. end
  89.  
  90. --[[
  91.     (De)Serialization for Lua types.
  92. ]]
  93.  
  94. local function intWidth(int)
  95.     local out = math.ceil((log2(int) + 1) / 8)
  96.     if out == math.huge or out == -math.huge then return 1 end
  97.     return out
  98. end
  99.  
  100. local types = {
  101.     boolean = "b",
  102.     double = "d",
  103.     posinteger = "p",
  104.     neginteger = "n",
  105.     string = "s",
  106.     table = "t",
  107.     ["function"] = "f",
  108.     ["nil"] = "_"
  109. }
  110.  
  111. local serialization = { }
  112. local deserialization = { }
  113.  
  114. function serialization.boolean(obj)
  115.     return obj and "\1" or "\0"
  116. end
  117.  
  118. function serialization.double(obj)
  119.     return double2str(obj)
  120. end
  121.  
  122. function serialization.integer(obj)
  123.     local width = intWidth(obj)
  124.     return stringchar(width) .. numbertobytes(obj, width)
  125. end
  126.  
  127. function serialization.string(obj)
  128.     local len = #obj
  129.     local width = intWidth(len)
  130.     return tableconcat({ stringchar(width), numbertobytes(len, width), obj })
  131. end
  132.  
  133. serialization["function"] = function(obj)
  134.     local ok, s = pcall(stringdump, obj)
  135.     if not ok then return "_" end
  136.     return numbertobytes(#s, 4) .. s
  137. end
  138.  
  139. function deserialization.b(idx, ser)
  140.     local ret = stringsub(ser[1], idx, idx) == "\1"
  141.     return ret, idx + 1
  142. end
  143.  
  144. function deserialization.d(idx, ser)
  145.     local ret = str2double(stringsub(ser[1], idx, idx + 8))
  146.     return ret, idx + 8
  147. end
  148.  
  149. function deserialization.p(idx, ser)
  150.     local width = stringtonumber(stringsub(ser[1], idx, idx))
  151.     local ret = stringtonumber(stringsub(ser[1], idx + 1, idx + width))
  152.     return ret, idx + width + 1
  153. end
  154.  
  155. function deserialization.n(idx, ser)
  156.     local width = stringtonumber(stringsub(ser[1], idx, idx))
  157.     local ret = stringtonumber(stringsub(ser[1], idx + 1, idx + width))
  158.     return -ret, idx + width + 1
  159. end
  160.  
  161. function deserialization.s(idx, ser)
  162.     local width = stringtonumber(stringsub(ser[1], idx, idx))
  163.     local len = stringtonumber(stringsub(ser[1], idx + 1, idx + width))
  164.     local ret = stringsub(ser[1], idx + width + 1, idx + width + len)
  165.     return ret, idx + width + len + 1
  166. end
  167.  
  168. function deserialization.f(idx, ser)
  169.     local len = stringtonumber(stringsub(ser[1], idx, idx + 3))
  170.     local ret = loadstring(stringsub(ser[1], idx + 4, idx + len + 3))
  171.     return ret, idx + len + 4
  172. end
  173.  
  174. function deserialization._(idx, ser)
  175.     return nil, idx
  176. end
  177.  
  178. local function yield()
  179.     os.queueEvent ""
  180.     os.pullEvent ""
  181. end
  182.  
  183. if not os.queueEvent then yield = function() end end
  184.  
  185. function serialize(obj)
  186.     -- State vars.
  187.     local ntables = 1
  188.     local tables = { }
  189.     local tableIDs = { }
  190.     local tableSerial = { }
  191.  
  192.     -- Internal recursive function.
  193.     local function serialize(obj)
  194.         yield()
  195.         local t = type(obj)
  196.         if t == "table" then
  197.             local len = #obj
  198.  
  199.             if tables[obj] then
  200.                 -- We already serialized this table. Just return the id.
  201.                 return tableIDs[obj]
  202.             end
  203.  
  204.             -- Insert table info.
  205.             local id = ntables
  206.             tables[obj] = true
  207.             local width = intWidth(ntables)
  208.             local ser = "t" .. numbertobytes(width, 1) .. numbertobytes(ntables, width)
  209.             tableIDs[obj] = ser
  210.  
  211.             -- Important to increment here so tables inside this one don't use the same id.
  212.             ntables = ntables + 1
  213.  
  214.             -- Serialize the table.
  215.             local serialConcat = { }
  216.  
  217.             -- Array part.
  218.             for i = 1, len do
  219.                 if obj[i] == nil then
  220.                     len = i - 1
  221.                     break
  222.                 end
  223.                 serialConcat[#serialConcat + 1] = serialize(obj[i])
  224.             end
  225.             serialConcat[#serialConcat + 1] = "\0"
  226.  
  227.             -- Table part.
  228.             for k, v in pairs(obj) do
  229.                 if type(k) ~= "number" or ((k > len or k < 1) or mathfloor(k) ~= k) then
  230.                     -- For each pair, serialize both the key and the value.
  231.                     local idx = #serialConcat
  232.                     serialConcat[idx + 1] = serialize(k)
  233.                     serialConcat[idx + 2] = serialize(v)
  234.                 end
  235.             end
  236.             serialConcat[#serialConcat + 1] = "\0"
  237.  
  238.             -- tableconcat is way faster than normal concatenation using .. when dealing with lots of strings.
  239.             -- Add this serialization to the table of serialized tables for quick access and later more concatenation.
  240.             tableSerial[id] = tableconcat(serialConcat)
  241.             return ser
  242.         else
  243.             -- Do serialization on a non-recursive type.
  244.             if t == "number" then
  245.                 -- Space optimization can be done for ints, so serialize them differently from doubles.
  246.                 -- OSMARKS EDIT: handle sign in type and not actual serialization
  247.                 if mathfloor(obj) == obj then
  248.                     local intval = serialization.integer(math.abs(obj))
  249.                     local typespec = "p"
  250.                     if obj < 0 then typespec = "n" end
  251.                     return typespec .. intval
  252.                 end
  253.                 return "d" .. serialization.double(obj)
  254.             end
  255.             local ser = types[t]
  256.             return obj == nil and ser or ser .. serialization[t](obj)
  257.         end
  258.     end
  259.  
  260.     -- Either serialize for a table or for a non-recursive type.
  261.     local ser = serialize(obj)
  262.     if type(obj) == "table" then
  263.         return tableconcat({ "t", tableconcat(tableSerial) })
  264.     end
  265.     return ser
  266. end
  267.  
  268. function deserialize(ser)
  269.     local idx = 1
  270.     local tables = { { } }
  271.     local serref = { ser }
  272.  
  273.     local function getchar()
  274.         local ret = stringsub(serref[1], idx, idx)
  275.         return ret ~= "" and ret or nil
  276.     end
  277.  
  278.     local function deserializeValue()
  279.         yield()
  280.         local t = getchar()
  281.         idx = idx + 1
  282.         if t == "t" then
  283.             -- Get table id.
  284.             local width = stringtonumber(getchar())
  285.             idx = idx + 1
  286.             local id = stringtonumber(stringsub(serref[1], idx, idx + width - 1))
  287.             idx = idx + width
  288.  
  289.             -- Create an empty table as a placeholder.
  290.             if not tables[id] then
  291.                 tables[id] = { }
  292.             end
  293.  
  294.             return tables[id]
  295.         else
  296.             local ret
  297.             ret, idx = deserialization[t](idx, serref)
  298.             return ret
  299.         end
  300.     end
  301.  
  302.     -- Either deserialize for a table or for a non-recursive type.
  303.     local i = 1
  304.     if getchar() == "t" then
  305.         idx = idx + 1
  306.         while getchar() do
  307.             if not tables[i] then tables[i] = { } end
  308.             local curtbl = tables[i]
  309.  
  310.             -- Array part.
  311.             while getchar() ~= "\0" do
  312.                 curtbl[#curtbl + 1] = deserializeValue()
  313.             end
  314.  
  315.             -- Table part.
  316.             idx = idx + 1
  317.             while getchar() ~= "\0" do
  318.                 curtbl[deserializeValue()] = deserializeValue()
  319.             end
  320.  
  321.             i = i + 1
  322.             idx = idx + 1
  323.         end
  324.         return tables[1]
  325.     end
  326.     return deserializeValue()
  327. end
  328.  
  329. return { serialize = serialize, deserialize = deserialize }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement