From: Alexander Turenko <alexander.turenko@tarantool.org> To: Vladimir Davydov <vdavydov.dev@gmail.com> Cc: Alexander Turenko <alexander.turenko@tarantool.org>, tarantool-patches@freelists.org Subject: [PATCH v3 4/7] lua: add non-recursive msgpack decoding functions Date: Wed, 10 Apr 2019 18:21:22 +0300 [thread overview] Message-ID: <a23c73adc1b83e116b5cc7153b0de29baad21eef.1554906327.git.alexander.turenko@tarantool.org> (raw) In-Reply-To: <cover.1554906327.git.alexander.turenko@tarantool.org> Needed for #3276. @TarantoolBot document Title: Non-recursive msgpack decoding functions Contracts: ``` msgpack.decode_array(buf.rpos, buf:size()) -> arr_len, new_rpos msgpack.decode_map(buf.rpos, buf:size()) -> map_len, new_rpos ``` These functions are intended to be used with a msgpack buffer received from net.box. A user may want to skip {[IPROTO_DATA_KEY] = ...} wrapper and an array header before pass the buffer to decode in some C function. See https://github.com/tarantool/tarantool/issues/2195 for more information re this net.box's API. Consider merger's docbot comment for usage examples. --- src/lua/msgpack.c | 80 +++++++++++++++ test/app-tap/msgpack.test.lua | 180 +++++++++++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 1 deletion(-) diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c index 1b1874eb2..f445840bf 100644 --- a/src/lua/msgpack.c +++ b/src/lua/msgpack.c @@ -422,6 +422,84 @@ lua_ibuf_msgpack_decode(lua_State *L) return 2; } +/** + * Verify and set arguments: data and size. + * + * Always return 0. In case of any fail raise a Lua error. + */ +static int +verify_decode_args(lua_State *L, const char *func_name, const char **data_p, + ptrdiff_t *size_p) +{ + /* Verify arguments count. */ + if (lua_gettop(L) != 2) + return luaL_error(L, "Usage: %s(ptr, size)", func_name); + + /* Verify ptr type. */ + uint32_t ctypeid; + const char *data = *(char **) luaL_checkcdata(L, 1, &ctypeid); + if (ctypeid != CTID_CHAR_PTR) + return luaL_error(L, "%s: 'char *' expected", func_name); + + /* Verify size type and value. */ + ptrdiff_t size = (ptrdiff_t) luaL_checkinteger(L, 2); + if (size <= 0) + return luaL_error(L, "%s: non-positive size", func_name); + + *data_p = data; + *size_p = size; + + return 0; +} + +/** + * msgpack.decode_array(buf.rpos, buf:size()) -> arr_len, new_rpos + */ +static int +lua_decode_array(lua_State *L) +{ + const char *func_name = "msgpack.decode_array"; + const char *data; + ptrdiff_t size; + verify_decode_args(L, func_name, &data, &size); + + if (mp_typeof(*data) != MP_ARRAY) + return luaL_error(L, "%s: unexpected msgpack type", func_name); + + if (mp_check_array(data, data + size) > 0) + return luaL_error(L, "%s: unexpected end of buffer", func_name); + + uint32_t len = mp_decode_array(&data); + + lua_pushinteger(L, len); + *(const char **) luaL_pushcdata(L, CTID_CHAR_PTR) = data; + return 2; +} + +/** + * msgpack.decode_map(buf.rpos, buf:size()) -> map_len, new_rpos + */ +static int +lua_decode_map(lua_State *L) +{ + const char *func_name = "msgpack.decode_map"; + const char *data; + ptrdiff_t size; + verify_decode_args(L, func_name, &data, &size); + + if (mp_typeof(*data) != MP_MAP) + return luaL_error(L, "%s: unexpected msgpack type", func_name); + + if (mp_check_map(data, data + size) > 0) + return luaL_error(L, "%s: unexpected end of buffer", func_name); + + uint32_t len = mp_decode_map(&data); + + lua_pushinteger(L, len); + *(const char **) luaL_pushcdata(L, CTID_CHAR_PTR) = data; + return 2; +} + static int lua_msgpack_new(lua_State *L); @@ -430,6 +508,8 @@ static const luaL_Reg msgpacklib[] = { { "decode", lua_msgpack_decode }, { "decode_unchecked", lua_msgpack_decode_unchecked }, { "ibuf_decode", lua_ibuf_msgpack_decode }, + { "decode_array", lua_decode_array }, + { "decode_map", lua_decode_map }, { "new", lua_msgpack_new }, { NULL, NULL } }; diff --git a/test/app-tap/msgpack.test.lua b/test/app-tap/msgpack.test.lua index 0e1692ad9..ee215dfb1 100755 --- a/test/app-tap/msgpack.test.lua +++ b/test/app-tap/msgpack.test.lua @@ -49,9 +49,186 @@ local function test_misc(test, s) test:ok(not st and e:match("null"), "null ibuf") end +local function test_decode_array_map(test, s) + local ffi = require('ffi') + + local usage_err = 'Usage: msgpack%.decode_[^_(]+%(ptr, size%)' + local end_of_buffer_err = 'msgpack%.decode_[^_]+: unexpected end of buffer' + local non_positive_size_err = 'msgpack.decode_[^_]+: non%-positive size' + + local decode_cases = { + { + 'fixarray', + func = s.decode_array, + data = ffi.cast('char *', '\x94'), + size = 1, + exp_len = 4, + exp_rewind = 1, + }, + { + 'array 16', + func = s.decode_array, + data = ffi.cast('char *', '\xdc\x00\x04'), + size = 3, + exp_len = 4, + exp_rewind = 3, + }, + { + 'array 32', + func = s.decode_array, + data = ffi.cast('char *', '\xdd\x00\x00\x00\x04'), + size = 5, + exp_len = 4, + exp_rewind = 5, + }, + { + 'truncated array 16', + func = s.decode_array, + data = ffi.cast('char *', '\xdc\x00'), + size = 2, + exp_err = end_of_buffer_err, + }, + { + 'truncated array 32', + func = s.decode_array, + data = ffi.cast('char *', '\xdd\x00\x00\x00'), + size = 4, + exp_err = end_of_buffer_err, + }, + { + 'fixmap', + func = s.decode_map, + data = ffi.cast('char *', '\x84'), + size = 1, + exp_len = 4, + exp_rewind = 1, + }, + { + 'map 16', + func = s.decode_map, + data = ffi.cast('char *', '\xde\x00\x04'), + size = 3, + exp_len = 4, + exp_rewind = 3, + }, + { + 'array 32', + func = s.decode_map, + data = ffi.cast('char *', '\xdf\x00\x00\x00\x04'), + size = 5, + exp_len = 4, + exp_rewind = 5, + }, + { + 'truncated map 16', + func = s.decode_map, + data = ffi.cast('char *', '\xde\x00'), + size = 2, + exp_err = end_of_buffer_err, + }, + { + 'truncated map 32', + func = s.decode_map, + data = ffi.cast('char *', '\xdf\x00\x00\x00'), + size = 4, + exp_err = end_of_buffer_err, + }, + } + + local bad_api_cases = { + { + 'wrong msgpack type', + data = ffi.cast('char *', '\xc0'), + size = 1, + exp_err = 'msgpack.decode_[^_]+: unexpected msgpack type', + }, + { + 'zero size buffer', + data = ffi.cast('char *', ''), + size = 0, + exp_err = non_positive_size_err, + }, + { + 'negative size buffer', + data = ffi.cast('char *', ''), + size = -1, + exp_err = non_positive_size_err, + }, + { + 'size is nil', + data = ffi.cast('char *', ''), + size = nil, + exp_err = 'bad argument', + }, + { + 'no arguments', + args = {}, + exp_err = usage_err, + }, + { + 'one argument', + args = {ffi.cast('char *', '')}, + exp_err = usage_err, + }, + { + 'data is nil', + args = {nil, 1}, + exp_err = 'expected cdata as 1 argument', + }, + { + 'data is not cdata', + args = {1, 1}, + exp_err = 'expected cdata as 1 argument', + }, + { + 'data with wrong cdata type', + args = {box.tuple.new(), 1}, + exp_err = "msgpack.decode_[^_]+: 'char %*' expected", + }, + { + 'size has wrong type', + args = {ffi.cast('char *', ''), 'eee'}, + exp_err = 'bad argument', + }, + } + + test:plan(#decode_cases + 2 * #bad_api_cases) + + -- Decode cases. + for _, case in ipairs(decode_cases) do + if case.exp_err ~= nil then + local ok, err = pcall(case.func, case.data, case.size) + local description = ('bad; %s'):format(case[1]) + test:ok(ok == false and err:match(case.exp_err), description) + else + local len, new_buf = case.func(case.data, case.size) + local rewind = new_buf - case.data + local description = ('good; %s'):format(case[1]) + test:is_deeply({len, rewind}, {case.exp_len, case.exp_rewind}, + description) + end + end + + -- Bad api usage cases. + for _, func_name in ipairs({'decode_array', 'decode_map'}) do + for _, case in ipairs(bad_api_cases) do + local ok, err + if case.args ~= nil then + local args_len = table.maxn(case.args) + ok, err = pcall(s[func_name], unpack(case.args, 1, args_len)) + else + ok, err = pcall(s[func_name], case.data, case.size) + end + local description = ('%s bad api usage; %s'):format(func_name, + case[1]) + test:ok(ok == false and err:match(case.exp_err), description) + end + end +end + tap.test("msgpack", function(test) local serializer = require('msgpack') - test:plan(10) + test:plan(11) test:test("unsigned", common.test_unsigned, serializer) test:test("signed", common.test_signed, serializer) test:test("double", common.test_double, serializer) @@ -62,4 +239,5 @@ tap.test("msgpack", function(test) test:test("ucdata", common.test_ucdata, serializer) test:test("offsets", test_offsets, serializer) test:test("misc", test_misc, serializer) + test:test("decode_array_map", test_decode_array_map, serializer) end) -- 2.20.1
next prev parent reply other threads:[~2019-04-10 15:21 UTC|newest] Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-04-10 15:21 [PATCH v3 0/7] Merger Alexander Turenko 2019-04-10 15:21 ` [PATCH v3 1/7] Add luaL_iscallable with support of cdata metatype Alexander Turenko 2019-04-18 17:30 ` [tarantool-patches] " Konstantin Osipov 2019-04-30 12:45 ` Vladimir Davydov 2019-04-10 15:21 ` [PATCH v3 2/7] Add functions to ease using Lua iterators from C Alexander Turenko 2019-04-18 17:31 ` [tarantool-patches] " Konstantin Osipov 2019-04-30 12:46 ` Vladimir Davydov 2019-04-10 15:21 ` [PATCH v3 3/7] lua: optimize creation of a tuple from a tuple Alexander Turenko 2019-04-18 17:32 ` [tarantool-patches] " Konstantin Osipov 2019-04-30 12:50 ` Vladimir Davydov 2019-04-30 15:07 ` Alexander Turenko 2019-04-10 15:21 ` Alexander Turenko [this message] 2019-04-18 17:35 ` [tarantool-patches] [PATCH v3 4/7] lua: add non-recursive msgpack decoding functions Konstantin Osipov 2019-04-18 18:30 ` Alexander Turenko 2019-04-18 18:33 ` Konstantin Osipov 2019-04-18 18:44 ` Alexander Turenko 2019-04-30 13:03 ` Vladimir Davydov 2019-04-30 18:38 ` Alexander Turenko 2019-04-10 15:21 ` [PATCH v3 5/7] net.box: add skip_header option to use with buffer Alexander Turenko 2019-04-18 17:37 ` [tarantool-patches] " Konstantin Osipov 2019-04-18 18:39 ` Alexander Turenko 2019-04-30 13:16 ` Vladimir Davydov 2019-04-30 18:39 ` Alexander Turenko 2019-04-10 15:21 ` [PATCH v3 6/7] Add merger for tuples streams (C part) Alexander Turenko 2019-04-25 11:43 ` [tarantool-patches] " Konstantin Osipov 2019-04-25 13:32 ` Alexander Turenko 2019-04-25 13:45 ` Konstantin Osipov 2019-04-25 15:32 ` [tarantool-patches] " Alexander Turenko 2019-04-25 16:42 ` Konstantin Osipov 2019-04-30 15:34 ` Vladimir Davydov 2019-05-07 22:14 ` Alexander Turenko 2019-04-10 15:21 ` [PATCH v3 7/7] Add merger for tuple streams (Lua part) Alexander Turenko 2019-04-25 11:46 ` [tarantool-patches] " Konstantin Osipov 2019-04-25 12:53 ` Alexander Turenko 2019-04-25 13:30 ` Konstantin Osipov 2019-04-30 17:37 ` Vladimir Davydov 2019-04-30 21:09 ` [tarantool-patches] " Konstantin Osipov 2019-05-02 9:48 ` Vladimir Davydov 2019-05-07 22:14 ` Alexander Turenko
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=a23c73adc1b83e116b5cc7153b0de29baad21eef.1554906327.git.alexander.turenko@tarantool.org \ --to=alexander.turenko@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [PATCH v3 4/7] lua: add non-recursive msgpack decoding functions' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox