├── .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 | }]
--------------------------------------------------------------------------------