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