summaryrefslogtreecommitdiff
path: root/extsrc/cson_amalgamation.c
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
committercashmere <cashmere@autistici.org>2026-09-04 09:05:24 +0200
commit4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch)
treec296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /extsrc/cson_amalgamation.c
downloadrelic-4f49b16079b27d7ce633619e4cddfdf2bffbc952.tar.gz
initial commitHEADmaster
Diffstat (limited to 'extsrc/cson_amalgamation.c')
-rw-r--r--extsrc/cson_amalgamation.c5712
1 files changed, 5712 insertions, 0 deletions
diff --git a/extsrc/cson_amalgamation.c b/extsrc/cson_amalgamation.c
new file mode 100644
index 0000000..e8f17ff
--- /dev/null
+++ b/extsrc/cson_amalgamation.c
@@ -0,0 +1,5712 @@
+#ifdef FOSSIL_ENABLE_JSON
+/* auto-generated! Do not edit! */
+#include "cson_amalgamation.h"
+/* begin file parser/JSON_parser.h */
+/* See JSON_parser.c for copyright information and licensing. */
+
+#ifndef JSON_PARSER_H
+#define JSON_PARSER_H
+
+/* JSON_parser.h */
+
+
+#include <stddef.h>
+
+/* Windows DLL stuff */
+#ifdef JSON_PARSER_DLL
+# ifdef _MSC_VER
+# ifdef JSON_PARSER_DLL_EXPORTS
+# define JSON_PARSER_DLL_API __declspec(dllexport)
+# else
+# define JSON_PARSER_DLL_API __declspec(dllimport)
+# endif
+# else
+# define JSON_PARSER_DLL_API
+# endif
+#else
+# define JSON_PARSER_DLL_API
+#endif
+
+/* Determine the integer type use to parse non-floating point numbers */
+#ifdef _WIN32
+typedef __int64 JSON_int_t;
+#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%I64d"
+#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
+#elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
+typedef long long JSON_int_t;
+#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
+#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
+#else
+typedef long JSON_int_t;
+#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
+#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum
+{
+ JSON_E_NONE = 0,
+ JSON_E_INVALID_CHAR,
+ JSON_E_INVALID_KEYWORD,
+ JSON_E_INVALID_ESCAPE_SEQUENCE,
+ JSON_E_INVALID_UNICODE_SEQUENCE,
+ JSON_E_INVALID_NUMBER,
+ JSON_E_NESTING_DEPTH_REACHED,
+ JSON_E_UNBALANCED_COLLECTION,
+ JSON_E_EXPECTED_KEY,
+ JSON_E_EXPECTED_COLON,
+ JSON_E_OUT_OF_MEMORY
+} JSON_error;
+
+typedef enum
+{
+ JSON_T_NONE = 0,
+ JSON_T_ARRAY_BEGIN,
+ JSON_T_ARRAY_END,
+ JSON_T_OBJECT_BEGIN,
+ JSON_T_OBJECT_END,
+ JSON_T_INTEGER,
+ JSON_T_FLOAT,
+ JSON_T_NULL,
+ JSON_T_TRUE,
+ JSON_T_FALSE,
+ JSON_T_STRING,
+ JSON_T_KEY,
+ JSON_T_MAX
+} JSON_type;
+
+typedef struct JSON_value_struct {
+ union {
+ JSON_int_t integer_value;
+
+ double float_value;
+
+ struct {
+ const char* value;
+ size_t length;
+ } str;
+ } vu;
+} JSON_value;
+
+typedef struct JSON_parser_struct* JSON_parser;
+
+/*! \brief JSON parser callback
+
+ \param ctx The pointer passed to new_JSON_parser.
+ \param type An element of JSON_type but not JSON_T_NONE.
+ \param value A representation of the parsed value. This parameter is NULL for
+ JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
+ JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
+ as zero-terminated C strings.
+
+ \return Non-zero if parsing should continue, else zero.
+*/
+typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);
+
+
+/**
+ A typedef for allocator functions semantically compatible with malloc().
+*/
+typedef void* (*JSON_malloc_t)(size_t n);
+/**
+ A typedef for deallocator functions semantically compatible with free().
+*/
+typedef void (*JSON_free_t)(void* mem);
+
+/*! \brief The structure used to configure a JSON parser object
+*/
+typedef struct {
+ /** Pointer to a callback, called when the parser has something to tell
+ the user. This parameter may be NULL. In this case the input is
+ merely checked for validity.
+ */
+ JSON_parser_callback callback;
+ /**
+ Callback context - client-specified data to pass to the
+ callback function. This parameter may be NULL.
+ */
+ void* callback_ctx;
+ /** Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting.
+ If negative, the parser can parse arbitrary levels of JSON, otherwise
+ the depth is the limit.
+ */
+ int depth;
+ /**
+ To allow C style comments in JSON, set to non-zero.
+ */
+ int allow_comments;
+ /**
+ To decode floating point numbers manually set this parameter to
+ non-zero.
+ */
+ int handle_floats_manually;
+ /**
+ The memory allocation routine, which must be semantically
+ compatible with malloc(3). If set to NULL, malloc(3) is used.
+
+ If this is set to a non-NULL value then the 'free' member MUST be
+ set to the proper deallocation counterpart for this function.
+ Failure to do so results in undefined behaviour at deallocation
+ time.
+ */
+ JSON_malloc_t malloc;
+ /**
+ The memory deallocation routine, which must be semantically
+ compatible with free(3). If set to NULL, free(3) is used.
+
+ If this is set to a non-NULL value then the 'alloc' member MUST be
+ set to the proper allocation counterpart for this function.
+ Failure to do so results in undefined behaviour at deallocation
+ time.
+ */
+ JSON_free_t free;
+} JSON_config;
+
+/*! \brief Initializes the JSON parser configuration structure to default values.
+
+ The default configuration is
+ - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c)
+ - no parsing, just checking for JSON syntax
+ - no comments
+ - Uses realloc() for memory de/allocation.
+
+ \param config. Used to configure the parser.
+*/
+JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
+
+/*! \brief Create a JSON parser object
+
+ \param config. Used to configure the parser. Set to NULL to use
+ the default configuration. See init_JSON_config. Its contents are
+ copied by this function, so it need not outlive the returned
+ object.
+
+ \return The parser object, which is owned by the caller and must eventually
+ be freed by calling delete_JSON_parser().
+*/
+JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
+
+/*! \brief Destroy a previously created JSON parser object. */
+JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc);
+
+/*! \brief Parse a character.
+
+ \return Non-zero, if all characters passed to this function are part of are valid JSON.
+*/
+JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
+
+/*! \brief Finalize parsing.
+
+ Call this method once after all input characters have been consumed.
+
+ \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
+*/
+JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
+
+/*! \brief Determine if a given string is valid JSON white space
+
+ \return Non-zero if the string is valid, zero otherwise.
+*/
+JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
+
+/*! \brief Gets the last error that occurred during the use of JSON_parser.
+
+ \return A value from the JSON_error enum.
+*/
+JSON_PARSER_DLL_API int JSON_parser_get_last_error(JSON_parser jc);
+
+/*! \brief Re-sets the parser to prepare it for another parse run.
+
+ \return True (non-zero) on success, 0 on error (e.g. !jc).
+*/
+JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* JSON_PARSER_H */
+/* end file parser/JSON_parser.h */
+/* begin file parser/JSON_parser.c */
+/*
+Copyright (c) 2007-2013 Jean Gressmann (jean@0x42.de)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/*
+ Changelog:
+ 2013-09-08
+ Updated license to to be compatible with Debian license requirements.
+
+ 2012-06-06
+ Fix for invalid UTF16 characters and some comment fixex (thomas.h.moog@intel.com).
+
+ 2010-11-25
+ Support for custom memory allocation (sgbeal@googlemail.com).
+
+ 2010-05-07
+ Added error handling for memory allocation failure (sgbeal@googlemail.com).
+ Added diagnosis errors for invalid JSON.
+
+ 2010-03-25
+ Fixed buffer overrun in grow_parse_buffer & cleaned up code.
+
+ 2009-10-19
+ Replaced long double in JSON_value_struct with double after reports
+ of strtold being broken on some platforms (charles@transmissionbt.com).
+
+ 2009-05-17
+ Incorporated benrudiak@googlemail.com fix for UTF16 decoding.
+
+ 2009-05-14
+ Fixed float parsing bug related to a locale being set that didn't
+ use '.' as decimal point character (charles@transmissionbt.com).
+
+ 2008-10-14
+ Renamed states.IN to states.IT to avoid name clash which IN macro
+ defined in windef.h (alexey.pelykh@gmail.com)
+
+ 2008-07-19
+ Removed some duplicate code & debugging variable (charles@transmissionbt.com)
+
+ 2008-05-28
+ Made JSON_value structure ansi C compliant. This bug was report by
+ trisk@acm.jhu.edu
+
+ 2008-05-20
+ Fixed bug reported by charles@transmissionbt.com where the switching
+ from static to dynamic parse buffer did not copy the static parse
+ buffer's content.
+*/
+
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <float.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+
+#ifdef _MSC_VER
+# if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
+# pragma warning(disable:4996) /* unsecure sscanf */
+# pragma warning(disable:4127) /* conditional expression is constant */
+# endif
+#endif
+
+
+#define true 1
+#define false 0
+#define XX -1 /* the universal error code */
+
+/* values chosen so that the object size is approx equal to one page (4K) */
+#ifndef JSON_PARSER_STACK_SIZE
+# define JSON_PARSER_STACK_SIZE 128
+#endif
+
+#ifndef JSON_PARSER_PARSE_BUFFER_SIZE
+# define JSON_PARSER_PARSE_BUFFER_SIZE 3500
+#endif
+
+typedef void* (*JSON_debug_malloc_t)(size_t bytes, const char* reason);
+
+#ifdef JSON_PARSER_DEBUG_MALLOC
+# define JSON_parser_malloc(func, bytes, reason) ((JSON_debug_malloc_t)func)(bytes, reason)
+#else
+# define JSON_parser_malloc(func, bytes, reason) func(bytes)
+#endif
+
+typedef unsigned short UTF16;
+
+struct JSON_parser_struct {
+ JSON_parser_callback callback;
+ void* ctx;
+ signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually, error;
+ char decimal_point;
+ UTF16 utf16_high_surrogate;
+ int current_char;
+ int depth;
+ int top;
+ int stack_capacity;
+ signed char* stack;
+ char* parse_buffer;
+ size_t parse_buffer_capacity;
+ size_t parse_buffer_count;
+ signed char static_stack[JSON_PARSER_STACK_SIZE];
+ char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE];
+ JSON_malloc_t malloc;
+ JSON_free_t free;
+};
+
+#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
+
+/*
+ Characters are mapped into these character classes. This allows for
+ a significant reduction in the size of the state transition table.
+*/
+
+
+
+enum classes {
+ C_SPACE, /* space */
+ C_WHITE, /* other whitespace */
+ C_LCURB, /* { */
+ C_RCURB, /* } */
+ C_LSQRB, /* [ */
+ C_RSQRB, /* ] */
+ C_COLON, /* : */
+ C_COMMA, /* , */
+ C_QUOTE, /* " */
+ C_BACKS, /* \ */
+ C_SLASH, /* / */
+ C_PLUS, /* + */
+ C_MINUS, /* - */
+ C_POINT, /* . */
+ C_ZERO , /* 0 */
+ C_DIGIT, /* 123456789 */
+ C_LOW_A, /* a */
+ C_LOW_B, /* b */
+ C_LOW_C, /* c */
+ C_LOW_D, /* d */
+ C_LOW_E, /* e */
+ C_LOW_F, /* f */
+ C_LOW_L, /* l */
+ C_LOW_N, /* n */
+ C_LOW_R, /* r */
+ C_LOW_S, /* s */
+ C_LOW_T, /* t */
+ C_LOW_U, /* u */
+ C_ABCDF, /* ABCDF */
+ C_E, /* E */
+ C_ETC, /* everything else */
+ C_STAR, /* * */
+ NR_CLASSES
+};
+
+static const signed char ascii_class[128] = {
+/*
+ This array maps the 128 ASCII characters into character classes.
+ The remaining Unicode characters should be mapped to C_ETC.
+ Non-whitespace control characters are errors.
+*/
+ XX, XX, XX, XX, XX, XX, XX, XX,
+ XX, C_WHITE, C_WHITE, XX, XX, C_WHITE, XX, XX,
+ XX, XX, XX, XX, XX, XX, XX, XX,
+ XX, XX, XX, XX, XX, XX, XX, XX,
+
+ C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
+ C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
+ C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
+ C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
+
+ C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,
+ C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
+ C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
+ C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,
+
+ C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
+ C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,
+ C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,
+ C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC
+};
+
+
+/*
+ The state codes.
+*/
+enum states {
+ GO, /* start */
+ OK, /* ok */
+ OB, /* object */
+ KE, /* key */
+ CO, /* colon */
+ VA, /* value */
+ AR, /* array */
+ ST, /* string */
+ ESC, /* escape */
+ U1, /* u1 */
+ U2, /* u2 */
+ U3, /* u3 */
+ U4, /* u4 */
+ MI, /* minus */
+ ZE, /* zero */
+ IT, /* integer */
+ FR, /* fraction */
+ E1, /* e */
+ E2, /* ex */
+ E3, /* exp */
+ T1, /* tr */
+ T2, /* tru */
+ T3, /* true */
+ F1, /* fa */
+ F2, /* fal */
+ F3, /* fals */
+ F4, /* false */
+ N1, /* nu */
+ N2, /* nul */
+ N3, /* null */
+ C1, /* / */
+ C2, /* / * */
+ C3, /* * */
+ FX, /* *.* *eE* */
+ D1, /* second UTF-16 character decoding started by \ */
+ D2, /* second UTF-16 character proceeded by u */
+ NR_STATES
+};
+
+enum actions
+{
+ CB = -10, /* comment begin */
+ CE = -11, /* comment end */
+ FA = -12, /* false */
+ TR = -13, /* false */
+ NU = -14, /* null */
+ DE = -15, /* double detected by exponent e E */
+ DF = -16, /* double detected by fraction . */
+ SB = -17, /* string begin */
+ MX = -18, /* integer detected by minus */
+ ZX = -19, /* integer detected by zero */
+ IX = -20, /* integer detected by 1-9 */
+ EX = -21, /* next char is escaped */
+ UC = -22 /* Unicode character read */
+};
+
+
+static const signed char state_transition_table[NR_STATES][NR_CLASSES] = {
+/*
+ The state transition table takes the current state and the current symbol,
+ and returns either a new state or an action. An action is represented as a
+ negative number. A JSON text is accepted if at the end of the text the
+ state is OK and if the mode is MODE_DONE.
+
+ white 1-9 ABCDF etc
+ space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
+/*start GO*/ {GO,GO,-6,XX,-5,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*ok OK*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*object OB*/ {OB,OB,XX,-9,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*key KE*/ {KE,KE,XX,XX,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*colon CO*/ {CO,CO,XX,XX,XX,XX,-2,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*value VA*/ {VA,VA,-6,XX,-5,XX,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
+/*array AR*/ {AR,AR,-6,XX,-5,-7,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
+/*string ST*/ {ST,XX,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
+/*escape ES*/ {XX,XX,XX,XX,XX,XX,XX,XX,ST,ST,ST,XX,XX,XX,XX,XX,XX,ST,XX,XX,XX,ST,XX,ST,ST,XX,ST,U1,XX,XX,XX,XX},
+/*u1 U1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U2,U2,U2,U2,U2,U2,U2,U2,XX,XX,XX,XX,XX,XX,U2,U2,XX,XX},
+/*u2 U2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U3,U3,U3,U3,U3,U3,U3,U3,XX,XX,XX,XX,XX,XX,U3,U3,XX,XX},
+/*u3 U3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U4,U4,U4,U4,U4,U4,U4,U4,XX,XX,XX,XX,XX,XX,U4,U4,XX,XX},
+/*u4 U4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,UC,UC,UC,UC,UC,UC,UC,UC,XX,XX,XX,XX,XX,XX,UC,UC,XX,XX},
+/*minus MI*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,ZE,IT,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*zero ZE*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*int IT*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,IT,IT,XX,XX,XX,XX,DE,XX,XX,XX,XX,XX,XX,XX,XX,DE,XX,XX},
+/*frac FR*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
+/*e E1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E2,E2,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*ex E2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*exp E3*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*tr T1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T2,XX,XX,XX,XX,XX,XX,XX},
+/*tru T2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T3,XX,XX,XX,XX},
+/*true T3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*fa F1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*fal F2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*fals F3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F4,XX,XX,XX,XX,XX,XX},
+/*false F4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*nu N1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N2,XX,XX,XX,XX},
+/*nul N2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*null N3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*/ C1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,C2},
+/*/star C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
+/** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
+/*_. FX*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
+/*\ D1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,D2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
+/*\ D2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U1,XX,XX,XX,XX},
+};
+
+
+/*
+ These modes can be pushed on the stack.
+*/
+enum modes {
+ MODE_ARRAY = 1,
+ MODE_DONE = 2,
+ MODE_KEY = 3,
+ MODE_OBJECT = 4
+};
+
+static void set_error(JSON_parser jc)
+{
+ switch (jc->state) {
+ case GO:
+ switch (jc->current_char) {
+ case '{': case '}': case '[': case ']':
+ jc->error = JSON_E_UNBALANCED_COLLECTION;
+ break;
+ default:
+ jc->error = JSON_E_INVALID_CHAR;
+ break;
+ }
+ break;
+ case OB:
+ jc->error = JSON_E_EXPECTED_KEY;
+ break;
+ case AR:
+ jc->error = JSON_E_UNBALANCED_COLLECTION;
+ break;
+ case CO:
+ jc->error = JSON_E_EXPECTED_COLON;
+ break;
+ case KE:
+ jc->error = JSON_E_EXPECTED_KEY;
+ break;
+ /* \uXXXX\uYYYY */
+ case U1: case U2: case U3: case U4: case D1: case D2:
+ jc->error = JSON_E_INVALID_UNICODE_SEQUENCE;
+ break;
+ /* true, false, null */
+ case T1: case T2: case T3: case F1: case F2: case F3: case F4: case N1: case N2: case N3:
+ jc->error = JSON_E_INVALID_KEYWORD;
+ break;
+ /* minus, integer, fraction, exponent */
+ case MI: case ZE: case IT: case FR: case E1: case E2: case E3:
+ jc->error = JSON_E_INVALID_NUMBER;
+ break;
+ default:
+ jc->error = JSON_E_INVALID_CHAR;
+ break;
+ }
+}
+
+static int
+push(JSON_parser jc, int mode)
+{
+/*
+ Push a mode onto the stack. Return false if there is overflow.
+*/
+ assert(jc->top <= jc->stack_capacity);
+
+ if (jc->depth < 0) {
+ if (jc->top == jc->stack_capacity) {
+ const size_t bytes_to_copy = jc->stack_capacity * sizeof(jc->stack[0]);
+ const size_t new_capacity = jc->stack_capacity * 2;
+ const size_t bytes_to_allocate = new_capacity * sizeof(jc->stack[0]);
+ void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "stack");
+ if (!mem) {
+ jc->error = JSON_E_OUT_OF_MEMORY;
+ return false;
+ }
+ jc->stack_capacity = (int)new_capacity;
+ memcpy(mem, jc->stack, bytes_to_copy);
+ if (jc->stack != &jc->static_stack[0]) {
+ jc->free(jc->stack);
+ }
+ jc->stack = (signed char*)mem;
+ }
+ } else {
+ if (jc->top == jc->depth) {
+ jc->error = JSON_E_NESTING_DEPTH_REACHED;
+ return false;
+ }
+ }
+ jc->stack[++jc->top] = (signed char)mode;
+ return true;
+}
+
+
+static int
+pop(JSON_parser jc, int mode)
+{
+/*
+ Pop the stack, assuring that the current mode matches the expectation.
+ Return false if there is underflow or if the modes mismatch.
+*/
+ if (jc->top < 0 || jc->stack[jc->top] != mode) {
+ return false;
+ }
+ jc->top -= 1;
+ return true;
+}
+
+
+#define parse_buffer_clear(jc) \
+ do {\
+ jc->parse_buffer_count = 0;\
+ jc->parse_buffer[0] = 0;\
+ } while (0)
+
+#define parse_buffer_pop_back_char(jc)\
+ do {\
+ assert(jc->parse_buffer_count >= 1);\
+ --jc->parse_buffer_count;\
+ jc->parse_buffer[jc->parse_buffer_count] = 0;\
+ } while (0)
+
+
+
+void delete_JSON_parser(JSON_parser jc)
+{
+ if (jc) {
+ if (jc->stack != &jc->static_stack[0]) {
+ jc->free((void*)jc->stack);
+ }
+ if (jc->parse_buffer != &jc->static_parse_buffer[0]) {
+ jc->free((void*)jc->parse_buffer);
+ }
+ jc->free((void*)jc);
+ }
+}
+
+int JSON_parser_reset(JSON_parser jc)
+{
+ if (NULL == jc) {
+ return false;
+ }
+
+ jc->state = GO;
+ jc->top = -1;
+
+ /* parser has been used previously? */
+ if (NULL == jc->parse_buffer) {
+
+ /* Do we want non-bound stack? */
+ if (jc->depth > 0) {
+ jc->stack_capacity = jc->depth;
+ if (jc->depth <= (int)COUNTOF(jc->static_stack)) {
+ jc->stack = &jc->static_stack[0];
+ } else {
+ const size_t bytes_to_alloc = jc->stack_capacity * sizeof(jc->stack[0]);
+ jc->stack = (signed char*)JSON_parser_malloc(jc->malloc, bytes_to_alloc, "stack");
+ if (jc->stack == NULL) {
+ return false;
+ }
+ }
+ } else {
+ jc->stack_capacity = (int)COUNTOF(jc->static_stack);
+ jc->depth = -1;
+ jc->stack = &jc->static_stack[0];
+ }
+
+ /* set up the parse buffer */
+ jc->parse_buffer = &jc->static_parse_buffer[0];
+ jc->parse_buffer_capacity = COUNTOF(jc->static_parse_buffer);
+ }
+
+ /* set parser to start */
+ push(jc, MODE_DONE);
+ parse_buffer_clear(jc);
+
+ return true;
+}
+
+JSON_parser
+new_JSON_parser(JSON_config const * config)
+{
+/*
+ new_JSON_parser starts the checking process by constructing a JSON_parser
+ object. It takes a depth parameter that restricts the level of maximum
+ nesting.
+
+ To continue the process, call JSON_parser_char for each character in the
+ JSON text, and then call JSON_parser_done to obtain the final result.
+ These functions are fully reentrant.
+*/
+
+ int use_std_malloc = false;
+ JSON_config default_config;
+ JSON_parser jc;
+ JSON_malloc_t alloc;
+
+ /* set to default configuration if none was provided */
+ if (NULL == config) {
+ /* initialize configuration */
+ init_JSON_config(&default_config);
+ config = &default_config;
+ }
+
+ /* use std malloc if either the allocator or deallocator function isn't set */
+ use_std_malloc = NULL == config->malloc || NULL == config->free;
+
+ alloc = use_std_malloc ? malloc : config->malloc;
+
+ jc = (JSON_parser)JSON_parser_malloc(alloc, sizeof(*jc), "parser");
+
+ if (NULL == jc) {
+ return NULL;
+ }
+
+ /* configure the parser */
+ memset(jc, 0, sizeof(*jc));
+ jc->malloc = alloc;
+ jc->free = use_std_malloc ? free : config->free;
+ jc->callback = config->callback;
+ jc->ctx = config->callback_ctx;
+ jc->allow_comments = (signed char)(config->allow_comments != 0);
+ jc->handle_floats_manually = (signed char)(config->handle_floats_manually != 0);
+ jc->decimal_point = *localeconv()->decimal_point;
+ /* We need to be able to push at least one object */
+ jc->depth = config->depth == 0 ? 1 : config->depth;
+
+ /* reset the parser */
+ if (!JSON_parser_reset(jc)) {
+ jc->free(jc);
+ return NULL;
+ }
+
+ return jc;
+}
+
+static int parse_buffer_grow(JSON_parser jc)
+{
+ const size_t bytes_to_copy = jc->parse_buffer_count * sizeof(jc->parse_buffer[0]);
+ const size_t new_capacity = jc->parse_buffer_capacity * 2;
+ const size_t bytes_to_allocate = new_capacity * sizeof(jc->parse_buffer[0]);
+ void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "parse buffer");
+
+ if (mem == NULL) {
+ jc->error = JSON_E_OUT_OF_MEMORY;
+ return false;
+ }
+
+ assert(new_capacity > 0);
+ memcpy(mem, jc->parse_buffer, bytes_to_copy);
+
+ if (jc->parse_buffer != &jc->static_parse_buffer[0]) {
+ jc->free(jc->parse_buffer);
+ }
+
+ jc->parse_buffer = (char*)mem;
+ jc->parse_buffer_capacity = new_capacity;
+
+ return true;
+}
+
+static int parse_buffer_reserve_for(JSON_parser jc, unsigned chars)
+{
+ while (jc->parse_buffer_count + chars + 1 > jc->parse_buffer_capacity) {
+ if (!parse_buffer_grow(jc)) {
+ assert(jc->error == JSON_E_OUT_OF_MEMORY);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#define parse_buffer_has_space_for(jc, count) \
+ (jc->parse_buffer_count + (count) + 1 <= jc->parse_buffer_capacity)
+
+#define parse_buffer_push_back_char(jc, c)\
+ do {\
+ assert(parse_buffer_has_space_for(jc, 1)); \
+ jc->parse_buffer[jc->parse_buffer_count++] = c;\
+ jc->parse_buffer[jc->parse_buffer_count] = 0;\
+ } while (0)
+
+#define assert_is_non_container_type(jc) \
+ assert( \
+ jc->type == JSON_T_NULL || \
+ jc->type == JSON_T_FALSE || \
+ jc->type == JSON_T_TRUE || \
+ jc->type == JSON_T_FLOAT || \
+ jc->type == JSON_T_INTEGER || \
+ jc->type == JSON_T_STRING)
+
+
+static int parse_parse_buffer(JSON_parser jc)
+{
+ if (jc->callback) {
+ JSON_value value, *arg = NULL;
+
+ if (jc->type != JSON_T_NONE) {
+ assert_is_non_container_type(jc);
+
+ switch(jc->type) {
+ case JSON_T_FLOAT:
+ arg = &value;
+ if (jc->handle_floats_manually) {
+ value.vu.str.value = jc->parse_buffer;
+ value.vu.str.length = jc->parse_buffer_count;
+ } else {
+ /* not checking with end pointer b/c there may be trailing ws */
+ value.vu.float_value = strtod(jc->parse_buffer, NULL);
+ }
+ break;
+ case JSON_T_INTEGER:
+ arg = &value;
+ sscanf(jc->parse_buffer, JSON_PARSER_INTEGER_SSCANF_TOKEN, &value.vu.integer_value);
+ break;
+ case JSON_T_STRING:
+ arg = &value;
+ value.vu.str.value = jc->parse_buffer;
+ value.vu.str.length = jc->parse_buffer_count;
+ break;
+ }
+
+ if (!(*jc->callback)(jc->ctx, jc->type, arg)) {
+ return false;
+ }
+ }
+ }
+
+ parse_buffer_clear(jc);
+
+ return true;
+}
+
+#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800)
+#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00)
+#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000)
+static const unsigned char utf8_lead_bits[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
+
+static int decode_unicode_char(JSON_parser jc)
+{
+ int i;
+ unsigned uc = 0;
+ char* p;
+ int trail_bytes;
+
+ assert(jc->parse_buffer_count >= 6);
+
+ p = &jc->parse_buffer[jc->parse_buffer_count - 4];
+
+ for (i = 12; i >= 0; i -= 4, ++p) {
+ unsigned x = *p;
+
+ if (x >= 'a') {
+ x -= ('a' - 10);
+ } else if (x >= 'A') {
+ x -= ('A' - 10);
+ } else {
+ x &= ~0x30u;
+ }
+
+ assert(x < 16);
+
+ uc |= x << i;
+ }
+
+ /* clear UTF-16 char from buffer */
+ jc->parse_buffer_count -= 6;
+ jc->parse_buffer[jc->parse_buffer_count] = 0;
+
+ if (uc == 0xffff || uc == 0xfffe) {
+ return false;
+ }
+
+ /* attempt decoding ... */
+ if (jc->utf16_high_surrogate) {
+ if (IS_LOW_SURROGATE(uc)) {
+ uc = DECODE_SURROGATE_PAIR(jc->utf16_high_surrogate, uc);
+ trail_bytes = 3;
+ jc->utf16_high_surrogate = 0;
+ } else {
+ /* high surrogate without a following low surrogate */
+ return false;
+ }
+ } else {
+ if (uc < 0x80) {
+ trail_bytes = 0;
+ } else if (uc < 0x800) {
+ trail_bytes = 1;
+ } else if (IS_HIGH_SURROGATE(uc)) {
+ /* save the high surrogate and wait for the low surrogate */
+ jc->utf16_high_surrogate = (UTF16)uc;
+ return true;
+ } else if (IS_LOW_SURROGATE(uc)) {
+ /* low surrogate without a preceding high surrogate */
+ return false;
+ } else {
+ trail_bytes = 2;
+ }
+ }
+
+ jc->parse_buffer[jc->parse_buffer_count++] = (char) ((uc >> (trail_bytes * 6)) | utf8_lead_bits[trail_bytes]);
+
+ for (i = trail_bytes * 6 - 6; i >= 0; i -= 6) {
+ jc->parse_buffer[jc->parse_buffer_count++] = (char) (((uc >> i) & 0x3F) | 0x80);
+ }
+
+ jc->parse_buffer[jc->parse_buffer_count] = 0;
+
+ return true;
+}
+
+static int add_escaped_char_to_parse_buffer(JSON_parser jc, int next_char)
+{
+ assert(parse_buffer_has_space_for(jc, 1));
+
+ jc->escaped = 0;
+ /* remove the backslash */
+ parse_buffer_pop_back_char(jc);
+ switch(next_char) {
+ case 'b':
+ parse_buffer_push_back_char(jc, '\b');
+ break;
+ case 'f':
+ parse_buffer_push_back_char(jc, '\f');
+ break;
+ case 'n':
+ parse_buffer_push_back_char(jc, '\n');
+ break;
+ case 'r':
+ parse_buffer_push_back_char(jc, '\r');
+ break;
+ case 't':
+ parse_buffer_push_back_char(jc, '\t');
+ break;
+ case '"':
+ parse_buffer_push_back_char(jc, '"');
+ break;
+ case '\\':
+ parse_buffer_push_back_char(jc, '\\');
+ break;
+ case '/':
+ parse_buffer_push_back_char(jc, '/');
+ break;
+ case 'u':
+ parse_buffer_push_back_char(jc, '\\');
+ parse_buffer_push_back_char(jc, 'u');
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static int add_char_to_parse_buffer(JSON_parser jc, int next_char, int next_class)
+{
+ if (!parse_buffer_reserve_for(jc, 1)) {
+ assert(JSON_E_OUT_OF_MEMORY == jc->error);
+ return false;
+ }
+
+ if (jc->escaped) {
+ if (!add_escaped_char_to_parse_buffer(jc, next_char)) {
+ jc->error = JSON_E_INVALID_ESCAPE_SEQUENCE;
+ return false;
+ }
+ } else if (!jc->comment) {
+ if ((jc->type != JSON_T_NONE) | !((next_class == C_SPACE) | (next_class == C_WHITE)) /* non-white-space */) {
+ parse_buffer_push_back_char(jc, (char)next_char);
+ }
+ }
+
+ return true;
+}
+
+#define assert_type_isnt_string_null_or_bool(jc) \
+ assert(jc->type != JSON_T_FALSE); \
+ assert(jc->type != JSON_T_TRUE); \
+ assert(jc->type != JSON_T_NULL); \
+ assert(jc->type != JSON_T_STRING)
+
+
+int
+JSON_parser_char(JSON_parser jc, int next_char)
+{
+/*
+ After calling new_JSON_parser, call this function for each character (or
+ partial character) in your JSON text. It can accept UTF-8, UTF-16, or
+ UTF-32. It returns true if things are looking ok so far. If it rejects the
+ text, it returns false.
+*/
+ int next_class, next_state;
+
+/*
+ Store the current char for error handling
+*/
+ jc->current_char = next_char;
+
+/*
+ Determine the character's class.
+*/
+ if (next_char < 0) {
+ jc->error = JSON_E_INVALID_CHAR;
+ return false;
+ }
+ if (next_char >= 128) {
+ next_class = C_ETC;
+ } else {
+ next_class = ascii_class[next_char];
+ if (next_class <= XX) {
+ set_error(jc);
+ return false;
+ }
+ }
+
+ if (!add_char_to_parse_buffer(jc, next_char, next_class)) {
+ return false;
+ }
+
+/*
+ Get the next state from the state transition table.
+*/
+ next_state = state_transition_table[jc->state][next_class];
+ if (next_state >= 0) {
+/*
+ Change the state.
+*/
+ jc->state = (signed char)next_state;
+ } else {
+/*
+ Or perform one of the actions.
+*/
+ switch (next_state) {
+/* Unicode character */
+ case UC:
+ if(!decode_unicode_char(jc)) {
+ jc->error = JSON_E_INVALID_UNICODE_SEQUENCE;
+ return false;
+ }
+ /* check if we need to read a second UTF-16 char */
+ if (jc->utf16_high_surrogate) {
+ jc->state = D1;
+ } else {
+ jc->state = ST;
+ }
+ break;
+/* escaped char */
+ case EX:
+ jc->escaped = 1;
+ jc->state = ESC;
+ break;
+/* integer detected by minus */
+ case MX:
+ jc->type = JSON_T_INTEGER;
+ jc->state = MI;
+ break;
+/* integer detected by zero */
+ case ZX:
+ jc->type = JSON_T_INTEGER;
+ jc->state = ZE;
+ break;
+/* integer detected by 1-9 */
+ case IX:
+ jc->type = JSON_T_INTEGER;
+ jc->state = IT;
+ break;
+
+/* floating point number detected by exponent*/
+ case DE:
+ assert_type_isnt_string_null_or_bool(jc);
+ jc->type = JSON_T_FLOAT;
+ jc->state = E1;
+ break;
+
+/* floating point number detected by fraction */
+ case DF:
+ assert_type_isnt_string_null_or_bool(jc);
+ if (!jc->handle_floats_manually) {
+/*
+ Some versions of strtod (which underlies sscanf) don't support converting
+ C-locale formated floating point values.
+*/
+ assert(jc->parse_buffer[jc->parse_buffer_count-1] == '.');
+ jc->parse_buffer[jc->parse_buffer_count-1] = jc->decimal_point;
+ }
+ jc->type = JSON_T_FLOAT;
+ jc->state = FX;
+ break;
+/* string begin " */
+ case SB:
+ parse_buffer_clear(jc);
+ assert(jc->type == JSON_T_NONE);
+ jc->type = JSON_T_STRING;
+ jc->state = ST;
+ break;
+
+/* n */
+ case NU:
+ assert(jc->type == JSON_T_NONE);
+ jc->type = JSON_T_NULL;
+ jc->state = N1;
+ break;
+/* f */
+ case FA:
+ assert(jc->type == JSON_T_NONE);
+ jc->type = JSON_T_FALSE;
+ jc->state = F1;
+ break;
+/* t */
+ case TR:
+ assert(jc->type == JSON_T_NONE);
+ jc->type = JSON_T_TRUE;
+ jc->state = T1;
+ break;
+
+/* closing comment */
+ case CE:
+ jc->comment = 0;
+ assert(jc->parse_buffer_count == 0);
+ assert(jc->type == JSON_T_NONE);
+ jc->state = jc->before_comment_state;
+ break;
+
+/* opening comment */
+ case CB:
+ if (!jc->allow_comments) {
+ return false;
+ }
+ parse_buffer_pop_back_char(jc);
+ if (!parse_parse_buffer(jc)) {
+ return false;
+ }
+ assert(jc->parse_buffer_count == 0);
+ assert(jc->type != JSON_T_STRING);
+ switch (jc->stack[jc->top]) {
+ case MODE_ARRAY:
+ case MODE_OBJECT:
+ switch(jc->state) {
+ case VA:
+ case AR:
+ jc->before_comment_state = jc->state;
+ break;
+ default:
+ jc->before_comment_state = OK;
+ break;
+ }
+ break;
+ default:
+ jc->before_comment_state = jc->state;
+ break;
+ }
+ jc->type = JSON_T_NONE;
+ jc->state = C1;
+ jc->comment = 1;
+ break;
+/* empty } */
+ case -9:
+ parse_buffer_clear(jc);
+ if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) {
+ return false;
+ }
+ if (!pop(jc, MODE_KEY)) {
+ return false;
+ }
+ jc->state = OK;
+ break;
+
+/* } */ case -8:
+ parse_buffer_pop_back_char(jc);
+ if (!parse_parse_buffer(jc)) {
+ return false;
+ }
+ if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) {
+ return false;
+ }
+ if (!pop(jc, MODE_OBJECT)) {
+ jc->error = JSON_E_UNBALANCED_COLLECTION;
+ return false;
+ }
+ jc->type = JSON_T_NONE;
+ jc->state = OK;
+ break;
+
+/* ] */ case -7:
+ parse_buffer_pop_back_char(jc);
+ if (!parse_parse_buffer(jc)) {
+ return false;
+ }
+ if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_END, NULL)) {
+ return false;
+ }
+ if (!pop(jc, MODE_ARRAY)) {
+ jc->error = JSON_E_UNBALANCED_COLLECTION;
+ return false;
+ }
+
+ jc->type = JSON_T_NONE;
+ jc->state = OK;
+ break;
+
+/* { */ case -6:
+ parse_buffer_pop_back_char(jc);
+ if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_BEGIN, NULL)) {
+ return false;
+ }
+ if (!push(jc, MODE_KEY)) {
+ return false;
+ }
+ assert(jc->type == JSON_T_NONE);
+ jc->state = OB;
+ break;
+
+/* [ */ case -5:
+ parse_buffer_pop_back_char(jc);
+ if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_BEGIN, NULL)) {
+ return false;
+ }
+ if (!push(jc, MODE_ARRAY)) {
+ return false;
+ }
+ assert(jc->type == JSON_T_NONE);
+ jc->state = AR;
+ break;
+
+/* string end " */ case -4:
+ parse_buffer_pop_back_char(jc);
+ switch (jc->stack[jc->top]) {
+ case MODE_KEY:
+ assert(jc->type == JSON_T_STRING);
+ jc->type = JSON_T_NONE;
+ jc->state = CO;
+
+ if (jc->callback) {
+ JSON_value value;
+ value.vu.str.value = jc->parse_buffer;
+ value.vu.str.length = jc->parse_buffer_count;
+ if (!(*jc->callback)(jc->ctx, JSON_T_KEY, &value)) {
+ return false;
+ }
+ }
+ parse_buffer_clear(jc);
+ break;
+ case MODE_ARRAY:
+ case MODE_OBJECT:
+ assert(jc->type == JSON_T_STRING);
+ if (!parse_parse_buffer(jc)) {
+ return false;
+ }
+ jc->type = JSON_T_NONE;
+ jc->state = OK;
+ break;
+ default:
+ return false;
+ }
+ break;
+
+/* , */ case -3:
+ parse_buffer_pop_back_char(jc);
+ if (!parse_parse_buffer(jc)) {
+ return false;
+ }
+ switch (jc->stack[jc->top]) {
+ case MODE_OBJECT:
+/*
+ A comma causes a flip from object mode to key mode.
+*/
+ if (!pop(jc, MODE_OBJECT) || !push(jc, MODE_KEY)) {
+ return false;
+ }
+ assert(jc->type != JSON_T_STRING);
+ jc->type = JSON_T_NONE;
+ jc->state = KE;
+ break;
+ case MODE_ARRAY:
+ assert(jc->type != JSON_T_STRING);
+ jc->type = JSON_T_NONE;
+ jc->state = VA;
+ break;
+ default:
+ return false;
+ }
+ break;
+
+/* : */ case -2:
+/*
+ A colon causes a flip from key mode to object mode.
+*/
+ parse_buffer_pop_back_char(jc);
+ if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) {
+ return false;
+ }
+ assert(jc->type == JSON_T_NONE);
+ jc->state = VA;
+ break;
+/*
+ Bad action.
+*/
+ default:
+ set_error(jc);
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+JSON_parser_done(JSON_parser jc)
+{
+ if ((jc->state == OK || jc->state == GO) && pop(jc, MODE_DONE))
+ {
+ return true;
+ }
+
+ jc->error = JSON_E_UNBALANCED_COLLECTION;
+ return false;
+}
+
+
+int JSON_parser_is_legal_white_space_string(const char* s)
+{
+ int c, char_class;
+
+ if (s == NULL) {
+ return false;
+ }
+
+ for (; *s; ++s) {
+ c = *s;
+
+ if (c < 0 || c >= 128) {
+ return false;
+ }
+
+ char_class = ascii_class[c];
+
+ if (char_class != C_SPACE && char_class != C_WHITE) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int JSON_parser_get_last_error(JSON_parser jc)
+{
+ return jc->error;
+}
+
+
+void init_JSON_config(JSON_config* config)
+{
+ if (config) {
+ memset(config, 0, sizeof(*config));
+
+ config->depth = JSON_PARSER_STACK_SIZE - 1;
+ config->malloc = malloc;
+ config->free = free;
+ }
+}
+
+#undef XX
+#undef COUNTOF
+#undef parse_buffer_clear
+#undef parse_buffer_pop_back_char
+/* end file parser/JSON_parser.c */
+/* begin file ./cson.c */
+#include <assert.h>
+#include <stdlib.h> /* malloc()/free() */
+#include <string.h>
+#include <errno.h>
+
+#ifdef _MSC_VER
+# if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
+# pragma warning( push )
+# pragma warning(disable:4996) /* unsecure sscanf (but snscanf() isn't in c89) */
+# pragma warning(disable:4244) /* complaining about data loss due
+ to integer precision in the
+ sqlite3 utf decoding routines */
+# endif
+#endif
+
+#if 1
+#include <stdio.h>
+#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
+#else
+static void noop_printf(char const * fmt, ...) {}
+#define MARKER if(0) printf
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/**
+ This type holds the "vtbl" for type-specific operations when
+ working with cson_value objects.
+
+ All cson_values of a given logical type share a pointer to a single
+ library-internal instance of this class.
+*/
+struct cson_value_api
+{
+ /**
+ The logical JavaScript/JSON type associated with
+ this object.
+ */
+ const cson_type_id typeID;
+ /**
+ Must free any memory associated with self,
+ but not free self. If self is NULL then
+ this function must do nothing.
+ */
+ void (*cleanup)( cson_value * self );
+ /**
+ POSSIBLE TODOs:
+
+ // Deep copy.
+ int (*clone)( cson_value const * self, cson_value ** tgt );
+
+ // Using JS semantics for true/value
+ char (*bool_value)( cson_value const * self );
+
+ // memcmp() return value semantics
+ int (*compare)( cson_value const * self, cson_value const * other );
+ */
+};
+
+typedef struct cson_value_api cson_value_api;
+
+/**
+ Empty-initialized cson_value_api object.
+*/
+#define cson_value_api_empty_m { \
+ CSON_TYPE_UNDEF/*typeID*/, \
+ NULL/*cleanup*/\
+ }
+/**
+ Empty-initialized cson_value_api object.
+*/
+/*static const cson_value_api cson_value_api_empty = cson_value_api_empty_m;*/
+
+
+typedef unsigned int cson_counter_t;
+struct cson_value
+{
+ /** The "vtbl" of type-specific operations. All instances
+ of a given logical value type share a single api instance.
+
+ Results are undefined if this value is NULL.
+ */
+ cson_value_api const * api;
+
+ /** The raw value. Its interpretation depends on the value of the
+ api member. Some value types require dynamically-allocated
+ memory, so one must always call cson_value_free() to destroy a
+ value when it is no longer needed. For stack-allocated values
+ (which client could SHOULD NOT USE unless they are intimately
+ familiar with the memory management rules and don't mind an
+ occasional leak or crash), use cson_value_clean() instead of
+ cson_value_free().
+ */
+ void * value;
+
+ /**
+ We use this to allow us to store cson_value instances in
+ multiple containers or multiple times within a single container
+ (provided no cycles are introduced).
+
+ Notes about the rc implementation:
+
+ - The refcount is for the cson_value instance itself, not its
+ value pointer.
+
+ - Instances start out with a refcount of 0 (not 1). Adding them
+ to a container will increase the refcount. Cleaning up the container
+ will decrement the count.
+
+ - cson_value_free() decrements the refcount (if it is not already
+ 0) and cleans/frees the value only when the refcount is 0.
+
+ - Some places in the internals add an "extra" reference to
+ objects to avoid a premature deletion. Don't try this at home.
+ */
+ cson_counter_t refcount;
+};
+
+
+/**
+ Empty-initialized cson_value object.
+*/
+const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m;
+const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m;
+const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m;
+const cson_buffer cson_buffer_empty = cson_buffer_empty_m;
+const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m;
+
+static void cson_value_destroy_zero_it( cson_value * self );
+static void cson_value_destroy_object( cson_value * self );
+/**
+ If self is-a array then this function destroys its contents,
+ else this function does nothing.
+*/
+static void cson_value_destroy_array( cson_value * self );
+
+static const cson_value_api cson_value_api_null = { CSON_TYPE_NULL, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_undef = { CSON_TYPE_UNDEF, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_bool = { CSON_TYPE_BOOL, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_integer = { CSON_TYPE_INTEGER, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_double = { CSON_TYPE_DOUBLE, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_string = { CSON_TYPE_STRING, cson_value_destroy_zero_it };
+static const cson_value_api cson_value_api_array = { CSON_TYPE_ARRAY, cson_value_destroy_array };
+static const cson_value_api cson_value_api_object = { CSON_TYPE_OBJECT, cson_value_destroy_object };
+
+static const cson_value cson_value_undef = { &cson_value_api_undef, NULL, 0 };
+static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NULL, 0 };
+static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 };
+static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 };
+static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 };
+static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 };
+
+/**
+ Strings are allocated as an instances of this class with N+1
+ trailing bytes, where N is the length of the string being
+ allocated. To convert a cson_string to c-string we simply increment
+ the cson_string pointer. To do the opposite we use (cstr -
+ sizeof(cson_string)). Zero-length strings are a special case
+ handled by a couple of the cson_string functions.
+*/
+struct cson_string
+{
+ unsigned int length;
+};
+#define cson_string_empty_m {0/*length*/}
+static const cson_string cson_string_empty = cson_string_empty_m;
+
+
+/**
+ Assumes V is a (cson_value*) ans V->value is a (T*). Returns
+ V->value cast to a (T*).
+*/
+#define CSON_CAST(T,V) ((T*)((V)->value))
+/**
+ Assumes V is a pointer to memory which is allocated as part of a
+ cson_value instance (the bytes immediately after that part).
+ Returns a pointer a a cson_value by subtracting sizeof(cson_value)
+ from that address and casting it to a (cson_value*)
+*/
+#define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
+
+/**
+ CSON_INT(V) assumes that V is a (cson_value*) of type
+ CSON_TYPE_INTEGER. This macro returns a (cson_int_t*) representing
+ its value (how that is stored depends on whether we are running in
+ 32- or 64-bit mode).
+ */
+#if CSON_VOID_PTR_IS_BIG
+# define CSON_INT(V) ((cson_int_t*)(&((V)->value)))
+#else
+# define CSON_INT(V) ((cson_int_t*)(V)->value)
+#endif
+
+#define CSON_DBL(V) CSON_CAST(cson_double_t,(V))
+#define CSON_STR(V) CSON_CAST(cson_string,(V))
+#define CSON_OBJ(V) CSON_CAST(cson_object,(V))
+#define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
+
+/**
+ Holds special shared "constant" (though they are non-const)
+ values.
+*/
+static struct CSON_EMPTY_HOLDER_
+{
+ char trueValue;
+ cson_string stringValue;
+} CSON_EMPTY_HOLDER = {
+ 1/*trueValue*/,
+ cson_string_empty_m
+};
+
+/**
+ Indexes into the CSON_SPECIAL_VALUES array.
+
+ If this enum changes in any way,
+ makes damned sure that CSON_SPECIAL_VALUES is updated
+ to match!!!
+*/
+enum CSON_INTERNAL_VALUES {
+
+ CSON_VAL_UNDEF = 0,
+ CSON_VAL_NULL = 1,
+ CSON_VAL_TRUE = 2,
+ CSON_VAL_FALSE = 3,
+ CSON_VAL_INT_0 = 4,
+ CSON_VAL_DBL_0 = 5,
+ CSON_VAL_STR_EMPTY = 6,
+ CSON_INTERNAL_VALUES_LENGTH
+};
+
+/**
+ Some "special" shared cson_value instances.
+
+ These values MUST be initialized in the order specified
+ by the CSON_INTERNAL_VALUES enum.
+
+ Note that they are not const because they are used as
+ shared-allocation objects in non-const contexts. However, the
+ public API provides no way to modifying them, and clients who
+ modify values directly are subject to The Wrath of Undefined
+ Behaviour.
+*/
+static cson_value CSON_SPECIAL_VALUES[] = {
+{ &cson_value_api_undef, NULL, 0 }, /* UNDEF */
+{ &cson_value_api_null, NULL, 0 }, /* NULL */
+{ &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */
+{ &cson_value_api_bool, NULL, 0 }, /* FALSE */
+{ &cson_value_api_integer, NULL, 0 }, /* INT_0 */
+{ &cson_value_api_double, NULL, 0 }, /* DBL_0 */
+{ &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */
+{ NULL, NULL, 0 }
+};
+
+
+/**
+ Returns non-0 (true) if m is one of our special
+ "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
+ "empty" values.
+
+ If this returns true, m MUST NOT be free()d!
+ */
+static char cson_value_is_builtin( void const * m )
+{
+ if((m >= (void const *)&CSON_EMPTY_HOLDER)
+ && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
+ return 1;
+ else return
+ ((m >= (void const *)&CSON_SPECIAL_VALUES[0])
+ && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
+ ? 1
+ : 0;
+}
+
+char const * cson_rc_string(int rc)
+{
+ switch(rc){
+#define CHECK(N) case CSON_RC_##N: return #N;
+ CHECK(OK);
+ CHECK(ArgError);
+ CHECK(RangeError);
+ CHECK(TypeError);
+ CHECK(IOError);
+ CHECK(AllocError);
+ CHECK(NYIError);
+ CHECK(InternalError);
+ CHECK(UnsupportedError);
+ CHECK(NotFoundError);
+ CHECK(UnknownError);
+ CHECK(Parse_INVALID_CHAR);
+ CHECK(Parse_INVALID_KEYWORD);
+ CHECK(Parse_INVALID_ESCAPE_SEQUENCE);
+ CHECK(Parse_INVALID_UNICODE_SEQUENCE);
+ CHECK(Parse_INVALID_NUMBER);
+ CHECK(Parse_NESTING_DEPTH_REACHED);
+ CHECK(Parse_UNBALANCED_COLLECTION);
+ CHECK(Parse_EXPECTED_KEY);
+ CHECK(Parse_EXPECTED_COLON);
+ default: return "UnknownError";
+ }
+#undef CHECK
+}
+
+/**
+ If CSON_LOG_ALLOC is true then the cson_malloc/realloc/free() routines
+ will log a message to stderr.
+*/
+#define CSON_LOG_ALLOC 0
+
+
+/**
+ CSON_FOSSIL_MODE is only for use in the Fossil
+ source tree, so that we can plug in to its allocators.
+ We can't do this by, e.g., defining macros for the
+ malloc/free funcs because fossil's lack of header files
+ means we would have to #include "main.c" here to
+ get the declarations.
+ */
+#if defined(CSON_FOSSIL_MODE)
+extern void *fossil_malloc(size_t n);
+extern void fossil_free(void *p);
+extern void *fossil_realloc(void *p, size_t n);
+# define CSON_MALLOC_IMPL fossil_malloc
+# define CSON_FREE_IMPL fossil_free
+# define CSON_REALLOC_IMPL fossil_realloc
+#endif
+
+#if !defined CSON_MALLOC_IMPL
+# define CSON_MALLOC_IMPL malloc
+#endif
+#if !defined CSON_FREE_IMPL
+# define CSON_FREE_IMPL free
+#endif
+#if !defined CSON_REALLOC_IMPL
+# define CSON_REALLOC_IMPL realloc
+#endif
+
+/**
+ A test/debug macro for simulating an OOM after the given number of
+ bytes have been allocated.
+*/
+#define CSON_SIMULATE_OOM 0
+#if CSON_SIMULATE_OOM
+static unsigned int cson_totalAlloced = 0;
+#endif
+
+/** Simple proxy for malloc(). descr is a description of the allocation. */
+static void * cson_malloc( size_t n, char const * descr )
+{
+#if CSON_LOG_ALLOC
+ fprintf(stderr, "Allocating %u bytes [%s].\n", (unsigned int)n, descr);
+#endif
+#if CSON_SIMULATE_OOM
+ cson_totalAlloced += n;
+ if( cson_totalAlloced > CSON_SIMULATE_OOM )
+ {
+ return NULL;
+ }
+#endif
+ return CSON_MALLOC_IMPL(n);
+}
+
+/** Simple proxy for free(). descr is a description of the memory being freed. */
+static void cson_free( void * p, char const * descr )
+{
+#if CSON_LOG_ALLOC
+ fprintf(stderr, "Freeing @%p [%s].\n", p, descr);
+#endif
+ if( !cson_value_is_builtin(p) )
+ {
+ CSON_FREE_IMPL( p );
+ }
+}
+/** Simple proxy for realloc(). descr is a description of the (re)allocation. */
+static void * cson_realloc( void * hint, size_t n, char const * descr )
+{
+#if CSON_LOG_ALLOC
+ fprintf(stderr, "%sllocating %u bytes [%s].\n",
+ hint ? "Rea" : "A",
+ (unsigned int)n, descr);
+#endif
+#if CSON_SIMULATE_OOM
+ cson_totalAlloced += n;
+ if( cson_totalAlloced > CSON_SIMULATE_OOM )
+ {
+ return NULL;
+ }
+#endif
+ if( 0==n )
+ {
+ cson_free(hint, descr);
+ return NULL;
+ }
+ else
+ {
+ return CSON_REALLOC_IMPL( hint, n );
+ }
+}
+
+
+#undef CSON_LOG_ALLOC
+#undef CSON_SIMULATE_OOM
+
+
+
+/**
+ CLIENTS CODE SHOULD NEVER USE THIS because it opens up doors to
+ memory leaks if it is not used in very controlled circumstances.
+ Users must be very aware of how the underlying memory management
+ works.
+
+ Frees any resources owned by val, but does not free val itself
+ (which may be stack-allocated). If !val or val->api or
+ val->api->cleanup are NULL then this is a no-op.
+
+ If v is a container type (object or array) its children are also
+ cleaned up, recursively.
+
+ After calling this, val will have the special "undefined" type.
+*/
+static void cson_value_clean( cson_value * val );
+
+/**
+ Increments cv's reference count by 1. As a special case, values
+ for which cson_value_is_builtin() returns true are not
+ modified. assert()s if (NULL==cv).
+*/
+static void cson_refcount_incr( cson_value * cv )
+{
+ assert( NULL != cv );
+ if( cson_value_is_builtin( cv ) )
+ { /* do nothing: we do not want to modify the shared
+ instances.
+ */
+ return;
+ }
+ else
+ {
+ ++cv->refcount;
+ }
+}
+
+#if 0
+int cson_value_refcount_set( cson_value * cv, unsigned short rc )
+{
+ if( NULL == cv ) return CSON_RC_ArgError;
+ else
+ {
+ cv->refcount = rc;
+ return 0;
+ }
+}
+#endif
+
+int cson_value_add_reference( cson_value * cv )
+{
+ if( NULL == cv ) return CSON_RC_ArgError;
+ else if( (cv->refcount+1) < cv->refcount )
+ {
+ return CSON_RC_RangeError;
+ }
+ else
+ {
+ cson_refcount_incr( cv );
+ return 0;
+ }
+}
+
+/**
+ If cv is NULL or cson_value_is_builtin(cv) returns true then this
+ function does nothing and returns 0, otherwise... If
+ cv->refcount is 0 or 1 then cson_value_clean(cv) is called, cv is
+ freed, and 0 is returned. If cv->refcount is any other value then
+ it is decremented and the new value is returned.
+*/
+static cson_counter_t cson_refcount_decr( cson_value * cv )
+{
+ if( (NULL == cv) || cson_value_is_builtin(cv) ) return 0;
+ else if( (0 == cv->refcount) || (0 == --cv->refcount) )
+ {
+ cson_value_clean(cv);
+ cson_free(cv,"cson_value::refcount=0");
+ return 0;
+ }
+ else return cv->refcount;
+}
+
+unsigned int cson_string_length_bytes( cson_string const * str )
+{
+ return str ? str->length : 0;
+}
+
+
+/**
+ Fetches v's string value as a non-const string.
+
+ cson_strings are intended to be immutable, but this form provides
+ access to the immutable bits, which are v->length bytes long. A
+ length-0 string is returned as NULL from here, as opposed to
+ "". (This is a side-effect of the string allocation mechanism.)
+ Returns NULL if !v or if v is the internal empty-string singleton.
+*/
+static char * cson_string_str(cson_string *v)
+{
+ /*
+ See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a
+ */
+#if 1
+ if( !v || (&CSON_EMPTY_HOLDER.stringValue == v) ) return NULL;
+ else return (char *)((unsigned char *)( v+1 ));
+#else
+ static char empty[2] = {0,0};
+ return ( NULL == v )
+ ? NULL
+ : (v->length
+ ? (char *) (((unsigned char *)v) + sizeof(cson_string))
+ : empty)
+ ;
+#endif
+}
+
+/**
+ Fetches v's string value as a const string.
+*/
+char const * cson_string_cstr(cson_string const *v)
+{
+ /*
+ See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a
+ */
+#if 1
+ if( ! v ) return NULL;
+ else if( v == &CSON_EMPTY_HOLDER.stringValue ) return "";
+ else {
+ assert((0 < v->length) && "How do we have a non-singleton empty string?");
+ return (char const *)((unsigned char const *)(v+1));
+ }
+#else
+ return (NULL == v)
+ ? NULL
+ : (v->length
+ ? (char const *) ((unsigned char const *)(v+1))
+ : "");
+#endif
+}
+
+
+#if 0
+/**
+ Just like strndup(3), in that neither are C89/C99-standard and both
+ are documented in detail in strndup(3).
+*/
+static char * cson_strdup( char const * src, size_t n )
+{
+ char * rc = (char *)cson_malloc(n+1, "cson_strdup");
+ if( ! rc ) return NULL;
+ memset( rc, 0, n+1 );
+ rc[n] = 0;
+ return strncpy( rc, src, n );
+}
+#endif
+
+int cson_string_cmp_cstr_n( cson_string const * str, char const * other, unsigned int otherLen )
+{
+ if( ! other && !str ) return 0;
+ else if( other && !str ) return 1;
+ else if( str && !other ) return -1;
+ else if( !otherLen ) return str->length ? 1 : 0;
+ else if( !str->length ) return otherLen ? -1 : 0;
+ else
+ {
+ unsigned const int max = (otherLen > str->length) ? otherLen : str->length;
+ int const rc = strncmp( cson_string_cstr(str), other, max );
+ return ( (0 == rc) && (otherLen != str->length) )
+ ? (str->length < otherLen) ? -1 : 1
+ : rc;
+ }
+}
+
+int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs )
+{
+ return cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs) ? strlen(rhs) : 0 );
+}
+int cson_string_cmp( cson_string const * lhs, cson_string const * rhs )
+{
+ return cson_string_cmp_cstr_n( lhs, cson_string_cstr(rhs), rhs ? rhs->length : 0 );
+}
+
+
+/**
+ If self is not NULL, *self is overwritten to have the undefined
+ type. self is not cleaned up or freed.
+*/
+void cson_value_destroy_zero_it( cson_value * self )
+{
+ if( self )
+ {
+ *self = cson_value_undef;
+ }
+}
+
+/**
+ A key/value pair collection.
+
+ Each of these objects owns its key/value pointers, and they
+ are cleaned up by cson_kvp_clean().
+*/
+struct cson_kvp
+{
+ cson_value * key;
+ cson_value * value;
+};
+#define cson_kvp_empty_m {NULL,NULL}
+static const cson_kvp cson_kvp_empty = cson_kvp_empty_m;
+
+/** @def CSON_OBJECT_PROPS_SORT
+
+ Don't use this - it has not been updated to account for internal
+ changes in cson_object.
+
+ If CSON_OBJECT_PROPS_SORT is set to a true value then
+ qsort() and bsearch() are used to sort (upon insertion)
+ and search cson_object::kvp property lists. This costs us
+ a re-sort on each insertion but searching is O(log n)
+ average/worst case (and O(1) best-case).
+
+ i'm not yet convinced that the overhead of the qsort() justifies
+ the potentially decreased search times - it has not been
+ measured. Object property lists tend to be relatively short in
+ JSON, and a linear search which uses the cson_string::length
+ property as a quick check is quite fast when one compares it with
+ the sort overhead required by the bsearch() approach.
+*/
+#define CSON_OBJECT_PROPS_SORT 0
+
+/** @def CSON_OBJECT_PROPS_SORT_USE_LENGTH
+
+ Don't use this - i'm not sure that it works how i'd like.
+
+ If CSON_OBJECT_PROPS_SORT_USE_LENGTH is true then
+ we use string lengths as quick checks when sorting
+ property keys. This leads to a non-intuitive sorting
+ order but "should" be faster.
+
+ This is ignored if CSON_OBJECT_PROPS_SORT is false.
+
+*/
+#define CSON_OBJECT_PROPS_SORT_USE_LENGTH 0
+
+#if CSON_OBJECT_PROPS_SORT
+
+/**
+ cson_kvp comparator for use with qsort(). ALMOST compares with
+ strcmp() semantics, but it uses the strings' lengths as a quicker
+ approach. This might give non-intuitive results, but it's faster.
+ */
+static int cson_kvp_cmp( void const * lhs, void const * rhs )
+{
+ cson_kvp const * lk = *((cson_kvp const * const*)lhs);
+ cson_kvp const * rk = *((cson_kvp const * const*)rhs);
+ cson_string const * l = cson_string_value(lk->key);
+ cson_string const * r = cson_string_value(rk->key);
+#if CSON_OBJECT_PROPS_SORT_USE_LENGTH
+ if( l->length < r->length ) return -1;
+ else if( l->length > r->length ) return 1;
+ else return strcmp( cson_string_cstr( l ), cson_string_cstr( r ) );
+#else
+ return strcmp( cson_string_cstr( l ),
+ cson_string_cstr( r ) );
+#endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/
+}
+#endif /*CSON_OBJECT_PROPS_SORT*/
+
+
+#if CSON_OBJECT_PROPS_SORT
+#error "Need to rework this for cson_string-to-cson_value refactoring"
+/**
+ A bsearch() comparison function which requires that lhs be a (char
+ const *) and rhs be-a (cson_kvp const * const *). It compares lhs
+ to rhs->key's value, using strcmp() semantics.
+ */
+static int cson_kvp_cmp_vs_cstr( void const * lhs, void const * rhs )
+{
+ char const * lk = (char const *)lhs;
+ cson_kvp const * rk =
+ *((cson_kvp const * const*)rhs)
+ ;
+#if CSON_OBJECT_PROPS_SORT_USE_LENGTH
+ unsigned int llen = strlen(lk);
+ if( llen < rk->key->length ) return -1;
+ else if( llen > rk->key->length ) return 1;
+ else return strcmp( lk, cson_string_cstr( rk->key ) );
+#else
+ return strcmp( lk, cson_string_cstr( rk->key ) );
+#endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/
+}
+#endif /*CSON_OBJECT_PROPS_SORT*/
+
+
+struct cson_kvp_list
+{
+ cson_kvp ** list;
+ unsigned int count;
+ unsigned int alloced;
+};
+typedef struct cson_kvp_list cson_kvp_list;
+#define cson_kvp_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/}
+/*static const cson_kvp_list cson_kvp_list_empty = cson_kvp_list_empty_m;*/
+
+struct cson_object
+{
+ cson_kvp_list kvp;
+};
+/*typedef struct cson_object cson_object;*/
+#define cson_object_empty_m { cson_kvp_list_empty_m/*kvp*/ }
+static const cson_object cson_object_empty = cson_object_empty_m;
+
+struct cson_value_list
+{
+ cson_value ** list;
+ unsigned int count;
+ unsigned int alloced;
+};
+typedef struct cson_value_list cson_value_list;
+#define cson_value_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/}
+static const cson_value_list cson_value_list_empty = cson_value_list_empty_m;
+
+struct cson_array
+{
+ cson_value_list list;
+};
+/*typedef struct cson_array cson_array;*/
+#define cson_array_empty_m { cson_value_list_empty_m/*list*/ }
+static const cson_array cson_array_empty = cson_array_empty_m;
+
+
+struct cson_parser
+{
+ JSON_parser p;
+ cson_value * root;
+ cson_value * node;
+ cson_array stack;
+ cson_string * ckey;
+ int errNo;
+ unsigned int totalKeyCount;
+ unsigned int totalValueCount;
+};
+typedef struct cson_parser cson_parser;
+static const cson_parser cson_parser_empty = {
+NULL/*p*/,
+NULL/*root*/,
+NULL/*node*/,
+cson_array_empty_m/*stack*/,
+NULL/*ckey*/,
+0/*errNo*/,
+0/*totalKeyCount*/,
+0/*totalValueCount*/
+};
+
+#if 1
+/* The following funcs are declared in generated code (cson_lists.h),
+ but we need early access to their decls for the Amalgamation build.
+*/
+static unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n );
+static unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n );
+static int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp );
+static void cson_kvp_list_clean( cson_kvp_list * self,
+ void (*cleaner)(cson_kvp * obj) );
+#if 0
+static int cson_value_list_append( cson_value_list * self, cson_value * cp );
+static void cson_value_list_clean( cson_value_list * self, void (*cleaner)(cson_value * obj));
+static int cson_kvp_list_visit( cson_kvp_list * self,
+ int (*visitor)(cson_kvp * obj, void * visitorState ),
+ void * visitorState );
+static int cson_value_list_visit( cson_value_list * self,
+ int (*visitor)(cson_value * obj, void * visitorState ),
+ void * visitorState );
+#endif
+#endif
+
+#if 0
+# define LIST_T cson_value_list
+# define VALUE_T cson_value *
+# define VALUE_T_IS_PTR 1
+# define LIST_T cson_kvp_list
+# define VALUE_T cson_kvp *
+# define VALUE_T_IS_PTR 1
+#else
+#endif
+
+/**
+ Allocates a new value of the specified type. Ownership is
+ transfered to the caller, who must eventually free it by passing it
+ to cson_value_free() or transfering ownership to a container.
+
+ extra is only valid for type CSON_TYPE_STRING, and must be the length
+ of the string to allocate + 1 byte (for the NUL).
+
+ The returned value->api member will be set appropriately and
+ val->value will be set to point to the memory allocated to hold the
+ native value type. Use the internal CSON_CAST() family of macros to
+ convert the cson_values to their corresponding native
+ representation.
+
+ Returns NULL on allocation error.
+
+ @see cson_value_new_array()
+ @see cson_value_new_object()
+ @see cson_value_new_string()
+ @see cson_value_new_integer()
+ @see cson_value_new_double()
+ @see cson_value_new_bool()
+ @see cson_value_free()
+*/
+static cson_value * cson_value_new(cson_type_id t, size_t extra)
+{
+ static const size_t vsz = sizeof(cson_value);
+ const size_t sz = vsz + extra;
+ size_t tx = 0;
+ cson_value def = cson_value_undef;
+ cson_value * v = NULL;
+ char const * reason = "cson_value_new";
+ switch(t)
+ {
+ case CSON_TYPE_ARRAY:
+ assert( 0 == extra );
+ def = cson_value_array_empty;
+ tx = sizeof(cson_array);
+ reason = "cson_value:array";
+ break;
+ case CSON_TYPE_DOUBLE:
+ assert( 0 == extra );
+ def = cson_value_double_empty;
+ tx = sizeof(cson_double_t);
+ reason = "cson_value:double";
+ break;
+ case CSON_TYPE_INTEGER:
+ assert( 0 == extra );
+ def = cson_value_integer_empty;
+#if !CSON_VOID_PTR_IS_BIG
+ tx = sizeof(cson_int_t);
+#endif
+ reason = "cson_value:int";
+ break;
+ case CSON_TYPE_STRING:
+ assert( 0 != extra );
+ def = cson_value_string_empty;
+ tx = sizeof(cson_string);
+ reason = "cson_value:string";
+ break;
+ case CSON_TYPE_OBJECT:
+ assert( 0 == extra );
+ def = cson_value_object_empty;
+ tx = sizeof(cson_object);
+ reason = "cson_value:object";
+ break;
+ default:
+ assert(0 && "Unhandled type in cson_value_new()!");
+ return NULL;
+ }
+ assert( def.api->typeID != CSON_TYPE_UNDEF );
+ v = (cson_value *)cson_malloc(sz+tx, reason);
+ if( v ) {
+ *v = def;
+ if(tx || extra){
+ memset(v+1, 0, tx + extra);
+ v->value = (void *)(v+1);
+ }
+ }
+ return v;
+}
+
+void cson_value_free(cson_value *v)
+{
+ cson_refcount_decr( v );
+}
+
+#if 0 /* we might actually want this later on. */
+/** Returns true if v is not NULL and has the given type ID. */
+static char cson_value_is_a( cson_value const * v, cson_type_id is )
+{
+ return (v && v->api && (v->api->typeID == is)) ? 1 : 0;
+}
+#endif
+
+cson_type_id cson_value_type_id( cson_value const * v )
+{
+ return (v && v->api) ? v->api->typeID : CSON_TYPE_UNDEF;
+}
+
+char cson_value_is_undef( cson_value const * v )
+{
+ return ( !v || !v->api || (v->api==&cson_value_api_undef))
+ ? 1 : 0;
+}
+#define ISA(T,TID) char cson_value_is_##T( cson_value const * v ) { \
+ /*return (v && v->api) ? cson_value_is_a(v,CSON_TYPE_##TID) : 0;*/ \
+ return (v && (v->api == &cson_value_api_##T)) ? 1 : 0; \
+ } extern char bogusPlaceHolderForEmacsIndention##TID
+ISA(null,NULL);
+ISA(bool,BOOL);
+ISA(integer,INTEGER);
+ISA(double,DOUBLE);
+ISA(string,STRING);
+ISA(array,ARRAY);
+ISA(object,OBJECT);
+#undef ISA
+char cson_value_is_number( cson_value const * v )
+{
+ return cson_value_is_integer(v) || cson_value_is_double(v);
+}
+
+
+void cson_value_clean( cson_value * val )
+{
+ if( val && val->api && val->api->cleanup )
+ {
+ if( ! cson_value_is_builtin( val ) )
+ {
+ cson_counter_t const rc = val->refcount;
+ val->api->cleanup(val);
+ *val = cson_value_undef;
+ val->refcount = rc;
+ }
+ }
+}
+
+static cson_value * cson_value_array_alloc(void)
+{
+ cson_value * v = cson_value_new(CSON_TYPE_ARRAY,0);
+ if( NULL != v )
+ {
+ cson_array * ar = CSON_ARRAY(v);
+ assert(NULL != ar);
+ *ar = cson_array_empty;
+ }
+ return v;
+}
+
+static cson_value * cson_value_object_alloc(void)
+{
+ cson_value * v = cson_value_new(CSON_TYPE_OBJECT,0);
+ if( NULL != v )
+ {
+ cson_object * obj = CSON_OBJ(v);
+ assert(NULL != obj);
+ *obj = cson_object_empty;
+ }
+ return v;
+}
+
+cson_value * cson_value_new_object(void)
+{
+ return cson_value_object_alloc();
+}
+
+cson_object * cson_new_object(void)
+{
+
+ return cson_value_get_object( cson_value_new_object() );
+}
+
+cson_value * cson_value_new_array(void)
+{
+ return cson_value_array_alloc();
+}
+
+
+cson_array * cson_new_array(void)
+{
+ return cson_value_get_array( cson_value_new_array() );
+}
+
+/**
+ Frees kvp->key and kvp->value and sets them to NULL, but does not free
+ kvp. If !kvp then this is a no-op.
+*/
+static void cson_kvp_clean( cson_kvp * kvp )
+{
+ if( kvp )
+ {
+ if(kvp->key)
+ {
+ cson_value_free(kvp->key);
+ kvp->key = NULL;
+ }
+ if(kvp->value)
+ {
+ cson_value_free( kvp->value );
+ kvp->value = NULL;
+ }
+ }
+}
+
+cson_string * cson_kvp_key( cson_kvp const * kvp )
+{
+ return kvp ? cson_value_get_string(kvp->key) : NULL;
+}
+cson_value * cson_kvp_value( cson_kvp const * kvp )
+{
+ return kvp ? kvp->value : NULL;
+}
+
+
+/**
+ Calls cson_kvp_clean(kvp) and then frees kvp.
+*/
+static void cson_kvp_free( cson_kvp * kvp )
+{
+ if( kvp )
+ {
+ cson_kvp_clean(kvp);
+ cson_free(kvp,"cson_kvp");
+ }
+}
+
+
+/**
+ cson_value_api::destroy_value() impl for Object
+ values. Cleans up self-owned memory and overwrites
+ self to have the undefined value, but does not
+ free self.
+*/
+static void cson_value_destroy_object( cson_value * self )
+{
+ if(self && self->value) {
+ cson_object * obj = (cson_object *)self->value;
+ assert( self->value == obj );
+ cson_kvp_list_clean( &obj->kvp, cson_kvp_free );
+ *self = cson_value_undef;
+ }
+}
+
+/**
+ Cleans up the contents of ar->list, but does not free ar.
+
+ After calling this, ar will have a length of 0.
+
+ If properlyCleanValues is 1 then cson_value_free() is called on
+ each non-NULL item, otherwise the outer list is destroyed but the
+ individual items are assumed to be owned by someone else and are
+ not freed.
+*/
+static void cson_array_clean( cson_array * ar, char properlyCleanValues )
+{
+ if( ar )
+ {
+ unsigned int i = 0;
+ cson_value * val = NULL;
+ for( ; i < ar->list.count; ++i )
+ {
+ val = ar->list.list[i];
+ if(val)
+ {
+ ar->list.list[i] = NULL;
+ if( properlyCleanValues )
+ {
+ cson_value_free( val );
+ }
+ }
+ }
+ cson_value_list_reserve(&ar->list,0);
+ ar->list = cson_value_list_empty
+ /* Pedantic note: reserve(0) already clears the list-specific
+ fields, but we do this just in case we ever add new fields
+ to cson_value_list which are not used in the reserve() impl.
+ */
+ ;
+ }
+}
+
+/**
+ cson_value_api::destroy_value() impl for Array
+ values. Cleans up self-owned memory and overwrites
+ self to have the undefined value, but does not
+ free self.
+*/
+static void cson_value_destroy_array( cson_value * self )
+{
+ cson_array * ar = cson_value_get_array(self);
+ if(ar) {
+ assert( self->value == ar );
+ cson_array_clean( ar, 1 );
+ *self = cson_value_undef;
+ }
+}
+
+int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state )
+{
+ int rc;
+ enum { BufSize = 1024 * 4 };
+ char rbuf[BufSize];
+ size_t total = 0;
+ unsigned int rlen = 0;
+ if( ! dest || ! src ) return CSON_RC_ArgError;
+ dest->used = 0;
+ while(1)
+ {
+ rlen = BufSize;
+ rc = src( state, rbuf, &rlen );
+ if( rc ) break;
+ total += rlen;
+ if( dest->capacity < (total+1) )
+ {
+ rc = cson_buffer_reserve( dest, total + 1);
+ if( 0 != rc ) break;
+ }
+ memcpy( dest->mem + dest->used, rbuf, rlen );
+ dest->used += rlen;
+ if( rlen < BufSize ) break;
+ }
+ if( !rc && dest->used )
+ {
+ assert( dest->used < dest->capacity );
+ dest->mem[dest->used] = 0;
+ }
+ return rc;
+}
+
+int cson_data_source_FILE( void * state, void * dest, unsigned int * n )
+{
+ FILE * f = (FILE*) state;
+ if( ! state || ! n || !dest ) return CSON_RC_ArgError;
+ else if( !*n ) return CSON_RC_RangeError;
+ *n = (unsigned int)fread( dest, 1, *n, f );
+ if( !*n )
+ {
+ return feof(f) ? 0 : CSON_RC_IOError;
+ }
+ return 0;
+}
+
+int cson_parse_FILE( cson_value ** tgt, FILE * src,
+ cson_parse_opt const * opt, cson_parse_info * err )
+{
+ return cson_parse( tgt, cson_data_source_FILE, src, opt, err );
+}
+
+
+int cson_value_fetch_bool( cson_value const * val, char * v )
+{
+ /**
+ FIXME: move the to-bool operation into cson_value_api, like we
+ do in the C++ API.
+ */
+ if( ! val || !val->api ) return CSON_RC_ArgError;
+ else
+ {
+ int rc = 0;
+ char b = 0;
+ switch( val->api->typeID )
+ {
+ case CSON_TYPE_ARRAY:
+ case CSON_TYPE_OBJECT:
+ b = 1;
+ break;
+ case CSON_TYPE_STRING: {
+ char const * str = cson_string_cstr(cson_value_get_string(val));
+ b = (str && *str) ? 1 : 0;
+ break;
+ }
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL:
+ break;
+ case CSON_TYPE_BOOL:
+ b = (NULL==val->value) ? 0 : 1;
+ break;
+ case CSON_TYPE_INTEGER: {
+ cson_int_t i = 0;
+ cson_value_fetch_integer( val, &i );
+ b = i ? 1 : 0;
+ break;
+ }
+ case CSON_TYPE_DOUBLE: {
+ cson_double_t d = 0.0;
+ cson_value_fetch_double( val, &d );
+ b = (0.0==d) ? 0 : 1;
+ break;
+ }
+ default:
+ rc = CSON_RC_TypeError;
+ break;
+ }
+ if( v ) *v = b;
+ return rc;
+ }
+}
+
+char cson_value_get_bool( cson_value const * val )
+{
+ char i = 0;
+ cson_value_fetch_bool( val, &i );
+ return i;
+}
+
+int cson_value_fetch_integer( cson_value const * val, cson_int_t * v )
+{
+ if( ! val || !val->api ) return CSON_RC_ArgError;
+ else
+ {
+ cson_int_t i = 0;
+ int rc = 0;
+ switch(val->api->typeID)
+ {
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL:
+ i = 0;
+ break;
+ case CSON_TYPE_BOOL: {
+ char b = 0;
+ cson_value_fetch_bool( val, &b );
+ i = b;
+ break;
+ }
+ case CSON_TYPE_INTEGER: {
+ cson_int_t const * x = CSON_INT(val);
+ if(!x)
+ {
+ assert( val == &CSON_SPECIAL_VALUES[CSON_VAL_INT_0] );
+ }
+ i = x ? *x : 0;
+ break;
+ }
+ case CSON_TYPE_DOUBLE: {
+ cson_double_t d = 0.0;
+ cson_value_fetch_double( val, &d );
+ i = (cson_int_t)d;
+ break;
+ }
+ case CSON_TYPE_STRING:
+ case CSON_TYPE_ARRAY:
+ case CSON_TYPE_OBJECT:
+ default:
+ rc = CSON_RC_TypeError;
+ break;
+ }
+ if(!rc && v) *v = i;
+ return rc;
+ }
+}
+
+cson_int_t cson_value_get_integer( cson_value const * val )
+{
+ cson_int_t i = 0;
+ cson_value_fetch_integer( val, &i );
+ return i;
+}
+
+int cson_value_fetch_double( cson_value const * val, cson_double_t * v )
+{
+ if( ! val || !val->api ) return CSON_RC_ArgError;
+ else
+ {
+ cson_double_t d = 0.0;
+ int rc = 0;
+ switch(val->api->typeID)
+ {
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL:
+ d = 0;
+ break;
+ case CSON_TYPE_BOOL: {
+ char b = 0;
+ cson_value_fetch_bool( val, &b );
+ d = b ? 1.0 : 0.0;
+ break;
+ }
+ case CSON_TYPE_INTEGER: {
+ cson_int_t i = 0;
+ cson_value_fetch_integer( val, &i );
+ d = i;
+ break;
+ }
+ case CSON_TYPE_DOUBLE: {
+ cson_double_t const* dv = CSON_DBL(val);
+ d = dv ? *dv : 0.0;
+ break;
+ }
+ default:
+ rc = CSON_RC_TypeError;
+ break;
+ }
+ if(v) *v = d;
+ return rc;
+ }
+}
+
+cson_double_t cson_value_get_double( cson_value const * val )
+{
+ cson_double_t i = 0.0;
+ cson_value_fetch_double( val, &i );
+ return i;
+}
+
+int cson_value_fetch_string( cson_value const * val, cson_string ** dest )
+{
+ if( ! val || ! dest ) return CSON_RC_ArgError;
+ else if( ! cson_value_is_string(val) ) return CSON_RC_TypeError;
+ else
+ {
+ if( dest ) *dest = CSON_STR(val);
+ return 0;
+ }
+}
+
+cson_string * cson_value_get_string( cson_value const * val )
+{
+ cson_string * rc = NULL;
+ cson_value_fetch_string( val, &rc );
+ return rc;
+}
+
+char const * cson_value_get_cstr( cson_value const * val )
+{
+ return cson_string_cstr( cson_value_get_string(val) );
+}
+
+int cson_value_fetch_object( cson_value const * val, cson_object ** obj )
+{
+ if( ! val ) return CSON_RC_ArgError;
+ else if( ! cson_value_is_object(val) ) return CSON_RC_TypeError;
+ else
+ {
+ if(obj) *obj = CSON_OBJ(val);
+ return 0;
+ }
+}
+cson_object * cson_value_get_object( cson_value const * v )
+{
+ cson_object * obj = NULL;
+ cson_value_fetch_object( v, &obj );
+ return obj;
+}
+
+int cson_value_fetch_array( cson_value const * val, cson_array ** ar)
+{
+ if( ! val ) return CSON_RC_ArgError;
+ else if( !cson_value_is_array(val) ) return CSON_RC_TypeError;
+ else
+ {
+ if(ar) *ar = CSON_ARRAY(val);
+ return 0;
+ }
+}
+
+cson_array * cson_value_get_array( cson_value const * v )
+{
+ cson_array * ar = NULL;
+ cson_value_fetch_array( v, &ar );
+ return ar;
+}
+
+cson_kvp * cson_kvp_alloc(void)
+{
+ cson_kvp * kvp = (cson_kvp*)cson_malloc(sizeof(cson_kvp),"cson_kvp");
+ if( kvp )
+ {
+ *kvp = cson_kvp_empty;
+ }
+ return kvp;
+}
+
+
+
+int cson_array_append( cson_array * ar, cson_value * v )
+{
+ if( !ar || !v ) return CSON_RC_ArgError;
+ else if( (ar->list.count+1) < ar->list.count ) return CSON_RC_RangeError;
+ else
+ {
+ if( !ar->list.alloced || (ar->list.count == ar->list.alloced-1))
+ {
+ unsigned int const n = ar->list.count ? (ar->list.count*2) : 7;
+ if( n > cson_value_list_reserve( &ar->list, n ) )
+ {
+ return CSON_RC_AllocError;
+ }
+ }
+ return cson_array_set( ar, ar->list.count, v );
+ }
+}
+
+#if 0
+/**
+ Removes and returns the last value from the given array,
+ shrinking its size by 1. Returns NULL if ar is NULL,
+ ar->list.count is 0, or the element at that index is NULL.
+
+
+ If removeRef is true then cson_value_free() is called to remove
+ ar's reference count for the value. In that case NULL is returned,
+ even if the object still has live references. If removeRef is false
+ then the caller takes over ownership of that reference count point.
+
+ If removeRef is false then the caller takes over ownership
+ of the return value, otherwise ownership is effectively
+ determined by any remaining references for the returned
+ value.
+*/
+static cson_value * cson_array_pop_back( cson_array * ar,
+ char removeRef )
+{
+ if( !ar ) return NULL;
+ else if( ! ar->list.count ) return NULL;
+ else
+ {
+ unsigned int const ndx = --ar->list.count;
+ cson_value * v = ar->list.list[ndx];
+ ar->list.list[ndx] = NULL;
+ if( removeRef )
+ {
+ cson_value_free( v );
+ v = NULL;
+ }
+ return v;
+ }
+}
+#endif
+
+cson_value * cson_value_new_bool( char v )
+{
+ return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
+}
+
+cson_value * cson_value_true(void)
+{
+ return &CSON_SPECIAL_VALUES[CSON_VAL_TRUE];
+}
+cson_value * cson_value_false(void)
+{
+ return &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
+}
+
+cson_value * cson_value_null(void)
+{
+ return &CSON_SPECIAL_VALUES[CSON_VAL_NULL];
+}
+
+cson_value * cson_new_int( cson_int_t v )
+{
+ return cson_value_new_integer(v);
+}
+
+cson_value * cson_value_new_integer( cson_int_t v )
+{
+ if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0];
+ else
+ {
+ cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0);
+#if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
+ assert( sizeof(cson_int_t) <= sizeof(void *) );
+#endif
+ if( c )
+ {
+ memcpy( CSON_INT(c), &v, sizeof(v) );
+ }
+ return c;
+ }
+}
+
+cson_value * cson_new_double( cson_double_t v )
+{
+ return cson_value_new_double(v);
+}
+
+cson_value * cson_value_new_double( cson_double_t v )
+{
+ if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0];
+ else
+ {
+ cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
+ if( c )
+ {
+ memcpy( CSON_DBL(c), &v, sizeof(v) );
+ }
+ return c;
+ }
+}
+
+cson_string * cson_new_string(char const * str, unsigned int len)
+{
+ if( !str || !*str || !len ) return &CSON_EMPTY_HOLDER.stringValue;
+ else
+ {
+ cson_value * c = cson_value_new(CSON_TYPE_STRING, len + 1/*NUL byte*/);
+ cson_string * s = NULL;
+ if( c )
+ {
+ char * dest = NULL;
+ s = CSON_STR(c);
+ *s = cson_string_empty;
+ assert( NULL != s );
+ s->length = len;
+ dest = cson_string_str(s);
+ assert( NULL != dest );
+ memcpy( dest, str, len );
+ dest[len] = 0;
+ }
+ return s;
+ }
+}
+
+cson_value * cson_value_new_string( char const * str, unsigned int len )
+{
+ return cson_string_value( cson_new_string(str, len) );
+}
+
+int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v )
+{
+ if( !ar) return CSON_RC_ArgError;
+ if( pos >= ar->list.count ) return CSON_RC_RangeError;
+ else
+ {
+ if(v) *v = ar->list.list[pos];
+ return 0;
+ }
+}
+
+cson_value * cson_array_get( cson_array const * ar, unsigned int pos )
+{
+ cson_value *v = NULL;
+ cson_array_value_fetch(ar, pos, &v);
+ return v;
+}
+
+int cson_array_length_fetch( cson_array const * ar, unsigned int * v )
+{
+ if( ! ar || !v ) return CSON_RC_ArgError;
+ else
+ {
+ if(v) *v = ar->list.count;
+ return 0;
+ }
+}
+
+unsigned int cson_array_length_get( cson_array const * ar )
+{
+ unsigned int i = 0;
+ cson_array_length_fetch(ar, &i);
+ return i;
+}
+
+int cson_array_reserve( cson_array * ar, unsigned int size )
+{
+ if( ! ar ) return CSON_RC_ArgError;
+ else if( size <= ar->list.alloced )
+ {
+ /* We don't want to introduce a can of worms by trying to
+ handle the cleanup from here.
+ */
+ return 0;
+ }
+ else
+ {
+ return (ar->list.alloced > cson_value_list_reserve( &ar->list, size ))
+ ? CSON_RC_AllocError
+ : 0
+ ;
+ }
+}
+
+int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v )
+{
+ if( !ar || !v ) return CSON_RC_ArgError;
+ else if( (ndx+1) < ndx) /* overflow */return CSON_RC_RangeError;
+ else
+ {
+ unsigned const int len = cson_value_list_reserve( &ar->list, ndx+1 );
+ if( len <= ndx ) return CSON_RC_AllocError;
+ else
+ {
+ cson_value * old = ar->list.list[ndx];
+ if( old )
+ {
+ if(old == v) return 0;
+ else cson_value_free(old);
+ }
+ cson_refcount_incr( v );
+ ar->list.list[ndx] = v;
+ if( ndx >= ar->list.count )
+ {
+ ar->list.count = ndx+1;
+ }
+ return 0;
+ }
+ }
+}
+
+/** @internal
+
+ Searchs for the given key in the given object.
+
+ Returns the found item on success, NULL on error. If ndx is not
+ NULL, it is set to the index (in obj->kvp.list) of the found
+ item. *ndx is not modified if no entry is found.
+*/
+static cson_kvp * cson_object_search_impl( cson_object const * obj, char const * key, unsigned int * ndx )
+{
+ if( obj && key && *key && obj->kvp.count)
+ {
+#if CSON_OBJECT_PROPS_SORT
+ cson_kvp ** s = (cson_kvp**)
+ bsearch( key, obj->kvp.list,
+ obj->kvp.count, sizeof(cson_kvp*),
+ cson_kvp_cmp_vs_cstr );
+ if( ndx && s )
+ { /* index of found record is required by
+ cson_object_unset(). Calculate the offset based on s...*/
+#if 0
+ *ndx = (((unsigned char const *)s - ((unsigned char const *)obj->kvp.list))
+ / sizeof(cson_kvp*));
+#else
+ *ndx = s - obj->kvp.list;
+#endif
+ }
+ return s ? *s : NULL;
+#else
+ cson_kvp_list const * li = &obj->kvp;
+ unsigned int i = 0;
+ cson_kvp * kvp;
+ const unsigned int klen = strlen(key);
+ for( ; i < li->count; ++i )
+ {
+ cson_string const * sKey;
+ kvp = li->list[i];
+ assert( kvp && kvp->key );
+ sKey = cson_value_get_string(kvp->key);
+ assert(sKey);
+ if( sKey->length != klen ) continue;
+ else if(0==strcmp(key,cson_string_cstr(sKey)))
+ {
+ if(ndx) *ndx = i;
+ return kvp;
+ }
+ }
+#endif
+ }
+ return NULL;
+}
+
+cson_value * cson_object_get( cson_object const * obj, char const * key )
+{
+ cson_kvp * kvp = cson_object_search_impl( obj, key, NULL );
+ return kvp ? kvp->value : NULL;
+}
+
+cson_value * cson_object_get_s( cson_object const * obj, cson_string const *key )
+{
+ cson_kvp * kvp = cson_object_search_impl( obj, cson_string_cstr(key), NULL );
+ return kvp ? kvp->value : NULL;
+}
+
+
+#if CSON_OBJECT_PROPS_SORT
+static void cson_object_sort_props( cson_object * obj )
+{
+ assert( NULL != obj );
+ if( obj->kvp.count )
+ {
+ qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
+ cson_kvp_cmp );
+ }
+
+}
+#endif
+
+int cson_object_unset( cson_object * obj, char const * key )
+{
+ if( ! obj || !key || !*key ) return CSON_RC_ArgError;
+ else
+ {
+ unsigned int ndx = 0;
+ cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx );
+ if( ! kvp )
+ {
+ return CSON_RC_NotFoundError;
+ }
+ assert( obj->kvp.count > 0 );
+ assert( obj->kvp.list[ndx] == kvp );
+ cson_kvp_free( kvp );
+ obj->kvp.list[ndx] = NULL;
+ { /* if my brain were bigger i'd use memmove(). */
+ unsigned int i = ndx;
+ for( ; i < obj->kvp.count; ++i )
+ {
+ obj->kvp.list[i] =
+ (i < (obj->kvp.alloced-1))
+ ? obj->kvp.list[i+1]
+ : NULL;
+ }
+ }
+ obj->kvp.list[--obj->kvp.count] = NULL;
+#if CSON_OBJECT_PROPS_SORT
+ cson_object_sort_props( obj );
+#endif
+ return 0;
+ }
+}
+
+int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v )
+{
+ if( !obj || !key ) return CSON_RC_ArgError;
+ else if( NULL == v ) return cson_object_unset( obj, cson_string_cstr(key) );
+ else
+ {
+ char const * cKey;
+ cson_value * vKey;
+ cson_kvp * kvp;
+ vKey = cson_string_value(key);
+ assert(vKey && (key==CSON_STR(vKey)));
+ if( vKey == CSON_VCAST(obj) ){
+ return CSON_RC_ArgError;
+ }
+ cKey = cson_string_cstr(key);
+ kvp = cson_object_search_impl( obj, cKey, NULL );
+ if( kvp )
+ { /* "I told 'em we've already got one!" */
+ if( kvp->key != vKey ){
+ cson_value_free( kvp->key );
+ cson_refcount_incr(vKey);
+ kvp->key = vKey;
+ }
+ if(kvp->value != v){
+ cson_value_free( kvp->value );
+ cson_refcount_incr( v );
+ kvp->value = v;
+ }
+ return 0;
+ }
+ if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
+ { /* reserve space */
+ unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6;
+ if( n > cson_kvp_list_reserve( &obj->kvp, n ) )
+ {
+ return CSON_RC_AllocError;
+ }
+ }
+ { /* insert new item... */
+ int rc = 0;
+ kvp = cson_kvp_alloc();
+ if( ! kvp )
+ {
+ return CSON_RC_AllocError;
+ }
+ rc = cson_kvp_list_append( &obj->kvp, kvp );
+ if( 0 != rc )
+ {
+ cson_kvp_free(kvp);
+ }
+ else
+ {
+ cson_refcount_incr(vKey);
+ cson_refcount_incr(v);
+ kvp->key = vKey;
+ kvp->value = v;
+#if CSON_OBJECT_PROPS_SORT
+ cson_object_sort_props( obj );
+#endif
+ }
+ return rc;
+ }
+ }
+
+}
+int cson_object_set( cson_object * obj, char const * key, cson_value * v )
+{
+ if( ! obj || !key || !*key ) return CSON_RC_ArgError;
+ else if( NULL == v )
+ {
+ return cson_object_unset( obj, key );
+ }
+ else
+ {
+ cson_string * cs = cson_new_string(key,strlen(key));
+ if(!cs) return CSON_RC_AllocError;
+ else
+ {
+ int const rc = cson_object_set_s(obj, cs, v);
+ if(rc) cson_value_free(cson_string_value(cs));
+ return rc;
+ }
+ }
+}
+
+cson_value * cson_object_take( cson_object * obj, char const * key )
+{
+ if( ! obj || !key || !*key ) return NULL;
+ else
+ {
+ /* FIXME: this is 90% identical to cson_object_unset(),
+ only with different refcount handling.
+ Consolidate them.
+ */
+ unsigned int ndx = 0;
+ cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx );
+ cson_value * rc = NULL;
+ if( ! kvp )
+ {
+ return NULL;
+ }
+ assert( obj->kvp.count > 0 );
+ assert( obj->kvp.list[ndx] == kvp );
+ rc = kvp->value;
+ assert( rc );
+ kvp->value = NULL;
+ cson_kvp_free( kvp );
+ assert( rc->refcount > 0 );
+ --rc->refcount;
+ obj->kvp.list[ndx] = NULL;
+ { /* if my brain were bigger i'd use memmove(). */
+ unsigned int i = ndx;
+ for( ; i < obj->kvp.count; ++i )
+ {
+ obj->kvp.list[i] =
+ (i < (obj->kvp.alloced-1))
+ ? obj->kvp.list[i+1]
+ : NULL;
+ }
+ }
+ obj->kvp.list[--obj->kvp.count] = NULL;
+#if CSON_OBJECT_PROPS_SORT
+ cson_object_sort_props( obj );
+#endif
+ return rc;
+ }
+}
+/** @internal
+
+ If p->node is-a Object then value is inserted into the object
+ using p->key. In any other case CSON_RC_InternalError is returned.
+
+ Returns CSON_RC_AllocError if an allocation fails.
+
+ Returns 0 on success. On error, parsing must be ceased immediately.
+
+ Ownership of val is ALWAYS TRANSFERED to this function. If this
+ function fails, val will be cleaned up and destroyed. (This
+ simplifies error handling in the core parser.)
+*/
+static int cson_parser_set_key( cson_parser * p, cson_value * val )
+{
+ assert( p && val );
+
+ if( p->ckey && cson_value_is_object(p->node) )
+ {
+ int rc;
+ cson_object * obj = cson_value_get_object(p->node);
+ cson_kvp * kvp = NULL;
+ assert( obj && (p->node->value == obj) );
+ /**
+ FIXME? Use cson_object_set() instead of our custom
+ finagling with the object? We do it this way to avoid an
+ extra alloc/strcpy of the key data.
+ */
+ if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
+ {
+ if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) )
+ {
+ cson_value_free(val);
+ return CSON_RC_AllocError;
+ }
+ }
+ kvp = cson_kvp_alloc();
+ if( ! kvp )
+ {
+ cson_value_free(val);
+ return CSON_RC_AllocError;
+ }
+ kvp->key = cson_string_value(p->ckey)/*transfer ownership*/;
+ assert(0 == kvp->key->refcount);
+ cson_refcount_incr(kvp->key);
+ p->ckey = NULL;
+ kvp->value = val;
+ cson_refcount_incr( val );
+ rc = cson_kvp_list_append( &obj->kvp, kvp );
+ if( 0 != rc )
+ {
+ cson_kvp_free( kvp );
+ }
+ else
+ {
+ ++p->totalValueCount;
+ }
+ return rc;
+ }
+ else
+ {
+ if(val) cson_value_free(val);
+ return p->errNo = CSON_RC_InternalError;
+ }
+
+}
+
+/** @internal
+
+ Pushes val into the current object/array parent node, depending on the
+ internal state of the parser.
+
+ Ownership of val is always transfered to this function, regardless of
+ success or failure.
+
+ Returns 0 on success. On error, parsing must be ceased immediately.
+*/
+static int cson_parser_push_value( cson_parser * p, cson_value * val )
+{
+ if( p->ckey )
+ { /* we're in Object mode */
+ assert( cson_value_is_object( p->node ) );
+ return cson_parser_set_key( p, val );
+ }
+ else if( cson_value_is_array( p->node ) )
+ { /* we're in Array mode */
+ cson_array * ar = cson_value_get_array( p->node );
+ int rc;
+ assert( ar && (ar == p->node->value) );
+ rc = cson_array_append( ar, val );
+ if( 0 != rc )
+ {
+ cson_value_free(val);
+ }
+ else
+ {
+ ++p->totalValueCount;
+ }
+ return rc;
+ }
+ else
+ { /* WTF? */
+ assert( 0 && "Internal error in cson_parser code" );
+ return p->errNo = CSON_RC_InternalError;
+ }
+}
+
+/**
+ Callback for JSON_parser API. Reminder: it returns 0 (meaning false)
+ on error!
+*/
+static int cson_parse_callback( void * cx, int type, JSON_value const * value )
+{
+ cson_parser * p = (cson_parser *)cx;
+ int rc = 0;
+#define ALLOC_V(T,V) cson_value * v = cson_value_new_##T(V); if( ! v ) { rc = CSON_RC_AllocError; break; }
+ switch(type) {
+ case JSON_T_ARRAY_BEGIN:
+ case JSON_T_OBJECT_BEGIN: {
+ cson_value * obja = (JSON_T_ARRAY_BEGIN == type)
+ ? cson_value_new_array()
+ : cson_value_new_object();
+ if( ! obja )
+ {
+ p->errNo = CSON_RC_AllocError;
+ break;
+ }
+ if( 0 != rc ) break;
+ if( ! p->root )
+ {
+ p->root = p->node = obja;
+ rc = cson_array_append( &p->stack, obja );
+ if( 0 != rc )
+ { /* work around a (potential) corner case in the cleanup code. */
+ cson_value_free( p->root );
+ p->root = NULL;
+ }
+ else
+ {
+ cson_refcount_incr( p->root )
+ /* simplifies cleanup later on. */
+ ;
+ ++p->totalValueCount;
+ }
+ }
+ else
+ {
+ rc = cson_array_append( &p->stack, obja );
+ if(rc) cson_value_free( obja );
+ else
+ {
+ rc = cson_parser_push_value( p, obja );
+ if( 0 == rc ) p->node = obja;
+ }
+ }
+ break;
+ }
+ case JSON_T_ARRAY_END:
+ case JSON_T_OBJECT_END: {
+ if( 0 == p->stack.list.count )
+ {
+ rc = CSON_RC_RangeError;
+ break;
+ }
+#if CSON_OBJECT_PROPS_SORT
+ if( cson_value_is_object(p->node) )
+ {/* kludge: the parser uses custom cson_object property
+ insertion as a malloc/strcpy-reduction optimization.
+ Because of that, we have to sort the property list
+ ourselves...
+ */
+ cson_object * obj = cson_value_get_object(p->node);
+ assert( NULL != obj );
+ cson_object_sort_props( obj );
+ }
+#endif
+
+#if 1
+ /* Reminder: do not use cson_array_pop_back( &p->stack )
+ because that will clean up the object, and we don't want
+ that. We just want to forget this reference
+ to it. The object is either the root or was pushed into
+ an object/array in the parse tree (and is owned by that
+ object/array).
+ */
+ --p->stack.list.count;
+ assert( p->node == p->stack.list.list[p->stack.list.count] );
+ cson_refcount_decr( p->node )
+ /* p->node might be owned by an outer object but we
+ need to remove the list's reference. For the
+ root node we manually add a reference to
+ avoid a special case here. Thus when we close
+ the root node, its refcount is still 1.
+ */;
+ p->stack.list.list[p->stack.list.count] = NULL;
+ if( p->stack.list.count )
+ {
+ p->node = p->stack.list.list[p->stack.list.count-1];
+ }
+ else
+ {
+ p->node = p->root;
+ }
+#else
+ /*
+ Causing a leak?
+ */
+ cson_array_pop_back( &p->stack, 1 );
+ if( p->stack.list.count )
+ {
+ p->node = p->stack.list.list[p->stack.list.count-1];
+ }
+ else
+ {
+ p->node = p->root;
+ }
+ assert( p->node && (1==p->node->refcount) );
+#endif
+ break;
+ }
+ case JSON_T_INTEGER: {
+ ALLOC_V(integer, value->vu.integer_value );
+ rc = cson_parser_push_value( p, v );
+ break;
+ }
+ case JSON_T_FLOAT: {
+ ALLOC_V(double, value->vu.float_value );
+ rc = cson_parser_push_value( p, v );
+ break;
+ }
+ case JSON_T_NULL: {
+ rc = cson_parser_push_value( p, cson_value_null() );
+ break;
+ }
+ case JSON_T_TRUE: {
+ rc = cson_parser_push_value( p, cson_value_true() );
+ break;
+ }
+ case JSON_T_FALSE: {
+ rc = cson_parser_push_value( p, cson_value_false() );
+ break;
+ }
+ case JSON_T_KEY: {
+ assert(!p->ckey);
+ p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length );
+ if( ! p->ckey )
+ {
+ rc = CSON_RC_AllocError;
+ break;
+ }
+ ++p->totalKeyCount;
+ break;
+ }
+ case JSON_T_STRING: {
+ cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
+ rc = ( NULL == v )
+ ? CSON_RC_AllocError
+ : cson_parser_push_value( p, v );
+ break;
+ }
+ default:
+ assert(0);
+ rc = CSON_RC_InternalError;
+ break;
+ }
+#undef ALLOC_V
+ return ((p->errNo = rc)) ? 0 : 1;
+}
+
+
+/**
+ Converts a JSON_error code to one of the cson_rc values.
+*/
+static int cson_json_err_to_rc( JSON_error jrc )
+{
+ switch(jrc)
+ {
+ case JSON_E_NONE: return 0;
+ case JSON_E_INVALID_CHAR: return CSON_RC_Parse_INVALID_CHAR;
+ case JSON_E_INVALID_KEYWORD: return CSON_RC_Parse_INVALID_KEYWORD;
+ case JSON_E_INVALID_ESCAPE_SEQUENCE: return CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE;
+ case JSON_E_INVALID_UNICODE_SEQUENCE: return CSON_RC_Parse_INVALID_UNICODE_SEQUENCE;
+ case JSON_E_INVALID_NUMBER: return CSON_RC_Parse_INVALID_NUMBER;
+ case JSON_E_NESTING_DEPTH_REACHED: return CSON_RC_Parse_NESTING_DEPTH_REACHED;
+ case JSON_E_UNBALANCED_COLLECTION: return CSON_RC_Parse_UNBALANCED_COLLECTION;
+ case JSON_E_EXPECTED_KEY: return CSON_RC_Parse_EXPECTED_KEY;
+ case JSON_E_EXPECTED_COLON: return CSON_RC_Parse_EXPECTED_COLON;
+ case JSON_E_OUT_OF_MEMORY: return CSON_RC_AllocError;
+ default:
+ return CSON_RC_InternalError;
+ }
+}
+
+/** @internal
+
+ Cleans up all contents of p but does not free p.
+
+ To properly take over ownership of the parser's root node on a
+ successful parse:
+
+ - Copy p->root's pointer and set p->root to NULL.
+ - Eventually free up p->root with cson_value_free().
+
+ If you do not set p->root to NULL, p->root will be freed along with
+ any other items inserted into it (or under it) during the parsing
+ process.
+*/
+static int cson_parser_clean( cson_parser * p )
+{
+ if( ! p ) return CSON_RC_ArgError;
+ else
+ {
+ if( p->p )
+ {
+ delete_JSON_parser(p->p);
+ p->p = NULL;
+ }
+ if( p->ckey ){
+ cson_value_free(cson_string_value(p->ckey));
+ }
+ cson_array_clean( &p->stack, 1 );
+ if( p->root )
+ {
+ cson_value_free( p->root );
+ }
+ *p = cson_parser_empty;
+ return 0;
+ }
+}
+
+
+int cson_parse( cson_value ** tgt, cson_data_source_f src, void * state,
+ cson_parse_opt const * opt_, cson_parse_info * info_ )
+{
+ unsigned char ch[2] = {0,0};
+ cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
+ int rc = 0;
+ unsigned int len = 1;
+ cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
+ cson_parser p = cson_parser_empty;
+ if( ! tgt || ! src ) return CSON_RC_ArgError;
+
+ {
+ JSON_config jopt = {0};
+ init_JSON_config( &jopt );
+ jopt.allow_comments = opt.allowComments;
+ jopt.depth = opt.maxDepth;
+ jopt.callback_ctx = &p;
+ jopt.handle_floats_manually = 0;
+ jopt.callback = cson_parse_callback;
+ p.p = new_JSON_parser(&jopt);
+ if( ! p.p )
+ {
+ return CSON_RC_AllocError;
+ }
+ }
+
+ do
+ { /* FIXME: buffer the input in multi-kb chunks. */
+ len = 1;
+ ch[0] = 0;
+ rc = src( state, ch, &len );
+ if( 0 != rc ) break;
+ else if( !len /* EOF */ ) break;
+ ++info.length;
+ if('\n' == ch[0])
+ {
+ ++info.line;
+ info.col = 0;
+ }
+ if( ! JSON_parser_char(p.p, ch[0]) )
+ {
+ rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
+ if(0==rc) rc = p.errNo;
+ if(0==rc) rc = CSON_RC_InternalError;
+ info.errorCode = rc;
+ break;
+ }
+ if( '\n' != ch[0]) ++info.col;
+ } while(1);
+ if( info_ )
+ {
+ info.totalKeyCount = p.totalKeyCount;
+ info.totalValueCount = p.totalValueCount;
+ *info_ = info;
+ }
+ if( 0 != rc )
+ {
+ cson_parser_clean(&p);
+ return rc;
+ }
+ if( ! JSON_parser_done(p.p) )
+ {
+ rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
+ cson_parser_clean(&p);
+ if(0==rc) rc = p.errNo;
+ if(0==rc) rc = CSON_RC_InternalError;
+ }
+ else
+ {
+ cson_value * root = p.root;
+ p.root = NULL;
+ cson_parser_clean(&p);
+ if( root )
+ {
+ assert( (1 == root->refcount) && "Detected memory mismanagement in the parser." );
+ root->refcount = 0
+ /* HUGE KLUDGE! Avoids having one too many references
+ in some client code, leading to a leak. Here we're
+ accommodating a memory management workaround in the
+ parser code which manually adds a reference to the
+ root node to keep it from being cleaned up
+ prematurely.
+ */;
+ *tgt = root;
+ }
+ else
+ { /* then can happen on empty input. */
+ rc = CSON_RC_UnknownError;
+ }
+ }
+ return rc;
+}
+
+/**
+ The UTF code was originally taken from sqlite3's public-domain
+ source code (http://sqlite.org), modified only slightly for use
+ here. This code generates some "possible data loss" warnings on
+ MSVC, but if this code is good enough for sqlite3 then it's damned
+ well good enough for me, so we disable that warning for Windows
+ builds.
+*/
+
+/*
+** This lookup table is used to help decode the first byte of
+** a multi-byte UTF8 character.
+*/
+static const unsigned char cson_utfTrans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
+};
+
+
+/*
+** Translate a single UTF-8 character. Return the unicode value.
+**
+** During translation, assume that the byte that zTerm points
+** is a 0x00.
+**
+** Write a pointer to the next unread byte back into *pzNext.
+**
+** Notes On Invalid UTF-8:
+**
+** * This routine never allows a 7-bit character (0x00 through 0x7f) to
+** be encoded as a multi-byte character. Any multi-byte character that
+** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
+**
+** * This routine never allows a UTF16 surrogate value to be encoded.
+** If a multi-byte character attempts to encode a value between
+** 0xd800 and 0xe000 then it is rendered as 0xfffd.
+**
+** * Bytes in the range of 0x80 through 0xbf which occur as the first
+** byte of a character are interpreted as single-byte characters
+** and rendered as themselves even though they are technically
+** invalid characters.
+**
+** * This routine accepts an infinite number of different UTF8 encodings
+** for unicode values 0x80 and greater. It do not change over-length
+** encodings to 0xfffd as some systems recommend.
+*/
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = cson_utfTrans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
+static int cson_utf8Read(
+ const unsigned char *z, /* First byte of UTF-8 character */
+ const unsigned char *zTerm, /* Pretend this byte is 0x00 */
+ const unsigned char **pzNext /* Write first byte past UTF-8 char here */
+){
+ int c;
+ READ_UTF8(z, zTerm, c);
+ *pzNext = z;
+ return c;
+}
+#undef READ_UTF8
+
+#ifdef _MSC_VER
+# if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
+# pragma warning( pop )
+# endif
+#endif
+
+unsigned int cson_string_length_utf8( cson_string const * str )
+{
+ if( ! str ) return 0;
+ else
+ {
+ char unsigned const * pos = (char unsigned const *)cson_string_cstr(str);
+ char unsigned const * end = pos + str->length;
+ unsigned int rc = 0;
+ for( ; (pos < end) && cson_utf8Read(pos, end, &pos);
+ ++rc )
+ {
+ };
+ return rc;
+ }
+}
+
+/**
+ Escapes the first len bytes of the given string as JSON and sends
+ it to the given output function (which will be called often - once
+ for each logical character). The output is also surrounded by
+ double-quotes.
+
+ A NULL str will be escaped as an empty string, though we should
+ arguably export it as "null" (without quotes). We do this because
+ in JavaScript (typeof null === "object"), and by outputing null
+ here we would effectively change the data type from string to
+ object.
+*/
+static int cson_str_to_json( char const * str, unsigned int len,
+ char escapeFwdSlash,
+ cson_data_dest_f f, void * state )
+{
+ if( NULL == f ) return CSON_RC_ArgError;
+ else if( !str || !*str || (0 == len) )
+ { /* special case for 0-length strings. */
+ return f( state, "\"\"", 2 );
+ }
+ else
+ {
+ unsigned char const * pos = (unsigned char const *)str;
+ unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
+ unsigned char const * next = NULL;
+ int ch;
+ unsigned char clen = 0;
+ char escChar[3] = {'\\',0,0};
+ enum { UBLen = 20 };
+ char ubuf[UBLen];
+ int rc = 0;
+ rc = f(state, "\"", 1 );
+ for( ; (pos < end) && (0 == rc); pos += clen )
+ {
+ ch = cson_utf8Read(pos, end, &next);
+ if( 0 == ch ) break;
+ assert( next > pos );
+ clen = next - pos;
+ assert( clen );
+ if( 1 == clen )
+ { /* ASCII */
+#if defined(CSON_FOSSIL_MODE)
+ /* Workaround for fossil repo artifact
+ f460839cff85d4e4f1360b366bb2858cef1411ea,
+ which has what appears to be latin1-encoded
+ text. file(1) thinks it's a FORTRAN program.
+ */
+ if(0xfffd==ch){
+ assert(*pos != ch);
+ /* MARKER("ch=%04x, *pos=%04x\n", ch, *pos); */
+ ch = *pos
+ /* We should arguably translate to '?', and
+ will if this problem ever comes up with a
+ non-latin1 encoding. For latin1 this
+ workaround incidentally corrects the output
+ to proper UTF8-escaped characters, and only
+ for that reason is it being kept around.
+ */;
+ goto assume_latin1;
+ }
+#endif
+ assert( (*pos == ch) && "Invalid UTF8" );
+ escChar[1] = 0;
+ switch(ch)
+ {
+ case '\t': escChar[1] = 't'; break;
+ case '\r': escChar[1] = 'r'; break;
+ case '\n': escChar[1] = 'n'; break;
+ case '\f': escChar[1] = 'f'; break;
+ case '\b': escChar[1] = 'b'; break;
+ case '/':
+ /*
+ Regarding escaping of forward-slashes. See the main exchange below...
+
+ --------------
+ From: Douglas Crockford <douglas@crockford.com>
+ To: Stephan Beal <sgbeal@googlemail.com>
+ Subject: Re: Is escaping of forward slashes required?
+
+ It is allowed, not required. It is allowed so that JSON can be safely
+ embedded in HTML, which can freak out when seeing strings containing
+ "</". JSON tolerates "<\/" for this reason.
+
+ On 4/8/2011 2:09 PM, Stephan Beal wrote:
+ > Hello, Jsonites,
+ >
+ > i'm a bit confused on a small grammatic detail of JSON:
+ >
+ > if i'm reading the grammar chart on http://www.json.org/ correctly,
+ > forward slashes (/) are supposed to be escaped in JSON. However, the
+ > JSON class provided with my browsers (Chrome and FF, both of which i
+ > assume are fairly standards/RFC-compliant) do not escape such characters.
+ >
+ > Is backslash-escaping forward slashes required? If so, what is the
+ > justification for it? (i ask because i find it unnecessary and hard to
+ > look at.)
+ --------------
+ */
+ if( escapeFwdSlash ) escChar[1] = '/';
+ break;
+ case '\\': escChar[1] = '\\'; break;
+ case '"': escChar[1] = '"'; break;
+ default: break;
+ }
+ if( escChar[1])
+ {
+ rc = f(state, escChar, 2);
+ }
+ else
+ {
+ rc = f(state, (char const *)pos, clen);
+ }
+ continue;
+ }
+ else
+ { /* UTF: transform it to \uXXXX */
+#if defined(CSON_FOSSIL_MODE)
+ assume_latin1:
+#endif
+ memset(ubuf,0,UBLen);
+ if(ch <= 0xFFFF){
+ rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x",ch);
+ if( rc != 6 )
+ {
+ rc = CSON_RC_RangeError;
+ break;
+ }
+ rc = f( state, ubuf, 6 );
+ }else{ /* encode as a UTF16 surrogate pair */
+ /* http://unicodebook.readthedocs.org/en/latest/unicode_encodings.html#surrogates */
+ ch -= 0x10000;
+ rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x\\u%04x",
+ (0xd800 | (ch>>10)),
+ (0xdc00 | (ch & 0x3ff)));
+ if( rc != 12 )
+ {
+ rc = CSON_RC_RangeError;
+ break;
+ }
+ rc = f( state, ubuf, 12 );
+ }
+ continue;
+ }
+ }
+ if( 0 == rc )
+ {
+ rc = f(state, "\"", 1 );
+ }
+ return rc;
+ }
+}
+
+int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter )
+{
+ if( ! obj || !iter ) return CSON_RC_ArgError;
+ else
+ {
+ iter->obj = obj;
+ iter->pos = 0;
+ return 0;
+ }
+}
+
+cson_kvp * cson_object_iter_next( cson_object_iterator * iter )
+{
+ if( ! iter || !iter->obj ) return NULL;
+ else if( iter->pos >= iter->obj->kvp.count ) return NULL;
+ else
+ {
+ cson_kvp * rc = iter->obj->kvp.list[iter->pos++];
+ while( (NULL==rc) && (iter->pos < iter->obj->kvp.count))
+ {
+ rc = iter->obj->kvp.list[iter->pos++];
+ }
+ return rc;
+ }
+}
+
+static int cson_output_null( cson_data_dest_f f, void * state )
+{
+ if( !f ) return CSON_RC_ArgError;
+ else
+ {
+ return f(state, "null", 4);
+ }
+}
+
+static int cson_output_bool( cson_value const * src, cson_data_dest_f f, void * state )
+{
+ if( !f ) return CSON_RC_ArgError;
+ else
+ {
+ char const v = cson_value_get_bool(src);
+ return f(state, v ? "true" : "false", v ? 4 : 5);
+ }
+}
+
+static int cson_output_integer( cson_value const * src, cson_data_dest_f f, void * state )
+{
+ if( !f ) return CSON_RC_ArgError;
+ else if( !cson_value_is_integer(src) ) return CSON_RC_TypeError;
+ else
+ {
+ enum { BufLen = 100 };
+ char b[BufLen];
+ int rc;
+ memset( b, 0, BufLen );
+ rc = snprintf( b, (size_t)BufLen, "%"CSON_INT_T_PFMT,
+ cson_value_get_integer(src) );
+ return ( rc<=0 )
+ ? CSON_RC_RangeError
+ : f( state, b, (unsigned int)rc )
+ ;
+ }
+}
+
+static int cson_output_double( cson_value const * src, cson_data_dest_f f, void * state )
+{
+ if( !f ) return CSON_RC_ArgError;
+ else if( !cson_value_is_double(src) ) return CSON_RC_TypeError;
+ else
+ {
+ enum { BufLen = 128 /* this must be relatively large or huge
+ doubles can cause us to overrun here,
+ resulting in stack-smashing errors.
+ */};
+ char b[BufLen];
+ int rc;
+ memset( b, 0, BufLen );
+ rc = snprintf( b, (size_t)BufLen, "%"CSON_DOUBLE_T_PFMT,
+ cson_value_get_double(src) );
+ if( rc<=0 ) return CSON_RC_RangeError;
+ else if(1)
+ { /* Strip trailing zeroes before passing it on... */
+ unsigned int urc = (unsigned int)rc;
+ char * pos = b + urc - 1;
+ for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
+ {
+ *pos = 0;
+ }
+ assert(urc && *pos);
+ return f( state, b, urc );
+ }
+ else
+ {
+ unsigned int urc = (unsigned int)rc;
+ return f( state, b, urc );
+ }
+ return 0;
+ }
+}
+
+static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state )
+{
+ if( !f ) return CSON_RC_ArgError;
+ else if( ! cson_value_is_string(src) ) return CSON_RC_TypeError;
+ else
+ {
+ cson_string const * str = cson_value_get_string(src);
+ assert( NULL != str );
+ return cson_str_to_json(cson_string_cstr(str), str->length, escapeFwdSlash, f, state);
+ }
+}
+
+
+/**
+ Outputs indention spacing to f().
+
+ blanks: (0)=no indentation, (1)=1 TAB per/level, (>1)=n spaces/level
+
+ depth is the current depth of the output tree, and determines how much
+ indentation to generate.
+
+ If blanks is 0 this is a no-op. Returns non-0 on error, and the
+ error code will always come from f().
+*/
+static int cson_output_indent( cson_data_dest_f f, void * state,
+ unsigned char blanks, unsigned int depth )
+{
+ if( 0 == blanks ) return 0;
+ else
+ {
+#if 0
+ /* FIXME: stuff the indention into the buffer and make a single
+ call to f().
+ */
+ enum { BufLen = 200 };
+ char buf[BufLen];
+#endif
+ unsigned int i;
+ unsigned int x;
+ char const ch = (1==blanks) ? '\t' : ' ';
+ int rc = f(state, "\n", 1 );
+ for( i = 0; (i < depth) && (0 == rc); ++i )
+ {
+ for( x = 0; (x < blanks) && (0 == rc); ++x )
+ {
+ rc = f(state, &ch, 1);
+ }
+ }
+ return rc;
+ }
+}
+
+static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state,
+ cson_output_opt const * fmt, unsigned int level );
+static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
+ cson_output_opt const * fmt, unsigned int level );
+/**
+ Main cson_output() implementation. Dispatches to a different impl depending
+ on src->api->typeID.
+
+ Returns 0 on success.
+*/
+static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state,
+ cson_output_opt const * fmt, unsigned int level )
+{
+ if( ! src || !f || !src->api ) return CSON_RC_ArgError;
+ else
+ {
+ int rc = 0;
+ assert(fmt);
+ switch( src->api->typeID )
+ {
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL:
+ rc = cson_output_null(f, state);
+ break;
+ case CSON_TYPE_BOOL:
+ rc = cson_output_bool(src, f, state);
+ break;
+ case CSON_TYPE_INTEGER:
+ rc = cson_output_integer(src, f, state);
+ break;
+ case CSON_TYPE_DOUBLE:
+ rc = cson_output_double(src, f, state);
+ break;
+ case CSON_TYPE_STRING:
+ rc = cson_output_string(src, fmt->escapeForwardSlashes, f, state);
+ break;
+ case CSON_TYPE_ARRAY:
+ rc = cson_output_array( src, f, state, fmt, level );
+ break;
+ case CSON_TYPE_OBJECT:
+ rc = cson_output_object( src, f, state, fmt, level );
+ break;
+ default:
+ rc = CSON_RC_TypeError;
+ break;
+ }
+ return rc;
+ }
+}
+
+
+static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state,
+ cson_output_opt const * fmt, unsigned int level )
+{
+ if( !src || !f || !fmt ) return CSON_RC_ArgError;
+ else if( ! cson_value_is_array(src) ) return CSON_RC_TypeError;
+ else if( level > fmt->maxDepth ) return CSON_RC_RangeError;
+ else
+ {
+ int rc;
+ unsigned int i;
+ cson_value const * v;
+ char doIndent = fmt->indentation ? 1 : 0;
+ cson_array const * ar = cson_value_get_array(src);
+ assert( NULL != ar );
+ if( 0 == ar->list.count )
+ {
+ return f(state, "[]", 2 );
+ }
+ else if( (1 == ar->list.count) && !fmt->indentSingleMemberValues ) doIndent = 0;
+ rc = f(state, "[", 1);
+ ++level;
+ if( doIndent )
+ {
+ rc = cson_output_indent( f, state, fmt->indentation, level );
+ }
+ for( i = 0; (i < ar->list.count) && (0 == rc); ++i )
+ {
+ v = ar->list.list[i];
+ if( v )
+ {
+ rc = cson_output_impl( v, f, state, fmt, level );
+ }
+ else
+ {
+ rc = cson_output_null( f, state );
+ }
+ if( 0 == rc )
+ {
+ if(i < (ar->list.count-1))
+ {
+ rc = f(state, ",", 1);
+ if( 0 == rc )
+ {
+ rc = doIndent
+ ? cson_output_indent( f, state, fmt->indentation, level )
+ : 0 /*f( state, " ", 1 )*/;
+ }
+ }
+ }
+ }
+ --level;
+ if( doIndent && (0 == rc) )
+ {
+ rc = cson_output_indent( f, state, fmt->indentation, level );
+ }
+ return (0 == rc)
+ ? f(state, "]", 1)
+ : rc;
+ }
+}
+
+static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
+ cson_output_opt const * fmt, unsigned int level )
+{
+ if( !src || !f || !fmt ) return CSON_RC_ArgError;
+ else if( ! cson_value_is_object(src) ) return CSON_RC_TypeError;
+ else if( level > fmt->maxDepth ) return CSON_RC_RangeError;
+ else
+ {
+ int rc;
+ unsigned int i;
+ cson_kvp const * kvp;
+ char doIndent = fmt->indentation ? 1 : 0;
+ cson_object const * obj = cson_value_get_object(src);
+ assert( (NULL != obj) && (NULL != fmt));
+ if( 0 == obj->kvp.count )
+ {
+ return f(state, "{}", 2 );
+ }
+ else if( (1 == obj->kvp.count) && !fmt->indentSingleMemberValues ) doIndent = 0;
+ rc = f(state, "{", 1);
+ ++level;
+ if( doIndent )
+ {
+ rc = cson_output_indent( f, state, fmt->indentation, level );
+ }
+ for( i = 0; (i < obj->kvp.count) && (0 == rc); ++i )
+ {
+ kvp = obj->kvp.list[i];
+ if( kvp && kvp->key )
+ {
+ cson_string const * sKey = cson_value_get_string(kvp->key);
+ char const * cKey = cson_string_cstr(sKey);
+ rc = cson_str_to_json(cKey, sKey->length,
+ fmt->escapeForwardSlashes, f, state);
+ if( 0 == rc )
+ {
+ rc = fmt->addSpaceAfterColon
+ ? f(state, ": ", 2 )
+ : f(state, ":", 1 )
+ ;
+ }
+ if( 0 == rc)
+ {
+ rc = ( kvp->value )
+ ? cson_output_impl( kvp->value, f, state, fmt, level )
+ : cson_output_null( f, state );
+ }
+ }
+ else
+ {
+ assert( 0 && "Possible internal error." );
+ continue /* internal error? */;
+ }
+ if( 0 == rc )
+ {
+ if(i < (obj->kvp.count-1))
+ {
+ rc = f(state, ",", 1);
+ if( 0 == rc )
+ {
+ rc = doIndent
+ ? cson_output_indent( f, state, fmt->indentation, level )
+ : 0 /*f( state, " ", 1 )*/;
+ }
+ }
+ }
+ }
+ --level;
+ if( doIndent && (0 == rc) )
+ {
+ rc = cson_output_indent( f, state, fmt->indentation, level );
+ }
+ return (0 == rc)
+ ? f(state, "}", 1)
+ : rc;
+ }
+}
+
+int cson_output( cson_value const * src, cson_data_dest_f f,
+ void * state, cson_output_opt const * fmt )
+{
+ int rc;
+ if(! fmt ) fmt = &cson_output_opt_empty;
+ rc = cson_output_impl(src, f, state, fmt, 0 );
+ if( (0 == rc) && fmt->addNewline )
+ {
+ rc = f(state, "\n", 1);
+ }
+ return rc;
+}
+
+int cson_data_dest_FILE( void * state, void const * src, unsigned int n )
+{
+ if( ! state ) return CSON_RC_ArgError;
+ else if( !src || !n ) return 0;
+ else
+ {
+ return ( 1 == fwrite( src, n, 1, (FILE*) state ) )
+ ? 0
+ : CSON_RC_IOError;
+ }
+}
+
+int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * fmt )
+{
+ int rc = 0;
+ if( fmt )
+ {
+ rc = cson_output( src, cson_data_dest_FILE, dest, fmt );
+ }
+ else
+ {
+ /* We normally want a newline on FILE output. */
+ cson_output_opt opt = cson_output_opt_empty;
+ opt.addNewline = 1;
+ rc = cson_output( src, cson_data_dest_FILE, dest, &opt );
+ }
+ if( 0 == rc )
+ {
+ fflush( dest );
+ }
+ return rc;
+}
+
+int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt )
+{
+ if( !src || !dest ) return CSON_RC_ArgError;
+ else
+ {
+ FILE * f = fopen(dest,"wb");
+ if( !f ) return CSON_RC_IOError;
+ else
+ {
+ int const rc = cson_output_FILE( src, f, fmt );
+ fclose(f);
+ return rc;
+ }
+ }
+}
+
+int cson_parse_filename( cson_value ** tgt, char const * src,
+ cson_parse_opt const * opt, cson_parse_info * err )
+{
+ if( !src || !tgt ) return CSON_RC_ArgError;
+ else
+ {
+ FILE * f = fopen(src, "r");
+ if( !f ) return CSON_RC_IOError;
+ else
+ {
+ int const rc = cson_parse_FILE( tgt, f, opt, err );
+ fclose(f);
+ return rc;
+ }
+ }
+}
+
+/** Internal type to hold state for a JSON input string.
+ */
+typedef struct cson_data_source_StringSource_
+{
+ /** Start of input string. */
+ char const * str;
+ /** Current iteration position. Must initially be == str. */
+ char const * pos;
+ /** Logical EOF, one-past-the-end of str. */
+ char const * end;
+} cson_data_source_StringSource_t;
+
+/**
+ A cson_data_source_f() implementation which requires the state argument
+ to be a properly populated (cson_data_source_StringSource_t*).
+*/
+static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n )
+{
+ if( !state || !n || !dest ) return CSON_RC_ArgError;
+ else if( !*n ) return 0 /* ignore this */;
+ else
+ {
+ unsigned int i;
+ cson_data_source_StringSource_t * ss = (cson_data_source_StringSource_t*) state;
+ unsigned char * tgt = (unsigned char *)dest;
+ for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt )
+ {
+ *tgt = *ss->pos;
+ }
+ *n = i;
+ return 0;
+ }
+}
+
+int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len,
+ cson_parse_opt const * opt, cson_parse_info * err )
+{
+ if( ! tgt || !src ) return CSON_RC_ArgError;
+ else if( !*src || (len<2/*2==len of {} and []*/) ) return CSON_RC_RangeError;
+ else
+ {
+ cson_data_source_StringSource_t ss;
+ ss.str = ss.pos = src;
+ ss.end = src + len;
+ return cson_parse( tgt, cson_data_source_StringSource, &ss, opt, err );
+ }
+
+}
+
+int cson_parse_buffer( cson_value ** tgt,
+ cson_buffer const * buf,
+ cson_parse_opt const * opt,
+ cson_parse_info * err )
+{
+ return ( !tgt || !buf || !buf->mem || !buf->used )
+ ? CSON_RC_ArgError
+ : cson_parse_string( tgt, (char const *)buf->mem,
+ buf->used, opt, err );
+}
+
+int cson_buffer_reserve( cson_buffer * buf, cson_size_t n )
+{
+ if( ! buf ) return CSON_RC_ArgError;
+ else if( 0 == n )
+ {
+ cson_free(buf->mem, "cson_buffer::mem");
+ *buf = cson_buffer_empty;
+ return 0;
+ }
+ else if( buf->capacity >= n )
+ {
+ return 0;
+ }
+ else
+ {
+ unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" );
+ if( ! x ) return CSON_RC_AllocError;
+ memset( x + buf->used, 0, n - buf->used );
+ buf->mem = x;
+ buf->capacity = n;
+ ++buf->timesExpanded;
+ return 0;
+ }
+}
+
+cson_size_t cson_buffer_fill( cson_buffer * buf, char c )
+{
+ if( !buf || !buf->capacity || !buf->mem ) return 0;
+ else
+ {
+ memset( buf->mem, c, buf->capacity );
+ return buf->capacity;
+ }
+}
+
+/**
+ cson_data_dest_f() implementation, used by cson_output_buffer().
+
+ arg MUST be a (cson_buffer*). This function appends n bytes at
+ position arg->used, expanding the buffer as necessary.
+*/
+static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
+{
+ if( !arg ) return CSON_RC_ArgError;
+ else if( ! n ) return 0;
+ else
+ {
+ cson_buffer * sb = (cson_buffer*)arg;
+ char const * data = (char const *)data_;
+ cson_size_t npos = sb->used + n;
+ unsigned int i;
+ if( npos >= sb->capacity )
+ {
+ const cson_size_t oldCap = sb->capacity;
+ const cson_size_t asz = npos * 2;
+ if( asz < npos ) return CSON_RC_ArgError; /* overflow */
+ else if( 0 != cson_buffer_reserve( sb, asz ) ) return CSON_RC_AllocError;
+ assert( (sb->capacity > oldCap) && "Internal error in memory buffer management!" );
+ /* make sure it gets NUL terminated. */
+ memset( sb->mem + oldCap, 0, (sb->capacity - oldCap) );
+ }
+ for( i = 0; i < n; ++i, ++sb->used )
+ {
+ sb->mem[sb->used] = data[i];
+ }
+ return 0;
+ }
+}
+
+
+int cson_output_buffer( cson_value const * v, cson_buffer * buf,
+ cson_output_opt const * opt )
+{
+ int rc = cson_output( v, cson_data_dest_cson_buffer, buf, opt );
+ if( 0 == rc )
+ { /* Ensure that the buffer is null-terminated. */
+ rc = cson_buffer_reserve( buf, buf->used + 1 );
+ if( 0 == rc )
+ {
+ buf->mem[buf->used] = 0;
+ }
+ }
+ return rc;
+}
+
+/** @internal
+
+Tokenizes an input string on a given separator. Inputs are:
+
+- (inp) = is a pointer to the pointer to the start of the input.
+
+- (separator) = the separator character
+
+- (end) = a pointer to NULL. i.e. (*end == NULL)
+
+This function scans *inp for the given separator char or a NUL char.
+Successive separators at the start of *inp are skipped. The effect is
+that, when this function is called in a loop, all neighboring
+separators are ignored. e.g. the string "aa.bb...cc" will tokenize to
+the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the
+separator is 'b'.
+
+Returns 0 (false) if it finds no token, else non-0 (true).
+
+Output:
+
+- (*inp) will be set to the first character of the next token.
+
+- (*end) will point to the one-past-the-end point of the token.
+
+If (*inp == *end) then the end of the string has been reached
+without finding a token.
+
+Post-conditions:
+
+- (*end == *inp) if no token is found.
+
+- (*end > *inp) if a token is found.
+
+It is intolerant of NULL values for (inp, end), and will assert() in
+debug builds if passed NULL as either parameter.
+*/
+static char cson_next_token( char const ** inp, char separator, char const ** end )
+{
+ char const * pos = NULL;
+ assert( inp && end && *inp );
+ if( *inp == *end ) return 0;
+ pos = *inp;
+ if( !*pos )
+ {
+ *end = pos;
+ return 0;
+ }
+ for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ }
+ *inp = pos;
+ for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
+ *end = pos;
+ return (pos > *inp) ? 1 : 0;
+}
+
+int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path )
+{
+ if( ! obj || !path ) return CSON_RC_ArgError;
+ else if( !*path || !*(1+path) ) return CSON_RC_RangeError;
+ else return cson_object_fetch_sub(obj, tgt, path+1, *path);
+}
+
+int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char sep )
+{
+ if( ! obj || !path ) return CSON_RC_ArgError;
+ else if( !*path || !sep ) return CSON_RC_RangeError;
+ else
+ {
+ char const * beg = path;
+ char const * end = NULL;
+ int rc;
+ unsigned int i, len;
+ unsigned int tokenCount = 0;
+ cson_value * cv = NULL;
+ cson_object const * curObj = obj;
+ enum { BufSize = 128 };
+ char buf[BufSize];
+ memset( buf, 0, BufSize );
+
+ while( cson_next_token( &beg, sep, &end ) )
+ {
+ if( beg == end ) break;
+ else
+ {
+ ++tokenCount;
+ beg = end;
+ end = NULL;
+ }
+ }
+ if( 0 == tokenCount ) return CSON_RC_RangeError;
+ beg = path;
+ end = NULL;
+ for( i = 0; i < tokenCount; ++i, beg=end, end=NULL )
+ {
+ rc = cson_next_token( &beg, sep, &end );
+ assert( 1 == rc );
+ assert( beg != end );
+ assert( end > beg );
+ len = end - beg;
+ if( len > (BufSize-1) ) return CSON_RC_RangeError;
+ memset( buf, 0, len + 1 );
+ memcpy( buf, beg, len );
+ buf[len] = 0;
+ cv = cson_object_get( curObj, buf );
+ if( NULL == cv ) return CSON_RC_NotFoundError;
+ else if( i == (tokenCount-1) )
+ {
+ if(tgt) *tgt = cv;
+ return 0;
+ }
+ else if( cson_value_is_object(cv) )
+ {
+ curObj = cson_value_get_object(cv);
+ assert((NULL != curObj) && "Detected mis-management of internal memory!");
+ }
+ /* TODO: arrays. Requires numeric parsing for the index. */
+ else
+ {
+ return CSON_RC_NotFoundError;
+ }
+ }
+ assert( i == tokenCount );
+ return CSON_RC_NotFoundError;
+ }
+}
+
+cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep )
+{
+ cson_value * v = NULL;
+ cson_object_fetch_sub( obj, &v, path, sep );
+ return v;
+}
+
+cson_value * cson_object_get_sub2( cson_object const * obj, char const * path )
+{
+ cson_value * v = NULL;
+ cson_object_fetch_sub2( obj, &v, path );
+ return v;
+}
+
+
+/**
+ If v is-a Object or Array then this function returns a deep
+ clone, otherwise it returns v. In either case, the refcount
+ of the returned value is increased by 1 by this call.
+*/
+static cson_value * cson_value_clone_ref( cson_value * v )
+{
+ cson_value * rc = NULL;
+#define TRY_SHARING 1
+#if TRY_SHARING
+ if(!v ) return rc;
+ else if( cson_value_is_object(v)
+ || cson_value_is_array(v))
+ {
+ rc = cson_value_clone( v );
+ }
+ else
+ {
+ rc = v;
+ }
+#else
+ rc = cson_value_clone(v);
+#endif
+#undef TRY_SHARING
+ cson_value_add_reference(rc);
+ return rc;
+}
+
+static cson_value * cson_value_clone_array( cson_value const * orig )
+{
+ unsigned int i = 0;
+ cson_array const * asrc = cson_value_get_array( orig );
+ unsigned int alen = cson_array_length_get( asrc );
+ cson_value * destV = NULL;
+ cson_array * destA = NULL;
+ assert( orig && asrc );
+ destV = cson_value_new_array();
+ if( NULL == destV ) return NULL;
+ destA = cson_value_get_array( destV );
+ assert( destA );
+ if( 0 != cson_array_reserve( destA, alen ) )
+ {
+ cson_value_free( destV );
+ return NULL;
+ }
+ for( ; i < alen; ++i )
+ {
+ cson_value * ch = cson_array_get( asrc, i );
+ if( NULL != ch )
+ {
+ cson_value * cl = cson_value_clone_ref( ch );
+ if( NULL == cl )
+ {
+ cson_value_free( destV );
+ return NULL;
+ }
+ if( 0 != cson_array_set( destA, i, cl ) )
+ {
+ cson_value_free( cl );
+ cson_value_free( destV );
+ return NULL;
+ }
+ cson_value_free(cl)/*remove our artificial reference */;
+ }
+ }
+ return destV;
+}
+
+static cson_value * cson_value_clone_object( cson_value const * orig )
+{
+ cson_object const * src = cson_value_get_object( orig );
+ cson_value * destV = NULL;
+ cson_object * dest = NULL;
+ cson_kvp const * kvp = NULL;
+ cson_object_iterator iter = cson_object_iterator_empty;
+ assert( orig && src );
+ if( 0 != cson_object_iter_init( src, &iter ) )
+ {
+ return NULL;
+ }
+ destV = cson_value_new_object();
+ if( NULL == destV ) return NULL;
+ dest = cson_value_get_object( destV );
+ assert( dest );
+ if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){
+ cson_value_free( destV );
+ return NULL;
+ }
+ while( (kvp = cson_object_iter_next( &iter )) )
+ {
+ cson_value * key = NULL;
+ cson_value * val = NULL;
+ assert( kvp->key && (kvp->key->refcount>0) );
+ key = cson_value_clone_ref(kvp->key);
+ val = key ? cson_value_clone_ref(kvp->value) : NULL;
+ if( ! key || !val ){
+ goto error;
+ }
+ assert( CSON_STR(key) );
+ if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) )
+ {
+ goto error;
+ }
+ /* remove our references */
+ cson_value_free(key);
+ cson_value_free(val);
+ continue;
+ error:
+ cson_value_free(key);
+ cson_value_free(val);
+ cson_value_free(destV);
+ destV = NULL;
+ break;
+ }
+ return destV;
+}
+
+cson_value * cson_value_clone( cson_value const * orig )
+{
+ if( NULL == orig ) return NULL;
+ else
+ {
+ switch( orig->api->typeID )
+ {
+ case CSON_TYPE_UNDEF:
+ assert(0 && "This should never happen.");
+ return NULL;
+ case CSON_TYPE_NULL:
+ return cson_value_null();
+ case CSON_TYPE_BOOL:
+ return cson_value_new_bool( cson_value_get_bool( orig ) );
+ case CSON_TYPE_INTEGER:
+ return cson_value_new_integer( cson_value_get_integer( orig ) );
+ break;
+ case CSON_TYPE_DOUBLE:
+ return cson_value_new_double( cson_value_get_double( orig ) );
+ break;
+ case CSON_TYPE_STRING: {
+ cson_string const * str = cson_value_get_string( orig );
+ return cson_value_new_string( cson_string_cstr( str ),
+ cson_string_length_bytes( str ) );
+ }
+ case CSON_TYPE_ARRAY:
+ return cson_value_clone_array( orig );
+ case CSON_TYPE_OBJECT:
+ return cson_value_clone_object( orig );
+ }
+ assert( 0 && "We can't get this far." );
+ return NULL;
+ }
+}
+
+cson_value * cson_string_value(cson_string const * s)
+{
+#define MT CSON_SPECIAL_VALUES[CSON_VAL_STR_EMPTY]
+ return s
+ ? ((s==MT.value) ? &MT : CSON_VCAST(s))
+ : NULL;
+#undef MT
+}
+
+cson_value * cson_object_value(cson_object const * s)
+{
+ return s
+ ? CSON_VCAST(s)
+ : NULL;
+}
+
+
+cson_value * cson_array_value(cson_array const * s)
+{
+ return s
+ ? CSON_VCAST(s)
+ : NULL;
+}
+
+void cson_free_object(cson_object *x)
+{
+ if(x) cson_value_free(cson_object_value(x));
+}
+void cson_free_array(cson_array *x)
+{
+ if(x) cson_value_free(cson_array_value(x));
+}
+
+void cson_free_string(cson_string *x)
+{
+ if(x) cson_value_free(cson_string_value(x));
+}
+void cson_free_value(cson_value *x)
+{
+ if(x) cson_value_free(x);
+}
+
+
+#if 0
+/* i'm not happy with this... */
+char * cson_pod_to_string( cson_value const * orig )
+{
+ if( ! orig ) return NULL;
+ else
+ {
+ enum { BufSize = 64 };
+ char * v = NULL;
+ switch( orig->api->typeID )
+ {
+ case CSON_TYPE_BOOL: {
+ char const bv = cson_value_get_bool(orig);
+ v = cson_strdup( bv ? "true" : "false",
+ bv ? 4 : 5 );
+ break;
+ }
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL: {
+ v = cson_strdup( "null", 4 );
+ break;
+ }
+ case CSON_TYPE_STRING: {
+ cson_string const * jstr = cson_value_get_string(orig);
+ unsigned const int slen = cson_string_length_bytes( jstr );
+ assert( NULL != jstr );
+ v = cson_strdup( cson_string_cstr( jstr ), slen );
+ break;
+ }
+ case CSON_TYPE_INTEGER: {
+ char buf[BufSize] = {0};
+ if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT,
+ cson_value_get_integer(orig)) )
+ {
+ v = cson_strdup( buf, strlen(buf) );
+ }
+ break;
+ }
+ case CSON_TYPE_DOUBLE: {
+ char buf[BufSize] = {0};
+ if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT,
+ cson_value_get_double(orig)) )
+ {
+ v = cson_strdup( buf, strlen(buf) );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return v;
+ }
+}
+#endif
+
+#if 0
+/* i'm not happy with this... */
+char * cson_pod_to_string( cson_value const * orig )
+{
+ if( ! orig ) return NULL;
+ else
+ {
+ enum { BufSize = 64 };
+ char * v = NULL;
+ switch( orig->api->typeID )
+ {
+ case CSON_TYPE_BOOL: {
+ char const bv = cson_value_get_bool(orig);
+ v = cson_strdup( bv ? "true" : "false",
+ bv ? 4 : 5 );
+ break;
+ }
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL: {
+ v = cson_strdup( "null", 4 );
+ break;
+ }
+ case CSON_TYPE_STRING: {
+ cson_string const * jstr = cson_value_get_string(orig);
+ unsigned const int slen = cson_string_length_bytes( jstr );
+ assert( NULL != jstr );
+ v = cson_strdup( cson_string_cstr( jstr ), slen );
+ break;
+ }
+ case CSON_TYPE_INTEGER: {
+ char buf[BufSize] = {0};
+ if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT,
+ cson_value_get_integer(orig)) )
+ {
+ v = cson_strdup( buf, strlen(buf) );
+ }
+ break;
+ }
+ case CSON_TYPE_DOUBLE: {
+ char buf[BufSize] = {0};
+ if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT,
+ cson_value_get_double(orig)) )
+ {
+ v = cson_strdup( buf, strlen(buf) );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return v;
+ }
+}
+#endif
+
+unsigned int cson_value_msize(cson_value const * v)
+{
+ if(!v) return 0;
+ else if( cson_value_is_builtin(v) ) return 0;
+ else {
+ unsigned int rc = sizeof(cson_value);
+ assert(NULL != v->api);
+ switch(v->api->typeID){
+ case CSON_TYPE_INTEGER:
+ assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]);
+ rc += sizeof(cson_int_t);
+ break;
+ case CSON_TYPE_DOUBLE:
+ assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0]);
+ rc += sizeof(cson_double_t);
+ break;
+ case CSON_TYPE_STRING:
+ rc += sizeof(cson_string)
+ + CSON_STR(v)->length + 1/*NUL*/;
+ break;
+ case CSON_TYPE_ARRAY:{
+ cson_array const * ar = CSON_ARRAY(v);
+ cson_value_list const * li;
+ unsigned int i = 0;
+ assert( NULL != ar );
+ li = &ar->list;
+ rc += sizeof(cson_array)
+ + (li->alloced * sizeof(cson_value *));
+ for( ; i < li->count; ++i ){
+ cson_value const * e = ar->list.list[i];
+ if( e ) rc += cson_value_msize( e );
+ }
+ break;
+ }
+ case CSON_TYPE_OBJECT:{
+ cson_object const * obj = CSON_OBJ(v);
+ unsigned int i = 0;
+ cson_kvp_list const * kl;
+ assert(NULL != obj);
+ kl = &obj->kvp;
+ rc += sizeof(cson_object)
+ + (kl->alloced * sizeof(cson_kvp*));
+ for( ; i < kl->count; ++i ){
+ cson_kvp const * kvp = kl->list[i];
+ assert(NULL != kvp);
+ rc += cson_value_msize(kvp->key);
+ rc += cson_value_msize(kvp->value);
+ }
+ break;
+ }
+ case CSON_TYPE_UNDEF:
+ case CSON_TYPE_NULL:
+ case CSON_TYPE_BOOL:
+ assert( 0 && "Should have been caught by is-builtin check!" );
+ break;
+ default:
+ assert(0 && "Invalid typeID!");
+ return 0;
+#undef RCCHECK
+ }
+ return rc;
+ }
+}
+
+int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){
+ cson_object_iterator iter = cson_object_iterator_empty;
+ int rc;
+ char const replace = (flags & CSON_MERGE_REPLACE);
+ char const recurse = !(flags & CSON_MERGE_NO_RECURSE);
+ cson_kvp const * kvp;
+ if((!dest || !src) || (dest==src)) return CSON_RC_ArgError;
+ rc = cson_object_iter_init( src, &iter );
+ if(rc) return rc;
+ while( (kvp = cson_object_iter_next(&iter) ) )
+ {
+ cson_string * key = cson_kvp_key(kvp);
+ cson_value * val = cson_kvp_value(kvp);
+ cson_value * check = cson_object_get_s( dest, key );
+ if(!check){
+ cson_object_set_s( dest, key, val );
+ continue;
+ }
+ else if(!replace && !recurse) continue;
+ else if(replace && !recurse){
+ cson_object_set_s( dest, key, val );
+ continue;
+ }
+ else if( recurse ){
+ if( cson_value_is_object(check) &&
+ cson_value_is_object(val) ){
+ rc = cson_object_merge( cson_value_get_object(check),
+ cson_value_get_object(val),
+ flags );
+ if(rc) return rc;
+ else continue;
+ }
+ else continue;
+ }
+ else continue;
+ }
+ return 0;
+}
+
+static cson_value * cson_guess_arg_type(char const *arg){
+ char * end = NULL;
+ if(!arg || !*arg) return cson_value_null();
+ else if(('0'>*arg) || ('9'<*arg)){
+ goto do_string;
+ }
+ else{ /* try numbers... */
+ long const val = strtol(arg, &end, 10);
+ if(!*end){
+ return cson_value_new_integer( (cson_int_t)val);
+ }
+ else if( '.' != *end ) {
+ goto do_string;
+ }
+ else {
+ double const val = strtod(arg, &end);
+ if(!*end){
+ return cson_value_new_double(val);
+ }
+ }
+ }
+ do_string:
+ return cson_value_new_string(arg, strlen(arg));
+}
+
+
+int cson_parse_argv_flags( int argc, char const * const * argv,
+ cson_object ** tgt, unsigned int * count ){
+ cson_object * o = NULL;
+ int rc = 0;
+ int i = 0;
+ if(argc<1 || !argc || !tgt) return CSON_RC_ArgError;
+ o = *tgt ? *tgt : cson_new_object();
+ if(count) *count = 0;
+ for( i = 0; i < argc; ++i ){
+ char const * arg = argv[i];
+ char const * key = arg;
+ char const * pos;
+ cson_string * k = NULL;
+ cson_value * v = NULL;
+ if('-' != *arg) continue;
+ while('-'==*key) ++key;
+ if(!*key) continue;
+ pos = key;
+ while( *pos && ('=' != *pos)) ++pos;
+ k = cson_new_string(key, pos-key);
+ if(!k){
+ rc = CSON_RC_AllocError;
+ break;
+ }
+ if(!*pos){ /** --key */
+ v = cson_value_true();
+ }else{ /** --key=...*/
+ assert('=' == *pos);
+ ++pos /*skip '='*/;
+ v = cson_guess_arg_type(pos);
+ }
+ if(0 != (rc=cson_object_set_s(o, k, v))){
+ cson_free_string(k);
+ cson_value_free(v);
+ break;
+ }
+ else if(count) ++*count;
+ }
+ if(o != *tgt){
+ if(rc) cson_free_object(o);
+ else *tgt = o;
+ }
+ return rc;
+}
+
+#if defined(__cplusplus)
+} /*extern "C"*/
+#endif
+
+#undef MARKER
+#undef CSON_OBJECT_PROPS_SORT
+#undef CSON_OBJECT_PROPS_SORT_USE_LENGTH
+#undef CSON_CAST
+#undef CSON_INT
+#undef CSON_DBL
+#undef CSON_STR
+#undef CSON_OBJ
+#undef CSON_ARRAY
+#undef CSON_VCAST
+#undef CSON_MALLOC_IMPL
+#undef CSON_FREE_IMPL
+#undef CSON_REALLOC_IMPL
+/* end file ./cson.c */
+/* begin file ./cson_lists.h */
+/* Auto-generated from cson_list.h. Edit at your own risk! */
+unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n )
+{
+ if( !self ) return 0;
+ else if(0 == n)
+ {
+ if(0 == self->alloced) return 0;
+ cson_free(self->list, "cson_value_list_reserve");
+ self->list = NULL;
+ self->alloced = self->count = 0;
+ return 0;
+ }
+ else if( self->alloced >= n )
+ {
+ return self->alloced;
+ }
+ else
+ {
+ size_t const sz = sizeof(cson_value *) * n;
+ cson_value * * m = (cson_value **)cson_realloc( self->list, sz, "cson_value_list_reserve" );
+ if( ! m ) return self->alloced;
+
+ memset( m + self->alloced, 0, (sizeof(cson_value *)*(n-self->alloced)));
+ self->alloced = n;
+ self->list = m;
+ return n;
+ }
+}
+int cson_value_list_append( cson_value_list * self, cson_value * cp )
+{
+ if( !self || !cp ) return cson_rc.ArgError;
+ else if( self->alloced > cson_value_list_reserve(self, self->count+1) )
+ {
+ return cson_rc.AllocError;
+ }
+ else
+ {
+ self->list[self->count++] = cp;
+ return 0;
+ }
+}
+int cson_value_list_visit( cson_value_list * self,
+
+ int (*visitor)(cson_value * obj, void * visitorState ),
+
+
+
+ void * visitorState )
+{
+ int rc = cson_rc.ArgError;
+ if( self && visitor )
+ {
+ unsigned int i = 0;
+ for( rc = 0; (i < self->count) && (0 == rc); ++i )
+ {
+
+ cson_value * obj = self->list[i];
+
+
+
+ if(obj) rc = visitor( obj, visitorState );
+ }
+ }
+ return rc;
+}
+void cson_value_list_clean( cson_value_list * self,
+
+ void (*cleaner)(cson_value * obj)
+
+
+
+ )
+{
+ if( self && cleaner && self->count )
+ {
+ unsigned int i = 0;
+ for( ; i < self->count; ++i )
+ {
+
+ cson_value * obj = self->list[i];
+
+
+
+ if(obj) cleaner(obj);
+ }
+ }
+ cson_value_list_reserve(self,0);
+}
+unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n )
+{
+ if( !self ) return 0;
+ else if(0 == n)
+ {
+ if(0 == self->alloced) return 0;
+ cson_free(self->list, "cson_kvp_list_reserve");
+ self->list = NULL;
+ self->alloced = self->count = 0;
+ return 0;
+ }
+ else if( self->alloced >= n )
+ {
+ return self->alloced;
+ }
+ else
+ {
+ size_t const sz = sizeof(cson_kvp *) * n;
+ cson_kvp * * m = (cson_kvp **)cson_realloc( self->list, sz, "cson_kvp_list_reserve" );
+ if( ! m ) return self->alloced;
+
+ memset( m + self->alloced, 0, (sizeof(cson_kvp *)*(n-self->alloced)));
+ self->alloced = n;
+ self->list = m;
+ return n;
+ }
+}
+int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp )
+{
+ if( !self || !cp ) return cson_rc.ArgError;
+ else if( self->alloced > cson_kvp_list_reserve(self, self->count+1) )
+ {
+ return cson_rc.AllocError;
+ }
+ else
+ {
+ self->list[self->count++] = cp;
+ return 0;
+ }
+}
+int cson_kvp_list_visit( cson_kvp_list * self,
+
+ int (*visitor)(cson_kvp * obj, void * visitorState ),
+
+
+
+ void * visitorState )
+{
+ int rc = cson_rc.ArgError;
+ if( self && visitor )
+ {
+ unsigned int i = 0;
+ for( rc = 0; (i < self->count) && (0 == rc); ++i )
+ {
+
+ cson_kvp * obj = self->list[i];
+
+
+
+ if(obj) rc = visitor( obj, visitorState );
+ }
+ }
+ return rc;
+}
+void cson_kvp_list_clean( cson_kvp_list * self,
+
+ void (*cleaner)(cson_kvp * obj)
+
+
+
+ )
+{
+ if( self && cleaner && self->count )
+ {
+ unsigned int i = 0;
+ for( ; i < self->count; ++i )
+ {
+
+ cson_kvp * obj = self->list[i];
+
+
+
+ if(obj) cleaner(obj);
+ }
+ }
+ cson_kvp_list_reserve(self,0);
+}
+/* end file ./cson_lists.h */
+/* begin file ./cson_sqlite3.c */
+/** @file cson_sqlite3.c
+
+This file contains the implementation code for the cson
+sqlite3-to-JSON API.
+
+License: the same as the cson core library.
+
+Author: Stephan Beal (http://wanderinghorse.net/home/stephan)
+*/
+#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
+#include <assert.h>
+#include <string.h> /* strlen() */
+
+#if 0
+#include <stdio.h>
+#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
+#else
+#define MARKER if(0) printf
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col )
+{
+ if( ! st ) return NULL;
+ else
+ {
+#if 0
+ sqlite3_value * val = sqlite3_column_type(st,col);
+ int const vtype = val ? sqlite3_value_type(val) : -1;
+ if( ! val ) return cson_value_null();
+#else
+ int const vtype = sqlite3_column_type(st,col);
+#endif
+ switch( vtype )
+ {
+ case SQLITE_NULL:
+ return cson_value_null();
+ case SQLITE_INTEGER:
+ /* FIXME: for large integers fall back to Double instead. */
+ return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col) );
+ case SQLITE_FLOAT:
+ return cson_value_new_double( sqlite3_column_double(st, col) );
+ case SQLITE_BLOB: /* arguably fall through... */
+ case SQLITE_TEXT: {
+ char const * str = (char const *)sqlite3_column_text(st,col);
+ return cson_value_new_string(str, str ? strlen(str) : 0);
+ }
+ default:
+ return NULL;
+ }
+ }
+}
+
+cson_value * cson_sqlite3_column_names( sqlite3_stmt * st )
+{
+ cson_value * aryV = NULL;
+ cson_array * ary = NULL;
+ char const * colName = NULL;
+ int i = 0;
+ int rc = 0;
+ int colCount = 0;
+ assert(st);
+ colCount = sqlite3_column_count(st);
+ if( colCount <= 0 ) return NULL;
+
+ aryV = cson_value_new_array();
+ if( ! aryV ) return NULL;
+ ary = cson_value_get_array(aryV);
+ assert(ary);
+ for( i = 0; (0 ==rc) && (i < colCount); ++i )
+ {
+ colName = sqlite3_column_name( st, i );
+ if( ! colName ) rc = cson_rc.AllocError;
+ else
+ {
+ rc = cson_array_set( ary, (unsigned int)i,
+ cson_value_new_string(colName, strlen(colName)) );
+ }
+ }
+ if( 0 == rc ) return aryV;
+ else
+ {
+ cson_value_free(aryV);
+ return NULL;
+ }
+}
+
+
+cson_value * cson_sqlite3_row_to_object2( sqlite3_stmt * st,
+ cson_array * colNames )
+{
+ cson_value * rootV = NULL;
+ cson_object * root = NULL;
+ cson_string * colName = NULL;
+ int i = 0;
+ int rc = 0;
+ cson_value * currentValue = NULL;
+ int const colCount = sqlite3_column_count(st);
+ if( !colCount || (colCount>cson_array_length_get(colNames)) ) {
+ return NULL;
+ }
+ rootV = cson_value_new_object();
+ if(!rootV) return NULL;
+ root = cson_value_get_object(rootV);
+ for( i = 0; i < colCount; ++i )
+ {
+ colName = cson_value_get_string( cson_array_get( colNames, i ) );
+ if( ! colName ) goto error;
+ currentValue = cson_sqlite3_column_to_value(st,i);
+ if( ! currentValue ) currentValue = cson_value_null();
+ rc = cson_object_set_s( root, colName, currentValue );
+ if( 0 != rc )
+ {
+ cson_value_free( currentValue );
+ goto error;
+ }
+ }
+ goto end;
+ error:
+ cson_value_free( rootV );
+ rootV = NULL;
+ end:
+ return rootV;
+}
+
+
+cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st )
+{
+#if 0
+ cson_value * arV = cson_sqlite3_column_names(st);
+ cson_array * ar = NULL;
+ cson_value * rc = NULL;
+ if(!arV) return NULL;
+ ar = cson_value_get_array(arV);
+ assert( NULL != ar );
+ rc = cson_sqlite3_row_to_object2(st, ar);
+ cson_value_free(arV);
+ return rc;
+#else
+ cson_value * rootV = NULL;
+ cson_object * root = NULL;
+ char const * colName = NULL;
+ int i = 0;
+ int rc = 0;
+ cson_value * currentValue = NULL;
+ int const colCount = sqlite3_column_count(st);
+ if( !colCount ) return NULL;
+ rootV = cson_value_new_object();
+ if(!rootV) return NULL;
+ root = cson_value_get_object(rootV);
+ for( i = 0; i < colCount; ++i )
+ {
+ colName = sqlite3_column_name( st, i );
+ if( ! colName ) goto error;
+ currentValue = cson_sqlite3_column_to_value(st,i);
+ if( ! currentValue ) currentValue = cson_value_null();
+ rc = cson_object_set( root, colName, currentValue );
+ if( 0 != rc )
+ {
+ cson_value_free( currentValue );
+ goto error;
+ }
+ }
+ goto end;
+ error:
+ cson_value_free( rootV );
+ rootV = NULL;
+ end:
+ return rootV;
+#endif
+}
+
+cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st )
+{
+ cson_value * aryV = NULL;
+ cson_array * ary = NULL;
+ int i = 0;
+ int rc = 0;
+ int const colCount = sqlite3_column_count(st);
+ if( ! colCount ) return NULL;
+ aryV = cson_value_new_array();
+ if( ! aryV ) return NULL;
+ ary = cson_value_get_array(aryV);
+ rc = cson_array_reserve(ary, (unsigned int) colCount );
+ if( 0 != rc ) goto error;
+
+ for( i = 0; i < colCount; ++i ){
+ cson_value * elem = cson_sqlite3_column_to_value(st,i);
+ if( ! elem ) goto error;
+ rc = cson_array_append(ary,elem);
+ if(0!=rc)
+ {
+ cson_value_free( elem );
+ goto end;
+ }
+ }
+ goto end;
+ error:
+ cson_value_free(aryV);
+ aryV = NULL;
+ end:
+ return aryV;
+}
+
+
+/**
+ Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
+ parameter is non-0.
+*/
+static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
+{
+#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
+ if( ! tgt || !st ) return cson_rc.ArgError;
+ else
+ {
+ cson_value * rootV = NULL;
+ cson_object * root = NULL;
+ cson_value * colsV = NULL;
+ cson_array * cols = NULL;
+ cson_value * rowsV = NULL;
+ cson_array * rows = NULL;
+ cson_value * objV = NULL;
+ int rc = 0;
+ int const colCount = sqlite3_column_count(st);
+ if( colCount <= 0 ) return cson_rc.ArgError;
+ rootV = cson_value_new_object();
+ if( ! rootV ) return cson_rc.AllocError;
+ colsV = cson_sqlite3_column_names(st);
+ if( ! colsV )
+ {
+ cson_value_free( rootV );
+ RETURN(cson_rc.AllocError);
+ }
+ cols = cson_value_get_array(colsV);
+ assert(NULL != cols);
+ root = cson_value_get_object(rootV);
+ rc = cson_object_set( root, "columns", colsV );
+ if( rc )
+ {
+ cson_value_free( colsV );
+ RETURN(rc);
+ }
+ rowsV = cson_value_new_array();
+ if( ! rowsV ) RETURN(cson_rc.AllocError);
+ rc = cson_object_set( root, "rows", rowsV );
+ if( rc )
+ {
+ cson_value_free( rowsV );
+ RETURN(rc);
+ }
+ rows = cson_value_get_array(rowsV);
+ assert(rows);
+ while( SQLITE_ROW == sqlite3_step(st) )
+ {
+ objV = cson_sqlite3_row_to_object2(st, cols);
+ if( ! objV ) RETURN(cson_rc.UnknownError);
+ rc = cson_array_append( rows, objV );
+ if( rc )
+ {
+ cson_value_free( objV );
+ RETURN(rc);
+ }
+ }
+ *tgt = rootV;
+ return 0;
+ }
+#undef RETURN
+}
+
+/**
+ Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
+ parameter is 0.
+*/
+static int cson_sqlite3_stmt_to_json_slim( sqlite3_stmt * st, cson_value ** tgt )
+{
+#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
+ if( ! tgt || !st ) return cson_rc.ArgError;
+ else
+ {
+ cson_value * rootV = NULL;
+ cson_object * root = NULL;
+ cson_value * aryV = NULL;
+ cson_value * rowsV = NULL;
+ cson_array * rows = NULL;
+ int rc = 0;
+ int const colCount = sqlite3_column_count(st);
+ if( colCount <= 0 ) return cson_rc.ArgError;
+ rootV = cson_value_new_object();
+ if( ! rootV ) return cson_rc.AllocError;
+ aryV = cson_sqlite3_column_names(st);
+ if( ! aryV )
+ {
+ cson_value_free( rootV );
+ RETURN(cson_rc.AllocError);
+ }
+ root = cson_value_get_object(rootV);
+ rc = cson_object_set( root, "columns", aryV );
+ if( rc )
+ {
+ cson_value_free( aryV );
+ RETURN(rc);
+ }
+ aryV = NULL;
+ rowsV = cson_value_new_array();
+ if( ! rowsV ) RETURN(cson_rc.AllocError);
+ rc = cson_object_set( root, "rows", rowsV );
+ if( 0 != rc )
+ {
+ cson_value_free( rowsV );
+ RETURN(rc);
+ }
+ rows = cson_value_get_array(rowsV);
+ assert(rows);
+ while( SQLITE_ROW == sqlite3_step(st) )
+ {
+ aryV = cson_sqlite3_row_to_array(st);
+ if( ! aryV ) RETURN(cson_rc.UnknownError);
+ rc = cson_array_append( rows, aryV );
+ if( 0 != rc )
+ {
+ cson_value_free( aryV );
+ RETURN(rc);
+ }
+ }
+ *tgt = rootV;
+ return 0;
+ }
+#undef RETURN
+}
+
+int cson_sqlite3_stmt_to_json( sqlite3_stmt * st, cson_value ** tgt, char fat )
+{
+ return fat
+ ? cson_sqlite3_stmt_to_json_fat(st,tgt)
+ : cson_sqlite3_stmt_to_json_slim(st,tgt)
+ ;
+}
+
+int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, char fat )
+{
+ if( !db || !tgt || !sql || !*sql ) return cson_rc.ArgError;
+ else
+ {
+ sqlite3_stmt * st = NULL;
+ int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
+ if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
+ rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
+ sqlite3_finalize( st );
+ return rc;
+ }
+}
+
+int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
+{
+ int rc = 0;
+ char convertErr = 0;
+ if(!st) return cson_rc.ArgError;
+ else if( ndx < 1 ) {
+ rc = cson_rc.RangeError;
+ }
+ else if( cson_value_is_array(v) ){
+ cson_array * ar = cson_value_get_array(v);
+ unsigned int len = cson_array_length_get(ar);
+ unsigned int i;
+ assert(NULL != ar);
+ for( i = 0; !rc && (i < len); ++i ){
+ rc = cson_sqlite3_bind_value( st, (int)i+ndx,
+ cson_array_get(ar, i));
+ }
+ }
+ else if(!v || cson_value_is_null(v)){
+ rc = sqlite3_bind_null(st,ndx);
+ convertErr = 1;
+ }
+ else if( cson_value_is_double(v) ){
+ rc = sqlite3_bind_double( st, ndx, cson_value_get_double(v) );
+ convertErr = 1;
+ }
+ else if( cson_value_is_bool(v) ){
+ rc = sqlite3_bind_int( st, ndx, cson_value_get_bool(v) ? 1 : 0 );
+ convertErr = 1;
+ }
+ else if( cson_value_is_integer(v) ){
+ rc = sqlite3_bind_int64( st, ndx, cson_value_get_integer(v) );
+ convertErr = 1;
+ }
+ else if( cson_value_is_string(v) ){
+ cson_string const * s = cson_value_get_string(v);
+ rc = sqlite3_bind_text( st, ndx,
+ cson_string_cstr(s),
+ cson_string_length_bytes(s),
+ SQLITE_TRANSIENT);
+ convertErr = 1;
+ }
+ else {
+ rc = cson_rc.TypeError;
+ }
+ if(convertErr && rc) switch(rc){
+ case SQLITE_TOOBIG:
+ case SQLITE_RANGE: rc = cson_rc.RangeError; break;
+ case SQLITE_NOMEM: rc = cson_rc.AllocError; break;
+ case SQLITE_IOERR: rc = cson_rc.IOError; break;
+ default: rc = cson_rc.UnknownError; break;
+ };
+ return rc;
+}
+
+
+#if defined(__cplusplus)
+} /*extern "C"*/
+#endif
+#undef MARKER
+#endif /* CSON_ENABLE_SQLITE3 */
+/* end file ./cson_sqlite3.c */
+#endif /* FOSSIL_ENABLE_JSON */