diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-02-28 20:18:52 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2023-02-28 20:46:06 +0800 |
commit | f6b9791212e86bac3e8128035dec2bfc5016927f (patch) | |
tree | 102e7ddbd2acfff973fd9a2d819a783139ce0167 | |
parent | 3f381f4d04aa70829a865c82292b7d23fec29c52 (diff) | |
download | rneovim-f6b9791212e86bac3e8128035dec2bfc5016927f.tar.gz rneovim-f6b9791212e86bac3e8128035dec2bfc5016927f.tar.bz2 rneovim-f6b9791212e86bac3e8128035dec2bfc5016927f.zip |
vim-patch:8.2.3438: cannot manipulate blobs
Problem: Cannot manipulate blobs.
Solution: Add blob2list() and list2blob(). (Yegappan Lakshmanan,
closes vim/vim#8868)
https://github.com/vim/vim/commit/5dfe467432638fac2e0156a2f9cd0d9eb569fb39
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
-rw-r--r-- | runtime/doc/builtin.txt | 28 | ||||
-rw-r--r-- | runtime/doc/usr_41.txt | 6 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 73 | ||||
-rw-r--r-- | src/nvim/testdir/test_blob.vim | 39 |
5 files changed, 147 insertions, 1 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 568b73ddfa..b194a104f7 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -55,6 +55,7 @@ assert_report({msg}) Number report a test failure assert_true({actual} [, {msg}]) Number assert {actual} is true atan({expr}) Float arc tangent of {expr} atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2} +blob2list({blob}) List convert {blob} into a list of numbers browse({save}, {title}, {initdir}, {default}) String put up a file requester browsedir({title}, {initdir}) String put up a directory requester @@ -296,7 +297,8 @@ libcallnr({lib}, {func}, {arg}) Number idem, but return a Number line({expr} [, {winid}]) Number line nr of cursor, last line or mark line2byte({lnum}) Number byte count of line {lnum} lispindent({lnum}) Number Lisp indent for line {lnum} -list2str({list} [, {utf8}]) String turn numbers in {list} into a String +list2blob({list}) Blob turn {list} of numbers into a Blob +list2str({list} [, {utf8}]) String turn {list} of numbers into a String localtime() Number current time log({expr}) Float natural logarithm (base e) of {expr} log10({expr}) Float logarithm of Float {expr} to base 10 @@ -777,6 +779,17 @@ atan2({expr1}, {expr2}) *atan2()* Can also be used as a |method|: > Compute()->atan2(1) +blob2list({blob}) *blob2list()* + Return a List containing the number value of each byte in Blob + {blob}. Examples: > + blob2list(0z0102.0304) returns [1, 2, 3, 4] + blob2list(0z) returns [] +< Returns an empty List on error. |list2blob()| does the + opposite. + + Can also be used as a |method|: > + GetBlob()->blob2list() +< *browse()* browse({save}, {title}, {initdir}, {default}) Put up a file requester. This only works when "has("browse")" @@ -4768,6 +4781,19 @@ lispindent({lnum}) *lispindent()* Can also be used as a |method|: > GetLnum()->lispindent() +list2blob({list}) *list2blob()* + Return a Blob concatenating all the number values in {list}. + Examples: > + list2blob([1, 2, 3, 4]) returns 0z01020304 + list2blob([]) returns 0z +< Returns an empty Blob on error. If one of the numbers is + negative or more than 255 error *E1239* is given. + + |blob2list()| does the opposite. + + Can also be used as a |method|: > + GetList()->list2blob() + list2str({list} [, {utf8}]) *list2str()* Convert each number in {list} to a character string can concatenate them all. Examples: > diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 0f2cfdd2ac..ed16f837cf 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -721,6 +721,10 @@ Floating point computation: *float-functions* isinf() check for infinity isnan() check for not a number +Blob manipulation: *blob-functions* + blob2list() get a list of numbers from a blob + list2blob() get a blob from a list of numbers + Other computation: *bitwise-function* and() bitwise AND invert() bitwise invert @@ -1321,6 +1325,8 @@ is a List with arguments. Function references are most useful in combination with a Dictionary, as is explained in the next section. +More information about defining your own functions here: |user-functions|. + ============================================================================== *41.8* Lists and Dictionaries diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 24263d9aee..ae10a7e035 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -49,6 +49,7 @@ return { assert_true={args={1, 2}, base=1}, atan={args=1, base=1, float_func="atan"}, atan2={args=2, base=1}, + blob2list={args=1, base=1}, browse={args=4}, browsedir={args=2}, bufadd={args=1, base=1}, @@ -249,6 +250,7 @@ return { line={args={1, 2}, base=1}, line2byte={args=1, base=1}, lispindent={args=1, base=1}, + list2blob={args=1, base=1}, list2str={args={1, 2}, base=1}, localtime={}, log={args=1, base=1, float_func="log"}, diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index d56efe30da..6037536548 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -48,10 +48,16 @@ static char e_dict_required_for_argument_nr[] = N_("E1206: Dictionary required for argument %d"); static char e_number_required_for_argument_nr[] = N_("E1210: Number required for argument %d"); +static char e_list_required_for_argument_nr[] + = N_("E1211: List required for argument %d"); static char e_string_or_list_required_for_argument_nr[] = N_("E1222: String or List required for argument %d"); static char e_list_or_blob_required_for_argument_nr[] = N_("E1226: List or Blob required for argument %d"); +static char e_blob_required_for_argument_nr[] + = N_("E1238: Blob required for argument %d"); +static char e_invalid_value_for_blob_nr[] + = N_("E1239: Invalid value for blob: %d"); static char e_string_or_function_required_for_argument_nr[] = N_("E1256: String or function required for argument %d"); @@ -2826,6 +2832,51 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) } } +/// blob2list() function +void f_blob2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (tv_check_for_blob_arg(argvars, 0) == FAIL) { + return; + } + + blob_T *const blob = argvars->vval.v_blob; + list_T *const l = rettv->vval.v_list; + for (int i = 0; i < tv_blob_len(blob); i++) { + tv_list_append_number(l, tv_blob_get(blob, i)); + } +} + +/// list2blob() function +void f_list2blob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_blob_alloc_ret(rettv); + blob_T *const blob = rettv->vval.v_blob; + + if (tv_check_for_list_arg(argvars, 0) == FAIL) { + return; + } + + list_T *const l = argvars->vval.v_list; + if (l == NULL) { + return; + } + + TV_LIST_ITER_CONST(l, li, { + bool error = false; + varnumber_T n = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + if (error || n < 0 || n > 255) { + if (!error) { + semsg(_(e_invalid_value_for_blob_nr), n); + } + ga_clear(&blob->bv_ga); + return; + } + ga_append(&blob->bv_ga, (uint8_t)n); + }); +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -3968,6 +4019,28 @@ int tv_check_for_opt_number_arg(const typval_T *const args, const int idx) || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL; } +/// Give an error and return FAIL unless "args[idx]" is a blob. +int tv_check_for_blob_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_BLOB) { + semsg(_(e_blob_required_for_argument_nr), idx + 1); + return FAIL; + } + return OK; +} + +/// Give an error and return FAIL unless "args[idx]" is a list. +int tv_check_for_list_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_LIST) { + semsg(_(e_list_required_for_argument_nr), idx + 1); + return FAIL; + } + return OK; +} + /// Give an error and return FAIL unless "args[idx]" is a dict. int tv_check_for_dict_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim index b01d9309fa..28df120b78 100644 --- a/src/nvim/testdir/test_blob.vim +++ b/src/nvim/testdir/test_blob.vim @@ -666,6 +666,45 @@ func Test_blob_sort() call CheckLegacyAndVim9Failure(['call sort([11, 0z11], "N")'], 'E974:') endfunc +" Tests for the blob2list() function +func Test_blob2list() + call assert_fails('let v = blob2list(10)', 'E1238: Blob required for argument 1') + eval 0zFFFF->blob2list()->assert_equal([255, 255]) + let tests = [[0z0102, [1, 2]], + \ [0z00, [0]], + \ [0z, []], + \ [0z00000000, [0, 0, 0, 0]], + \ [0zAABB.CCDD, [170, 187, 204, 221]]] + for t in tests + call assert_equal(t[0]->blob2list(), t[1]) + endfor + exe 'let v = 0z' .. repeat('000102030405060708090A0B0C0D0E0F', 64) + call assert_equal(1024, blob2list(v)->len()) + call assert_equal([4, 8, 15], [v[100], v[1000], v[1023]]) + call assert_equal([], blob2list(v:_null_blob)) +endfunc + +" Tests for the list2blob() function +func Test_list2blob() + call assert_fails('let b = list2blob(0z10)', 'E1211: List required for argument 1') + let tests = [[[1, 2], 0z0102], + \ [[0], 0z00], + \ [[], 0z], + \ [[0, 0, 0, 0], 0z00000000], + \ [[170, 187, 204, 221], 0zAABB.CCDD], + \ ] + for t in tests + call assert_equal(t[0]->list2blob(), t[1]) + endfor + call assert_fails('let b = list2blob([1, []])', 'E745:') + call assert_fails('let b = list2blob([-1])', 'E1239:') + call assert_fails('let b = list2blob([256])', 'E1239:') + let b = range(16)->repeat(64)->list2blob() + call assert_equal(1024, b->len()) + call assert_equal([4, 8, 15], [b[100], b[1000], b[1023]]) + call assert_equal(0z, list2blob(v:_null_list)) +endfunc + " The following used to cause an out-of-bounds memory access func Test_blob2string() let v = '0z' .. repeat('01010101.', 444) |