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; // LCOV_EXCL_LINE 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 - 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++] = '\\'; // LCOV_EXCL_LINE 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 if (buf == NULL) buf = ""; // buffer must not be initialized with NULL 644 cxBufferInit(&json->buffer, (char*) buf, size, 645 NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); 646 json->buffer.size = size; 647 return 0; 648 } else { 649 return size != cxBufferAppend(buf, 1, size, &json->buffer); 650 } 651 } 652 653 static void json_add_state(CxJson *json, int state) { 654 // we have guaranteed the necessary space with cx_array_simple_reserve() 655 // therefore, we can safely add the state in the simplest way possible 656 json->states[json->states_size++] = state; 657 } 658 659 #define return_rec(code) \ 660 token_destroy(&token); \ 661 return code 662 663 static enum cx_json_status json_parse(CxJson *json) { 664 // Reserve a pointer for a possibly read value 665 CxJsonValue *vbuf = NULL; 666 667 // grab the next token 668 CxJsonToken token; 669 { 670 enum cx_json_status ret = token_parse_next(json, &token); 671 if (ret != CX_JSON_NO_ERROR) { 672 return ret; 673 } 674 } 675 676 // pop the current state 677 assert(json->states_size > 0); 678 int state = json->states[--json->states_size]; 679 680 // guarantee that at least two more states fit on the stack 681 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); 682 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { 683 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE 684 } 685 686 687 // 0 JP_STATE_VALUE_BEGIN value begin 688 // 10 JP_STATE_VALUE_END expect value end 689 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object) 690 // 11 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose 691 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array) 692 // 12 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose 693 // 5 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose 694 // 6 JP_STATE_OBJ_NAME object, expect name 695 // 7 JP_STATE_OBJ_COLON object, expect ':' 696 697 if (state < 3) { 698 // push expected end state to the stack 699 json_add_state(json, 10 + state); 700 switch (token.tokentype) { 701 case CX_JSON_TOKEN_BEGIN_ARRAY: { 702 if (json_create_value(json, CX_JSON_ARRAY) == NULL) { 703 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 704 } 705 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); 706 return_rec(CX_JSON_NO_ERROR); 707 } 708 case CX_JSON_TOKEN_BEGIN_OBJECT: { 709 if (json_create_value(json, CX_JSON_OBJECT) == NULL) { 710 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 711 } 712 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); 713 return_rec(CX_JSON_NO_ERROR); 714 } 715 case CX_JSON_TOKEN_STRING: { 716 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { 717 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 718 } 719 cxmutstr str = unescape_string(json->allocator, token.content); 720 if (str.ptr == NULL) { 721 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 722 } 723 vbuf->value.string = str; 724 return_rec(CX_JSON_NO_ERROR); 725 } 726 case CX_JSON_TOKEN_INTEGER: 727 case CX_JSON_TOKEN_NUMBER: { 728 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; 729 if (NULL == (vbuf = json_create_value(json, type))) { 730 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 731 } 732 if (type == CX_JSON_INTEGER) { 733 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { 734 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); 735 } 736 } else { 737 if (cx_strtod(token.content, &vbuf->value.number)) { 738 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod() 739 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE 740 } 741 } 742 return_rec(CX_JSON_NO_ERROR); 743 } 744 case CX_JSON_TOKEN_LITERAL: { 745 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { 746 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 747 } 748 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { 749 vbuf->value.literal = CX_JSON_TRUE; 750 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { 751 vbuf->value.literal = CX_JSON_FALSE; 752 } else { 753 vbuf->value.literal = CX_JSON_NULL; 754 } 755 return_rec(CX_JSON_NO_ERROR); 756 } 757 default: { 758 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 759 } 760 } 761 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) { 762 // expect ',' or ']' 763 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { 764 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); 765 return_rec(CX_JSON_NO_ERROR); 766 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { 767 // discard the array from the value buffer 768 json->vbuf_size--; 769 return_rec(CX_JSON_NO_ERROR); 770 } else { 771 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 772 } 773 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { 774 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { 775 // discard the obj from the value buffer 776 json->vbuf_size--; 777 return_rec(CX_JSON_NO_ERROR); 778 } else { 779 // expect string 780 if (token.tokentype != CX_JSON_TOKEN_STRING) { 781 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 782 } 783 784 // add new entry 785 cxmutstr name = unescape_string(json->allocator, token.content); 786 if (name.ptr == NULL) { 787 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 788 } 789 assert(json->uncompleted_member.name.ptr == NULL); 790 json->uncompleted_member.name = name; 791 assert(json->vbuf_size > 0); 792 793 // next state 794 json_add_state(json, JP_STATE_OBJ_COLON); 795 return_rec(CX_JSON_NO_ERROR); 796 } 797 } else if (state == JP_STATE_OBJ_COLON) { 798 // expect ':' 799 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) { 800 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 801 } 802 // next state 803 json_add_state(json, JP_STATE_VALUE_BEGIN_OBJ); 804 return_rec(CX_JSON_NO_ERROR); 805 } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) { 806 // expect ',' or '}' 807 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { 808 json_add_state(json, JP_STATE_OBJ_NAME); 809 return_rec(CX_JSON_NO_ERROR); 810 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { 811 // discard the obj from the value buffer 812 json->vbuf_size--; 813 return_rec(CX_JSON_NO_ERROR); 814 } else { 815 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 816 } 817 } else { 818 // should be unreachable 819 assert(false); 820 return_rec(-1); // LCOV_EXCL_LINE 821 } 822 } 823 824 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { 825 // initialize output value 826 *value = &cx_json_value_nothing; 827 828 // check if the buffer has been filled 829 if (json->buffer.space == NULL) { 830 return CX_JSON_NULL_DATA; 831 } 832 833 // parse data 834 CxJsonStatus result; 835 do { 836 result = json_parse(json); 837 if (result == CX_JSON_NO_ERROR && json->states_size == 1) { 838 // final state reached 839 assert(json->states[0] == JP_STATE_VALUE_END); 840 assert(json->vbuf_size == 0); 841 842 // write output value 843 *value = json->parsed; 844 json->parsed = NULL; 845 846 // re-initialize state machine 847 json->states[0] = JP_STATE_VALUE_BEGIN; 848 849 return CX_JSON_NO_ERROR; 850 } 851 } while (result == CX_JSON_NO_ERROR); 852 853 // the parser might think there is no data 854 // but when we did not reach the final state, 855 // we know that there must be more to come 856 if (result == CX_JSON_NO_DATA && json->states_size > 1) { 857 return CX_JSON_INCOMPLETE_DATA; 858 } 859 860 return result; 861 } 862 863 void cxJsonValueFree(CxJsonValue *value) { 864 if (value == NULL || value->type == CX_JSON_NOTHING) return; 865 switch (value->type) { 866 case CX_JSON_OBJECT: { 867 CxJsonObject obj = value->value.object; 868 for (size_t i = 0; i < obj.values_size; i++) { 869 cxJsonValueFree(obj.values[i].value); 870 cx_strfree_a(value->allocator, &obj.values[i].name); 871 } 872 cxFree(value->allocator, obj.values); 873 cxFree(value->allocator, obj.indices); 874 break; 875 } 876 case CX_JSON_ARRAY: { 877 CxJsonArray array = value->value.array; 878 for (size_t i = 0; i < array.array_size; i++) { 879 cxJsonValueFree(array.array[i]); 880 } 881 cxFree(value->allocator, array.array); 882 break; 883 } 884 case CX_JSON_STRING: { 885 cxFree(value->allocator, value->value.string.ptr); 886 break; 887 } 888 default: { 889 break; 890 } 891 } 892 cxFree(value->allocator, value); 893 } 894 895 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) { 896 if (allocator == NULL) allocator = cxDefaultAllocator; 897 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 898 if (v == NULL) return NULL; 899 v->allocator = allocator; 900 v->type = CX_JSON_OBJECT; 901 cx_array_initialize_a(allocator, v->value.object.values, 16); 902 if (v->value.object.values == NULL) { // LCOV_EXCL_START 903 cxFree(allocator, v); 904 return NULL; 905 // LCOV_EXCL_STOP 906 } 907 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); 908 if (v->value.object.indices == NULL) { // LCOV_EXCL_START 909 cxFree(allocator, v->value.object.values); 910 cxFree(allocator, v); 911 return NULL; 912 // LCOV_EXCL_STOP 913 } 914 return v; 915 } 916 917 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { 918 if (allocator == NULL) allocator = cxDefaultAllocator; 919 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 920 if (v == NULL) return NULL; 921 v->allocator = allocator; 922 v->type = CX_JSON_ARRAY; 923 cx_array_initialize_a(allocator, v->value.array.array, 16); 924 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } 925 return v; 926 } 927 928 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { 929 if (allocator == NULL) allocator = cxDefaultAllocator; 930 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 931 if (v == NULL) return NULL; 932 v->allocator = allocator; 933 v->type = CX_JSON_NUMBER; 934 v->value.number = num; 935 return v; 936 } 937 938 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { 939 if (allocator == NULL) allocator = cxDefaultAllocator; 940 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 941 if (v == NULL) return NULL; 942 v->allocator = allocator; 943 v->type = CX_JSON_INTEGER; 944 v->value.integer = num; 945 return v; 946 } 947 948 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) { 949 if (allocator == NULL) allocator = cxDefaultAllocator; 950 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 951 if (v == NULL) return NULL; 952 v->allocator = allocator; 953 v->type = CX_JSON_STRING; 954 cxmutstr s = cx_strdup_a(allocator, str); 955 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } 956 v->value.string = s; 957 return v; 958 } 959 960 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { 961 if (allocator == NULL) allocator = cxDefaultAllocator; 962 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 963 if (v == NULL) return NULL; 964 v->allocator = allocator; 965 v->type = CX_JSON_LITERAL; 966 v->value.literal = lit; 967 return v; 968 } 969 970 // LCOV_EXCL_START 971 // never called as long as malloc() does not return NULL 972 static void json_arr_free_temp(CxJsonValue** values, size_t count) { 973 for (size_t i = 0; i < count; i++) { 974 if (values[i] == NULL) break; 975 cxJsonValueFree(values[i]); 976 } 977 cxFreeDefault(values); 978 } 979 // LCOV_EXCL_STOP 980 981 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { 982 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 983 if (values == NULL) return -1; 984 for (size_t i = 0; i < count; i++) { 985 values[i] = cxJsonCreateNumber(arr->allocator, num[i]); 986 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 987 } 988 int ret = cxJsonArrAddValues(arr, values, count); 989 cxFreeDefault(values); 990 return ret; 991 } 992 993 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) { 994 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 995 if (values == NULL) return -1; 996 for (size_t i = 0; i < count; i++) { 997 values[i] = cxJsonCreateInteger(arr->allocator, num[i]); 998 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 999 } 1000 int ret = cxJsonArrAddValues(arr, values, count); 1001 cxFreeDefault(values); 1002 return ret; 1003 } 1004 1005 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) { 1006 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1007 if (values == NULL) return -1; 1008 for (size_t i = 0; i < count; i++) { 1009 values[i] = cxJsonCreateString(arr->allocator, str[i]); 1010 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1011 } 1012 int ret = cxJsonArrAddValues(arr, values, count); 1013 cxFreeDefault(values); 1014 return ret; 1015 } 1016 1017 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { 1018 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1019 if (values == NULL) return -1; 1020 for (size_t i = 0; i < count; i++) { 1021 values[i] = cxJsonCreateString(arr->allocator, str[i]); 1022 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1023 } 1024 int ret = cxJsonArrAddValues(arr, values, count); 1025 cxFreeDefault(values); 1026 return ret; 1027 } 1028 1029 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) { 1030 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 1031 if (values == NULL) return -1; 1032 for (size_t i = 0; i < count; i++) { 1033 values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); 1034 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 1035 } 1036 int ret = cxJsonArrAddValues(arr, values, count); 1037 cxFreeDefault(values); 1038 return ret; 1039 } 1040 1041 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { 1042 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); 1043 assert(arr->type == CX_JSON_ARRAY); 1044 return cx_array_simple_copy_a(&value_realloc, 1045 arr->value.array.array, 1046 arr->value.array.array_size, 1047 val, count 1048 ); 1049 } 1050 1051 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { 1052 cxmutstr k = cx_strdup_a(obj->allocator, name); 1053 if (k.ptr == NULL) return -1; 1054 CxJsonObjValue kv = {k, child}; 1055 if (json_add_objvalue(obj, kv)) { 1056 // LCOV_EXCL_START 1057 cx_strfree_a(obj->allocator, &k); 1058 return 1; 1059 // LCOV_EXCL_STOP 1060 } else { 1061 return 0; 1062 } 1063 } 1064 1065 CxJsonValue* cx_json_obj_put_obj(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* cx_json_obj_put_arr(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* cx_json_obj_put_number(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* cx_json_obj_put_integer(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* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring 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* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) { 1101 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit); 1102 if (v == NULL) return NULL; 1103 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} 1104 return v; 1105 } 1106 1107 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { 1108 if (index >= value->value.array.array_size) { 1109 return &cx_json_value_nothing; 1110 } 1111 return value->value.array.array[index]; 1112 } 1113 1114 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { 1115 if (index >= value->value.array.array_size) { 1116 return NULL; 1117 } 1118 CxJsonValue *ret = value->value.array.array[index]; 1119 // TODO: replace with a low level cx_array_remove() 1120 size_t count = value->value.array.array_size - index - 1; 1121 if (count > 0) { 1122 memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); 1123 } 1124 value->value.array.array_size--; 1125 return ret; 1126 } 1127 1128 char *cxJsonAsString(const CxJsonValue *value) { 1129 return value->value.string.ptr; 1130 } 1131 1132 cxstring cxJsonAsCxString(const CxJsonValue *value) { 1133 return cx_strcast(value->value.string); 1134 } 1135 1136 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { 1137 return value->value.string; 1138 } 1139 1140 double cxJsonAsDouble(const CxJsonValue *value) { 1141 if (value->type == CX_JSON_INTEGER) { 1142 return (double) value->value.integer; 1143 } else { 1144 return value->value.number; 1145 } 1146 } 1147 1148 int64_t cxJsonAsInteger(const CxJsonValue *value) { 1149 if (value->type == CX_JSON_INTEGER) { 1150 return value->value.integer; 1151 } else { 1152 return (int64_t) value->value.number; 1153 } 1154 } 1155 1156 CxIterator cxJsonArrIter(const CxJsonValue *value) { 1157 return cxIteratorPtr( 1158 value->value.array.array, 1159 value->value.array.array_size, 1160 true // arrays need to keep order 1161 ); 1162 } 1163 1164 CxIterator cxJsonObjIter(const CxJsonValue *value) { 1165 return cxIterator( 1166 value->value.object.values, 1167 sizeof(CxJsonObjValue), 1168 value->value.object.values_size, 1169 true // TODO: objects do not always need to keep order 1170 ); 1171 } 1172 1173 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { 1174 size_t index = json_find_objvalue(value, name); 1175 if (index >= value->value.object.values_size) { 1176 return &cx_json_value_nothing; 1177 } else { 1178 return value->value.object.values[index].value; 1179 } 1180 } 1181 1182 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { 1183 size_t index = json_find_objvalue(value, name); 1184 if (index >= value->value.object.values_size) { 1185 return NULL; 1186 } else { 1187 CxJsonObjValue kv = value->value.object.values[index]; 1188 cx_strfree_a(value->allocator, &kv.name); 1189 // TODO: replace with cx_array_remove() / cx_array_remove_fast() 1190 value->value.object.values_size--; 1191 memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); 1192 return kv.value; 1193 } 1194 } 1195 1196 CxJsonWriter cxJsonWriterCompact(void) { 1197 return (CxJsonWriter) { 1198 false, 1199 true, 1200 6, 1201 false, 1202 4, 1203 false 1204 }; 1205 } 1206 1207 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { 1208 return (CxJsonWriter) { 1209 true, 1210 true, 1211 6, 1212 use_spaces, 1213 4, 1214 false 1215 }; 1216 } 1217 1218 static int cx_json_writer_indent( 1219 void *target, 1220 cx_write_func wfunc, 1221 const CxJsonWriter *settings, 1222 unsigned int depth 1223 ) { 1224 if (depth == 0) return 0; 1225 1226 // determine the width and characters to use 1227 const char* indent; // for 32 prepared chars 1228 size_t width = depth; 1229 if (settings->indent_space) { 1230 if (settings->indent == 0) return 0; 1231 width *= settings->indent; 1232 indent = " "; 1233 } else { 1234 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"; 1235 } 1236 1237 // calculate the number of write calls and write 1238 size_t full = width / 32; 1239 size_t remaining = width % 32; 1240 for (size_t i = 0; i < full; i++) { 1241 if (32 != wfunc(indent, 1, 32, target)) return 1; 1242 } 1243 if (remaining != wfunc(indent, 1, remaining, target)) return 1; 1244 1245 return 0; 1246 } 1247 1248 1249 int cx_json_write_rec( 1250 void *target, 1251 const CxJsonValue *value, 1252 cx_write_func wfunc, 1253 const CxJsonWriter *settings, 1254 unsigned int depth 1255 ) { 1256 // keep track of written items 1257 // the idea is to reduce the number of jumps for error checking 1258 size_t actual = 0, expected = 0; 1259 1260 // small buffer for number to string conversions 1261 char numbuf[40]; 1262 1263 // recursively write the values 1264 switch (value->type) { 1265 case CX_JSON_OBJECT: { 1266 const char *begin_obj = "{\n"; 1267 if (settings->pretty) { 1268 actual += wfunc(begin_obj, 1, 2, target); 1269 expected += 2; 1270 } else { 1271 actual += wfunc(begin_obj, 1, 1, target); 1272 expected++; 1273 } 1274 depth++; 1275 size_t elem_count = value->value.object.values_size; 1276 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { 1277 // get the member either via index array or directly 1278 size_t elem_idx = settings->sort_members 1279 ? look_idx 1280 : value->value.object.indices[look_idx]; 1281 CxJsonObjValue *member = &value->value.object.values[elem_idx]; 1282 1283 // possible indentation 1284 if (settings->pretty) { 1285 if (cx_json_writer_indent(target, wfunc, settings, depth)) { 1286 return 1; // LCOV_EXCL_LINE 1287 } 1288 } 1289 1290 // the name 1291 actual += wfunc("\"", 1, 1, target); 1292 cxmutstr name = escape_string(member->name, settings->escape_slash); 1293 actual += wfunc(name.ptr, 1, name.length, target); 1294 if (name.ptr != member->name.ptr) { 1295 cx_strfree(&name); 1296 } 1297 actual += wfunc("\"", 1, 1, target); 1298 const char *obj_name_sep = ": "; 1299 if (settings->pretty) { 1300 actual += wfunc(obj_name_sep, 1, 2, target); 1301 expected += 4 + member->name.length; 1302 } else { 1303 actual += wfunc(obj_name_sep, 1, 1, target); 1304 expected += 3 + member->name.length; 1305 } 1306 1307 // the value 1308 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; 1309 1310 // end of object-value 1311 if (look_idx < elem_count - 1) { 1312 const char *obj_value_sep = ",\n"; 1313 if (settings->pretty) { 1314 actual += wfunc(obj_value_sep, 1, 2, target); 1315 expected += 2; 1316 } else { 1317 actual += wfunc(obj_value_sep, 1, 1, target); 1318 expected++; 1319 } 1320 } else { 1321 if (settings->pretty) { 1322 actual += wfunc("\n", 1, 1, target); 1323 expected ++; 1324 } 1325 } 1326 } 1327 depth--; 1328 if (settings->pretty) { 1329 if (cx_json_writer_indent(target, wfunc, settings, depth)) return 1; 1330 } 1331 actual += wfunc("}", 1, 1, target); 1332 expected++; 1333 break; 1334 } 1335 case CX_JSON_ARRAY: { 1336 actual += wfunc("[", 1, 1, target); 1337 expected++; 1338 CxIterator iter = cxJsonArrIter(value); 1339 cx_foreach(CxJsonValue*, element, iter) { 1340 if (cx_json_write_rec( 1341 target, element, 1342 wfunc, settings, depth) 1343 ) { 1344 return 1; // LCOV_EXCL_LINE 1345 } 1346 1347 if (iter.index < iter.elem_count - 1) { 1348 const char *arr_value_sep = ", "; 1349 if (settings->pretty) { 1350 actual += wfunc(arr_value_sep, 1, 2, target); 1351 expected += 2; 1352 } else { 1353 actual += wfunc(arr_value_sep, 1, 1, target); 1354 expected++; 1355 } 1356 } 1357 } 1358 actual += wfunc("]", 1, 1, target); 1359 expected++; 1360 break; 1361 } 1362 case CX_JSON_STRING: { 1363 actual += wfunc("\"", 1, 1, target); 1364 cxmutstr str = escape_string(value->value.string, settings->escape_slash); 1365 actual += wfunc(str.ptr, 1, str.length, target); 1366 if (str.ptr != value->value.string.ptr) { 1367 cx_strfree(&str); 1368 } 1369 actual += wfunc("\"", 1, 1, target); 1370 expected += 2 + value->value.string.length; 1371 break; 1372 } 1373 case CX_JSON_NUMBER: { 1374 int precision = settings->frac_max_digits; 1375 // because of the way how %g is defined, we need to 1376 // double the precision and truncate ourselves 1377 precision = 1 + (precision > 15 ? 30 : 2 * precision); 1378 snprintf(numbuf, 40, "%.*g", precision, value->value.number); 1379 char *dot, *exp; 1380 unsigned char max_digits; 1381 // find the decimal separator and hope that it's one of . or , 1382 dot = strchr(numbuf, '.'); 1383 if (dot == NULL) { 1384 dot = strchr(numbuf, ','); 1385 } 1386 if (dot == NULL) { 1387 // no decimal separator found 1388 // output everything until a possible exponent 1389 max_digits = 30; 1390 dot = numbuf; 1391 } else { 1392 // found a decimal separator 1393 // output everything until the separator 1394 // and set max digits to what the settings say 1395 size_t len = dot - numbuf; 1396 actual += wfunc(numbuf, 1, len, target); 1397 expected += len; 1398 max_digits = settings->frac_max_digits; 1399 if (max_digits > 15) { 1400 max_digits = 15; 1401 } 1402 // locale independent separator 1403 if (max_digits > 0) { 1404 actual += wfunc(".", 1, 1, target); 1405 expected++; 1406 } 1407 dot++; 1408 } 1409 // find the exponent 1410 exp = strchr(dot, 'e'); 1411 if (exp == NULL) { 1412 // no exponent - output the rest 1413 if (max_digits > 0) { 1414 size_t len = strlen(dot); 1415 if (len > max_digits) { 1416 len = max_digits; 1417 } 1418 actual += wfunc(dot, 1, len, target); 1419 expected += len; 1420 } 1421 } else { 1422 // exponent found - truncate the frac digits 1423 // and then output the rest 1424 if (max_digits > 0) { 1425 size_t len = exp - dot - 1; 1426 if (len > max_digits) { 1427 len = max_digits; 1428 } 1429 actual += wfunc(dot, 1, len, target); 1430 expected += len; 1431 } 1432 actual += wfunc("e", 1, 1, target); 1433 expected++; 1434 exp++; 1435 size_t len = strlen(exp); 1436 actual += wfunc(exp, 1, len, target); 1437 expected += len; 1438 } 1439 break; 1440 } 1441 case CX_JSON_INTEGER: { 1442 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); 1443 size_t len = strlen(numbuf); 1444 actual += wfunc(numbuf, 1, len, target); 1445 expected += len; 1446 break; 1447 } 1448 case CX_JSON_LITERAL: { 1449 if (value->value.literal == CX_JSON_TRUE) { 1450 actual += wfunc("true", 1, 4, target); 1451 expected += 4; 1452 } else if (value->value.literal == CX_JSON_FALSE) { 1453 actual += wfunc("false", 1, 5, target); 1454 expected += 5; 1455 } else { 1456 actual += wfunc("null", 1, 4, target); 1457 expected += 4; 1458 } 1459 break; 1460 } 1461 case CX_JSON_NOTHING: { 1462 // deliberately supported as an empty string! 1463 // users might want to just write the result 1464 // of a get operation without testing the value 1465 // and therefore this should not blow up 1466 break; 1467 } 1468 default: assert(false); // LCOV_EXCL_LINE 1469 } 1470 1471 return expected != actual; 1472 } 1473 1474 int cxJsonWrite( 1475 void *target, 1476 const CxJsonValue *value, 1477 cx_write_func wfunc, 1478 const CxJsonWriter *settings 1479 ) { 1480 assert(target != NULL); 1481 assert(value != NULL); 1482 assert(wfunc != NULL); 1483 1484 CxJsonWriter writer_default = cxJsonWriterCompact(); 1485 if (settings == NULL) { 1486 settings = &writer_default; 1487 } 1488 return cx_json_write_rec(target, value, wfunc, settings, 0); 1489 } 1490