diff options
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/dep/extern/json-builder.c')
-rw-r--r-- | Build/source/texk/web2c/mfluadir/otfcc/dep/extern/json-builder.c | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/dep/extern/json-builder.c b/Build/source/texk/web2c/mfluadir/otfcc/dep/extern/json-builder.c new file mode 100644 index 00000000000..9e36667bafd --- /dev/null +++ b/Build/source/texk/web2c/mfluadir/otfcc/dep/extern/json-builder.c @@ -0,0 +1,889 @@ + +/* 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 "emyg-dtoa/emyg-dtoa.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <math.h> + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +const static 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 0: + case 0xb: + measured_length += 6; + break; + 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 0: + *buf++ = '\\'; + *buf++ = 'u'; + *buf++ = '0'; + *buf++ = '0'; + *buf++ = '0'; + *buf++ = '0'; + continue; + 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; + case 0xb: + *buf++ = '\\'; + *buf++ = 'u'; + *buf++ = '0'; + *buf++ = '0'; + *buf++ = '0'; + *buf++ = 'b'; + 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; +#ifdef CARYLL_USE_PRE_SERIALIZED + case json_pre_serialized: + + total += value->u.string.length; + break; +#endif + 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: { + char buffer[256]; + emyg_dtoa(value->u.dbl, buffer); + total += strlen(buffer); + 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; + 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; +#ifdef CARYLL_USE_PRE_SERIALIZED + case json_pre_serialized: + memcpy(buf, value->u.string.ptr, value->u.string.length); + buf += value->u.string.length; + break; +#endif + 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: { + char tmp[256]; + emyg_dtoa(value->u.dbl, tmp); + memcpy(buf, tmp, strlen(tmp)); + buf += strlen(tmp); + 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: +#ifdef CARYLL_USE_PRE_SERIALIZED + case json_pre_serialized: +#endif + free(value->u.string.ptr); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + free(cur_value); + } +} |