diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /extsrc/cson_amalgamation.c | |
| download | relic-master.tar.gz | |
Diffstat (limited to 'extsrc/cson_amalgamation.c')
| -rw-r--r-- | extsrc/cson_amalgamation.c | 5712 |
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 */ |
