├── .gitignore ├── LICENSE ├── README.md ├── sjson.h └── tests ├── CMakeLists.txt ├── test-decode.c ├── test-encode.c └── twitter.json /.gitignore: -------------------------------------------------------------------------------- 1 | # External junk 2 | .DS_Store 3 | _ReSharper* 4 | *.opensdf 5 | *.sdf 6 | *.dir 7 | *.suo 8 | *.user 9 | *.bak 10 | Win32 11 | Win64 12 | Debug 13 | Release 14 | Profile 15 | Development 16 | Obj 17 | Bin 18 | Lib 19 | .tags 20 | .tags_sorted_by_file 21 | *.lnk 22 | ipch 23 | __pycache__ 24 | Thumbs.db 25 | .build* 26 | 27 | # Gradle 28 | .gradle 29 | /local.properties 30 | /.idea/workspace.xml 31 | /.idea/libraries 32 | /build 33 | /captures 34 | 35 | 36 | # Generated files 37 | docs/Doxyfile 38 | docs/html 39 | docs/warnings.txt 40 | imgui.ini 41 | install 42 | *.log 43 | .vscode 44 | 45 | # Compiled binaries 46 | *.so 47 | *.dylib 48 | *.dll 49 | *.a 50 | *.lib 51 | *.exe 52 | .build 53 | *.fso 54 | *.vso 55 | *.pyc 56 | *.obj 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Sepehr Taghdisian 4 | 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 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | 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 ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sjson: Fast and portable C single header json Encoder/Decoder 2 | [@septag](https://twitter.com/septagh) 3 | 4 | This is actually a fork of Joseph's awesome Json encoder/decoder code from his [repo](https://github.com/rustyrussell/ccan/tree/master/ccan/json). 5 | If you want to see the performance and analysis of the original encoder/decoder (which is _ccan/json_) visit [here](https://github.com/miloyip/nativejson-benchmark). 6 | The encoder/decoder code is almost the same. What I did was adding object pools and string pages (sjson_context) 7 | that eliminates many micro memory allocations, which should improve encode/decode speed and data access performance. 8 | I also added malloc/free and libc API overrides, and made the library single header, so it makes it very easy to integrate it into other programs 9 | 10 | ### Features 11 | 12 | - Single header C-file 13 | - UTF8 support 14 | - Fast with minimal allocations (Internal Object pool, String pool, ..) 15 | - Overriable libc functions like malloc/free/memcpy/.. 16 | - Supports both Json encoding and decoding 17 | - Encoder supports prettify 18 | - No dependencies 19 | - Simple and easy to use C-API 20 | 21 | ### Usage 22 | 23 | ```c 24 | #define SJSON_IMPLEMENT 25 | #include "sjson.h" 26 | ``` 27 | 28 | For more information, check out the header file itself. 29 | 30 | ### Links 31 | - [cj5](https://github.com/septag/cj5): Fast single header Json5 parser based on [jsmn](https://github.com/zserge/jsmn) 32 | 33 | [License (BSD 2-clause)](https://github.com/septag/sjson/blob/master/LICENSE) 34 | -------------------------------------------------------------------------- 35 | 36 | 37 | 38 | 39 | 40 | Copyright 2018 Sepehr Taghdisian. All rights reserved. 41 | 42 | https://github.com/septag/sjson 43 | 44 | Redistribution and use in source and binary forms, with or without 45 | modification, are permitted provided that the following conditions are met: 46 | 47 | 1. Redistributions of source code must retain the above copyright notice, 48 | this list of conditions and the following disclaimer. 49 | 50 | 2. Redistributions in binary form must reproduce the above copyright notice, 51 | this list of conditions and the following disclaimer in the documentation 52 | and/or other materials provided with the distribution. 53 | 54 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR 55 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 56 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 57 | EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 58 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 59 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 60 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 61 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 62 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 63 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 | 65 | -------------------------------------------------------------------------------- /sjson.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018 Sepehr Taghdisian (septag@github). All rights reserved. 3 | // License: https://github.com/septag/sjson#license-bsd-2-clause 4 | // 5 | // Original code by Joseph A. Adams (joeyadams3.14159@gmail.com) 6 | // 7 | // sjson.h - v1.2.0 - Fast single header json encoder/decoder 8 | // This is actually a fork of Joseph's awesome Json encoder/decoder code from his repo: 9 | // https://github.com/rustyrussell/ccan/tree/master/ccan/json 10 | // The encoder/decoder code is almost the same. What I did was adding object pools and string pages (sjson_context) 11 | // that eliminates many micro memory allocations, which improves encode/decode speed and data access performance 12 | // I also added malloc/free and libc API overrides, and made the library single header 13 | // 14 | // FEATURES: 15 | // - Single header C-file 16 | // - UTF8 support 17 | // - Fast with minimal allocations (Object pool, String pool, ..) 18 | // - Overriable malloc/free/memcpy/.. 19 | // - Have both Json Decoder/Encoder 20 | // - Encoder supports pretify 21 | // - No dependencies 22 | // - Simple and easy to use C api 23 | // 24 | // USAGE: 25 | // Data types: 26 | // sjson_tag Json object tag type, string, number, object, etc.. 27 | // sjson_node Data structure that represents json DOM node 28 | // sjson_context Encoder/decoder context, almost all API functions need a context to allocate and manage memory 29 | // It's not thread-safe, so don't use the same context in multiple threads 30 | // API: 31 | // --- CREATION 32 | // sjson_create_context creates a json context: 33 | // @PARAM pool_size number of initial items in object pool (default=512) 34 | // this is actually the number of DOM nodes in json and can be grown 35 | // for large files with lots of elements, you can set this paramter to higher values 36 | // @PARAM str_buffer_size String buffer size in bytes (default=4096) 37 | // Strings in json decoder, encoder uses a special allocator that allocates string from a pool 38 | // String pool will be grown from the initial size to store more json string data 39 | // Depending on the size of the json data, you can set this value higher or lower 40 | // @PARAM alloc_user A user-defined pointer that is passed to allocations, in case you override memory alloc functions 41 | // sjson_destroy_context Destroys a json context and frees all memory 42 | // after the context destroys, all created json nodes will become invalid 43 | // 44 | // sjson_reset_context Resets the context. No memory will be freed, just resets the buffers so they can be reused 45 | // but the DOM nodes and strings will be invalidated next time you parse or decode a json 46 | // 47 | // --- DECODE/ENCODE/VALIDATE 48 | // sjson_decode Decodes the json text and returns DOM document root node 49 | // sjson_encode Encodes the json root node to json string, does not prettify 50 | // Generated string can be freed by calling 'sjson_free_string' on the returned pointer 51 | // sjson_stringify Encodes the json root node to pretty json string 52 | // @PARAM space sets number of spaces for tab, example two-space for a tab is " " 53 | // sjson_free_string String pointer returned by stringify and encode should be freed by calling this function 54 | // sjson_validate Validates the json string, returns false if json string is invalid 55 | // 56 | // --- LOOKUP/TRAVERSE 57 | // sjson_find_element Finds an element by index inside array 58 | // sjson_find_member Finds an element by name inside object 59 | // sjson_find_member_nocase Finds an element by name inside object, ignores case 60 | // sjson_first_child Gets first child of Object or Array types 61 | // sjson_foreach Iterates through an Object or array child elements, first parameter should be a pre-defined json_node* 62 | // 63 | // --- HIGHER LEVEL LOOKUP 64 | // sjson_get_int Gets an integer value from a child of the specified parent node, sets to 'default_val' if node is not found 65 | // sjson_get_int64 Gets an 64-bit integer value from a child of the specified parent node, sets to 'default_val' if node is not found 66 | // sjson_get_float Gets a float value from a child of the specified parent node, sets to 'default_val' if node is not found 67 | // sjson_get_double Gets a double value from a child of the specified parent node, sets to 'default_val' if node is not found 68 | // sjson_get_string Gets a string value from a child of the specified parent node, sets to 'default_val' if node is not found 69 | // sjson_get_bool Gets a boolean value from a child of the specified parent node, sets to 'default_val' if node is not found 70 | // sjson_get_floats Returns float array from a child of the specified parent node, returns false if node is not found or does not have enough elements to fill the variable 71 | // sjson_get_ints Returns integer array from a child of the specified parent node, returns false if node is not found or does not have enough elements to fill the variable 72 | // 73 | // --- CONSTRUCTION 74 | // sjson_mknull Creates a NULL type json node 75 | // sjson_mknumber Creates and sets a number type json node 76 | // sjson_mkstring Creates and sets a string type json node 77 | // sjson_mkobject Creates an object type json node 78 | // sjson_mkarray Creates an array type json node 79 | // sjson_mkbool Creates and sets boolean type json node 80 | // sjson_append_element Appends the node to the end of an array 81 | // sjson_prepend_element Prepends the node to the beginning of an array 82 | // sjson_append_member Appends the node to the members of an object, should provide the key name to the element 83 | // sjson_prepend_member Prepends the node to the members of an object, should provide the key name to the element 84 | // sjson_remove_from_parent Removes the node from it's parrent, if there is any 85 | // sjson_delete_node Deletes the node and all of it's children (object/array) 86 | // 87 | // --- HIGHER LEVEL CONSTRUCTION these functions use a combination of above to facilitate some operations 88 | // sjson_put_obj Add an object with a key(name) to the child of the parent node 89 | // sjson_put_array Add an array with a key(name) to the child of the parent node 90 | // sjson_put_int Add an integer value with a key(name) to the child of the specified parent node 91 | // sjson_put_float Add float value with a key(name) to the child of the specified parent node 92 | // sjson_put_double Add double value with a key(name) to the child of the specified parent node 93 | // sjson_put_bool Add boolean value with a key(name) to the child of the specified parent node 94 | // sjson_put_string Add string value with a key(name) to the child of the specified parent node 95 | // sjson_put_floats Add float array with a key(name) to the child of the specified parent node 96 | // sjson_put_ints Add integer array with a key(name) to the child of the specified parent node 97 | // sjson_put_string Add string array with a key(name) to the child of the specified parent node 98 | // 99 | // --- DEBUG 100 | // sjson_check Checks and validates the json DOM node recursively 101 | // Returns false and writes a report to 'errmsg' parameter if DOM document has any encoding problems 102 | // 103 | // IMPLEMENTATION: 104 | // 105 | // To include and implement sjson in your project, you should define SJSON_IMPLEMENT before including sjson.h 106 | // You can also override memory allocation or any libc functions that the library uses to your own 107 | // Here's a list of stuff that can be overriden: 108 | // ALLOCATIONS 109 | // - sjson_malloc(user, size) 'user' is the pointer that is passed in sjson_create_context 110 | // - sjson_free(user, ptr) 111 | // - sjson_realloc(user, ptr, size) 112 | // DEBUG/ASSERT 113 | // - sjson_assert 114 | // STRINGS 115 | // - sjson_stricmp 116 | // - sjson_strcpy 117 | // - sjson_strcmp 118 | // - sjson_snprintf 119 | // MEMORY 120 | // - sjson_memcpy 121 | // - sjson_memset 122 | // - sjson_out_of_memory happens when sjson cannot allocate memory internally 123 | // Default behaviour is that it asserts and exits the program 124 | // Example: 125 | // #define SJSON_IMPLEMENT 126 | // #define sjson_malloc(user, size) MyMalloc(user, size) 127 | // #define sjson_free(user, ptr) MyFree(user, ptr) 128 | // #define sjson_realloc(user, ptr, size) MyRealloc(user, ptr, size) 129 | // #include "sjson.h" 130 | // ... 131 | // 132 | // NOTE: on sjson_reset_context 133 | // what reset_context does is that is resets the internal buffers without freeing them 134 | // this makes context re-usable for the next json document, so don't have to regrow buffers or create another context 135 | // Example: 136 | // sjson_context* ctx = sjson_create_context(0, 0, NULL); // initial creation 137 | // sjson_decode(ctx, json1); // decode json1 138 | // ... // do some work on data 139 | // sjson_reset_context(ctx); // reset the buffers, make sure you don't need json1 data 140 | // sjson_decode(ctx, json2); // decode another json 141 | // ... 142 | // 143 | #pragma once 144 | 145 | #ifndef SJSON_H_ 146 | #define SJSON_H_ 147 | 148 | #ifdef _MSC_VER 149 | # ifndef __cplusplus 150 | 151 | # define false 0 152 | # define true 1 153 | # define bool _Bool 154 | 155 | /* For compilers that don't have the builtin _Bool type. */ 156 | # if ((defined(_MSC_VER) && _MSC_VER < 1800) || \ 157 | (defined __GNUC__&& __STDC_VERSION__ < 199901L && __GNUC__ < 3)) && !defined(_lint) 158 | typedef unsigned char _Bool; 159 | # endif 160 | 161 | # endif /* !__cplusplus */ 162 | 163 | #define __bool_true_false_are_defined 1 164 | #else 165 | # include 166 | #endif 167 | #include 168 | #include 169 | 170 | // Json DOM object type 171 | typedef enum sjson_tag { 172 | SJSON_NULL, 173 | SJSON_BOOL, 174 | SJSON_STRING, 175 | SJSON_NUMBER, 176 | SJSON_ARRAY, 177 | SJSON_OBJECT, 178 | } sjson_tag; 179 | 180 | // Json DOM node struct 181 | typedef struct sjson_node 182 | { 183 | struct sjson_node* parent; // only if parent is an object or array (NULL otherwise) 184 | struct sjson_node* prev; 185 | struct sjson_node* next; 186 | 187 | char* key; // only if parent is an object (NULL otherwise). Must be valid UTF-8. 188 | 189 | sjson_tag tag; 190 | union { 191 | bool bool_; // SJSON_BOOL 192 | char* string_; // SJSON_STRING: Must be valid UTF-8. 193 | double number_; // SJSON_NUMBER 194 | struct { 195 | struct sjson_node* head; 196 | struct sjson_node* tail; 197 | } children; // SJSON_ARRAY, SJSON_OBJECT 198 | }; 199 | } sjson_node; 200 | 201 | // Json context, handles memory, pools, etc. 202 | // Almost every API needs a valid context to work 203 | // Not multi-threaded. Do not use the same context in multiple threads 204 | typedef struct sjson_context sjson_context; 205 | 206 | #ifdef __cplusplus 207 | extern "C" { 208 | #endif 209 | 210 | sjson_context* sjson_create_context(int pool_size, int str_buffer_size, void* alloc_user); 211 | void sjson_destroy_context(sjson_context* ctx); 212 | void sjson_reset_context(sjson_context* ctx); 213 | 214 | // Encoding, decoding, and validation 215 | sjson_node* sjson_decode(sjson_context* ctx, const char* json); 216 | char* sjson_encode(sjson_context* ctx, const sjson_node* node); 217 | char* sjson_encode_string(sjson_context* ctx, const char* str); 218 | char* sjson_stringify(sjson_context* ctx, const sjson_node* node, const char* space); 219 | void sjson_free_string(sjson_context* ctx, char* str); 220 | 221 | bool sjson_validate(sjson_context* ctx, const char* json); 222 | 223 | // Lookup and traversal 224 | sjson_node* sjson_find_element(sjson_node* array, int index); 225 | sjson_node* sjson_find_member(sjson_node* object, const char* key); 226 | sjson_node* sjson_find_member_nocase(sjson_node* object, const char *name); 227 | sjson_node* sjson_first_child(const sjson_node* node); 228 | int sjson_child_count(const sjson_node* node); 229 | 230 | // Higher level lookup/get functions 231 | int sjson_get_int(sjson_node* parent, const char* key, int default_val); 232 | int64_t sjson_get_int64(sjson_node* parent, const char* key, int64_t default_val); 233 | float sjson_get_float(sjson_node* parent, const char* key, float default_val); 234 | double sjson_get_double(sjson_node* parent, const char* key, double default_val); 235 | const char* sjson_get_string(sjson_node* parent, const char* key, const char* default_val); 236 | bool sjson_get_bool(sjson_node* parent, const char* key, bool default_val); 237 | bool sjson_get_floats(float* out, int count, sjson_node* parent, const char* key); 238 | bool sjson_get_ints(int* out, int count, sjson_node* parent, const char* key); 239 | bool sjson_get_uints(uint32_t* out, int count, sjson_node* parent, const char* key); 240 | bool sjson_get_int16s(int16_t* out, int count, sjson_node* parent, const char* key); 241 | bool sjson_get_uint16s(uint16_t* out, int count, sjson_node* parent, const char* key); 242 | 243 | #define sjson_foreach(i, object_or_array) \ 244 | for ((i) = sjson_first_child(object_or_array); \ 245 | (i) != NULL; \ 246 | (i) = (i)->next) 247 | 248 | // Construction and manipulation 249 | sjson_node* sjson_mknull(sjson_context* ctx); 250 | sjson_node* sjson_mkbool(sjson_context* ctx, bool b); 251 | sjson_node* sjson_mkstring(sjson_context* ctx, const char *s); 252 | sjson_node* sjson_mknumber(sjson_context* ctx, double n); 253 | sjson_node* sjson_mkarray(sjson_context* ctx); 254 | sjson_node* sjson_mkobject(sjson_context* ctx); 255 | 256 | void sjson_append_element(sjson_node* array, sjson_node* element); 257 | void sjson_prepend_element(sjson_node* array, sjson_node* element); 258 | void sjson_append_member(sjson_context* ctx, sjson_node* object, const char* key, sjson_node* value); 259 | void sjson_prepend_member(sjson_context* ctx, sjson_node* object, const char* key, sjson_node* value); 260 | 261 | void sjson_remove_from_parent(sjson_node* node); 262 | 263 | void sjson_delete_node(sjson_context* ctx, sjson_node* node); 264 | 265 | // Higher level construction 266 | sjson_node* sjson_put_obj(sjson_context* ctx, sjson_node* parent, const char* key); 267 | sjson_node* sjson_put_array(sjson_context* ctx, sjson_node* parent, const char* key); 268 | sjson_node* sjson_put_int(sjson_context* ctx, sjson_node* parent, const char* key, int val); 269 | sjson_node* sjson_put_int64(sjson_context* ctx, sjson_node* parent, const char* key, int64_t val); 270 | sjson_node* sjson_put_float(sjson_context* ctx, sjson_node* parent, const char* key, float val); 271 | sjson_node* sjson_put_double(sjson_context* ctx, sjson_node* parent, const char* key, double val); 272 | sjson_node* sjson_put_bool(sjson_context* ctx, sjson_node* parent, const char* key, bool val); 273 | sjson_node* sjson_put_string(sjson_context* ctx, sjson_node* parent, const char* key, const char* val); 274 | sjson_node* sjson_put_floats(sjson_context* ctx, sjson_node* parent, const char* key, const float* vals, int count); 275 | sjson_node* sjson_put_ints(sjson_context* ctx, sjson_node* parent, const char* key, const int* vals, int count); 276 | sjson_node* sjson_put_strings(sjson_context* ctx, sjson_node* parent, const char* key, const char** vals, int count); 277 | sjson_node* sjson_put_uints(sjson_context* ctx, sjson_node* parent, const char* key, const uint32_t* vals, int count); 278 | sjson_node* sjson_put_int16s(sjson_context* ctx, sjson_node* parent, const char* key, const int16_t* vals, int count); 279 | sjson_node* sjson_put_uint16s(sjson_context* ctx, sjson_node* parent, const char* key, const uint16_t* vals, int count); 280 | 281 | // Debugging 282 | 283 | /* 284 | * Look for structure and encoding problems in a sjson_node or its descendents. 285 | * 286 | * If a problem is detected, return false, writing a description of the problem 287 | * to errmsg (unless errmsg is NULL). 288 | */ 289 | bool sjson_check(const sjson_node* node, char errmsg[256]); 290 | 291 | #ifdef __cplusplus 292 | } 293 | #endif 294 | 295 | #endif // SJSON_H_ 296 | 297 | #if defined(SJSON_IMPLEMENT) 298 | 299 | /* 300 | Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) 301 | All rights reserved. 302 | 303 | Permission is hereby granted, free of charge, to any person obtaining a copy 304 | of this software and associated documentation files (the "Software"), to deal 305 | in the Software without restriction, including without limitation the rights 306 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 307 | copies of the Software, and to permit persons to whom the Software is 308 | furnished to do so, subject to the following conditions: 309 | 310 | The above copyright notice and this permission notice shall be included in 311 | all copies or substantial portions of the Software. 312 | 313 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 314 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 315 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 316 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 317 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 318 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 319 | THE SOFTWARE. 320 | */ 321 | 322 | #ifndef sjson_strcpy 323 | # define __STDC_WANT_LIB_EXT1__ 1 324 | #endif 325 | 326 | #include 327 | 328 | #ifndef sjson_malloc 329 | # include 330 | # define sjson_malloc(user, size) malloc(size) 331 | # define sjson_free(user, ptr) free(ptr) 332 | # define sjson_realloc(user, ptr, size) realloc(ptr, size) 333 | #endif 334 | 335 | #ifndef sjson_assert 336 | # include 337 | # define sjson_assert(_e) assert(_e) 338 | #endif 339 | 340 | #ifndef sjson_out_of_memory 341 | # define sjson_out_of_memory() do { sjson_assert(0 && "Out of memory"); exit(EXIT_FAILURE); } while(0) 342 | #endif 343 | 344 | #ifndef sjson_snprintf 345 | # include 346 | # define sjson_snprintf snprintf 347 | #endif 348 | 349 | #ifndef sjson_strcmp 350 | # include 351 | # define sjson_strcmp(_a, _b) strcmp(_a, _b) 352 | #endif 353 | 354 | #ifndef sjson_memset 355 | # include 356 | # define sjson_memset(_p, _b, _n) memset(_p, _b, _n) 357 | #endif 358 | 359 | #ifndef sjson_memcpy 360 | # include 361 | # define sjson_memcpy(_a, _b, _n) memcpy(_a, _b, _n) 362 | #endif 363 | 364 | #ifndef sjson_stricmp 365 | # ifdef _WIN32 366 | # include 367 | # define sjson_stricmp(_a, _b) _stricmp(_a, _b) 368 | # else 369 | # include 370 | # define sjson_stricmp(_a, _b) strcasecmp(_a, _b) 371 | # endif 372 | #endif 373 | 374 | #ifndef sjson_strlen 375 | # include 376 | # define sjson_strlen(_str) strlen(_str) 377 | #endif 378 | 379 | #ifndef sjson_strcpy 380 | # include 381 | # ifdef _MSC_VER 382 | # define sjson_strcpy(_a, _s, _b) strcpy_s(_a, _s, _b) 383 | # else 384 | # define sjson_strcpy(_a, _s, _b) strcpy(_a, _b) 385 | # endif 386 | #endif 387 | 388 | #define sjson__align_mask(_value, _mask) ( ( (_value)+(_mask) ) & ( (~0)&(~(_mask) ) ) ) 389 | 390 | typedef struct sjson__str_page 391 | { 392 | int offset; 393 | int size; 394 | struct sjson__str_page* next; 395 | struct sjson__str_page* prev; 396 | } sjson__str_page; 397 | 398 | typedef struct sjson__node_page 399 | { 400 | int iter; 401 | int capacity; 402 | sjson_node** ptrs; 403 | sjson_node* buff; 404 | struct sjson__node_page* next; 405 | struct sjson__node_page* prev; 406 | } sjson__node_page; 407 | 408 | typedef struct sjson_context 409 | { 410 | void* alloc_user; 411 | int pool_size; 412 | int str_buffer_size; 413 | sjson__node_page* node_pages; 414 | sjson__str_page* str_pages; 415 | sjson__str_page* cur_str_page; 416 | char* cur_str; 417 | } sjson_context; 418 | 419 | //////////////////////////////////////////////////////////////////////////////////////////////////// 420 | // Growable string buffer 421 | typedef struct sjson__sb 422 | { 423 | char *cur; 424 | char *end; 425 | char *start; 426 | } sjson__sb; 427 | 428 | static inline void sjson__sb_init(sjson_context* ctx, sjson__sb* sb, int reserve_sz) 429 | { 430 | int ssize = reserve_sz >= ctx->str_buffer_size ? reserve_sz : ctx->str_buffer_size; 431 | sb->start = (char*) sjson_malloc(ctx->alloc_user, ssize); 432 | if (sb->start == NULL) 433 | sjson_out_of_memory(); 434 | sb->cur = sb->start; 435 | sb->end = sb->start + ssize - 1; 436 | } 437 | 438 | /* sb and need may be evaluated multiple times. */ 439 | #define sjson__sb_need(ctx, sb, need) do { \ 440 | if ((sb)->end - (sb)->cur < (need)) \ 441 | sjson__sb_grow(ctx, sb, need); \ 442 | } while (0) 443 | 444 | static inline void sjson__sb_grow(sjson_context* ctx, sjson__sb* sb, int need) 445 | { 446 | size_t length = sb->cur - sb->start; 447 | size_t alloc = sb->end - sb->start + 1; 448 | 449 | do { 450 | alloc <<= 1; 451 | } while (alloc <= length + need); 452 | 453 | sb->start = (char*)sjson_realloc(ctx->alloc_user, sb->start, alloc); 454 | if (sb->start == NULL) { 455 | sjson_out_of_memory(); 456 | } 457 | sb->cur = sb->start + length; 458 | sb->end = sb->start + alloc - 1; 459 | } 460 | 461 | static inline void sjson__sb_put(sjson_context* ctx, sjson__sb* sb, const char *bytes, int count) 462 | { 463 | sjson__sb_need(ctx, sb, count); 464 | sjson_memcpy(sb->cur, bytes, count); 465 | sb->cur += count; 466 | } 467 | 468 | #define sjson__sb_putc(ctx, sb, c) do { \ 469 | if ((sb)->cur >= (sb)->end) \ 470 | sjson__sb_grow(ctx, sb, 1); \ 471 | *(sb)->cur++ = (c); \ 472 | } while (0) 473 | 474 | static inline void sjson__sb_puts(sjson_context* ctx, sjson__sb* sb, const char *str) 475 | { 476 | sjson__sb_put(ctx, sb, str, (int)sjson_strlen(str)); 477 | } 478 | 479 | static inline char* sjson__sb_finish(sjson__sb* sb) 480 | { 481 | *sb->cur = 0; 482 | sjson_assert(sb->start <= sb->cur && sjson_strlen(sb->start) == (int)(intptr_t)(sb->cur - sb->start)); 483 | return sb->start; 484 | } 485 | 486 | static inline void sjson__sb_free(sjson_context* ctx, sjson__sb* sb) 487 | { 488 | sjson_free(ctx->alloc_user, sb->start); 489 | } 490 | 491 | //////////////////////////////////////////////////////////////////////////////////////////////////// 492 | // Node page 493 | static inline sjson__node_page* sjson__page_create(void* alloc_user, int capacity) 494 | { 495 | capacity = sjson__align_mask(capacity, 15); 496 | uint8_t* buff = (uint8_t*)sjson_malloc(alloc_user, 497 | sizeof(sjson__node_page) + (sizeof(sjson_node) + sizeof(sjson_node*))*capacity); 498 | if (!buff) { 499 | sjson_out_of_memory(); 500 | return NULL; 501 | } 502 | 503 | sjson__node_page* page = (sjson__node_page*)buff; 504 | page->iter = capacity; 505 | page->capacity = capacity; 506 | page->next = page->prev = NULL; 507 | 508 | buff += sizeof(sjson__node_page); 509 | page->ptrs = (sjson_node**)buff; 510 | buff += sizeof(sjson_node*)*capacity; 511 | page->buff = (sjson_node*)buff; 512 | 513 | for (int i = 0; i < capacity; i++) 514 | page->ptrs[capacity - i - 1] = &page->buff[i]; 515 | 516 | return page; 517 | } 518 | 519 | static inline void sjson__page_destroy(sjson__node_page* page, void* alloc_user) 520 | { 521 | sjson_assert(page); 522 | page->capacity = page->iter = 0; 523 | sjson_free(alloc_user, page); 524 | } 525 | 526 | static inline sjson_node* sjson__page_new(sjson__node_page* page) 527 | { 528 | if (page->iter > 0) { 529 | return page->ptrs[--page->iter]; 530 | } else { 531 | sjson_assert(0 && "Node page capacity is full"); 532 | return NULL; 533 | } 534 | } 535 | 536 | static inline bool sjson__page_full(const sjson__node_page* page) 537 | { 538 | return page->iter == 0; 539 | } 540 | 541 | static inline bool sjson__page_valid(const sjson__node_page* page, sjson_node* ptr) 542 | { 543 | uintptr_t uptr = (uintptr_t)ptr; 544 | bool inbuf = uptr >= (uintptr_t)page->buff && 545 | uptr < (uintptr_t)(page->buff + page->capacity*sizeof(sjson_node)); 546 | bool valid = (uintptr_t)((uint8_t*)ptr - (uint8_t*)page->buff) % sizeof(sjson_node) == 0; 547 | return inbuf & valid; 548 | } 549 | 550 | static inline void sjson__page_del(sjson__node_page* page, sjson_node* ptr) 551 | { 552 | sjson_assert(page->iter != page->capacity && "Cannot delete more objects"); 553 | sjson_assert(sjson__page_valid(page, ptr) && "Pointer does not belong to page"); 554 | page->ptrs[page->iter++] = ptr; 555 | } 556 | 557 | static inline void sjson__page_add_list(sjson__node_page** pfirst, sjson__node_page* node) 558 | { 559 | if (*pfirst) { 560 | (*pfirst)->prev = node; 561 | node->next = *pfirst; 562 | } 563 | 564 | *pfirst = node; 565 | } 566 | 567 | static inline void sjson__page_remove_list(sjson__node_page** pfirst, sjson__node_page* node) 568 | { 569 | if (node->prev) 570 | node->prev->next = node->next; 571 | if (node->next) 572 | node->next->prev = node->prev; 573 | if (*pfirst == node) 574 | *pfirst = node->next; 575 | node->prev = node->next = NULL; 576 | } 577 | 578 | //////////////////////////////////////////////////////////////////////////////////////////////////// 579 | // json string buffer page 580 | static inline sjson__str_page* sjson__str_page_create(void* alloc_user, int size) 581 | { 582 | size = sjson__align_mask(size, 7); 583 | 584 | // allocate the page and reserve memory for string-buffer after the struct 585 | sjson__str_page* page = (sjson__str_page*)sjson_malloc(alloc_user, size + sizeof(sjson__str_page)); 586 | if (!page) { 587 | sjson_out_of_memory(); 588 | return NULL; 589 | } 590 | 591 | page->offset = 0; 592 | page->size = size; 593 | page->next = page->prev = NULL; 594 | 595 | return page; 596 | } 597 | 598 | static inline void sjson__str_page_destroy(sjson__str_page* page, void* alloc_user) 599 | { 600 | sjson_assert(page); 601 | sjson_free(alloc_user, page); 602 | } 603 | 604 | static inline char* sjson__str_page_ptr(sjson__str_page* page) 605 | { 606 | char* buff = (char*)(page + 1); 607 | return buff + page->offset; 608 | } 609 | 610 | static inline char* sjson__str_page_startptr(sjson__str_page* page) 611 | { 612 | return (char*)(page + 1); 613 | } 614 | 615 | static inline bool sjson__str_page_grow(sjson__str_page* page, int size) 616 | { 617 | if (page->offset + size <= page->size) { 618 | page->offset += size; 619 | return true; 620 | } else { 621 | return false; 622 | } 623 | } 624 | 625 | static inline void sjson__str_page_add_list(sjson__str_page** pfirst, sjson__str_page* node) 626 | { 627 | if (*pfirst) { 628 | (*pfirst)->prev = node; 629 | node->next = *pfirst; 630 | } 631 | *pfirst = node; 632 | } 633 | 634 | static inline void sjson__str_page_remove_list(sjson__str_page** pfirst, sjson__str_page* node) 635 | { 636 | if (node->prev) 637 | node->prev->next = node->next; 638 | if (node->next) 639 | node->next->prev = node->prev; 640 | if (*pfirst == node) 641 | *pfirst = node->next; 642 | node->prev = node->next = NULL; 643 | } 644 | 645 | sjson_context* sjson_create_context(int pool_size, int str_buffer_size, void* alloc_user) 646 | { 647 | sjson_context* ctx = (sjson_context*)sjson_malloc(alloc_user, sizeof(sjson_context)); 648 | if (!ctx) { 649 | sjson_out_of_memory(); 650 | return NULL; 651 | } 652 | sjson_memset(ctx, 0x00, sizeof(sjson_context)); 653 | 654 | if (pool_size <= 0) 655 | pool_size = 512; 656 | if (str_buffer_size <= 0) 657 | str_buffer_size = 4096; 658 | 659 | ctx->alloc_user = alloc_user; 660 | ctx->pool_size = pool_size; 661 | ctx->str_buffer_size = str_buffer_size; 662 | 663 | // Create first pool page 664 | ctx->node_pages = sjson__page_create(alloc_user, pool_size); 665 | if (!ctx->node_pages) 666 | return NULL; 667 | 668 | ctx->str_pages = NULL; 669 | ctx->cur_str_page = NULL; 670 | return ctx; 671 | } 672 | 673 | void sjson_destroy_context(sjson_context* ctx) 674 | { 675 | sjson_assert(ctx); 676 | 677 | // destroy node pages 678 | sjson__node_page* npage = ctx->node_pages; 679 | while (npage) { 680 | sjson__node_page* next = npage->next; 681 | sjson__page_destroy(npage, ctx->alloc_user); 682 | npage = next; 683 | } 684 | 685 | sjson__str_page* spage = ctx->str_pages; 686 | while (spage) { 687 | sjson__str_page* next = spage->next; 688 | sjson__str_page_destroy(spage, ctx->alloc_user); 689 | spage = next; 690 | } 691 | 692 | sjson_free(ctx->alloc_user, ctx); 693 | } 694 | 695 | void sjson_reset_context(sjson_context* ctx) 696 | { 697 | // 698 | for (sjson__node_page* npage = ctx->node_pages; npage; npage = npage->next) { 699 | int capacity = npage->capacity; 700 | npage->iter = capacity; 701 | for (int i = 0; i < capacity; i++) 702 | npage->ptrs[capacity - i - 1] = &npage->buff[i]; 703 | } 704 | 705 | // 706 | for (sjson__str_page* spage = ctx->str_pages; spage; spage = spage->next) { 707 | spage->offset = 0; 708 | } 709 | 710 | // TODO: maybe we can reverse the linked-lists to get better cache coherency 711 | } 712 | 713 | static inline sjson_node* sjson__new_node(sjson_context* ctx, sjson_tag tag) 714 | { 715 | sjson__node_page* npage = ctx->node_pages; 716 | while (npage && sjson__page_full(npage)) 717 | npage = npage->next; 718 | 719 | if (npage == NULL) { 720 | npage = sjson__page_create(ctx->alloc_user, ctx->pool_size); 721 | sjson_assert(npage); 722 | sjson__page_add_list(&ctx->node_pages, npage); 723 | } 724 | 725 | sjson_node* node = sjson__page_new(npage); 726 | sjson_memset(node, 0x0, sizeof(sjson_node)); 727 | node->tag = tag; 728 | return node; 729 | } 730 | 731 | static inline void sjson__del_node(sjson_context* ctx, sjson_node* node) 732 | { 733 | sjson__node_page* npage = ctx->node_pages; 734 | while (npage && !sjson__page_valid(npage, node)) 735 | npage = npage->next; 736 | 737 | sjson_assert(npage && "sjson_node doesn't belong to any page - check the pointer"); 738 | sjson__page_del(npage, node); 739 | } 740 | 741 | // Usage: 742 | static inline char* sjson__str_begin(sjson_context* ctx, int init_sz) 743 | { 744 | sjson_assert (ctx->cur_str_page == NULL && "Should call sjson__str_end before begin"); 745 | 746 | // find a page that can grow to requested size 747 | sjson__str_page* spage = ctx->str_pages; 748 | while (spage && (spage->offset + init_sz) <= spage->size) 749 | spage = spage->next; 750 | 751 | // create a new string page 752 | if (spage == NULL) { 753 | int page_sz = ctx->str_buffer_size; 754 | if (init_sz > (page_sz >> 1)) { 755 | page_sz = page_sz << 1; 756 | page_sz = sjson__align_mask(init_sz, page_sz-1); 757 | } 758 | spage = sjson__str_page_create(ctx->alloc_user, page_sz); 759 | sjson_assert(spage); 760 | sjson__str_page_add_list(&ctx->str_pages, spage); 761 | } 762 | 763 | sjson_assert(spage->offset + init_sz <= spage->size); 764 | char* ptr = sjson__str_page_ptr(spage); 765 | spage->offset += init_sz; 766 | 767 | ctx->cur_str_page = spage; 768 | ctx->cur_str = ptr; 769 | 770 | return ptr; 771 | } 772 | 773 | // Returns the pointer to the begining of the string, pointer may be reallocated and changed from sjson__str_begin 774 | static inline char* sjson__str_grow(sjson_context* ctx, int grow_sz) 775 | { 776 | sjson_assert (ctx->cur_str_page && "Should call sjson__str_begin before grow"); 777 | 778 | sjson__str_page* spage = ctx->cur_str_page; 779 | if (sjson__str_page_grow(spage, grow_sz)) { 780 | return ctx->cur_str; 781 | } else { 782 | int page_sz = spage->size; 783 | int cur_str_sz = (int)(uintptr_t)(sjson__str_page_ptr(spage) - ctx->cur_str); 784 | int total_sz = cur_str_sz + grow_sz; 785 | if (total_sz > (page_sz >> 1)) { 786 | page_sz <<= 1; 787 | page_sz = total_sz > page_sz ? total_sz : page_sz; 788 | } 789 | 790 | sjson__str_page* newspage = sjson__str_page_create(ctx->alloc_user, page_sz); 791 | sjson_assert(newspage); 792 | sjson_assert(total_sz <= newspage->size); 793 | char* ptr = sjson__str_page_startptr(newspage); 794 | newspage->offset += total_sz; 795 | 796 | // copy previous buffer into the new one 797 | if (cur_str_sz > 0) 798 | sjson_memcpy(ptr, ctx->cur_str, cur_str_sz); 799 | 800 | // check and see if we can remove the old page completely 801 | if (ctx->cur_str == sjson__str_page_startptr(spage)) { 802 | sjson__str_page_remove_list(&ctx->str_pages, spage); 803 | sjson__str_page_destroy(spage, ctx->alloc_user); 804 | } 805 | 806 | ctx->cur_str_page = newspage; 807 | ctx->cur_str = ptr; 808 | return ptr; 809 | } 810 | } 811 | 812 | static inline char* sjson__str_end(sjson_context* ctx) 813 | { 814 | sjson_assert (ctx->cur_str_page && "Should call sjson__str_begin before end"); 815 | char* ptr = ctx->cur_str; 816 | ctx->cur_str_page = NULL; 817 | ctx->cur_str = NULL; 818 | return ptr; 819 | } 820 | 821 | static char* sjson__strdup(sjson_context* ctx, const char *str) 822 | { 823 | int len = (int)sjson_strlen(str); 824 | char* ret = sjson__str_begin(ctx, len + 1); 825 | sjson_memcpy(ret, str, len); 826 | ret[len] = '\0'; 827 | sjson__str_end(ctx); 828 | return ret; 829 | } 830 | 831 | /* 832 | * Unicode helper functions 833 | * 834 | * These are taken from the ccan/charset module and customized a bit. 835 | * Putting them here means the compiler can (choose to) inline them, 836 | * and it keeps ccan/json from having a dependency. 837 | */ 838 | 839 | /* 840 | * Type for Unicode codepoints. 841 | * We need our own because wchar_t might be 16 bits. 842 | */ 843 | typedef uint32_t json__uchar_t; 844 | 845 | /* 846 | * Validate a single UTF-8 character starting at @s. 847 | * The string must be null-terminated. 848 | * 849 | * If it's valid, return its length (1 thru 4). 850 | * If it's invalid or clipped, return 0. 851 | * 852 | * This function implements the syntax given in RFC3629, which is 853 | * the same as that given in The Unicode Standard, Version 6.0. 854 | * 855 | * It has the following properties: 856 | * 857 | * * All codepoints U+0000..U+10FFFF may be encoded, 858 | * except for U+D800..U+DFFF, which are reserved 859 | * for UTF-16 surrogate pair encoding. 860 | * * UTF-8 byte sequences longer than 4 bytes are not permitted, 861 | * as they exceed the range of Unicode. 862 | * * The sixty-six Unicode "non-characters" are permitted 863 | * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). 864 | */ 865 | static int sjson__utf8_validate_cz(const char *s) 866 | { 867 | unsigned char c = *s++; 868 | 869 | if (c <= 0x7F) { /* 00..7F */ 870 | return 1; 871 | } else if (c <= 0xC1) { /* 80..C1 */ 872 | /* Disallow overlong 2-byte sequence. */ 873 | return 0; 874 | } else if (c <= 0xDF) { /* C2..DF */ 875 | /* Make sure subsequent byte is in the range 0x80..0xBF. */ 876 | if (((unsigned char)*s++ & 0xC0) != 0x80) 877 | return 0; 878 | 879 | return 2; 880 | } else if (c <= 0xEF) { /* E0..EF */ 881 | /* Disallow overlong 3-byte sequence. */ 882 | if (c == 0xE0 && (unsigned char)*s < 0xA0) 883 | return 0; 884 | 885 | /* Disallow U+D800..U+DFFF. */ 886 | if (c == 0xED && (unsigned char)*s > 0x9F) 887 | return 0; 888 | 889 | /* Make sure subsequent bytes are in the range 0x80..0xBF. */ 890 | if (((unsigned char)*s++ & 0xC0) != 0x80) 891 | return 0; 892 | if (((unsigned char)*s++ & 0xC0) != 0x80) 893 | return 0; 894 | 895 | return 3; 896 | } else if (c <= 0xF4) { /* F0..F4 */ 897 | /* Disallow overlong 4-byte sequence. */ 898 | if (c == 0xF0 && (unsigned char)*s < 0x90) 899 | return 0; 900 | 901 | /* Disallow codepoints beyond U+10FFFF. */ 902 | if (c == 0xF4 && (unsigned char)*s > 0x8F) 903 | return 0; 904 | 905 | /* Make sure subsequent bytes are in the range 0x80..0xBF. */ 906 | if (((unsigned char)*s++ & 0xC0) != 0x80) 907 | return 0; 908 | if (((unsigned char)*s++ & 0xC0) != 0x80) 909 | return 0; 910 | if (((unsigned char)*s++ & 0xC0) != 0x80) 911 | return 0; 912 | 913 | return 4; 914 | } else { /* F5..FF */ 915 | return 0; 916 | } 917 | } 918 | 919 | /* Validate a null-terminated UTF-8 string. */ 920 | static bool sjson__utf8_validate(const char *s) 921 | { 922 | int len; 923 | 924 | for (; *s != 0; s += len) { 925 | len = sjson__utf8_validate_cz(s); 926 | if (len == 0) 927 | return false; 928 | } 929 | 930 | return true; 931 | } 932 | 933 | /* 934 | * Read a single UTF-8 character starting at @s, 935 | * returning the length, in bytes, of the character read. 936 | * 937 | * This function assumes input is valid UTF-8, 938 | * and that there are enough characters in front of @s. 939 | */ 940 | static int sjson__utf8_read_char(const char *s, json__uchar_t *out) 941 | { 942 | const unsigned char *c = (const unsigned char*) s; 943 | 944 | sjson_assert(sjson__utf8_validate_cz(s)); 945 | 946 | if (c[0] <= 0x7F) { 947 | /* 00..7F */ 948 | *out = c[0]; 949 | return 1; 950 | } else if (c[0] <= 0xDF) { 951 | /* C2..DF (unless input is invalid) */ 952 | *out = ((json__uchar_t)c[0] & 0x1F) << 6 | 953 | ((json__uchar_t)c[1] & 0x3F); 954 | return 2; 955 | } else if (c[0] <= 0xEF) { 956 | /* E0..EF */ 957 | *out = ((json__uchar_t)c[0] & 0xF) << 12 | 958 | ((json__uchar_t)c[1] & 0x3F) << 6 | 959 | ((json__uchar_t)c[2] & 0x3F); 960 | return 3; 961 | } else { 962 | /* F0..F4 (unless input is invalid) */ 963 | *out = ((json__uchar_t)c[0] & 0x7) << 18 | 964 | ((json__uchar_t)c[1] & 0x3F) << 12 | 965 | ((json__uchar_t)c[2] & 0x3F) << 6 | 966 | ((json__uchar_t)c[3] & 0x3F); 967 | return 4; 968 | } 969 | } 970 | 971 | /* 972 | * Write a single UTF-8 character to @s, 973 | * returning the length, in bytes, of the character written. 974 | * 975 | * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. 976 | * 977 | * This function will write up to 4 bytes to @out. 978 | */ 979 | static int sjson__utf8_write_char(json__uchar_t unicode, char *out) 980 | { 981 | unsigned char *o = (unsigned char*) out; 982 | 983 | sjson_assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF)); 984 | 985 | if (unicode <= 0x7F) { 986 | /* U+0000..U+007F */ 987 | *o++ = unicode; 988 | return 1; 989 | } else if (unicode <= 0x7FF) { 990 | /* U+0080..U+07FF */ 991 | *o++ = 0xC0 | unicode >> 6; 992 | *o++ = 0x80 | (unicode & 0x3F); 993 | return 2; 994 | } else if (unicode <= 0xFFFF) { 995 | /* U+0800..U+FFFF */ 996 | *o++ = 0xE0 | unicode >> 12; 997 | *o++ = 0x80 | (unicode >> 6 & 0x3F); 998 | *o++ = 0x80 | (unicode & 0x3F); 999 | return 3; 1000 | } else { 1001 | /* U+10000..U+10FFFF */ 1002 | *o++ = 0xF0 | unicode >> 18; 1003 | *o++ = 0x80 | (unicode >> 12 & 0x3F); 1004 | *o++ = 0x80 | (unicode >> 6 & 0x3F); 1005 | *o++ = 0x80 | (unicode & 0x3F); 1006 | return 4; 1007 | } 1008 | } 1009 | 1010 | /* 1011 | * Compute the Unicode codepoint of a UTF-16 surrogate pair. 1012 | * 1013 | * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. 1014 | * If they aren't, this function returns false. 1015 | */ 1016 | static bool sjson__from_surrogate_pair(uint16_t uc, uint16_t lc, json__uchar_t *unicode) 1017 | { 1018 | if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { 1019 | *unicode = 0x10000 + ((((json__uchar_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); 1020 | return true; 1021 | } else { 1022 | return false; 1023 | } 1024 | } 1025 | 1026 | /* 1027 | * Construct a UTF-16 surrogate pair given a Unicode codepoint. 1028 | * 1029 | * @unicode must be U+10000..U+10FFFF. 1030 | */ 1031 | static void sjson__to_surrogate_pair(json__uchar_t unicode, uint16_t *uc, uint16_t *lc) 1032 | { 1033 | json__uchar_t n; 1034 | 1035 | sjson_assert(unicode >= 0x10000 && unicode <= 0x10FFFF); 1036 | 1037 | n = unicode - 0x10000; 1038 | *uc = ((n >> 10) & 0x3FF) | 0xD800; 1039 | *lc = (n & 0x3FF) | 0xDC00; 1040 | } 1041 | 1042 | #define sjson__is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ') 1043 | #define sjson__is_digit(c) ((c) >= '0' && (c) <= '9') 1044 | 1045 | static bool sjson__parse_value(sjson_context* ctx, const char **sp, sjson_node **out); 1046 | static bool sjson__parse_string(sjson_context* ctx, const char **sp, char **out); 1047 | static bool sjson__parse_number(const char **sp, double *out); 1048 | static bool sjson__parse_array(sjson_context* ctx, const char **sp, sjson_node **out); 1049 | static bool sjson__parse_object(sjson_context* ctx, const char **sp, sjson_node **out); 1050 | static bool sjson__parse_hex16(const char **sp, uint16_t *out); 1051 | 1052 | static bool sjson__expect_literal(const char **sp, const char *str); 1053 | static void sjson__skip_space(const char **sp); 1054 | 1055 | static void sjson__emit_value(sjson_context* ctx, sjson__sb* out, const sjson_node *node); 1056 | static void sjson__emit_value_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *node, const char *space, int indent_level); 1057 | static void sjson__emit_string(sjson_context* ctx, sjson__sb* out, const char *str); 1058 | static void sjson__emit_number(sjson_context* ctx, sjson__sb* out, double num); 1059 | static void sjson__emit_array(sjson_context* ctx, sjson__sb* out, const sjson_node* array); 1060 | static void sjson__emit_array_indented(sjson_context* ctx, sjson__sb* out, const sjson_node* array, const char *space, int indent_level); 1061 | static void sjson__emit_object(sjson_context* ctx, sjson__sb* out, const sjson_node *object); 1062 | static void sjson__emit_object_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *object, const char *space, int indent_level); 1063 | 1064 | static int sjson__write_hex16(char *out, uint16_t val); 1065 | 1066 | static void sjson__append_node(sjson_node *parent, sjson_node *child); 1067 | static void sjson__prepend_node(sjson_node *parent, sjson_node *child); 1068 | static void sjson__append_member(sjson_node *object, char *key, sjson_node *value); 1069 | 1070 | /* Assertion-friendly validity checks */ 1071 | static bool sjson__tag_is_valid(unsigned int tag); 1072 | static bool sjson__number_is_valid(const char *num); 1073 | 1074 | sjson_node *sjson_decode(sjson_context* ctx, const char *json) 1075 | { 1076 | const char *s = json; 1077 | sjson_node *ret; 1078 | 1079 | sjson__skip_space(&s); 1080 | if (!sjson__parse_value(ctx, &s, &ret)) 1081 | return NULL; 1082 | 1083 | sjson__skip_space(&s); 1084 | if (*s != 0) { 1085 | sjson_delete_node(ctx, ret); 1086 | return NULL; 1087 | } 1088 | 1089 | return ret; 1090 | } 1091 | 1092 | char* sjson_encode(sjson_context* ctx, const sjson_node *node) 1093 | { 1094 | return sjson_stringify(ctx, node, NULL); 1095 | } 1096 | 1097 | char* sjson_encode_string(sjson_context* ctx, const char *str) 1098 | { 1099 | sjson__sb sb; 1100 | sjson__sb_init(ctx, &sb, ctx->str_buffer_size); 1101 | 1102 | sjson__emit_string(ctx, &sb, str); 1103 | 1104 | return sjson__sb_finish(&sb); 1105 | } 1106 | 1107 | char* sjson_stringify(sjson_context* ctx, const sjson_node *node, const char *space) 1108 | { 1109 | sjson__sb sb; 1110 | sjson__sb_init(ctx, &sb, ctx->str_buffer_size); 1111 | 1112 | if (space != NULL) 1113 | sjson__emit_value_indented(ctx, &sb, node, space, 0); 1114 | else 1115 | sjson__emit_value(ctx, &sb, node); 1116 | 1117 | return sjson__sb_finish(&sb); 1118 | } 1119 | 1120 | void sjson_free_string(sjson_context* ctx, char* str) 1121 | { 1122 | sjson_free(ctx->alloc_user, str); 1123 | } 1124 | 1125 | void sjson_delete_node(sjson_context* ctx, sjson_node *node) 1126 | { 1127 | if (node != NULL) { 1128 | sjson_remove_from_parent(node); 1129 | 1130 | switch (node->tag) { 1131 | case SJSON_ARRAY: 1132 | case SJSON_OBJECT: 1133 | { 1134 | sjson_node *child, *next; 1135 | for (child = node->children.head; child != NULL; child = next) { 1136 | next = child->next; 1137 | sjson_delete_node(ctx, child); 1138 | } 1139 | break; 1140 | } 1141 | default:; 1142 | } 1143 | 1144 | sjson__del_node(ctx, node); 1145 | } 1146 | } 1147 | 1148 | bool sjson_validate(sjson_context* ctx, const char *json) 1149 | { 1150 | const char *s = json; 1151 | 1152 | sjson__skip_space(&s); 1153 | if (!sjson__parse_value(ctx, &s, NULL)) 1154 | return false; 1155 | 1156 | sjson__skip_space(&s); 1157 | if (*s != 0) 1158 | return false; 1159 | 1160 | return true; 1161 | } 1162 | 1163 | sjson_node *sjson_find_element(sjson_node *array, int index) 1164 | { 1165 | sjson_node *element; 1166 | int i = 0; 1167 | 1168 | if (array == NULL || array->tag != SJSON_ARRAY) 1169 | return NULL; 1170 | 1171 | sjson_foreach(element, array) { 1172 | if (i == index) 1173 | return element; 1174 | i++; 1175 | } 1176 | 1177 | return NULL; 1178 | } 1179 | 1180 | sjson_node *sjson_find_member(sjson_node *object, const char *name) 1181 | { 1182 | sjson_node *member; 1183 | 1184 | if (object == NULL || object->tag != SJSON_OBJECT) 1185 | return NULL; 1186 | 1187 | sjson_foreach(member, object) 1188 | if (sjson_strcmp(member->key, name) == 0) 1189 | return member; 1190 | 1191 | return NULL; 1192 | } 1193 | 1194 | sjson_node* sjson_find_member_nocase(sjson_node* object, const char *name) 1195 | { 1196 | sjson_node *member; 1197 | 1198 | if (object == NULL || object->tag != SJSON_OBJECT) 1199 | return NULL; 1200 | 1201 | sjson_foreach(member, object) 1202 | if (sjson_stricmp(member->key, name) == 0) 1203 | return member; 1204 | 1205 | return NULL; 1206 | } 1207 | 1208 | sjson_node *sjson_first_child(const sjson_node *node) 1209 | { 1210 | if (node != NULL && (node->tag == SJSON_ARRAY || node->tag == SJSON_OBJECT)) 1211 | return node->children.head; 1212 | return NULL; 1213 | } 1214 | 1215 | int sjson_child_count(const sjson_node* node) 1216 | { 1217 | int count = 0; 1218 | sjson_node* item; 1219 | sjson_foreach(item, node) ++count; 1220 | return count; 1221 | } 1222 | 1223 | 1224 | int sjson_get_int(sjson_node* parent, const char* key, int default_val) 1225 | { 1226 | sjson_node* p = sjson_find_member(parent, key); 1227 | if (p) { 1228 | sjson_assert(p->tag == SJSON_NUMBER); 1229 | return (int)p->number_; 1230 | } else { 1231 | return default_val; 1232 | } 1233 | } 1234 | 1235 | int64_t sjson_get_int64(sjson_node* parent, const char* key, int64_t default_val) 1236 | { 1237 | sjson_node* p = sjson_find_member(parent, key); 1238 | if (p) { 1239 | sjson_assert(p->tag == SJSON_NUMBER); 1240 | return (int64_t)p->number_; 1241 | } else { 1242 | return default_val; 1243 | } 1244 | } 1245 | 1246 | float sjson_get_float(sjson_node* parent, const char* key, float default_val) 1247 | { 1248 | sjson_node* p = sjson_find_member(parent, key); 1249 | if (p) { 1250 | sjson_assert(p->tag == SJSON_NUMBER); 1251 | return (float)p->number_; 1252 | } else { 1253 | return default_val; 1254 | } 1255 | } 1256 | 1257 | double sjson_get_double(sjson_node* parent, const char* key, double default_val) 1258 | { 1259 | sjson_node* p = sjson_find_member(parent, key); 1260 | if (p) { 1261 | sjson_assert(p->tag == SJSON_NUMBER); 1262 | return p->number_; 1263 | } else { 1264 | return default_val; 1265 | } 1266 | } 1267 | 1268 | const char* sjson_get_string(sjson_node* parent, const char* key, const char* default_val) 1269 | { 1270 | sjson_node* p = sjson_find_member(parent, key); 1271 | if (p) { 1272 | sjson_assert(p->tag == SJSON_STRING); 1273 | return p->string_; 1274 | } else { 1275 | return default_val; 1276 | } 1277 | } 1278 | 1279 | bool sjson_get_bool(sjson_node* parent, const char* key, bool default_val) 1280 | { 1281 | sjson_node* p = sjson_find_member(parent, key); 1282 | if (p) { 1283 | sjson_assert(p->tag == SJSON_BOOL); 1284 | return p->bool_; 1285 | } else { 1286 | return default_val; 1287 | } 1288 | } 1289 | 1290 | bool sjson_get_floats(float* out, int count, sjson_node* parent, const char* key) 1291 | { 1292 | sjson_node* p = key ? sjson_find_member(parent, key) : parent; 1293 | if (p) { 1294 | sjson_assert(p->tag == SJSON_ARRAY); 1295 | int index = 0; 1296 | for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { 1297 | sjson_assert(elem->tag == SJSON_NUMBER); 1298 | out[index++] = (float)elem->number_; 1299 | } 1300 | return index == count; 1301 | } else { 1302 | return false; 1303 | } 1304 | } 1305 | 1306 | bool sjson_get_ints(int* out, int count, sjson_node* parent, const char* key) 1307 | { 1308 | sjson_node* p = key ? sjson_find_member(parent, key) : parent; 1309 | if (p) { 1310 | sjson_assert(p->tag == SJSON_ARRAY); 1311 | int index = 0; 1312 | for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { 1313 | sjson_assert(elem->tag == SJSON_NUMBER); 1314 | out[index++] = (int)elem->number_; 1315 | } 1316 | return index == count; 1317 | } else { 1318 | return false; 1319 | } 1320 | } 1321 | 1322 | bool sjson_get_uints(uint32_t* out, int count, sjson_node* parent, const char* key) 1323 | { 1324 | sjson_node* p = key ? sjson_find_member(parent, key) : parent; 1325 | if (p) { 1326 | sjson_assert(p->tag == SJSON_ARRAY); 1327 | int index = 0; 1328 | for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { 1329 | sjson_assert(elem->tag == SJSON_NUMBER); 1330 | out[index++] = (uint32_t)elem->number_; 1331 | } 1332 | return index == count; 1333 | } else { 1334 | return false; 1335 | } 1336 | } 1337 | 1338 | bool sjson_get_int16s(int16_t* out, int count, sjson_node* parent, const char* key) 1339 | { 1340 | sjson_node* p = key ? sjson_find_member(parent, key) : parent; 1341 | if (p) { 1342 | sjson_assert(p->tag == SJSON_ARRAY); 1343 | int index = 0; 1344 | for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { 1345 | sjson_assert(elem->tag == SJSON_NUMBER); 1346 | out[index++] = (int16_t)elem->number_; 1347 | } 1348 | return index == count; 1349 | } else { 1350 | return false; 1351 | } 1352 | } 1353 | 1354 | bool sjson_get_uint16s(uint16_t* out, int count, sjson_node* parent, const char* key) 1355 | { 1356 | sjson_node* p = key ? sjson_find_member(parent, key) : parent; 1357 | if (p) { 1358 | sjson_assert(p->tag == SJSON_ARRAY); 1359 | int index = 0; 1360 | for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { 1361 | sjson_assert(elem->tag == SJSON_NUMBER); 1362 | out[index++] = (uint16_t)elem->number_; 1363 | } 1364 | return index == count; 1365 | } else { 1366 | return false; 1367 | } 1368 | } 1369 | 1370 | sjson_node *sjson_mknull(sjson_context* ctx) 1371 | { 1372 | return sjson__new_node(ctx, SJSON_NULL); 1373 | } 1374 | 1375 | sjson_node *sjson_mkbool(sjson_context* ctx, bool b) 1376 | { 1377 | sjson_node *ret = sjson__new_node(ctx, SJSON_BOOL); 1378 | ret->bool_ = b; 1379 | return ret; 1380 | } 1381 | 1382 | static sjson_node *sjson__mkstring(sjson_context* ctx, char *s) 1383 | { 1384 | sjson_node *ret = sjson__new_node(ctx, SJSON_STRING); 1385 | ret->string_ = s; 1386 | return ret; 1387 | } 1388 | 1389 | sjson_node *sjson_mkstring(sjson_context* ctx, const char *s) 1390 | { 1391 | return sjson__mkstring(ctx, sjson__strdup(ctx, s)); 1392 | } 1393 | 1394 | sjson_node *sjson_mknumber(sjson_context* ctx, double n) 1395 | { 1396 | sjson_node *node = sjson__new_node(ctx, SJSON_NUMBER); 1397 | node->number_ = n; 1398 | return node; 1399 | } 1400 | 1401 | sjson_node *sjson_mkarray(sjson_context* ctx) 1402 | { 1403 | return sjson__new_node(ctx, SJSON_ARRAY); 1404 | } 1405 | 1406 | sjson_node *sjson_mkobject(sjson_context* ctx) 1407 | { 1408 | return sjson__new_node(ctx, SJSON_OBJECT); 1409 | } 1410 | 1411 | static void sjson__append_node(sjson_node *parent, sjson_node *child) 1412 | { 1413 | child->parent = parent; 1414 | child->prev = parent->children.tail; 1415 | child->next = NULL; 1416 | 1417 | if (parent->children.tail != NULL) 1418 | parent->children.tail->next = child; 1419 | else 1420 | parent->children.head = child; 1421 | parent->children.tail = child; 1422 | } 1423 | 1424 | static void sjson__prepend_node(sjson_node *parent, sjson_node *child) 1425 | { 1426 | child->parent = parent; 1427 | child->prev = NULL; 1428 | child->next = parent->children.head; 1429 | 1430 | if (parent->children.head != NULL) 1431 | parent->children.head->prev = child; 1432 | else 1433 | parent->children.tail = child; 1434 | parent->children.head = child; 1435 | } 1436 | 1437 | static void sjson__append_member(sjson_node *object, char *key, sjson_node *value) 1438 | { 1439 | value->key = key; 1440 | sjson__append_node(object, value); 1441 | } 1442 | 1443 | void sjson_append_element(sjson_node *array, sjson_node *element) 1444 | { 1445 | sjson_assert(array->tag == SJSON_ARRAY); 1446 | sjson_assert(element->parent == NULL); 1447 | 1448 | sjson__append_node(array, element); 1449 | } 1450 | 1451 | void sjson_prepend_element(sjson_node *array, sjson_node *element) 1452 | { 1453 | sjson_assert(array->tag == SJSON_ARRAY); 1454 | sjson_assert(element->parent == NULL); 1455 | 1456 | sjson__prepend_node(array, element); 1457 | } 1458 | 1459 | void sjson_append_member(sjson_context* ctx, sjson_node *object, const char *key, sjson_node *value) 1460 | { 1461 | sjson_assert(object->tag == SJSON_OBJECT); 1462 | sjson_assert(value->parent == NULL); 1463 | 1464 | sjson__append_member(object, sjson__strdup(ctx, key), value); 1465 | } 1466 | 1467 | void sjson_prepend_member(sjson_context* ctx, sjson_node *object, const char *key, sjson_node *value) 1468 | { 1469 | sjson_assert(object->tag == SJSON_OBJECT); 1470 | sjson_assert(value->parent == NULL); 1471 | 1472 | value->key = sjson__strdup(ctx, key); 1473 | sjson__prepend_node(object, value); 1474 | } 1475 | 1476 | void sjson_remove_from_parent(sjson_node *node) 1477 | { 1478 | sjson_node *parent = node->parent; 1479 | 1480 | if (parent != NULL) { 1481 | if (node->prev != NULL) 1482 | node->prev->next = node->next; 1483 | else 1484 | parent->children.head = node->next; 1485 | if (node->next != NULL) 1486 | node->next->prev = node->prev; 1487 | else 1488 | parent->children.tail = node->prev; 1489 | 1490 | node->parent = NULL; 1491 | node->prev = node->next = NULL; 1492 | node->key = NULL; 1493 | } 1494 | } 1495 | 1496 | sjson_node* sjson_put_obj(sjson_context* ctx, sjson_node* parent, const char* key) 1497 | { 1498 | sjson_node* n = sjson_mkobject(ctx); 1499 | sjson_append_member(ctx, parent, key, n); 1500 | return n; 1501 | } 1502 | 1503 | sjson_node* sjson_put_array(sjson_context* ctx, sjson_node* parent, const char* key) 1504 | { 1505 | sjson_node* n = sjson_mkarray(ctx); 1506 | sjson_append_member(ctx, parent, key, n); 1507 | return n; 1508 | } 1509 | 1510 | sjson_node* sjson_put_int(sjson_context* ctx, sjson_node* parent, const char* key, int val) 1511 | { 1512 | sjson_node* n = sjson_mknumber(ctx, (double)val); 1513 | sjson_assert(n); 1514 | sjson_append_member(ctx, parent, key, n); 1515 | return n; 1516 | } 1517 | 1518 | sjson_node* sjson_put_int64(sjson_context* ctx, sjson_node* parent, const char* key, int64_t val) 1519 | { 1520 | sjson_node* n = sjson_mknumber(ctx, (double)val); 1521 | sjson_assert(n); 1522 | sjson_append_member(ctx, parent, key, n); 1523 | return n; 1524 | } 1525 | 1526 | sjson_node* sjson_put_float(sjson_context* ctx, sjson_node* parent, const char* key, float val) 1527 | { 1528 | sjson_node* n = sjson_mknumber(ctx, (double)val); 1529 | sjson_assert(n); 1530 | sjson_append_member(ctx, parent, key, n); 1531 | return n; 1532 | } 1533 | 1534 | sjson_node* sjson_put_double(sjson_context* ctx, sjson_node* parent, const char* key, double val) 1535 | { 1536 | sjson_node* n = sjson_mknumber(ctx, val); 1537 | sjson_assert(n); 1538 | sjson_append_member(ctx, parent, key, n); 1539 | return n; 1540 | } 1541 | 1542 | sjson_node* sjson_put_bool(sjson_context* ctx, sjson_node* parent, const char* key, bool val) 1543 | { 1544 | sjson_node* n = sjson_mkbool(ctx, val); 1545 | sjson_assert(n); 1546 | sjson_append_member(ctx, parent, key, n); 1547 | return n; 1548 | } 1549 | 1550 | sjson_node* sjson_put_string(sjson_context* ctx, sjson_node* parent, const char* key, const char* val) 1551 | { 1552 | sjson_node* n = sjson_mkstring(ctx, val); 1553 | sjson_assert(n); 1554 | sjson_append_member(ctx, parent, key, n); 1555 | return n; 1556 | } 1557 | 1558 | sjson_node* sjson_put_floats(sjson_context* ctx, sjson_node* parent, const char* key, const float* vals, int count) 1559 | { 1560 | sjson_node* a = sjson_mkarray(ctx); 1561 | sjson_assert(a); 1562 | for (int i = 0; i < count; i++) { 1563 | sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); 1564 | sjson_assert(n); 1565 | sjson_append_element(a, n); 1566 | } 1567 | sjson_append_member(ctx, parent, key, a); 1568 | return a; 1569 | } 1570 | 1571 | sjson_node* sjson_put_ints(sjson_context* ctx, sjson_node* parent, const char* key, const int* vals, int count) 1572 | { 1573 | sjson_node* a = sjson_mkarray(ctx); 1574 | sjson_assert(a); 1575 | for (int i = 0; i < count; i++) { 1576 | sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); 1577 | sjson_assert(n); 1578 | sjson_append_element(a, n); 1579 | } 1580 | sjson_append_member(ctx, parent, key, a); 1581 | return a; 1582 | } 1583 | 1584 | sjson_node* sjson_put_uints(sjson_context* ctx, sjson_node* parent, const char* key, const uint32_t* vals, int count) 1585 | { 1586 | sjson_node* a = sjson_mkarray(ctx); 1587 | sjson_assert(a); 1588 | for (int i = 0; i < count; i++) { 1589 | sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); 1590 | sjson_assert(n); 1591 | sjson_append_element(a, n); 1592 | } 1593 | sjson_append_member(ctx, parent, key, a); 1594 | return a; 1595 | } 1596 | 1597 | sjson_node* sjson_put_int16s(sjson_context* ctx, sjson_node* parent, const char* key, const int16_t* vals, int count) 1598 | { 1599 | sjson_node* a = sjson_mkarray(ctx); 1600 | sjson_assert(a); 1601 | for (int i = 0; i < count; i++) { 1602 | sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); 1603 | sjson_assert(n); 1604 | sjson_append_element(a, n); 1605 | } 1606 | sjson_append_member(ctx, parent, key, a); 1607 | return a; 1608 | } 1609 | 1610 | sjson_node* sjson_put_uint16s(sjson_context* ctx, sjson_node* parent, const char* key, const uint16_t* vals, int count) 1611 | { 1612 | sjson_node* a = sjson_mkarray(ctx); 1613 | sjson_assert(a); 1614 | for (int i = 0; i < count; i++) { 1615 | sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); 1616 | sjson_assert(n); 1617 | sjson_append_element(a, n); 1618 | } 1619 | sjson_append_member(ctx, parent, key, a); 1620 | return a; 1621 | } 1622 | 1623 | sjson_node* sjson_put_strings(sjson_context* ctx, sjson_node* parent, const char* key, const char** vals, int count) 1624 | { 1625 | sjson_node* a = sjson_mkarray(ctx); 1626 | sjson_assert(a); 1627 | for (int i = 0; i < count; i++) { 1628 | sjson_node* n = sjson_mkstring(ctx, vals[i]); 1629 | sjson_assert(n); 1630 | sjson_append_element(a, n); 1631 | } 1632 | sjson_append_member(ctx, parent, key, a); 1633 | return a; 1634 | } 1635 | 1636 | static bool sjson__parse_value(sjson_context* ctx, const char **sp, sjson_node **out) 1637 | { 1638 | const char *s = *sp; 1639 | 1640 | switch (*s) { 1641 | case 'n': 1642 | if (sjson__expect_literal(&s, "null")) { 1643 | if (out) 1644 | *out = sjson_mknull(ctx); 1645 | *sp = s; 1646 | return true; 1647 | } 1648 | return false; 1649 | 1650 | case 'f': 1651 | if (sjson__expect_literal(&s, "false")) { 1652 | if (out) 1653 | *out = sjson_mkbool(ctx, false); 1654 | *sp = s; 1655 | return true; 1656 | } 1657 | return false; 1658 | 1659 | case 't': 1660 | if (sjson__expect_literal(&s, "true")) { 1661 | if (out) 1662 | *out = sjson_mkbool(ctx, true); 1663 | *sp = s; 1664 | return true; 1665 | } 1666 | return false; 1667 | 1668 | case '"': { 1669 | char *str = NULL; 1670 | if (sjson__parse_string(ctx, &s, out ? &str : NULL)) { 1671 | if (out) 1672 | *out = sjson__mkstring(ctx, str); 1673 | *sp = s; 1674 | return true; 1675 | } 1676 | return false; 1677 | } 1678 | 1679 | case '[': 1680 | if (sjson__parse_array(ctx, &s, out)) { 1681 | *sp = s; 1682 | return true; 1683 | } 1684 | return false; 1685 | 1686 | case '{': 1687 | if (sjson__parse_object(ctx, &s, out)) { 1688 | *sp = s; 1689 | return true; 1690 | } 1691 | return false; 1692 | 1693 | default: { 1694 | double num = 0; 1695 | if (sjson__parse_number(&s, out ? &num : NULL)) { 1696 | if (out) 1697 | *out = sjson_mknumber(ctx, num); 1698 | *sp = s; 1699 | return true; 1700 | } 1701 | return false; 1702 | } 1703 | } 1704 | } 1705 | 1706 | static bool sjson__parse_array(sjson_context* ctx, const char **sp, sjson_node **out) 1707 | { 1708 | const char *s = *sp; 1709 | sjson_node *ret = out ? sjson_mkarray(ctx) : NULL; 1710 | sjson_node *element = NULL; 1711 | 1712 | if (*s++ != '[') 1713 | goto failure; 1714 | sjson__skip_space(&s); 1715 | 1716 | if (*s == ']') { 1717 | s++; 1718 | goto success; 1719 | } 1720 | 1721 | for (;;) { 1722 | if (!sjson__parse_value(ctx, &s, out ? &element : NULL)) 1723 | goto failure; 1724 | sjson__skip_space(&s); 1725 | 1726 | if (out) 1727 | sjson_append_element(ret, element); 1728 | 1729 | if (*s == ']') { 1730 | s++; 1731 | goto success; 1732 | } 1733 | 1734 | if (*s++ != ',') 1735 | goto failure; 1736 | sjson__skip_space(&s); 1737 | } 1738 | 1739 | success: 1740 | *sp = s; 1741 | if (out) 1742 | *out = ret; 1743 | return true; 1744 | 1745 | failure: 1746 | sjson_delete_node(ctx, ret); 1747 | return false; 1748 | } 1749 | 1750 | static bool sjson__parse_object(sjson_context* ctx, const char **sp, sjson_node **out) 1751 | { 1752 | const char *s = *sp; 1753 | sjson_node *ret = out ? sjson_mkobject(ctx) : NULL; 1754 | char *key = NULL; 1755 | sjson_node *value = NULL; 1756 | 1757 | if (*s++ != '{') 1758 | goto failure; 1759 | sjson__skip_space(&s); 1760 | 1761 | if (*s == '}') { 1762 | s++; 1763 | goto success; 1764 | } 1765 | 1766 | for (;;) { 1767 | if (!sjson__parse_string(ctx, &s, out ? &key : NULL)) 1768 | goto failure; 1769 | sjson__skip_space(&s); 1770 | 1771 | if (*s++ != ':') 1772 | goto failure_free_key; 1773 | sjson__skip_space(&s); 1774 | 1775 | if (!sjson__parse_value(ctx, &s, out ? &value : NULL)) 1776 | goto failure_free_key; 1777 | sjson__skip_space(&s); 1778 | 1779 | if (out) 1780 | sjson__append_member(ret, key, value); 1781 | 1782 | if (*s == '}') { 1783 | s++; 1784 | goto success; 1785 | } 1786 | 1787 | if (*s++ != ',') 1788 | goto failure; 1789 | sjson__skip_space(&s); 1790 | } 1791 | 1792 | success: 1793 | *sp = s; 1794 | if (out) 1795 | *out = ret; 1796 | return true; 1797 | 1798 | failure_free_key: 1799 | failure: 1800 | sjson_delete_node(ctx, ret); 1801 | return false; 1802 | } 1803 | 1804 | static bool sjson__parse_string(sjson_context* ctx, const char **sp, char **out) 1805 | { 1806 | const char* s = *sp; 1807 | char throwaway_buffer[4]; /* enough space for a UTF-8 character */ 1808 | char* b; 1809 | char* src = NULL; 1810 | 1811 | if (*s++ != '"') 1812 | return false; 1813 | 1814 | if (out) { 1815 | b = sjson__str_begin(ctx, 4); 1816 | src = b; 1817 | } else { 1818 | b = throwaway_buffer; 1819 | } 1820 | 1821 | while (*s != '"') { 1822 | unsigned char c = *s++; 1823 | 1824 | /* Parse next character, and write it to b. */ 1825 | if (c == '\\') { 1826 | c = *s++; 1827 | switch (c) { 1828 | case '"': 1829 | case '\\': 1830 | case '/': 1831 | *b++ = c; 1832 | break; 1833 | case 'b': 1834 | *b++ = '\b'; 1835 | break; 1836 | case 'f': 1837 | *b++ = '\f'; 1838 | break; 1839 | case 'n': 1840 | *b++ = '\n'; 1841 | break; 1842 | case 'r': 1843 | *b++ = '\r'; 1844 | break; 1845 | case 't': 1846 | *b++ = '\t'; 1847 | break; 1848 | case 'u': 1849 | { 1850 | uint16_t uc, lc; 1851 | json__uchar_t unicode; 1852 | 1853 | if (!sjson__parse_hex16(&s, &uc)) 1854 | goto failed; 1855 | 1856 | if (uc >= 0xD800 && uc <= 0xDFFF) { 1857 | /* Handle UTF-16 surrogate pair. */ 1858 | if (*s++ != '\\' || *s++ != 'u' || !sjson__parse_hex16(&s, &lc)) 1859 | goto failed; /* Incomplete surrogate pair. */ 1860 | if (!sjson__from_surrogate_pair(uc, lc, &unicode)) 1861 | goto failed; /* Invalid surrogate pair. */ 1862 | } else if (uc == 0) { 1863 | /* Disallow "\u0000". */ 1864 | goto failed; 1865 | } else { 1866 | unicode = uc; 1867 | } 1868 | 1869 | b += sjson__utf8_write_char(unicode, b); 1870 | break; 1871 | } 1872 | default: 1873 | /* Invalid escape */ 1874 | goto failed; 1875 | } 1876 | } else if (c <= 0x1F) { 1877 | /* Control characters are not allowed in string literals. */ 1878 | goto failed; 1879 | } else { 1880 | /* Validate and echo a UTF-8 character. */ 1881 | int len; 1882 | 1883 | s--; 1884 | len = sjson__utf8_validate_cz(s); 1885 | if (len == 0) 1886 | goto failed; /* Invalid UTF-8 character. */ 1887 | 1888 | while (len--) 1889 | *b++ = *s++; 1890 | } 1891 | 1892 | /* 1893 | * Update sb to know about the new bytes, 1894 | * and set up b to write another character. 1895 | */ 1896 | if (out) { 1897 | int offset = (int)(uintptr_t)(b - src); 1898 | src = sjson__str_grow(ctx, 4); 1899 | b = src + offset; 1900 | } else { 1901 | b = throwaway_buffer; 1902 | } 1903 | } 1904 | s++; 1905 | 1906 | if (out) { 1907 | *b = '\0'; 1908 | *out = sjson__str_end(ctx); 1909 | } 1910 | *sp = s; 1911 | return true; 1912 | 1913 | failed: 1914 | if (out) 1915 | sjson__str_end(ctx); 1916 | return false; 1917 | } 1918 | 1919 | /* 1920 | * The JSON spec says that a number shall follow this precise pattern 1921 | * (spaces and quotes added for readability): 1922 | * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? 1923 | * 1924 | * However, some JSON parsers are more liberal. For instance, PHP accepts 1925 | * '.5' and '1.'. JSON.parse accepts '+3'. 1926 | * 1927 | * This function takes the strict approach. 1928 | */ 1929 | bool sjson__parse_number(const char **sp, double *out) 1930 | { 1931 | const char *s = *sp; 1932 | 1933 | /* '-'? */ 1934 | if (*s == '-') 1935 | s++; 1936 | 1937 | /* (0 | [1-9][0-9]*) */ 1938 | if (*s == '0') { 1939 | s++; 1940 | } else { 1941 | if (!sjson__is_digit(*s)) 1942 | return false; 1943 | do { 1944 | s++; 1945 | } while (sjson__is_digit(*s)); 1946 | } 1947 | 1948 | /* ('.' [0-9]+)? */ 1949 | if (*s == '.') { 1950 | s++; 1951 | if (!sjson__is_digit(*s)) 1952 | return false; 1953 | do { 1954 | s++; 1955 | } while (sjson__is_digit(*s)); 1956 | } 1957 | 1958 | /* ([Ee] [+-]? [0-9]+)? */ 1959 | if (*s == 'E' || *s == 'e') { 1960 | s++; 1961 | if (*s == '+' || *s == '-') 1962 | s++; 1963 | if (!sjson__is_digit(*s)) 1964 | return false; 1965 | do { 1966 | s++; 1967 | } while (sjson__is_digit(*s)); 1968 | } 1969 | 1970 | if (out) 1971 | *out = strtod(*sp, NULL); 1972 | 1973 | *sp = s; 1974 | return true; 1975 | } 1976 | 1977 | static void sjson__skip_space(const char **sp) 1978 | { 1979 | const char *s = *sp; 1980 | while (sjson__is_space(*s)) 1981 | s++; 1982 | *sp = s; 1983 | } 1984 | 1985 | static void sjson__emit_value(sjson_context* ctx, sjson__sb* out, const sjson_node *node) 1986 | { 1987 | sjson_assert(sjson__tag_is_valid(node->tag)); 1988 | switch (node->tag) { 1989 | case SJSON_NULL: 1990 | sjson__sb_puts(ctx, out, "null"); 1991 | break; 1992 | case SJSON_BOOL: 1993 | sjson__sb_puts(ctx, out, node->bool_ ? "true" : "false"); 1994 | break; 1995 | case SJSON_STRING: 1996 | sjson__emit_string(ctx, out, node->string_); 1997 | break; 1998 | case SJSON_NUMBER: 1999 | sjson__emit_number(ctx, out, node->number_); 2000 | break; 2001 | case SJSON_ARRAY: 2002 | sjson__emit_array(ctx, out, node); 2003 | break; 2004 | case SJSON_OBJECT: 2005 | sjson__emit_object(ctx, out, node); 2006 | break; 2007 | default: 2008 | sjson_assert(false); 2009 | } 2010 | } 2011 | 2012 | void sjson__emit_value_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *node, const char *space, int indent_level) 2013 | { 2014 | sjson_assert(sjson__tag_is_valid(node->tag)); 2015 | switch (node->tag) { 2016 | case SJSON_BOOL: 2017 | sjson__sb_puts(ctx, out, node->bool_ ? "true" : "false"); 2018 | break; 2019 | case SJSON_STRING: 2020 | sjson__emit_string(ctx, out, node->string_); 2021 | break; 2022 | case SJSON_NUMBER: 2023 | sjson__emit_number(ctx, out, node->number_); 2024 | break; 2025 | case SJSON_ARRAY: 2026 | sjson__emit_array_indented(ctx, out, node, space, indent_level); 2027 | break; 2028 | case SJSON_OBJECT: 2029 | sjson__emit_object_indented(ctx, out, node, space, indent_level); 2030 | break; 2031 | case SJSON_NULL: 2032 | sjson__sb_puts(ctx, out, "null"); 2033 | break; 2034 | default: 2035 | sjson_assert(false); 2036 | } 2037 | } 2038 | 2039 | static void sjson__emit_array(sjson_context* ctx, sjson__sb* out, const sjson_node *array) 2040 | { 2041 | const sjson_node *element; 2042 | 2043 | sjson__sb_putc(ctx, out, '['); 2044 | sjson_foreach(element, array) { 2045 | sjson__emit_value(ctx, out, element); 2046 | if (element->next != NULL) 2047 | sjson__sb_putc(ctx, out, ','); 2048 | } 2049 | sjson__sb_putc(ctx, out, ']'); 2050 | } 2051 | 2052 | static void sjson__emit_array_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *array, const char *space, int indent_level) 2053 | { 2054 | const sjson_node *element = array->children.head; 2055 | int i; 2056 | 2057 | if (element == NULL) { 2058 | sjson__sb_puts(ctx, out, "[]"); 2059 | return; 2060 | } 2061 | 2062 | sjson__sb_puts(ctx, out, "[\n"); 2063 | while (element != NULL) { 2064 | for (i = 0; i < indent_level + 1; i++) 2065 | sjson__sb_puts(ctx, out, space); 2066 | sjson__emit_value_indented(ctx, out, element, space, indent_level + 1); 2067 | 2068 | element = element->next; 2069 | sjson__sb_puts(ctx, out, element != NULL ? ",\n" : "\n"); 2070 | } 2071 | for (i = 0; i < indent_level; i++) 2072 | sjson__sb_puts(ctx, out, space); 2073 | sjson__sb_putc(ctx, out, ']'); 2074 | } 2075 | 2076 | static void sjson__emit_object(sjson_context* ctx, sjson__sb* out, const sjson_node *object) 2077 | { 2078 | const sjson_node *member; 2079 | 2080 | sjson__sb_putc(ctx, out, '{'); 2081 | sjson_foreach(member, object) { 2082 | sjson__emit_string(ctx, out, member->key); 2083 | sjson__sb_putc(ctx, out, ':'); 2084 | sjson__emit_value(ctx, out, member); 2085 | if (member->next != NULL) 2086 | sjson__sb_putc(ctx, out, ','); 2087 | } 2088 | sjson__sb_putc(ctx, out, '}'); 2089 | } 2090 | 2091 | static void sjson__emit_object_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *object, const char *space, int indent_level) 2092 | { 2093 | const sjson_node *member = object->children.head; 2094 | int i; 2095 | 2096 | if (member == NULL) { 2097 | sjson__sb_puts(ctx, out, "{}"); 2098 | return; 2099 | } 2100 | 2101 | sjson__sb_puts(ctx, out, "{\n"); 2102 | while (member != NULL) { 2103 | for (i = 0; i < indent_level + 1; i++) 2104 | sjson__sb_puts(ctx, out, space); 2105 | sjson__emit_string(ctx, out, member->key); 2106 | sjson__sb_puts(ctx, out, ": "); 2107 | sjson__emit_value_indented(ctx, out, member, space, indent_level + 1); 2108 | 2109 | member = member->next; 2110 | sjson__sb_puts(ctx, out, member != NULL ? ",\n" : "\n"); 2111 | } 2112 | for (i = 0; i < indent_level; i++) 2113 | sjson__sb_puts(ctx, out, space); 2114 | sjson__sb_putc(ctx, out, '}'); 2115 | } 2116 | 2117 | void sjson__emit_string(sjson_context* ctx, sjson__sb* out, const char *str) 2118 | { 2119 | bool escape_unicode = false; 2120 | const char *s = str; 2121 | char *b; 2122 | 2123 | sjson_assert(sjson__utf8_validate(str)); 2124 | 2125 | /* 2126 | * 14 bytes is enough space to write up to two 2127 | * \uXXXX escapes and two quotation marks. 2128 | */ 2129 | sjson__sb_need(ctx, out, 14); 2130 | b = out->cur; 2131 | 2132 | *b++ = '"'; 2133 | while (*s != 0) { 2134 | unsigned char c = *s++; 2135 | 2136 | /* Encode the next character, and write it to b. */ 2137 | switch (c) { 2138 | case '"': 2139 | *b++ = '\\'; 2140 | *b++ = '"'; 2141 | break; 2142 | case '\\': 2143 | *b++ = '\\'; 2144 | *b++ = '\\'; 2145 | break; 2146 | case '\b': 2147 | *b++ = '\\'; 2148 | *b++ = 'b'; 2149 | break; 2150 | case '\f': 2151 | *b++ = '\\'; 2152 | *b++ = 'f'; 2153 | break; 2154 | case '\n': 2155 | *b++ = '\\'; 2156 | *b++ = 'n'; 2157 | break; 2158 | case '\r': 2159 | *b++ = '\\'; 2160 | *b++ = 'r'; 2161 | break; 2162 | case '\t': 2163 | *b++ = '\\'; 2164 | *b++ = 't'; 2165 | break; 2166 | default: { 2167 | int len; 2168 | 2169 | s--; 2170 | len = sjson__utf8_validate_cz(s); 2171 | 2172 | if (len == 0) { 2173 | /* 2174 | * Handle invalid UTF-8 character gracefully in production 2175 | * by writing a replacement character (U+FFFD) 2176 | * and skipping a single byte. 2177 | * 2178 | * This should never happen when assertions are enabled 2179 | * due to the assertion at the beginning of this function. 2180 | */ 2181 | sjson_assert(false); 2182 | if (escape_unicode) { 2183 | sjson_strcpy(b, 7, "\\uFFFD"); 2184 | b += 6; 2185 | } else { 2186 | *b++ = (char)0xEF; 2187 | *b++ = (char)0xBF; 2188 | *b++ = (char)0xBD; 2189 | } 2190 | s++; 2191 | } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { 2192 | /* Encode using \u.... */ 2193 | uint32_t unicode; 2194 | 2195 | s += sjson__utf8_read_char(s, &unicode); 2196 | 2197 | if (unicode <= 0xFFFF) { 2198 | *b++ = '\\'; 2199 | *b++ = 'u'; 2200 | b += sjson__write_hex16(b, unicode); 2201 | } else { 2202 | /* Produce a surrogate pair. */ 2203 | uint16_t uc, lc; 2204 | sjson_assert(unicode <= 0x10FFFF); 2205 | sjson__to_surrogate_pair(unicode, &uc, &lc); 2206 | *b++ = '\\'; 2207 | *b++ = 'u'; 2208 | b += sjson__write_hex16(b, uc); 2209 | *b++ = '\\'; 2210 | *b++ = 'u'; 2211 | b += sjson__write_hex16(b, lc); 2212 | } 2213 | } else { 2214 | /* Write the character directly. */ 2215 | while (len--) 2216 | *b++ = *s++; 2217 | } 2218 | 2219 | break; 2220 | } 2221 | } 2222 | 2223 | /* 2224 | * Update *out to know about the new bytes, 2225 | * and set up b to write another encoded character. 2226 | */ 2227 | out->cur = b; 2228 | sjson__sb_need(ctx, out, 14); 2229 | b = out->cur; 2230 | } 2231 | *b++ = '"'; 2232 | 2233 | out->cur = b; 2234 | } 2235 | 2236 | static void sjson__emit_number(sjson_context* ctx, sjson__sb* out, double num) 2237 | { 2238 | /* 2239 | * This isn't exactly how JavaScript renders numbers, 2240 | * but it should produce valid JSON for reasonable numbers 2241 | * preserve precision well enough, and avoid some oddities 2242 | * like 0.3 -> 0.299999999999999988898 . 2243 | */ 2244 | char buf[64]; 2245 | sjson_snprintf(buf, sizeof(buf), "%.16g", num); 2246 | 2247 | if (sjson__number_is_valid(buf)) 2248 | sjson__sb_puts(ctx, out, buf); 2249 | else 2250 | sjson__sb_puts(ctx, out, "null"); 2251 | } 2252 | 2253 | static bool sjson__tag_is_valid(unsigned int tag) 2254 | { 2255 | return (/* tag >= SJSON_NULL && */ tag <= SJSON_OBJECT); 2256 | } 2257 | 2258 | static bool sjson__number_is_valid(const char *num) 2259 | { 2260 | return (sjson__parse_number(&num, NULL) && *num == '\0'); 2261 | } 2262 | 2263 | static bool sjson__expect_literal(const char **sp, const char *str) 2264 | { 2265 | const char *s = *sp; 2266 | 2267 | while (*str != '\0') 2268 | if (*s++ != *str++) 2269 | return false; 2270 | 2271 | *sp = s; 2272 | return true; 2273 | } 2274 | 2275 | /* 2276 | * Parses exactly 4 hex characters (capital or lowercase). 2277 | * Fails if any input chars are not [0-9A-Fa-f]. 2278 | */ 2279 | static bool sjson__parse_hex16(const char **sp, uint16_t *out) 2280 | { 2281 | const char *s = *sp; 2282 | uint16_t ret = 0; 2283 | uint16_t i; 2284 | uint16_t tmp; 2285 | char c; 2286 | 2287 | for (i = 0; i < 4; i++) { 2288 | c = *s++; 2289 | if (c >= '0' && c <= '9') 2290 | tmp = c - '0'; 2291 | else if (c >= 'A' && c <= 'F') 2292 | tmp = c - 'A' + 10; 2293 | else if (c >= 'a' && c <= 'f') 2294 | tmp = c - 'a' + 10; 2295 | else 2296 | return false; 2297 | 2298 | ret <<= 4; 2299 | ret += tmp; 2300 | } 2301 | 2302 | if (out) 2303 | *out = ret; 2304 | *sp = s; 2305 | return true; 2306 | } 2307 | 2308 | /* 2309 | * Encodes a 16-bit number into hexadecimal, 2310 | * writing exactly 4 hex chars. 2311 | */ 2312 | static int sjson__write_hex16(char *out, uint16_t val) 2313 | { 2314 | const char *hex = "0123456789ABCDEF"; 2315 | 2316 | *out++ = hex[(val >> 12) & 0xF]; 2317 | *out++ = hex[(val >> 8) & 0xF]; 2318 | *out++ = hex[(val >> 4) & 0xF]; 2319 | *out++ = hex[ val & 0xF]; 2320 | 2321 | return 4; 2322 | } 2323 | 2324 | bool sjson_check(const sjson_node* node, char errmsg[256]) 2325 | { 2326 | #define problem(...) do { \ 2327 | if (errmsg != NULL) \ 2328 | sjson_snprintf(errmsg, 256, __VA_ARGS__); \ 2329 | return false; \ 2330 | } while (0) 2331 | 2332 | if (node->key != NULL && !sjson__utf8_validate(node->key)) 2333 | problem("key contains invalid UTF-8"); 2334 | 2335 | if (!sjson__tag_is_valid(node->tag)) 2336 | problem("tag is invalid (%u)", node->tag); 2337 | 2338 | if (node->tag == SJSON_BOOL) { 2339 | if (node->bool_ != false && node->bool_ != true) 2340 | problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); 2341 | } else if (node->tag == SJSON_STRING) { 2342 | if (node->string_ == NULL) 2343 | problem("string_ is NULL"); 2344 | if (!sjson__utf8_validate(node->string_)) 2345 | problem("string_ contains invalid UTF-8"); 2346 | } else if (node->tag == SJSON_ARRAY || node->tag == SJSON_OBJECT) { 2347 | sjson_node *head = node->children.head; 2348 | sjson_node *tail = node->children.tail; 2349 | 2350 | if (head == NULL || tail == NULL) { 2351 | if (head != NULL) 2352 | problem("tail is NULL, but head is not"); 2353 | if (tail != NULL) 2354 | problem("head is NULL, but tail is not"); 2355 | } else { 2356 | sjson_node *child; 2357 | sjson_node *last = NULL; 2358 | 2359 | if (head->prev != NULL) 2360 | problem("First child's prev pointer is not NULL"); 2361 | 2362 | for (child = head; child != NULL; last = child, child = child->next) { 2363 | if (child == node) 2364 | problem("node is its own child"); 2365 | if (child->next == child) 2366 | problem("child->next == child (cycle)"); 2367 | if (child->next == head) 2368 | problem("child->next == head (cycle)"); 2369 | 2370 | if (child->parent != node) 2371 | problem("child does not point back to parent"); 2372 | if (child->next != NULL && child->next->prev != child) 2373 | problem("child->next does not point back to child"); 2374 | 2375 | if (node->tag == SJSON_ARRAY && child->key != NULL) 2376 | problem("Array element's key is not NULL"); 2377 | if (node->tag == SJSON_OBJECT && child->key == NULL) 2378 | problem("Object member's key is NULL"); 2379 | 2380 | if (!sjson_check(child, errmsg)) 2381 | return false; 2382 | } 2383 | 2384 | if (last != tail) 2385 | problem("tail does not match pointer found by starting at head and following next links"); 2386 | } 2387 | } 2388 | 2389 | return true; 2390 | 2391 | #undef problem 2392 | } 2393 | 2394 | #endif // SJSON_IMPLEMENT 2395 | 2396 | // 2397 | // Version History: 2398 | // 1.0.0 Initial release 2399 | // 1.1.0 Added higher level json get/put functions 2400 | // Implemented sjson_reset_context 2401 | // 1.1.1 Fixed some macro defines (sjson_strcpy, sjson_snprintf) 2402 | // 2403 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(test-sjson) 2 | cmake_minimum_required(VERSION 3.0) 3 | 4 | add_executable(test-decode "test-decode.c" "../sjson.h") 5 | add_executable(test-encode "test-encode.c" "../sjson.h") 6 | -------------------------------------------------------------------------------- /tests/test-decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SJSON_IMPLEMENT 5 | #include "../sjson.h" 6 | 7 | char* read_file(const char* filename) 8 | { 9 | FILE* f = fopen(filename, "rb"); 10 | if (!f) { 11 | return NULL; 12 | } 13 | 14 | fseek(f, 0, SEEK_END); 15 | int size = ftell(f); 16 | char* buff = NULL; 17 | if (size > 0) { 18 | buff = (char*)malloc(size + 1); 19 | if (buff) { 20 | fseek(f, 0, SEEK_SET); 21 | fread(buff, size, 1, f); 22 | buff[size] = '\0'; 23 | } 24 | } 25 | fclose(f); 26 | 27 | return buff; 28 | } 29 | 30 | int main(int argc, char* argv[]) 31 | { 32 | sjson_context* ctx = sjson_create_context(0, 0, NULL); 33 | if (!ctx) { 34 | puts("Could not create sjson_context"); 35 | return -1; 36 | } 37 | 38 | char* twitter_json = read_file("twitter.json"); 39 | if (!twitter_json) { 40 | puts("Could not read 'twitter.json'. Check if the file exists in the current path"); 41 | return -1; 42 | } 43 | 44 | sjson_node* jroot = sjson_decode(ctx, twitter_json); 45 | if (!jroot) { 46 | puts("Parsing twitter.json failed"); 47 | return -1; 48 | } 49 | 50 | char* text = sjson_stringify(ctx, jroot, " "); 51 | puts("Re-encoded: "); 52 | puts(text); 53 | sjson_free_string(ctx, text); 54 | 55 | free(twitter_json); 56 | sjson_destroy_context(ctx); 57 | return 0; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tests/test-encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SJSON_IMPLEMENT 5 | #include "../sjson.h" 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | sjson_context* ctx = sjson_create_context(0, 0, NULL); 10 | if (!ctx) { 11 | puts("Could not create sjson_context"); 12 | return -1; 13 | } 14 | 15 | sjson_node* jroot = sjson_mkobject(ctx); 16 | sjson_put_string(ctx, jroot, "string", "Dances with Frogs"); 17 | sjson_put_bool(ctx, jroot, "boolean", true); 18 | sjson_put_double(ctx, jroot, "number", 1.666); 19 | sjson_append_member(ctx, jroot, "nil", sjson_mknull(ctx)); // lower-level API 20 | 21 | // Lower-level api for constructing any type of array 22 | sjson_node* jarr = sjson_mkarray(ctx); 23 | sjson_append_member(ctx, jroot, "arr", jarr); 24 | { 25 | sjson_append_element(jarr, sjson_mknumber(ctx, 9.0)); 26 | sjson_append_element(jarr, sjson_mkbool(ctx, false)); 27 | sjson_append_element(jarr, sjson_mkstring(ctx, "element3")); 28 | } 29 | 30 | // Higher level for constructing for example float array 31 | const float pt[] = {1.5f, 3.0f, 5.0f, 1.0f}; 32 | sjson_put_floats(ctx, jroot, "vector", pt, 4); 33 | 34 | char errmsg[256]; 35 | if (!sjson_check(jroot, errmsg)) { 36 | puts(errmsg); 37 | return -1; 38 | } 39 | 40 | char* encoded = sjson_stringify(ctx, jroot, " "); 41 | puts("Encoded: "); 42 | puts(encoded); 43 | 44 | sjson_destroy_context(ctx); 45 | return 0; 46 | } -------------------------------------------------------------------------------- /tests/twitter.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "created_at": "Thu Jun 22 21:00:00 +0000 2017", 3 | "id": 877994604561387500, 4 | "id_str": "877994604561387520", 5 | "text": "Creating a Grocery List Manager Using Angular, Part 1: Add & Display Items https://t.co/xFox78juL1 #Angular", 6 | "truncated": false, 7 | "entities": { 8 | "hashtags": [{ 9 | "text": "Angular", 10 | "indices": [103, 111] 11 | }], 12 | "symbols": [], 13 | "user_mentions": [], 14 | "urls": [{ 15 | "url": "https://t.co/xFox78juL1", 16 | "expanded_url": "http://buff.ly/2sr60pf", 17 | "display_url": "buff.ly/2sr60pf", 18 | "indices": [79, 102] 19 | }] 20 | }, 21 | "source": "Buffer", 22 | "user": { 23 | "id": 772682964, 24 | "id_str": "772682964", 25 | "name": "SitePoint JavaScript", 26 | "screen_name": "SitePointJS", 27 | "location": "Melbourne, Australia", 28 | "description": "Keep up with JavaScript tutorials, tips, tricks and articles at SitePoint.", 29 | "url": "http://t.co/cCH13gqeUK", 30 | "entities": { 31 | "url": { 32 | "urls": [{ 33 | "url": "http://t.co/cCH13gqeUK", 34 | "expanded_url": "http://sitepoint.com/javascript", 35 | "display_url": "sitepoint.com/javascript", 36 | "indices": [0, 22] 37 | }] 38 | }, 39 | "description": { 40 | "urls": [] 41 | } 42 | }, 43 | "protected": false, 44 | "followers_count": 2145, 45 | "friends_count": 18, 46 | "listed_count": 328, 47 | "created_at": "Wed Aug 22 02:06:33 +0000 2012", 48 | "favourites_count": 57, 49 | "utc_offset": 43200, 50 | "time_zone": "Wellington" 51 | } 52 | }] --------------------------------------------------------------------------------