| |
| /* 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 "sg_json_builder.h" |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| /* This code was fetched from https://github.com/json-parser/json-builder |
| * and comes with the 2 clause BSD license (shown above) which is the same |
| * license that most of the rest of this package uses. */ |
| |
| #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); |
| } |
| } |
| |
| |
| |
| |
| |
| |