diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2020-11-18 03:11:00 +0000 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2021-09-15 21:19:22 +0100 |
commit | 9095101743b2606fb1d5f7a5a1216f22d2fb2b4a (patch) | |
tree | de186e64199ab977e0aa1beca08ac8aee9907a4d /src/nvim/eval | |
parent | 685cf398130c61c158401b992a1893c2405cd7d2 (diff) | |
download | rneovim-9095101743b2606fb1d5f7a5a1216f22d2fb2b4a.tar.gz rneovim-9095101743b2606fb1d5f7a5a1216f22d2fb2b4a.tar.bz2 rneovim-9095101743b2606fb1d5f7a5a1216f22d2fb2b4a.zip |
vim-patch:8.1.0735: cannot handle binary data
Problem: Cannot handle binary data.
Solution: Add the Blob type. (Yasuhiro Matsumoto, closes vim/vim#3638)
https://github.com/vim/vim/commit/6e5ea8d2a995b32bbc5972edc4f827b959f2702f
Nvim-specific Blob conversions are implemented in future commits.
Refactor write_blob() to use a FileDescriptor, as f_writefile() was
refactored to use one (does not apply to read_blob()).
Use var_check_lock() in f_add() for Blobs from v8.1.0897.
Add a modeline to test_blob.vim and fix some doc typos.
Include if_perl.txt's VIM::Blob() documentation. Interestingly, this
function already worked before this port, as it just returns a Blob
string literal, not an actual Blob object.
N/A patches for version.c:
vim-patch:8.1.0741: viminfo with Blob is not tested
Problem: Viminfo with Blob is not tested.
Solution: Extend the viminfo test. Fix reading a blob. Fixed storing a
special variable value.
https://github.com/vim/vim/commit/8c8b8bb56c724cc1bfc3d8520eec33f2d399697c
vim-patch:8.1.1022: may use NULL pointer when out of memory
Problem: May use NULL pointer when out of memory. (Coverity)
Solution: Check for blob_alloc() returning NULL.
https://github.com/vim/vim/commit/e142a9467a7f6845a426d8db6efedf246d3c13ac
Diffstat (limited to 'src/nvim/eval')
-rw-r--r-- | src/nvim/eval/encode.c | 31 | ||||
-rw-r--r-- | src/nvim/eval/executor.c | 14 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 218 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 130 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 69 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 12 |
6 files changed, 442 insertions, 32 deletions
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index a4d7af7971..45927db0ac 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -319,6 +319,28 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + do { \ + const blob_T *const blob_ = (blob); \ + const int len_ = (len); \ + if (len_ == 0) { \ + ga_concat(gap, "[]"); \ + } else { \ + ga_grow(gap, 1 + len_ * 5); \ + ga_append(gap, '['); \ + char numbuf[NUMBUFLEN]; \ + for (int i_ = 0; i_ < len_; i_++) { \ + if (i_ > 0) { \ + ga_append(gap, ','); \ + } \ + vim_snprintf((char *)numbuf, ARRAY_SIZE(numbuf), "0x%02X", \ + (int)tv_blob_get(blob_, i_)); \ + ga_concat(gap, numbuf); \ + } \ + ga_append(gap, ']'); \ + } \ + } while (0) + #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ do { \ char numbuf[NUMBUFLEN]; \ @@ -705,6 +727,10 @@ static inline int convert_to_json_string(garray_T *const gap, return FAIL; \ } while (0) +#undef TYPVAL_ENCODE_CONV_BLOB +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + abort() /* TODO(seandewar) */ \ + #undef TYPVAL_ENCODE_CONV_FUNC_START #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ return conv_error(_("E474: Error while dumping %s, %s: " \ @@ -770,6 +796,7 @@ bool encode_check_json_key(const typval_T *const tv) #undef TYPVAL_ENCODE_CONV_STRING #undef TYPVAL_ENCODE_CONV_STR_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_BLOB #undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FUNC_START @@ -904,6 +931,9 @@ char *encode_tv2json(typval_T *tv, size_t *len) } \ } while (0) +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + abort() /* TODO(seandewar) */ \ + #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ msgpack_pack_int64(packer, (int64_t)(num)) @@ -982,6 +1012,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) #undef TYPVAL_ENCODE_CONV_STRING #undef TYPVAL_ENCODE_CONV_STR_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_BLOB #undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FUNC_START diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 8ac2c3b8eb..5ced2a0535 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -38,6 +38,20 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, case VAR_SPECIAL: { break; } + case VAR_BLOB: { + if (*op != '+' || tv2->v_type != VAR_BLOB) { + break; + } + // Blob += Blob + if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) { + blob_T *const b1 = tv1->vval.v_blob; + blob_T *const b2 = tv2->vval.v_blob; + for (int i = 0; i < tv_blob_len(b2); i++) { + ga_append(&b1->bv_ga, (char)tv_blob_get(b2, i)); + } + } + return OK; + } case VAR_LIST: { if (*op != '+' || tv2->v_type != VAR_LIST) { break; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 801b0f9d1c..dbb00da911 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -321,6 +321,13 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } + } else if (argvars[0].v_type == VAR_BLOB) { + blob_T *const b = argvars[0].vval.v_blob; + if (b != NULL + && !var_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) { + ga_append(&b->bv_ga, (char_u)tv_get_number(&argvars[1])); + tv_copy(&argvars[0], rettv); + } } else { EMSG(_(e_listreq)); } @@ -1874,6 +1881,10 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = argvars[0].vval.v_special == kSpecialVarNull; break; } + case VAR_BLOB: { + n = (tv_blob_len(argvars[0].vval.v_blob) == 0); + break; + } case VAR_UNKNOWN: { internal_error("f_empty(UNKNOWN)"); break; @@ -2791,7 +2802,19 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) typval_T *tv = NULL; bool what_is_dict = false; - if (argvars[0].v_type == VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + const int idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + rettv->v_type = VAR_NUMBER; + if (idx >= tv_blob_len(argvars[0].vval.v_blob)) { + EMSGN(_(e_blobidx), idx); + } else { + rettv->vval.v_number = tv_blob_get(argvars[0].vval.v_blob, idx); + } + } + } else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; @@ -4790,7 +4813,31 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool ic = false; rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + int start = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) { + start = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + } + blob_T *const b = argvars[0].vval.v_blob; + if (b == NULL) { + return; + } + for (idx = start; idx < tv_blob_len(b); idx++) { + typval_T tv; + tv.v_type = VAR_NUMBER; + tv.vval.v_number = tv_blob_get(b, idx); + if (tv_equal(&tv, &argvars[1], ic, false)) { + rettv->vval.v_number = idx; + return; + } + } + return; + } else if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } @@ -4920,7 +4967,37 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; bool error = false; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + long before = 0; + const int len = tv_blob_len(argvars[0].vval.v_blob); + + if (argvars[2].v_type != VAR_UNKNOWN) { + before = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // type error; errmsg already given + } + if (before < 0 || before > len) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + const int val = tv_get_number_chk(&argvars[1], &error); + if (error) { + return; + } + if (val < 0 || val > 255) { + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + + ga_grow(&argvars[0].vval.v_blob->bv_ga, 1); + char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + memmove(p + before + 1, p + before, (size_t)len - before); + *(p + before) = val; + argvars[0].vval.v_blob->bv_ga.ga_len++; + + tv_copy(&argvars[0], rettv); + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), N_("insert() argument"), TV_TRANSLATE)) { @@ -5581,6 +5658,10 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_get_string(&argvars[0])); break; } + case VAR_BLOB: { + rettv->vval.v_number = tv_blob_len(argvars[0].vval.v_blob); + break; + } case VAR_LIST: { rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; @@ -6894,6 +6975,7 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool binary = false; + bool blob = false; FILE *fd; char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1 int io_size = sizeof(buf); @@ -6906,14 +6988,14 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { binary = true; + } else if (strcmp(tv_get_string(&argvars[1]), "B") == 0) { + blob = true; } if (argvars[2].v_type != VAR_UNKNOWN) { maxline = tv_get_number(&argvars[2]); } } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); - // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -6922,6 +7004,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + if (blob) { + tv_blob_alloc_ret(rettv); + if (!read_blob(fd, rettv->vval.v_blob)) { + EMSG("cannot read file"); + tv_blob_free(rettv->vval.v_blob); + } + fclose(fd); + return; + } + + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); @@ -7190,6 +7284,55 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } + } else if (argvars[0].v_type == VAR_BLOB) { + bool error = false; + idx = (long)tv_get_number_chk(&argvars[1], &error); + + if (!error) { + blob_T *const b = argvars[0].vval.v_blob; + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + EMSGN(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T)(*(p + idx)); + memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + b->bv_ga.ga_len--; + } else { + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + EMSGN(_(e_blobidx), end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = end - idx + 1; + ga_grow(&blob->bv_ga, end - idx + 1); + + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + tv_blob_set_ret(rettv, blob); + + memmove(p + idx, p + end + 1, (size_t)(len - end)); + b->bv_ga.ga_len -= end - idx + 1; + } + } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), @@ -7465,13 +7608,25 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - if (argvars[0].v_type != VAR_LIST) { + if (argvars[0].v_type == VAR_BLOB) { + blob_T *const b = argvars[0].vval.v_blob; + const int len = tv_blob_len(b); + + for (int i = 0; i < len / 2; i++) { + const char_u tmp = tv_blob_get(b, i); + tv_blob_set(b, i, tv_blob_get(b, len - i - 1)); + tv_blob_set(b, len - i - 1, tmp); + } + tv_blob_set_ret(rettv, b); + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("reverse() argument"), TV_TRANSLATE)) { - tv_list_reverse(l); - tv_list_set_ret(rettv, l); + } else { + list_T *const l = argvars[0].vval.v_list; + if (!var_check_lock(tv_list_locked(l), N_("reverse() argument"), + TV_TRANSLATE)) { + tv_list_reverse(l); + tv_list_set_ret(rettv, l); + } } } @@ -11291,15 +11446,16 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = -1; switch (argvars[0].v_type) { - case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; - case VAR_STRING: n = VAR_TYPE_STRING; break; + case VAR_NUMBER: n = VAR_TYPE_NUMBER; break; + case VAR_STRING: n = VAR_TYPE_STRING; break; case VAR_PARTIAL: - case VAR_FUNC: n = VAR_TYPE_FUNC; break; - case VAR_LIST: n = VAR_TYPE_LIST; break; - case VAR_DICT: n = VAR_TYPE_DICT; break; - case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; - case VAR_BOOL: n = VAR_TYPE_BOOL; break; - case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break; + case VAR_FUNC: n = VAR_TYPE_FUNC; break; + case VAR_LIST: n = VAR_TYPE_LIST; break; + case VAR_DICT: n = VAR_TYPE_DICT; break; + case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; + case VAR_BOOL: n = VAR_TYPE_BOOL; break; + case VAR_SPECIAL: n = VAR_TYPE_SPECIAL; break; + case VAR_BLOB: n = VAR_TYPE_BLOB; break; case VAR_UNKNOWN: { internal_error("f_type(UNKNOWN)"); break; @@ -11678,16 +11834,16 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "writefile()"); + if (argvars[0].v_type == VAR_LIST) { + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { + return; + } + }); + } else if (argvars[0].v_type != VAR_BLOB) { + EMSG2(_(e_invarg2), "writefile()"); return; } - const list_T *const list = argvars[0].vval.v_list; - TV_LIST_ITER_CONST(list, li, { - if (!tv_check_str_or_nr(TV_LIST_ITEM_TV(li))) { - return; - } - }); bool binary = false; bool append = false; @@ -11727,7 +11883,13 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(&fp, list, binary)) { + bool write_ok; + if (argvars[0].v_type == VAR_BLOB) { + write_ok = write_blob(&fp, argvars[0].vval.v_blob); + } else { + write_ok = write_list(&fp, argvars[0].vval.v_list, binary); + } + if (write_ok) { rettv->vval.v_number = 0; } if ((error = file_close(&fp, do_fsync)) != 0) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 22b3bf026b..86e43e0819 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2125,6 +2125,73 @@ void tv_dict_set_keys_readonly(dict_T *const dict) }); } +//{{{1 Blobs +//{{{2 Alloc/free + +/// Allocate an empty blob. +/// +/// Caller should take care of the reference count. +/// +/// @return [allocated] new blob. +blob_T *tv_blob_alloc(void) + FUNC_ATTR_NONNULL_RET +{ + blob_T *const blob = xcalloc(1, sizeof(blob_T)); + ga_init(&blob->bv_ga, 1, 100); + return blob; +} + +/// Free a blob. Ignores the reference count. +/// +/// @param[in,out] b Blob to free. +void tv_blob_free(blob_T *const b) + FUNC_ATTR_NONNULL_ALL +{ + ga_clear(&b->bv_ga); + xfree(b); +} + +/// Unreference a blob. +/// +/// Decrements the reference count and frees blob when it becomes zero. +/// +/// @param[in,out] b Blob to operate on. +void tv_blob_unref(blob_T *const b) +{ + if (b != NULL && --b->bv_refcount <= 0) { + tv_blob_free(b); + } +} + +//{{{2 Operations on the whole blob + +/// Check whether two blobs are equal. +/// +/// @param[in] b1 First blob. +/// @param[in] b2 Second blob. +/// +/// @return true if blobs are equal, false otherwise. +bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (b1 == NULL || b2 == NULL) { + return false; + } + if (b1 == b2) { + return true; + } + if (tv_blob_len(b1) != tv_blob_len(b2)) { + return false; + } + + for (int i = 0; i < b1->bv_ga.ga_len; i++) { + if (tv_blob_get(b1, i) != tv_blob_get(b2, i)) { + return false; + } + } + return true; +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -2169,6 +2236,18 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) tv_dict_set_ret(ret_tv, d); } +/// Allocate an empty blob for a return value. +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where blob is saved. +void tv_blob_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + blob_T *const b = tv_blob_alloc(); + tv_blob_set_ret(ret_tv, b); +} + //{{{3 Clear #define TYPVAL_ENCODE_ALLOW_SPECIALS false @@ -2210,6 +2289,13 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + do { \ + tv_blob_unref(tv->vval.v_blob); \ + tv->vval.v_blob = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + static inline int _nothing_conv_func_start(typval_T *const tv, char_u *const fun) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) @@ -2392,6 +2478,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, #undef TYPVAL_ENCODE_CONV_STRING #undef TYPVAL_ENCODE_CONV_STR_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_BLOB #undef TYPVAL_ENCODE_CONV_FUNC_START #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF @@ -2449,6 +2536,10 @@ void tv_free(typval_T *tv) xfree(tv->vval.v_string); break; } + case VAR_BLOB: { + tv_blob_unref(tv->vval.v_blob); + break; + } case VAR_LIST: { tv_list_unref(tv->vval.v_list); break; @@ -2509,6 +2600,12 @@ void tv_copy(const typval_T *const from, typval_T *const to) } break; } + case VAR_BLOB: { + if (from->vval.v_blob != NULL) { + to->vval.v_blob->bv_refcount++; + } + break; + } case VAR_LIST: { tv_list_ref(to->vval.v_list); break; @@ -2560,6 +2657,13 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, tv->v_lock); switch (tv->v_type) { + case VAR_BLOB: { + blob_T *const b = tv->vval.v_blob; + if (b != NULL) { + CHANGE_LOCK(lock, b->bv_lock); + } + break; + } case VAR_LIST: { list_T *const l = tv->vval.v_list; if (l != NULL) { @@ -2646,10 +2750,11 @@ bool tv_check_lock(const typval_T *tv, const char *name, VarLockStatus lock = VAR_UNLOCKED; switch (tv->v_type) { - // case VAR_BLOB: - // if (tv->vval.v_blob != NULL) - // lock = tv->vval.v_blob->bv_lock; - // break; + case VAR_BLOB: + if (tv->vval.v_blob != NULL) { + lock = tv->vval.v_blob->bv_lock; + } + break; case VAR_LIST: if (tv->vval.v_list != NULL) { lock = tv->vval.v_list->lv_lock; @@ -2769,6 +2874,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, recursive_cnt--; return r; } + case VAR_BLOB: { + return tv_blob_equal(tv1->vval.v_blob, tv2->vval.v_blob); + } case VAR_NUMBER: { return tv1->vval.v_number == tv2->vval.v_number; } @@ -2835,6 +2943,10 @@ bool tv_check_str_or_nr(const typval_T *const tv) EMSG(_("E728: Expected a Number or a String, Dictionary found")); return false; } + case VAR_BLOB: { + EMSG(_("E974: Expected a Number or a String, Blob found")); + return false; + } case VAR_BOOL: { EMSG(_("E5299: Expected a Number or a String, Boolean found")); return false; @@ -2860,6 +2972,7 @@ static const char *const num_errors[] = { [VAR_LIST]=N_("E745: Using a List as a Number"), [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), [VAR_FLOAT]=N_("E805: Using a Float as a Number"), + [VAR_BLOB]=N_("E974: Using a Blob as a Number"), [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), }; @@ -2888,6 +3001,7 @@ bool tv_check_num(const typval_T *const tv) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: + case VAR_BLOB: case VAR_UNKNOWN: { EMSG(_(num_errors[tv->v_type])); return false; @@ -2905,6 +3019,7 @@ static const char *const str_errors[] = { [VAR_LIST]=N_("E730: using List as a String"), [VAR_DICT]=N_("E731: using Dictionary as a String"), [VAR_FLOAT]=((const char *)e_float_as_string), + [VAR_BLOB]=N_("E976: using Blob as a String"), [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), }; @@ -2933,6 +3048,7 @@ bool tv_check_str(const typval_T *const tv) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: + case VAR_BLOB: case VAR_UNKNOWN: { EMSG(_(str_errors[tv->v_type])); return false; @@ -2980,6 +3096,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_PARTIAL: case VAR_LIST: case VAR_DICT: + case VAR_BLOB: case VAR_FLOAT: { EMSG(_(num_errors[tv->v_type])); break; @@ -3075,6 +3192,10 @@ float_T tv_get_float(const typval_T *const tv) EMSG(_("E907: Using a special value as a Float")); break; } + case VAR_BLOB: { + EMSG(_("E975: Using a Blob as a Float")); + break; + } case VAR_UNKNOWN: { emsgf(_(e_intern2), "tv_get_float(UNKNOWN)"); break; @@ -3134,6 +3255,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: + case VAR_BLOB: case VAR_UNKNOWN: { EMSG(_(str_errors[tv->v_type])); return false; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index ef49fa1de6..5aecaccee9 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -64,6 +64,7 @@ enum ListLenSpecials { typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; +typedef struct blobvar_S blob_T; typedef struct ufunc ufunc_T; @@ -123,6 +124,7 @@ typedef enum { VAR_SPECIAL, ///< Special value (null), .v_special ///< is used. VAR_PARTIAL, ///< Partial, .v_partial is used. + VAR_BLOB, ///< Blob, .v_blob is used. } VarType; /// Structure that holds an internal variable value @@ -138,6 +140,7 @@ typedef struct { list_T *v_list; ///< List for VAR_LIST, can be NULL. dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. partial_T *v_partial; ///< Closure: function with args. + blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL. } vval; ///< Actual value. } typval_T; @@ -252,6 +255,13 @@ struct dictvar_S { LuaRef lua_table_ref; }; +/// Structure to hold info about a Blob +struct blobvar_S { + garray_T bv_ga; ///< Growarray with the data. + int bv_refcount; ///< Reference count. + VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED. +}; + /// Type used for script ID typedef int scid_T; /// Format argument for scid_T @@ -711,6 +721,65 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } +static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); + +/// Set a blob as the return value. +/// +/// Increments the reference count. +/// +/// @param[out] tv Object to receive the blob. +/// @param[in,out] b Blob to pass to the object. +static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) +{ + tv->v_type = VAR_BLOB; + tv->vval.v_blob = b; + if (b != NULL) { + b->bv_refcount++; + } +} + +static inline int tv_blob_len(const blob_T *const b) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the length of the data in the blob, in bytes. +/// +/// @param[in] b Blob to check. +static inline int tv_blob_len(const blob_T *const b) +{ + if (b == NULL) { + return 0; + } + return b->bv_ga.ga_len; +} + +static inline char_u tv_blob_get(const blob_T *const b, int idx) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the byte at index `idx` in the blob. +/// +/// @param[in] b Blob to index. Cannot be NULL. +/// @param[in] idx Index in a blob. Must be valid. +/// +/// @return Byte value at the given index. +static inline char_u tv_blob_get(const blob_T *const b, int idx) +{ + return ((char_u *)b->bv_ga.ga_data)[idx]; +} + +static inline void tv_blob_set(blob_T *const b, int idx, char_u c) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +/// Store the byte `c` at index `idx` in the blob. +/// +/// @param[in] b Blob to index. Cannot be NULL. +/// @param[in] idx Index in a blob. Must be valid. +/// @param[in] c Value to store. +static inline void tv_blob_set(blob_T *const b, int idx, char_u c) +{ + ((char_u *)b->bv_ga.ga_data)[idx] = c; +} + /// Initialize VimL object /// /// Initializes to unlocked VAR_UNKNOWN object. diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 91c948ce7e..cd1be1eecc 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -83,6 +83,13 @@ /// @param len String length. /// @param type EXT type. +/// @def TYPVAL_ENCODE_CONV_BLOB +/// @brief Macros used to convert a blob +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. +/// @param blob Pointer to the blob to convert. +/// @param len Blob length. + /// @def TYPVAL_ENCODE_CONV_FUNC_START /// @brief Macros used when starting to convert a funcref or a partial /// @@ -330,6 +337,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float); break; } + case VAR_BLOB: { + TYPVAL_ENCODE_CONV_BLOB(tv, tv->vval.v_blob, + tv_blob_len(tv->vval.v_blob)); + break; + } case VAR_FUNC: { TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string); TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0); |