mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
eval: Remove most of msgpack* functions limitations
This commit is contained in:
parent
5a7135fa1c
commit
7fbefd585e
@ -1517,6 +1517,12 @@ v:mouse_col Column number for a mouse click obtained with |getchar()|.
|
||||
This is the screen column number, like with |virtcol()|. The
|
||||
value is zero when there was no mouse button click.
|
||||
|
||||
*v:msgpack_types* *msgpack_types-variable*
|
||||
v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()|
|
||||
and |msgpackdump()|. All types inside dictionary are fixed
|
||||
(not editable) empty lists. To check whether some list is one
|
||||
of msgpack types, use |is| operator.
|
||||
|
||||
*v:oldfiles* *oldfiles-variable*
|
||||
v:oldfiles List of file names that is loaded from the |viminfo| file on
|
||||
startup. These are the files that Vim remembers marks for.
|
||||
@ -4624,11 +4630,11 @@ msgpackdump({list}) *msgpackdump()*
|
||||
messagepack).
|
||||
|
||||
Limitations:
|
||||
1. |Funcref|s cannot be dumped as funcrefs, they are dumped as
|
||||
NIL objects instead.
|
||||
2. NIL and BOOL objects are never dumped, as well as objects
|
||||
from EXT family.
|
||||
3. Strings are always dumped as BIN strings.
|
||||
1. |Funcref|s cannot be dumped.
|
||||
2. Containers that reference themselves cannot be dumped.
|
||||
3. Dictionary keys are always dumped as STR strings.
|
||||
4. Other strings are always dumped as BIN strings.
|
||||
5. Points 3. and 4. do not apply to |msgpack-special-dict|s.
|
||||
|
||||
msgpackparse({list}) *msgpackparse()*
|
||||
Convert a |readfile()|-style list to a list of VimL objects.
|
||||
@ -4639,15 +4645,66 @@ msgpackparse({list}) *msgpackparse()*
|
||||
< This will read |shada-file| to `shada_objects` list.
|
||||
|
||||
Limitations:
|
||||
1. Strings that contain one of EXT format family objects
|
||||
cannot be parsed by msgpackparse().
|
||||
2. It may appear that parsed integers do not fit in |Number|
|
||||
range. Even if your NeoVim installation uses 64-bit
|
||||
Numbers, it may appear that string contain 64-bit unsigned
|
||||
number above INT64_MAX.
|
||||
3. NIL objects are parsed as zeroes. BOOL objects are parsed
|
||||
as either 1 (true) or 0 (false).
|
||||
4. BIN and STR strings cannot be distinguished after parsing.
|
||||
1. Mapping ordering is not preserved unless messagepack
|
||||
mapping is dumped using generic mapping
|
||||
(|msgpack-special-map|).
|
||||
2. Since the parser aims to preserve all data untouched
|
||||
(except for 1.) some strings are parsed to
|
||||
|msgpack-special-dict| format which is not convenient to
|
||||
use.
|
||||
*msgpack-special-dict*
|
||||
Some messagepack strings may be parsed to special
|
||||
dictionaries. Special dictionaries are dictionaries which
|
||||
|
||||
1. Contain exactly two keys: `_TYPE` and `_VALUE`.
|
||||
2. `_TYPE` key is one of the types found in |v:msgpack_types|
|
||||
variable.
|
||||
3. Value for `_VALUE` has the following format (Key column
|
||||
contains name of the key from |v:msgpack_types|):
|
||||
|
||||
Key Value ~
|
||||
nil Zero, ignored when dumping.
|
||||
boolean One or zero. When dumping it is only checked that
|
||||
value is a |Number|.
|
||||
integer |List| with four numbers: sign (-1 or 1), highest two
|
||||
bits, number with bits from 62nd to 31st, lowest 31
|
||||
bits. I.e. to get actual number one will need to use
|
||||
code like >
|
||||
_VALUE[0] * ((_VALUE[1] << 62)
|
||||
& (_VALUE[2] << 31)
|
||||
& _VALUE[3])
|
||||
< Special dictionary with this type will appear in
|
||||
|msgpackparse()| output under one of the following
|
||||
circumstances:
|
||||
1. |Number| is 32-bit and value is either above
|
||||
INT32_MAX or below INT32_MIN.
|
||||
2. |Number| is 64-bit and value is above INT64_MAX. It
|
||||
cannot possibly be below INT64_MIN because msgpack
|
||||
C parser does not support such values.
|
||||
float |Float|. This value cannot possibly appear in
|
||||
|msgpackparse()| output.
|
||||
string |readfile()|-style list of strings. This value will
|
||||
appear in |msgpackparse()| output if string contains
|
||||
zero byte or if string is a mapping key and mapping is
|
||||
being represented as special dictionary for other
|
||||
reasons.
|
||||
binary |readfile()|-style list of strings. This value will
|
||||
appear in |msgpackparse()| output if binary string
|
||||
contains zero byte.
|
||||
array |List|. This value cannot appear in |msgpackparse()|
|
||||
output.
|
||||
*msgpack-special-map*
|
||||
map |List| of |List|s with two items (key and value) each.
|
||||
This value will appear in |msgpackparse()| output if
|
||||
parsed mapping contains one of the following keys:
|
||||
1. Any key that is not a string (including keys which
|
||||
are binary strings).
|
||||
2. String with NUL byte inside.
|
||||
3. Duplicate key.
|
||||
4. Empty key.
|
||||
ext |List| with two values: first is a signed integer
|
||||
representing extension type. Second is
|
||||
|readfile()|-style list of strings.
|
||||
|
||||
nextnonblank({lnum}) *nextnonblank()*
|
||||
Return the line number of the first line at or below {lnum}
|
||||
|
736
src/nvim/eval.c
736
src/nvim/eval.c
@ -97,6 +97,7 @@
|
||||
#include "nvim/os/dl.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/lib/kvec.h"
|
||||
|
||||
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
|
||||
|
||||
@ -439,6 +440,7 @@ static struct vimvar {
|
||||
{VV_NAME("progpath", VAR_STRING), VV_RO},
|
||||
{VV_NAME("command_output", VAR_STRING), 0},
|
||||
{VV_NAME("completed_item", VAR_DICT), VV_RO},
|
||||
{VV_NAME("msgpack_types", VAR_DICT), VV_RO},
|
||||
};
|
||||
|
||||
/* shorthand */
|
||||
@ -469,6 +471,29 @@ typedef struct {
|
||||
uint64_t id;
|
||||
} TerminalJobData;
|
||||
|
||||
/// Structure representing current VimL to messagepack conversion state
|
||||
typedef struct {
|
||||
enum {
|
||||
kMPConvDict, ///< Convert dict_T *dictionary.
|
||||
kMPConvList, ///< Convert list_T *list.
|
||||
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
const dict_T *dict; ///< Currently converted dictionary.
|
||||
const hashitem_T *hi; ///< Currently converted dictionary item.
|
||||
size_t todo; ///< Amount of items left to process.
|
||||
} d; ///< State of dictionary conversion.
|
||||
struct {
|
||||
const list_T *list; ///< Currently converted list.
|
||||
const listitem_T *li; ///< Currently converted list item.
|
||||
} l; ///< State of list or generic mapping conversion.
|
||||
} data; ///< Data to convert.
|
||||
} MPConvStackVal;
|
||||
|
||||
/// Stack used to convert VimL values to messagepack.
|
||||
typedef kvec_t(MPConvStackVal) MPConvStack;
|
||||
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "eval.c.generated.h"
|
||||
@ -489,6 +514,40 @@ static int disable_job_defer = 0;
|
||||
static uint64_t current_job_id = 1;
|
||||
static PMap(uint64_t) *jobs = NULL;
|
||||
|
||||
typedef enum {
|
||||
kMPNil,
|
||||
kMPBoolean,
|
||||
kMPInteger,
|
||||
kMPFloat,
|
||||
kMPString,
|
||||
kMPBinary,
|
||||
kMPArray,
|
||||
kMPMap,
|
||||
kMPExt,
|
||||
} MessagePackType;
|
||||
static const char *const msgpack_type_names[] = {
|
||||
[kMPNil] = "nil",
|
||||
[kMPBoolean] = "boolean",
|
||||
[kMPInteger] = "integer",
|
||||
[kMPFloat] = "float",
|
||||
[kMPString] = "string",
|
||||
[kMPBinary] = "binary",
|
||||
[kMPArray] = "array",
|
||||
[kMPMap] = "map",
|
||||
[kMPExt] = "ext",
|
||||
};
|
||||
static const list_T *msgpack_type_lists[] = {
|
||||
[kMPNil] = NULL,
|
||||
[kMPBoolean] = NULL,
|
||||
[kMPInteger] = NULL,
|
||||
[kMPFloat] = NULL,
|
||||
[kMPString] = NULL,
|
||||
[kMPBinary] = NULL,
|
||||
[kMPArray] = NULL,
|
||||
[kMPMap] = NULL,
|
||||
[kMPExt] = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the global and v: variables.
|
||||
*/
|
||||
@ -521,6 +580,26 @@ void eval_init(void)
|
||||
/* add to compat scope dict */
|
||||
hash_add(&compat_hashtab, p->vv_di.di_key);
|
||||
}
|
||||
|
||||
dict_T *const msgpack_types_dict = dict_alloc();
|
||||
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
|
||||
list_T *const type_list = list_alloc();
|
||||
type_list->lv_lock = VAR_FIXED;
|
||||
dictitem_T *const di = dictitem_alloc((char_u *) msgpack_type_names[i]);
|
||||
di->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||
di->di_tv = (typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.vval = { .v_list = type_list, },
|
||||
};
|
||||
msgpack_type_lists[i] = type_list;
|
||||
if (dict_add(msgpack_types_dict, di) == FAIL) {
|
||||
// There must not be duplicate items in this dictionary by definition.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
msgpack_types_dict->dv_lock = VAR_FIXED;
|
||||
|
||||
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
|
||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
|
||||
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
|
||||
set_vim_var_nr(VV_HLSEARCH, 1L);
|
||||
@ -11921,12 +12000,389 @@ static int msgpack_list_write(void *data, const char *buf, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Convert readfile()-style list to a char * buffer with length
|
||||
///
|
||||
/// @param[in] list Converted list.
|
||||
/// @param[out] ret_len Resulting buffer length.
|
||||
/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
|
||||
/// zero.
|
||||
///
|
||||
/// @return true in case of success, false in case of failure.
|
||||
static inline bool vim_list_to_buf(const list_T *const list,
|
||||
size_t *const ret_len, char **const ret_buf)
|
||||
FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
size_t len = 0;
|
||||
if (list != NULL) {
|
||||
for (const listitem_T *li = list->lv_first;
|
||||
li != NULL;
|
||||
li = li->li_next) {
|
||||
if (li->li_tv.v_type != VAR_STRING) {
|
||||
return false;
|
||||
}
|
||||
len++;
|
||||
if (li->li_tv.vval.v_string != 0) {
|
||||
len += STRLEN(li->li_tv.vval.v_string);
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
*ret_len = len;
|
||||
if (len == 0) {
|
||||
*ret_buf = NULL;
|
||||
return true;
|
||||
}
|
||||
ListReaderState lrstate = init_lrstate(list);
|
||||
char *const buf = xmalloc(len);
|
||||
size_t read_bytes;
|
||||
if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
|
||||
assert(false);
|
||||
}
|
||||
assert(len == read_bytes);
|
||||
*ret_buf = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Convert one VimL value to msgpack
|
||||
///
|
||||
/// @param packer Messagepack packer.
|
||||
/// @param[out] mpstack Stack with values to convert. Only used for pushing
|
||||
/// values to it.
|
||||
/// @param[in] tv Converted value.
|
||||
///
|
||||
/// @return OK in case of success, FAIL otherwise.
|
||||
static int convert_one_value(msgpack_packer *const packer,
|
||||
MPConvStack *const mpstack,
|
||||
const typval_T *const tv)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
switch (tv->v_type) {
|
||||
#define CHECK_SELF_REFERENCE(conv_type, vval_name, ptr) \
|
||||
do { \
|
||||
for (size_t i = 0; i < kv_size(*mpstack); i++) { \
|
||||
if (kv_A(*mpstack, i).type == conv_type \
|
||||
&& kv_A(*mpstack, i).data.vval_name == ptr) { \
|
||||
EMSG2(_(e_invarg2), "container references itself"); \
|
||||
return FAIL; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
case VAR_STRING: {
|
||||
if (tv->vval.v_string == NULL) {
|
||||
msgpack_pack_bin(packer, 0);
|
||||
} else {
|
||||
const size_t len = STRLEN(tv->vval.v_string);
|
||||
msgpack_pack_bin(packer, len);
|
||||
msgpack_pack_bin_body(packer, tv->vval.v_string, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VAR_NUMBER: {
|
||||
msgpack_pack_int64(packer, (int64_t) tv->vval.v_number);
|
||||
break;
|
||||
}
|
||||
case VAR_FLOAT: {
|
||||
msgpack_pack_double(packer, (double) tv->vval.v_float);
|
||||
break;
|
||||
}
|
||||
case VAR_FUNC: {
|
||||
EMSG2(_(e_invarg2), "attempt to dump function reference");
|
||||
return FAIL;
|
||||
}
|
||||
case VAR_LIST: {
|
||||
if (tv->vval.v_list == NULL) {
|
||||
msgpack_pack_array(packer, 0);
|
||||
break;
|
||||
}
|
||||
CHECK_SELF_REFERENCE(kMPConvList, l.list, tv->vval.v_list);
|
||||
msgpack_pack_array(packer, tv->vval.v_list->lv_len);
|
||||
kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvList,
|
||||
.data = {
|
||||
.l = {
|
||||
.list = tv->vval.v_list,
|
||||
.li = tv->vval.v_list->lv_first,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case VAR_DICT: {
|
||||
if (tv->vval.v_dict == NULL) {
|
||||
msgpack_pack_map(packer, 0);
|
||||
break;
|
||||
}
|
||||
const dictitem_T *type_di;
|
||||
const dictitem_T *val_di;
|
||||
if (tv->vval.v_dict->dv_hashtab.ht_used == 2
|
||||
&& (type_di = dict_find((dict_T *) tv->vval.v_dict,
|
||||
(char_u *) "_TYPE", -1)) != NULL
|
||||
&& type_di->di_tv.v_type == VAR_LIST
|
||||
&& (val_di = dict_find((dict_T *) tv->vval.v_dict,
|
||||
(char_u *) "_VAL", -1)) != NULL) {
|
||||
size_t i;
|
||||
for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) {
|
||||
if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(msgpack_type_lists)) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
switch ((MessagePackType) i) {
|
||||
case kMPNil: {
|
||||
msgpack_pack_nil(packer);
|
||||
break;
|
||||
}
|
||||
case kMPBoolean: {
|
||||
if (val_di->di_tv.v_type != VAR_NUMBER) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
if (val_di->di_tv.vval.v_number) {
|
||||
msgpack_pack_true(packer);
|
||||
} else {
|
||||
msgpack_pack_false(packer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kMPInteger: {
|
||||
const list_T *val_list;
|
||||
varnumber_T sign;
|
||||
varnumber_T highest_bits;
|
||||
varnumber_T high_bits;
|
||||
varnumber_T low_bits;
|
||||
// List of 4 integers; first is signed (should be 1 or -1, but this
|
||||
// is not checked), second is unsigned and have at most one (sign is
|
||||
// -1) or two (sign is 1) non-zero bits (number of bits is not
|
||||
// checked), other unsigned and have at most 31 non-zero bits
|
||||
// (number of bits is not checked).
|
||||
if (val_di->di_tv.v_type != VAR_LIST
|
||||
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|
||||
|| val_list->lv_len != 4
|
||||
|| val_list->lv_first->li_tv.v_type != VAR_NUMBER
|
||||
|| (sign = val_list->lv_first->li_tv.vval.v_number) == 0
|
||||
|| val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
|
||||
|| (highest_bits =
|
||||
val_list->lv_first->li_next->li_tv.vval.v_number) < 0
|
||||
|| val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
|
||||
|| (high_bits =
|
||||
val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
|
||||
|| val_list->lv_last->li_tv.v_type != VAR_NUMBER
|
||||
|| (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62)
|
||||
| (uint64_t) (((uint64_t) high_bits) << 31)
|
||||
| (uint64_t) low_bits);
|
||||
if (sign > 0) {
|
||||
msgpack_pack_uint64(packer, number);
|
||||
} else {
|
||||
msgpack_pack_int64(packer, (int64_t) (-number));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kMPFloat: {
|
||||
if (val_di->di_tv.v_type != VAR_FLOAT) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
msgpack_pack_double(packer, val_di->di_tv.vval.v_float);
|
||||
break;
|
||||
}
|
||||
case kMPString:
|
||||
case kMPBinary: {
|
||||
const bool is_string = ((MessagePackType) i == kMPString);
|
||||
if (val_di->di_tv.v_type != VAR_LIST) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
size_t len;
|
||||
char *buf;
|
||||
if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
if (is_string) {
|
||||
msgpack_pack_str(packer, len);
|
||||
} else {
|
||||
msgpack_pack_bin(packer, len);
|
||||
}
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
if (is_string) {
|
||||
msgpack_pack_str_body(packer, buf, len);
|
||||
} else {
|
||||
msgpack_pack_bin_body(packer, buf, len);
|
||||
}
|
||||
xfree(buf);
|
||||
break;
|
||||
}
|
||||
case kMPArray: {
|
||||
if (val_di->di_tv.v_type != VAR_LIST) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
CHECK_SELF_REFERENCE(kMPConvList, l.list,
|
||||
val_di->di_tv.vval.v_list);
|
||||
msgpack_pack_array(packer, val_di->di_tv.vval.v_list->lv_len);
|
||||
kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvList,
|
||||
.data = {
|
||||
.l = {
|
||||
.list = val_di->di_tv.vval.v_list,
|
||||
.li = val_di->di_tv.vval.v_list->lv_first,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case kMPMap: {
|
||||
if (val_di->di_tv.v_type != VAR_LIST) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
if (val_di->di_tv.vval.v_list == NULL) {
|
||||
msgpack_pack_map(packer, 0);
|
||||
break;
|
||||
}
|
||||
const list_T *const val_list = val_di->di_tv.vval.v_list;
|
||||
for (const listitem_T *li = val_list->lv_first; li != NULL;
|
||||
li = li->li_next) {
|
||||
if (li->li_tv.v_type != VAR_LIST
|
||||
|| li->li_tv.vval.v_list->lv_len != 2) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
}
|
||||
CHECK_SELF_REFERENCE(kMPConvPairs, l.list, val_list);
|
||||
msgpack_pack_map(packer, val_list->lv_len);
|
||||
kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvPairs,
|
||||
.data = {
|
||||
.l = {
|
||||
.list = val_list,
|
||||
.li = val_list->lv_first,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case kMPExt: {
|
||||
const list_T *val_list;
|
||||
varnumber_T type;
|
||||
if (val_di->di_tv.v_type != VAR_LIST
|
||||
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|
||||
|| val_list->lv_len != 2
|
||||
|| (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
|
||||
|| (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
|
||||
|| type < INT8_MIN
|
||||
|| (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
size_t len;
|
||||
char *buf;
|
||||
if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
|
||||
&len, &buf)) {
|
||||
goto vim_to_msgpack_regural_dict;
|
||||
}
|
||||
msgpack_pack_ext(packer, len, (int8_t) type);
|
||||
msgpack_pack_ext_body(packer, buf, len);
|
||||
xfree(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
vim_to_msgpack_regural_dict:
|
||||
CHECK_SELF_REFERENCE(kMPConvDict, d.dict, tv->vval.v_dict);
|
||||
msgpack_pack_map(packer, tv->vval.v_dict->dv_hashtab.ht_used);
|
||||
kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvDict,
|
||||
.data = {
|
||||
.d = {
|
||||
.dict = tv->vval.v_dict,
|
||||
.hi = tv->vval.v_dict->dv_hashtab.ht_array,
|
||||
.todo = tv->vval.v_dict->dv_hashtab.ht_used,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef CHECK_SELF_REFERENCE
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Convert typval_T to messagepack
|
||||
static int vim_to_msgpack(msgpack_packer *const packer,
|
||||
const typval_T *const tv)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
MPConvStack mpstack;
|
||||
kv_init(mpstack);
|
||||
if (convert_one_value(packer, &mpstack, tv) == FAIL) {
|
||||
goto vim_to_msgpack_error_ret;
|
||||
}
|
||||
while (kv_size(mpstack)) {
|
||||
MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1);
|
||||
const typval_T *cur_tv;
|
||||
switch (cur_mpsv->type) {
|
||||
case kMPConvDict: {
|
||||
if (!cur_mpsv->data.d.todo) {
|
||||
(void) kv_pop(mpstack);
|
||||
continue;
|
||||
}
|
||||
while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
|
||||
cur_mpsv->data.d.hi++;
|
||||
}
|
||||
const dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
|
||||
cur_mpsv->data.d.todo--;
|
||||
cur_mpsv->data.d.hi++;
|
||||
const size_t key_len = STRLEN(&di->di_key[0]);
|
||||
msgpack_pack_str(packer, key_len);
|
||||
msgpack_pack_str_body(packer, &di->di_key[0], key_len);
|
||||
cur_tv = &di->di_tv;
|
||||
break;
|
||||
}
|
||||
case kMPConvList: {
|
||||
if (cur_mpsv->data.l.li == NULL) {
|
||||
(void) kv_pop(mpstack);
|
||||
continue;
|
||||
}
|
||||
cur_tv = &cur_mpsv->data.l.li->li_tv;
|
||||
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
|
||||
break;
|
||||
}
|
||||
case kMPConvPairs: {
|
||||
if (cur_mpsv->data.l.li == NULL) {
|
||||
(void) kv_pop(mpstack);
|
||||
continue;
|
||||
}
|
||||
const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
|
||||
if (convert_one_value(packer, &mpstack, &kv_pair->lv_first->li_tv)
|
||||
== FAIL) {
|
||||
goto vim_to_msgpack_error_ret;
|
||||
}
|
||||
cur_tv = &kv_pair->lv_last->li_tv;
|
||||
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (convert_one_value(packer, &mpstack, cur_tv) == FAIL) {
|
||||
goto vim_to_msgpack_error_ret;
|
||||
}
|
||||
}
|
||||
kv_destroy(mpstack);
|
||||
return OK;
|
||||
vim_to_msgpack_error_ret:
|
||||
kv_destroy(mpstack);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/// "msgpackdump()" function
|
||||
static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (argvars[0].v_type != VAR_LIST) {
|
||||
EMSG2(_(e_listarg), "msgpackdump()");
|
||||
return;
|
||||
}
|
||||
list_T *ret_list = rettv_list_alloc(rettv);
|
||||
const list_T *list = argvars[0].vval.v_list;
|
||||
@ -11935,9 +12391,9 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write);
|
||||
for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
|
||||
Object obj = vim_to_object((typval_T *) &li->li_tv);
|
||||
msgpack_rpc_from_object(obj, lpacker);
|
||||
api_free_object(obj);
|
||||
if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
msgpack_packer_free(lpacker);
|
||||
}
|
||||
@ -11983,7 +12439,257 @@ static int read_from_list(ListReaderState *const state, char *const buf,
|
||||
}
|
||||
}
|
||||
*read_bytes = nbuf;
|
||||
return NOTDONE;
|
||||
return (state->offset < state->li_length || state->li->li_next != NULL
|
||||
? NOTDONE
|
||||
: OK);
|
||||
}
|
||||
|
||||
/// Initialize ListReaderState structure
|
||||
static inline ListReaderState init_lrstate(const list_T *const list)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
return (ListReaderState) {
|
||||
.li = list->lv_first,
|
||||
.offset = 0,
|
||||
.li_length = (list->lv_first->li_tv.vval.v_string == NULL
|
||||
? 0
|
||||
: STRLEN(list->lv_first->li_tv.vval.v_string)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert msgpack object to a VimL one
|
||||
static int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
#define INIT_SPECIAL_DICT(tv, type, val) \
|
||||
do { \
|
||||
dict_T *const dict = dict_alloc(); \
|
||||
dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \
|
||||
type_di->di_tv.v_type = VAR_LIST; \
|
||||
type_di->di_tv.v_lock = 0; \
|
||||
type_di->di_tv.vval.v_list = (list_T *) 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"); \
|
||||
val_di->di_tv = val; \
|
||||
dict_add(dict, val_di); \
|
||||
tv->v_type = VAR_DICT; \
|
||||
dict->dv_refcount++; \
|
||||
tv->vval.v_dict = dict; \
|
||||
} while (0)
|
||||
switch (mobj.type) {
|
||||
case MSGPACK_OBJECT_NIL: {
|
||||
INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) {
|
||||
.v_type = VAR_NUMBER,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_number = 0 },
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_BOOLEAN: {
|
||||
INIT_SPECIAL_DICT(rettv, kMPBoolean,
|
||||
((typval_T) {
|
||||
.v_type = VAR_NUMBER,
|
||||
.v_lock = 0,
|
||||
.vval = {
|
||||
.v_number = (varnumber_T) mobj.via.boolean,
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_POSITIVE_INTEGER: {
|
||||
if (mobj.via.u64 <= VARNUMBER_MAX) {
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_NUMBER,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_number = (varnumber_T) mobj.via.u64 },
|
||||
};
|
||||
} else {
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
INIT_SPECIAL_DICT(rettv, kMPInteger,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
|
||||
if (mobj.via.i64 >= VARNUMBER_MIN) {
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_NUMBER,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_number = (varnumber_T) mobj.via.i64 },
|
||||
};
|
||||
} else {
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
INIT_SPECIAL_DICT(rettv, kMPInteger,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_FLOAT: {
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_FLOAT,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_float = mobj.via.f64 },
|
||||
};
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_STR: {
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
INIT_SPECIAL_DICT(rettv, kMPString,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
|
||||
== -1) {
|
||||
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 = 0,
|
||||
.vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
|
||||
};
|
||||
break;
|
||||
}
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
INIT_SPECIAL_DICT(rettv, kMPBinary,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
|
||||
== -1) {
|
||||
return FAIL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_ARRAY: {
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_list = list },
|
||||
};
|
||||
for (size_t i = 0; i < mobj.via.array.size; i++) {
|
||||
listitem_T *const li = listitem_alloc();
|
||||
li->li_tv.v_type = VAR_UNKNOWN;
|
||||
list_append(list, li);
|
||||
if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_MAP: {
|
||||
for (size_t i = 0; i < mobj.via.map.size; i++) {
|
||||
if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
|
||||
|| mobj.via.map.ptr[i].key.via.str.size == 0
|
||||
|| memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
|
||||
mobj.via.map.ptr[i].key.via.str.size) != NULL) {
|
||||
goto msgpack_to_vim_generic_map;
|
||||
}
|
||||
}
|
||||
dict_T *const dict = dict_alloc();
|
||||
dict->dv_refcount++;
|
||||
*rettv = (typval_T) {
|
||||
.v_type = VAR_DICT,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_dict = dict },
|
||||
};
|
||||
for (size_t i = 0; i < mobj.via.map.size; i++) {
|
||||
dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
|
||||
+ mobj.via.map.ptr[i].key.via.str.size);
|
||||
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) {
|
||||
// Duplicate key: fallback to generic map
|
||||
clear_tv(rettv);
|
||||
xfree(di);
|
||||
goto msgpack_to_vim_generic_map;
|
||||
}
|
||||
if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
msgpack_to_vim_generic_map: {}
|
||||
list_T *const list = list_alloc();
|
||||
list->lv_refcount++;
|
||||
INIT_SPECIAL_DICT(rettv, kMPMap,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
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();
|
||||
key_li->li_tv.v_type = VAR_UNKNOWN;
|
||||
list_append(kv_pair, key_li);
|
||||
listitem_T *const val_li = listitem_alloc();
|
||||
val_li->li_tv.v_type = VAR_UNKNOWN;
|
||||
list_append(kv_pair, val_li);
|
||||
if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSGPACK_OBJECT_EXT: {
|
||||
list_T *const list = 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);
|
||||
INIT_SPECIAL_DICT(rettv, kMPExt,
|
||||
((typval_T) {
|
||||
.v_type = VAR_LIST,
|
||||
.v_lock = 0,
|
||||
.vval = { .v_list = list },
|
||||
}));
|
||||
if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr,
|
||||
mobj.via.ext.size) == -1) {
|
||||
return FAIL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef INIT_SPECIAL_DICT
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// "msgpackparse" function
|
||||
@ -12002,13 +12708,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
|
||||
EMSG2(_(e_invarg2), "List item is not a string");
|
||||
return;
|
||||
}
|
||||
ListReaderState lrstate = {
|
||||
.li = list->lv_first,
|
||||
.offset = 0,
|
||||
.li_length = (list->lv_first->li_tv.vval.v_string == NULL
|
||||
? 0
|
||||
: STRLEN(list->lv_first->li_tv.vval.v_string)),
|
||||
};
|
||||
ListReaderState lrstate = init_lrstate(list);
|
||||
msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE);
|
||||
if (unpacker == NULL) {
|
||||
EMSG(_(e_outofmem));
|
||||
@ -12044,19 +12744,13 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
|
||||
goto f_msgpackparse_exit;
|
||||
}
|
||||
if (result == MSGPACK_UNPACK_SUCCESS) {
|
||||
Object obj;
|
||||
if (!msgpack_rpc_to_object(&unpacked.data, &obj)) {
|
||||
EMSG2(_(e_invarg2), "Failed to convert parsed string to Object");
|
||||
goto f_msgpackparse_exit;
|
||||
}
|
||||
listitem_T *li = listitem_alloc();
|
||||
Error err;
|
||||
if (!object_to_vim(obj, &li->li_tv, &err)) {
|
||||
EMSG2(_(e_invarg2), err.msg);
|
||||
li->li_tv.v_type = VAR_UNKNOWN;
|
||||
list_append(ret_list, li);
|
||||
if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) {
|
||||
EMSG2(_(e_invarg2), "Failed to convert msgpack string");
|
||||
goto f_msgpackparse_exit;
|
||||
}
|
||||
list_append(ret_list, li);
|
||||
api_free_object(obj);
|
||||
}
|
||||
if (result == MSGPACK_UNPACK_CONTINUE) {
|
||||
if (rlret == OK) {
|
||||
|
@ -65,6 +65,7 @@ enum {
|
||||
VV_PROGPATH,
|
||||
VV_COMMAND_OUTPUT,
|
||||
VV_COMPLETED_ITEM,
|
||||
VV_MSGPACK_TYPES,
|
||||
VV_LEN, /* number of v: vars */
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
#ifndef NVIM_EVAL_DEFS_H
|
||||
#define NVIM_EVAL_DEFS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "nvim/hashtab.h"
|
||||
|
||||
typedef int varnumber_T;
|
||||
typedef double float_T;
|
||||
|
||||
#define VARNUMBER_MAX INT_MAX
|
||||
#define VARNUMBER_MIN INT_MIN
|
||||
|
||||
typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
|
||||
|
@ -325,20 +325,27 @@ describe('msgpack*() functions', function()
|
||||
}
|
||||
obj_test('are able to dump and restore rather big object', big_obj)
|
||||
|
||||
it('dump funcref as nil and restore as zero', function()
|
||||
execute('let dumped = msgpackdump([function("tr")])')
|
||||
eq({"\192"}, eval('dumped'))
|
||||
eq({0}, eval('msgpackparse(dumped)'))
|
||||
obj_test('are able to dump and restore floating-point value', {0.125})
|
||||
|
||||
it('restore nil as special dict', function()
|
||||
execute('let dumped = ["\\xC0"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL=0}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.nil'))
|
||||
end)
|
||||
|
||||
it('restore boolean false as zero', function()
|
||||
execute('let dumped = ["\\xC2"]')
|
||||
eq({0}, eval('msgpackparse(dumped)'))
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL=0}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean'))
|
||||
end)
|
||||
|
||||
it('restore boolean true as one', function()
|
||||
execute('let dumped = ["\\xC3"]')
|
||||
eq({1}, eval('msgpackparse(dumped)'))
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL=1}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.boolean'))
|
||||
end)
|
||||
|
||||
it('dump string as BIN 8', function()
|
||||
@ -346,13 +353,254 @@ describe('msgpack*() functions', function()
|
||||
eq({"\196\004Test"}, eval('msgpackdump(obj)'))
|
||||
end)
|
||||
|
||||
it('restore FIXSTR as string', function()
|
||||
it('restore FIXSTR as special dict', function()
|
||||
execute('let dumped = ["\\xa2ab"]')
|
||||
eq({'ab'}, eval('msgpackparse(dumped)'))
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string'))
|
||||
end)
|
||||
|
||||
it('restore BIN 8 as string', function()
|
||||
execute('let dumped = ["\\xC4\\x02ab"]')
|
||||
eq({'ab'}, eval('msgpackparse(dumped)'))
|
||||
end)
|
||||
|
||||
it('restore FIXEXT1 as special dictionary', function()
|
||||
execute('let dumped = ["\\xD4\\x10", ""]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext'))
|
||||
end)
|
||||
|
||||
it('restore MAP with BIN key as special dictionary', function()
|
||||
execute('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
||||
end)
|
||||
|
||||
it('restore MAP with duplicate STR keys as special dictionary', function()
|
||||
execute('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'a'}}, ''},
|
||||
{{_TYPE={}, _VAL={'a'}}, ''}}}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
||||
eq(1, eval('g:parsed[0]._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
||||
eq(1, eval('g:parsed[0]._VAL[1][0]._TYPE is v:msgpack_types.string'))
|
||||
end)
|
||||
|
||||
it('restore MAP with MAP key as special dictionary', function()
|
||||
execute('let dumped = ["\\x81\\x80\\xC4\\n"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed'))
|
||||
eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map'))
|
||||
end)
|
||||
|
||||
it('can restore and dump UINT64_MAX', function()
|
||||
execute('let dumped = ["\\xCF" . repeat("\\xFF", 8)]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
execute('let dumped2 = msgpackdump(parsed)')
|
||||
eq(1, eval('type(parsed[0]) == type(0) ' ..
|
||||
'|| parsed[0]._TYPE is v:msgpack_types.integer'))
|
||||
if eval('type(parsed[0]) == type(0)') == 1 then
|
||||
eq(1, eval('0xFFFFFFFFFFFFFFFF == parsed[0]'))
|
||||
else
|
||||
eq({_TYPE={}, _VAL={1, 3, 0x7FFFFFFF, 0x7FFFFFFF}}, eval('parsed[0]'))
|
||||
end
|
||||
eq(1, eval('dumped ==# dumped2'))
|
||||
end)
|
||||
|
||||
it('can restore and dump INT64_MIN', function()
|
||||
execute('let dumped = ["\\xD3\\x80" . repeat("\\n", 7)]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
execute('let dumped2 = msgpackdump(parsed)')
|
||||
eq(1, eval('type(parsed[0]) == type(0) ' ..
|
||||
'|| parsed[0]._TYPE is v:msgpack_types.integer'))
|
||||
if eval('type(parsed[0]) == type(0)') == 1 then
|
||||
eq(1, eval('-0x8000000000000000 == parsed[0]'))
|
||||
else
|
||||
eq({_TYPE={}, _VAL={-1, 2, 0, 0}}, eval('parsed[0]'))
|
||||
end
|
||||
eq(1, eval('dumped ==# dumped2'))
|
||||
end)
|
||||
|
||||
it('can restore and dump BIN string with zero byte', function()
|
||||
execute('let dumped = ["\\xC4\\x01\\n"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
execute('let dumped2 = msgpackdump(parsed)')
|
||||
eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed'))
|
||||
eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary'))
|
||||
eq(1, eval('dumped ==# dumped2'))
|
||||
end)
|
||||
|
||||
it('can restore and dump STR string with zero byte', function()
|
||||
execute('let dumped = ["\\xA1\\n"]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
execute('let dumped2 = msgpackdump(parsed)')
|
||||
eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed'))
|
||||
eq(1, eval('parsed[0]._TYPE is v:msgpack_types.string'))
|
||||
eq(1, eval('dumped ==# dumped2'))
|
||||
end)
|
||||
|
||||
it('can restore and dump BIN string with NL', function()
|
||||
execute('let dumped = ["\\xC4\\x01", ""]')
|
||||
execute('let parsed = msgpackparse(dumped)')
|
||||
execute('let dumped2 = msgpackdump(parsed)')
|
||||
eq({"\n"}, eval('parsed'))
|
||||
eq(1, eval('dumped ==# dumped2'))
|
||||
end)
|
||||
|
||||
it('can dump generic mapping with generic mapping keys and values', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
||||
execute('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
||||
execute('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
|
||||
execute('call add(todump._VAL, [todumpv1, todumpv2])')
|
||||
eq({'\129\128\128'}, eval('msgpackdump([todump])'))
|
||||
end)
|
||||
|
||||
it('can dump generic mapping with ext', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
|
||||
eq({'\212\005', ''}, eval('msgpackdump([todump])'))
|
||||
end)
|
||||
|
||||
it('can dump generic mapping with array', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}')
|
||||
eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])'))
|
||||
end)
|
||||
|
||||
it('can dump generic mapping with UINT64_MAX', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
|
||||
execute('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]')
|
||||
eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])'))
|
||||
end)
|
||||
|
||||
it('can dump generic mapping with INT64_MIN', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.integer}')
|
||||
execute('let todump._VAL = [-1, 2, 0, 0]')
|
||||
eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])'))
|
||||
end)
|
||||
|
||||
it('dump and restore generic mapping with floating-point value', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}')
|
||||
eq({0.125}, eval('msgpackparse(msgpackdump([todump]))'))
|
||||
end)
|
||||
|
||||
it('fails to dump a function reference', function()
|
||||
execute('let Todump = function("tr")')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([Todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: attempt to dump function reference',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a function reference in a list', function()
|
||||
execute('let todump = [function("tr")]')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: attempt to dump function reference',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive list', function()
|
||||
execute('let todump = [[[]]]')
|
||||
execute('call add(todump[0][0], todump)')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive dict', function()
|
||||
execute('let todump = {"d": {"d": {}}}')
|
||||
execute('call extend(todump.d.d, {"d": todump})')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive list in a special dict', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
|
||||
execute('call add(todump._VAL, todump)')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive (key) map in a special dict', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
|
||||
execute('call add(todump._VAL, [todump, 0])')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive (val) map in a special dict', function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
|
||||
execute('call add(todump._VAL, [0, todump])')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
|
||||
it('fails to dump a recursive (val) special list in a special dict',
|
||||
function()
|
||||
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
|
||||
execute('call add(todump._VAL, [0, todump._VAL])')
|
||||
execute([[
|
||||
try
|
||||
let dumped = msgpackdump([todump])
|
||||
let exception = 0
|
||||
catch
|
||||
let exception = v:exception
|
||||
endtry
|
||||
]])
|
||||
eq('Vim(let):E475: Invalid argument: container references itself',
|
||||
eval('exception'))
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user