diff options
Diffstat (limited to 'src/nvim/eval/decode.c')
-rw-r--r-- | src/nvim/eval/decode.c | 304 |
1 files changed, 160 insertions, 144 deletions
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..0933b1bf9c 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -1,12 +1,17 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include <stddef.h> #include <msgpack.h> -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" +#include "nvim/macros.h" #include "nvim/message.h" +#include "nvim/globals.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" #include "nvim/vim.h" // OK, FAIL @@ -51,16 +56,16 @@ static inline void create_special_dict(typval_T *const rettv, typval_T val) FUNC_ATTR_NONNULL_ALL { - dict_T *const dict = dict_alloc(); - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + dict_T *const dict = tv_dict_alloc(); + dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; type_di->di_tv.vval.v_list->lv_refcount++; - dict_add(dict, type_di); - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + tv_dict_add(dict, type_di); + dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; - dict_add(dict, val_di); + tv_dict_add(dict, val_di); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -118,18 +123,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, if (last_container.container.vval.v_list->lv_len != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = listitem_alloc(); + listitem_T *obj_li = tv_list_item_alloc(); obj_li->li_tv = obj.val; - list_append(last_container.container.vval.v_list, obj_li); + tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } ValuesStackItem key = kv_pop(*stack); @@ -138,34 +143,35 @@ static inline int json_decoder_pop(ValuesStackItem obj, assert(!(key.is_special_string || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); - dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); - clear_tv(&key.val); - if (dict_add(last_container.container.vval.v_dict, obj_di) + dictitem_T *const obj_di = tv_dict_item_alloc( + (const char *)key.val.vval.v_string); + tv_clear(&key.val); + if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = list_alloc(); - list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv = key.val; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv = obj.val; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); } } else { // Object with key only if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { EMSG2(_("E474: Expected string key: %s"), *pp); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } else if (!obj.didcomma && (last_container.special_val == NULL && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } // Handle empty key and key represented as special dictionary @@ -173,16 +179,16 @@ static inline int json_decoder_pop(ValuesStackItem obj, && (obj.is_special_string || obj.val.vval.v_string == NULL || *obj.val.vval.v_string == NUL - || dict_find(last_container.container.vval.v_dict, - obj.val.vval.v_string, -1))) { - clear_tv(&obj.val); + || tv_dict_find(last_container.container.vval.v_dict, + (const char *)obj.val.vval.v_string, -1))) { + tv_clear(&obj.val); // Restart (void) kv_pop(*container_stack); ValuesStackItem last_container_val = kv_A(*stack, last_container.stack_index); while (kv_size(*stack) > last_container.stack_index) { - clear_tv(&(kv_pop(*stack).val)); + tv_clear(&(kv_pop(*stack).val)); } *pp = last_container.s; *didcomma = last_container_val.didcomma; @@ -218,10 +224,80 @@ static inline int json_decoder_pop(ValuesStackItem obj, } \ } while (0) +/// Create a new special dictionary that ought to represent a MAP +/// +/// @param[out] ret_tv Address where new special dictionary is saved. +/// +/// @return [allocated] list which should contain key-value pairs. Return value +/// may be safely ignored. +list_T *decode_create_map_special_dict(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + list_T *const list = tv_list_alloc(); + list->lv_refcount++; + create_special_dict(ret_tv, kMPMap, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + return list; +} + +/// Convert char* string to typval_T +/// +/// Depending on whether string has (no) NUL bytes, it may use a special +/// dictionary or decode string to VAR_STRING. +/// +/// @param[in] s String to decode. +/// @param[in] len String length. +/// @param[in] hasnul Whether string has NUL byte, not or it was not yet +/// determined. +/// @param[in] binary If true, save special string type as kMPBinary, +/// otherwise kMPString. +/// @param[in] s_allocated If true, then `s` was allocated and can be saved in +/// a returned structure. If it is not saved there, it +/// will be freed. +/// +/// @return Decoded string. +typval_T decode_string(const char *const s, const size_t len, + const TriState hasnul, const bool binary, + const bool s_allocated) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(s != NULL || len == 0); + const bool really_hasnul = (hasnul == kNone + ? ((s != NULL) && (memchr(s, NUL, len) != NULL)) + : (bool)hasnul); + if (really_hasnul) { + list_T *const list = tv_list_alloc(); + list->lv_refcount++; + typval_T tv; + create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval = { .v_list = list }, + })); + const int elw_ret = encode_list_write((void *)list, s, len); + if (s_allocated) { + xfree((void *)s); + } + if (elw_ret == -1) { + tv_clear(&tv); + return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + } + return tv; + } else { + return (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval = { .v_string = (char_u *)( + s_allocated ? (char *)s : xmemdupz(s, len)) }, + }; + } +} + /// Parse JSON double-quoted string /// -/// @param[in] conv Defines conversion necessary to convert UTF-8 string to -/// &encoding. /// @param[in] buf Buffer being converted. /// @param[in] buf_len Length of the buffer. /// @param[in,out] pp Pointer to the start of the string. Must point to '"'. @@ -238,8 +314,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, /// value when decoder is restarted, otherwise unused. /// /// @return OK in case of success, FAIL in case of error. -static inline int parse_json_string(vimconv_T *const conv, - const char *const buf, const size_t buf_len, +static inline int parse_json_string(const char *const buf, const size_t buf_len, const char **const pp, ValuesStack *const stack, ContainerStack *const container_stack, @@ -360,8 +435,8 @@ static inline int parse_json_string(vimconv_T *const conv, case 'u': { const char ubuf[] = { t[1], t[2], t[3], t[4] }; t += 4; - unsigned long ch; - vim_str2nr((char_u *) ubuf, NULL, NULL, + uvarnumber_T ch; + vim_str2nr((char_u *)ubuf, NULL, NULL, STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); if (ch == 0) { hasnul = true; @@ -414,43 +489,13 @@ static inline int parse_json_string(vimconv_T *const conv, } PUT_FST_IN_PAIR(fst_in_pair, str_end); #undef PUT_FST_IN_PAIR - if (conv->vc_type != CONV_NONE) { - size_t str_len = (size_t) (str_end - str); - char *const new_str = (char *) string_convert(conv, (char_u *) str, - &str_len); - if (new_str == NULL) { - emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"), - (int) str_len, str); - xfree(str); - goto parse_json_string_fail; - } - xfree(str); - str = new_str; - str_end = new_str + str_len; - } - if (hasnul) { - typval_T obj; - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(&obj, kMPString, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, str, (size_t) (str_end - str)) - == -1) { - clear_tv(&obj); - goto parse_json_string_fail; - } - xfree(str); - POP(obj, true); - } else { - *str_end = NUL; - POP(((typval_T) { - .v_type = VAR_STRING, - .vval = { .v_string = (char_u *) str }, - }), false); + *str_end = NUL; + typval_T obj = decode_string( + str, (size_t)(str_end - str), hasnul ? kTrue : kFalse, false, true); + if (obj.v_type == VAR_UNKNOWN) { + goto parse_json_string_fail; } + POP(obj, obj.v_type != VAR_STRING); goto parse_json_string_ret; parse_json_string_fail: ret = FAIL; @@ -564,7 +609,7 @@ parse_json_number_check: tv.v_type = VAR_FLOAT; } else { // Convert integer - long nr; + varnumber_T nr; int num_len; vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); if ((int) exp_num_len != num_len) { @@ -572,7 +617,7 @@ parse_json_number_check: "to integer vim_str2nr consumed %i bytes in place of %zu"), (int) exp_num_len, s, num_len, exp_num_len); } - tv.vval.v_number = (varnumber_T) nr; + tv.vval.v_number = nr; } if (json_decoder_pop(OBJ(tv, false, *didcomma, *didcolon), stack, container_stack, @@ -624,9 +669,6 @@ int json_decode_string(const char *const buf, const size_t buf_len, EMSG(_("E474: Attempt to decode a blank string")); return FAIL; } - vimconv_T conv = { .vc_type = CONV_NONE }; - convert_setup(&conv, (char_u *) "utf-8", p_enc); - conv.vc_fail = true; int ret = OK; ValuesStack stack = KV_INITIAL_VALUE; ContainerStack container_stack = KV_INITIAL_VALUE; @@ -772,7 +814,7 @@ json_decode_string_cycle_start: break; } case '"': { - if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack, + if (parse_json_string(buf, buf_len, &p, &stack, &container_stack, &next_map_special, &didcomma, &didcolon) == FAIL) { // Error message was already given @@ -806,7 +848,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); list->lv_refcount++; typval_T tv = { .v_type = VAR_LIST, @@ -827,15 +869,9 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); - val_list->lv_refcount++; - create_special_dict(&tv, kMPMap, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = val_list }, - })); + val_list = decode_create_map_special_dict(&tv); } else { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; tv = (typval_T) { .v_type = VAR_DICT, @@ -887,7 +923,7 @@ json_decode_string_after_cycle: json_decode_string_fail: ret = FAIL; while (kv_size(stack)) { - clear_tv(&(kv_pop(stack).val)); + tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: kv_destroy(stack); @@ -933,7 +969,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -941,10 +977,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, })); uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + tv_list_append_number(list, 1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -956,22 +992,28 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + uint64_t n = -((uint64_t)mobj.via.i64); + tv_list_append_number(list, -1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { *rettv = (typval_T) { .v_type = VAR_FLOAT, .v_lock = VAR_UNLOCKED, @@ -980,43 +1022,23 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPString, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) - == -1) { + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false, + false); + if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } break; } case MSGPACK_OBJECT_BIN: { - if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { - *rettv = (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, - }; - break; - } - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPBinary, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); - if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) - == -1) { + *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true, + false); + if (rettv->v_type == VAR_UNKNOWN) { return FAIL; } break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1024,9 +1046,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); + listitem_T *const li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); + tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { return FAIL; } @@ -1042,7 +1064,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) goto msgpack_to_vim_generic_map; } } - dict_T *const dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -1055,9 +1077,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, mobj.via.map.ptr[i].key.via.str.size); di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { + if (tv_dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map - clear_tv(rettv); + tv_clear(rettv); xfree(di); goto msgpack_to_vim_generic_map; } @@ -1067,22 +1089,16 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); - list->lv_refcount++; - create_special_dict(rettv, kMPMap, ((typval_T) { - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval = { .v_list = list }, - })); + list_T *const list = decode_create_map_special_dict(rettv); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(list, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { return FAIL; } @@ -1093,11 +1109,11 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); + tv_list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = tv_list_alloc(); + tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, |