aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2020-11-18 03:11:00 +0000
committerSean Dewar <seandewar@users.noreply.github.com>2021-09-15 21:19:22 +0100
commit9095101743b2606fb1d5f7a5a1216f22d2fb2b4a (patch)
treede186e64199ab977e0aa1beca08ac8aee9907a4d /src/nvim/eval
parent685cf398130c61c158401b992a1893c2405cd7d2 (diff)
downloadrneovim-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.c31
-rw-r--r--src/nvim/eval/executor.c14
-rw-r--r--src/nvim/eval/funcs.c218
-rw-r--r--src/nvim/eval/typval.c130
-rw-r--r--src/nvim/eval/typval.h69
-rw-r--r--src/nvim/eval/typval_encode.c.h12
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);