/// @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 "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/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",
[kSpecialVarNone] = "none",
[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;
if (list->lv_last == NULL) {
list_append_string(list, NULL, 0);
}
listitem_T *li = list->lv_last;
do {
const char *line_start = line_end;
line_end = xmemscan(line_start, NL, (size_t) (end - line_start));
if (line_end == line_start) {
list_append_allocated_string(list, NULL);
} else {
const size_t line_length = (size_t) (line_end - line_start);
char *str;
if (li == NULL) {
str = xmemdupz(line_start, line_length);
} else {
const size_t li_len = (li->li_tv.vval.v_string == NULL
? 0
: STRLEN(li->li_tv.vval.v_string));
li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string,
li_len + line_length + 1);
str = (char *) li->li_tv.vval.v_string + li_len;
memmove(str, line_start, line_length);
str[line_length] = 0;
}
for (size_t i = 0; i < line_length; i++) {
if (str[i] == NUL) {
str[i] = NL;
}
}
if (li == NULL) {
list_append_allocated_string(list, str);
} else {
li = NULL;
}
if (line_end == end - 1) {
list_append_allocated_string(list, NULL);
}
}
line_end++;
} while (line_end < end);
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
|