/// @file encode.c
///
/// File containing functions for encoding and decoding VimL values.
///
/// Split out from eval.c.
#include <msgpack.h>
#include <inttypes.h>
#include <assert.h>
#include <math.h>
#include "nvim/eval/encode.h"
#include "nvim/buffer_defs.h" // vimconv_T
#include "nvim/eval.h"
#include "nvim/eval_defs.h"
#include "nvim/garray.h"
#include "nvim/mbyte.h"
#include "nvim/message.h"
#include "nvim/memory.h"
#include "nvim/charset.h" // vim_isprintc()
#include "nvim/macros.h"
#include "nvim/ascii.h"
#include "nvim/vim.h" // For _()
#include "nvim/lib/kvec.h"
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
#define utf_char2len(b) ((size_t)utf_char2len(b))
#define string_convert(a, b, c) \
((char *)string_convert((vimconv_T *)a, (char_u *)b, c))
#define convert_setup(vcp, from, to) \
(convert_setup(vcp, (char_u *)from, (char_u *)to))
/// 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 {
dict_T *dict; ///< Currently converted dictionary.
hashitem_T *hi; ///< Currently converted dictionary item.
size_t todo; ///< Amount of items left to process.
} d; ///< State of dictionary conversion.
struct {
list_T *list; ///< Currently converted list.
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;
const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null",
[kSpecialVarTrue] = "true",
[kSpecialVarFalse] = "false",
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/encode.c.generated.h"
#endif
/// Msgpack callback for writing to readfile()-style list
int encode_list_write(void *data, const char *buf, size_t len)
{
if (len == 0) {
return 0;
}
list_T *const list = (list_T *) data;
const char *const end = buf + len;
const char *line_end = buf;
listitem_T *li = list->lv_last;
// Continue the last list element
if (li != NULL) {
line_end = xmemscan(buf, NL, len);
if (line_end != buf) {
const size_t line_length = (size_t)(line_end - buf);
char *str = (char *)li->li_tv.vval.v_string;
const size_t li_len = (str == NULL ? 0 : strlen(str));
li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1);
str = (char *)li->li_tv.vval.v_string + li_len;
memcpy(str, buf, line_length);
str[line_length] = 0;
memchrsub(str, NUL, NL, line_length);
}
line_end++;
}
while (line_end < end) {
const char *line_start = line_end;
line_end = xmemscan(line_start, NL, (size_t) (end - line_start));
char *str = NULL;
if (line_end != line_start) {
const size_t line_length = (size_t)(line_end - line_start);
str = xmemdupz(line_start, line_length);
memchrsub(str, NUL, NL, line_length);
}
list_append_allocated_string(list, str);
line_end++;
}
if (line_end == end) {
list_append_allocated_string(list, NULL);
}
return 0;
}
/// Abort conversion to string after a recursion error.
static bool did_echo_string_emsg = false;
/// Show a error message when converting to msgpack value
///
/// @param[in] msg Error message to dump. Must contain exactly two %s that
/// will be replaced with what was being dumped: first with
/// something like “F” or “function argument”, second with path
/// to the failed value.
/// @param[in] mpstack Path to the failed value.
/// @param[in] objname Dumped object name.
///
/// @return FAIL.
static int conv_error(const char *const msg, const MPConvStack *const mpstack,
const char *const objname)
FUNC_ATTR_NONNULL_ALL
{
garray_T msg_ga;
ga_init(&msg_ga, (int)sizeof(char), 80);
char *const key_msg = _("key %s");
char *const key_pair_msg = _("key %s at index %i from special map");
char *const idx_msg = _("index %i");
for (size_t i = 0; i < kv_size(*mpstack); i++) {
if (i != 0) {
ga_concat(&msg_ga, ", ");
}
MPConvStackVal v = kv_A(*mpstack, i);
switch (v.type) {
case kMPConvDict: {
typval_T key_tv = {
.v_type = VAR_STRING,
.vval = { .v_string
|