This repository has been archived by the owner on Jan 30, 2023. It is now read-only.
Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
luajit-msgpack-pure/luajit-msgpack-pure.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
604 lines (528 sloc)
15.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local ffi = require "ffi" | |
| local bit = require "bit" | |
| local math = require "math" | |
| local IS_LUAFFI = not rawget(_G,"jit") | |
| -- standard cdefs | |
| ffi.cdef[[ | |
| void free(void *ptr); | |
| void *realloc(void *ptr, size_t size); | |
| void *malloc(size_t size); | |
| ]] | |
| -- cache bitops | |
| local bor,band,rshift = bit.bor,bit.band,bit.rshift | |
| -- shared ffi data | |
| local t_buf = ffi.new("unsigned char[8]") | |
| local t_buf2 = ffi.new("unsigned char[8]") | |
| -- VLA ctype constructor | |
| local uchar_vla = ffi.typeof("unsigned char[?]") | |
| -- endianness | |
| local LITTLE_ENDIAN = ffi.abi("le") | |
| local rcopy = function(dst,src,len) | |
| local n = len-1 | |
| for i=0,n do dst[i] = src[n-i] end | |
| end | |
| -- buffer | |
| local MSGPACK_SBUFFER_INIT_SIZE = 8192 | |
| local buffer = {} | |
| local sbuffer_init = function(self) | |
| self.size = 0 | |
| self.alloc = MSGPACK_SBUFFER_INIT_SIZE | |
| self.data = ffi.cast("unsigned char *",ffi.C.malloc(MSGPACK_SBUFFER_INIT_SIZE)) | |
| end | |
| local sbuffer_destroy = function(self) | |
| ffi.C.free(buffer.data) | |
| end | |
| local sbuffer_realloc = function(self,len) | |
| if self.alloc - self.size < len then | |
| local nsize = self.alloc * 2 | |
| while nsize < self.alloc + len do nsize = nsize * 2 end | |
| self.data = ffi.cast("unsigned char *",ffi.C.realloc(self.data,nsize)) | |
| self.alloc = nsize | |
| end | |
| end | |
| local sbuffer_append_str = function(self,buf,len) | |
| sbuffer_realloc(self,len) | |
| ffi.copy(self.data+self.size,buf,len) | |
| self.size = self.size + len | |
| end | |
| local sbuffer_append_byte = function(self,b) | |
| sbuffer_realloc(self,1) | |
| self.data[self.size] = b | |
| self.size = self.size + 1 | |
| end | |
| local sbuffer_append_tbl = function(self,t) | |
| local len = #t | |
| sbuffer_realloc(self,len) | |
| local p = self.data + self.size - 1 | |
| for i=1,len do p[i] = t[i] end | |
| self.size = self.size + len | |
| end | |
| local sbuffer_append_intx, sbuffer_append_int64 | |
| if LITTLE_ENDIAN then | |
| sbuffer_append_intx = function(self,n,x,h) | |
| local t = {h} | |
| for i=x-8,8,-8 do t[#t+1] = band(rshift(n,i),0xff) end | |
| t[#t+1] = band(n,0xff) | |
| sbuffer_append_tbl(self,t) | |
| end | |
| sbuffer_append_int64 = function(self,n,h) | |
| local q,r = math.floor(n/2^32),n%(2^32) | |
| local t = {h} | |
| for i=24,8,-8 do t[#t+1] = band(rshift(q,i),0xff) end | |
| t[5] = band(q,0xff) | |
| for i=24,8,-8 do t[#t+1] = band(rshift(r,i),0xff) end | |
| t[9] = band(r,0xff) | |
| sbuffer_append_tbl(self,t) | |
| end | |
| else | |
| sbuffer_append_intx = function(self,n,x,h) | |
| local t = {h, band(n,0xff)} | |
| for i=8,x-8,8 do t[#t+1] = band(rshift(n,i),0xff) end | |
| sbuffer_append_tbl(self,t) | |
| end | |
| sbuffer_append_int64 = function(self,n,h) | |
| local q,r = math.floor(n/2^32),n%(2^32) | |
| local t = {h, band(r,0xff)} | |
| for i=8,24,8 do t[#t+1] = band(rshift(r,i),0xff) end | |
| t[6] = band(q,0xff) | |
| for i=8,24,8 do t[#t+1] = band(rshift(q,i),0xff) end | |
| sbuffer_append_tbl(self,t) | |
| end | |
| end | |
| --- packers | |
| local packers = {} | |
| packers.dynamic = function(data) | |
| return packers[type(data)](data) | |
| end | |
| packers["nil"] = function() | |
| sbuffer_append_byte(buffer,0xc0) | |
| end | |
| packers.boolean = function(data) | |
| if data then -- pack true | |
| sbuffer_append_byte(buffer,0xc3) | |
| else -- pack false | |
| sbuffer_append_byte(buffer,0xc2) | |
| end | |
| end | |
| local set_fp_type = function(t) | |
| local ptype,typebyte,_posinf,_neginf,_nan | |
| if t == "double" then | |
| typebyte,ptype = 0xcb,ffi.typeof("double *") | |
| _posinf = {typebyte,0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00} | |
| _neginf = {typebyte,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00} | |
| _nan = {typebyte,0xff,0xf8,0x00,0x00,0x00,0x00,0x00,0x00} | |
| elseif t == "float" then | |
| typebyte,ptype = 0xca,ffi.typeof("float *") | |
| _posinf = {typebyte,0x7f,0x80,0x00,0x00} | |
| _neginf = {typebyte,0xff,0x80,0x00,0x00} | |
| _nan = {typebyte,0xff,0x88,0x00,0x00} | |
| else return nil end | |
| local len = ffi.sizeof(t) | |
| if LITTLE_ENDIAN then | |
| packers.fpnum = function(n) | |
| ffi.cast(ptype,t_buf2)[0] = n | |
| rcopy(t_buf,t_buf2,len) | |
| sbuffer_append_byte(buffer,typebyte) | |
| sbuffer_append_str(buffer,t_buf,len) | |
| end | |
| else | |
| packers.fpnum = function(n) | |
| ffi.cast(ptype,t_buf)[0] = n | |
| sbuffer_append_byte(buffer,typebyte) | |
| sbuffer_append_str(buffer,t_buf,len) | |
| end | |
| end | |
| packers.posinf = function() | |
| sbuffer_append_tbl(buffer,_posinf) | |
| end | |
| packers.neginf = function() | |
| sbuffer_append_tbl(buffer,_neginf) | |
| end | |
| packers.nan = function() | |
| sbuffer_append_tbl(buffer,_nan) | |
| end | |
| return true | |
| end | |
| set_fp_type("double") -- default | |
| packers.number = function(n) | |
| if math.floor(n) == n then -- integer | |
| if n >= 0 then -- positive integer | |
| if n < 128 then -- positive fixnum | |
| sbuffer_append_byte(buffer,n) | |
| elseif n < 256 then -- uint8 | |
| sbuffer_append_tbl(buffer,{0xcc,n}) | |
| elseif n < 2^16 then -- uint16 | |
| sbuffer_append_intx(buffer,n,16,0xcd) | |
| elseif n < 2^32 then -- uint32 | |
| sbuffer_append_intx(buffer,n,32,0xce) | |
| elseif n == math.huge then -- +inf | |
| packers.posinf() | |
| else -- uint64 | |
| sbuffer_append_int64(buffer,n,0xcf) | |
| end | |
| else -- negative integer | |
| if n >= -32 then -- negative fixnum | |
| sbuffer_append_byte(buffer,bor(0xe0,n)) | |
| elseif n >= -128 then -- int8 | |
| sbuffer_append_tbl(buffer,{0xd0,n}) | |
| elseif n >= -2^15 then -- int16 | |
| sbuffer_append_intx(buffer,n,16,0xd1) | |
| elseif n >= -2^31 then -- int32 | |
| sbuffer_append_intx(buffer,n,32,0xd2) | |
| elseif n == -math.huge then -- -inf | |
| packers.neginf() | |
| else -- int64 | |
| sbuffer_append_int64(buffer,n,0xd3) | |
| end | |
| end | |
| elseif n ~= n then -- nan | |
| packers.nan() | |
| else -- floating point | |
| packers.fpnum(n) | |
| end | |
| end | |
| packers.string = function(data) | |
| local n = #data | |
| if n < 32 then | |
| sbuffer_append_byte(buffer,bor(0xa0,n)) | |
| elseif n < 2^16 then | |
| sbuffer_append_intx(buffer,n,16,0xda) | |
| elseif n < 2^32 then | |
| sbuffer_append_intx(buffer,n,32,0xdb) | |
| else | |
| error("overflow") | |
| end | |
| sbuffer_append_str(buffer,data,n) | |
| end | |
| packers["function"] = function(data) | |
| error("unimplemented", data) | |
| end | |
| packers.userdata = function(data) | |
| if IS_LUAFFI then | |
| return packers.cdata(data) | |
| else | |
| error("unimplemented") | |
| end | |
| end | |
| packers.thread = function(data) | |
| error("unimplemented", data) | |
| end | |
| packers.array = function(data,ndata) | |
| ndata = ndata or #data | |
| if ndata < 16 then | |
| sbuffer_append_byte(buffer,bor(0x90,ndata)) | |
| elseif ndata < 2^16 then | |
| sbuffer_append_intx(buffer,ndata,16,0xdc) | |
| elseif ndata < 2^32 then | |
| sbuffer_append_intx(buffer,ndata,32,0xdd) | |
| else | |
| error("overflow") | |
| end | |
| for i=1,ndata do packers[type(data[i])](data[i]) end | |
| end | |
| packers.map = function(data,ndata) | |
| if not ndata then | |
| ndata = 0 | |
| for _ in pairs(data) do ndata = ndata+1 end | |
| end | |
| if ndata < 16 then | |
| sbuffer_append_byte(buffer,bor(0x80,ndata)) | |
| elseif ndata < 2^16 then | |
| sbuffer_append_intx(buffer,ndata,16,0xde) | |
| elseif ndata < 2^32 then | |
| sbuffer_append_intx(buffer,ndata,32,0xdf) | |
| else | |
| error("overflow") | |
| end | |
| for k,v in pairs(data) do | |
| packers[type(k)](k) | |
| packers[type(v)](v) | |
| end | |
| end | |
| local set_table_classifier = function(f) | |
| packers.table = function(data) | |
| local obj_type,ndata = f(data) | |
| packers[obj_type](data,ndata) | |
| end | |
| end | |
| local table_classifier_keys = function(data) | |
| -- slightly slower, does not access values at all | |
| local is_map,ndata,nmax = false,0,0 | |
| for k,_ in pairs(data) do | |
| if (type(k) == "number") and (k > 0) and (math.floor(k) == k) then | |
| if k > nmax then nmax = k end | |
| else is_map = true end | |
| ndata = ndata+1 | |
| end | |
| if (nmax ~= ndata) then -- there are holes | |
| is_map = true | |
| end -- else nmax == ndata == #data | |
| return (is_map and "map" or "array"),ndata | |
| end | |
| local table_classifier_values = function(data) | |
| -- slightly faster, accesses values | |
| local is_map,ndata = false,0 | |
| for _ in pairs(data) do | |
| ndata = ndata + 1 | |
| if rawget(data,ndata) == nil then is_map = true end | |
| end | |
| return (is_map and "map" or "array"),ndata | |
| end | |
| set_table_classifier(table_classifier_keys) | |
| packers.cdata = function(data) -- msgpack-js | |
| local n = ffi.sizeof(data) | |
| if not n then | |
| error("cannot pack cdata of unknown size") | |
| elseif n < 2^16 then | |
| sbuffer_append_intx(buffer,n,16,0xd8) | |
| elseif n < 2^32 then | |
| sbuffer_append_intx(buffer,n,32,0xd9) | |
| else | |
| error("overflow") | |
| end | |
| sbuffer_append_str(buffer,data,n) | |
| end | |
| -- types decoding | |
| local types_map = { | |
| [0xc0] = "nil", | |
| [0xc2] = "false", | |
| [0xc3] = "true", | |
| [0xc4] = "nil", -- msgpack-js | |
| [0xca] = "float", | |
| [0xcb] = "double", | |
| [0xcc] = "uint8", | |
| [0xcd] = "uint16", | |
| [0xce] = "uint32", | |
| [0xcf] = "uint64", | |
| [0xd0] = "int8", | |
| [0xd1] = "int16", | |
| [0xd2] = "int32", | |
| [0xd3] = "int64", | |
| [0xd8] = "buf16", -- msgpack-js | |
| [0xd9] = "buf32", -- msgpack-js | |
| [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_pos" | |
| 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 | |
| if LITTLE_ENDIAN then | |
| unpack_number = function(buf,offset,ntype,nlen) | |
| rcopy(t_buf,buf.data+offset+1,nlen) | |
| return tonumber(ffi.cast(ntype,t_buf)[0]) | |
| end | |
| else | |
| unpack_number = function(buf,offset,ntype) | |
| return tonumber(ffi.cast(ntype,buf.data+offset+1)[0]) | |
| end | |
| end | |
| local unpacker_number = function(buf,offset) | |
| local obj_type = type_for(buf.data[offset]) | |
| local nlen = types_len_map[obj_type] | |
| local ntype | |
| if (obj_type == "float") or (obj_type == "double") then | |
| ntype = obj_type .. " *" | |
| else ntype = obj_type .. "_t *" end | |
| if offset + nlen >= buf.size then return nil,nil end | |
| return offset+nlen+1,unpack_number(buf,offset,ntype,nlen) | |
| end | |
| local unpack_map = function(buf,offset,n) | |
| local r = {} | |
| local k,v | |
| for _=1,n do | |
| offset,k = unpackers.dynamic(buf,offset) | |
| if not offset then | |
| -- Whole map is not available in the buffer | |
| return nil, r | |
| end | |
| offset,v = unpackers.dynamic(buf,offset) | |
| if not offset then | |
| -- Whole map is not available in the buffer | |
| return nil, r | |
| end | |
| r[k] = v | |
| end | |
| return offset,r | |
| end | |
| local unpack_array = function(buf,offset,n) | |
| local r = {} | |
| for i=1,n do | |
| offset,r[i] = unpackers.dynamic(buf,offset) | |
| if not offset then | |
| -- Whole array is not available in the buffer | |
| return nil, r | |
| end | |
| end | |
| return offset,r | |
| end | |
| unpackers.dynamic = function(buf,offset) | |
| assert(offset, "non nil offset is expected") | |
| if offset >= buf.size then return nil,nil end | |
| local obj_type = type_for(buf.data[offset]) | |
| return unpackers[obj_type](buf,offset) | |
| end | |
| unpackers.undefined = function(buf,offset) | |
| error("unimplemented", buf, offset) | |
| 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_pos = function(buf,offset) | |
| return offset+1,buf.data[offset] | |
| end | |
| unpackers.uint8 = function(buf,offset) | |
| if offset + 1 >= buf.size then return nil,nil end | |
| return offset+2,buf.data[offset+1] | |
| end | |
| unpackers.uint16 = unpacker_number | |
| unpackers.uint32 = unpacker_number | |
| unpackers.uint64 = unpacker_number | |
| unpackers.fixnum_neg = function(buf,offset) | |
| -- alternative to cast below: | |
| -- return offset+1,-band(bxor(buf.data[offset],0x1f),0x1f)-1 | |
| return offset+1,ffi.cast("int8_t *",buf.data)[offset] | |
| end | |
| unpackers.int8 = function(buf,offset) | |
| if offset + 1 >= buf.size then return nil,nil end | |
| return offset+2,ffi.cast("int8_t *",buf.data+offset+1)[0] | |
| end | |
| unpackers.int16 = unpacker_number | |
| unpackers.int32 = unpacker_number | |
| unpackers.int64 = unpacker_number | |
| unpackers.float = unpacker_number | |
| unpackers.double = unpacker_number | |
| unpackers.fixraw = function(buf,offset) | |
| local n = band(buf.data[offset],0x1f) | |
| if offset + n >= buf.size then return nil,nil end | |
| return offset+n+1,ffi.string(buf.data+offset+1,n) | |
| end | |
| unpackers.buf16 = function(buf,offset) | |
| if offset + 2 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint16_t *",2) | |
| if offset + n + 2 >= buf.size then return nil,nil end | |
| local r = uchar_vla(n) | |
| ffi.copy(r,buf.data+offset+3,n) | |
| return offset+n+3,r | |
| end | |
| unpackers.buf32 = function(buf,offset) | |
| if offset + 4 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint32_t *",4) | |
| if offset + n + 4 >= buf.size then return nil,nil end | |
| local r = uchar_vla(n) | |
| ffi.copy(r,buf.data+offset+5,n) | |
| return offset+n+5,r | |
| end | |
| unpackers.raw16 = function(buf,offset) | |
| if offset + 2 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint16_t *",2) | |
| if offset + n + 2 >= buf.size then return nil,nil end | |
| return offset+n+3,ffi.string(buf.data+offset+3,n) | |
| end | |
| unpackers.raw32 = function(buf,offset) | |
| if offset + 4 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint32_t *",4) | |
| if offset + n + 4 >= buf.size then return nil,nil end | |
| return offset+n+5,ffi.string(buf.data+offset+5,n) | |
| end | |
| unpackers.fixarray = function(buf,offset) | |
| local n = band(buf.data[offset],0x0f) | |
| return unpack_array(buf,offset+1,n) | |
| end | |
| unpackers.array16 = function(buf,offset) | |
| if offset + 2 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint16_t *",2) | |
| return unpack_array(buf,offset+3,n) | |
| end | |
| unpackers.array32 = function(buf,offset) | |
| if offset + 4 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint32_t *",4) | |
| return unpack_array(buf,offset+5,n) | |
| end | |
| unpackers.fixmap = function(buf,offset) | |
| local n = band(buf.data[offset],0x0f) | |
| return unpack_map(buf,offset+1,n) | |
| end | |
| unpackers.map16 = function(buf,offset) | |
| if offset + 2 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint16_t *",2) | |
| return unpack_map(buf,offset+3,n) | |
| end | |
| unpackers.map32 = function(buf,offset) | |
| if offset + 4 >= buf.size then return nil,nil end | |
| local n = unpack_number(buf,offset,"uint32_t *",4) | |
| return unpack_map(buf,offset+5,n) | |
| end | |
| -- Main functions | |
| local ljp_pack = function(data) | |
| sbuffer_init(buffer) | |
| packers.dynamic(data) | |
| local s = ffi.string(buffer.data,buffer.size) | |
| sbuffer_destroy(buffer) | |
| 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 | |
| sbuffer_init(buffer) | |
| sbuffer_append_str(buffer,s,#s) | |
| local data | |
| offset,data = unpackers.dynamic(buffer,offset) | |
| sbuffer_destroy(buffer) | |
| return offset,data | |
| end | |
| local ljp_pack_raw = function(raw) | |
| sbuffer_init(buffer) | |
| packers.dynamic(raw) | |
| local data, size = buffer.data, buffer.size | |
| buffer.data = nil | |
| return data, size | |
| end | |
| local ljp_unpack_raw = function(ptr, size, offset) | |
| if offset == nil then offset = 0 end | |
| local buf = { | |
| data = ffi.cast("unsigned char *", ptr), | |
| size = size | |
| } | |
| local data | |
| offset, data = unpackers.dynamic(buf, offset) | |
| return offset, data | |
| end | |
| return { | |
| pack = ljp_pack, | |
| unpack = ljp_unpack, | |
| pack_raw = ljp_pack_raw, | |
| unpack_raw = ljp_unpack_raw, | |
| set_fp_type = set_fp_type, | |
| table_classifiers = { | |
| keys = table_classifier_keys, | |
| values = table_classifier_values, | |
| }, | |
| set_table_classifier = set_table_classifier, | |
| packers = packers, | |
| unpackers = unpackers, | |
| } |