(trunk libT) update our copy of Jean's JSON parser

This commit is contained in:
Charles Kerr
2009-05-15 13:16:34 +00:00
parent 74a4c2f2d5
commit f3868f0775
3 changed files with 182 additions and 179 deletions
+108 -79
View File
@@ -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 <assert.h>
#include <ctype.h>
#include <float.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#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;
+73 -99
View File
@@ -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 <stddef.h>
/* 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 <inttypes.h>
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 */
+1 -1
View File
@@ -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;