From f3868f0775d9651585be2ff9a5df148cbadb6981 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 15 May 2009 13:16:34 +0000 Subject: [PATCH] (trunk libT) update our copy of Jean's JSON parser --- libtransmission/JSON_parser.c | 187 ++++++++++++++++++++-------------- libtransmission/JSON_parser.h | 172 +++++++++++++------------------ libtransmission/json.c | 2 +- 3 files changed, 182 insertions(+), 179 deletions(-) diff --git a/libtransmission/JSON_parser.c b/libtransmission/JSON_parser.c index 284f3f693..cc1e5a36e 100644 --- a/libtransmission/JSON_parser.c +++ b/libtransmission/JSON_parser.c @@ -27,26 +27,30 @@ SOFTWARE. */ /* - Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2008. + Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2009. For the added features the license above applies also. Changelog: - 2008/10/14 - - Renamed states.IN to states.IT to avoid name clash which IN macro - defined in windef.h (alexey.pelykh@gmail.com) + 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/07/19 - - Removed some duplicate code & debugging variable (Charles.Kerr@noaa.gov) + 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-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.Kerr@noaa.gov where the switching - from static to dynamic parse buffer did not copy the static parse - buffer's content. + 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. */ @@ -54,19 +58,19 @@ SOFTWARE. #include #include #include -#include #include #include #include #include +#include #include "JSON_parser.h" #include "ConvertUTF.h" #ifdef _MSC_VER - #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ - #pragma warning(disable:4996) /* unsecure sscanf */ - #endif +# if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ +# pragma warning(disable:4996) // unsecure sscanf +# endif #endif @@ -84,7 +88,7 @@ SOFTWARE. #endif -typedef struct JSON_parser_struct { +struct JSON_parser_struct { JSON_parser_callback callback; void* ctx; signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually; @@ -93,18 +97,19 @@ typedef struct JSON_parser_struct { long top; signed char* stack; long stack_capacity; - signed char static_stack[JSON_PARSER_STACK_SIZE]; + char decimal_point; char* parse_buffer; size_t parse_buffer_capacity; size_t parse_buffer_count; size_t comment_begin_offset; + signed char static_stack[JSON_PARSER_STACK_SIZE]; char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE]; -} * JSON_parser; +}; #define COUNTOF(x) (sizeof(x)/sizeof(x[0])) /* - Characters are mapped into these 31 character classes. This allows for + Characters are mapped into these character classes. This allows for a significant reduction in the size of the state transition table. */ @@ -432,6 +437,10 @@ new_JSON_parser(JSON_config* config) jc->ctx = config->callback_ctx; jc->allow_comments = config->allow_comments != 0; jc->handle_floats_manually = config->handle_floats_manually != 0; + + /* set up decimal point */ + jc->decimal_point = *localeconv()->decimal_point; + return jc; } @@ -455,6 +464,15 @@ static void grow_parse_buffer(JSON_parser jc) 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) { @@ -462,13 +480,7 @@ static int parse_parse_buffer(JSON_parser jc) JSON_value value, *arg = NULL; if (jc->type != JSON_T_NONE) { - 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); + assert_is_non_container_type(jc); switch(jc->type) { case JSON_T_FLOAT: @@ -477,13 +489,10 @@ static int parse_parse_buffer(JSON_parser jc) value.vu.str.value = jc->parse_buffer; value.vu.str.length = jc->parse_buffer_count; } else { - /* the json spec requires a '.' decimal point regardless of locale */ - char numeric[128]; - snprintf(numeric, sizeof(numeric), "%s", setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, "POSIX" ); - sscanf(jc->parse_buffer, "%Lf", &value.vu.float_value); - value.vu.float_value = strtod(jc->parse_buffer, NULL); - setlocale(LC_NUMERIC, numeric); + /*sscanf(jc->parse_buffer, "%Lf", &value.vu.float_value);*/ + + /* not checking with end pointer b/c there may be trailing ws */ + value.vu.float_value = strtold(jc->parse_buffer, NULL); } break; case JSON_T_INTEGER: @@ -576,38 +585,12 @@ static int decode_unicode_char(JSON_parser jc) return true; } - -int -JSON_parser_char(JSON_parser jc, int next_char) +static int add_escaped_char_to_parse_buffer(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; - -/* - Determine the character's class. -*/ - if (next_char < 0) { - return false; - } - if (next_char >= 128) { - next_class = C_ETC; - } else { - next_class = ascii_class[next_char]; - if (next_class <= __) { - return false; - } - } - - if (jc->escaped) { - jc->escaped = 0; - /* remove the backslash */ - parse_buffer_pop_back_char(jc); - switch(next_char) { + 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; @@ -638,14 +621,58 @@ JSON_parser_char(JSON_parser jc, int next_char) break; default: 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 add_char_to_parse_buffer(jc, next_char, next_class) \ + do { \ + if (jc->escaped) { \ + if (!add_escaped_char_to_parse_buffer(jc, next_char)) \ + 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); \ + } \ + } \ + } while (0) + + +#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; + +/* + Determine the character's class. +*/ + if (next_char < 0) { + return false; + } + if (next_char >= 128) { + next_class = C_ETC; + } else { + next_class = ascii_class[next_char]; + if (next_class <= __) { + return false; } } - + add_char_to_parse_buffer(jc, next_char, next_class); /* Get the next state from the state transition table. @@ -696,20 +723,22 @@ JSON_parser_char(JSON_parser jc, int next_char) /* floating point number detected by exponent*/ case DE: - assert(jc->type != JSON_T_FALSE); - assert(jc->type != JSON_T_TRUE); - assert(jc->type != JSON_T_NULL); - assert(jc->type != JSON_T_STRING); + 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(jc->type != JSON_T_FALSE); - assert(jc->type != JSON_T_TRUE); - assert(jc->type != JSON_T_NULL); - assert(jc->type != JSON_T_STRING); + 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; diff --git a/libtransmission/JSON_parser.h b/libtransmission/JSON_parser.h index 37ce5dce5..3780aae56 100644 --- a/libtransmission/JSON_parser.h +++ b/libtransmission/JSON_parser.h @@ -1,7 +1,3 @@ -#ifndef __TRANSMISSION__ -#error only libtransmission should #include this header. -#endif - #ifndef JSON_PARSER_H #define JSON_PARSER_H @@ -11,31 +7,33 @@ #include /* Windows DLL stuff */ -/* - #ifdef _WIN32 - # 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 - */ +#ifdef _WIN32 +# 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 -#include -typedef int64_t JSON_int_t; -#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%" PRId64 -#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%" PRId64 +/* Determine the integer type use to parse non-floating point numbers */ +#if __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 +#endif -typedef enum +typedef enum { JSON_T_NONE = 0, JSON_T_ARRAY_BEGIN, @@ -52,127 +50,103 @@ typedef enum JSON_T_MAX } JSON_type; -typedef struct JSON_value_struct -{ - union - { - JSON_int_t integer_value; - +typedef struct JSON_value_struct { + union { + JSON_int_t integer_value; + long double float_value; - - struct - { + + struct { const char* value; - size_t length; + size_t length; } str; } vu; } JSON_value; -/*! \brief JSON parser callback +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 SON_T_FALSE. String values are always - returned + \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 SON_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 struct JSON_value_struct* - value ); +*/ +typedef int (*JSON_parser_callback)(void* ctx, int type, const struct JSON_value_struct* value); -/*! \brief The structure used to configure a JSON parser object - - \param depth If negative, the parser can parse arbitrary levels of JSON, - otherwise +/*! \brief The structure used to configure a JSON parser object + + \param depth If negative, the parser can parse arbitrary levels of JSON, otherwise the depth is the limit - \param Pointer to a callback. This parameter may be NULL. In this case the - input is merely checked for validity. + \param Pointer to a callback. This parameter may be NULL. In this case the input is merely checked for validity. \param Callback context. This parameter may be NULL. - \param depth. Specifies the levels of nested JSON to allow. Negative numbers - yield unlimited nesting. + \param depth. Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting. \param allowComments. To allow C style comments in JSON, set to non-zero. - \param handleFloatsManually. To decode floating point numbers manually set - this parameter to non-zero. - + \param handleFloatsManually. To decode floating point numbers manually set this parameter to non-zero. + \return The parser object. - */ -typedef struct JSON_config_struct -{ - JSON_parser_callback callback; - void* callback_ctx; - int depth; - int allow_comments; - int handle_floats_manually; +*/ +typedef struct { + JSON_parser_callback callback; + void* callback_ctx; + int depth; + int allow_comments; + int handle_floats_manually; } JSON_config; -/*! \brief Initializes the JSON parser configuration structure to default - values. +/*! \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) + - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c) - no parsing, just checking for JSON syntax - no comments \param config. Used to configure the parser. - */ -JSON_PARSER_DLL_API void init_JSON_config( - JSON_config* config ); +*/ +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. +/*! \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 - + \return The parser object. - */ -JSON_PARSER_DLL_API extern struct JSON_parser_struct* new_JSON_parser( - JSON_config* config ); +*/ +JSON_PARSER_DLL_API extern JSON_parser new_JSON_parser(JSON_config* config); /*! \brief Destroy a previously created JSON parser object. */ -JSON_PARSER_DLL_API extern void delete_JSON_parser( - struct JSON_parser_struct* jc ); +JSON_PARSER_DLL_API extern 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 extern int JSON_parser_char( - struct JSON_parser_struct* jc, - int - next_char ); + \return Non-zero, if all characters passed to this function are part of are valid JSON. +*/ +JSON_PARSER_DLL_API extern 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 extern int JSON_parser_done( - struct JSON_parser_struct* jc ); +*/ +JSON_PARSER_DLL_API extern int JSON_parser_done(JSON_parser jc); -/*! \brief Determine if a given string is valid JSON white space +/*! \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 extern int - JSON_parser_is_legal_white_space_string( - const char* s ); +*/ +JSON_PARSER_DLL_API extern int JSON_parser_is_legal_white_space_string(const char* s); #ifdef __cplusplus } -#endif - +#endif + #endif /* JSON_PARSER_H */ diff --git a/libtransmission/json.c b/libtransmission/json.c index 266fe6edc..2e62a5cfd 100644 --- a/libtransmission/json.c +++ b/libtransmission/json.c @@ -144,7 +144,7 @@ tr_jsonParse( const void * vbuf, int err = 0; const unsigned char * buf = vbuf; const void * bufend = buf + len; - struct JSON_config_struct config; + JSON_config config; struct JSON_parser_struct * checker; struct json_benc_data data;