mirror of
				https://github.com/taigrr/tplinkController
				synced 2025-01-18 04:43:13 -08:00 
			
		
		
		
	Add JSON builder/parser.
This commit is contained in:
		
							parent
							
								
									c57babc9b1
								
							
						
					
					
						commit
						4ca74c5eba
					
				
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,6 +1,7 @@ | |||||||
| target  ?= hs100 | target  ?= hs100 | ||||||
| objects := $(patsubst %.c,%.o,$(wildcard *.c)) | objects := $(patsubst %.c,%.o,$(wildcard *.c)) | ||||||
| 
 | 
 | ||||||
|  | LDFLAGS=-lm | ||||||
| CFLAGS=-std=c99 -Os | CFLAGS=-std=c99 -Os | ||||||
| ifdef DEBUG | ifdef DEBUG | ||||||
| CFLAGS+=-Wall -Werror -ggdb | CFLAGS+=-Wall -Werror -ggdb | ||||||
|  | |||||||
							
								
								
									
										994
									
								
								json-builder.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										994
									
								
								json-builder.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,994 @@ | |||||||
|  | 
 | ||||||
|  | /* vim: set et ts=3 sw=3 sts=3 ft=c:
 | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2014 James McLaughlin.  All rights reserved. | ||||||
|  |  * https://github.com/udp/json-builder
 | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *   documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "json-builder.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  |     #define snprintf _snprintf | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static const json_serialize_opts default_opts = | ||||||
|  | { | ||||||
|  |    json_serialize_mode_single_line, | ||||||
|  |    0, | ||||||
|  |    3  /* indent_size */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct json_builder_value | ||||||
|  | { | ||||||
|  |    json_value value; | ||||||
|  | 
 | ||||||
|  |    int is_builder_value; | ||||||
|  | 
 | ||||||
|  |    size_t additional_length_allocated; | ||||||
|  |    size_t length_iterated; | ||||||
|  | 
 | ||||||
|  | } json_builder_value; | ||||||
|  | 
 | ||||||
