You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
10 KiB
Lua
374 lines
10 KiB
Lua
--[[----------------------------------------------------------------------------
|
|
|
|
MessagePack encoder / decoder written in pure Lua 5.3 / Lua 5.4
|
|
written by Sebastian Steinhauer <s.steinhauer@yahoo.de>
|
|
|
|
This is free and unencumbered software released into the public domain.
|
|
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
distribute this software, either in source code form or as a compiled
|
|
binary, for any purpose, commercial or non-commercial, and by any
|
|
means.
|
|
|
|
In jurisdictions that recognize copyright laws, the author or authors
|
|
of this software dedicate any and all copyright interest in the
|
|
software to the public domain. We make this dedication for the benefit
|
|
of the public at large and to the detriment of our heirs and
|
|
successors. We intend this dedication to be an overt act of
|
|
relinquishment in perpetuity of all present and future rights to this
|
|
software under copyright law.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
For more information, please refer to <http://unlicense.org/>
|
|
|
|
--]]----------------------------------------------------------------------------
|
|
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
|
|
|
|
|
|
--[[----------------------------------------------------------------------------
|
|
|
|
Encoder
|
|
|
|
--]]----------------------------------------------------------------------------
|
|
local encode_value -- forward declaration
|
|
|
|
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 -- check if we can use float
|
|
return pack('>Bf', 0xca, value)
|
|
else
|
|
return pack('>Bd', 0xcb, value)
|
|
end
|
|
end
|
|
end,
|
|
['string'] = function(value)
|
|
local len = #value
|
|
if utf8len(value) then -- check if it is a real utf8 string or just byte junk
|
|
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 -- encode it as byte-junk :)
|
|
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 -- it seems to be a proper Lua array
|
|
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 -- encode as a map
|
|
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
|
|
|
|
|
|
--[[----------------------------------------------------------------------------
|
|
|
|
Decoder
|
|
|
|
--]]----------------------------------------------------------------------------
|
|
local decode_value -- forward declaration
|
|
|
|
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,
|
|
}
|
|
|
|
-- add fix-array, fix-map, fix-string, fix-int stuff
|
|
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
|
|
|
|
|
|
--[[----------------------------------------------------------------------------
|
|
|
|
Interface
|
|
|
|
--]]----------------------------------------------------------------------------
|
|
return {
|
|
_AUTHOR = 'Sebastian Steinhauer <s.steinhauer@yahoo.de>',
|
|
_VERSION = '0.6.1',
|
|
|
|
-- primary encode function
|
|
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 just one value
|
|
encode_one = function(value)
|
|
local ok, data = pcall(encode_value, value)
|
|
if ok then
|
|
return data
|
|
else
|
|
return nil, 'cannot encode MessagePack'
|
|
end
|
|
end,
|
|
|
|
-- primary decode function
|
|
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 just one value
|
|
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,
|
|
}
|
|
|
|
--[[----------------------------------------------------------------------------
|
|
--]]---------------------------------------------------------------------------- |