UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cx/json.h" 30 31 #include <string.h> 32 #include <assert.h> 33 #include <stdio.h> 34 #include <inttypes.h> 35 #include <ctype.h> 36 37 /* 38 * RFC 8259 39 * https://tools.ietf.org/html/rfc8259 40 */ 41 42 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING}; 43 44 static int json_cmp_objvalue(const void *l, const void *r) { 45 const CxJsonObjValue *left = l; 46 const CxJsonObjValue *right = r; 47 return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name)); 48 } 49 50 static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) { 51 assert(obj->type == CX_JSON_OBJECT); 52 CxJsonObjValue kv_dummy; 53 kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length); 54 return cx_array_binary_search( 55 obj->value.object.values, 56 obj->value.object.values_size, 57 sizeof(CxJsonObjValue), 58 &kv_dummy, 59 json_cmp_objvalue 60 ); 61 } 62 63 static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) { 64 assert(objv->type == CX_JSON_OBJECT); 65 const CxAllocator * const al = objv->allocator; 66 CxJsonObject *obj = &(objv->value.object); 67 68 // determine the index where we need to insert the new member 69 size_t index = cx_array_binary_search_sup( 70 obj->values, 71 obj->values_size, 72 sizeof(CxJsonObjValue), 73 &member, json_cmp_objvalue 74 ); 75 76 // is the name already present? 77 if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) { 78 // free the original value 79 cx_strfree_a(al, &obj->values[index].name); 80 cxJsonValueFree(obj->values[index].value); 81 // replace the item 82 obj->values[index] = member; 83 84 // nothing more to do 85 return 0; 86 } 87 88 // determine the old capacity and reserve for one more element 89 CxArrayReallocator arealloc = cx_array_reallocator(al, NULL); 90 size_t oldcap = obj->values_capacity; 91 if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1; 92 93 // check the new capacity, if we need to realloc the index array 94 size_t newcap = obj->values_capacity; 95 if (newcap > oldcap) { 96 if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) { 97 return 1; 98 } 99 } 100 101 // check if append or insert 102 if (index < obj->values_size) { 103 // move the other elements 104 memmove( 105 &obj->values[index+1], 106 &obj->values[index], 107 (obj->values_size - index) * sizeof(CxJsonObjValue) 108 ); 109 // increase indices for the moved elements 110 for (size_t i = 0; i < obj->values_size ; i++) { 111 if (obj->indices[i] >= index) { 112 obj->indices[i]++; 113 } 114 } 115 } 116 117 // insert the element and set the index 118 obj->values[index] = member; 119 obj->indices[obj->values_size] = index; 120 obj->values_size++; 121 122 return 0; 123 } 124 125 static void token_destroy(CxJsonToken *token) { 126 if (token->allocated) { 127 cx_strfree(&token->content); 128 } 129 } 130 131 static int num_isexp(const char *content, size_t length, size_t pos) { 132 if (pos >= length) { 133 return 0; 134 } 135 136 int ok = 0; 137 for (size_t i = pos; i < length; i++) { 138 char c = content[i]; 139 if (isdigit((unsigned char)c)) { 140 ok = 1; 141 } else if (i == pos) { 142 if (!(c == '+' || c == '-')) { 143 return 0; 144 } 145 } else { 146 return 0; 147 } 148 } 149 150 return ok; 151 } 152 153 static CxJsonTokenType token_numbertype(const char *content, size_t length) { 154 if (length == 0) return CX_JSON_TOKEN_ERROR; 155 156 if (content[0] != '-' && !isdigit((unsigned char)content[0])) { 157 return CX_JSON_TOKEN_ERROR; 158 } 159 160 CxJsonTokenType type = CX_JSON_TOKEN_INTEGER; 161 for (size_t i = 1; i < length; i++) { 162 if (content[i] == '.') { 163 if (type == CX_JSON_TOKEN_NUMBER) { 164 return CX_JSON_TOKEN_ERROR; // more than one decimal separator 165 } 166 type = CX_JSON_TOKEN_NUMBER; 167 } else if (content[i] == 'e' || content[i] == 'E') { 168 return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR; 169 } else if (!isdigit((unsigned char)content[i])) { 170 return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep 171 } 172 } 173 174 return type; 175 } 176 177 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { 178 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); 179 bool allocated = false; 180 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { 181 allocated = true; 182 str = cx_strcat_m(json->uncompleted.content, 1, str); 183 if (str.ptr == NULL) { // LCOV_EXCL_START 184 return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; 185 } // LCOV_EXCL_STOP 186 } 187 json->uncompleted = (CxJsonToken){0}; 188 CxJsonTokenType ttype; 189 if (isstring) { 190 ttype = CX_JSON_TOKEN_STRING; 191 } else { 192 cxstring s = cx_strcast(str); 193 if (!cx_strcmp(s, CX_STR("true")) || !cx_strcmp(s, CX_STR("false")) 194 || !cx_strcmp(s, CX_STR("null"))) { 195 ttype = CX_JSON_TOKEN_LITERAL; 196 } else { 197 ttype = token_numbertype(str.ptr, str.length); 198 } 199 } 200 if (ttype == CX_JSON_TOKEN_ERROR) { 201 if (allocated) { 202 cx_strfree(&str); 203 } 204 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {NULL, 0}}; 205 } 206 return (CxJsonToken){ttype, allocated, str}; 207 } 208 209 static CxJsonTokenType char2ttype(char c) { 210 switch (c) { 211 case '[': { 212 return CX_JSON_TOKEN_BEGIN_ARRAY; 213 } 214 case '{': { 215 return CX_JSON_TOKEN_BEGIN_OBJECT; 216 } 217 case ']': { 218 return CX_JSON_TOKEN_END_ARRAY; 219 } 220 case '}': { 221 return CX_JSON_TOKEN_END_OBJECT; 222 } 223 case ':': { 224 return CX_JSON_TOKEN_NAME_SEPARATOR; 225 } 226 case ',': { 227 return CX_JSON_TOKEN_VALUE_SEPARATOR; 228 } 229 case '""': { 230 return CX_JSON_TOKEN_STRING; 231 } 232 default: { 233 if (isspace((unsigned char)c)) { 234 return CX_JSON_TOKEN_SPACE; 235 } 236 } 237 } 238 return CX_JSON_NO_TOKEN; 239 } 240 241 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { 242 // check if there is data in the buffer 243 if (cxBufferEof(&json->buffer)) { 244 return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? 245 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; 246 } 247 248 // current token type and start index 249 CxJsonTokenType ttype = json->uncompleted.tokentype; 250 size_t token_part_start = json->buffer.pos; 251 252 bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING 253 && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; 254 255 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { 256 char c = json->buffer.space[i]; 257 if (ttype != CX_JSON_TOKEN_STRING) { 258 // currently non-string token 259 CxJsonTokenType ctype = char2ttype(c); // start of new token? 260 if (ttype == CX_JSON_NO_TOKEN) { 261 if (ctype == CX_JSON_TOKEN_SPACE) { 262 json->buffer.pos++; 263 continue; 264 } else if (ctype == CX_JSON_TOKEN_STRING) { 265 // begin string 266 ttype = CX_JSON_TOKEN_STRING; 267 token_part_start = i; 268 } else if (ctype != CX_JSON_NO_TOKEN) { 269 // single-char token 270 json->buffer.pos = i + 1; 271 *result = (CxJsonToken){ctype, false, {NULL, 0}}; 272 return CX_JSON_NO_ERROR; 273 } else { 274 ttype = CX_JSON_TOKEN_LITERAL; // number or literal 275 token_part_start = i; 276 } 277 } else { 278 // finish token 279 if (ctype != CX_JSON_NO_TOKEN) { 280 *result = token_create(json, false, token_part_start, i); 281 if (result->tokentype == CX_JSON_NO_TOKEN) { 282 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 283 } 284 if (result->tokentype == CX_JSON_TOKEN_ERROR) { 285 return CX_JSON_FORMAT_ERROR_NUMBER; 286 } 287 json->buffer.pos = i; 288 return CX_JSON_NO_ERROR; 289 } 290 } 291 } else { 292 // currently inside a string 293 if (escape_end_of_string) { 294 escape_end_of_string = false; 295 } else { 296 if (c == '""') { 297 *result = token_create(json, true, token_part_start, i + 1); 298 if (result->tokentype == CX_JSON_NO_TOKEN) { 299 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 300 } 301 json->buffer.pos = i + 1; 302 return CX_JSON_NO_ERROR; 303 } else if (c == '\\') { 304 escape_end_of_string = true; 305 } 306 } 307 } 308 } 309 310 if (ttype != CX_JSON_NO_TOKEN) { 311 // uncompleted token 312 size_t uncompleted_len = json->buffer.size - token_part_start; 313 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { 314 // current token is uncompleted 315 // save current token content 316 CxJsonToken uncompleted = { 317 ttype, true, 318 cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len)) 319 }; 320 if (uncompleted.content.ptr == NULL) { 321 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 322 } 323 json->uncompleted = uncompleted; 324 } else { 325 // previously we also had an uncompleted token 326 // combine the uncompleted token with the current token 327 assert(json->uncompleted.allocated); 328 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, 329 cx_strn(json->buffer.space + token_part_start, uncompleted_len)); 330 if (str.ptr == NULL) { 331 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 332 } 333 json->uncompleted.content = str; 334 } 335 // advance the buffer position - we saved the stuff in the uncompleted token 336 json->buffer.pos += uncompleted_len; 337 } 338 339 return CX_JSON_INCOMPLETE_DATA; 340 } 341 342 // converts a Unicode codepoint to utf8 343 static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) { 344 if (codepoint <= 0x7F) { 345 *output_buf = (char)codepoint; 346 return 1; 347 } else if (codepoint <= 0x7FF) { 348 output_buf[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F)); 349 output_buf[1] = (char)(0x80 | (codepoint & 0x3F)); 350 return 2; 351 } else if (codepoint <= 0xFFFF) { 352 output_buf[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F)); 353 output_buf[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); 354 output_buf[2] = (char)(0x80 | (codepoint & 0x3F)); 355 return 3; 356 } else if (codepoint <= 0x10FFFF) { 357 output_buf[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07)); 358 output_buf[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); 359 output_buf[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); 360 output_buf[3] = (char)(0x80 | (codepoint & 0x3F)); 361 return 4; 362 } 363 364 return 0; // LCOV_EXCL_LINE 365 } 366 367 // converts a utf16 surrogate pair to utf8 368 static inline uint32_t utf16pair_to_codepoint(uint16_t c0, uint16_t c1) { 369 return ((c0 - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000; 370 } 371 372 static unsigned unescape_unicode_string(cxstring str, char *utf8buf) { 373 // str is supposed to start with "\uXXXX" or "\uXXXX\uXXXX" 374 // remaining bytes in the string are ignored (str may be larger!) 375 376 if (str.length < 6 || str.ptr[0] != '\\' || str.ptr[1] != 'u') { 377 return 0; 378 } 379 380 unsigned utf8len = 0; 381 cxstring ustr1 = { str.ptr + 2, 4}; 382 uint16_t utf16a, utf16b; 383 if (!cx_strtou16_lc(ustr1, &utf16a, 16, "")) { 384 uint32_t codepoint; 385 if (utf16a < 0xD800 || utf16a > 0xE000) { 386 // character is in the Basic Multilingual Plane 387 // and encoded as a single utf16 char 388 codepoint = utf16a; 389 utf8len = codepoint_to_utf8(codepoint, utf8buf); 390 } else if (utf16a >= 0xD800 && utf16a <= 0xDBFF) { 391 // character is encoded as a surrogate pair 392 // get next 6 bytes 393 if (str.length >= 12) { 394 if (str.ptr[6] == '\\' && str.ptr[7] == 'u') { 395 cxstring ustr2 = { str.ptr+8, 4 }; 396 if (!cx_strtou16_lc(ustr2, &utf16b, 16, "") 397 && utf16b >= 0xDC00 && utf16b <= 0xDFFF) { 398 codepoint = utf16pair_to_codepoint(utf16a, utf16b); 399 utf8len = codepoint_to_utf8(codepoint, utf8buf); 400 } 401 } 402 } 403 } 404 } 405 return utf8len; 406 } 407 408 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { 409 // note: this function expects that str contains the enclosing quotes! 410 411 cxmutstr result; 412 result.length = 0; 413 result.ptr = cxMalloc(a, str.length - 1); 414 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE 415 416 bool u = false; 417 for (size_t i = 1; i < str.length - 1; i++) { 418 char c = str.ptr[i]; 419 if (u) { 420 u = false; 421 if (c == 'n') { 422 c = '\n'; 423 } else if (c == '""') { 424 c = '""'; 425 } else if (c == 't') { 426 c = '\t'; 427 } else if (c == 'r') { 428 c = '\r'; 429 } else if (c == '\\') { 430 c = '\\'; 431 } else if (c == '/') { 432 c = '/'; // always unescape, we don't need settings here 433 } else if (c == 'f') { 434 c = '\f'; 435 } else if (c == 'b') { 436 c = '\b'; 437 } else if (c == 'u') { 438 char utf8buf[4]; 439 unsigned utf8len = unescape_unicode_string( 440 cx_strn(str.ptr + i - 1, str.length + 1 - i), 441 utf8buf 442 ); 443 if(utf8len > 0) { 444 i += utf8len < 4 ? 4 : 10; 445 // add all bytes from utf8buf except the last char 446 // to the result (last char will be added below) 447 utf8len--; 448 c = utf8buf[utf8len]; 449 for (unsigned x = 0; x < utf8len; x++) { 450 result.ptr[result.length++] = utf8buf[x]; 451 } 452 } else { 453 // decoding failed, ignore the entire sequence 454 result.ptr[result.length++] = '\\'; 455 } 456 } else { 457 // TODO: discuss the behavior for unrecognized escape sequences 458 // most parsers throw an error here - we just ignore it 459 result.ptr[result.length++] = '\\'; 460 } 461 462 result.ptr[result.length++] = c; 463 } else { 464 if (c == '\\') { 465 u = true; 466 } else { 467 result.ptr[result.length++] = c; 468 } 469 } 470 } 471 result.ptr[result.length] = 0; 472 473 return result; 474 } 475 476 static cxmutstr escape_string(cxmutstr str, bool escape_slash) { 477 // note: this function produces the string without enclosing quotes 478 // the reason is that we don't want to allocate memory just for that 479 CxBuffer buf = {0}; 480 481 bool all_printable = true; 482 for (size_t i = 0; i < str.length; i++) { 483 unsigned char c = str.ptr[i]; 484 bool escape = c < 0x20 || c == '\\' || c == '""' 485 || (escape_slash && c == '/'); 486 487 if (all_printable && escape) { 488 size_t capa = str.length + 32; 489 char *space = cxMallocDefault(capa); 490 if (space == NULL) return cx_mutstrn(NULL, 0); 491 cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); 492 cxBufferWrite(str.ptr, 1, i, &buf); 493 all_printable = false; 494 } 495 if (escape) { 496 cxBufferPut(&buf, '\\'); 497 if (c == '\"') { 498 cxBufferPut(&buf, '\"'); 499 } else if (c == '\n') { 500 cxBufferPut(&buf, 'n'); 501 } else if (c == '\t') { 502 cxBufferPut(&buf, 't'); 503 } else if (c == '\r') { 504 cxBufferPut(&buf, 'r'); 505 } else if (c == '\\') { 506 cxBufferPut(&buf, '\\'); 507 } else if (c == '/') { 508 cxBufferPut(&buf, '/'); 509 } else if (c == '\f') { 510 cxBufferPut(&buf, 'f'); 511 } else if (c == '\b') { 512 cxBufferPut(&buf, 'b'); 513 } else { 514 char code[6]; 515 snprintf(code, sizeof(code), "u%04x", (unsigned int) c); 516 cxBufferPutString(&buf, code); 517 } 518 } else if (!all_printable) { 519 cxBufferPut(&buf, c); 520 } 521 } 522 if (!all_printable) { 523 str = cx_mutstrn(buf.space, buf.size); 524 } 525 cxBufferDestroy(&buf); 526 return str; 527 } 528 529 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { 530 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); 531 if (v == NULL) return NULL; // LCOV_EXCL_LINE 532 533 // initialize the value 534 v->type = type; 535 v->allocator = json->allocator; 536 if (type == CX_JSON_ARRAY) { 537 cx_array_initialize_a(json->allocator, v->value.array.array, 16); 538 if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE 539 } else if (type == CX_JSON_OBJECT) { 540 cx_array_initialize_a(json->allocator, v->value.object.values, 16); 541 v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); 542 if (v->value.object.values == NULL || 543 v->value.object.indices == NULL) 544 goto create_json_value_exit_error; // LCOV_EXCL_LINE 545 } 546 547 // add the new value to a possible parent 548 if (json->vbuf_size > 0) { 549 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; 550 assert(parent != NULL); 551 if (parent->type == CX_JSON_ARRAY) { 552 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); 553 if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) { 554 goto create_json_value_exit_error; // LCOV_EXCL_LINE 555 } 556 } else if (parent->type == CX_JSON_OBJECT) { 557 // the member was already created after parsing the name 558 assert(json->uncompleted_member.name.ptr != NULL); 559 json->uncompleted_member.value = v; 560 if (json_add_objvalue(parent, json->uncompleted_member)) { 561 goto create_json_value_exit_error; // LCOV_EXCL_LINE 562 } 563 json->uncompleted_member.name = (cxmutstr) {NULL, 0}; 564 } else { 565 assert(false); // LCOV_EXCL_LINE 566 } 567 } 568 569 // add the new value to the stack, if it is an array or object 570 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { 571 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); 572 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { 573 goto create_json_value_exit_error; // LCOV_EXCL_LINE 574 } 575 } 576 577 // if currently no value is parsed, this is now the value of interest 578 if (json->parsed == NULL) { 579 json->parsed = v; 580 } 581 582 return v; 583 // LCOV_EXCL_START 584 create_json_value_exit_error: 585 cxJsonValueFree(v); 586 return NULL; 587 // LCOV_EXCL_STOP 588 } 589 590 #define JP_STATE_VALUE_BEGIN 0 591 #define JP_STATE_VALUE_END 10 592 #define JP_STATE_VALUE_BEGIN_OBJ 1 593 #define JP_STATE_OBJ_SEP_OR_CLOSE 11 594 #define JP_STATE_VALUE_BEGIN_AR 2 595 #define JP_STATE_ARRAY_SEP_OR_CLOSE 12 596 #define JP_STATE_OBJ_NAME_OR_CLOSE 5 597 #define JP_STATE_OBJ_NAME 6 598 #define JP_STATE_OBJ_COLON 7 599 600 void cxJsonInit(CxJson *json, const CxAllocator *allocator) { 601 if (allocator == NULL) { 602 allocator = cxDefaultAllocator; 603 } 604 605 memset(json, 0, sizeof(CxJson)); 606 json->allocator = allocator; 607 608 json->states = json->states_internal; 609 json->states_capacity = cx_nmemb(json->states_internal); 610 json->states[0] = JP_STATE_VALUE_BEGIN; 611 json->states_size = 1; 612 613 json->vbuf = json->vbuf_internal; 614 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); 615 } 616 617 void cxJsonDestroy(CxJson *json) { 618 cxBufferDestroy(&json->buffer); 619 if (json->states != json->states_internal) { 620 cxFreeDefault(json->states); 621 } 622 if (json->vbuf != json->vbuf_internal) { 623 cxFreeDefault(json->vbuf); 624 } 625 cxJsonValueFree(json->parsed); 626 json->parsed = NULL; 627 if (json->uncompleted_member.name.ptr != NULL) { 628 cx_strfree_a(json->allocator, &json->uncompleted_member.name); 629 json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL}; 630 } 631 } 632 633 void cxJsonReset(CxJson *json) { 634 const CxAllocator *allocator = json->allocator; 635 cxJsonDestroy(json); 636 cxJsonInit(json, allocator); 637 } 638 639 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { 640 if (cxBufferEof(&json->buffer)) { 641 // reinitialize the buffer 642 cxBufferDestroy(&json->buffer); 643 cxBufferInit(&json->buffer, (char*) buf, size, 644 NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); 645 json->buffer.size = size; 646 return 0; 647 } else { 648 return size != cxBufferAppend(buf, 1, size, &json->buffer); 649 } 650 } 651 652 static void json_add_state(CxJson *json, int state) { 653 // we have guaranteed the necessary space with cx_array_simple_reserve() 654 // therefore, we can safely add the state in the simplest way possible 655 json->states[json->states_size++] = state; 656 } 657 658 #define return_rec(code) \ 659 token_destroy(&token); \ 660 return code 661 662 static enum cx_json_status json_parse(CxJson *json) { 663 // Reserve a pointer for a possibly read value 664 CxJsonValue *vbuf = NULL; 665 666 // grab the next token 667 CxJsonToken token; 668 { 669 enum cx_json_status ret = token_parse_next(json, &token); 670 if (ret != CX_JSON_NO_ERROR) { 671 return ret; 672 } 673 } 674 675 // pop the current state 676 assert(json->states_size > 0); 677 int state = json->states[--json->states_size]; 678 679 // guarantee that at least two more states fit on the stack 680 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); 681 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { 682 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 683 } 684 685 686 // 0 JP_STATE_VALUE_BEGIN value begin 687 // 10 JP_STATE_VALUE_END expect value end 688 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object) 689 // 11 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose 690 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array) 691 // 12 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose 692 // 5 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose 693 // 6 JP_STATE_OBJ_NAME object, expect name 694 // 7 JP_STATE_OBJ_COLON object, expect ':' 695 696 if (state < 3) { 697 // push expected end state to the stack 698 json_add_state(json, 10 + state); 699 switch (token.tokentype) { 700 case CX_JSON_TOKEN_BEGIN_ARRAY: { 701 if (json_create_value(json, CX_JSON_ARRAY) == NULL) { 702 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 703 } 704 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); 705 return_rec(CX_JSON_NO_ERROR); 706 } 707 case CX_JSON_TOKEN_BEGIN_OBJECT: { 708 if (json_create_value(json, CX_JSON_OBJECT) == NULL) { 709 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 710 } 711 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); 712 return_rec(CX_JSON_NO_ERROR); 713 } 714 case CX_JSON_TOKEN_STRING: { 715 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { 716 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 717 } 718 cxmutstr str = unescape_string(json->allocator, token.content); 719 if (str.ptr == NULL) { 720 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 721 } 722 vbuf->value.string = str; 723 return_rec(CX_JSON_NO_ERROR); 724 } 725 case CX_JSON_TOKEN_INTEGER: 726 case CX_JSON_TOKEN_NUMBER: { 727 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; 728 if (NULL == (vbuf = json_create_value(json, type))) { 729 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 730 } 731 if (type == CX_JSON_INTEGER) { 732 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { 733 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); 734 } 735 } else { 736 if (cx_strtod(token.content, &vbuf->value.number)) { 737 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); 738 } 739 } 740 return_rec(CX_JSON_NO_ERROR); 741 } 742 case CX_JSON_TOKEN_LITERAL: { 743 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { 744 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 745 } 746 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { 747 vbuf->value.literal = CX_JSON_TRUE; 748 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { 749 vbuf->value.literal = CX_JSON_FALSE; 750 } else { 751 vbuf->value.literal = CX_JSON_NULL; 752 } 753 return_rec(CX_JSON_NO_ERROR); 754 } 755 default: { 756 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 757 } 758 } 759 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) { 760 // expect ',' or ']' 761 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { 762 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); 763 return_rec(CX_JSON_NO_ERROR); 764 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { 765 // discard the array from the value buffer 766 json->vbuf_size--; 767 return_rec(CX_JSON_NO_ERROR); 768 } else { 769 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 770 } 771 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { 772 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { 773 // discard the obj from the value buffer 774 json->vbuf_size--; 775 return_rec(CX_JSON_NO_ERROR); 776 } else { 777 // expect string 778 if (token.tokentype != CX_JSON_TOKEN_STRING) { 779 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 780 } 781 782 // add new entry 783 cxmutstr name = unescape_string(json->allocator, token.content); 784 if (name.ptr == NULL) { 785 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 786 } 787 assert(json->uncompleted_member.name.ptr == NULL); 788 json->uncompleted_member.name = name; 789 assert(json->vbuf_size > 0); 790 791 // next state 792 json_add_state(json, JP_STATE_OBJ_COLON); 793 return_rec(CX_JSON_NO_ERROR); 794 } 795 } else if (state == JP_STATE_OBJ_COLON) { 796 // expect ':' 797 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) { 798 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 799 } 800 // next state 801 json_add_state(json, JP_STATE_VALUE_BEGIN_OBJ); 802 return_rec(CX_JSON_NO_ERROR); 803 } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) { 804 // expect ',' or '}' 805 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { 806 json_add_state(json, JP_STATE_OBJ_NAME); 807 return_rec(CX_JSON_NO_ERROR); 808 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { 809 // discard the obj from the value buffer 810 json->vbuf_size--; 811 return_rec(CX_JSON_NO_ERROR); 812 } else { 813 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 814 } 815 } else { 816 // should be unreachable 817 assert(false); 818 return_rec(-1); 819 } 820 } 821 822 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { 823 // check if buffer has been filled 824 if (json->buffer.space == NULL) { 825 return CX_JSON_NULL_DATA; 826 } 827 828 // initialize output value 829 *value = &cx_json_value_nothing; 830 831 // parse data 832 CxJsonStatus result; 833 do { 834 result = json_parse(json); 835 if (result == CX_JSON_NO_ERROR && json->states_size == 1) { 836 // final state reached 837 assert(json->states[0] == JP_STATE_VALUE_END); 838 assert(json->vbuf_size == 0); 839 840 // write output value 841 *value = json->parsed; 842 json->parsed = NULL; 843 844 // re-initialize state machine 845 json->states[0] = JP_STATE_VALUE_BEGIN; 846 847 return CX_JSON_NO_ERROR; 848 } 849 } while (result == CX_JSON_NO_ERROR); 850 851 // the parser might think there is no data 852 // but when we did not reach the final state, 853 // we know that there must be more to come 854 if (result == CX_JSON_NO_DATA && json->states_size > 1) { 855 return CX_JSON_INCOMPLETE_DATA; 856 } 857 858 return result; 859 } 860 861 void cxJsonValueFree(CxJsonValue *value) { 862 if (value == NULL || value->type == CX_JSON_NOTHING) return; 863 switch (value->type) { 864 case CX_JSON_OBJECT: { 865 CxJsonObject obj = value->value.object; 866 for (size_t i = 0; i < obj.values_size; i++) { 867 cxJsonValueFree(obj.values[i].value); 868 cx_strfree_a(value->allocator, &obj.values[i].name); 869 } 870 cxFree(value->allocator, obj.values); 871 cxFree(value->allocator, obj.indices); 872 break; 873 } 874 case CX_JSON_ARRAY: { 875 CxJsonArray array = value->value.array; 876 for (size_t i = 0; i < array.array_size; i++) { 877 cxJsonValueFree(array.array[i]); 878 } 879 cxFree(value->allocator, array.array); 880 break; 881 } 882 case CX_JSON_STRING: { 883 cxFree(value->allocator, value->value.string.ptr); 884 break; 885 } 886 default: { 887 break; 888 } 889 } 890 cxFree(value->allocator, value); 891 } 892 893 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) { 894 if (allocator == NULL) allocator = cxDefaultAllocator; 895 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 896 if (v == NULL) return NULL; 897 v->allocator = allocator; 898 v->type = CX_JSON_OBJECT; 899 cx_array_initialize_a(allocator, v->value.object.values, 16); 900 if (v->value.object.values == NULL) { // LCOV_EXCL_START 901 cxFree(allocator, v); 902 return NULL; 903 // LCOV_EXCL_STOP 904 } 905 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); 906 if (v->value.object.indices == NULL) { // LCOV_EXCL_START 907 cxFree(allocator, v->value.object.values); 908 cxFree(allocator, v); 909 return NULL; 910 // LCOV_EXCL_STOP 911 } 912 return v; 913 } 914 915 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { 916 if (allocator == NULL) allocator = cxDefaultAllocator; 917 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 918 if (v == NULL) return NULL; 919 v->allocator = allocator; 920 v->type = CX_JSON_ARRAY; 921 cx_array_initialize_a(allocator, v->value.array.array, 16); 922 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } 923 return v; 924 } 925 926 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { 927 if (allocator == NULL) allocator = cxDefaultAllocator; 928 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 929 if (v == NULL) return NULL; 930 v->allocator = allocator; 931 v->type = CX_JSON_NUMBER; 932 v->value.number = num; 933 return v; 934 } 935 936 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { 937 if (allocator == NULL) allocator = cxDefaultAllocator; 938 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 939 if (v == NULL) return NULL; 940 v->allocator = allocator; 941 v->type = CX_JSON_INTEGER; 942 v->value.integer = num; 943 return v; 944 } 945 946 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) { 947 return cxJsonCreateCxString(allocator, cx_str(str)); 948 } 949 950 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) { 951 if (allocator == NULL) allocator = cxDefaultAllocator; 952 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 953 if (v == NULL) return NULL; 954 v->allocator = allocator; 955 v->type = CX_JSON_STRING; 956 cxmutstr s = cx_strdup_a(allocator, str); 957 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } 958 v->value.string = s; 959 return v; 960 } 961 962 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { 963 if (allocator == NULL) allocator = cxDefaultAllocator; 964 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 965 if (v == NULL) return NULL; 966 v->allocator = allocator; 967 v->type = CX_JSON_LITERAL; 968 v->value.literal = lit; 969 return v; 970 } 971 972 // LCOV_EXCL_START 973 // never called as long as malloc() does not return NULL 974 static void json_arr_free_temp(CxJsonValue** values, size_t count) { 975 for (size_t i = 0; i < count; i++) { 976 if (values[i] == NULL) break; 977 cxJsonValueFree(values[i]); 978 } 979 cxFreeDefault(values); 980 } 981 // LCOV_EXCL_STOP 982 983 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { 984 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 985 if (values == NULL) return -1; 986 for (size_t i = 0; i < count; i++) { 987 values[i] = cxJsonCreateNumber(arr->allocator, num[i]); 988 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 989 } 990 int ret = cxJsonArrAddValues(arr, values, count); 991 cxFreeDefault(values); 992 return ret; 993 } 994 995 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) { 996 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 997 if (values == NULL) return -1; 998 for (size_t i = 0; i < count; i++) { 999 values[i] = cxJsonCreateInteger(arr->allocator, num[i]); 1000 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1001 } 1002 int ret = cxJsonArrAddValues(arr, values, count); 1003 cxFreeDefault(values); 1004 return ret; 1005 } 1006 1007 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) { 1008 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1009 if (values == NULL) return -1; 1010 for (size_t i = 0; i < count; i++) { 1011 values[i] = cxJsonCreateString(arr->allocator, str[i]); 1012 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1013 } 1014 int ret = cxJsonArrAddValues(arr, values, count); 1015 cxFreeDefault(values); 1016 return ret; 1017 } 1018 1019 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { 1020 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1021 if (values == NULL) return -1; 1022 for (size_t i = 0; i < count; i++) { 1023 values[i] = cxJsonCreateCxString(arr->allocator, str[i]); 1024 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1025 } 1026 int ret = cxJsonArrAddValues(arr, values, count); 1027 cxFreeDefault(values); 1028 return ret; 1029 } 1030 1031 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) { 1032 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1033 if (values == NULL) return -1; 1034 for (size_t i = 0; i < count; i++) { 1035 values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); 1036 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1037 } 1038 int ret = cxJsonArrAddValues(arr, values, count); 1039 cxFreeDefault(values); 1040 return ret; 1041 } 1042 1043 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { 1044 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); 1045 assert(arr->type == CX_JSON_ARRAY); 1046 return cx_array_simple_copy_a(&value_realloc, 1047 arr->value.array.array, 1048 arr->value.array.array_size, 1049 val, count 1050 ); 1051 } 1052 1053 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { 1054 cxmutstr k = cx_strdup_a(obj->allocator, name); 1055 if (k.ptr == NULL) return -1; 1056 CxJsonObjValue kv = {k, child}; 1057 if (json_add_objvalue(obj, kv)) { 1058 cx_strfree_a(obj->allocator, &k); 1059 return 1; 1060 } else { 1061 return 0; 1062 } 1063 } 1064 1065 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { 1066 CxJsonValue* v = cxJsonCreateObj(obj->allocator); 1067 if (v == NULL) return NULL; 1068 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1069 return v; 1070 } 1071 1072 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) { 1073 CxJsonValue* v = cxJsonCreateArr(obj->allocator); 1074 if (v == NULL) return NULL; 1075 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1076 return v; 1077 } 1078 1079 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) { 1080 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num); 1081 if (v == NULL) return NULL; 1082 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1083 return v; 1084 } 1085 1086 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) { 1087 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num); 1088 if (v == NULL) return NULL; 1089 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1090 return v; 1091 } 1092 1093 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) { 1094 CxJsonValue* v = cxJsonCreateString(obj->allocator, str); 1095 if (v == NULL) return NULL; 1096 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1097 return v; 1098 } 1099 1100 CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) { 1101 CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str); 1102 if (v == NULL) return NULL; 1103 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1104 return v; 1105 } 1106 1107 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) { 1108 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit); 1109 if (v == NULL) return NULL; 1110 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} 1111 return v; 1112 } 1113 1114 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { 1115 if (index >= value->value.array.array_size) { 1116 return &cx_json_value_nothing; 1117 } 1118 return value->value.array.array[index]; 1119 } 1120 1121 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { 1122 if (index >= value->value.array.array_size) { 1123 return NULL; 1124 } 1125 CxJsonValue *ret = value->value.array.array[index]; 1126 // TODO: replace with a low level cx_array_remove() 1127 size_t count = value->value.array.array_size - index - 1; 1128 if (count > 0) { 1129 memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); 1130 } 1131 value->value.array.array_size--; 1132 return ret; 1133 } 1134 1135 char *cxJsonAsString(const CxJsonValue *value) { 1136 return value->value.string.ptr; 1137 } 1138 1139 cxstring cxJsonAsCxString(const CxJsonValue *value) { 1140 return cx_strcast(value->value.string); 1141 } 1142 1143 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { 1144 return value->value.string; 1145 } 1146 1147 double cxJsonAsDouble(const CxJsonValue *value) { 1148 if (value->type == CX_JSON_INTEGER) { 1149 return (double) value->value.integer; 1150 } else { 1151 return value->value.number; 1152 } 1153 } 1154 1155 int64_t cxJsonAsInteger(const CxJsonValue *value) { 1156 if (value->type == CX_JSON_INTEGER) { 1157 return value->value.integer; 1158 } else { 1159 return (int64_t) value->value.number; 1160 } 1161 } 1162 1163 CxIterator cxJsonArrIter(const CxJsonValue *value) { 1164 return cxIteratorPtr( 1165 value->value.array.array, 1166 value->value.array.array_size, 1167 true // arrays need to keep order 1168 ); 1169 } 1170 1171 CxIterator cxJsonObjIter(const CxJsonValue *value) { 1172 return cxIterator( 1173 value->value.object.values, 1174 sizeof(CxJsonObjValue), 1175 value->value.object.values_size, 1176 true // TODO: objects do not always need to keep order 1177 ); 1178 } 1179 1180 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { 1181 size_t index = json_find_objvalue(value, name); 1182 if (index >= value->value.object.values_size) { 1183 return &cx_json_value_nothing; 1184 } else { 1185 return value->value.object.values[index].value; 1186 } 1187 } 1188 1189 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { 1190 size_t index = json_find_objvalue(value, name); 1191 if (index >= value->value.object.values_size) { 1192 return NULL; 1193 } else { 1194 CxJsonObjValue kv = value->value.object.values[index]; 1195 cx_strfree_a(value->allocator, &kv.name); 1196 // TODO: replace with cx_array_remove() / cx_array_remove_fast() 1197 value->value.object.values_size--; 1198 memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); 1199 return kv.value; 1200 } 1201 } 1202 1203 CxJsonWriter cxJsonWriterCompact(void) { 1204 return (CxJsonWriter) { 1205 false, 1206 true, 1207 6, 1208 false, 1209 4, 1210 false 1211 }; 1212 } 1213 1214 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { 1215 return (CxJsonWriter) { 1216 true, 1217 true, 1218 6, 1219 use_spaces, 1220 4, 1221 false 1222 }; 1223 } 1224 1225 static int cx_json_writer_indent( 1226 void *target, 1227 cx_write_func wfunc, 1228 const CxJsonWriter *settings, 1229 unsigned int depth 1230 ) { 1231 if (depth == 0) return 0; 1232 1233 // determine the width and characters to use 1234 const char* indent; // for 32 prepared chars 1235 size_t width = depth; 1236 if (settings->indent_space) { 1237 if (settings->indent == 0) return 0; 1238 width *= settings->indent; 1239 indent = " "; 1240 } else { 1241 indent = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; 1242 } 1243 1244 // calculate the number of write calls and write 1245 size_t full = width / 32; 1246 size_t remaining = width % 32; 1247 for (size_t i = 0; i < full; i++) { 1248 if (32 != wfunc(indent, 1, 32, target)) return 1; 1249 } 1250 if (remaining != wfunc(indent, 1, remaining, target)) return 1; 1251 1252 return 0; 1253 } 1254 1255 1256 int cx_json_write_rec( 1257 void *target, 1258 const CxJsonValue *value, 1259 cx_write_func wfunc, 1260 const CxJsonWriter *settings, 1261 unsigned int depth 1262 ) { 1263 // keep track of written items 1264 // the idea is to reduce the number of jumps for error checking 1265 size_t actual = 0, expected = 0; 1266 1267 // small buffer for number to string conversions 1268 char numbuf[40]; 1269 1270 // recursively write the values 1271 switch (value->type) { 1272 case CX_JSON_OBJECT: { 1273 const char *begin_obj = "{\n"; 1274 if (settings->pretty) { 1275 actual += wfunc(begin_obj, 1, 2, target); 1276 expected += 2; 1277 } else { 1278 actual += wfunc(begin_obj, 1, 1, target); 1279 expected++; 1280 } 1281 depth++; 1282 size_t elem_count = value->value.object.values_size; 1283 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { 1284 // get the member either via index array or directly 1285 size_t elem_idx = settings->sort_members 1286 ? look_idx 1287 : value->value.object.indices[look_idx]; 1288 CxJsonObjValue *member = &value->value.object.values[elem_idx]; 1289 if (settings->sort_members) { 1290 depth++;depth--; 1291 } 1292 1293 // possible indentation 1294 if (settings->pretty) { 1295 if (cx_json_writer_indent(target, wfunc, settings, depth)) { 1296 return 1; // LCOV_EXCL_LINE 1297 } 1298 } 1299 1300 // the name 1301 actual += wfunc("\"", 1, 1, target); 1302 cxmutstr name = escape_string(member->name, settings->escape_slash); 1303 actual += wfunc(name.ptr, 1, name.length, target); 1304 if (name.ptr != member->name.ptr) { 1305 cx_strfree(&name); 1306 } 1307 actual += wfunc("\"", 1, 1, target); 1308 const char *obj_name_sep = ": "; 1309 if (settings->pretty) { 1310 actual += wfunc(obj_name_sep, 1, 2, target); 1311 expected += 4 + member->name.length; 1312 } else { 1313 actual += wfunc(obj_name_sep, 1, 1, target); 1314 expected += 3 + member->name.length; 1315 } 1316 1317 // the value 1318 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; 1319 1320 // end of object-value 1321 if (look_idx < elem_count - 1) { 1322 const char *obj_value_sep = ",\n"; 1323 if (settings->pretty) { 1324 actual += wfunc(obj_value_sep, 1, 2, target); 1325 expected += 2; 1326 } else { 1327 actual += wfunc(obj_value_sep, 1, 1, target); 1328 expected++; 1329 } 1330 } else { 1331 if (settings->pretty) { 1332 actual += wfunc("\n", 1, 1, target); 1333 expected ++; 1334 } 1335 } 1336 } 1337 depth--; 1338 if (settings->pretty) { 1339 if (cx_json_writer_indent(target, wfunc, settings, depth)) return 1; 1340 } 1341 actual += wfunc("}", 1, 1, target); 1342 expected++; 1343 break; 1344 } 1345 case CX_JSON_ARRAY: { 1346 actual += wfunc("[", 1, 1, target); 1347 expected++; 1348 CxIterator iter = cxJsonArrIter(value); 1349 cx_foreach(CxJsonValue*, element, iter) { 1350 if (cx_json_write_rec( 1351 target, element, 1352 wfunc, settings, depth) 1353 ) return 1; 1354 1355 if (iter.index < iter.elem_count - 1) { 1356 const char *arr_value_sep = ", "; 1357 if (settings->pretty) { 1358 actual += wfunc(arr_value_sep, 1, 2, target); 1359 expected += 2; 1360 } else { 1361 actual += wfunc(arr_value_sep, 1, 1, target); 1362 expected++; 1363 } 1364 } 1365 } 1366 actual += wfunc("]", 1, 1, target); 1367 expected++; 1368 break; 1369 } 1370 case CX_JSON_STRING: { 1371 actual += wfunc("\"", 1, 1, target); 1372 cxmutstr str = escape_string(value->value.string, settings->escape_slash); 1373 actual += wfunc(str.ptr, 1, str.length, target); 1374 if (str.ptr != value->value.string.ptr) { 1375 cx_strfree(&str); 1376 } 1377 actual += wfunc("\"", 1, 1, target); 1378 expected += 2 + value->value.string.length; 1379 break; 1380 } 1381 case CX_JSON_NUMBER: { 1382 int precision = settings->frac_max_digits; 1383 // because of the way how %g is defined, we need to 1384 // double the precision and truncate ourselves 1385 precision = 1 + (precision > 15 ? 30 : 2 * precision); 1386 snprintf(numbuf, 40, "%.*g", precision, value->value.number); 1387 char *dot, *exp; 1388 unsigned char max_digits; 1389 // find the decimal separator and hope that it's one of . or , 1390 dot = strchr(numbuf, '.'); 1391 if (dot == NULL) { 1392 dot = strchr(numbuf, ','); 1393 } 1394 if (dot == NULL) { 1395 // no decimal separator found 1396 // output everything until a possible exponent 1397 max_digits = 30; 1398 dot = numbuf; 1399 } else { 1400 // found a decimal separator 1401 // output everything until the separator 1402 // and set max digits to what the settings say 1403 size_t len = dot - numbuf; 1404 actual += wfunc(numbuf, 1, len, target); 1405 expected += len; 1406 max_digits = settings->frac_max_digits; 1407 if (max_digits > 15) { 1408 max_digits = 15; 1409 } 1410 // locale independent separator 1411 if (max_digits > 0) { 1412 actual += wfunc(".", 1, 1, target); 1413 expected++; 1414 } 1415 dot++; 1416 } 1417 // find the exponent 1418 exp = strchr(dot, 'e'); 1419 if (exp == NULL) { 1420 // no exponent - output the rest 1421 if (max_digits > 0) { 1422 size_t len = strlen(dot); 1423 if (len > max_digits) { 1424 len = max_digits; 1425 } 1426 actual += wfunc(dot, 1, len, target); 1427 expected += len; 1428 } 1429 } else { 1430 // exponent found - truncate the frac digits 1431 // and then output the rest 1432 if (max_digits > 0) { 1433 size_t len = exp - dot - 1; 1434 if (len > max_digits) { 1435 len = max_digits; 1436 } 1437 actual += wfunc(dot, 1, len, target); 1438 expected += len; 1439 } 1440 actual += wfunc("e", 1, 1, target); 1441 expected++; 1442 exp++; 1443 size_t len = strlen(exp); 1444 actual += wfunc(exp, 1, len, target); 1445 expected += len; 1446 } 1447 break; 1448 } 1449 case CX_JSON_INTEGER: { 1450 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); 1451 size_t len = strlen(numbuf); 1452 actual += wfunc(numbuf, 1, len, target); 1453 expected += len; 1454 break; 1455 } 1456 case CX_JSON_LITERAL: { 1457 if (value->value.literal == CX_JSON_TRUE) { 1458 actual += wfunc("true", 1, 4, target); 1459 expected += 4; 1460 } else if (value->value.literal == CX_JSON_FALSE) { 1461 actual += wfunc("false", 1, 5, target); 1462 expected += 5; 1463 } else { 1464 actual += wfunc("null", 1, 4, target); 1465 expected += 4; 1466 } 1467 break; 1468 } 1469 case CX_JSON_NOTHING: { 1470 // deliberately supported as an empty string! 1471 // users might want to just write the result 1472 // of a get operation without testing the value 1473 // and therefore this should not blow up 1474 break; 1475 } 1476 default: assert(false); // LCOV_EXCL_LINE 1477 } 1478 1479 return expected != actual; 1480 } 1481 1482 int cxJsonWrite( 1483 void *target, 1484 const CxJsonValue *value, 1485 cx_write_func wfunc, 1486 const CxJsonWriter *settings 1487 ) { 1488 assert(target != NULL); 1489 assert(value != NULL); 1490 assert(wfunc != NULL); 1491 1492 CxJsonWriter writer_default = cxJsonWriterCompact(); 1493 if (settings == NULL) { 1494 settings = &writer_default; 1495 } 1496 return cx_json_write_rec(target, value, wfunc, settings, 0); 1497 } 1498