neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

encode.c (34092B)


      1 /// @file encode.c
      2 ///
      3 /// File containing functions for encoding and decoding Vimscript values.
      4 ///
      5 /// Split out from eval.c.
      6 
      7 #include <assert.h>
      8 #include <inttypes.h>
      9 #include <math.h>
     10 #include <stdbool.h>
     11 #include <stddef.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "klib/kvec.h"
     16 #include "nvim/api/private/helpers.h"
     17 #include "nvim/ascii_defs.h"
     18 #include "nvim/eval.h"
     19 #include "nvim/eval/encode.h"
     20 #include "nvim/eval/typval.h"
     21 #include "nvim/eval/typval_encode.h"
     22 #include "nvim/garray.h"
     23 #include "nvim/gettext_defs.h"
     24 #include "nvim/globals.h"
     25 #include "nvim/hashtab.h"
     26 #include "nvim/macros_defs.h"
     27 #include "nvim/math.h"
     28 #include "nvim/mbyte.h"
     29 #include "nvim/memory.h"
     30 #include "nvim/message.h"
     31 #include "nvim/msgpack_rpc/packer.h"
     32 #include "nvim/strings.h"
     33 #include "nvim/types_defs.h"
     34 #include "nvim/vim_defs.h"  // For _()
     35 
     36 const char *const encode_bool_var_names[] = {
     37  [kBoolVarTrue] = "v:true",
     38  [kBoolVarFalse] = "v:false",
     39 };
     40 
     41 const char *const encode_special_var_names[] = {
     42  [kSpecialVarNull] = "v:null",
     43 };
     44 
     45 #include "eval/encode.c.generated.h"
     46 
     47 /// Msgpack callback for writing to a Blob
     48 int encode_blob_write(void *const data, const char *const buf, const size_t len)
     49  FUNC_ATTR_NONNULL_ARG(1)
     50 {
     51  ga_concat_len(&((blob_T *)data)->bv_ga, buf, len);
     52  return (int)len;
     53 }
     54 
     55 /// Msgpack callback for writing to readfile()-style list
     56 void encode_list_write(void *const data, const char *const buf, const size_t len)
     57  FUNC_ATTR_NONNULL_ARG(1)
     58 {
     59  if (len == 0) {
     60    return;
     61  }
     62  list_T *const list = (list_T *)data;
     63  const char *const end = buf + len;
     64  const char *line_end = buf;
     65  listitem_T *li = tv_list_last(list);
     66 
     67  // Continue the last list element
     68  if (li != NULL) {
     69    line_end = xmemscan(buf, NL, len);
     70    if (line_end != buf) {
     71      const size_t line_length = (size_t)(line_end - buf);
     72      char *str = TV_LIST_ITEM_TV(li)->vval.v_string;
     73      const size_t li_len = (str == NULL ? 0 : strlen(str));
     74      TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(str, li_len + line_length + 1);
     75      str = TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
     76      memcpy(str, buf, line_length);
     77      str[line_length] = 0;
     78      memchrsub(str, NUL, NL, line_length);
     79    }
     80    line_end++;
     81  }
     82 
     83  while (line_end < end) {
     84    const char *line_start = line_end;
     85    line_end = xmemscan(line_start, NL, (size_t)(end - line_start));
     86    char *str = NULL;
     87    if (line_end != line_start) {
     88      const size_t line_length = (size_t)(line_end - line_start);
     89      str = xmemdupz(line_start, line_length);
     90      memchrsub(str, NUL, NL, line_length);
     91    }
     92    tv_list_append_allocated_string(list, str);
     93    line_end++;
     94  }
     95  if (line_end == end) {
     96    tv_list_append_allocated_string(list, NULL);
     97  }
     98 }
     99 
    100 /// Abort conversion to string after a recursion error.
    101 static bool did_echo_string_emsg = false;
    102 
    103 /// Show a error message when converting to msgpack value
    104 ///
    105 /// @param[in]  msg  Error message to dump. Must contain exactly two %s that
    106 ///                  will be replaced with what was being dumped: first with
    107 ///                  something like “F” or “function argument”, second with path
    108 ///                  to the failed value.
    109 /// @param[in]  mpstack  Path to the failed value.
    110 /// @param[in]  objname  Dumped object name.
    111 ///
    112 /// @return FAIL.
    113 static int conv_error(const char *const msg, const MPConvStack *const mpstack,
    114                      const char *const objname)
    115  FUNC_ATTR_NONNULL_ALL
    116 {
    117  garray_T msg_ga;
    118  ga_init(&msg_ga, (int)sizeof(char), 80);
    119  const char *const key_msg = _("key %s");
    120  const char *const key_pair_msg = _("key %s at index %i from special map");
    121  const char *const idx_msg = _("index %i");
    122  const char *const partial_arg_msg = _("partial");
    123  const char *const partial_arg_i_msg = _("argument %i");
    124  const char *const partial_self_msg = _("partial self dictionary");
    125  for (size_t i = 0; i < kv_size(*mpstack); i++) {
    126    if (i != 0) {
    127      GA_CONCAT_LITERAL(&msg_ga, ", ");
    128    }
    129    MPConvStackVal v = kv_A(*mpstack, i);
    130    switch (v.type) {
    131    case kMPConvDict: {
    132      typval_T key_tv = {
    133        .v_type = VAR_STRING,
    134        .vval = { .v_string =
    135                    (v.data.d.hi ==
    136                     NULL ? v.data.d.dict->dv_hashtab.ht_array : (v.data.d.hi -
    137                                                                  1))->hi_key },
    138      };
    139      char *const key = encode_tv2string(&key_tv, NULL);
    140      vim_snprintf(IObuff, IOSIZE, key_msg, key);
    141      xfree(key);
    142      ga_concat(&msg_ga, IObuff);
    143      break;
    144    }
    145    case kMPConvPairs:
    146    case kMPConvList: {
    147      const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
    148                       ? 0
    149                       : (v.data.l.li == NULL
    150                          ? tv_list_len(v.data.l.list) - 1
    151                          : tv_list_idx_of_item(v.data.l.list,
    152                                                TV_LIST_ITEM_PREV(v.data.l.list,
    153                                                                  v.data.l.li))));
    154      const listitem_T *const li = (v.data.l.li == NULL
    155                                    ? tv_list_last(v.data.l.list)
    156                                    : TV_LIST_ITEM_PREV(v.data.l.list,
    157                                                        v.data.l.li));
    158      if (v.type == kMPConvList
    159          || li == NULL
    160          || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
    161              && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
    162        vim_snprintf(IObuff, IOSIZE, idx_msg, idx);
    163        ga_concat(&msg_ga, IObuff);
    164      } else {
    165        assert(li != NULL);
    166        listitem_T *const first_item =
    167          tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list);
    168        assert(first_item != NULL);
    169        typval_T key_tv = *TV_LIST_ITEM_TV(first_item);
    170        char *const key = encode_tv2echo(&key_tv, NULL);
    171        vim_snprintf(IObuff, IOSIZE, key_pair_msg, key, idx);
    172        xfree(key);
    173        ga_concat(&msg_ga, IObuff);
    174      }
    175      break;
    176    }
    177    case kMPConvPartial:
    178      switch (v.data.p.stage) {
    179      case kMPConvPartialArgs:
    180        abort();
    181        break;
    182      case kMPConvPartialSelf:
    183        ga_concat(&msg_ga, partial_arg_msg);
    184        break;
    185      case kMPConvPartialEnd:
    186        ga_concat(&msg_ga, partial_self_msg);
    187        break;
    188      }
    189      break;
    190    case kMPConvPartialList: {
    191      const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
    192      vim_snprintf(IObuff, IOSIZE, partial_arg_i_msg, idx);
    193      ga_concat(&msg_ga, IObuff);
    194      break;
    195    }
    196    }
    197  }
    198  semsg(msg, _(objname), (kv_size(*mpstack) == 0
    199                          ? _("itself")
    200                          : (char *)msg_ga.ga_data));
    201  ga_clear(&msg_ga);
    202  return FAIL;
    203 }
    204 
    205 /// Convert readfile()-style list to a char * buffer with length
    206 ///
    207 /// @param[in]  list  Converted list.
    208 /// @param[out]  ret_len  Resulting buffer length.
    209 /// @param[out]  ret_buf  Allocated buffer with the result or NULL if ret_len is
    210 ///                       zero.
    211 ///
    212 /// @return true in case of success, false in case of failure.
    213 bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, char **const ret_buf)
    214  FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    215 {
    216  size_t len = 0;
    217  TV_LIST_ITER_CONST(list, li, {
    218    if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
    219      return false;
    220    }
    221    len++;
    222    if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) {
    223      len += strlen(TV_LIST_ITEM_TV(li)->vval.v_string);
    224    }
    225  });
    226  if (len) {
    227    len--;
    228  }
    229  *ret_len = len;
    230  if (len == 0) {
    231    *ret_buf = NULL;
    232    return true;
    233  }
    234  ListReaderState lrstate = encode_init_lrstate(list);
    235  char *const buf = xmalloc(len);
    236  size_t read_bytes;
    237  if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
    238    abort();
    239  }
    240  assert(len == read_bytes);
    241  *ret_buf = buf;
    242  return true;
    243 }
    244 
    245 /// Read bytes from list
    246 ///
    247 /// @param[in,out]  state  Structure describing position in list from which
    248 ///                        reading should start. Is updated to reflect position
    249 ///                        at which reading ended.
    250 /// @param[out]  buf  Buffer to write to.
    251 /// @param[in]  nbuf  Buffer length.
    252 /// @param[out]  read_bytes  Is set to amount of bytes read.
    253 ///
    254 /// @return OK when reading was finished, FAIL in case of error (i.e. list item
    255 ///         was not a string), NOTDONE if reading was successful, but there are
    256 ///         more bytes to read.
    257 int encode_read_from_list(ListReaderState *const state, char *const buf, const size_t nbuf,
    258                          size_t *const read_bytes)
    259  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    260 {
    261  char *const buf_end = buf + nbuf;
    262  char *p = buf;
    263  while (p < buf_end) {
    264    assert(state->li_length == 0
    265           || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
    266    for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
    267      assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
    268      const char ch = TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++];
    269      *p++ = (char)(ch == (char)NL ? (char)NUL : ch);
    270    }
    271    if (p < buf_end) {
    272      state->li = TV_LIST_ITEM_NEXT(state->list, state->li);
    273      if (state->li == NULL) {
    274        *read_bytes = (size_t)(p - buf);
    275        return OK;
    276      }
    277      *p++ = NL;
    278      if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) {
    279        *read_bytes = (size_t)(p - buf);
    280        return FAIL;
    281      }
    282      state->offset = 0;
    283      state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL
    284                          ? 0
    285                          : strlen(TV_LIST_ITEM_TV(state->li)->vval.v_string));
    286    }
    287  }
    288  *read_bytes = nbuf;
    289  return ((state->offset < state->li_length
    290           || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL)
    291          ? NOTDONE
    292          : OK);
    293 }
    294 
    295 #define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
    296  do { \
    297    const char *const buf_ = (buf); \
    298    if (buf_ == NULL) { \
    299      GA_CONCAT_LITERAL(gap, "''"); \
    300    } else { \
    301      const size_t len_ = (len); \
    302      ga_grow(gap, (int)(2 + len_ + memcnt(buf_, '\'', len_))); \
    303      ga_append(gap, '\''); \
    304      for (size_t i_ = 0; i_ < len_; i_++) { \
    305        if (buf_[i_] == '\'') { \
    306          ga_append(gap, '\''); \
    307        } \
    308        ga_append(gap, (uint8_t)buf_[i_]); \
    309      } \
    310      ga_append(gap, '\''); \
    311    } \
    312  } while (0)
    313 
    314 #define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
    315  TYPVAL_ENCODE_CONV_STRING(tv, buf, len)
    316 
    317 #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
    318 
    319 #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
    320  do { \
    321    const blob_T *const blob_ = (blob); \
    322    const int len_ = (len); \
    323    if (len_ == 0) { \
    324      GA_CONCAT_LITERAL(gap, "0z"); \
    325    } else { \
    326      /* Allocate space for "0z", the two hex chars per byte, and a */ \
    327      /* "." separator after every eight hex chars. */ \
    328      /* Example: "0z00112233.44556677.8899" */ \
    329      ga_grow(gap, 2 + 2 * len_ + (len_ - 1) / 4); \
    330      GA_CONCAT_LITERAL(gap, "0z"); \
    331      char numbuf[NUMBUFLEN]; \
    332      for (int i_ = 0; i_ < len_; i_++) { \
    333        if (i_ > 0 && (i_ & 3) == 0) { \
    334          ga_append(gap, '.'); \
    335        } \
    336        size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), "%02X", \
    337                                                (int)tv_blob_get(blob_, i_)); \
    338        ga_concat_len(gap, numbuf, numbuflen); \
    339      } \
    340    } \
    341  } while (0)
    342 
    343 #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
    344  do { \
    345    char numbuf[NUMBUFLEN]; \
    346    size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
    347                                            "%" PRId64, (int64_t)(num)); \
    348    ga_concat_len(gap, numbuf, numbuflen); \
    349  } while (0)
    350 
    351 #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
    352  do { \
    353    const float_T flt_ = (flt); \
    354    switch (xfpclassify(flt_)) { \
    355    case FP_NAN: { \
    356        GA_CONCAT_LITERAL(gap, "str2float('nan')"); \
    357        break; \
    358    } \
    359    case FP_INFINITE: { \
    360        if (flt_ < 0) { \
    361          ga_append(gap, '-'); \
    362        } \
    363        GA_CONCAT_LITERAL(gap, "str2float('inf')"); \
    364        break; \
    365    } \
    366    default: { \
    367        char numbuf[NUMBUFLEN]; \
    368        size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
    369                                                "%g", flt_); \
    370        ga_concat_len(gap, numbuf, numbuflen); \
    371    } \
    372    } \
    373  } while (0)
    374 
    375 #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
    376  do { \
    377    const char *const fun_ = (fun); \
    378    if (fun_ == NULL) { \
    379      internal_error("string(): NULL function name"); \
    380      GA_CONCAT_LITERAL(gap, "function(NULL"); \
    381    } else { \
    382      const char *const prefix_ = (prefix); \
    383      GA_CONCAT_LITERAL(gap, "function("); \
    384      const int name_off = gap->ga_len; \
    385      ga_concat(gap, prefix_); \
    386      TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
    387      /* <prefix>'<fun>' -> '<prefix><fun>'. */ \
    388      ((char *)gap->ga_data)[name_off] = '\''; \
    389      memcpy((char *)gap->ga_data + name_off + 1, prefix_, strlen(prefix_)); \
    390    } \
    391  } while (0)
    392 
    393 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
    394  do { \
    395    if ((len) != 0) { \
    396      GA_CONCAT_LITERAL(gap, ", "); \
    397    } \
    398  } while (0)
    399 
    400 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
    401  do { \
    402    if ((ptrdiff_t)(len) != -1) { \
    403      GA_CONCAT_LITERAL(gap, ", "); \
    404    } \
    405  } while (0)
    406 
    407 #define TYPVAL_ENCODE_CONV_FUNC_END(tv) \
    408  ga_append(gap, ')')
    409 
    410 #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
    411  GA_CONCAT_LITERAL(gap, "[]")
    412 
    413 #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
    414  ga_append(gap, '[')
    415 
    416 #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
    417 
    418 #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
    419  GA_CONCAT_LITERAL(gap, "{}")
    420 
    421 #define TYPVAL_ENCODE_CHECK_BEFORE
    422 
    423 #define TYPVAL_ENCODE_CONV_NIL(tv) \
    424  GA_CONCAT_LITERAL(gap, "v:null")
    425 
    426 #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
    427  do { \
    428    if (num) { \
    429      GA_CONCAT_LITERAL(gap, "v:true"); \
    430    } else { \
    431      GA_CONCAT_LITERAL(gap, "v:false"); \
    432    } \
    433  } while (0)
    434 
    435 #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
    436 
    437 #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
    438  ga_append(gap, '{')
    439 
    440 #define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
    441 
    442 #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
    443  ga_append(gap, '}')
    444 
    445 #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
    446  GA_CONCAT_LITERAL(gap, ": ")
    447 
    448 #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
    449  GA_CONCAT_LITERAL(gap, ", ")
    450 
    451 #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
    452 
    453 #define TYPVAL_ENCODE_CONV_LIST_END(tv) \
    454  ga_append(gap, ']')
    455 
    456 #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
    457  TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
    458 
    459 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
    460  do { \
    461    if (!did_echo_string_emsg) { \
    462      /* Only give this message once for a recursive call to avoid */ \
    463      /* flooding the user with errors. */ \
    464      did_echo_string_emsg = true; \
    465      emsg(_("E724: unable to correctly dump variable " \
    466             "with self-referencing container")); \
    467    } \
    468    char ebuf[NUMBUFLEN + 7]; \
    469    size_t backref = 0; \
    470    for (; backref < kv_size(*mpstack); backref++) { \
    471      const MPConvStackVal mpval = kv_A(*mpstack, backref); \
    472      if (mpval.type == (conv_type)) { \
    473        if ((conv_type) == kMPConvDict) { \
    474          if ((void *)mpval.data.d.dict == (void *)(val)) { \
    475            break; \
    476          } \
    477        } else if ((conv_type) == kMPConvList) { \
    478          if ((void *)mpval.data.l.list == (void *)(val)) { \
    479            break; \
    480          } \
    481        } \
    482      } \
    483    } \
    484    vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \
    485    ga_concat(gap, &ebuf[0]); \
    486  } while (0)
    487 
    488 #define TYPVAL_ENCODE_ALLOW_SPECIALS false
    489 
    490 #define TYPVAL_ENCODE_SCOPE static
    491 #define TYPVAL_ENCODE_NAME string
    492 #define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
    493 #define TYPVAL_ENCODE_FIRST_ARG_NAME gap
    494 #include "nvim/eval/typval_encode.c.h"
    495 #undef TYPVAL_ENCODE_SCOPE
    496 #undef TYPVAL_ENCODE_NAME
    497 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
    498 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
    499 
    500 #undef TYPVAL_ENCODE_CONV_RECURSE
    501 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
    502  do { \
    503    char ebuf[NUMBUFLEN + 7]; \
    504    size_t backref = 0; \
    505    for (; backref < kv_size(*mpstack); backref++) { \
    506      const MPConvStackVal mpval = kv_A(*mpstack, backref); \
    507      if (mpval.type == (conv_type)) { \
    508        if ((conv_type) == kMPConvDict) { \
    509          if ((void *)mpval.data.d.dict == (void *)(val)) { \
    510            break; \
    511          } \
    512        } else if ((conv_type) == kMPConvList) { \
    513          if ((void *)mpval.data.l.list == (void *)(val)) { \
    514            break; \
    515          } \
    516        } \
    517      } \
    518    } \
    519    if ((conv_type) == kMPConvDict) { \
    520      vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \
    521    } else { \
    522      vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \
    523    } \
    524    ga_concat(gap, &ebuf[0]); \
    525    return OK; \
    526  } while (0)
    527 
    528 #define TYPVAL_ENCODE_SCOPE
    529 #define TYPVAL_ENCODE_NAME echo
    530 #define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
    531 #define TYPVAL_ENCODE_FIRST_ARG_NAME gap
    532 #include "nvim/eval/typval_encode.c.h"
    533 #undef TYPVAL_ENCODE_SCOPE
    534 #undef TYPVAL_ENCODE_NAME
    535 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
    536 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
    537 
    538 #undef TYPVAL_ENCODE_CONV_RECURSE
    539 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
    540  do { \
    541    if (!did_echo_string_emsg) { \
    542      /* Only give this message once for a recursive call to avoid */ \
    543      /* flooding the user with errors. */ \
    544      did_echo_string_emsg = true; \
    545      emsg(_("E724: unable to correctly dump variable " \
    546             "with self-referencing container")); \
    547    } \
    548  } while (0)
    549 
    550 #undef TYPVAL_ENCODE_ALLOW_SPECIALS
    551 #define TYPVAL_ENCODE_ALLOW_SPECIALS true
    552 
    553 #define TYPVAL_ENCODE_CHECK_BEFORE
    554 
    555 #undef TYPVAL_ENCODE_CONV_NIL
    556 #define TYPVAL_ENCODE_CONV_NIL(tv) \
    557  GA_CONCAT_LITERAL(gap, "null")
    558 
    559 #undef TYPVAL_ENCODE_CONV_BOOL
    560 #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
    561  do { \
    562    if (num) { \
    563      GA_CONCAT_LITERAL(gap, "true"); \
    564    } else { \
    565      GA_CONCAT_LITERAL(gap, "false"); \
    566    } \
    567  } while (0)
    568 
    569 #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
    570 #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
    571  do { \
    572    char numbuf[NUMBUFLEN]; \
    573    size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
    574                                            "%" PRIu64, (num)); \
    575    ga_concat_len(gap, numbuf, numbuflen); \
    576  } while (0)
    577 
    578 #undef TYPVAL_ENCODE_CONV_FLOAT
    579 #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
    580  do { \
    581    const float_T flt_ = (flt); \
    582    switch (xfpclassify(flt_)) { \
    583    case FP_NAN: { \
    584        emsg(_("E474: Unable to represent NaN value in JSON")); \
    585        return FAIL; \
    586    } \
    587    case FP_INFINITE: { \
    588        emsg(_("E474: Unable to represent infinity in JSON")); \
    589        return FAIL; \
    590    } \
    591    default: { \
    592        char numbuf[NUMBUFLEN]; \
    593        size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
    594                                                "%g", flt_); \
    595        ga_concat_len(gap, numbuf, numbuflen); \
    596        break; \
    597    } \
    598    } \
    599  } while (0)
    600 
    601 /// Escape sequences used in JSON
    602 static const char escapes[][3] = {
    603  [BS] = "\\b",
    604  [TAB] = "\\t",
    605  [NL] = "\\n",
    606  [CAR] = "\\r",
    607  ['"'] = "\\\"",
    608  ['\\'] = "\\\\",
    609  [FF] = "\\f",
    610 };
    611 
    612 static const char xdigits[] = "0123456789ABCDEF";
    613 
    614 /// Convert given string to JSON string
    615 ///
    616 /// @param[out]  gap  Garray where result will be saved.
    617 /// @param[in]  buf  Converted string.
    618 /// @param[in]  len  Converted string length.
    619 ///
    620 /// @return OK in case of success, FAIL otherwise.
    621 static inline int convert_to_json_string(garray_T *const gap, const char *const buf,
    622                                         const size_t len)
    623  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE
    624 {
    625  const char *utf_buf = buf;
    626  if (utf_buf == NULL) {
    627    GA_CONCAT_LITERAL(gap, "\"\"");
    628  } else {
    629    size_t utf_len = len;
    630    char *tofree = NULL;
    631    size_t str_len = 0;
    632    // Encode character as \uNNNN if
    633    // 1. It is an ASCII control character (0x0 .. 0x1F; 0x7F not
    634    //    utf_printable and thus not checked specially).
    635    // 2. Code point is not printable according to utf_printable().
    636    // This is done to make resulting values displayable on screen also not from
    637    // Neovim.
    638 #define ENCODE_RAW(ch) \
    639  ((ch) >= 0x20 && utf_printable(ch))
    640    for (size_t i = 0; i < utf_len;) {
    641      const int ch = utf_ptr2char(utf_buf + i);
    642      const size_t shift = (ch == 0 ? 1 : ((size_t)utf_ptr2len(utf_buf + i)));
    643      assert(shift > 0);
    644      i += shift;
    645      switch (ch) {
    646      case BS:
    647      case TAB:
    648      case NL:
    649      case FF:
    650      case CAR:
    651      case '"':
    652      case '\\':
    653        str_len += 2;
    654        break;
    655      default:
    656        if (ch > 0x7F && shift == 1) {
    657          semsg(_("E474: String \"%.*s\" contains byte that does not start "
    658                  "any UTF-8 character"),
    659                (int)(utf_len - (i - shift)), utf_buf + i - shift);
    660          xfree(tofree);
    661          return FAIL;
    662        } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END)
    663                   || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) {
    664          semsg(_("E474: UTF-8 string contains code point which belongs "
    665                  "to a surrogate pair: %.*s"),
    666                (int)(utf_len - (i - shift)), utf_buf + i - shift);
    667          xfree(tofree);
    668          return FAIL;
    669        } else if (ENCODE_RAW(ch)) {
    670          str_len += shift;
    671        } else {
    672          str_len += ((sizeof("\\u1234") - 1)
    673                      * (size_t)(1 + (ch >= SURROGATE_FIRST_CHAR)));
    674        }
    675        break;
    676      }
    677    }
    678    ga_append(gap, '"');
    679    ga_grow(gap, (int)str_len);
    680    for (size_t i = 0; i < utf_len;) {
    681      const int ch = utf_ptr2char(utf_buf + i);
    682      const size_t shift = (ch == 0 ? 1 : ((size_t)utf_char2len(ch)));
    683      assert(shift > 0);
    684      // Is false on invalid unicode, but this should already be handled.
    685      assert(ch == 0 || shift == ((size_t)utf_ptr2len(utf_buf + i)));
    686      switch (ch) {
    687      case BS:
    688      case TAB:
    689      case NL:
    690      case FF:
    691      case CAR:
    692      case '"':
    693      case '\\':
    694        ga_concat_len(gap, escapes[ch], 2);
    695        break;
    696      default:
    697        if (ENCODE_RAW(ch)) {
    698          ga_concat_len(gap, utf_buf + i, shift);
    699        } else if (ch < SURROGATE_FIRST_CHAR) {
    700          ga_concat_len(gap, ((const char[]) {
    701            '\\', 'u',
    702            xdigits[(ch >> (4 * 3)) & 0xF],
    703            xdigits[(ch >> (4 * 2)) & 0xF],
    704            xdigits[(ch >> (4 * 1)) & 0xF],
    705            xdigits[(ch >> (4 * 0)) & 0xF],
    706          }), sizeof("\\u1234") - 1);
    707        } else {
    708          const int tmp = ch - SURROGATE_FIRST_CHAR;
    709          const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1));
    710          const int lo = SURROGATE_LO_END + ((tmp >>  0) & ((1 << 10) - 1));
    711          ga_concat_len(gap, ((const char[]) {
    712            '\\', 'u',
    713            xdigits[(hi >> (4 * 3)) & 0xF],
    714            xdigits[(hi >> (4 * 2)) & 0xF],
    715            xdigits[(hi >> (4 * 1)) & 0xF],
    716            xdigits[(hi >> (4 * 0)) & 0xF],
    717            '\\', 'u',
    718            xdigits[(lo >> (4 * 3)) & 0xF],
    719            xdigits[(lo >> (4 * 2)) & 0xF],
    720            xdigits[(lo >> (4 * 1)) & 0xF],
    721            xdigits[(lo >> (4 * 0)) & 0xF],
    722          }), (sizeof("\\u1234") - 1) * 2);
    723        }
    724        break;
    725      }
    726      i += shift;
    727    }
    728    ga_append(gap, '"');
    729    xfree(tofree);
    730  }
    731  return OK;
    732 }
    733 
    734 #undef TYPVAL_ENCODE_CONV_STRING
    735 #define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
    736  do { \
    737    if (convert_to_json_string(gap, (buf), (len)) != OK) { \
    738      return FAIL; \
    739    } \
    740  } while (0)
    741 
    742 #undef TYPVAL_ENCODE_CONV_EXT_STRING
    743 #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
    744  do { \
    745    xfree(buf); \
    746    emsg(_("E474: Unable to convert EXT string to JSON")); \
    747    return FAIL; \
    748  } while (0)
    749 
    750 #undef TYPVAL_ENCODE_CONV_BLOB
    751 #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
    752  do { \
    753    const blob_T *const blob_ = (blob); \
    754    const int len_ = (len); \
    755    if (len_ == 0) { \
    756      GA_CONCAT_LITERAL(gap, "[]"); \
    757    } else { \
    758      ga_append(gap, '['); \
    759      char numbuf[NUMBUFLEN]; \
    760      for (int i_ = 0; i_ < len_; i_++) { \
    761        if (i_ > 0) { \
    762          GA_CONCAT_LITERAL(gap, ", "); \
    763        } \
    764        size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
    765                                                "%d", (int)tv_blob_get(blob_, i_)); \
    766        ga_concat_len(gap, numbuf, numbuflen); \
    767      } \
    768      ga_append(gap, ']'); \
    769    } \
    770  } while (0)
    771 
    772 #undef TYPVAL_ENCODE_CONV_FUNC_START
    773 #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
    774  return conv_error(_("E474: Error while dumping %s, %s: " \
    775                      "attempt to dump function reference"), \
    776                    mpstack, objname)
    777 
    778 /// Check whether given key can be used in json_encode()
    779 ///
    780 /// @param[in]  tv  Key to check.
    781 bool encode_check_json_key(const typval_T *const tv)
    782  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
    783 {
    784  if (tv->v_type == VAR_STRING) {
    785    return true;
    786  }
    787  if (tv->v_type != VAR_DICT) {
    788    return false;
    789  }
    790  const dict_T *const spdict = tv->vval.v_dict;
    791  if (spdict->dv_hashtab.ht_used != 2) {
    792    return false;
    793  }
    794  const dictitem_T *type_di;
    795  const dictitem_T *val_di;
    796  if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL
    797      || type_di->di_tv.v_type != VAR_LIST
    798      || type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString]
    799      || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL
    800      || val_di->di_tv.v_type != VAR_LIST) {
    801    return false;
    802  }
    803  if (val_di->di_tv.vval.v_list == NULL) {
    804    return true;
    805  }
    806  TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, {
    807    if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
    808      return false;
    809    }
    810  });
    811  return true;
    812 }
    813 
    814 #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
    815 #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
    816  do { \
    817    if (!encode_check_json_key(&(key))) { \
    818      emsg(_("E474: Invalid key in special dictionary")); \
    819      goto label; \
    820    } \
    821  } while (0)
    822 
    823 #define TYPVAL_ENCODE_SCOPE static
    824 #define TYPVAL_ENCODE_NAME json
    825 #define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
    826 #define TYPVAL_ENCODE_FIRST_ARG_NAME gap
    827 #include "nvim/eval/typval_encode.c.h"
    828 #undef TYPVAL_ENCODE_SCOPE
    829 #undef TYPVAL_ENCODE_NAME
    830 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
    831 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
    832 
    833 #undef TYPVAL_ENCODE_CONV_STRING
    834 #undef TYPVAL_ENCODE_CONV_STR_STRING
    835 #undef TYPVAL_ENCODE_CONV_EXT_STRING
    836 #undef TYPVAL_ENCODE_CONV_BLOB
    837 #undef TYPVAL_ENCODE_CONV_NUMBER
    838 #undef TYPVAL_ENCODE_CONV_FLOAT
    839 #undef TYPVAL_ENCODE_CONV_FUNC_START
    840 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
    841 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
    842 #undef TYPVAL_ENCODE_CONV_FUNC_END
    843 #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
    844 #undef TYPVAL_ENCODE_CONV_LIST_START
    845 #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
    846 #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
    847 #undef TYPVAL_ENCODE_CHECK_BEFORE
    848 #undef TYPVAL_ENCODE_CONV_NIL
    849 #undef TYPVAL_ENCODE_CONV_BOOL
    850 #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
    851 #undef TYPVAL_ENCODE_CONV_DICT_START
    852 #undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
    853 #undef TYPVAL_ENCODE_CONV_DICT_END
    854 #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
    855 #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
    856 #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
    857 #undef TYPVAL_ENCODE_CONV_LIST_END
    858 #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
    859 #undef TYPVAL_ENCODE_CONV_RECURSE
    860 #undef TYPVAL_ENCODE_ALLOW_SPECIALS
    861 
    862 /// Return a string with the string representation of a variable.
    863 /// Puts quotes around strings, so that they can be parsed back by eval().
    864 ///
    865 /// @param[in]  tv  typval_T to convert.
    866 /// @param[out]  len  Location where length of the result will be saved.
    867 ///
    868 /// @return String representation of the variable or NULL.
    869 char *encode_tv2string(typval_T *tv, size_t *len)
    870  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
    871 {
    872  garray_T ga;
    873  ga_init(&ga, (int)sizeof(char), 80);
    874  const int evs_ret = encode_vim_to_string(&ga, tv,
    875                                           N_("encode_tv2string() argument"));
    876  (void)evs_ret;
    877  assert(evs_ret == OK);
    878  did_echo_string_emsg = false;
    879  if (len != NULL) {
    880    *len = (size_t)ga.ga_len;
    881  }
    882  ga_append(&ga, NUL);
    883  return (char *)ga.ga_data;
    884 }
    885 
    886 /// Return a string with the string representation of a variable.
    887 /// Does not put quotes around strings, as ":echo" displays values.
    888 ///
    889 /// @param[in]  tv  typval_T to convert.
    890 /// @param[out]  len  Location where length of the result will be saved.
    891 ///
    892 /// @return String representation of the variable or NULL.
    893 char *encode_tv2echo(typval_T *tv, size_t *len)
    894  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
    895 {
    896  garray_T ga;
    897  ga_init(&ga, (int)sizeof(char), 80);
    898  if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
    899    if (tv->vval.v_string != NULL) {
    900      ga_concat(&ga, tv->vval.v_string);
    901    }
    902  } else {
    903    const int eve_ret = encode_vim_to_echo(&ga, tv, N_(":echo argument"));
    904    (void)eve_ret;
    905    assert(eve_ret == OK);
    906  }
    907  if (len != NULL) {
    908    *len = (size_t)ga.ga_len;
    909  }
    910  ga_append(&ga, NUL);
    911  return (char *)ga.ga_data;
    912 }
    913 
    914 /// Return a string with the string representation of a variable.
    915 /// Puts quotes around strings, so that they can be parsed back by eval().
    916 ///
    917 /// @param[in]  tv  typval_T to convert.
    918 /// @param[out]  len  Location where length of the result will be saved.
    919 ///
    920 /// @return String representation of the variable or NULL.
    921 char *encode_tv2json(typval_T *tv, size_t *len)
    922  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
    923 {
    924  garray_T ga;
    925  ga_init(&ga, (int)sizeof(char), 80);
    926  const int evj_ret = encode_vim_to_json(&ga, tv,
    927                                         N_("encode_tv2json() argument"));
    928  if (!evj_ret) {
    929    ga_clear(&ga);
    930  }
    931  did_echo_string_emsg = false;
    932  if (len != NULL) {
    933    *len = (size_t)ga.ga_len;
    934  }
    935  ga_append(&ga, NUL);
    936  return (char *)ga.ga_data;
    937 }
    938 
    939 #define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
    940  mpack_bin(cbuf_as_string(buf, (len)), packer); \
    941 
    942 #define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
    943  mpack_str(cbuf_as_string(buf, (len)), packer); \
    944 
    945 #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
    946  mpack_ext(buf, (len), (int8_t)(type), packer); \
    947 
    948 #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
    949  mpack_bin(cbuf_as_string((blob) ? (blob)->bv_ga.ga_data : NULL, (size_t)(len)), packer);
    950 
    951 #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
    952  mpack_integer(&packer->ptr, (Integer)(num))
    953 
    954 #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
    955  mpack_float8(&packer->ptr, (double)(flt))
    956 
    957 #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
    958  return conv_error(_("E5004: Error while dumping %s, %s: " \
    959                      "attempt to dump function reference"), \
    960                    mpstack, objname)
    961 
    962 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
    963 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
    964 #define TYPVAL_ENCODE_CONV_FUNC_END(tv)
    965 
    966 #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
    967  mpack_array(&packer->ptr, 0)
    968 
    969 #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
    970  mpack_array(&packer->ptr, (uint32_t)(len))
    971 
    972 #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
    973 
    974 #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
    975  mpack_map(&packer->ptr, 0)
    976 
    977 #define TYPVAL_ENCODE_CHECK_BEFORE \
    978  mpack_check_buffer(packer)
    979 
    980 #define TYPVAL_ENCODE_CONV_NIL(tv) \
    981  mpack_nil(&packer->ptr)
    982 
    983 #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
    984  mpack_bool(&packer->ptr, (bool)num); \
    985 
    986 #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
    987  mpack_uint64(&packer->ptr, (num))
    988 
    989 #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
    990  mpack_map(&packer->ptr, (uint32_t)(len))
    991 
    992 #define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
    993 
    994 #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict)
    995 
    996 #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
    997 
    998 #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
    999 
   1000 #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
   1001 
   1002 #define TYPVAL_ENCODE_CONV_LIST_END(tv)
   1003 
   1004 #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
   1005 
   1006 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
   1007  return conv_error(_("E5005: Unable to dump %s: " \
   1008                      "container references itself in %s"), \
   1009                    mpstack, objname)
   1010 
   1011 #define TYPVAL_ENCODE_ALLOW_SPECIALS true
   1012 
   1013 #define TYPVAL_ENCODE_SCOPE
   1014 #define TYPVAL_ENCODE_NAME msgpack
   1015 #define TYPVAL_ENCODE_FIRST_ARG_TYPE PackerBuffer *const
   1016 #define TYPVAL_ENCODE_FIRST_ARG_NAME packer
   1017 #include "nvim/eval/typval_encode.c.h"
   1018 #undef TYPVAL_ENCODE_SCOPE
   1019 #undef TYPVAL_ENCODE_NAME
   1020 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
   1021 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
   1022 
   1023 #undef TYPVAL_ENCODE_CONV_STRING
   1024 #undef TYPVAL_ENCODE_CONV_STR_STRING
   1025 #undef TYPVAL_ENCODE_CONV_EXT_STRING
   1026 #undef TYPVAL_ENCODE_CONV_BLOB
   1027 #undef TYPVAL_ENCODE_CONV_NUMBER
   1028 #undef TYPVAL_ENCODE_CONV_FLOAT
   1029 #undef TYPVAL_ENCODE_CONV_FUNC_START
   1030 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
   1031 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
   1032 #undef TYPVAL_ENCODE_CONV_FUNC_END
   1033 #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
   1034 #undef TYPVAL_ENCODE_CONV_LIST_START
   1035 #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
   1036 #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
   1037 #undef TYPVAL_ENCODE_CHECK_BEFORE
   1038 #undef TYPVAL_ENCODE_CONV_NIL
   1039 #undef TYPVAL_ENCODE_CONV_BOOL
   1040 #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
   1041 #undef TYPVAL_ENCODE_CONV_DICT_START
   1042 #undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
   1043 #undef TYPVAL_ENCODE_CONV_DICT_END
   1044 #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
   1045 #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
   1046 #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
   1047 #undef TYPVAL_ENCODE_CONV_LIST_END
   1048 #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
   1049 #undef TYPVAL_ENCODE_CONV_RECURSE
   1050 #undef TYPVAL_ENCODE_ALLOW_SPECIALS
   1051 
   1052 /// Initialize ListReaderState structure
   1053 ListReaderState encode_init_lrstate(const list_T *const list)
   1054  FUNC_ATTR_NONNULL_ALL
   1055 {
   1056  return (ListReaderState) {
   1057    .list = list,
   1058    .li = tv_list_first(list),
   1059    .offset = 0,
   1060    .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL
   1061                  ? 0
   1062                  : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)),
   1063  };
   1064 }