Модуль msgpack

Модуль msgpack

Общие сведения

Модуль msgpack берет строки в формате MsgPack и декодирует их или берет ряд значений в ином формате и кодирует их в формат MsgPack. MsgPack интенсивно используется в Tarantool’е, поскольку кортежи хранятся в виде массивов в формате MsgPack.

Указатель

Ниже приведен перечень всех функций и элементов модуля msgpack.

Имя Назначение
msgpack.encode() Конвертация Lua-объекта в MsgPack-строку
msgpack.decode() Конвертация MsgPack-строки в Lua-объект
msgpack.decode_unchecked() Конвертация MsgPack-строки в Lua-объект
msgpack.decode_array_header Skip array header in a MsgPack string
msgpack.decode_map_header Skip map header in a MsgPack string
__serialize parameter Output structure specification
msgpack.cfg Изменение конфигурации
msgpack.NULL Аналог «nil» в языке Lua
msgpack.encode(lua_value)

Конвертация Lua-объекта в MsgPack-строку.

Параметры:
  • lua_value – скалярное значение или значение из Lua-таблицы.
возвращает:

оригинальное значение, преобразованное в MsgPack-строку.

тип возвращаемого значения:
 

строка

msgpack.decode(msgpack_string[, start_position])

Конвертация MsgPack-строки в Lua-объект.

Параметры:
  • msgpack_string (string) – строка в формате MsgPack.
  • start_position (integer) – откуда начинать, минимальное значение = 1, максимальное = длина строки, по умолчанию = 1.
возвращает:
  • (если msgpack_string в правильном MsgPack-формате) содержимое msgpack_string в формате Lua-таблицы, (в противном случае) скалярное значение, строка или число;
  • «next_start_position». Если расшифровка decode останавливается после разбора байта N в msgpack_string, то «next_start_position» = N + 1, а decode(msgpack_string, next_start_position) продолжит разбор с места остановки предыдущего decode плюс 1. Как правило, decode разбирает всю строку msgpack_string, поэтому «next_start_position» будет равняться string.len(msgpack_string) + 1.
тип возвращаемого значения:
 

таблица и число

msgpack.decode_unchecked(string)

Конвертация MsgPack-строки в Lua-объект. Поскольку проверка не проводится, decode_unchecked() может работать с указателями строки в буфере в отличие от decode(). Пример см. в модуле buffer.

Параметры:
  • string – строка в формате MsgPack.
возвращает:
  • оригинальное содержание в формате Lua-таблицы;
  • количество декодированных байтов.
тип возвращаемого значения:
 

Lua-объект.

msgpack.decode_array_header(byte-array, size)

Call the mp_decode_array function in the MsgPuck library and return the array size and a pointer to the first array component. A subsequent call to msgpack_decode can decode the component instead of the whole array.

Параметры:
  • byte-array – a pointer to a byte array formatted as MsgPack.
  • size – a number greater than or equal to the string’s length
возвращает:
  • the size of the array;
  • a pointer to after the array header.
-- Example of decode_array_header
-- Suppose we have the raw data '\x93\x01\x02\x03'.
-- \x93 is MsgPack encoding for a header of a three-item array.
-- We want to skip it and decode the next three items.
msgpack=require('msgpack'); ffi=require('ffi')
x,y=msgpack.decode_array_header(ffi.cast('char*','\x93\x01\x02\x03'),4)
a=msgpack.decode(y,1);b=msgpack.decode(y+1,1);c=msgpack.decode(y+2,1);
a,b,c
-- The result will be: 1,2,3.
msgpack.decode_map_header(byte-array, size)

Call the mp_decode_map function in the MsgPuck library and return the map size and a pointer to the first map component. A subsequent call to msgpack_decode can decode the component instead of the whole map.

Параметры:
  • byte-array – a pointer to a byte array formatted as MsgPack.
  • size – a number greater than or equal to the byte array’s length
возвращает:
  • the size of the map;
  • a pointer to after the map header.
-- Example of decode_map_header
-- Suppose we have the raw data '\x81\xa2\x41\x41\xc3'.
-- \x81 is MsgPack encoding for a header of a one-item map.
-- We want to skip it and decode the next map item.
msgpack=require('msgpack'); ffi=require('ffi')
x,y=msgpack.decode_map_header(ffi.cast('char*','\x81\xa2\x41\x41\xc3'),5)
a=msgpack.decode(y,3);b=msgpack.decode(y+3,1)
x,a,b
-- The result will be: 1,"AA", true.

__serialize parameter:

Структуру MsgPack-вывода можно указать с помощью __serialize:

  • „seq“, „sequence“, „array“ - table encoded as an array
  • „map“, „mappping“ - table encoded as a map
  • function - the meta-method called to unpack serializable representation of table, cdata or userdata objects

Serializing „A“ and „B“ with different __serialize values brings different results. To show this, here is a routine which encodes {'A','B'} both as an array and as a map, then displays each result in hexadecimal.

function hexdump(bytes)
    local result = ''
    for i = 1, #bytes do
        result = result .. string.format("%x", string.byte(bytes, i)) .. ' '
    end
    return result
end

msgpack = require('msgpack')
m1 = msgpack.encode(setmetatable({'A', 'B'}, {
                             __serialize = "seq"
                          }))
m2 = msgpack.encode(setmetatable({'A', 'B'}, {
                             __serialize = "map"
                          }))
print('array encoding: ', hexdump(m1))
print('map encoding: ', hexdump(m2))

