1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include <libwebsockets.h> 26#include <private-lib-core.h> 27 28#include <assert.h> 29 30signed char 31lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason) 32{ 33 lws_struct_args_t *a = (lws_struct_args_t *)ctx->user; 34 const lws_struct_map_t *map = a->map_st[ctx->pst_sp]; 35 size_t n = a->map_entries_st[ctx->pst_sp], imp = 0; 36 lejp_callback cb = map->lejp_cb; 37 38 if (reason == LEJPCB_PAIR_NAME && strcmp(ctx->path, "schema")) { 39 /* 40 * If not "schema", the schema is implicit rather than 41 * explicitly given, ie, he just goes ahead and starts using 42 * member names that imply a particular type. For example, he 43 * may have an implicit type normally, and a different one for 44 * exceptions that just starts using "error-message" or whatever 45 * and we can understand that's the exception type now. 46 * 47 * Let's look into each of the maps in the top level array 48 * and match the first one that mentions the name he gave here, 49 * and bind to the associated type / create a toplevel object 50 * of that type. 51 */ 52 53 while (n--) { 54 int m, child_members = (int)map->child_map_size; 55 56 for (m = 0; m < child_members; m++) { 57 const lws_struct_map_t *child = &map->child_map[m]; 58 if (!strcmp(ctx->path, child->colname)) { 59 /* 60 * We matched on him... map is pointing 61 * to the right toplevel type, let's 62 * just pick up from there as if we 63 * matched the explicit schema name... 64 */ 65 ctx->path_match = 1; 66 imp = 1; 67 goto matched; 68 } 69 } 70 map++; 71 } 72 lwsl_notice("%s: can't match implicit schema %s\n", 73 __func__, ctx->path); 74 75 return -1; 76 } 77 78 if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1) 79 return 0; 80 81 /* If "schema", then look for a matching name in the map array */ 82 83 while (n--) { 84 if (strcmp(ctx->buf, map->colname)) { 85 map++; 86 continue; 87 } 88 89matched: 90 91 a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size); 92 if (!a->dest) { 93 lwsl_err("%s: OOT\n", __func__); 94 95 return 1; 96 } 97 a->dest_len = map->aux; 98 if (!ctx->pst_sp) 99 a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]); 100 101 if (!cb) 102 cb = lws_struct_default_lejp_cb; 103 104 lejp_parser_push(ctx, a->dest, &map->child_map[0].colname, 105 (uint8_t)map->child_map_size, cb); 106 a->map_st[ctx->pst_sp] = map->child_map; 107 a->map_entries_st[ctx->pst_sp] = map->child_map_size; 108 109 // lwsl_notice("%s: child map ofs_clist %d\n", __func__, 110 // (int)a->map_st[ctx->pst_sp]->ofs_clist); 111 112 if (imp) 113 return cb(ctx, reason); 114 115 return 0; 116 } 117 118 lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf); 119 120 return 1; 121} 122 123static int 124lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args, 125 const lws_struct_map_t *map, uint8_t *ch) 126{ 127 lejp_callback cb = map->lejp_cb; 128 129 if (!cb) 130 cb = lws_struct_default_lejp_cb; 131 132 lejp_parser_push(ctx, ch, (const char * const*)map->child_map, 133 (uint8_t)map->child_map_size, cb); 134 135 args->map_st[ctx->pst_sp] = map->child_map; 136 args->map_entries_st[ctx->pst_sp] = map->child_map_size; 137 138 return 0; 139} 140 141signed char 142lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason) 143{ 144 lws_struct_args_t *args = (lws_struct_args_t *)ctx->user; 145 const lws_struct_map_t *map, *pmap = NULL; 146 uint8_t *ch; 147 size_t n; 148 char *u; 149 150 if (reason == LEJPCB_ARRAY_END) { 151 lejp_parser_pop(ctx); 152 153 return 0; 154 } 155 156 if (reason == LEJPCB_ARRAY_START) { 157 if (!ctx->path_match) 158 lwsl_err("%s: ARRAY_START with ctx->path_match 0\n", __func__); 159 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; 160 161 if (map->type == LSMT_LIST) 162 lws_struct_lejp_push(ctx, args, map, NULL); 163 164 return 0; 165 } 166 167 if (ctx->pst_sp) 168 pmap = &args->map_st[ctx->pst_sp - 1] 169 [ctx->pst[ctx->pst_sp - 1].path_match - 1]; 170 171 if (reason == LEJPCB_OBJECT_START) { 172 173 if (!ctx->path_match) { 174 ctx->pst[ctx->pst_sp].user = NULL; 175 176 return 0; 177 } 178 179 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; 180 n = args->map_entries_st[ctx->pst_sp]; 181 182 if (map->type != LSMT_CHILD_PTR && map->type != LSMT_LIST) { 183 ctx->pst[ctx->pst_sp].user = NULL; 184 185 return 0; 186 } 187 pmap = map; 188 189 lws_struct_lejp_push(ctx, args, map, NULL); 190 } 191 192 if (reason == LEJPCB_OBJECT_END && pmap) { 193 if (pmap->type == LSMT_CHILD_PTR) 194 lejp_parser_pop(ctx); 195 196 if (ctx->pst_sp) 197 pmap = &args->map_st[ctx->pst_sp - 1] 198 [ctx->pst[ctx->pst_sp - 1].path_match - 1]; 199 } 200 201 if (!ctx->path_match) 202 return 0; 203 204 map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; 205 n = args->map_entries_st[ctx->pst_sp]; 206 207 if (map->type == LSMT_SCHEMA) { 208 209 while (n--) { 210 if (strncmp(map->colname, ctx->buf, ctx->npos)) { 211 map++; 212 continue; 213 } 214 215 /* instantiate the correct toplevel object */ 216 217 ch = lwsac_use_zero(&args->ac, map->aux, 218 args->ac_block_size); 219 if (!ch) { 220 lwsl_err("OOM\n"); 221 222 return 1; 223 } 224 225 lws_struct_lejp_push(ctx, args, map, ch); 226 227 return 0; 228 } 229 lwsl_notice("%s: unknown schema %.*s, tried %d\n", __func__, 230 ctx->npos, ctx->buf, 231 (int)args->map_entries_st[ctx->pst_sp]); 232 233 goto cleanup; 234 } 235 236 if (!ctx->pst[ctx->pst_sp].user) { 237 struct lws_dll2_owner *owner; 238 struct lws_dll2 *list; 239 240 /* create list item object if none already */ 241 242 if (!ctx->path_match || !pmap) 243 return 0; 244 245 map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1]; 246 n = args->map_entries_st[ctx->pst_sp - 1]; 247 248 if (!ctx->pst_sp) 249 return 0; 250 251 if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR) 252 return 1; 253 254 /* we need to create a child or array item object */ 255 256 owner = (struct lws_dll2_owner *) 257 (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs); 258 259 assert(pmap->aux); 260 261 /* instantiate one of the child objects */ 262 263 ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac, 264 pmap->aux, args->ac_block_size); 265 if (!ctx->pst[ctx->pst_sp].user) { 266 lwsl_err("OOM\n"); 267 268 return 1; 269 } 270 lwsl_info("%s: created '%s' object size %d\n", __func__, 271 pmap->colname, (int)pmap->aux); 272 273 switch (pmap->type) { 274 case LSMT_LIST: 275 list = (struct lws_dll2 *) 276 ((char *)ctx->pst[ctx->pst_sp].user + 277 pmap->ofs_clist); 278 279 lws_dll2_add_tail(list, owner); 280 break; 281 case LSMT_CHILD_PTR: 282 *((void **)owner) = ctx->pst[ctx->pst_sp].user; 283 break; 284 default: 285 assert(0); 286 break; 287 } 288 } 289 290 if (!ctx->path_match) 291 return 0; 292 293 if (reason == LEJPCB_VAL_STR_CHUNK) { 294 lejp_collation_t *coll; 295 296 /* don't cache stuff we are going to ignore */ 297 298 if (map->type == LSMT_STRING_CHAR_ARRAY && 299 args->chunks_length >= map->aux) 300 return 0; 301 302 coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll), 303 sizeof(*coll)); 304 if (!coll) { 305 lwsl_err("%s: OOT\n", __func__); 306 307 return 1; 308 } 309 coll->chunks.prev = NULL; 310 coll->chunks.next = NULL; 311 coll->chunks.owner = NULL; 312 313 coll->len = ctx->npos; 314 lws_dll2_add_tail(&coll->chunks, &args->chunks_owner); 315 316 memcpy(coll->buf, ctx->buf, ctx->npos); 317 318 args->chunks_length += ctx->npos; 319 320 return 0; 321 } 322 323 if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT && 324 reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE) 325 return 0; 326 327 /* this is the end of the string */ 328 329 if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) { 330 void **pp = (void **) 331 (((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs); 332 333 *pp = ctx->pst[ctx->pst_sp].user; 334 } 335 336 u = (char *)ctx->pst[ctx->pst_sp].user; 337 if (!u) 338 u = (char *)ctx->pst[ctx->pst_sp - 1].user; 339 340 { 341 char **pp, *s; 342 size_t lim, b; 343 long long li; 344 345 switch (map->type) { 346 case LSMT_SIGNED: 347 if (map->aux == sizeof(signed char)) { 348 signed char *pc; 349 pc = (signed char *)(u + map->ofs); 350 *pc = (signed char)atoi(ctx->buf); 351 break; 352 } 353 if (map->aux == sizeof(int)) { 354 int *pi; 355 pi = (int *)(u + map->ofs); 356 *pi = atoi(ctx->buf); 357 break; 358 } 359 if (map->aux == sizeof(long)) { 360 long *pl; 361 pl = (long *)(u + map->ofs); 362 *pl = atol(ctx->buf); 363 } else { 364 long long *pll; 365 pll = (long long *)(u + map->ofs); 366 *pll = atoll(ctx->buf); 367 } 368 break; 369 370 case LSMT_UNSIGNED: 371 if (map->aux == sizeof(unsigned char)) { 372 unsigned char *pc; 373 pc = (unsigned char *)(u + map->ofs); 374 *pc = (unsigned char)(unsigned int)atoi(ctx->buf); 375 break; 376 } 377 if (map->aux == sizeof(unsigned int)) { 378 unsigned int *pi; 379 pi = (unsigned int *)(u + map->ofs); 380 *pi = (unsigned int)atoi(ctx->buf); 381 break; 382 } 383 if (map->aux == sizeof(unsigned long)) { 384 unsigned long *pl; 385 pl = (unsigned long *)(u + map->ofs); 386 *pl = (unsigned long)atol(ctx->buf); 387 } else { 388 unsigned long long *pll; 389 pll = (unsigned long long *)(u + map->ofs); 390 *pll = (unsigned long long)atoll(ctx->buf); 391 } 392 break; 393 394 case LSMT_BOOLEAN: 395 li = reason == LEJPCB_VAL_TRUE; 396 if (map->aux == sizeof(char)) { 397 char *pc; 398 pc = (char *)(u + map->ofs); 399 *pc = (char)li; 400 break; 401 } 402 if (map->aux == sizeof(int)) { 403 int *pi; 404 pi = (int *)(u + map->ofs); 405 *pi = (int)li; 406 } else { 407 uint64_t *p64; 408 p64 = (uint64_t *)(u + map->ofs); 409 *p64 = (uint64_t)li; 410 } 411 break; 412 413 case LSMT_STRING_CHAR_ARRAY: 414 s = (char *)(u + map->ofs); 415 lim = map->aux - 1; 416 goto chunk_copy; 417 418 case LSMT_STRING_PTR: 419 pp = (char **)(u + map->ofs); 420 lim = args->chunks_length + ctx->npos; 421 s = lwsac_use(&args->ac, lim + 1, args->ac_block_size); 422 if (!s) 423 goto cleanup; 424 *pp = s; 425 426chunk_copy: 427 s[lim] = '\0'; 428 /* copy up to lim from the string chunk ac first */ 429 lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, 430 args->chunks_owner.head) { 431 lejp_collation_t *coll = (lejp_collation_t *)p; 432 433 if (lim) { 434 b = (unsigned int)coll->len; 435 if (b > lim) 436 b = lim; 437 memcpy(s, coll->buf, b); 438 s += b; 439 lim -= b; 440 } 441 } lws_end_foreach_dll_safe(p, p1); 442 443 lwsac_free(&args->ac_chunks); 444 args->chunks_owner.count = 0; 445 args->chunks_owner.head = NULL; 446 args->chunks_owner.tail = NULL; 447 448 if (lim) { 449 b = ctx->npos; 450 if (b > lim) 451 b = lim; 452 memcpy(s, ctx->buf, b); 453 s[b] = '\0'; 454 } 455 break; 456 default: 457 break; 458 } 459 } 460 461 if (args->cb) 462 args->cb(args->dest, args->cb_arg); 463 464 return 0; 465 466cleanup: 467 lwsl_notice("%s: cleanup\n", __func__); 468 lwsac_free(&args->ac_chunks); 469 args->chunks_owner.count = 0; 470 args->chunks_owner.head = NULL; 471 args->chunks_owner.tail = NULL; 472 473 return 1; 474} 475 476static const char * schema[] = { "schema" }; 477 478int 479lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user) 480{ 481 /* 482 * By default we are looking to match on a toplevel member called 483 * "schema", against an LSM_SCHEMA 484 */ 485 if (!cb) 486 cb = lws_struct_schema_only_lejp_cb; 487 lejp_construct(ctx, cb, user, schema, 1); 488 489 ctx->path_stride = sizeof(lws_struct_map_t); 490 491 return 0; 492} 493 494lws_struct_serialize_t * 495lws_struct_json_serialize_create(const lws_struct_map_t *map, 496 size_t map_entries, int flags, 497 const void *ptoplevel) 498{ 499 lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__); 500 lws_struct_serialize_st_t *j; 501 502 if (!js) 503 return NULL; 504 505 js->flags = flags; 506 507 j = &js->st[0]; 508 j->map = map; 509 j->map_entries = map_entries; 510 j->obj = ptoplevel; 511 j->idt = 0; 512 513 return js; 514} 515 516void 517lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs) 518{ 519 if (!*pjs) 520 return; 521 522 lws_free(*pjs); 523 524 *pjs = NULL; 525} 526 527static void 528lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen) 529{ 530 if (js->flags & LSSERJ_FLAG_PRETTY) { 531 int n; 532 533 *(*pbuf)++ = '\n'; 534 (*plen)--; 535 for (n = 0; n < js->st[js->sp].idt; n++) { 536 *(*pbuf)++ = ' '; 537 (*plen)--; 538 } 539 } 540} 541 542lws_struct_json_serialize_result_t 543lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf, 544 size_t len, size_t *written) 545{ 546 lws_struct_serialize_st_t *j; 547 const lws_struct_map_t *map; 548 size_t budget = 0, olen = len, m; 549 struct lws_dll2_owner *o; 550 unsigned long long uli; 551 const char *q; 552 const void *p; 553 char dbuf[72]; 554 long long li; 555 int n, used; 556 557 *written = 0; 558 *buf = '\0'; 559 560 while (len > sizeof(dbuf) + 20) { 561 j = &js->st[js->sp]; 562 map = &j->map[j->map_entry]; 563 q = j->obj + map->ofs; 564 565 /* early check if the entry should be elided */ 566 567 switch (map->type) { 568 case LSMT_STRING_CHAR_ARRAY: 569 if (!q) 570 goto up; 571 break; 572 case LSMT_STRING_PTR: 573 case LSMT_CHILD_PTR: 574 q = (char *)*(char **)q; 575 if (!q) 576 goto up; 577 break; 578 579 case LSMT_LIST: 580 o = (struct lws_dll2_owner *)q; 581 p = j->dllpos = lws_dll2_get_head(o); 582 if (!p) 583 goto up; 584 break; 585 586 case LSMT_BLOB_PTR: 587 goto up; 588 589 default: 590 break; 591 } 592 593 if (j->subsequent) { 594 *buf++ = ','; 595 len--; 596 lws_struct_pretty(js, &buf, &len); 597 } 598 j->subsequent = 1; 599 600 if (map->type != LSMT_SCHEMA && !js->offset) { 601 n = lws_snprintf((char *)buf, len, "\"%s\":", 602 map->colname); 603 buf += n; 604 len = len - (unsigned int)n; 605 if (js->flags & LSSERJ_FLAG_PRETTY) { 606 *buf++ = ' '; 607 len--; 608 } 609 } 610 611 switch (map->type) { 612 case LSMT_BOOLEAN: 613 case LSMT_UNSIGNED: 614 if (map->aux == sizeof(char)) { 615 uli = *(unsigned char *)q; 616 } else { 617 if (map->aux == sizeof(int)) { 618 uli = *(unsigned int *)q; 619 } else { 620 if (map->aux == sizeof(long)) 621 uli = *(unsigned long *)q; 622 else 623 uli = *(unsigned long long *)q; 624 } 625 } 626 q = dbuf; 627 628 if (map->type == LSMT_BOOLEAN) { 629 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), 630 "%s", uli ? "true" : "false"); 631 } else 632 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), 633 "%llu", uli); 634 break; 635 636 case LSMT_SIGNED: 637 if (map->aux == sizeof(signed char)) { 638 li = (long long)*(signed char *)q; 639 } else { 640 if (map->aux == sizeof(int)) { 641 li = (long long)*(int *)q; 642 } else { 643 if (map->aux == sizeof(long)) 644 li = (long long)*(long *)q; 645 else 646 li = *(long long *)q; 647 } 648 } 649 q = dbuf; 650 budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), "%lld", li); 651 break; 652 653 case LSMT_STRING_CHAR_ARRAY: 654 case LSMT_STRING_PTR: 655 if (!js->offset) { 656 *buf++ = '\"'; 657 len--; 658 } 659 break; 660 661 case LSMT_LIST: 662 *buf++ = '['; 663 len--; 664 if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH) 665 return LSJS_RESULT_ERROR; 666 667 /* add a stack level to handle parsing array members */ 668 669 o = (struct lws_dll2_owner *)q; 670 p = j->dllpos = lws_dll2_get_head(o); 671 672 if (!j->dllpos) { 673 *buf++ = ']'; 674 len--; 675 goto up; 676 } 677 678 n = j->idt; 679 j = &js->st[++js->sp]; 680 j->idt = (char)(n + 2); 681 j->map = map->child_map; 682 j->map_entries = map->child_map_size; 683 j->size = map->aux; 684 j->subsequent = 0; 685 j->map_entry = 0; 686 lws_struct_pretty(js, &buf, &len); 687 *buf++ = '{'; 688 len--; 689 lws_struct_pretty(js, &buf, &len); 690 if (p) 691 j->obj = ((char *)p) - j->map->ofs_clist; 692 else 693 j->obj = NULL; 694 continue; 695 696 case LSMT_CHILD_PTR: 697 698 if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH) 699 return LSJS_RESULT_ERROR; 700 701 /* add a stack level to handle parsing child members */ 702 703 n = j->idt; 704 j = &js->st[++js->sp]; 705 j->idt = (char)(n + 2); 706 j->map = map->child_map; 707 j->map_entries = map->child_map_size; 708 j->size = map->aux; 709 j->subsequent = 0; 710 j->map_entry = 0; 711 *buf++ = '{'; 712 len--; 713 lws_struct_pretty(js, &buf, &len); 714 j->obj = q; 715 716 continue; 717 718 case LSMT_SCHEMA: 719 q = dbuf; 720 *buf++ = '{'; 721 len--; 722 j = &js->st[++js->sp]; 723 lws_struct_pretty(js, &buf, &len); 724 if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA)) { 725 budget = (unsigned int)lws_snprintf(dbuf, 15, "\"schema\":"); 726 if (js->flags & LSSERJ_FLAG_PRETTY) 727 dbuf[budget++] = ' '; 728 729 budget += (unsigned int)lws_snprintf(dbuf + budget, 730 sizeof(dbuf) - budget, 731 "\"%s\"", map->colname); 732 } 733 734 735 if (js->sp != 1) 736 return LSJS_RESULT_ERROR; 737 j->map = map->child_map; 738 j->map_entries = map->child_map_size; 739 j->size = map->aux; 740 j->subsequent = 0; 741 j->map_entry = 0; 742 j->obj = js->st[js->sp - 1].obj; 743 j->dllpos = NULL; 744 if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA)) 745 /* we're actually at the same level */ 746 j->subsequent = 1; 747 j->idt = 1; 748 break; 749 default: 750 break; 751 } 752 753 switch (map->type) { 754 case LSMT_STRING_CHAR_ARRAY: 755 case LSMT_STRING_PTR: 756 /* 757 * This is a bit tricky... we have to escape the string 758 * which may 6x its length depending on what the 759 * contents are. 760 * 761 * We offset the unescaped string starting point first 762 */ 763 764 q += js->offset; 765 budget = strlen(q); /* how much unescaped is left */ 766 767 /* 768 * This is going to escape as much as it can fit, and 769 * let us know the amount of input that was consumed 770 * in "used". 771 */ 772 773 lws_json_purify((char *)buf, q, (int)len, &used); 774 m = strlen((const char *)buf); 775 buf += m; 776 len -= m; 777 js->remaining = budget - (unsigned int)used; 778 js->offset = (unsigned int)used; 779 if (!js->remaining) 780 js->offset = 0; 781 782 break; 783 default: 784 q += js->offset; 785 budget -= js->remaining; 786 787 if (budget > len) { 788 js->remaining = budget - len; 789 js->offset = len; 790 budget = len; 791 } else { 792 js->remaining = 0; 793 js->offset = 0; 794 } 795 796 memcpy(buf, q, budget); 797 buf += budget; 798 *buf = '\0'; 799 len -= budget; 800 break; 801 } 802 803 804 805 switch (map->type) { 806 case LSMT_STRING_CHAR_ARRAY: 807 case LSMT_STRING_PTR: 808 *buf++ = '\"'; 809 len--; 810 break; 811 case LSMT_SCHEMA: 812 continue; 813 default: 814 break; 815 } 816 817 if (js->remaining) 818 continue; 819up: 820 if (++j->map_entry < j->map_entries) 821 continue; 822 823 if (!js->sp) 824 continue; 825 js->sp--; 826 if (!js->sp) { 827 lws_struct_pretty(js, &buf, &len); 828 *buf++ = '}'; 829 len--; 830 lws_struct_pretty(js, &buf, &len); 831 break; 832 } 833 js->offset = 0; 834 j = &js->st[js->sp]; 835 map = &j->map[j->map_entry]; 836 837 if (map->type == LSMT_CHILD_PTR) { 838 lws_struct_pretty(js, &buf, &len); 839 *buf++ = '}'; 840 len--; 841 842 /* we have done the singular child pointer */ 843 844 js->offset = 0; 845 goto up; 846 } 847 848 if (map->type != LSMT_LIST) 849 continue; 850 /* 851 * we are coming back up to an array map, it means we should 852 * advance to the next array member if there is one 853 */ 854 855 lws_struct_pretty(js, &buf, &len); 856 *buf++ = '}'; 857 len--; 858 859 p = j->dllpos = j->dllpos->next; 860 if (j->dllpos) { 861 /* 862 * there was another item in the array to do... let's 863 * move on to that and do it 864 */ 865 *buf++ = ','; 866 len--; 867 lws_struct_pretty(js, &buf, &len); 868 js->offset = 0; 869 j = &js->st[++js->sp]; 870 j->map_entry = 0; 871 map = &j->map[j->map_entry]; 872 873 *buf++ = '{'; 874 len--; 875 lws_struct_pretty(js, &buf, &len); 876 877 j->subsequent = 0; 878 j->obj = ((char *)p) - j->map->ofs_clist; 879 continue; 880 } 881 882 /* there are no further items in the array */ 883 884 js->offset = 0; 885 lws_struct_pretty(js, &buf, &len); 886 *buf++ = ']'; 887 len--; 888 goto up; 889 } 890 891 *written = olen - len; 892 *buf = '\0'; /* convenience, a NUL after the official end */ 893 894 return LSJS_RESULT_FINISH; 895} 896