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 }