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 * @file json.h 30 * @brief Interface for parsing data from JSON files. 31 * @author Mike Becker 32 * @author Olaf Wintermann 33 * @copyright 2-Clause BSD License 34 */ 35 36 #ifndef UCX_JSON_H 37 #define UCX_JSON_H 38 39 #include "common.h" 40 #include "allocator.h" 41 #include "string.h" 42 #include "buffer.h" 43 #include "array_list.h" 44 45 #include <string.h> 46 47 #ifdef __cplusplus 48 extern "C" { 49 #endif 50 51 52 /** 53 * The type of the parsed token. 54 */ 55 enum cx_json_token_type { 56 /** 57 * No complete token parsed, yet. 58 */ 59 CX_JSON_NO_TOKEN, 60 /** 61 * The presumed token contains syntactical errors. 62 */ 63 CX_JSON_TOKEN_ERROR, 64 /** 65 * A "begin of array" '[' token. 66 */ 67 CX_JSON_TOKEN_BEGIN_ARRAY, 68 /** 69 * A "begin of object" '{' token. 70 */ 71 CX_JSON_TOKEN_BEGIN_OBJECT, 72 /** 73 * An "end of array" ']' token. 74 */ 75 CX_JSON_TOKEN_END_ARRAY, 76 /** 77 * An "end of object" '}' token. 78 */ 79 CX_JSON_TOKEN_END_OBJECT, 80 /** 81 * A colon ':' token separating names and values. 82 */ 83 CX_JSON_TOKEN_NAME_SEPARATOR, 84 /** 85 * A comma ',' token separating object entries or array elements. 86 */ 87 CX_JSON_TOKEN_VALUE_SEPARATOR, 88 /** 89 * A string token. 90 */ 91 CX_JSON_TOKEN_STRING, 92 /** 93 * A number token that can be represented as integer. 94 */ 95 CX_JSON_TOKEN_INTEGER, 96 /** 97 * A number token that cannot be represented as integer. 98 */ 99 CX_JSON_TOKEN_NUMBER, 100 /** 101 * A literal token. 102 */ 103 CX_JSON_TOKEN_LITERAL, 104 /** 105 * A white-space token. 106 */ 107 CX_JSON_TOKEN_SPACE 108 }; 109 110 /** 111 * The type of some JSON value. 112 */ 113 enum cx_json_value_type { 114 /** 115 * Reserved. 116 */ 117 CX_JSON_NOTHING, // this allows us to always return non-NULL values 118 /** 119 * A JSON object. 120 */ 121 CX_JSON_OBJECT, 122 /** 123 * A JSON array. 124 */ 125 CX_JSON_ARRAY, 126 /** 127 * A string. 128 */ 129 CX_JSON_STRING, 130 /** 131 * A number that contains an integer. 132 */ 133 CX_JSON_INTEGER, 134 /** 135 * A number, not necessarily an integer. 136 */ 137 CX_JSON_NUMBER, 138 /** 139 * A literal (true, false, null). 140 */ 141 CX_JSON_LITERAL 142 }; 143 144 /** 145 * JSON literal types. 146 */ 147 enum cx_json_literal { 148 /** 149 * The @c null literal. 150 */ 151 CX_JSON_NULL, 152 /** 153 * The @c true literal. 154 */ 155 CX_JSON_TRUE, 156 /** 157 * The @c false literal. 158 */ 159 CX_JSON_FALSE 160 }; 161 162 /** 163 * Type alias for the token type enum. 164 */ 165 typedef enum cx_json_token_type CxJsonTokenType; 166 /** 167 * Type alias for the value type enum. 168 */ 169 typedef enum cx_json_value_type CxJsonValueType; 170 171 /** 172 * Type alias for the JSON parser interface. 173 */ 174 typedef struct cx_json_s CxJson; 175 176 /** 177 * Type alias for the token struct. 178 */ 179 typedef struct cx_json_token_s CxJsonToken; 180 181 /** 182 * Type alias for the JSON value struct. 183 */ 184 typedef struct cx_json_value_s CxJsonValue; 185 186 /** 187 * Type alias for the JSON array struct. 188 */ 189 typedef struct cx_json_array_s CxJsonArray; 190 /** 191 * Type alias for the JSON object struct. 192 */ 193 typedef struct cx_json_object_s CxJsonObject; 194 /** 195 * Type alias for a JSON string. 196 */ 197 typedef struct cx_mutstr_s CxJsonString; 198 /** 199 * Type alias for a number that can be represented as 64-bit signed integer. 200 */ 201 typedef int64_t CxJsonInteger; 202 /** 203 * Type alias for number that is not an integer. 204 */ 205 typedef double CxJsonNumber; 206 /** 207 * Type alias for a JSON literal. 208 */ 209 typedef enum cx_json_literal CxJsonLiteral; 210 211 /** 212 * Type alias for a key/value pair in a JSON object. 213 */ 214 typedef struct cx_json_obj_value_s CxJsonObjValue; 215 216 /** 217 * JSON array structure. 218 */ 219 struct cx_json_array_s { 220 /** 221 * The array data. 222 */ 223 CX_ARRAY_DECLARE(CxJsonValue*, array); 224 }; 225 226 /** 227 * JSON object structure. 228 */ 229 struct cx_json_object_s { 230 /** 231 * The key/value entries. 232 */ 233 CX_ARRAY_DECLARE(CxJsonObjValue, values); 234 /** 235 * The original indices to reconstruct the order in which the members were added. 236 */ 237 size_t *indices; 238 }; 239 240 /** 241 * Structure for a key/value entry in a JSON object. 242 */ 243 struct cx_json_obj_value_s { 244 /** 245 * The key (or name in JSON terminology) of the value. 246 */ 247 cxmutstr name; 248 /** 249 * The value. 250 */ 251 CxJsonValue *value; 252 }; 253 254 /** 255 * Structure for a JSON value. 256 */ 257 struct cx_json_value_s { 258 /** 259 * The allocator with which the value was allocated. 260 * 261 * Required for recursively deallocating memory of objects and arrays. 262 */ 263 const CxAllocator *allocator; 264 /** 265 * The type of this value. 266 * 267 * Specifies how the @c value union shall be resolved. 268 */ 269 CxJsonValueType type; 270 /** 271 * The value data. 272 */ 273 union { 274 /** 275 * The array data if type is #CX_JSON_ARRAY. 276 */ 277 CxJsonArray array; 278 /** 279 * The object data if type is #CX_JSON_OBJECT. 280 */ 281 CxJsonObject object; 282 /** 283 * The string data if type is #CX_JSON_STRING. 284 */ 285 CxJsonString string; 286 /** 287 * The integer if type is #CX_JSON_INTEGER. 288 */ 289 CxJsonInteger integer; 290 /** 291 * The number if type is #CX_JSON_NUMBER. 292 */ 293 CxJsonNumber number; 294 /** 295 * The literal type if type is #CX_JSON_LITERAL. 296 */ 297 CxJsonLiteral literal; 298 } value; 299 }; 300 301 /** 302 * Internally used structure for a parsed token. 303 * 304 * You should never need to use this in your code. 305 */ 306 struct cx_json_token_s { 307 /** 308 * The token type. 309 */ 310 CxJsonTokenType tokentype; 311 /** 312 * True, iff the @c content must be passed to cx_strfree(). 313 */ 314 bool allocated; 315 /** 316 * The token text, if any. 317 * 318 * This is not necessarily set when the token type already 319 * uniquely identifies the content. 320 */ 321 cxmutstr content; 322 }; 323 324 /** 325 * The JSON parser interface. 326 */ 327 struct cx_json_s { 328 /** 329 * The allocator used for produced JSON values. 330 */ 331 const CxAllocator *allocator; 332 /** 333 * The input buffer. 334 */ 335 CxBuffer buffer; 336 337 /** 338 * Used internally. 339 * 340 * Remembers the prefix of the last uncompleted token. 341 */ 342 CxJsonToken uncompleted; 343 344 /** 345 * A pointer to an intermediate state of the currently parsed value. 346 * 347 * Never access this value manually. 348 */ 349 CxJsonValue *parsed; 350 351 /** 352 * A pointer to an intermediate state of a currently parsed object member. 353 * 354 * Never access this value manually. 355 */ 356 CxJsonObjValue uncompleted_member; 357 358 /** 359 * State stack. 360 */ 361 CX_ARRAY_DECLARE_SIZED(int, states, unsigned); 362 363 /** 364 * Value buffer stack. 365 */ 366 CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned); 367 368 /** 369 * Internally reserved memory for the state stack. 370 */ 371 int states_internal[8]; 372 373 /** 374 * Internally reserved memory for the value buffer stack. 375 */ 376 CxJsonValue* vbuf_internal[8]; 377 378 /** 379 * Used internally. 380 */ 381 bool tokenizer_escape; // TODO: check if it can be replaced with look-behind 382 }; 383 384 /** 385 * Status codes for the json interface. 386 */ 387 enum cx_json_status { 388 /** 389 * Everything is fine. 390 */ 391 CX_JSON_NO_ERROR, 392 /** 393 * The input buffer does not contain more data. 394 */ 395 CX_JSON_NO_DATA, 396 /** 397 * The input ends unexpectedly. 398 * 399 * Refill the buffer with cxJsonFill() to complete the json data. 400 */ 401 CX_JSON_INCOMPLETE_DATA, 402 /** 403 * Not used as a status and never returned by any function. 404 * 405 * You can use this enumerator to check for all "good" status results 406 * by checking if the status is less than @c CX_JSON_OK. 407 * 408 * A "good" status means, that you can refill data and continue parsing. 409 */ 410 CX_JSON_OK, 411 /** 412 * The input buffer has never been filled. 413 */ 414 CX_JSON_NULL_DATA, 415 /** 416 * Allocating memory for the internal buffer failed. 417 */ 418 CX_JSON_BUFFER_ALLOC_FAILED, 419 /** 420 * Allocating memory for a json value failed. 421 */ 422 CX_JSON_VALUE_ALLOC_FAILED, 423 /** 424 * A number value is incorrectly formatted. 425 */ 426 CX_JSON_FORMAT_ERROR_NUMBER, 427 /** 428 * The tokenizer found something unexpected. 429 */ 430 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN 431 }; 432 433 /** 434 * Typedef for the json status enum. 435 */ 436 typedef enum cx_json_status CxJsonStatus; 437 438 /** 439 * The JSON writer settings. 440 */ 441 struct cx_json_writer_s { 442 /** 443 * Set true to enable pretty output. 444 */ 445 bool pretty; 446 /** 447 * Set false to output the members in the order in which they were added. 448 */ 449 bool sort_members; 450 /** 451 * The maximum number of fractional digits in a number value. 452 */ 453 uint8_t frac_max_digits; 454 /** 455 * Set true to use spaces instead of tab characters. 456 * Indentation is only used in pretty output. 457 */ 458 bool indent_space; 459 /** 460 * If @c indent_space is true, this is the number of spaces per tab. 461 * Indentation is only used in pretty output. 462 */ 463 uint8_t indent; 464 }; 465 466 /** 467 * Typedef for the json writer. 468 */ 469 typedef struct cx_json_writer_s CxJsonWriter; 470 471 /** 472 * Creates a default writer configuration for compact output. 473 * 474 * @return new JSON writer settings 475 */ 476 cx_attr_nodiscard 477 CxJsonWriter cxJsonWriterCompact(void); 478 479 /** 480 * Creates a default writer configuration for pretty output. 481 * 482 * @param use_spaces false if you want tabs, true if you want four spaces instead 483 * @return new JSON writer settings 484 */ 485 cx_attr_nodiscard 486 CxJsonWriter cxJsonWriterPretty(bool use_spaces); 487 488 /** 489 * Writes a JSON value to a buffer or stream. 490 * 491 * This function blocks until all data is written or an error when trying 492 * to write data occurs. 493 * The write operation is not atomic in the sense that it might happen 494 * that the data is only partially written when an error occurs with no 495 * way to indicate how much data was written. 496 * To avoid this problem, you can use a CxBuffer as @p target which is 497 * unlikely to fail a write operation and either use the buffer's flush 498 * feature to relay the data or use the data in the buffer manually to 499 * write it to the actual target. 500 * 501 * @param target the buffer or stream where to write to 502 * @param value the value that shall be written 503 * @param wfunc the write function to use 504 * @param settings formatting settings (or @c NULL to use a compact default) 505 * @retval zero success 506 * @retval non-zero when no or not all data could be written 507 */ 508 cx_attr_nonnull_arg(1, 2, 3) 509 int cxJsonWrite( 510 void* target, 511 const CxJsonValue* value, 512 cx_write_func wfunc, 513 const CxJsonWriter* settings 514 ); 515 516 /** 517 * Initializes the json interface. 518 * 519 * @param json the json interface 520 * @param allocator the allocator that shall be used for the produced values 521 * @see cxJsonDestroy() 522 */ 523 cx_attr_nonnull_arg(1) 524 void cxJsonInit(CxJson *json, const CxAllocator *allocator); 525 526 /** 527 * Destroys the json interface. 528 * 529 * @param json the json interface 530 * @see cxJsonInit() 531 */ 532 cx_attr_nonnull 533 void cxJsonDestroy(CxJson *json); 534 535 /** 536 * Destroys and re-initializes the json interface. 537 * 538 * You might want to use this, to reset the parser after 539 * encountering a syntax error. 540 * 541 * @param json the json interface 542 */ 543 cx_attr_nonnull 544 static inline void cxJsonReset(CxJson *json) { 545 const CxAllocator *allocator = json->allocator; 546 cxJsonDestroy(json); 547 cxJsonInit(json, allocator); 548 } 549 550 /** 551 * Fills the input buffer. 552 * 553 * @remark The JSON interface tries to avoid copying the input data. 554 * When you use this function and cxJsonNext() interleaving, 555 * no copies are performed. However, you must not free the 556 * pointer to the data in that case. When you invoke the fill 557 * function more than once before calling cxJsonNext(), 558 * the additional data is appended - inevitably leading to 559 * an allocation of a new buffer and copying the previous contents. 560 * 561 * @param json the json interface 562 * @param buf the source buffer 563 * @param len the length of the source buffer 564 * @retval zero success 565 * @retval non-zero internal allocation error 566 * @see cxJsonFill() 567 */ 568 cx_attr_nonnull 569 cx_attr_access_r(2, 3) 570 int cxJsonFilln(CxJson *json, const char *buf, size_t len); 571 572 #ifdef __cplusplus 573 } // extern "C" 574 575 cx_attr_nonnull 576 static inline int cxJsonFill( 577 CxJson *json, 578 cxstring str 579 ) { 580 return cxJsonFilln(json, str.ptr, str.length); 581 } 582 583 cx_attr_nonnull 584 static inline int cxJsonFill( 585 CxJson *json, 586 cxmutstr str 587 ) { 588 return cxJsonFilln(json, str.ptr, str.length); 589 } 590 591 cx_attr_nonnull 592 cx_attr_cstr_arg(2) 593 static inline int cxJsonFill( 594 CxJson *json, 595 const char *str 596 ) { 597 return cxJsonFilln(json, str, strlen(str)); 598 } 599 600 extern "C" { 601 #else // __cplusplus 602 /** 603 * Fills the input buffer. 604 * 605 * The JSON interface tries to avoid copying the input data. 606 * When you use this function and cxJsonNext() interleaving, 607 * no copies are performed. However, you must not free the 608 * pointer to the data in that case. When you invoke the fill 609 * function more than once before calling cxJsonNext(), 610 * the additional data is appended - inevitably leading to 611 * an allocation of a new buffer and copying the previous contents. 612 * 613 * @param json the json interface 614 * @param str the source string 615 * @retval zero success 616 * @retval non-zero internal allocation error 617 * @see cxJsonFilln() 618 */ 619 #define cxJsonFill(json, str) _Generic((str), \ 620 cxstring: cx_json_fill_cxstr, \ 621 cxmutstr: cx_json_fill_mutstr, \ 622 char*: cx_json_fill_str, \ 623 const char*: cx_json_fill_str) \ 624 (json, str) 625 626 /** 627 * @copydoc cxJsonFill() 628 */ 629 cx_attr_nonnull 630 static inline int cx_json_fill_cxstr( 631 CxJson *json, 632 cxstring str 633 ) { 634 return cxJsonFilln(json, str.ptr, str.length); 635 } 636 637 /** 638 * @copydoc cxJsonFill() 639 */ 640 cx_attr_nonnull 641 static inline int cx_json_fill_mutstr( 642 CxJson *json, 643 cxmutstr str 644 ) { 645 return cxJsonFilln(json, str.ptr, str.length); 646 } 647 648 /** 649 * @copydoc cxJsonFill() 650 */ 651 cx_attr_nonnull 652 cx_attr_cstr_arg(2) 653 static inline int cx_json_fill_str( 654 CxJson *json, 655 const char *str 656 ) { 657 return cxJsonFilln(json, str, strlen(str)); 658 } 659 #endif 660 661 /** 662 * Creates a new (empty) JSON object. 663 * 664 * @param allocator the allocator to use 665 * @return the new JSON object or @c NULL if allocation fails 666 * @see cxJsonObjPutObj() 667 * @see cxJsonArrAddValues() 668 */ 669 cx_attr_nodiscard 670 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); 671 672 /** 673 * Creates a new (empty) JSON array. 674 * 675 * @param allocator the allocator to use 676 * @return the new JSON array or @c NULL if allocation fails 677 * @see cxJsonObjPutArr() 678 * @see cxJsonArrAddValues() 679 */ 680 cx_attr_nodiscard 681 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); 682 683 /** 684 * Creates a new JSON number value. 685 * 686 * @param allocator the allocator to use 687 * @param num the numeric value 688 * @return the new JSON value or @c NULL if allocation fails 689 * @see cxJsonObjPutNumber() 690 * @see cxJsonArrAddNumbers() 691 */ 692 cx_attr_nodiscard 693 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); 694 695 /** 696 * Creates a new JSON number value based on an integer. 697 * 698 * @param allocator the allocator to use 699 * @param num the numeric value 700 * @return the new JSON value or @c NULL if allocation fails 701 * @see cxJsonObjPutInteger() 702 * @see cxJsonArrAddIntegers() 703 */ 704 cx_attr_nodiscard 705 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); 706 707 /** 708 * Creates a new JSON string. 709 * 710 * @param allocator the allocator to use 711 * @param str the string data 712 * @return the new JSON value or @c NULL if allocation fails 713 * @see cxJsonCreateString() 714 * @see cxJsonObjPutString() 715 * @see cxJsonArrAddStrings() 716 */ 717 cx_attr_nodiscard 718 cx_attr_nonnull_arg(2) 719 cx_attr_cstr_arg(2) 720 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); 721 722 /** 723 * Creates a new JSON string. 724 * 725 * @param allocator the allocator to use 726 * @param str the string data 727 * @return the new JSON value or @c NULL if allocation fails 728 * @see cxJsonCreateCxString() 729 * @see cxJsonObjPutCxString() 730 * @see cxJsonArrAddCxStrings() 731 */ 732 cx_attr_nodiscard 733 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); 734 735 /** 736 * Creates a new JSON literal. 737 * 738 * @param allocator the allocator to use 739 * @param lit the type of literal 740 * @return the new JSON value or @c NULL if allocation fails 741 * @see cxJsonObjPutLiteral() 742 * @see cxJsonArrAddLiterals() 743 */ 744 cx_attr_nodiscard 745 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); 746 747 /** 748 * Adds number values to a JSON array. 749 * 750 * @param arr the JSON array 751 * @param num the array of values 752 * @param count the number of elements 753 * @retval zero success 754 * @retval non-zero allocation failure 755 */ 756 cx_attr_nonnull 757 cx_attr_access_r(2, 3) 758 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); 759 760 /** 761 * Adds number values, of which all are integers, to a JSON array. 762 * 763 * @param arr the JSON array 764 * @param num the array of values 765 * @param count the number of elements 766 * @retval zero success 767 * @retval non-zero allocation failure 768 */ 769 cx_attr_nonnull 770 cx_attr_access_r(2, 3) 771 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); 772 773 /** 774 * Adds strings to a JSON array. 775 * 776 * The strings will be copied with the allocator of the array. 777 * 778 * @param arr the JSON array 779 * @param str the array of strings 780 * @param count the number of elements 781 * @retval zero success 782 * @retval non-zero allocation failure 783 * @see cxJsonArrAddCxStrings() 784 */ 785 cx_attr_nonnull 786 cx_attr_access_r(2, 3) 787 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); 788 789 /** 790 * Adds strings to a JSON array. 791 * 792 * The strings will be copied with the allocator of the array. 793 * 794 * @param arr the JSON array 795 * @param str the array of strings 796 * @param count the number of elements 797 * @retval zero success 798 * @retval non-zero allocation failure 799 * @see cxJsonArrAddStrings() 800 */ 801 cx_attr_nonnull 802 cx_attr_access_r(2, 3) 803 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); 804 805 /** 806 * Adds literals to a JSON array. 807 * 808 * @param arr the JSON array 809 * @param lit the array of literal types 810 * @param count the number of elements 811 * @retval zero success 812 * @retval non-zero allocation failure 813 */ 814 cx_attr_nonnull 815 cx_attr_access_r(2, 3) 816 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); 817 818 /** 819 * Add arbitrary values to a JSON array. 820 * 821 * @attention In contrast to all other add functions, this function adds the values 822 * directly to the array instead of copying them. 823 * 824 * @param arr the JSON array 825 * @param val the values 826 * @param count the number of elements 827 * @retval zero success 828 * @retval non-zero allocation failure 829 */ 830 cx_attr_nonnull 831 cx_attr_access_r(2, 3) 832 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); 833 834 /** 835 * Adds or replaces a value within a JSON object. 836 * 837 * The value will be directly added and not copied. 838 * 839 * @note If a value with the specified @p name already exists, 840 * it will be (recursively) freed with its own allocator. 841 * 842 * @param obj the JSON object 843 * @param name the name of the value 844 * @param child the value 845 * @retval zero success 846 * @retval non-zero allocation failure 847 */ 848 cx_attr_nonnull 849 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); 850 851 /** 852 * Creates a new JSON object and adds it to an existing object. 853 * 854 * @param obj the target JSON object 855 * @param name the name of the new value 856 * @return the new value or @c NULL if allocation fails 857 * @see cxJsonObjPut() 858 * @see cxJsonCreateObj() 859 */ 860 cx_attr_nonnull 861 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); 862 863 /** 864 * Creates a new JSON array and adds it to an object. 865 * 866 * @param obj the target JSON object 867 * @param name the name of the new value 868 * @return the new value or @c NULL if allocation fails 869 * @see cxJsonObjPut() 870 * @see cxJsonCreateArr() 871 */ 872 cx_attr_nonnull 873 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); 874 875 /** 876 * Creates a new JSON number and adds it to an object. 877 * 878 * @param obj the target JSON object 879 * @param name the name of the new value 880 * @param num the numeric value 881 * @return the new value or @c NULL if allocation fails 882 * @see cxJsonObjPut() 883 * @see cxJsonCreateNumber() 884 */ 885 cx_attr_nonnull 886 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); 887 888 /** 889 * Creates a new JSON number, based on an integer, and adds it to an object. 890 * 891 * @param obj the target JSON object 892 * @param name the name of the new value 893 * @param num the numeric value 894 * @return the new value or @c NULL if allocation fails 895 * @see cxJsonObjPut() 896 * @see cxJsonCreateInteger() 897 */ 898 cx_attr_nonnull 899 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); 900 901 /** 902 * Creates a new JSON string and adds it to an object. 903 * 904 * The string data is copied. 905 * 906 * @param obj the target JSON object 907 * @param name the name of the new value 908 * @param str the string data 909 * @return the new value or @c NULL if allocation fails 910 * @see cxJsonObjPut() 911 * @see cxJsonCreateString() 912 */ 913 cx_attr_nonnull 914 cx_attr_cstr_arg(3) 915 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); 916 917 /** 918 * Creates a new JSON string and adds it to an object. 919 * 920 * The string data is copied. 921 * 922 * @param obj the target JSON object 923 * @param name the name of the new value 924 * @param str the string data 925 * @return the new value or @c NULL if allocation fails 926 * @see cxJsonObjPut() 927 * @see cxJsonCreateCxString() 928 */ 929 cx_attr_nonnull 930 CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); 931 932 /** 933 * Creates a new JSON literal and adds it to an object. 934 * 935 * @param obj the target JSON object 936 * @param name the name of the new value 937 * @param lit the type of literal 938 * @return the new value or @c NULL if allocation fails 939 * @see cxJsonObjPut() 940 * @see cxJsonCreateLiteral() 941 */ 942 cx_attr_nonnull 943 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); 944 945 /** 946 * Recursively deallocates the memory of a JSON value. 947 * 948 * @remark The type of each deallocated value will be changed 949 * to #CX_JSON_NOTHING and values of such type will be skipped 950 * by the de-allocation. That means, this function protects 951 * you from double-frees when you are accidentally freeing 952 * a nested value and then the parent value (or vice versa). 953 * 954 * @param value the value 955 */ 956 void cxJsonValueFree(CxJsonValue *value); 957 958 /** 959 * Tries to obtain the next JSON value. 960 * 961 * Before this function can be called, the input buffer needs 962 * to be filled with cxJsonFill(). 963 * 964 * When this function returns #CX_JSON_INCOMPLETE_DATA, you can 965 * add the missing data with another invocation of cxJsonFill() 966 * and then repeat the call to cxJsonNext(). 967 * 968 * @param json the json interface 969 * @param value a pointer where the next value shall be stored 970 * @retval CX_JSON_NO_ERROR successfully retrieve the @p value 971 * @retval CX_JSON_NO_DATA there is no (more) data in the buffer to read from 972 * @retval CX_JSON_INCOMPLETE_DATA an incomplete value was read 973 * and more data needs to be filled 974 * @retval CX_JSON_NULL_DATA the buffer was never initialized 975 * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed 976 * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for a CxJsonValue failed 977 * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number 978 * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error 979 */ 980 cx_attr_nonnull 981 cx_attr_access_w(2) 982 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); 983 984 /** 985 * Checks if the specified value is a JSON object. 986 * 987 * @param value a pointer to the value 988 * @retval true the value is a JSON object 989 * @retval false otherwise 990 */ 991 cx_attr_nonnull 992 static inline bool cxJsonIsObject(const CxJsonValue *value) { 993 return value->type == CX_JSON_OBJECT; 994 } 995 996 /** 997 * Checks if the specified value is a JSON array. 998 * 999 * @param value a pointer to the value 1000 * @retval true the value is a JSON array 1001 * @retval false otherwise 1002 */ 1003 cx_attr_nonnull 1004 static inline bool cxJsonIsArray(const CxJsonValue *value) { 1005 return value->type == CX_JSON_ARRAY; 1006 } 1007 1008 /** 1009 * Checks if the specified value is a string. 1010 * 1011 * @param value a pointer to the value 1012 * @retval true the value is a string 1013 * @retval false otherwise 1014 */ 1015 cx_attr_nonnull 1016 static inline bool cxJsonIsString(const CxJsonValue *value) { 1017 return value->type == CX_JSON_STRING; 1018 } 1019 1020 /** 1021 * Checks if the specified value is a JSON number. 1022 * 1023 * This function will return true for both floating point and 1024 * integer numbers. 1025 * 1026 * @param value a pointer to the value 1027 * @retval true the value is a JSON number 1028 * @retval false otherwise 1029 * @see cxJsonIsInteger() 1030 */ 1031 cx_attr_nonnull 1032 static inline bool cxJsonIsNumber(const CxJsonValue *value) { 1033 return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER; 1034 } 1035 1036 /** 1037 * Checks if the specified value is an integer number. 1038 * 1039 * @param value a pointer to the value 1040 * @retval true the value is an integer number 1041 * @retval false otherwise 1042 * @see cxJsonIsNumber() 1043 */ 1044 cx_attr_nonnull 1045 static inline bool cxJsonIsInteger(const CxJsonValue *value) { 1046 return value->type == CX_JSON_INTEGER; 1047 } 1048 1049 /** 1050 * Checks if the specified value is a JSON literal. 1051 * 1052 * JSON literals are @c true, @c false, and @c null. 1053 * 1054 * @param value a pointer to the value 1055 * @retval true the value is a JSON literal 1056 * @retval false otherwise 1057 * @see cxJsonIsTrue() 1058 * @see cxJsonIsFalse() 1059 * @see cxJsonIsNull() 1060 */ 1061 cx_attr_nonnull 1062 static inline bool cxJsonIsLiteral(const CxJsonValue *value) { 1063 return value->type == CX_JSON_LITERAL; 1064 } 1065 1066 /** 1067 * Checks if the specified value is a Boolean literal. 1068 * 1069 * @param value a pointer to the value 1070 * @retval true the value is either @c true or @c false 1071 * @retval false otherwise 1072 * @see cxJsonIsTrue() 1073 * @see cxJsonIsFalse() 1074 */ 1075 cx_attr_nonnull 1076 static inline bool cxJsonIsBool(const CxJsonValue *value) { 1077 return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; 1078 } 1079 1080 /** 1081 * Checks if the specified value is @c true. 1082 * 1083 * @remark Be advised, that this is not the same as 1084 * testing @c !cxJsonIsFalse(v). 1085 * 1086 * @param value a pointer to the value 1087 * @retval true the value is @c true 1088 * @retval false otherwise 1089 * @see cxJsonIsBool() 1090 * @see cxJsonIsFalse() 1091 */ 1092 cx_attr_nonnull 1093 static inline bool cxJsonIsTrue(const CxJsonValue *value) { 1094 return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; 1095 } 1096 1097 /** 1098 * Checks if the specified value is @c false. 1099 * 1100 * @remark Be advised, that this is not the same as 1101 * testing @c !cxJsonIsTrue(v). 1102 * 1103 * @param value a pointer to the value 1104 * @retval true the value is @c false 1105 * @retval false otherwise 1106 * @see cxJsonIsBool() 1107 * @see cxJsonIsTrue() 1108 */ 1109 cx_attr_nonnull 1110 static inline bool cxJsonIsFalse(const CxJsonValue *value) { 1111 return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; 1112 } 1113 1114 /** 1115 * Checks if the specified value is @c null. 1116 * 1117 * @param value a pointer to the value 1118 * @retval true the value is @c null 1119 * @retval false otherwise 1120 * @see cxJsonIsLiteral() 1121 */ 1122 cx_attr_nonnull 1123 static inline bool cxJsonIsNull(const CxJsonValue *value) { 1124 return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; 1125 } 1126 1127 /** 1128 * Obtains a C string from the given JSON value. 1129 * 1130 * If the @p value is not a string, the behavior is undefined. 1131 * 1132 * @param value the JSON value 1133 * @return the value represented as C string 1134 * @see cxJsonIsString() 1135 */ 1136 cx_attr_nonnull 1137 cx_attr_returns_nonnull 1138 static inline char *cxJsonAsString(const CxJsonValue *value) { 1139 return value->value.string.ptr; 1140 } 1141 1142 /** 1143 * Obtains a UCX string from the given JSON value. 1144 * 1145 * If the @p value is not a string, the behavior is undefined. 1146 * 1147 * @param value the JSON value 1148 * @return the value represented as UCX string 1149 * @see cxJsonIsString() 1150 */ 1151 cx_attr_nonnull 1152 static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { 1153 return cx_strcast(value->value.string); 1154 } 1155 1156 /** 1157 * Obtains a mutable UCX string from the given JSON value. 1158 * 1159 * If the @p value is not a string, the behavior is undefined. 1160 * 1161 * @param value the JSON value 1162 * @return the value represented as mutable UCX string 1163 * @see cxJsonIsString() 1164 */ 1165 cx_attr_nonnull 1166 static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { 1167 return value->value.string; 1168 } 1169 1170 /** 1171 * Obtains a double-precision floating point value from the given JSON value. 1172 * 1173 * If the @p value is not a JSON number, the behavior is undefined. 1174 * 1175 * @param value the JSON value 1176 * @return the value represented as double 1177 * @see cxJsonIsNumber() 1178 */ 1179 cx_attr_nonnull 1180 static inline double cxJsonAsDouble(const CxJsonValue *value) { 1181 if (value->type == CX_JSON_INTEGER) { 1182 return (double) value->value.integer; 1183 } else { 1184 return value->value.number; 1185 } 1186 } 1187 1188 /** 1189 * Obtains a 64-bit signed integer from the given JSON value. 1190 * 1191 * If the @p value is not a JSON number, the behavior is undefined. 1192 * If it is a JSON number, but not an integer, the value will be 1193 * converted to an integer, possibly losing precision. 1194 * 1195 * @param value the JSON value 1196 * @return the value represented as double 1197 * @see cxJsonIsNumber() 1198 * @see cxJsonIsInteger() 1199 */ 1200 cx_attr_nonnull 1201 static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { 1202 if (value->type == CX_JSON_INTEGER) { 1203 return value->value.integer; 1204 } else { 1205 return (int64_t) value->value.number; 1206 } 1207 } 1208 1209 /** 1210 * Obtains a Boolean value from the given JSON value. 1211 * 1212 * If the @p value is not a JSON literal, the behavior is undefined. 1213 * The @c null literal is interpreted as @c false. 1214 * 1215 * @param value the JSON value 1216 * @return the value represented as double 1217 * @see cxJsonIsLiteral() 1218 */ 1219 cx_attr_nonnull 1220 static inline bool cxJsonAsBool(const CxJsonValue *value) { 1221 return value->value.literal == CX_JSON_TRUE; 1222 } 1223 1224 /** 1225 * Returns the size of a JSON array. 1226 * 1227 * If the @p value is not a JSON array, the behavior is undefined. 1228 * 1229 * @param value the JSON value 1230 * @return the size of the array 1231 * @see cxJsonIsArray() 1232 */ 1233 cx_attr_nonnull 1234 static inline size_t cxJsonArrSize(const CxJsonValue *value) { 1235 return value->value.array.array_size; 1236 } 1237 1238 /** 1239 * Returns an element from a JSON array. 1240 * 1241 * If the @p value is not a JSON array, the behavior is undefined. 1242 * 1243 * This function guarantees to return a value. If the index is 1244 * out of bounds, the returned value will be of type 1245 * #CX_JSON_NOTHING, but never @c NULL. 1246 * 1247 * @param value the JSON value 1248 * @param index the index in the array 1249 * @return the value at the specified index 1250 * @see cxJsonIsArray() 1251 */ 1252 cx_attr_nonnull 1253 cx_attr_returns_nonnull 1254 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); 1255 1256 /** 1257 * Returns an iterator over the JSON array elements. 1258 * 1259 * The iterator yields values of type @c CxJsonValue* . 1260 * 1261 * If the @p value is not a JSON array, the behavior is undefined. 1262 * 1263 * @param value the JSON value 1264 * @return an iterator over the array elements 1265 * @see cxJsonIsArray() 1266 */ 1267 cx_attr_nonnull 1268 cx_attr_nodiscard 1269 CxIterator cxJsonArrIter(const CxJsonValue *value); 1270 1271 /** 1272 * Returns an iterator over the JSON object members. 1273 * 1274 * The iterator yields values of type @c CxJsonObjValue* which 1275 * contain the name and value of the member. 1276 * 1277 * If the @p value is not a JSON object, the behavior is undefined. 1278 * 1279 * @param value the JSON value 1280 * @return an iterator over the object members 1281 * @see cxJsonIsObject() 1282 */ 1283 cx_attr_nonnull 1284 cx_attr_nodiscard 1285 CxIterator cxJsonObjIter(const CxJsonValue *value); 1286 1287 /** 1288 * @copydoc cxJsonObjGet() 1289 */ 1290 cx_attr_nonnull 1291 cx_attr_returns_nonnull 1292 CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name); 1293 1294 #ifdef __cplusplus 1295 } // extern "C" 1296 1297 CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) { 1298 return cx_json_obj_get_cxstr(value, name); 1299 } 1300 1301 CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) { 1302 return cx_json_obj_get_cxstr(value, cx_strcast(name)); 1303 } 1304 1305 CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) { 1306 return cx_json_obj_get_cxstr(value, cx_str(name)); 1307 } 1308 1309 extern "C" { 1310 #else 1311 /** 1312 * Returns a value corresponding to a key in a JSON object. 1313 * 1314 * If the @p value is not a JSON object, the behavior is undefined. 1315 * 1316 * This function guarantees to return a JSON value. If the 1317 * object does not contain @p name, the returned JSON value 1318 * will be of type #CX_JSON_NOTHING, but never @c NULL. 1319 * 1320 * @param value the JSON object 1321 * @param name the key to look up 1322 * @return the value corresponding to the key 1323 * @see cxJsonIsObject() 1324 */ 1325 #define cxJsonObjGet(value, name) _Generic((name), \ 1326 cxstring: cx_json_obj_get_cxstr, \ 1327 cxmutstr: cx_json_obj_get_mutstr, \ 1328 char*: cx_json_obj_get_str, \ 1329 const char*: cx_json_obj_get_str) \ 1330 (value, name) 1331 1332 /** 1333 * @copydoc cxJsonObjGet() 1334 */ 1335 cx_attr_nonnull 1336 cx_attr_returns_nonnull 1337 static inline CxJsonValue *cx_json_obj_get_mutstr(const CxJsonValue *value, cxmutstr name) { 1338 return cx_json_obj_get_cxstr(value, cx_strcast(name)); 1339 } 1340 1341 /** 1342 * @copydoc cxJsonObjGet() 1343 */ 1344 cx_attr_nonnull 1345 cx_attr_returns_nonnull 1346 cx_attr_cstr_arg(2) 1347 static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) { 1348 return cx_json_obj_get_cxstr(value, cx_str(name)); 1349 } 1350 #endif 1351 1352 #ifdef __cplusplus 1353 } 1354 #endif 1355 1356 #endif /* UCX_JSON_H */ 1357 1358