diff options
-rw-r--r-- | runtime/doc/eval.txt | 14 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/eval/encode.c | 8 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 15 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 54 |
5 files changed, 60 insertions, 33 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 94be7b595e..013bff3d20 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2534,7 +2534,7 @@ min({expr}) Number minimum value of items in {expr} mkdir({name} [, {path} [, {prot}]]) Number create directory {name} mode([expr]) String current editing mode -msgpackdump({list}) List dump a list of objects to msgpack +msgpackdump({list} [, {type}]) List/Blob dump objects to msgpack msgpackparse({list}) List parse msgpack to a list of objects nextnonblank({lnum}) Number line nr of non-blank line >= {lnum} nr2char({expr}[, {utf8}]) String single char with ASCII/UTF8 value {expr} @@ -6824,11 +6824,15 @@ mode([expr]) Return a string that indicates the current mode. the leading character(s). Also see |visualmode()|. -msgpackdump({list}) *msgpackdump()* - Convert a list of VimL objects to msgpack. Returned value is - |readfile()|-style list. Example: > +msgpackdump({list} [, {type}]) *msgpackdump()* + Convert a list of VimL objects to msgpack. Returned value is a + |readfile()|-style list. When {type} contains "B", a |Blob| is + returned instead. Example: > call writefile(msgpackdump([{}]), 'fname.mpack', 'b') -< This will write the single 0x80 byte to `fname.mpack` file +< or, using a |Blob|: > + call writefile(msgpackdump([{}], 'B'), 'fname.mpack') +< + This will write the single 0x80 byte to a `fname.mpack` file (dictionary with zero items is represented by 0x80 byte in messagepack). diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index a7242ba73a..5f355abe3c 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -250,7 +250,7 @@ return { min={args=1, base=1}, mkdir={args={1, 3}}, mode={args={0, 1}}, - msgpackdump={args=1}, + msgpackdump={args={1, 2}}, msgpackparse={args=1}, nextnonblank={args=1}, nr2char={args={1, 2}}, diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1c2274c410..b5e50e7ef5 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -47,6 +47,14 @@ const char *const encode_special_var_names[] = { # include "eval/encode.c.generated.h" #endif +/// Msgpack callback for writing to a Blob +int encode_blob_write(void *const data, const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ARG(1) +{ + ga_concat_len(&((blob_T *)data)->bv_ga, buf, len); + return (int)len; +} + /// Msgpack callback for writing to readfile()-style list int encode_list_write(void *const data, const char *const buf, const size_t len) FUNC_ATTR_NONNULL_ARG(1) diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e83e0ac0cc..a2ea74b19b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6506,9 +6506,16 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); list_T *const list = argvars[0].vval.v_list; - msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); + msgpack_packer *packer; + if (argvars[1].v_type != VAR_UNKNOWN + && strequal(tv_get_string(&argvars[1]), "B")) { + tv_blob_alloc_ret(rettv); + packer = msgpack_packer_new(rettv->vval.v_blob, &encode_blob_write); + } else { + packer = msgpack_packer_new(tv_list_alloc_ret(rettv, kListLenMayKnow), + &encode_list_write); + } const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; @@ -6516,11 +6523,11 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER(list, li, { vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { + if (encode_vim_to_msgpack(packer, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } }); - msgpack_packer_free(lpacker); + msgpack_packer_free(packer); } /// "msgpackparse" function diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index d95cea57e4..3db977257c 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -1,6 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear -local funcs = helpers.funcs local eval, eq = helpers.eval, helpers.eq local command = helpers.command local nvim = helpers.nvim @@ -511,13 +510,22 @@ end) describe('msgpackdump() function', function() before_each(clear) + local dump_eq = function(exp_list, arg_expr) + local l = {} + for i,v in ipairs(exp_list) do + l[i] = v:gsub('\n', '\000') + end + local exp_blobstr = table.concat(l, '\n') + eq(exp_list, eval('msgpackdump(' .. arg_expr .. ')')) + eq(exp_blobstr, eval('msgpackdump(' .. arg_expr .. ', "B")')) + end + it('dumps string as BIN 8', function() - nvim('set_var', 'obj', {'Test'}) - eq({"\196\004Test"}, eval('msgpackdump(obj)')) + dump_eq({'\196\004Test'}, '["Test"]') end) it('dumps blob as BIN 8', function() - eq({'\196\005Bl\nb!'}, eval('msgpackdump([0z426c006221])')) + dump_eq({'\196\005Bl\nb!'}, '[0z426c006221]') end) it('can dump generic mapping with generic mapping keys and values', function() @@ -525,56 +533,56 @@ describe('msgpackdump() function', function() command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('call add(todump._VAL, [todumpv1, todumpv2])') - eq({'\129\128\128'}, eval('msgpackdump([todump])')) + dump_eq({'\129\128\128'}, '[todump]') end) it('can dump v:true', function() - eq({'\195'}, funcs.msgpackdump({true})) + dump_eq({'\195'}, '[v:true]') end) it('can dump v:false', function() - eq({'\194'}, funcs.msgpackdump({false})) + dump_eq({'\194'}, '[v:false]') end) - it('can v:null', function() - command('let todump = v:null') + it('can dump v:null', function() + dump_eq({'\192'}, '[v:null]') end) it('can dump special bool mapping (true)', function() command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') - eq({'\195'}, eval('msgpackdump([todump])')) + dump_eq({'\195'}, '[todump]') end) it('can dump special bool mapping (false)', function() command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') - eq({'\194'}, eval('msgpackdump([todump])')) + dump_eq({'\194'}, '[todump]') end) it('can dump special nil mapping', function() command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') - eq({'\192'}, eval('msgpackdump([todump])')) + dump_eq({'\192'}, '[todump]') end) it('can dump special ext mapping', function() command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') - eq({'\212\005', ''}, eval('msgpackdump([todump])')) + dump_eq({'\212\005', ''}, '[todump]') end) it('can dump special array mapping', function() command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') - eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) + dump_eq({'\146\005\145\196\n'}, '[todump]') end) it('can dump special UINT64_MAX mapping', function() command('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') - eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) + dump_eq({'\207\255\255\255\255\255\255\255\255'}, '[todump]') end) it('can dump special INT64_MIN mapping', function() command('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump._VAL = [-1, 2, 0, 0]') - eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) + dump_eq({'\211\128\n\n\n\n\n\n\n'}, '[todump]') end) it('fails to dump a function reference', function() @@ -613,13 +621,13 @@ describe('msgpackdump() function', function() it('can dump dict with two same dicts inside', function() command('let inter = {}') command('let todump = {"a": inter, "b": inter}') - eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])')) + dump_eq({"\130\161a\128\161b\128"}, '[todump]') end) it('can dump list with two same lists inside', function() command('let inter = []') command('let todump = [inter, inter]') - eq({"\146\144\144"}, eval('msgpackdump([todump])')) + dump_eq({"\146\144\144"}, '[todump]') end) it('fails to dump a recursive list in a special dict', function() @@ -670,9 +678,9 @@ describe('msgpackdump() function', function() exc_exec('call msgpackdump()')) end) - it('fails when called with two arguments', function() + it('fails when called with three arguments', function() eq('Vim(call):E118: Too many arguments for function: msgpackdump', - exc_exec('call msgpackdump(["", ""], 1)')) + exc_exec('call msgpackdump(["", ""], 1, 2)')) end) it('fails to dump a string', function() @@ -714,9 +722,9 @@ describe('msgpackdump() function', function() end) it('can dump NULL string', function() - eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])')) - eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) - eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + dump_eq({'\196\n'}, '[$XXX_UNEXISTENT_VAR_XXX]') + dump_eq({'\196\n'}, '[{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]') + dump_eq({'\160'}, '[{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]') end) it('can dump NULL blob', function() |