local pack, unpack = string.pack, string.unpack
local mtype, utf8len = math.type, utf8.len
local tconcat, tunpack = table.concat, table.unpack
local ssub = string.sub
local type, pcall, pairs, select = type, pcall, pairs, select
local encode_value
local function is_an_array(value)
local expected = 1
for k in pairs(value) do
if k ~= expected then
return false
end
expected = expected + 1
end
return true
end
local encoder_functions = {
['nil'] = function()
return pack('B', 0xc0)
end,
['boolean'] = function(value)
if value then
return pack('B', 0xc3)
else
return pack('B', 0xc2)
end
end,
['number'] = function(value)
if mtype(value) == 'integer' then
if value >= 0 then
if value < 128 then
return pack('B', value)
elseif value <= 0xff then
return pack('BB', 0xcc, value)
elseif value <= 0xffff then
return pack('>BI2', 0xcd, value)
elseif value <= 0xffffffff then
return pack('>BI4', 0xce, value)
else
return pack('>BI8', 0xcf, value)
end
else
if value >= -32 then
return pack('B', 0xe0 + (value + 32))
elseif value >= -128 then
return pack('Bb', 0xd0, value)
elseif value >= -32768 then
return pack('>Bi2', 0xd1, value)
elseif value >= -2147483648 then
return pack('>Bi4', 0xd2, value)
else
return pack('>Bi8', 0xd3, value)
end
end
else
local test = unpack('f', pack('f', value))
if test == value then return pack('>Bf', 0xca, value)
else
return pack('>Bd', 0xcb, value)
end
end
end,
['string'] = function(value)
local len = #value
if utf8len(value) then if len < 32 then
return pack('B', 0xa0 + len) .. value
elseif len < 256 then
return pack('>Bs1', 0xd9, value)
elseif len < 65536 then
return pack('>Bs2', 0xda, value)
else
return pack('>Bs4', 0xdb, value)
end
else if len < 256 then
return pack('>Bs1', 0xc4, value)
elseif len < 65536 then
return pack('>Bs2', 0xc5, value)
else
return pack('>Bs4', 0xc6, value)
end
end
end,
['table'] = function(value)
if is_an_array(value) then local elements = {}
for i, v in pairs(value) do
elements[i] = encode_value(v)
end
local length = #elements
if length < 16 then
return pack('>B', 0x90 + length) .. tconcat(elements)
elseif length < 65536 then
return pack('>Bi2', 0xdc, length) .. tconcat(elements)
else
return pack('>Bi4', 0xdd, length) .. tconcat(elements)
end
else local elements = {}
for k, v in pairs(value) do
elements[#elements + 1] = encode_value(k)
elements[#elements + 1] = encode_value(v)
end
local length = #elements // 2
if length < 16 then
return pack('>B', 0x80 + length) .. tconcat(elements)
elseif length < 65536 then
return pack('>Bi2', 0xde, length) .. tconcat(elements)
else
return pack('>Bi4', 0xdf, length) .. tconcat(elements)
end
end
end,
}
encode_value = function(value)
return encoder_functions[type(value)](value)
end
local function encode(...)
local data = {}
for i = 1, select('#', ...) do
data[#data + 1] = encode_value(select(i, ...))
end
return tconcat(data)
end
local decode_value
local function decode_array(data, position, length)
local elements, value = {}
for i = 1, length do
value, position = decode_value(data, position)
elements[i] = value
end
return elements, position
end
local function decode_map(data, position, length)
local elements, key, value = {}
for i = 1, length do
key, position = decode_value(data, position)
value, position = decode_value(data, position)
elements[key] = value
end
return elements, position
end
local decoder_functions = {
[0xc0] = function(data, position)
return nil, position
end,
[0xc2] = function(data, position)
return false, position
end,
[0xc3] = function(data, position)
return true, position
end,
[0xc4] = function(data, position)
return unpack('>s1', data, position)
end,
[0xc5] = function(data, position)
return unpack('>s2', data, position)
end,
[0xc6] = function(data, position)
return unpack('>s4', data, position)
end,
[0xca] = function(data, position)
return unpack('>f', data, position)
end,
[0xcb] = function(data, position)
return unpack('>d', data, position)
end,
[0xcc] = function(data, position)
return unpack('>B', data, position)
end,
[0xcd] = function(data, position)
return unpack('>I2', data, position)
end,
[0xce] = function(data, position)
return unpack('>I4', data, position)
end,
[0xcf] = function(data, position)
return unpack('>I8', data, position)
end,
[0xd0] = function(data, position)
return unpack('>b', data, position)
end,
[0xd1] = function(data, position)
return unpack('>i2', data, position)
end,
[0xd2] = function(data, position)
return unpack('>i4', data, position)
end,
[0xd3] = function(data, position)
return unpack('>i8', data, position)
end,
[0xd9] = function(data, position)
return unpack('>s1', data, position)
end,
[0xda] = function(data, position)
return unpack('>s2', data, position)
end,
[0xdb] = function(data, position)
return unpack('>s4', data, position)
end,
[0xdc] = function(data, position)
local length
length, position = unpack('>I2', data, position)
return decode_array(data, position, length)
end,
[0xdd] = function(data, position)
local length
length, position = unpack('>I4', data, position)
return decode_array(data, position, length)
end,
[0xde] = function(data, position)
local length
length, position = unpack('>I2', data, position)
return decode_map(data, position, length)
end,
[0xdf] = function(data, position)
local length
length, position = unpack('>I4', data, position)
return decode_map(data, position, length)
end,
}
for i = 0x00, 0x7f do
decoder_functions[i] = function(data, position)
return i, position
end
end
for i = 0x80, 0x8f do
decoder_functions[i] = function(data, position)
return decode_map(data, position, i - 0x80)
end
end
for i = 0x90, 0x9f do
decoder_functions[i] = function(data, position)
return decode_array(data, position, i - 0x90)
end
end
for i = 0xa0, 0xbf do
decoder_functions[i] = function(data, position)
local length = i - 0xa0
return ssub(data, position, position + length - 1), position + length
end
end
for i = 0xe0, 0xff do
decoder_functions[i] = function(data, position)
return -32 + (i - 0xe0), position
end
end
decode_value = function(data, position)
local byte, value
byte, position = unpack('B', data, position)
value, position = decoder_functions[byte](data, position)
return value, position
end
return {
_AUTHOR = 'Sebastian Steinhauer <s.steinhauer@yahoo.de>',
_VERSION = '0.6.0',
encode = function(...)
local data, ok = {}
for i = 1, select('#', ...) do
ok, data[i] = pcall(encode_value, select(i, ...))
if not ok then
return nil, 'cannot encode MessagePack'
end
end
return tconcat(data)
end,
encode_one = function(value)
local ok, data = pcall(encode_value, value)
if ok then
return data
else
return nil, 'cannot encode MessagePack'
end
end,
decode = function(data, position)
local values, value, ok = {}
position = position or 1
while position <= #data do
ok, value, position = pcall(decode_value, data, position)
if ok then
values[#values + 1] = value
else
return nil, 'cannot decode MessagePack'
end
end
return tunpack(values)
end,
decode_one = function(data, position)
local value, ok
ok, value, position = pcall(decode_value, data, position or 1)
if ok then
return value, position
else
return nil, 'cannot decode MessagePack'
end
end,
}