To: vim_dev@googlegroups.com Subject: Patch 8.2.3438 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3438 Problem: Cannot manipulate blobs. Solution: Add blob2list() and list2blob(). (Yegappan Lakshmanan, closes #8868) Files: runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/blob.c, src/errors.h, src/evalfunc.c, src/proto/blob.pro, src/proto/typval.pro, src/testdir/test_blob.vim, src/testdir/test_vim9_builtin.vim, src/typval.c *** ../vim-8.2.3437/runtime/doc/eval.txt 2021-08-29 12:36:43.301865015 +0200 --- runtime/doc/eval.txt 2021-09-14 17:49:11.560861109 +0200 *************** *** 2452,2469 **** balloon_gettext() String current text in the balloon balloon_show({expr}) none show {expr} inside the balloon balloon_split({msg}) List split {msg} as used for a balloon browse({save}, {title}, {initdir}, {default}) String put up a file requester browsedir({title}, {initdir}) String put up a directory requester bufadd({name}) Number add a buffer to the buffer list ! bufexists({expr}) Number |TRUE| if buffer {expr} exists ! buflisted({expr}) Number |TRUE| if buffer {expr} is listed ! bufload({expr}) Number load buffer {expr} if not loaded yet ! bufloaded({expr}) Number |TRUE| if buffer {expr} is loaded ! bufname([{expr}]) String Name of the buffer {expr} ! bufnr([{expr} [, {create}]]) Number Number of the buffer {expr} ! bufwinid({expr}) Number window ID of buffer {expr} ! bufwinnr({expr}) Number window number of buffer {expr} byte2line({byte}) Number line number at byte count {byte} byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr} byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr} --- 2469,2487 ---- balloon_gettext() String current text in the balloon balloon_show({expr}) none show {expr} inside the balloon balloon_split({msg}) List split {msg} as used for a balloon + 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 bufadd({name}) Number add a buffer to the buffer list ! bufexists({buf}) Number |TRUE| if buffer {buf} exists ! buflisted({buf}) Number |TRUE| if buffer {buf} is listed ! bufload({buf}) Number load buffer {buf} if not loaded yet ! bufloaded({buf}) Number |TRUE| if buffer {buf} is loaded ! bufname([{buf}]) String Name of the buffer {buf} ! bufnr([{buf} [, {create}]]) Number Number of the buffer {buf} ! bufwinid({buf}) Number window ID of buffer {buf} ! bufwinnr({buf}) Number window number of buffer {buf} byte2line({byte}) Number line number at byte count {byte} byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr} byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr} *************** *** 2704,2710 **** 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 listener_add({callback} [, {buf}]) Number add a callback to listen to changes listener_flush([{buf}]) none invoke listener callbacks --- 2722,2729 ---- 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} ! list2blob({list}) Blob turn {list} of numbers into a Blob ! list2str({list} [, {utf8}]) String turn {list} of numbers into a String listener_add({callback} [, {buf}]) Number add a callback to listen to changes listener_flush([{buf}]) none invoke listener callbacks *************** *** 3337,3342 **** --- 3357,3373 ---- < {only available when compiled with the |+balloon_eval_term| feature} + 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")" *************** *** 7143,7148 **** --- 7221,7239 ---- 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: > *** ../vim-8.2.3437/runtime/doc/usr_41.txt 2021-08-16 21:38:38.127122597 +0200 --- runtime/doc/usr_41.txt 2021-09-14 17:39:39.273540696 +0200 *************** *** 715,720 **** --- 723,732 ---- 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 *************** *** 1441,1446 **** --- 1453,1460 ---- 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 *** ../vim-8.2.3437/src/blob.c 2021-08-04 19:25:50.614808524 +0200 --- src/blob.c 2021-09-14 17:42:01.405372943 +0200 *************** *** 483,486 **** --- 483,547 ---- } } + /* + * blob2list() function + */ + void + f_blob2list(typval_T *argvars, typval_T *rettv) + { + blob_T *blob; + list_T *l; + int i; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (check_for_blob_arg(argvars, 0) == FAIL) + return; + + blob = argvars->vval.v_blob; + l = rettv->vval.v_list; + for (i = 0; i < blob_len(blob); i++) + list_append_number(l, blob_get(blob, i)); + } + + /* + * list2blob() function + */ + void + f_list2blob(typval_T *argvars, typval_T *rettv) + { + list_T *l; + listitem_T *li; + blob_T *blob; + + if (rettv_blob_alloc(rettv) == FAIL) + return; + blob = rettv->vval.v_blob; + + if (check_for_list_arg(argvars, 0) == FAIL) + return; + + l = argvars->vval.v_list; + if (l == NULL) + return; + + FOR_ALL_LIST_ITEMS(l, li) + { + int error; + varnumber_T n; + + error = FALSE; + n = tv_get_number_chk(&li->li_tv, &error); + if (error == TRUE || 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, n); + } + } + #endif // defined(FEAT_EVAL) *** ../vim-8.2.3437/src/errors.h 2021-09-12 20:57:56.821929392 +0200 --- src/errors.h 2021-09-14 17:42:39.885327370 +0200 *************** *** 660,662 **** --- 660,666 ---- INIT(= N_("E1236: Cannot use %s itself, it is imported with '*'")); EXTERN char e_no_such_user_defined_command_in_current_buffer_str[] INIT(= N_("E1237: No such user-defined command in current buffer: %s")); + EXTERN char e_blob_required_for_argument_nr[] + INIT(= N_("E1238: Blob required for argument %d")); + EXTERN char e_invalid_value_for_blob_nr[] + INIT(= N_("E1239: Invalid value for blob: %d")); *** ../vim-8.2.3437/src/evalfunc.c 2021-09-08 14:57:38.237188053 +0200 --- src/evalfunc.c 2021-09-14 17:39:39.273540696 +0200 *************** *** 289,294 **** --- 289,303 ---- } /* + * Check "type" is a blob + */ + static int + arg_blob(type_T *type, argcontext_T *context) + { + return check_arg_type(&t_blob, type, context); + } + + /* * Check "type" is a bool or number 0 or 1. */ static int *************** *** 680,685 **** --- 689,695 ---- /* * Lists of functions that check the argument types of a builtin function. */ + static argcheck_T arg1_blob[] = {arg_blob}; static argcheck_T arg1_bool[] = {arg_bool}; static argcheck_T arg1_buffer[] = {arg_buffer}; static argcheck_T arg1_buffer_or_dict_any[] = {arg_buffer_or_dict_any}; *************** *** 1169,1174 **** --- 1179,1186 ---- NULL #endif }, + {"blob2list", 1, 1, FEARG_1, arg1_blob, + ret_list_number, f_blob2list}, {"browse", 4, 4, 0, arg4_browse, ret_string, f_browse}, {"browsedir", 2, 2, 0, arg2_string, *************** *** 1589,1594 **** --- 1601,1608 ---- ret_number, f_line2byte}, {"lispindent", 1, 1, FEARG_1, arg1_lnum, ret_number, f_lispindent}, + {"list2blob", 1, 1, FEARG_1, arg1_list_number, + ret_blob, f_list2blob}, {"list2str", 1, 2, FEARG_1, arg2_list_number_bool, ret_string, f_list2str}, {"listener_add", 1, 2, FEARG_2, arg2_any_buffer, *** ../vim-8.2.3437/src/proto/blob.pro 2021-08-04 19:25:50.614808524 +0200 --- src/proto/blob.pro 2021-09-14 17:39:39.273540696 +0200 *************** *** 19,22 **** --- 19,24 ---- int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet); int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src); void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); + void f_blob2list(typval_T *argvars, typval_T *rettv); + void f_list2blob(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.2.3437/src/proto/typval.pro 2021-07-28 11:51:44.317061732 +0200 --- src/proto/typval.pro 2021-09-14 17:39:39.273540696 +0200 *************** *** 17,22 **** --- 17,23 ---- int check_for_float_or_nr_arg(typval_T *args, int idx); int check_for_bool_arg(typval_T *args, int idx); int check_for_opt_bool_arg(typval_T *args, int idx); + int check_for_blob_arg(typval_T *args, int idx); int check_for_list_arg(typval_T *args, int idx); int check_for_opt_list_arg(typval_T *args, int idx); int check_for_dict_arg(typval_T *args, int idx); *** ../vim-8.2.3437/src/testdir/test_blob.vim 2021-08-04 19:25:50.618808515 +0200 --- src/testdir/test_blob.vim 2021-09-14 17:46:29.753054129 +0200 *************** *** 638,641 **** --- 638,680 ---- 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(test_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(test_null_list())) + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.3437/src/testdir/test_vim9_builtin.vim 2021-09-11 20:20:34.011127608 +0200 --- src/testdir/test_vim9_builtin.vim 2021-09-14 17:39:39.273540696 +0200 *************** *** 287,292 **** --- 287,296 ---- assert_fails('balloon_split(true)', 'E1174:') enddef + def Test_blob2list() + CheckDefAndScriptFailure2(['blob2list(10)'], 'E1013: Argument 1: type mismatch, expected blob but got number', 'E1238: Blob required for argument 1') + enddef + def Test_browse() CheckFeature browse *************** *** 572,577 **** --- 576,582 ---- assert_equal(97, char2nr('a', 0)) assert_equal(97, char2nr('a', true)) assert_equal(97, char2nr('a', false)) + char2nr('')->assert_equal(0) enddef def Test_charclass() *************** *** 786,791 **** --- 791,798 ---- CheckDefAndScriptFailure2(['escape(true, false)'], 'E1013: Argument 1: type mismatch, expected string but got bool', 'E1174: String required for argument 1') CheckDefAndScriptFailure2(['escape("a", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2') assert_equal('a\:b', escape("a:b", ":")) + escape('abc', '')->assert_equal('abc') + escape('', ':')->assert_equal('') enddef def Test_eval() *************** *** 1921,1926 **** --- 1928,1938 ---- assert_equal(0, lispindent(1)) enddef + def Test_list2blob() + CheckDefAndScriptFailure2(['list2blob(10)'], 'E1013: Argument 1: type mismatch, expected list but got number', 'E1211: List required for argument 1') + CheckDefFailure(['list2blob([0z10, 0z02])'], 'E1013: Argument 1: type mismatch, expected list but got list') + enddef + def Test_list2str_str2list_utf8() var s = "\u3042\u3044" var l = [0x3042, 0x3044] *** ../vim-8.2.3437/src/typval.c 2021-09-13 21:36:23.737110573 +0200 --- src/typval.c 2021-09-14 17:39:39.277540692 +0200 *************** *** 471,476 **** --- 471,493 ---- } /* + * Give an error and return FAIL unless "args[idx]" is a blob. + */ + int + check_for_blob_arg(typval_T *args, int idx) + { + if (args[idx].v_type != VAR_BLOB) + { + if (idx >= 0) + semsg(_(e_blob_required_for_argument_nr), idx + 1); + else + emsg(_(e_blob_required)); + return FAIL; + } + return OK; + } + + /* * Give an error and return FAIL unless "args[idx]" is a list. */ int *** ../vim-8.2.3437/src/version.c 2021-09-13 22:17:33.950410680 +0200 --- src/version.c 2021-09-14 17:41:02.257442880 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3438, /**/ -- Q: Why do Norwegians excel at text editing? A: Their ancestors are Vi-kings /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///