local table = require("table") local string = require("string") local luabit = require("bit") local tostr = string.char local double_decode_count = 0 local double_encode_count = 0 -- cache bitops local band, rshift = luabit.band, luabit.brshift if not rshift then -- luajit differ from luabit rshift = luabit.rshift end local function byte_mod(x,v) if x < 0 then x = x + 256 end return (x%v) end -- buffer local strbuf = "" -- for unpacking local strary = {} -- for packing local function strary_append_int16(n,h) if n < 0 then n = n + 65536 end table.insert( strary, tostr(h, math.floor(n / 256), n % 256 ) ) end local function strary_append_int32(n,h) if n < 0 then n = n + 4294967296 end table.insert(strary, tostr(h, math.floor(n / 16777216), math.floor(n / 65536) % 256, math.floor(n / 256) % 256, n % 256 )) end local doubleto8bytes local strary_append_double = function(n) -- assume double double_encode_count = double_encode_count + 1 local b = doubleto8bytes(n) table.insert( strary, tostr(0xcb)) table.insert( strary, string.reverse(b) ) -- reverse: make big endian double precision end --- IEEE 754 -- out little endian doubleto8bytes = function(x) local function grab_byte(v) return math.floor(v / 256), tostr(math.fmod(math.floor(v), 256)) end local sign = 0 if x < 0 then sign = 1; x = -x end local mantissa, exponent = math.frexp(x) if x == 0 then -- zero mantissa, exponent = 0, 0 elseif x == 1/0 then mantissa, exponent = 0, 2047 else mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) exponent = exponent + 1022 end local v, byte = "" -- convert to bytes x = mantissa for _ = 1,6 do _, byte = grab_byte(x); v = v..byte -- 47:0 end x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48 x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56 return v, x end local function bitstofrac(ary) local x = 0 local cur = 0.5 for _, v in ipairs(ary) do x = x + cur * v cur = cur / 2 end return x end local function bytestobits(ary) local out={} for _, v in ipairs(ary) do for j = 0, 7, 1 do table.insert(out, band( rshift(v,7-j), 1 ) ) end end return out end -- get little endian local function bytestodouble(v) -- sign:1bit -- exp: 11bit (2048, bias=1023) local sign = math.floor(v:byte(8) / 128) local exp = band( v:byte(8), 127 ) * 16 + rshift( v:byte(7), 4 ) - 1023 -- bias -- frac: 52 bit local fracbytes = { band( v:byte(7), 15 ), v:byte(6), v:byte(5), v:byte(4), v:byte(3), v:byte(2), v:byte(1) -- big endian } local bits = bytestobits(fracbytes) for _ = 1, 4 do table.remove(bits,1) end if sign == 1 then sign = -1 else sign = 1 end local frac = bitstofrac(bits) if exp == -1023 and frac==0 then return 0 end if exp == 1024 and frac==0 then return 1/0 *sign end local real = math.ldexp(1+frac,exp) return real * sign end --- packers local packers = {} packers.dynamic = function(data) local t = type(data) return packers[t](data) end packers["nil"] = function() table.insert( strary, tostr(0xc0)) end packers.boolean = function(data) if data then -- pack true table.insert( strary, tostr(0xc3)) else -- pack false table.insert( strary, tostr(0xc2)) end end packers.number = function(n) if math.floor(n) == n then -- integer if n >= 0 then -- positive integer if n < 128 then -- positive fixnum table.insert( strary, tostr(n)) elseif n < 256 then -- uint8 table.insert(strary, tostr(0xcc,n)) elseif n < 65536 then -- uint16 strary_append_int16(n,0xcd) elseif n < 4294967296 then -- uint32 strary_append_int32(n,0xce) else -- lua cannot handle uint64, so double strary_append_double(n) end else -- negative integer if n >= -32 then -- negative fixnum table.insert( strary, tostr( 0xe0 + ((n+256)%32)) ) elseif n >= -128 then -- int8 table.insert( strary, tostr(0xd0,byte_mod(n,0x100))) elseif n >= -32768 then -- int16 strary_append_int16(n,0xd1) elseif n >= -2147483648 then -- int32 strary_append_int32(n,0xd2) else -- lua cannot handle int64, so double strary_append_double(n) end end else -- floating point strary_append_double(n) end end packers.string = function(data) local n = #data if n < 32 then table.insert( strary, tostr( 0xa0+n ) ) elseif n < 65536 then strary_append_int16(n,0xda) elseif n < 4294967296 then strary_append_int32(n,0xdb) else error("overflow") end table.insert( strary, data) end packers["function"] = function() error("unimplemented:function") end packers.userdata = function() error("unimplemented:userdata") end packers.thread = function() error("unimplemented:thread") end packers.table = function(data) local is_map,ndata,nmax = false,0,0 for k,_ in pairs(data) do if type(k) == "number" then if k > nmax then nmax = k end else is_map = true end ndata = ndata+1 end if is_map then -- pack as map if ndata < 16 then table.insert( strary, tostr(0x80+ndata)) elseif ndata < 65536 then strary_append_int16(ndata,0xde) elseif ndata < 4294967296 then strary_append_int32(ndata,0xdf) else error("overflow") end for k,v in pairs(data) do packers[type(k)](k) packers[type(v)](v) end else -- pack as array if nmax < 16 then table.insert( strary, tostr( 0x90+nmax ) ) elseif nmax < 65536 then strary_append_int16(nmax,0xdc) elseif nmax < 4294967296 then strary_append_int32(nmax,0xdd) else error("overflow") end for i=1,nmax do packers[type(data[i])](data[i]) end end end -- types decoding local types_map = { [0xc0] = "nil", [0xc2] = "false", [0xc3] = "true", [0xca] = "float", [0xcb] = "double", [0xcc] = "uint8", [0xcd] = "uint16", [0xce] = "uint32", [0xcf] = "uint64", [0xd0] = "int8", [0xd1] = "int16", [0xd2] = "int32", [0xd3] = "int64", [0xda] = "raw16", [0xdb] = "raw32", [0xdc] = "array16", [0xdd] = "array32", [0xde] = "map16", [0xdf] = "map32", } local type_for = function(n) if types_map[n] then return types_map[n] elseif n < 0xc0 then if n < 0x80 then return "fixnum_posi" elseif n < 0x90 then return "fixmap" elseif n < 0xa0 then return "fixarray" else return "fixraw" end elseif n > 0xdf then return "fixnum_neg" else return "undefined" end end local types_len_map = { uint16 = 2, uint32 = 4, uint64 = 8, int16 = 2, int32 = 4, int64 = 8, float = 4, double = 8, } --- unpackers local unpackers = {} local unpack_number = function(offset,ntype,nlen) local b1,b2,b3,b4,b5,b6,b7,b8 if nlen>=2 then b1,b2 = string.byte( strbuf, offset+1, offset+2 ) end if nlen>=4 then b3,b4 = string.byte( strbuf, offset+3, offset+4 ) end if nlen>=8 then b5,b6,b7,b8 = string.byte( strbuf, offset+5, offset+8 ) end if ntype == "uint16_t" then return b1 * 256 + b2 elseif ntype == "uint32_t" then return b1*65536*256 + b2*65536 + b3 * 256 + b4 elseif ntype == "int16_t" then local n = b1 * 256 + b2 local nn = (65536 - n)*-1 if nn == -65536 then nn = 0 end return nn elseif ntype == "int32_t" then local n = b1*65536*256 + b2*65536 + b3 * 256 + b4 local nn = ( 4294967296 - n ) * -1 if nn == -4294967296 then nn = 0 end return nn elseif ntype == "double_t" then local s = tostr(b8,b7,b6,b5,b4,b3,b2,b1) double_decode_count = double_decode_count + 1 local n = bytestodouble( s ) return n else error("unpack_number: not impl:" .. ntype ) end end local function unpacker_number(offset) local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) local nlen = types_len_map[obj_type] local ntype if (obj_type == "float") then error("float is not implemented") else ntype = obj_type .. "_t" end return offset+nlen+1,unpack_number(offset+1,ntype,nlen) end local function unpack_map(offset,n) local r = {} local k,v for _ = 1, n do offset,k = unpackers.dynamic(offset) assert(offset) offset,v = unpackers.dynamic(offset) assert(offset) r[k] = v end return offset,r end local function unpack_array(offset,n) local r = {} for i=1,n do offset,r[i] = unpackers.dynamic(offset) assert(offset) end return offset,r end function unpackers.dynamic(offset) if offset >= #strbuf then error("need more data") end local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) return unpackers[obj_type](offset) end function unpackers.undefined() error("unimplemented:undefined") end unpackers["nil"] = function(offset) return offset+1,nil end unpackers["false"] = function(offset) return offset+1,false end unpackers["true"] = function(offset) return offset+1,true end unpackers.fixnum_posi = function(offset) return offset+1, string.byte(strbuf, offset+1, offset+1) end unpackers.uint8 = function(offset) return offset+2, string.byte(strbuf, offset+2, offset+2) end unpackers.uint16 = unpacker_number unpackers.uint32 = unpacker_number unpackers.uint64 = unpacker_number unpackers.fixnum_neg = function(offset) -- alternative to cast below: local n = string.byte( strbuf, offset+1, offset+1) local nn = ( 256 - n ) * -1 return offset+1, nn end unpackers.int8 = function(offset) local i = string.byte( strbuf, offset+2, offset+2 ) if i > 127 then i = (256 - i ) * -1 end return offset+2, i end unpackers.int16 = unpacker_number unpackers.int32 = unpacker_number unpackers.int64 = unpacker_number unpackers.float = unpacker_number unpackers.double = unpacker_number unpackers.fixraw = function(offset) local n = byte_mod( string.byte( strbuf, offset+1, offset+1) ,0x1f+1) -- print("unpackers.fixraw: offset:", offset, "#buf:", #buf, "n:",n ) local b if ( #strbuf - 1 - offset ) < n then error("require more data") end if n > 0 then b = string.sub( strbuf, offset + 1 + 1, offset + 1 + 1 + n - 1 ) else b = "" end return offset+n+1, b end unpackers.raw16 = function(offset) local n = unpack_number(offset+1,"uint16_t",2) if ( #strbuf - 1 - 2 - offset ) < n then error("require more data") end local b = string.sub( strbuf, offset+1+1+2, offset+1 + 1+2 + n - 1 ) return offset+n+3, b end unpackers.raw32 = function(offset) local n = unpack_number(offset+1,"uint32_t",4) if ( #strbuf - 1 - 4 - offset ) < n then error( "require more data (possibly bug)") end local b = string.sub( strbuf, offset+1+ 1+4, offset+1 + 1+4 +n -1 ) return offset+n+5,b end unpackers.fixarray = function(offset) return unpack_array( offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) end unpackers.array16 = function(offset) return unpack_array(offset+3,unpack_number(offset+1,"uint16_t",2)) end unpackers.array32 = function(offset) return unpack_array(offset+5,unpack_number(offset+1,"uint32_t",4)) end unpackers.fixmap = function(offset) return unpack_map(offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) end unpackers.map16 = function(offset) return unpack_map(offset+3,unpack_number(offset+1,"uint16_t",2)) end unpackers.map32 = function(offset) return unpack_map(offset+5,unpack_number(offset+1,"uint32_t",4)) end -- Main functions local ljp_pack = function(data) strary={} packers.dynamic(data) local s = table.concat(strary,"") return s end local ljp_unpack = function(s,offset) if offset == nil then offset = 0 end if type(s) ~= "string" then return false,"invalid argument" end local data strbuf = s offset,data = unpackers.dynamic(offset) return offset,data end local function ljp_stat() return { double_decode_count = double_decode_count, double_encode_count = double_encode_count } end local msgpack = { pack = ljp_pack, unpack = ljp_unpack, stat = ljp_stat } return msgpack