diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2022-04-17 14:15:24 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2022-04-17 14:15:24 +0000 |
commit | f26bb872bf4f043a1b817c808a6947fcb03ecda4 (patch) | |
tree | 132ab4d71c41c26fe05e12a57fc9e684ffbd08ec /lib | |
parent | 5e7262973c7b0bf753e5e560956c176d9d12424d (diff) | |
download | sg3_utils-f26bb872bf4f043a1b817c808a6947fcb03ecda4.tar.gz |
sg_lib: add hex2fp(); sg_rtpg: https://github.com/hreinecke/sg3_utils/pull/79 applies; json_builder work
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@944 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/Makefile.in | 44 | ||||
-rw-r--r-- | lib/sg_json_builder.c | 995 | ||||
-rw-r--r-- | lib/sg_json_builder.h | 324 | ||||
-rw-r--r-- | lib/sg_lib.c | 18 |
5 files changed, 1364 insertions, 21 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index ec3932ed..b481816d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -6,7 +6,8 @@ libsgutils2_la_SOURCES = \ sg_cmds_basic2.c \ sg_cmds_extra.c \ sg_cmds_mmc.c \ - sg_pt_common.c + sg_pt_common.c \ + sg_json_builder.c if OS_LINUX if PT_DUMMY @@ -100,4 +101,3 @@ libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined -release ${PACKAGE_VE libsgutils2_la_LIBADD = @GETOPT_O_FILES@ libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@ - diff --git a/lib/Makefile.in b/lib/Makefile.in index 3b07a603..1a6dbeee 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -146,9 +146,10 @@ am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \ sg_lib_names.c sg_cmds_basic.c sg_cmds_basic2.c \ - sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_pt_dummy.c \ - sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c sg_pt_win32.c \ - sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c sg_pt_haiku.c + sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_json_builder.c \ + sg_pt_dummy.c sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c \ + sg_pt_win32.c sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c \ + sg_pt_haiku.c @OS_LINUX_TRUE@@PT_DUMMY_TRUE@am__objects_1 = sg_pt_dummy.lo @OS_LINUX_TRUE@@PT_DUMMY_FALSE@am__objects_2 = sg_pt_linux.lo \ @OS_LINUX_TRUE@@PT_DUMMY_FALSE@ sg_io_linux.lo \ @@ -166,11 +167,12 @@ am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \ @OS_OTHER_TRUE@am__objects_13 = sg_pt_dummy.lo am_libsgutils2_la_OBJECTS = sg_lib.lo sg_lib_data.lo sg_lib_names.lo \ sg_cmds_basic.lo sg_cmds_basic2.lo sg_cmds_extra.lo \ - sg_cmds_mmc.lo sg_pt_common.lo $(am__objects_1) \ - $(am__objects_2) $(am__objects_3) $(am__objects_4) \ - $(am__objects_5) $(am__objects_6) $(am__objects_7) \ - $(am__objects_8) $(am__objects_9) $(am__objects_10) \ - $(am__objects_11) $(am__objects_12) $(am__objects_13) + sg_cmds_mmc.lo sg_pt_common.lo sg_json_builder.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) $(am__objects_6) \ + $(am__objects_7) $(am__objects_8) $(am__objects_9) \ + $(am__objects_10) $(am__objects_11) $(am__objects_12) \ + $(am__objects_13) libsgutils2_la_OBJECTS = $(am_libsgutils2_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -198,12 +200,13 @@ am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/sg_cmds_basic.Plo \ ./$(DEPDIR)/sg_cmds_basic2.Plo ./$(DEPDIR)/sg_cmds_extra.Plo \ ./$(DEPDIR)/sg_cmds_mmc.Plo ./$(DEPDIR)/sg_io_linux.Plo \ - ./$(DEPDIR)/sg_lib.Plo ./$(DEPDIR)/sg_lib_data.Plo \ - ./$(DEPDIR)/sg_lib_names.Plo ./$(DEPDIR)/sg_pt_common.Plo \ - ./$(DEPDIR)/sg_pt_dummy.Plo ./$(DEPDIR)/sg_pt_freebsd.Plo \ - ./$(DEPDIR)/sg_pt_haiku.Plo ./$(DEPDIR)/sg_pt_linux.Plo \ - ./$(DEPDIR)/sg_pt_linux_nvme.Plo ./$(DEPDIR)/sg_pt_osf1.Plo \ - ./$(DEPDIR)/sg_pt_solaris.Plo ./$(DEPDIR)/sg_pt_win32.Plo + ./$(DEPDIR)/sg_json_builder.Plo ./$(DEPDIR)/sg_lib.Plo \ + ./$(DEPDIR)/sg_lib_data.Plo ./$(DEPDIR)/sg_lib_names.Plo \ + ./$(DEPDIR)/sg_pt_common.Plo ./$(DEPDIR)/sg_pt_dummy.Plo \ + ./$(DEPDIR)/sg_pt_freebsd.Plo ./$(DEPDIR)/sg_pt_haiku.Plo \ + ./$(DEPDIR)/sg_pt_linux.Plo ./$(DEPDIR)/sg_pt_linux_nvme.Plo \ + ./$(DEPDIR)/sg_pt_osf1.Plo ./$(DEPDIR)/sg_pt_solaris.Plo \ + ./$(DEPDIR)/sg_pt_win32.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -376,11 +379,11 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_lib_names.c \ sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \ - sg_pt_common.c $(am__append_1) $(am__append_2) $(am__append_3) \ - $(am__append_4) $(am__append_5) $(am__append_6) \ - $(am__append_7) $(am__append_8) $(am__append_9) \ - $(am__append_10) $(am__append_11) $(am__append_12) \ - $(am__append_13) + sg_pt_common.c sg_json_builder.c $(am__append_1) \ + $(am__append_2) $(am__append_3) $(am__append_4) \ + $(am__append_5) $(am__append_6) $(am__append_7) \ + $(am__append_8) $(am__append_9) $(am__append_10) \ + $(am__append_11) $(am__append_12) $(am__append_13) @DEBUG_FALSE@DBG_CFLAGS = # This is active if --enable-debug given to ./configure @@ -489,6 +492,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_extra.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_io_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_json_builder.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_names.Plo@am__quote@ # am--include-marker @@ -667,6 +671,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/sg_cmds_extra.Plo -rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo -rm -f ./$(DEPDIR)/sg_io_linux.Plo + -rm -f ./$(DEPDIR)/sg_json_builder.Plo -rm -f ./$(DEPDIR)/sg_lib.Plo -rm -f ./$(DEPDIR)/sg_lib_data.Plo -rm -f ./$(DEPDIR)/sg_lib_names.Plo @@ -729,6 +734,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/sg_cmds_extra.Plo -rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo -rm -f ./$(DEPDIR)/sg_io_linux.Plo + -rm -f ./$(DEPDIR)/sg_json_builder.Plo -rm -f ./$(DEPDIR)/sg_lib.Plo -rm -f ./$(DEPDIR)/sg_lib_data.Plo -rm -f ./$(DEPDIR)/sg_lib_names.Plo diff --git a/lib/sg_json_builder.c b/lib/sg_json_builder.c new file mode 100644 index 00000000..4d616989 --- /dev/null +++ b/lib/sg_json_builder.c @@ -0,0 +1,995 @@ + +/* 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> + +#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); + } +} + + + + + + diff --git a/lib/sg_json_builder.h b/lib/sg_json_builder.h new file mode 100644 index 00000000..9d83b026 --- /dev/null +++ b/lib/sg_json_builder.h @@ -0,0 +1,324 @@ + +/* 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 + +/* + * Used to require json.h from json-parser but what was need as been + * included in this header. + * https://github.com/udp/json-parser + */ +/* #include "json.h" */ + +#ifndef json_char + #define json_char char +#endif + +#ifndef json_int_t + #undef JSON_INT_T_OVERRIDDEN + #if defined(_MSC_VER) + #define json_int_t __int64 + #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__cplusplus) && __cplusplus >= 201103L) + /* C99 and C++11 */ + #include <stdint.h> + #define json_int_t int_fast64_t + #else + /* C89 */ + #define json_int_t long + #endif +#else + #define JSON_INT_T_OVERRIDDEN 1 +#endif + +#include <stddef.h> + +#ifdef __cplusplus + + #include <string.h> + + extern "C" + { + +#endif + +typedef struct +{ + unsigned long max_memory; /* should be size_t, but would modify the API */ + 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) + json_object_entry * begin () const + { return values; + } + json_object_entry * end () const + { return values + length; + } + #endif + + } object; + + struct + { + unsigned int length; + struct _json_value ** values; + + #if defined(__cplusplus) + _json_value ** begin () const + { return values; + } + _json_value ** 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 + + + /* C++ operator sugar removed */ + +} json_value; + +#if 0 +#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 *); +#endif + +/* <<< end of code from json-parser's json.h >>> */ + + +/* 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 + + + diff --git a/lib/sg_lib.c b/lib/sg_lib.c index c4fe1d7d..36af6e6f 100644 --- a/lib/sg_lib.c +++ b/lib/sg_lib.c @@ -3049,6 +3049,24 @@ hex2str(const uint8_t * b_str, int len, const char * leadin, int format, return dStrHexStr((const char *)b_str, len, leadin, format, b_len, b); } +void +hex2fp(const uint8_t * b_str, int len, const char * leadin, int format, + FILE * fp) +{ + int k, num; + char b[800]; /* allow for 4 lines of 16 bytes (in hex) each */ + + if (leadin && (strlen(leadin) > 118)) { + fprintf(fp, ">>> leadin parameter is too large\n"); + return; + } + for (k = 0; k < len; k += num) { + num = ((k + 64) < len) ? 64 : (len - k); + hex2str(b_str + k, num, leadin, format, sizeof(b), b); + fprintf(fp, "%s", b); + } +} + /* Returns true when executed on big endian machine; else returns false. * Useful for displaying ATA identify words (which need swapping on a * big endian machine). */ |