(rpc) refresh our snapshot of Jean Gressmann's JSON parser

This commit is contained in:
Charles Kerr
2008-05-21 14:19:49 +00:00
parent c7b9c8c043
commit 8b31bd54cb
5 changed files with 278 additions and 199 deletions
-117
View File
@@ -1,117 +0,0 @@
#ifndef JSON_CHECKER_H
#define JSON_CHECKER_H
/* JSON_checker.h */
#include <stddef.h>
/* Windows DLL stuff */
#ifdef _WIN32
# ifdef JSON_CHECKER_DLL_EXPORTS
# define JSON_CHECKER_DLL_API __declspec(dllexport)
# else
# define JSON_CHECKER_DLL_API __declspec(dllimport)
# endif
#else
# define JSON_CHECKER_DLL_API
#endif
/* 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_CHECKER_INTEGER_SSCANF_TOKEN "%lld"
#define JSON_CHECKER_INTEGER_SPRINTF_TOKEN "%lld"
#else
typedef long JSON_int_t;
#define JSON_CHECKER_INTEGER_SSCANF_TOKEN "%ld"
#define JSON_CHECKER_INTEGER_SPRINTF_TOKEN "%ld"
#endif
#ifdef __cplusplus
extern "C" {
#endif
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_type;
typedef struct JSON_value_struct {
union {
JSON_int_t integer_value;
long double float_value;
struct {
const char* string_value;
size_t string_length;
};
};
} JSON_value;
/*! \brief JSON parser callback
\param ctx The pointer passed to new_JSON_checker.
\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.
\return Non-zero if parsing should continue, else zero.
*/
typedef int (JSON_checker_callback)(void* ctx, int type, const struct JSON_value_struct* value);
/*! \brief Create 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.
\param Callback context. This parameter may be NULL.
\return The parser object.
*/
JSON_CHECKER_DLL_API extern struct JSON_checker_struct* new_JSON_checker(int depth, JSON_checker_callback*, void* ctx, int allowComments);
/*! \brief Destroy a previously created JSON parser object. */
JSON_CHECKER_DLL_API extern void delete_JSON_checker(struct JSON_checker_struct* jc);
/*! \brief Parse a character.
\return Non-zero, if all characters passed to this function are part of are valid JSON.
*/
JSON_CHECKER_DLL_API extern int JSON_checker_char(struct JSON_checker_struct* 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_CHECKER_DLL_API extern int JSON_checker_done(struct JSON_checker_struct* jc);
/*! \brief Determine if a given string is valid JSON white space
\return Non-zero if the string is valid, zero otherwise.
*/
JSON_CHECKER_DLL_API extern int JSON_checker_is_legal_white_space_string(const char* s);
#ifdef __cplusplus
}
#endif
#endif /* JSON_CHECKER_H */
@@ -1,4 +1,4 @@
/* JSON_checker.c */
/* JSON_parser.c */
/* 2007-08-24 */
@@ -27,7 +27,7 @@ SOFTWARE.
*/
/*
Callbacks, comments, and Unicode handling by Jean Gressmann (jean@0x42.de), 2007.
Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2008.
For the added features the license above applies also.
*/
@@ -42,7 +42,7 @@ SOFTWARE.
#include <stdlib.h>
#include <string.h>
#include "JSON_checker.h"
#include "JSON_parser.h"
#include "ConvertUTF.h"
#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
@@ -55,31 +55,31 @@ SOFTWARE.
#define __ -1 /* the universal error code */
/* values chosen so that the object approx. fits into a page (4K) */
#ifndef JSON_CHECKER_STACK_SIZE
# define JSON_CHECKER_STACK_SIZE 128
#ifndef JSON_PARSER_STACK_SIZE
# define JSON_PARSER_STACK_SIZE 128
#endif
#ifndef JSON_CHECKER_PARSE_BUFFER_SIZE
# define JSON_CHECKER_PARSE_BUFFER_SIZE 3500
#ifndef JSON_PARSER_PARSE_BUFFER_SIZE
# define JSON_PARSER_PARSE_BUFFER_SIZE 3500
#endif
typedef struct JSON_checker_struct {
JSON_checker_callback* callback;
typedef struct JSON_parser_struct {
JSON_parser_callback callback;
void* ctx;
signed char state, before_comment_state, type, escaped, comment, allow_comments;
signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually;
UTF16 utf16_decode_buffer[2];
long depth;
long top;
signed char* stack;
long stack_capacity;
signed char static_stack[JSON_CHECKER_STACK_SIZE];
signed char static_stack[JSON_PARSER_STACK_SIZE];
char* parse_buffer;
size_t parse_buffer_capacity;
size_t parse_buffer_count;
size_t comment_begin_offset;
char static_parse_buffer[JSON_CHECKER_PARSE_BUFFER_SIZE];
} * JSON_checker;
char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE];
} * JSON_parser;
#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
@@ -274,7 +274,7 @@ enum modes {
};
static int
push(JSON_checker jc, int mode)
push(JSON_parser jc, int mode)
{
/*
Push a mode onto the stack. Return false if there is overflow.
@@ -302,7 +302,7 @@ push(JSON_checker jc, int mode)
static int
pop(JSON_checker jc, int mode)
pop(JSON_parser jc, int mode)
{
/*
Pop the stack, assuring that the current mode matches the expectation.
@@ -315,13 +315,21 @@ pop(JSON_checker jc, int mode)
return true;
}
static void parse_buffer_clear(JSON_checker jc)
{
jc->parse_buffer_count = 0;
jc->parse_buffer[0] = 0;
}
void delete_JSON_checker(JSON_checker jc)
#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]) {
@@ -334,20 +342,37 @@ void delete_JSON_checker(JSON_checker jc)
}
}
JSON_checker
new_JSON_checker(int depth, JSON_checker_callback* callback, void* ctx, int allowComments)
JSON_parser
new_JSON_parser(JSON_config* config)
{
/*
new_JSON_checker starts the checking process by constructing a JSON_checker
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_checker_char for each character in the
JSON text, and then call JSON_checker_done to obtain the final result.
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.
*/
JSON_checker jc = (JSON_checker)malloc(sizeof(struct JSON_checker_struct));
memset(jc, 0, sizeof(struct JSON_checker_struct));
int depth = 0;
JSON_config default_config;
JSON_parser jc = malloc(sizeof(struct JSON_parser_struct));
memset(jc, 0, sizeof(*jc));
/* initialize configuration */
init_JSON_config(&default_config);
/* set to default configuration if none was provided */
if (config == NULL) {
config = &default_config;
}
depth = config->depth;
/* We need to be able to push at least one object */
if (depth == 0) {
@@ -356,6 +381,8 @@ new_JSON_checker(int depth, JSON_checker_callback* callback, void* ctx, int allo
jc->state = GO;
jc->top = -1;
/* Do we want non-bound stack? */
if (depth > 0) {
jc->stack_capacity = depth;
jc->depth = depth;
@@ -369,42 +396,43 @@ new_JSON_checker(int depth, JSON_checker_callback* callback, void* ctx, int allo
jc->depth = -1;
jc->stack = &jc->static_stack[0];
}
/* set parser to start */
push(jc, MODE_DONE);
/* set up the parse buffer */
jc->parse_buffer = &jc->static_parse_buffer[0];
jc->parse_buffer_capacity = COUNTOF(jc->static_parse_buffer);
parse_buffer_clear(jc);
jc->callback = callback;
jc->ctx = ctx;
jc->allow_comments = allowComments != 0;
/* set up callback, comment & float handling */
jc->callback = config->callback;
jc->ctx = config->callback_ctx;
jc->allow_comments = config->allow_comments != 0;
jc->handle_floats_manually = config->handle_floats_manually != 0;
return jc;
}
static void parse_buffer_push_back_char(JSON_checker jc, char c)
static void grow_parse_buffer(JSON_parser jc)
{
if (jc->parse_buffer_count + 1 >= jc->parse_buffer_capacity) {
jc->parse_buffer_capacity *= 2;
if (jc->parse_buffer == &jc->static_parse_buffer[0]) {
jc->parse_buffer = (char*)malloc(jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
memcpy( jc->parse_buffer, jc->static_parse_buffer, jc->parse_buffer_count );
} else {
jc->parse_buffer = (char*)realloc(jc->parse_buffer, jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
}
jc->parse_buffer_capacity *= 2;
if (jc->parse_buffer == &jc->static_parse_buffer[0]) {
jc->parse_buffer = (char*)malloc(jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
memcpy(jc->parse_buffer, jc->static_parse_buffer, jc->parse_buffer_count);
} else {
jc->parse_buffer = (char*)realloc(jc->parse_buffer, jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
}
jc->parse_buffer[jc->parse_buffer_count++] = c;
jc->parse_buffer[jc->parse_buffer_count] = 0;
}
static void parse_buffer_pop_back_chars(JSON_checker jc, size_t count)
{
assert(jc->parse_buffer_count >= count);
jc->parse_buffer_count -= count;
jc->parse_buffer[jc->parse_buffer_count] = 0;
}
#define parse_buffer_push_back_char(jc, c)\
do {\
if (jc->parse_buffer_count + 1 >= jc->parse_buffer_capacity) grow_parse_buffer(jc);\
jc->parse_buffer[jc->parse_buffer_count++] = c;\
jc->parse_buffer[jc->parse_buffer_count] = 0;\
} while (0)
static int parse_parse_buffer(JSON_checker jc)
static int parse_parse_buffer(JSON_parser jc)
{
if (jc->callback) {
int result = 1;
@@ -423,11 +451,16 @@ static int parse_parse_buffer(JSON_checker jc)
switch(jc->type) {
case JSON_T_FLOAT:
arg = &value;
result = sscanf(jc->parse_buffer, "%Lf", &value.float_value);
if (jc->handle_floats_manually) {
value.string_value = jc->parse_buffer;
value.string_length = jc->parse_buffer_count;
} else {
result = sscanf(jc->parse_buffer, "%Lf", &value.float_value);
}
break;
case JSON_T_INTEGER:
arg = &value;
result = sscanf(jc->parse_buffer, JSON_CHECKER_INTEGER_SSCANF_TOKEN, &value.integer_value);
result = sscanf(jc->parse_buffer, JSON_PARSER_INTEGER_SSCANF_TOKEN, &value.integer_value);
break;
case JSON_T_STRING:
arg = &value;
@@ -442,13 +475,12 @@ static int parse_parse_buffer(JSON_checker jc)
}
}
jc->parse_buffer_count = 0;
jc->parse_buffer[0] = 0;
parse_buffer_clear(jc);
return true;
}
static int decode_unicode_char(JSON_checker jc)
static int decode_unicode_char(JSON_parser jc)
{
const unsigned chars = jc->utf16_decode_buffer[0] ? 2 : 1;
int i;
@@ -518,13 +550,13 @@ static int decode_unicode_char(JSON_checker jc)
int
JSON_checker_char(JSON_checker jc, int next_char)
JSON_parser_char(JSON_parser jc, int next_char)
{
/*
After calling new_JSON_checker, call this function for each character (or
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 deletes the JSON_checker object and returns false.
text, it returns false.
*/
int next_class, next_state;
@@ -546,7 +578,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
if (jc->escaped) {
jc->escaped = 0;
/* remove the backslash */
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
switch(next_char) {
case 'b':
parse_buffer_push_back_char(jc, '\b');
@@ -693,7 +725,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
if (!jc->allow_comments) {
return false;
}
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (!parse_parse_buffer(jc)) {
return false;
}
@@ -721,11 +753,8 @@ JSON_checker_char(JSON_checker jc, int next_char)
jc->comment = 1;
break;
/* empty } */
case -9:
parse_buffer_pop_back_chars(jc, 1);
if (!parse_parse_buffer(jc)) {
return false;
}
case -9:
parse_buffer_clear(jc);
if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) {
return false;
}
@@ -736,7 +765,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* } */ case -8:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (!parse_parse_buffer(jc)) {
return false;
}
@@ -751,7 +780,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* ] */ case -7:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (!parse_parse_buffer(jc)) {
return false;
}
@@ -767,7 +796,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* { */ case -6:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_BEGIN, NULL)) {
return false;
}
@@ -779,7 +808,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* [ */ case -5:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_BEGIN, NULL)) {
return false;
}
@@ -791,7 +820,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* string end " */ case -4:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
switch (jc->stack[jc->top]) {
case MODE_KEY:
assert(jc->type == JSON_T_STRING);
@@ -823,7 +852,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
break;
/* , */ case -3:
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (!parse_parse_buffer(jc)) {
return false;
}
@@ -853,7 +882,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
/*
A colon causes a flip from key mode to object mode.
*/
parse_buffer_pop_back_chars(jc, 1);
parse_buffer_pop_back_char(jc);
if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) {
return false;
}
@@ -872,7 +901,7 @@ JSON_checker_char(JSON_checker jc, int next_char)
int
JSON_checker_done(JSON_checker jc)
JSON_parser_done(JSON_parser jc)
{
const int result = jc->state == OK && pop(jc, MODE_DONE);
@@ -880,7 +909,7 @@ JSON_checker_done(JSON_checker jc)
}
int JSON_checker_is_legal_white_space_string(const char* s)
int JSON_parser_is_legal_white_space_string(const char* s)
{
int c, char_class;
@@ -904,3 +933,14 @@ int JSON_checker_is_legal_white_space_string(const char* s)
return true;
}
void init_JSON_config(JSON_config* config)
{
if (config) {
memset(config, 0, sizeof(*config));
config->depth = JSON_PARSER_STACK_SIZE - 1;
}
}
+150
View File
@@ -0,0 +1,150 @@
#ifndef JSON_PARSER_H
#define JSON_PARSER_H
/* JSON_parser.h */
#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
/* 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
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;
long double float_value;
struct {
const char* string_value;
size_t string_length;
};
};
} JSON_value;
/*! \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
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);
/*! \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 Callback context. This parameter may be NULL.
\param allowComments. To allow C style comments in JSON, set to non-zero.
\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.
\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;
} 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
\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
\return The parser object.
*/
JSON_PARSER_DLL_API extern struct JSON_parser_struct* 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);
/*! \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);
/*! \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);
/*! \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);
#ifdef __cplusplus
}
#endif
#endif /* JSON_PARSER_H */
+2 -2
View File
@@ -16,7 +16,7 @@ libtransmission_a_SOURCES = \
handshake.c \
inout.c \
json.c \
JSON_checker.c \
JSON_parser.c \
list.c \
makemeta.c \
metainfo.c \
@@ -57,7 +57,7 @@ noinst_HEADERS = \
handshake.h \
inout.h \
json.h \
JSON_checker.h \
JSON_parser.h \
list.h \
makemeta.h \
metainfo.h \
+11 -5
View File
@@ -17,7 +17,7 @@
#include <event.h> /* evbuffer */
#include "JSON_checker.h"
#include "JSON_parser.h"
#include "transmission.h"
#include "bencode.h"
@@ -127,15 +127,21 @@ tr_jsonParse( const void * vbuf,
int err = 0;
const char * buf = vbuf;
const void * bufend = buf + len;
struct JSON_checker_struct * checker;
struct JSON_config_struct config;
struct JSON_parser_struct * checker;
struct json_benc_data data;
init_JSON_config( &config );
config.callback = callback;
config.callback_ctx = &data;
config.depth = -1;
data.key = NULL;
data.top = setme_benc;
data.stack = tr_ptrArrayNew( );
checker = new_JSON_checker( -1, callback, &data, 0 );
while( ( buf != bufend ) && JSON_checker_char( checker, *buf ) )
checker = new_JSON_parser( &config );
while( ( buf != bufend ) && JSON_parser_char( checker, *buf ) )
++buf;
if( buf != bufend )
err = TR_ERROR;
@@ -143,7 +149,7 @@ tr_jsonParse( const void * vbuf,
if( setme_end )
*setme_end = (const uint8_t*) buf;
delete_JSON_checker( checker );
delete_JSON_parser( checker );
tr_ptrArrayFree( data.stack, NULL );
return err;
}