Результат:

array encoding: 92 a1 41 a1 42
map encoding:   82 01 a1 41 02 a1 42

The MsgPack Specification page explains that the first encoding means:

fixarray(2), fixstr(1), "A", fixstr(1), "B"

а значение второго результата кодирования:

fixmap(2), key(1), fixstr(1), "A", key(2), fixstr(2), "B"

Ниже приведены примеры всех стандартных типов: слева отображение в Lua-таблице, а справа – имя и кодировка в формате MsgPack.

Стандартные типы в MsgPack-кодировке

{} „fixmap“ = 80, если метатаблица – ассоциативный массив „map“, в противном случае, „fixarray“ = 90
„a“ „fixstr“ = a1 61
false „false“ = c2
true „true“ = c3
127 „positive fixint“ = 7f
65535 „uint 16“ = cd ff ff
4294967295 „uint 32“ = ce ff ff ff ff
nil „nil“ = c0
msgpack.NULL то же, что и nil
[0] = 5 „fixmap(1)“ + „positive fixint“ (для ключа) + „positive fixint“ (для значения) = 81 00 05
[0] = nil „fixmap(0)“ = 80 – nil не хранится, если это отсутствующее значение ассоциативного массива
1,5 „float 64“ = cb 3f f8 00 00 00 00 00 00
msgpack.cfg(table)

Some MsgPack configuration settings can be changed.

The values are all either integers or boolean true/false.

Option Default Назначение
cfg.encode_max_depth 128 Max recursion depth for encoding
cfg.encode_deep_as_nil false A flag saying whether to crop tables with nesting level deeper than cfg.encode_max_depth. Not-encoded fields are replaced with one null. If not set, too high nesting is considered an error.
cfg.encode_invalid_numbers true A flag saying whether to enable encoding of NaN and Inf numbers
cfg.encode_load_metatables true A flag saying whether the serializer will follow __serialize metatable field
cfg.encode_use_tostring false A flag saying whether to use tostring() for unknown types
cfg.encode_invalid_as_nil false A flag saying whether to use NULL for non-recognized types
cfg.encode_sparse_convert true A flag saying whether to handle excessively sparse arrays as maps. See detailed description below
cfg.encode_sparse_ratio 2 1/encode_sparse_ratio is the permissible percentage of missing values in a sparse array
cfg.encode_sparse_safe 10 A limit ensuring that small Lua arrays are always encoded as sparse arrays (instead of generating an error or encoding as a map)
cfg.decode_invalid_numbers true A flag saying whether to enable decoding of NaN and Inf numbers
cfg.decode_save_metatables true A flag saying whether to set metatables for all arrays and maps

Sparse arrays features:

During encoding, the MsgPack encoder tries to classify tables into one of four kinds:

  • map - at least one table index is not unsigned integer
  • regular array - all array indexes are available
  • sparse array - at least one array index is missing
  • excessively sparse array - the number of values missing exceeds the configured ratio

An array is excessively sparse when all the following conditions are met:

  • encode_sparse_ratio > 0
  • max(table) > encode_sparse_safe
  • max(table) > count(table) * encode_sparse_ratio

MsgPack encoder will never consider an array to be excessively sparse when encode_sparse_ratio = 0. The encode_sparse_safe limit ensures that small Lua arrays are always encoded as sparse arrays. By default, attempting to encode an excessively sparse array will generate an error. If encode_sparse_convert is set to true, excessively sparse arrays will be handled as maps.

msgpack.cfg() example 1:

If msgpack.cfg.encode_invalid_numbers = true (the default), then NaN and Inf are legal values. If that is not desirable, then ensure that msgpack.encode() will not accept them, by saying msgpack.cfg{encode_invalid_numbers = false}, thus:

tarantool> msgpack = require('msgpack'); msgpack.cfg{encode_invalid_numbers = true}
---
...
tarantool> msgpack.decode(msgpack.encode{1, 0 / 0, 1 / 0, false})
---
- [1, -nan, inf, false]
- 22
...
tarantool> msgpack.cfg{encode_invalid_numbers = false}
---
...
tarantool> msgpack.decode(msgpack.encode{1, 0 / 0, 1 / 0, false})
---
- error: ... number must not be NaN or Inf'
...

msgpack.cfg example 2:

To avoid generating errors on attempts to encode unknown data types as userdata/cdata, you can use this code:

tarantool> httpc = require('http.client').new()
---
...

tarantool> msgpack.encode(httpc.curl)
---
- error: unsupported Lua type 'userdata'
...

tarantool> msgpack.encode(httpc.curl, {encode_use_tostring=true})
---
- '"userdata: 0x010a4ef2a0"'
...

Примечание

To achieve the same effect for only one call to msgpack.encode() (i.e. without changing the configuration permanently), you can use msgpack.encode({1, x, y, 2}, {encode_invalid_numbers = true}).

Similar configuration settings exist for JSON and YAML.

msgpack.NULL

Значение, сопоставимое с нулевым значением «nil» в языке Lua, которое можно использовать в качестве объекта-заполнителя в кортеже.

Example

tarantool> msgpack = require('msgpack')
---
...
tarantool> y = msgpack.encode({'a',1,'b',2})
---
...
tarantool> z = msgpack.decode(y)
---
...
tarantool> z[1], z[2], z[3], z[4]
---
- a
- 1
- b
- 2
...
tarantool> box.space.tester:insert{20, msgpack.NULL, 20}
---
- [20, null, 20]
...