|  | static int builderize (json_value * value) | ||||||
|  | { | ||||||
|  |    if (((json_builder_value *) value)->is_builder_value) | ||||||
|  |       return 1; | ||||||
|  |     | ||||||
|  |    if (value->type == json_object) | ||||||
|  |    { | ||||||
|  |       unsigned int i; | ||||||
|  | 
 | ||||||
|  |       /* Values straight out of the parser have the names of object entries
 | ||||||
|  |        * allocated in the same allocation as the values array itself.  This is | ||||||
|  |        * not desirable when manipulating values because the names would be easy | ||||||
|  |        * to clobber. | ||||||
|  |        */ | ||||||
|  |       for (i = 0; i < value->u.object.length; ++ i) | ||||||
|  |       { | ||||||
|  |          json_char * name_copy; | ||||||
|  |          json_object_entry * entry = &value->u.object.values [i]; | ||||||
|  | 
 | ||||||
|  |          if (! (name_copy = (json_char *) malloc ((entry->name_length + 1) * sizeof (json_char)))) | ||||||
|  |             return 0; | ||||||
|  | 
 | ||||||
|  |          memcpy (name_copy, entry->name, entry->name_length + 1); | ||||||
|  |          entry->name = name_copy; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const size_t json_builder_extra = sizeof(json_builder_value) - sizeof(json_value); | ||||||
|  | 
 | ||||||
|  | /* These flags are set up from the opts before serializing to make the
 | ||||||
|  |  * serializer conditions simpler. | ||||||
|  |  */ | ||||||
|  | const int f_spaces_around_brackets = (1 << 0); | ||||||
|  | const int f_spaces_after_commas    = (1 << 1); | ||||||
|  | const int f_spaces_after_colons    = (1 << 2); | ||||||
|  | const int f_tabs                   = (1 << 3); | ||||||
|  | 
 | ||||||
|  | static int get_serialize_flags (json_serialize_opts opts) | ||||||
|  | { | ||||||
|  |    int flags = 0; | ||||||
|  | 
 | ||||||
|  |    if (opts.mode == json_serialize_mode_packed) | ||||||
|  |       return 0; | ||||||
|  | 
 | ||||||
|  |    if (opts.mode == json_serialize_mode_multiline) | ||||||
|  |    { | ||||||
|  |       if (opts.opts & json_serialize_opt_use_tabs) | ||||||
|  |          flags |= f_tabs; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       if (! (opts.opts & json_serialize_opt_pack_brackets)) | ||||||
|  |          flags |= f_spaces_around_brackets; | ||||||
|  | 
 | ||||||
|  |       if (! (opts.opts & json_serialize_opt_no_space_after_comma)) | ||||||
|  |          flags |= f_spaces_after_commas; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (! (opts.opts & json_serialize_opt_no_space_after_colon)) | ||||||
|  |       flags |= f_spaces_after_colons; | ||||||
|  | 
 | ||||||
|  |    return flags; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_array_new (size_t length) | ||||||
|  | { | ||||||
|  |     json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  | 
 | ||||||
|  |     if (!value) | ||||||
|  |        return NULL; | ||||||
|  | 
 | ||||||
|  |     ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |     value->type = json_array; | ||||||
|  | 
 | ||||||
|  |     if (! (value->u.array.values = (json_value **) malloc (length * sizeof (json_value *)))) | ||||||
|  |     { | ||||||
|  |        free (value); | ||||||
|  |        return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ((json_builder_value *) value)->additional_length_allocated = length; | ||||||
|  | 
 | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_array_push (json_value * array, json_value * value) | ||||||
|  | { | ||||||
|  |    assert (array->type == json_array); | ||||||
|  | 
 | ||||||
|  |    if (!builderize (array) || !builderize (value)) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    if (((json_builder_value *) array)->additional_length_allocated > 0) | ||||||
|  |    { | ||||||
|  |       -- ((json_builder_value *) array)->additional_length_allocated; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       json_value ** values_new = (json_value **) realloc | ||||||
|  |             (array->u.array.values, sizeof (json_value *) * (array->u.array.length + 1)); | ||||||
|  | 
 | ||||||
|  |       if (!values_new) | ||||||
|  |          return NULL; | ||||||
|  | 
 | ||||||
|  |       array->u.array.values = values_new; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    array->u.array.values [array->u.array.length] = value; | ||||||
|  |    ++ array->u.array.length; | ||||||
|  | 
 | ||||||
|  |    value->parent = array; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_object_new (size_t length) | ||||||
|  | { | ||||||
|  |     json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  | 
 | ||||||
|  |     if (!value) | ||||||
|  |        return NULL; | ||||||
|  | 
 | ||||||
|  |     ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |     value->type = json_object; | ||||||
|  | 
 | ||||||
|  |     if (! (value->u.object.values = (json_object_entry *) calloc | ||||||
|  |            (length, sizeof (*value->u.object.values)))) | ||||||
|  |     { | ||||||
|  |        free (value); | ||||||
|  |        return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ((json_builder_value *) value)->additional_length_allocated = length; | ||||||
|  | 
 | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_object_push (json_value * object, | ||||||
|  |                                const json_char * name, | ||||||
|  |                                json_value * value) | ||||||
|  | { | ||||||
|  |    return json_object_push_length (object, strlen (name), name, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_object_push_length (json_value * object, | ||||||
|  |                                       unsigned int name_length, const json_char * name, | ||||||
|  |                                       json_value * value) | ||||||
|  | { | ||||||
|  |    json_char * name_copy; | ||||||
|  | 
 | ||||||
|  |    assert (object->type == json_object); | ||||||
|  | 
 | ||||||
|  |    if (! (name_copy = (json_char *) malloc ((name_length + 1) * sizeof (json_char)))) | ||||||
|  |       return NULL; | ||||||
|  |     | ||||||
|  |    memcpy (name_copy, name, name_length * sizeof (json_char)); | ||||||
|  |    name_copy [name_length] = 0; | ||||||
|  | 
 | ||||||
|  |    if (!json_object_push_nocopy (object, name_length, name_copy, value)) | ||||||
|  |    { | ||||||
|  |       free (name_copy); | ||||||
|  |       return NULL; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_object_push_nocopy (json_value * object, | ||||||
|  |                                       unsigned int name_length, json_char * name, | ||||||
|  |                                       json_value * value) | ||||||
|  | { | ||||||
|  |    json_object_entry * entry; | ||||||
|  | 
 | ||||||
|  |    assert (object->type == json_object); | ||||||
|  | 
 | ||||||
|  |    if (!builderize (object) || !builderize (value)) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    if (((json_builder_value *) object)->additional_length_allocated > 0) | ||||||
|  |    { | ||||||
|  |       -- ((json_builder_value *) object)->additional_length_allocated; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       json_object_entry * values_new = (json_object_entry *) | ||||||
|  |             realloc (object->u.object.values, sizeof (*object->u.object.values) | ||||||
|  |                             * (object->u.object.length + 1)); | ||||||
|  | 
 | ||||||
|  |       if (!values_new) | ||||||
|  |          return NULL; | ||||||
|  | 
 | ||||||
|  |       object->u.object.values = values_new; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    entry = object->u.object.values + object->u.object.length; | ||||||
|  | 
 | ||||||
|  |    entry->name_length = name_length; | ||||||
|  |    entry->name = name; | ||||||
|  |    entry->value = value; | ||||||
|  | 
 | ||||||
|  |    ++ object->u.object.length; | ||||||
|  | 
 | ||||||
|  |    value->parent = object; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_string_new (const json_char * buf) | ||||||
|  | { | ||||||
|  |    return json_string_new_length (strlen (buf), buf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_string_new_length (unsigned int length, const json_char * buf) | ||||||
|  | { | ||||||
|  |    json_value * value; | ||||||
|  |    json_char * copy = (json_char *) malloc ((length + 1) * sizeof (json_char)); | ||||||
|  | 
 | ||||||
|  |    if (!copy) | ||||||
|  |       return NULL; | ||||||
|  |     | ||||||
|  |    memcpy (copy, buf, length * sizeof (json_char)); | ||||||
|  |    copy [length] = 0; | ||||||
|  | 
 | ||||||
|  |    if (! (value = json_string_new_nocopy (length, copy))) | ||||||
|  |    { | ||||||
|  |       free (copy); | ||||||
|  |       return NULL; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_string_new_nocopy (unsigned int length, json_char * buf) | ||||||
|  | { | ||||||
|  |    json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  |     | ||||||
|  |    if (!value) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    value->type = json_string; | ||||||
|  |    value->u.string.length = length; | ||||||
|  |    value->u.string.ptr = buf; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_integer_new (json_int_t integer) | ||||||
|  | { | ||||||
|  |    json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  |     | ||||||
|  |    if (!value) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    value->type = json_integer; | ||||||
|  |    value->u.integer = integer; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_double_new (double dbl) | ||||||
|  | { | ||||||
|  |    json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  |     | ||||||
|  |    if (!value) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    value->type = json_double; | ||||||
|  |    value->u.dbl = dbl; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_boolean_new (int b) | ||||||
|  | { | ||||||
|  |    json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  |     | ||||||
|  |    if (!value) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    value->type = json_boolean; | ||||||
|  |    value->u.boolean = b; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_null_new (void) | ||||||
|  | { | ||||||
|  |    json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); | ||||||
|  |     | ||||||
|  |    if (!value) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    ((json_builder_value *) value)->is_builder_value = 1; | ||||||
|  | 
 | ||||||
|  |    value->type = json_null; | ||||||
|  | 
 | ||||||
|  |    return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void json_object_sort (json_value * object, json_value * proto) | ||||||
|  | { | ||||||
|  |    unsigned int i, out_index = 0; | ||||||
|  | 
 | ||||||
|  |    if (!builderize (object)) | ||||||
|  |       return;  /* TODO error */ | ||||||
|  | 
 | ||||||
|  |    assert (object->type == json_object); | ||||||
|  |    assert (proto->type == json_object); | ||||||
|  | 
 | ||||||
|  |    for (i = 0; i < proto->u.object.length; ++ i) | ||||||
|  |    { | ||||||
|  |       unsigned int j; | ||||||
|  |       json_object_entry proto_entry = proto->u.object.values [i]; | ||||||
|  | 
 | ||||||
|  |       for (j = 0; j < object->u.object.length; ++ j) | ||||||
|  |       { | ||||||
|  |          json_object_entry entry = object->u.object.values [j]; | ||||||
|  | 
 | ||||||
|  |          if (entry.name_length != proto_entry.name_length) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          if (memcmp (entry.name, proto_entry.name, entry.name_length) != 0) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          object->u.object.values [j] = object->u.object.values [out_index]; | ||||||
|  |          object->u.object.values [out_index] = entry; | ||||||
|  | 
 | ||||||
|  |          ++ out_index; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_value * json_object_merge (json_value * objectA, json_value * objectB) | ||||||
|  | { | ||||||
|  |    unsigned int i; | ||||||
|  | 
 | ||||||
|  |    assert (objectA->type == json_object); | ||||||
|  |    assert (objectB->type == json_object); | ||||||
|  |    assert (objectA != objectB); | ||||||
|  | 
 | ||||||
|  |    if (!builderize (objectA) || !builderize (objectB)) | ||||||
|  |       return NULL; | ||||||
|  | 
 | ||||||
|  |    if (objectB->u.object.length <= | ||||||
|  |         ((json_builder_value *) objectA)->additional_length_allocated) | ||||||
|  |    { | ||||||
|  |       ((json_builder_value *) objectA)->additional_length_allocated | ||||||
|  |           -= objectB->u.object.length; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       json_object_entry * values_new; | ||||||
|  | 
 | ||||||
|  |       unsigned int alloc = | ||||||
|  |           objectA->u.object.length | ||||||
|  |               + ((json_builder_value *) objectA)->additional_length_allocated | ||||||
|  |               + objectB->u.object.length; | ||||||
|  | 
 | ||||||
|  |       if (! (values_new = (json_object_entry *) | ||||||
|  |             realloc (objectA->u.object.values, sizeof (json_object_entry) * alloc))) | ||||||
|  |       { | ||||||
|  |           return NULL; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       objectA->u.object.values = values_new; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (i = 0; i < objectB->u.object.length; ++ i) | ||||||
|  |    { | ||||||
|  |       json_object_entry * entry = &objectA->u.object.values[objectA->u.object.length + i]; | ||||||
|  | 
 | ||||||
|  |       *entry = objectB->u.object.values[i]; | ||||||
|  |       entry->value->parent = objectA; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    objectA->u.object.length += objectB->u.object.length; | ||||||
|  | 
 | ||||||
|  |    free (objectB->u.object.values); | ||||||
|  |    free (objectB); | ||||||
|  | 
 | ||||||
|  |    return objectA; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static size_t measure_string (unsigned int length, | ||||||
|  |                               const json_char * str) | ||||||
|  | { | ||||||
|  |    unsigned int i; | ||||||
|  |    size_t measured_length = 0; | ||||||
|  | 
 | ||||||
|  |    for(i = 0; i < length; ++ i) | ||||||
|  |    { | ||||||
|  |       json_char c = str [i]; | ||||||
|  | 
 | ||||||
|  |       switch (c) | ||||||
|  |       { | ||||||
|  |       case '"': | ||||||
|  |       case '\\': | ||||||
|  |       case '\b': | ||||||
|  |       case '\f': | ||||||
|  |       case '\n': | ||||||
|  |       case '\r': | ||||||
|  |       case '\t': | ||||||
|  | 
 | ||||||
|  |          measured_length += 2; | ||||||
|  |          break; | ||||||
|  | 
 | ||||||
|  |       default: | ||||||
|  | 
 | ||||||
|  |          ++ measured_length; | ||||||
|  |          break; | ||||||
|  |       }; | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  |    return measured_length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define PRINT_ESCAPED(c) do {  \ | ||||||
|  |    *buf ++ = '\\';             \ | ||||||
|  |    *buf ++ = (c);              \ | ||||||
|  | } while(0);                    \ | ||||||
|  | 
 | ||||||
|  | static size_t serialize_string (json_char * buf, | ||||||
|  |                                 unsigned int length, | ||||||
|  |                                 const json_char * str) | ||||||
|  | { | ||||||
|  |    json_char * orig_buf = buf; | ||||||
|  |    unsigned int i; | ||||||
|  | 
 | ||||||
|  |    for(i = 0; i < length; ++ i) | ||||||
|  |    { | ||||||
|  |       json_char c = str [i]; | ||||||
|  | 
 | ||||||
|  |       switch (c) | ||||||
|  |       { | ||||||
|  |       case '"':   PRINT_ESCAPED ('\"');  continue; | ||||||
|  |       case '\\':  PRINT_ESCAPED ('\\');  continue; | ||||||
|  |       case '\b':  PRINT_ESCAPED ('b');   continue; | ||||||
|  |       case '\f':  PRINT_ESCAPED ('f');   continue; | ||||||
|  |       case '\n':  PRINT_ESCAPED ('n');   continue; | ||||||
|  |       case '\r':  PRINT_ESCAPED ('r');   continue; | ||||||
|  |       case '\t':  PRINT_ESCAPED ('t');   continue; | ||||||
|  | 
 | ||||||
|  |       default: | ||||||
|  | 
 | ||||||
|  |          *buf ++ = c; | ||||||
|  |          break; | ||||||
|  |       }; | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  |    return buf - orig_buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t json_measure (json_value * value) | ||||||
|  | { | ||||||
|  |    return json_measure_ex (value, default_opts); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define MEASURE_NEWLINE() do {                     \ | ||||||
|  |    ++ newlines;                                    \ | ||||||
|  |    indents += depth;                               \ | ||||||
|  | } while(0);                                        \ | ||||||
|  | 
 | ||||||
|  | size_t json_measure_ex (json_value * value, json_serialize_opts opts) | ||||||
|  | { | ||||||
|  |    size_t total = 1;  /* null terminator */ | ||||||
|  |    size_t newlines = 0; | ||||||
|  |    size_t depth = 0; | ||||||
|  |    size_t indents = 0; | ||||||
|  |    int flags; | ||||||
|  |    int bracket_size, comma_size, colon_size; | ||||||
|  | 
 | ||||||
|  |    flags = get_serialize_flags (opts); | ||||||
|  | 
 | ||||||
|  |    /* to reduce branching
 | ||||||
|  |     */ | ||||||
|  |    bracket_size = flags & f_spaces_around_brackets ? 2 : 1; | ||||||
|  |    comma_size = flags & f_spaces_after_commas ? 2 : 1; | ||||||
|  |    colon_size = flags & f_spaces_after_colons ? 2 : 1; | ||||||
|  | 
 | ||||||
|  |    while (value) | ||||||
|  |    { | ||||||
|  |       json_int_t integer; | ||||||
|  |       json_object_entry * entry; | ||||||
|  | 
 | ||||||
|  |       switch (value->type) | ||||||
|  |       { | ||||||
|  |          case json_array: | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == 0) | ||||||
|  |             { | ||||||
|  |                if (value->u.array.length == 0) | ||||||
|  |                { | ||||||
|  |                   total += 2;  /* `[]` */ | ||||||
|  |                   break; | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|  |                total += bracket_size;  /* `[` */ | ||||||
|  | 
 | ||||||
|  |                ++ depth; | ||||||
|  |                MEASURE_NEWLINE(); /* \n after [ */ | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == value->u.array.length) | ||||||
|  |             { | ||||||
|  |                -- depth; | ||||||
|  |                MEASURE_NEWLINE(); | ||||||
|  |                total += bracket_size;  /* `]` */ | ||||||
|  | 
 | ||||||
|  |                ((json_builder_value *) value)->length_iterated = 0; | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated > 0) | ||||||
|  |             { | ||||||
|  |                total += comma_size;  /* `, ` */ | ||||||
|  | 
 | ||||||
|  |                MEASURE_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ((json_builder_value *) value)->length_iterated++; | ||||||
|  |             value = value->u.array.values [((json_builder_value *) value)->length_iterated - 1]; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_object: | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == 0) | ||||||
|  |             { | ||||||
|  |                if (value->u.object.length == 0) | ||||||
|  |                { | ||||||
|  |                   total += 2;  /* `{}` */ | ||||||
|  |                   break; | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|  |                total += bracket_size;  /* `{` */ | ||||||
|  | 
 | ||||||
|  |                ++ depth; | ||||||
|  |                MEASURE_NEWLINE(); /* \n after { */ | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == value->u.object.length) | ||||||
|  |             { | ||||||
|  |                -- depth; | ||||||
|  |                MEASURE_NEWLINE(); | ||||||
|  |                total += bracket_size;  /* `}` */ | ||||||
|  | 
 | ||||||
|  |                ((json_builder_value *) value)->length_iterated = 0; | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated > 0) | ||||||
|  |             { | ||||||
|  |                total += comma_size;  /* `, ` */ | ||||||
|  |                MEASURE_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             entry = value->u.object.values + (((json_builder_value *) value)->length_iterated ++); | ||||||
|  | 
 | ||||||
|  |             total += 2 + colon_size;  /* `"": ` */ | ||||||
|  |             total += measure_string (entry->name_length, entry->name); | ||||||
|  | 
 | ||||||
|  |             value = entry->value; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_string: | ||||||
|  | 
 | ||||||
|  |             total += 2;  /* `""` */ | ||||||
|  |             total += measure_string (value->u.string.length, value->u.string.ptr); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_integer: | ||||||
|  | 
 | ||||||
|  |             integer = value->u.integer; | ||||||
|  | 
 | ||||||
|  |             if (integer < 0) | ||||||
|  |             { | ||||||
|  |                total += 1;  /* `-` */ | ||||||
|  |                integer = - integer; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ++ total;  /* first digit */ | ||||||
|  | 
 | ||||||
|  |             while (integer >= 10) | ||||||
|  |             { | ||||||
|  |                ++ total;  /* another digit */ | ||||||
|  |                integer /= 10; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_double: | ||||||
|  | 
 | ||||||
|  |             total += snprintf (NULL, 0, "%g", value->u.dbl); | ||||||
|  | 
 | ||||||
|  |             /* Because sometimes we need to add ".0" if sprintf does not do it
 | ||||||
|  |              * for us. Downside is that we allocate more bytes than strictly | ||||||
|  |              * needed for serialization. | ||||||
|  |              */ | ||||||
|  |             total += 2; | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_boolean: | ||||||
|  | 
 | ||||||
|  |             total += value->u.boolean ?  | ||||||
|  |                4:  /* `true` */ | ||||||
|  |                5;  /* `false` */ | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_null: | ||||||
|  | 
 | ||||||
|  |             total += 4;  /* `null` */ | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          default: | ||||||
|  |             break; | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       value = value->parent; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (opts.mode == json_serialize_mode_multiline) | ||||||
|  |    { | ||||||
|  |       total += newlines * (((opts.opts & json_serialize_opt_CRLF) ? 2 : 1) + opts.indent_size); | ||||||
|  |       total += indents * opts.indent_size; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return total; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void json_serialize (json_char * buf, json_value * value) | ||||||
|  | { | ||||||
|  |    json_serialize_ex (buf, value, default_opts); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define PRINT_NEWLINE() do {                          \ | ||||||
|  |    if (opts.mode == json_serialize_mode_multiline) {  \ | ||||||
|  |       if (opts.opts & json_serialize_opt_CRLF)        \ | ||||||
|  |          *buf ++ = '\r';                              \ | ||||||
|  |       *buf ++ = '\n';                                 \ | ||||||
|  |       for(i = 0; i < indent; ++ i)                    \ | ||||||
|  |          *buf ++ = indent_char;                       \ | ||||||
|  |    }                                                  \ | ||||||
|  | } while(0);                                           \ | ||||||
|  | 
 | ||||||
|  | #define PRINT_OPENING_BRACKET(c) do {                 \ | ||||||
|  |    *buf ++ = (c);                                     \ | ||||||
|  |    if (flags & f_spaces_around_brackets)              \ | ||||||
|  |       *buf ++ = ' ';                                  \ | ||||||
|  | } while(0);                                           \ | ||||||
|  | 
 | ||||||
|  | #define PRINT_CLOSING_BRACKET(c) do {                 \ | ||||||
|  |    if (flags & f_spaces_around_brackets)              \ | ||||||
|  |       *buf ++ = ' ';                                  \ | ||||||
|  |    *buf ++ = (c);                                     \ | ||||||
|  | } while(0);                                           \ | ||||||
|  | 
 | ||||||
|  | void json_serialize_ex (json_char * buf, json_value * value, json_serialize_opts opts) | ||||||
|  | { | ||||||
|  |    json_int_t integer, orig_integer; | ||||||
|  |    json_object_entry * entry; | ||||||
|  |    json_char * ptr, * dot; | ||||||
|  |    int indent = 0; | ||||||
|  |    char indent_char; | ||||||
|  |    int i; | ||||||
|  |    int flags; | ||||||
|  | 
 | ||||||
|  |    flags = get_serialize_flags (opts); | ||||||
|  | 
 | ||||||
|  |    indent_char = flags & f_tabs ? '\t' : ' '; | ||||||
|  | 
 | ||||||
|  |    while (value) | ||||||
|  |    { | ||||||
|  |       switch (value->type) | ||||||
|  |       { | ||||||
|  |          case json_array: | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == 0) | ||||||
|  |             { | ||||||
|  |                if (value->u.array.length == 0) | ||||||
|  |                { | ||||||
|  |                   *buf ++ = '['; | ||||||
|  |                   *buf ++ = ']'; | ||||||
|  | 
 | ||||||
|  |                   break; | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|  |                PRINT_OPENING_BRACKET ('['); | ||||||
|  | 
 | ||||||
|  |                indent += opts.indent_size; | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == value->u.array.length) | ||||||
|  |             { | ||||||
|  |                indent -= opts.indent_size; | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |                PRINT_CLOSING_BRACKET (']'); | ||||||
|  | 
 | ||||||
|  |                ((json_builder_value *) value)->length_iterated = 0; | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated > 0) | ||||||
|  |             { | ||||||
|  |                *buf ++ = ','; | ||||||
|  | 
 | ||||||
|  |                if (flags & f_spaces_after_commas) | ||||||
|  |                   *buf ++ = ' '; | ||||||
|  | 
 | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ((json_builder_value *) value)->length_iterated++; | ||||||
|  |             value = value->u.array.values [((json_builder_value *) value)->length_iterated - 1]; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_object: | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == 0) | ||||||
|  |             { | ||||||
|  |                if (value->u.object.length == 0) | ||||||
|  |                { | ||||||
|  |                   *buf ++ = '{'; | ||||||
|  |                   *buf ++ = '}'; | ||||||
|  | 
 | ||||||
|  |                   break; | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|  |                PRINT_OPENING_BRACKET ('{'); | ||||||
|  | 
 | ||||||
|  |                indent += opts.indent_size; | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated == value->u.object.length) | ||||||
|  |             { | ||||||
|  |                indent -= opts.indent_size; | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |                PRINT_CLOSING_BRACKET ('}'); | ||||||
|  | 
 | ||||||
|  |                ((json_builder_value *) value)->length_iterated = 0; | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->length_iterated > 0) | ||||||
|  |             { | ||||||
|  |                *buf ++ = ','; | ||||||
|  | 
 | ||||||
|  |                if (flags & f_spaces_after_commas) | ||||||
|  |                   *buf ++ = ' '; | ||||||
|  | 
 | ||||||
|  |                PRINT_NEWLINE(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             entry = value->u.object.values + (((json_builder_value *) value)->length_iterated ++); | ||||||
|  | 
 | ||||||
|  |             *buf ++ = '\"'; | ||||||
|  |             buf += serialize_string (buf, entry->name_length, entry->name); | ||||||
|  |             *buf ++ = '\"'; | ||||||
|  |             *buf ++ = ':'; | ||||||
|  | 
 | ||||||
|  |             if (flags & f_spaces_after_colons) | ||||||
|  |                *buf ++ = ' '; | ||||||
|  | 
 | ||||||
|  |             value = entry->value; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_string: | ||||||
|  | 
 | ||||||
|  |             *buf ++ = '\"'; | ||||||
|  |             buf += serialize_string (buf, value->u.string.length, value->u.string.ptr); | ||||||
|  |             *buf ++ = '\"'; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_integer: | ||||||
|  | 
 | ||||||
|  |             integer = value->u.integer; | ||||||
|  | 
 | ||||||
|  |             if (integer < 0) | ||||||
|  |             { | ||||||
|  |                *buf ++ = '-'; | ||||||
|  |                integer = - integer; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             orig_integer = integer; | ||||||
|  | 
 | ||||||
|  |             ++ buf; | ||||||
|  | 
 | ||||||
|  |             while (integer >= 10) | ||||||
|  |             { | ||||||
|  |                ++ buf; | ||||||
|  |                integer /= 10; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             integer = orig_integer; | ||||||
|  |             ptr = buf; | ||||||
|  | 
 | ||||||
|  |             do | ||||||
|  |             { | ||||||
|  |                *-- ptr = "0123456789"[integer % 10]; | ||||||
|  | 
 | ||||||
|  |             } while ((integer /= 10) > 0); | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_double: | ||||||
|  | 
 | ||||||
|  |             ptr = buf; | ||||||
|  | 
 | ||||||
|  |             buf += sprintf (buf, "%g", value->u.dbl); | ||||||
|  | 
 | ||||||
|  |             if ((dot = strchr (ptr, ','))) | ||||||
|  |             { | ||||||
|  |                *dot = '.'; | ||||||
|  |             } | ||||||
|  |             else if (!strchr (ptr, '.') && !strchr (ptr, 'e')) | ||||||
|  |             { | ||||||
|  |                *buf ++ = '.'; | ||||||
|  |                *buf ++ = '0'; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_boolean: | ||||||
|  | 
 | ||||||
|  |             if (value->u.boolean) | ||||||
|  |             { | ||||||
|  |                memcpy (buf, "true", 4); | ||||||
|  |                buf += 4; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                memcpy (buf, "false", 5); | ||||||
|  |                buf += 5; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          case json_null: | ||||||
|  | 
 | ||||||
|  |             memcpy (buf, "null", 4); | ||||||
|  |             buf += 4; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          default: | ||||||
|  |             break; | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       value = value->parent; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    *buf = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void json_builder_free (json_value * value) | ||||||
|  | { | ||||||
|  |    json_value * cur_value; | ||||||
|  | 
 | ||||||
|  |    if (!value) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |    value->parent = 0; | ||||||
|  | 
 | ||||||
|  |    while (value) | ||||||
|  |    { | ||||||
|  |       switch (value->type) | ||||||
|  |       { | ||||||
|  |          case json_array: | ||||||
|  | 
 | ||||||
|  |             if (!value->u.array.length) | ||||||
|  |             { | ||||||
|  |                free (value->u.array.values); | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             value = value->u.array.values [-- value->u.array.length]; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_object: | ||||||
|  | 
 | ||||||
|  |             if (!value->u.object.length) | ||||||
|  |             { | ||||||
|  |                free (value->u.object.values); | ||||||
|  |                break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             -- value->u.object.length; | ||||||
|  | 
 | ||||||
|  |             if (((json_builder_value *) value)->is_builder_value) | ||||||
|  |             { | ||||||
|  |                /* Names are allocated separately for builder values.  In parser
 | ||||||
|  |                 * values, they are part of the same allocation as the values array | ||||||
|  |                 * itself. | ||||||
|  |                 */ | ||||||
|  |                free (value->u.object.values [value->u.object.length].name); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             value = value->u.object.values [value->u.object.length].value; | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |          case json_string: | ||||||
|  | 
 | ||||||
|  |             free (value->u.string.ptr); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |          default: | ||||||
|  |             break; | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       cur_value = value; | ||||||
|  |       value = value->parent; | ||||||
|  |       free (cur_value); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										159
									
								
								json-builder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								json-builder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,159 @@ | |||||||
|  | 
 | ||||||
|  | /* vim: set et ts=3 sw=3 sts=3 ft=c:
 | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2014 James McLaughlin.  All rights reserved. | ||||||
|  |  * https://github.com/udp/json-builder
 | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *   documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _JSON_BUILDER_H | ||||||
|  | #define _JSON_BUILDER_H | ||||||
|  | 
 | ||||||
|  | /* Requires json.h from json-parser
 | ||||||
|  |  * https://github.com/udp/json-parser
 | ||||||
|  |  */ | ||||||
|  | #include "json.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" | ||||||
|  | { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* IMPORTANT NOTE:  If you want to use json-builder functions with values
 | ||||||
|  |  * allocated by json-parser as part of the parsing process, you must pass | ||||||
|  |  * json_builder_extra as the value_extra setting in json_settings when | ||||||
|  |  * parsing.  Otherwise there will not be room for the extra state and | ||||||
|  |  * json-builder WILL invoke undefined behaviour. | ||||||
|  |  * | ||||||
|  |  * Also note that unlike json-parser, json-builder does not currently support | ||||||
|  |  * custom allocators (for no particular reason other than that it doesn't have | ||||||
|  |  * any settings or global state.) | ||||||
|  |  */ | ||||||
|  | extern const size_t json_builder_extra; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Arrays
 | ||||||
|  |  *** | ||||||
|  |  * Note that all of these length arguments are just a hint to allow for | ||||||
|  |  * pre-allocation - passing 0 is fine. | ||||||
|  |  */ | ||||||
|  | json_value * json_array_new (size_t length); | ||||||
|  | json_value * json_array_push (json_value * array, json_value *); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Objects
 | ||||||
|  |  ***/ | ||||||
|  | json_value * json_object_new (size_t length); | ||||||
|  | 
 | ||||||
|  | json_value * json_object_push (json_value * object, | ||||||
|  |                                const json_char * name, | ||||||
|  |                                json_value *); | ||||||
|  | 
 | ||||||
|  | /* Same as json_object_push, but doesn't call strlen() for you.
 | ||||||
|  |  */ | ||||||
|  | json_value * json_object_push_length (json_value * object, | ||||||
|  |                                       unsigned int name_length, const json_char * name, | ||||||
|  |                                       json_value *); | ||||||
|  | 
 | ||||||
|  | /* Same as json_object_push_length, but doesn't copy the name buffer before
 | ||||||
|  |  * storing it in the value.  Use this micro-optimisation at your own risk. | ||||||
|  |  */ | ||||||
|  | json_value * json_object_push_nocopy (json_value * object, | ||||||
|  |                                       unsigned int name_length, json_char * name, | ||||||
|  |                                       json_value *); | ||||||
|  | 
 | ||||||
|  | /* Merges all entries from objectB into objectA and destroys objectB.
 | ||||||
|  |  */ | ||||||
|  | json_value * json_object_merge (json_value * objectA, json_value * objectB); | ||||||
|  | 
 | ||||||
|  | /* Sort the entries of an object based on the order in a prototype object.
 | ||||||
|  |  * Helpful when reading JSON and writing it again to preserve user order. | ||||||
|  |  */ | ||||||
|  | void json_object_sort (json_value * object, json_value * proto); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Strings
 | ||||||
|  |  ***/ | ||||||
|  | json_value * json_string_new (const json_char *); | ||||||
|  | json_value * json_string_new_length (unsigned int length, const json_char *); | ||||||
|  | json_value * json_string_new_nocopy (unsigned int length, json_char *); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Everything else
 | ||||||
|  |  ***/ | ||||||
|  | json_value * json_integer_new (json_int_t); | ||||||
|  | json_value * json_double_new (double); | ||||||
|  | json_value * json_boolean_new (int); | ||||||
|  | json_value * json_null_new (void); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Serializing
 | ||||||
|  |  ***/ | ||||||
|  | #define json_serialize_mode_multiline     0 | ||||||
|  | #define json_serialize_mode_single_line   1 | ||||||
|  | #define json_serialize_mode_packed        2 | ||||||
|  | 
 | ||||||
|  | #define json_serialize_opt_CRLF                    (1 << 1) | ||||||
|  | #define json_serialize_opt_pack_brackets           (1 << 2) | ||||||
|  | #define json_serialize_opt_no_space_after_comma    (1 << 3) | ||||||
|  | #define json_serialize_opt_no_space_after_colon    (1 << 4) | ||||||
|  | #define json_serialize_opt_use_tabs                (1 << 5) | ||||||
|  | 
 | ||||||
|  | typedef struct json_serialize_opts | ||||||
|  | { | ||||||
|  |    int mode; | ||||||
|  |    int opts; | ||||||
|  |    int indent_size; | ||||||
|  | 
 | ||||||
|  | } json_serialize_opts; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Returns a length in characters that is at least large enough to hold the
 | ||||||
|  |  * value in its serialized form, including a null terminator. | ||||||
|  |  */ | ||||||
|  | size_t json_measure (json_value *); | ||||||
|  | size_t json_measure_ex (json_value *, json_serialize_opts); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Serializes a JSON value into the buffer given (which must already be
 | ||||||
|  |  * allocated with a length of at least json_measure(value, opts)) | ||||||
|  |  */ | ||||||
|  | void json_serialize (json_char * buf, json_value *); | ||||||
|  | void json_serialize_ex (json_char * buf, json_value *, json_serialize_opts); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*** Cleaning up
 | ||||||
|  |  ***/ | ||||||
|  | void json_builder_free (json_value *); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										283
									
								
								json.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								json.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,283 @@ | |||||||
|  | 
 | ||||||
|  | /* vim: set et ts=3 sw=3 sts=3 ft=c:
 | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved. | ||||||
|  |  * https://github.com/udp/json-parser
 | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *   notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *   documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _JSON_H | ||||||
|  | #define _JSON_H | ||||||
|  | 
 | ||||||
|  | #ifndef json_char | ||||||
|  |    #define json_char char | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef json_int_t | ||||||
|  |    #ifndef _MSC_VER | ||||||
|  |       #include <inttypes.h> | ||||||
|  |       #define json_int_t int64_t | ||||||
|  |    #else | ||||||
|  |       #define json_int_t __int64 | ||||||
|  |    #endif | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | 
 | ||||||
|  |    #include <string.h> | ||||||
|  | 
 | ||||||
|  |    extern "C" | ||||||
|  |    { | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    unsigned long max_memory; | ||||||
|  |    int settings; | ||||||
|  | 
 | ||||||
|  |    /* Custom allocator support (leave null to use malloc/free)
 | ||||||
|  |     */ | ||||||
|  | 
 | ||||||
|  |    void * (* mem_alloc) (size_t, int zero, void * user_data); | ||||||
|  |    void (* mem_free) (void *, void * user_data); | ||||||
|  | 
 | ||||||
|  |    void * user_data;  /* will be passed to mem_alloc and mem_free */ | ||||||
|  | 
 | ||||||
|  |    size_t value_extra;  /* how much extra space to allocate for values? */ | ||||||
|  | 
 | ||||||
|  | } json_settings; | ||||||
|  | 
 | ||||||
|  | #define json_enable_comments  0x01 | ||||||
|  | 
 | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |    json_none, | ||||||
|  |    json_object, | ||||||
|  |    json_array, | ||||||
|  |    json_integer, | ||||||
|  |    json_double, | ||||||
|  |    json_string, | ||||||
|  |    json_boolean, | ||||||
|  |    json_null | ||||||
|  | 
 | ||||||
|  | } json_type; | ||||||
|  | 
 | ||||||
|  | extern const struct _json_value json_value_none; | ||||||
|  |         | ||||||
|  | typedef struct _json_object_entry | ||||||
|  | { | ||||||
|  |     json_char * name; | ||||||
|  |     unsigned int name_length; | ||||||
|  |      | ||||||
|  |     struct _json_value * value; | ||||||
|  |      | ||||||
|  | } json_object_entry; | ||||||
|  | 
 | ||||||
|  | typedef struct _json_value | ||||||
|  | { | ||||||
|  |    struct _json_value * parent; | ||||||
|  | 
 | ||||||
|  |    json_type type; | ||||||
|  | 
 | ||||||
|  |    union | ||||||
|  |    { | ||||||
|  |       int boolean; | ||||||
|  |       json_int_t integer; | ||||||
|  |       double dbl; | ||||||
|  | 
 | ||||||
|  |       struct | ||||||
|  |       { | ||||||
|  |          unsigned int length; | ||||||
|  |          json_char * ptr; /* null terminated */ | ||||||
|  | 
 | ||||||
|  |       } string; | ||||||
|  | 
 | ||||||
|  |       struct | ||||||
|  |       { | ||||||
|  |          unsigned int length; | ||||||
|  | 
 | ||||||
|  |          json_object_entry * values; | ||||||
|  | 
 | ||||||
|  |          #if defined(__cplusplus) && __cplusplus >= 201103L | ||||||
|  |          decltype(values) begin () const | ||||||
|  |          {  return values; | ||||||
|  |          } | ||||||
|  |          decltype(values) end () const | ||||||
|  |          {  return values + length; | ||||||
|  |          } | ||||||
|  |          #endif | ||||||
|  | 
 | ||||||
|  |       } object; | ||||||
|  | 
 | ||||||
|  |       struct | ||||||
|  |       { | ||||||
|  |          unsigned int length; | ||||||
|  |          struct _json_value ** values; | ||||||
|  | 
 | ||||||
|  |          #if defined(__cplusplus) && __cplusplus >= 201103L | ||||||
|  |          decltype(values) begin () const | ||||||
|  |          {  return values; | ||||||
|  |          } | ||||||
|  |          decltype(values) end () const | ||||||
|  |          {  return values + length; | ||||||
|  |          } | ||||||
|  |          #endif | ||||||
|  | 
 | ||||||
|  |       } array; | ||||||
|  | 
 | ||||||
|  |    } u; | ||||||
|  | 
 | ||||||
|  |    union | ||||||
|  |    { | ||||||
|  |       struct _json_value * next_alloc; | ||||||
|  |       void * object_mem; | ||||||
|  | 
 | ||||||
|  |    } _reserved; | ||||||
|  | 
 | ||||||
|  |    #ifdef JSON_TRACK_SOURCE | ||||||
|  | 
 | ||||||
|  |       /* Location of the value in the source JSON
 | ||||||
|  |        */ | ||||||
|  |       unsigned int line, col; | ||||||
|  | 
 | ||||||
|  |    #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |    /* Some C++ operator sugar */ | ||||||
|  | 
 | ||||||
|  |    #ifdef __cplusplus | ||||||
|  | 
 | ||||||
|  |       public: | ||||||
|  | 
 | ||||||
|  |          inline _json_value () | ||||||
|  |          {  memset (this, 0, sizeof (_json_value)); | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline const struct _json_value &operator [] (int index) const | ||||||
|  |          { | ||||||
|  |             if (type != json_array || index < 0 | ||||||
|  |                      || ((unsigned int) index) >= u.array.length) | ||||||
|  |             { | ||||||
|  |                return json_value_none; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return *u.array.values [index]; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline const struct _json_value &operator [] (const char * index) const | ||||||
|  |          {  | ||||||
|  |             if (type != json_object) | ||||||
|  |                return json_value_none; | ||||||
|  | 
 | ||||||
|  |             for (unsigned int i = 0; i < u.object.length; ++ i) | ||||||
|  |                if (!strcmp (u.object.values [i].name, index)) | ||||||
|  |                   return *u.object.values [i].value; | ||||||
|  | 
 | ||||||
|  |             return json_value_none; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline operator const char * () const | ||||||
|  |          {   | ||||||
|  |             switch (type) | ||||||
|  |             { | ||||||
|  |                case json_string: | ||||||
|  |                   return u.string.ptr; | ||||||
|  | 
 | ||||||
|  |                default: | ||||||
|  |                   return ""; | ||||||
|  |             }; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline operator json_int_t () const | ||||||
|  |          {   | ||||||
|  |             switch (type) | ||||||
|  |             { | ||||||
|  |                case json_integer: | ||||||
|  |                   return u.integer; | ||||||
|  | 
 | ||||||
|  |                case json_double: | ||||||
|  |                   return (json_int_t) u.dbl; | ||||||
|  | 
 | ||||||
|  |                default: | ||||||
|  |                   return 0; | ||||||
|  |             }; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline operator bool () const | ||||||
|  |          {   | ||||||
|  |             if (type != json_boolean) | ||||||
|  |                return false; | ||||||
|  | 
 | ||||||
|  |             return u.boolean != 0; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          inline operator double () const | ||||||
|  |          {   | ||||||
|  |             switch (type) | ||||||
|  |             { | ||||||
|  |                case json_integer: | ||||||
|  |                   return (double) u.integer; | ||||||
|  | 
 | ||||||
|  |                case json_double: | ||||||
|  |                   return u.dbl; | ||||||
|  | 
 | ||||||
|  |                default: | ||||||
|  |                   return 0; | ||||||
|  |             }; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |    #endif | ||||||
|  | 
 | ||||||
|  | } json_value; | ||||||
|  |         | ||||||
|  | json_value * json_parse (const json_char * json, | ||||||
|  |                          size_t length); | ||||||
|  | 
 | ||||||
|  | #define json_error_max 128 | ||||||
|  | json_value * json_parse_ex (json_settings * settings, | ||||||
|  |                             const json_char * json, | ||||||
|  |                             size_t length, | ||||||
|  |                             char * error); | ||||||
|  | 
 | ||||||
|  | void json_value_free (json_value *); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Not usually necessary, unless you used a custom mem_alloc and now want to
 | ||||||
|  |  * use a custom mem_free. | ||||||
|  |  */ | ||||||
|  | void json_value_free_ex (json_settings * settings, | ||||||
|  |                          json_value *); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  |    } /* extern "C" */ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user