diff options
author | Lloyd Hilaiel <lloyd@hilaiel.com> | 2011-04-21 09:36:26 -0600 |
---|---|---|
committer | Lloyd Hilaiel <lloyd@hilaiel.com> | 2011-04-21 13:55:45 -0600 |
commit | 0858bb341280956947df1cfae33eb5ac6ecfe6f7 (patch) | |
tree | 2f61e004f7a892f00f6f34d38a8a194618400b59 | |
parent | 6aefdaab38d81f33c46e33353c5a156884ea022d (diff) | |
download | yajl-0858bb341280956947df1cfae33eb5ac6ecfe6f7.tar.gz |
rework programmatic configuration of yajl. both generator and parser now have a yajl_XXX_config() function that accepts varargs so that configuration is simple, and new config options can be added in the future that preserve backwards binary compatibility. closes #23.
-rw-r--r-- | reformatter/json_reformat.c | 65 | ||||
-rw-r--r-- | src/api/yajl_gen.h | 61 | ||||
-rw-r--r-- | src/api/yajl_parse.h | 89 | ||||
-rw-r--r-- | src/yajl.c | 75 | ||||
-rw-r--r-- | src/yajl_gen.c | 106 | ||||
-rw-r--r-- | src/yajl_parser.c | 8 | ||||
-rw-r--r-- | src/yajl_parser.h | 4 | ||||
-rw-r--r-- | test/cases/ac_difficult_json_c_test_case_with_comments.json (renamed from test/cases/difficult_json_c_test_case_with_comments.json) | 0 | ||||
-rw-r--r-- | test/cases/ac_difficult_json_c_test_case_with_comments.json.gold (renamed from test/cases/difficult_json_c_test_case_with_comments.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ac_simple_with_comments.json (renamed from test/cases/dc_simple_with_comments.json) | 0 | ||||
-rw-r--r-- | test/cases/ac_simple_with_comments.json.gold | 9 | ||||
-rw-r--r-- | test/cases/ag_false_then_garbage.json (renamed from test/cases/false_then_garbage.json) | 0 | ||||
-rw-r--r-- | test/cases/ag_false_then_garbage.json.gold (renamed from test/cases/false_then_garbage.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ag_null_then_garbage.json (renamed from test/cases/null_then_garbage.json) | 0 | ||||
-rw-r--r-- | test/cases/ag_null_then_garbage.json.gold (renamed from test/cases/null_then_garbage.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ag_true_then_garbage.json (renamed from test/cases/true_then_garbage.json) | 0 | ||||
-rw-r--r-- | test/cases/ag_true_then_garbage.json.gold (renamed from test/cases/true_then_garbage.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ap_array_open.json (renamed from test/cases/array_open.json) | 0 | ||||
-rw-r--r-- | test/cases/ap_array_open.json.gold (renamed from test/cases/array_open.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ap_eof_str.json (renamed from test/cases/eof_str.json) | 0 | ||||
-rw-r--r-- | test/cases/ap_eof_str.json.gold (renamed from test/cases/eof_str.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ap_map_open.json (renamed from test/cases/map_open.json) | 0 | ||||
-rw-r--r-- | test/cases/ap_map_open.json.gold (renamed from test/cases/map_open.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/ap_partial_ok.json (renamed from test/cases/partial_ok.json) | 0 | ||||
-rw-r--r-- | test/cases/ap_partial_ok.json.gold (renamed from test/cases/partial_ok.json.gold) | 0 | ||||
-rw-r--r-- | test/cases/dc_simple_with_comments.json.gold | 5 | ||||
-rw-r--r-- | test/cases/multiple.json.gold | 1 | ||||
-rw-r--r-- | test/cases/simple_with_comments.json.gold | 6 | ||||
-rwxr-xr-x | test/run_tests.sh | 43 | ||||
-rw-r--r-- | test/yajl_test.c | 25 | ||||
-rw-r--r-- | verify/json_verify.c | 51 |
31 files changed, 290 insertions, 258 deletions
diff --git a/reformatter/json_reformat.c b/reformatter/json_reformat.c index 070ca46..ddcab01 100644 --- a/reformatter/json_reformat.c +++ b/reformatter/json_reformat.c @@ -119,13 +119,18 @@ main(int argc, char ** argv) yajl_handle hand; static unsigned char fileData[65536]; /* generator config */ - yajl_gen_config conf = { 1, " " }; - yajl_gen g; + yajl_gen g; yajl_status stat; size_t rd; - /* allow comments */ - yajl_parser_config cfg = { 1, 1 }; - int retval = 0, done = 0; + int retval = 0; + + g = yajl_gen_alloc(NULL); + yajl_gen_config(g, yajl_gen_beautify, 1); + + /* ok. open file. let's read and parse */ + hand = yajl_alloc(&callbacks, NULL, (void *) g); + /* and let's allow comments by default */ + yajl_config(hand, yajl_allow_comments, 1); /* check arguments.*/ int a = 1; @@ -134,10 +139,10 @@ main(int argc, char ** argv) for ( i=1; i < strlen(argv[a]); i++) { switch (argv[a][i]) { case 'm': - conf.beautify = 0; + yajl_gen_config(g, yajl_gen_beautify, 0); break; case 'u': - cfg.checkUTF8 = 0; + yajl_config(hand, yajl_dont_validate_strings, 1); break; default: fprintf(stderr, "unrecognized option: '%c'\n\n", argv[a][i]); @@ -149,41 +154,26 @@ main(int argc, char ** argv) if (a < argc) { usage(argv[0]); } - - g = yajl_gen_alloc(&conf, NULL); - /* ok. open file. let's read and parse */ - hand = yajl_alloc(&callbacks, &cfg, NULL, (void *) g); - - while (!done) { + + + for (;;) { rd = fread((void *) fileData, 1, sizeof(fileData) - 1, stdin); - + if (rd == 0) { if (!feof(stdin)) { fprintf(stderr, "error on file read.\n"); retval = 1; - break; } - done = 1; + break; } fileData[rd] = 0; - - if (done) - /* parse any remaining buffered data */ - stat = yajl_parse_complete(hand); - else - /* read file data, pass to parser */ - stat = yajl_parse(hand, fileData, rd); - - if (stat != yajl_status_ok && - stat != yajl_status_insufficient_data) + + stat = yajl_parse(hand, fileData, rd); + + if (stat != yajl_status_ok) break; + { - unsigned char * str = yajl_get_error(hand, 1, fileData, rd); - fprintf(stderr, "%s", (const char *) str); - yajl_free_error(hand, str); - retval = 1; - break; - } else { const unsigned char * buf; size_t len; yajl_gen_get_buf(g, &buf, &len); @@ -192,8 +182,17 @@ main(int argc, char ** argv) } } + stat = yajl_parse_complete(hand); + + if (stat != yajl_status_ok) { + unsigned char * str = yajl_get_error(hand, 1, fileData, rd); + fprintf(stderr, "%s", (const char *) str); + yajl_free_error(hand, str); + retval = 1; + } + yajl_gen_free(g); yajl_free(hand); - + return retval; } diff --git a/src/api/yajl_gen.h b/src/api/yajl_gen.h index 4d229ec..b7c475f 100644 --- a/src/api/yajl_gen.h +++ b/src/api/yajl_gen.h @@ -60,15 +60,35 @@ extern "C" { const char * str, size_t len); - /** configuration structure for the generator */ - typedef struct { + /** configuration parameters for the parser, these may be passed to + * yajl_gen_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { /** generate indented (beautiful) output */ - unsigned int beautify; - /** an opportunity to define an indent string. such as \\t or - * some number of spaces. default is four spaces ' '. This - * member is only relevant when beautify is true */ - const char * indentString; - } yajl_gen_config; + yajl_gen_beautify = 0x01, + /** + * Set an indent string which is used when yajl_gen_beautify + * is enabled. Maybe something like \\t or some number of + * spaces. The default is four spaces ' '. + */ + yajl_gen_indent_string = 0x02, + /** + * Set a function and context argument that should be used to + * output generated json. the function should conform to the + * yajl_print_t prototype while the context argument is a + * void * of your choosing. + * + * example: + * yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr); + */ + yajl_gen_print_callback = 0x04 + } yajl_gen_option; + + /** allow the modification of generator options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise + */ + YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...); /** allocate a generator handle * \param config a pointer to a structure containing parameters which @@ -80,30 +100,7 @@ extern "C" { * * \returns an allocated handle on success, NULL on failure (bad params) */ - YAJL_API yajl_gen yajl_gen_alloc(const yajl_gen_config * config, - const yajl_alloc_funcs * allocFuncs); - - /** allocate a generator handle that will print to the specified - * callback rather than storing the results in an internal buffer. - * \param callback a pointer to a printer function. May be NULL - * in which case, the results will be store in an - * internal buffer. - * \param config a pointer to a structure containing parameters - * which configure the behavior of the json - * generator. - * \param allocFuncs an optional pointer to a structure which allows - * the client to overide the memory allocation - * used by yajl. May be NULL, in which case - * malloc/free/realloc will be used. - * \param ctx a context pointer that will be passed to the - * printer callback. - * - * \returns an allocated handle on success, NULL on failure (bad params) - */ - YAJL_API yajl_gen yajl_gen_alloc2(const yajl_print_t callback, - const yajl_gen_config * config, - const yajl_alloc_funcs * allocFuncs, - void * ctx); + YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs); /** free a generator handle */ YAJL_API void yajl_gen_free(yajl_gen handle); diff --git a/src/api/yajl_parse.h b/src/api/yajl_parse.h index 9c85496..0b868db 100644 --- a/src/api/yajl_parse.h +++ b/src/api/yajl_parse.h @@ -35,8 +35,6 @@ extern "C" { yajl_status_ok, /** a client callback returned zero, stopping the parse */ yajl_status_client_canceled, - /** not returned. here for cmpatability */ - yajl_status_insufficient_data, /** An error occured during the parse. Call yajl_get_error for * more information about the encountered error */ yajl_status_error @@ -93,49 +91,74 @@ extern "C" { int (* yajl_end_array)(void * ctx); } yajl_callbacks; - /** configuration structure for the generator */ - typedef struct { - /** if nonzero, javascript style comments will be allowed in - * the json input, both slash star and slash slash */ - unsigned int allowComments; - /** if nonzero, invalid UTF8 strings will cause a parse - * error */ - unsigned int checkUTF8; - } yajl_parser_config; - /** allocate a parser handle * \param callbacks a yajl callbacks structure specifying the * functions to call when different JSON entities * are encountered in the input text. May be NULL, * which is only useful for validation. - * \param config configuration parameters for the parse. + * \param afs memory allocation functions, may be NULL for to use + * C runtime library routines (malloc and friends) * \param ctx a context pointer that will be passed to callbacks. */ YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, - const yajl_parser_config * config, - const yajl_alloc_funcs * allocFuncs, + yajl_alloc_funcs * afs, void * ctx); - /** - * Forbid trailing garbage from following a JSON document. - * Whitespace is not considered garbage. - */ - YAJL_API void yajl_forbid_trailing_garbage(yajl_handle h); - - /** - * Allow multiple values to be parsed by a single handle. - * The entire text must be valid JSON, and values can be seperated - * by any kind of whitespace. - */ - YAJL_API void yajl_allow_multiple_values(yajl_handle h); - - /** - * Setting this flag causes the handle to enter an error - * state if yajl_parse_complete is called in the middle of - * a value. + /** configuration parameters for the parser, these may be passed to + * yajl_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { + /** Ignore javascript style comments present in + * JSON input. Non-standard, but rather fun + * arguments: toggled off with integer zero, on otherwise. + * + * example: + * yajl_config(h, yajl_allow_comments, 1); // turn comment support on + */ + yajl_allow_comments = 0x01, + /** + * When set the parser will verify that all strings in JSON input are + * valid UTF8 and will emit a parse error if this is not so. When set, + * this option makes parsing slightly more expensive (~10%) (XXX: get real numbers) + * + * example: + * yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking + */ + yajl_dont_validate_strings = 0x02, + /** + * By default, upon calls to yajl_parse_complete(), yajl will + * ensure the entire input text was consumed and will raise an error + * otherwise. Enabling this flag will cause yajl to disable this + * check. This can be useful when parsing json out of a that contains more + * than a single JSON document. + */ + yajl_allow_trailing_garbage = 0x04, + /** + * Allow multiple values to be parsed by a single handle. The + * entire text must be valid JSON, and values can be seperated + * by any kind of whitespace. This flag will change the + * behavior of the parser, and cause it continue parsing after + * a value is parsed, rather than transitioning into a + * complete state. This option can be useful when parsing multiple + * values from an input stream. + */ + yajl_allow_multiple_values = 0x08, + /** + * When yajl_parse_complete() is called the parser will + * check that the top level value was completely consumed. I.E., + * if called whilst in the middle of parsing a value + * yajl will enter an error state (premature EOF). Setting this + * flag suppresses that check and the corresponding error. + */ + yajl_allow_partial_values = 0x10 + } yajl_option; + + /** allow the modification of parser options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise */ - YAJL_API void yajl_forbid_partial_values(yajl_handle h); + YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...); /** free a parser handle */ YAJL_API void yajl_free(yajl_handle handle); @@ -21,6 +21,7 @@ #include <stdlib.h> #include <string.h> +#include <stdarg.h> #include <assert.h> const char * @@ -34,9 +35,6 @@ yajl_status_to_string(yajl_status stat) case yajl_status_client_canceled: statStr = "client canceled parse"; break; - case yajl_status_insufficient_data: - statStr = "eof was met before the parse could complete"; - break; case yajl_status_error: statStr = "parse error"; break; @@ -46,12 +44,9 @@ yajl_status_to_string(yajl_status stat) yajl_handle yajl_alloc(const yajl_callbacks * callbacks, - const yajl_parser_config * config, - const yajl_alloc_funcs * afs, + yajl_alloc_funcs * afs, void * ctx) { - unsigned int allowComments = 0; - unsigned int validateUTF8 = 0; yajl_handle hand = NULL; yajl_alloc_funcs afsBuffer; @@ -71,41 +66,40 @@ yajl_alloc(const yajl_callbacks * callbacks, /* copy in pointers to allocation routines */ memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); - if (config != NULL) { - allowComments = config->allowComments; - validateUTF8 = config->checkUTF8; - } - hand->callbacks = callbacks; hand->ctx = ctx; - hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8); + hand->lexer = NULL; hand->bytesConsumed = 0; hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); - hand->flags = allow_trailing_garbage | allow_partial_values; + hand->flags = 0; yajl_bs_init(hand->stateStack, &(hand->alloc)); - yajl_bs_push(hand->stateStack, yajl_state_start); return hand; } - -void -yajl_forbid_trailing_garbage(yajl_handle h) -{ - h->flags &= ~allow_trailing_garbage; -} - -void -yajl_allow_multiple_values(yajl_handle h) +int +yajl_config(yajl_handle h, yajl_option opt, ...) { - h->flags |= allow_multiple_values; -} + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_allow_comments: + case yajl_dont_validate_strings: + case yajl_allow_trailing_garbage: + case yajl_allow_multiple_values: + case yajl_allow_partial_values: + if (va_arg(ap, int)) h->flags |= opt; + else h->flags &= ~opt; + break; + default: + rv = 0; + } + va_end(ap); -void -yajl_forbid_partial_values(yajl_handle h) -{ - h->flags &= ~allow_partial_values; + return rv; } void @@ -113,7 +107,10 @@ yajl_free(yajl_handle handle) { yajl_bs_free(handle->stateStack); yajl_buf_free(handle->decodeBuf); - yajl_lex_free(handle->lexer); + if (handle->lexer) { + yajl_lex_free(handle->lexer); + handle->lexer = NULL; + } YA_FREE(&(handle->alloc), handle); } @@ -122,6 +119,14 @@ yajl_parse(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen) { yajl_status status; + + // lazy allocate the lexer + if (hand->lexer == NULL) { + hand->lexer = yajl_lex_alloc(&(hand->alloc), + hand->flags & yajl_allow_comments, + !(hand->flags & yajl_dont_validate_strings)); + } + status = yajl_do_parse(hand, jsonText, jsonTextLen); return status; } @@ -130,13 +135,7 @@ yajl_parse(yajl_handle hand, const unsigned char * jsonText, yajl_status yajl_parse_complete(yajl_handle hand) { - /* The particular case we want to handle is a trailing number. - * Further input consisting of digits could cause our interpretation - * of the number to change (buffered "1" but "2" comes in). - * A very simple approach to this is to inject whitespace to terminate - * any number in the lex buffer. - */ - return yajl_do_finish(hand); + return yajl_do_finish(hand); } unsigned char * diff --git a/src/yajl_gen.c b/src/yajl_gen.c index 210a918..d50b492 100644 --- a/src/yajl_gen.c +++ b/src/yajl_gen.c @@ -22,6 +22,7 @@ #include <string.h> #include <stdio.h> #include <math.h> +#include <stdarg.h> typedef enum { yajl_gen_start, @@ -36,8 +37,8 @@ typedef enum { struct yajl_gen_t { + unsigned int flags; unsigned int depth; - unsigned int pretty; const char * indentString; yajl_gen_state state[YAJL_MAX_DEPTH]; yajl_print_t print; @@ -46,18 +47,53 @@ struct yajl_gen_t yajl_alloc_funcs alloc; }; -yajl_gen -yajl_gen_alloc(const yajl_gen_config * config, - const yajl_alloc_funcs * afs) +int +yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...) { - return yajl_gen_alloc2(NULL, config, afs, NULL); + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_gen_beautify: + if (va_arg(ap, int)) g->flags |= opt; + else g->flags &= ~opt; + break; + case yajl_gen_indent_string: { + const char *indent = va_arg(ap, const char *); + g->indentString = indent; + for (; *indent; indent++) { + if (*indent != '\n' + && *indent != '\v' + && *indent != '\f' + && *indent != '\t' + && *indent != '\r' + && *indent != ' ') + { + g->indentString = NULL; + rv = 0; + } + } + break; + } + case yajl_gen_print_callback: + yajl_buf_free(g->ctx); + g->print = va_arg(ap, const yajl_print_t); + g->ctx = va_arg(ap, void *); + break; + default: + rv = 0; + } + + va_end(ap); + + return rv; } + + yajl_gen -yajl_gen_alloc2(const yajl_print_t callback, - const yajl_gen_config * config, - const yajl_alloc_funcs * afs, - void * ctx) +yajl_gen_alloc(const yajl_alloc_funcs * afs) { yajl_gen g = NULL; yajl_alloc_funcs afsBuffer; @@ -80,35 +116,9 @@ yajl_gen_alloc2(const yajl_print_t callback, /* copy in pointers to allocation routines */ memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); - if (config) { - const char *indent = config->indentString; - g->pretty = config->beautify; - g->indentString = config->indentString; - if (indent) { - for (; *indent; indent++) { - if (*indent != '\n' - && *indent != '\v' - && *indent != '\f' - && *indent != '\t' - && *indent != '\r' - && *indent != ' ') { - g->indentString = NULL; - break; - } - } - } - if (!g->indentString) { - g->indentString = " "; - } - } - - if (callback) { - g->print = callback; - g->ctx = ctx; - } else { - g->print = (yajl_print_t)&yajl_buf_append; - g->ctx = yajl_buf_alloc(&(g->alloc)); - } + g->print = (yajl_print_t)&yajl_buf_append; + g->ctx = yajl_buf_alloc(&(g->alloc)); + g->indentString = " "; return g; } @@ -124,14 +134,14 @@ yajl_gen_free(yajl_gen g) if (g->state[g->depth] == yajl_gen_map_key || \ g->state[g->depth] == yajl_gen_in_array) { \ g->print(g->ctx, ",", 1); \ - if (g->pretty) g->print(g->ctx, "\n", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \ } else if (g->state[g->depth] == yajl_gen_map_val) { \ g->print(g->ctx, ":", 1); \ - if (g->pretty) g->print(g->ctx, " ", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \ } #define INSERT_WHITESPACE \ - if (g->pretty) { \ + if ((g->flags & yajl_gen_beautify)) { \ if (g->state[g->depth] != yajl_gen_map_val) { \ unsigned int _i; \ for (_i=0;_i<g->depth;_i++) \ @@ -182,9 +192,9 @@ yajl_gen_free(yajl_gen g) } \ #define FINAL_NEWLINE \ - if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ - g->print(g->ctx, "\n", 1); - + if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \ + g->print(g->ctx, "\n", 1); + yajl_gen_status yajl_gen_integer(yajl_gen g, long long int number) { @@ -270,7 +280,7 @@ yajl_gen_map_open(yajl_gen g) g->state[g->depth] = yajl_gen_map_start; g->print(g->ctx, "{", 1); - if (g->pretty) g->print(g->ctx, "\n", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -281,7 +291,7 @@ yajl_gen_map_close(yajl_gen g) ENSURE_VALID_STATE; DECREMENT_DEPTH; - if (g->pretty) g->print(g->ctx, "\n", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); APPENDED_ATOM; INSERT_WHITESPACE; g->print(g->ctx, "}", 1); @@ -296,7 +306,7 @@ yajl_gen_array_open(yajl_gen g) INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_array_start; g->print(g->ctx, "[", 1); - if (g->pretty) g->print(g->ctx, "\n", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -306,7 +316,7 @@ yajl_gen_array_close(yajl_gen g) { ENSURE_VALID_STATE; DECREMENT_DEPTH; - if (g->pretty) g->print(g->ctx, "\n", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); APPENDED_ATOM; INSERT_WHITESPACE; g->print(g->ctx, "]", 1); diff --git a/src/yajl_parser.c b/src/yajl_parser.c index 5838be7..65d5ed6 100644 --- a/src/yajl_parser.c +++ b/src/yajl_parser.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "api/yajl_parse.h" #include "yajl_lex.h" #include "yajl_parser.h" #include "yajl_encode.h" @@ -161,9 +162,10 @@ yajl_do_finish(yajl_handle hand) case yajl_state_lexical_error: return yajl_status_error; case yajl_state_got_value: + case yajl_state_parse_complete: return yajl_status_ok; default: - if (!(hand->flags & allow_partial_values)) + if (!(hand->flags & yajl_allow_partial_values)) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "premature EOF"; @@ -187,11 +189,11 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, around_again: switch (yajl_bs_current(hand->stateStack)) { case yajl_state_parse_complete: - if (hand->flags & allow_multiple_values) { + if (hand->flags & yajl_allow_multiple_values) { yajl_bs_set(hand->stateStack, yajl_state_got_value); goto around_again; } - if (!(hand->flags & allow_trailing_garbage)) { + if (!(hand->flags & yajl_allow_trailing_garbage)) { if (*offset != jsonTextLen) { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); diff --git a/src/yajl_parser.h b/src/yajl_parser.h index 4151b71..97ef207 100644 --- a/src/yajl_parser.h +++ b/src/yajl_parser.h @@ -57,10 +57,6 @@ struct yajl_handle_t { unsigned int flags; }; -#define allow_trailing_garbage 0x01 -#define allow_multiple_values 0x02 -#define allow_partial_values 0x04 - yajl_status yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, size_t jsonTextLen); diff --git a/test/cases/difficult_json_c_test_case_with_comments.json b/test/cases/ac_difficult_json_c_test_case_with_comments.json index 2463c71..2463c71 100644 --- a/test/cases/difficult_json_c_test_case_with_comments.json +++ b/test/cases/ac_difficult_json_c_test_case_with_comments.json diff --git a/test/cases/difficult_json_c_test_case_with_comments.json.gold b/test/cases/ac_difficult_json_c_test_case_with_comments.json.gold index c3adc11..c3adc11 100644 --- a/test/cases/difficult_json_c_test_case_with_comments.json.gold +++ b/test/cases/ac_difficult_json_c_test_case_with_comments.json.gold diff --git a/test/cases/dc_simple_with_comments.json b/test/cases/ac_simple_with_comments.json index 3b79bba..3b79bba 100644 --- a/test/cases/dc_simple_with_comments.json +++ b/test/cases/ac_simple_with_comments.json diff --git a/test/cases/ac_simple_with_comments.json.gold b/test/cases/ac_simple_with_comments.json.gold new file mode 100644 index 0000000..80fcad2 --- /dev/null +++ b/test/cases/ac_simple_with_comments.json.gold @@ -0,0 +1,9 @@ +map open '{' +key: 'this' +string: 'is' +key: 'really' +string: 'simple' +key: 'json' +string: 'right?' +map close '}' +memory leaks: 0 diff --git a/test/cases/false_then_garbage.json b/test/cases/ag_false_then_garbage.json index 78f4e96..78f4e96 100644 --- a/test/cases/false_then_garbage.json +++ b/test/cases/ag_false_then_garbage.json diff --git a/test/cases/false_then_garbage.json.gold b/test/cases/ag_false_then_garbage.json.gold index e55fa1f..e55fa1f 100644 --- a/test/cases/false_then_garbage.json.gold +++ b/test/cases/ag_false_then_garbage.json.gold diff --git a/test/cases/null_then_garbage.json b/test/cases/ag_null_then_garbage.json index 7b65b35..7b65b35 100644 --- a/test/cases/null_then_garbage.json +++ b/test/cases/ag_null_then_garbage.json diff --git a/test/cases/null_then_garbage.json.gold b/test/cases/ag_null_then_garbage.json.gold index 94ad0fa..94ad0fa 100644 --- a/test/cases/null_then_garbage.json.gold +++ b/test/cases/ag_null_then_garbage.json.gold diff --git a/test/cases/true_then_garbage.json b/test/cases/ag_true_then_garbage.json index 9151612..9151612 100644 --- a/test/cases/true_then_garbage.json +++ b/test/cases/ag_true_then_garbage.json diff --git a/test/cases/true_then_garbage.json.gold b/test/cases/ag_true_then_garbage.json.gold index 0858bf7..0858bf7 100644 --- a/test/cases/true_then_garbage.json.gold +++ b/test/cases/ag_true_then_garbage.json.gold diff --git a/test/cases/array_open.json b/test/cases/ap_array_open.json index 558ed37..558ed37 100644 --- a/test/cases/array_open.json +++ b/test/cases/ap_array_open.json diff --git a/test/cases/array_open.json.gold b/test/cases/ap_array_open.json.gold index 478b6b9..478b6b9 100644 --- a/test/cases/array_open.json.gold +++ b/test/cases/ap_array_open.json.gold diff --git a/test/cases/eof_str.json b/test/cases/ap_eof_str.json index 6a21793..6a21793 100644 --- a/test/cases/eof_str.json +++ b/test/cases/ap_eof_str.json diff --git a/test/cases/eof_str.json.gold b/test/cases/ap_eof_str.json.gold index 736730b..736730b 100644 --- a/test/cases/eof_str.json.gold +++ b/test/cases/ap_eof_str.json.gold diff --git a/test/cases/map_open.json b/test/cases/ap_map_open.json index 98232c6..98232c6 100644 --- a/test/cases/map_open.json +++ b/test/cases/ap_map_open.json diff --git a/test/cases/map_open.json.gold b/test/cases/ap_map_open.json.gold index ab1f33d..ab1f33d 100644 --- a/test/cases/map_open.json.gold +++ b/test/cases/ap_map_open.json.gold diff --git a/test/cases/partial_ok.json b/test/cases/ap_partial_ok.json index 2fbd027..2fbd027 100644 --- a/test/cases/partial_ok.json +++ b/test/cases/ap_partial_ok.json diff --git a/test/cases/partial_ok.json.gold b/test/cases/ap_partial_ok.json.gold index 9f754c7..9f754c7 100644 --- a/test/cases/partial_ok.json.gold +++ b/test/cases/ap_partial_ok.json.gold diff --git a/test/cases/dc_simple_with_comments.json.gold b/test/cases/dc_simple_with_comments.json.gold deleted file mode 100644 index d222e9b..0000000 --- a/test/cases/dc_simple_with_comments.json.gold +++ /dev/null @@ -1,5 +0,0 @@ -map open '{' -key: 'this' -string: 'is' -lexical error: probable comment found in input text, comments are not enabled. -memory leaks: 0 diff --git a/test/cases/multiple.json.gold b/test/cases/multiple.json.gold index c687717..0f6f3df 100644 --- a/test/cases/multiple.json.gold +++ b/test/cases/multiple.json.gold @@ -1,3 +1,4 @@ map open '{' map close '}' +parse error: trailing garbage memory leaks: 0 diff --git a/test/cases/simple_with_comments.json.gold b/test/cases/simple_with_comments.json.gold index 80fcad2..d222e9b 100644 --- a/test/cases/simple_with_comments.json.gold +++ b/test/cases/simple_with_comments.json.gold @@ -1,9 +1,5 @@ map open '{' key: 'this' string: 'is' -key: 'really' -string: 'simple' -key: 'json' -string: 'right?' -map close '}' +lexical error: probable comment found in input text, comments are not enabled. memory leaks: 0 diff --git a/test/run_tests.sh b/test/run_tests.sh index 9a7d0fc..0d75ed3 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -28,47 +28,54 @@ fi ${ECHO} "using test binary: $testBin" +testBinShort=`basename $testBin` + testsSucceeded=0 testsTotal=0 for file in cases/*.json ; do - allowComments="-c" - forbidGarbage="" + allowComments="" + allowGarbage="" allowMultiple="" - noPartials="" + allowPartials="" # if the filename starts with dc_, we disallow comments for this test case $(basename $file) in - dc_*) - allowComments="" + ac_*) + allowComments="-c " ;; - fg_*) - forbidGarbage="-g" + ag_*) + allowGarbage="-g " ;; am_*) - allowMultiple="-m"; + allowMultiple="-m "; ;; - np_*) - noPartials="-p"; + ap_*) + allowPartials="-p "; ;; esac - ${ECHO} -n " test case: '$file': " + fileShort=`basename $file` + testName=`echo $fileShort | sed -e 's/\.json$//'` + + ${ECHO} -n " test ($testName): " iter=1 - success="success" + success="SUCCESS" - ${ECHO} "$testBin $noPartials $allowComments $forbidGarbage $allowMultiple -b $iter < $file > ${file}.test " + ${ECHO} -n "$testBinShort $allowPartials$allowComments$allowGarbage$allowMultiple-b $iter < $fileShort > ${fileShort}.test : " # parse with a read buffer size ranging from 1-31 to stress stream parsing - while [ $iter -lt 32 ] && [ $success = "success" ] ; do - $testBin $noPartials $allowComments $forbidGarbage $allowMultiple -b $iter < $file > ${file}.test 2>&1 - diff ${DIFF_FLAGS} ${file}.gold ${file}.test + while [ $iter -lt 32 ] && [ $success = "SUCCESS" ] ; do + $testBin $allowPartials $allowComments $allowGarbage $allowMultiple -b $iter < $file > ${file}.test 2>&1 + diff ${DIFF_FLAGS} ${file}.gold ${file}.test > ${file}.out if [ $? -eq 0 ] ; then if [ $iter -eq 31 ] ; then : $(( testsSucceeded += 1)) ; fi - else + else success="FAILURE" iter=32 + ${ECHO} + cat ${file}.out fi : $(( iter += 1 )) - rm ${file}.test + rm ${file}.test ${file}.out done ${ECHO} $success diff --git a/test/yajl_test.c b/test/yajl_test.c index 1b07f8b..7d507f8 100644 --- a/test/yajl_test.c +++ b/test/yajl_test.c @@ -156,10 +156,10 @@ static void usage(const char * progname) "to stdout\n" " -b set the read buffer size\n" " -c allow comments\n" - " -g forbid *g*arbage after valid JSON text\n" + " -g allow *g*arbage after valid JSON text\n" " -m allows the parser to consume multiple JSON values\n" " from a single string separated by whitespace\n" - " -p partial JSON documents should be considered invalid\n", + " -p partial JSON documents should not cause errors\n", progname); exit(1); } @@ -173,9 +173,8 @@ main(int argc, char ** argv) size_t bufSize = BUF_SIZE; yajl_status stat; size_t rd; - yajl_parser_config cfg = { 0, 1 }; int i, j; - int multiple = 0 ,trailing = 0, partial = 0; + /* memory allocation debugging: allocate a structure which collects * statistics */ yajlTestMemoryContext memCtx = { 0,0 }; @@ -191,10 +190,13 @@ main(int argc, char ** argv) allocFuncs.ctx = (void *) &memCtx; + /* allocate the parser */ + hand = yajl_alloc(&callbacks, &allocFuncs, NULL); + /* check arguments. We expect exactly one! */ for (i=1;i<argc;i++) { if (!strcmp("-c", argv[i])) { - cfg.allowComments = 1; + yajl_config(hand, yajl_allow_comments, 1); } else if (!strcmp("-b", argv[i])) { if (++i >= argc) usage(argv[0]); @@ -212,11 +214,11 @@ main(int argc, char ** argv) bufSize); } } else if (!strcmp("-g", argv[i])) { - trailing = 1; + yajl_config(hand, yajl_allow_trailing_garbage, 1); } else if (!strcmp("-m", argv[i])) { - multiple = 1; partial = 1; + yajl_config(hand, yajl_allow_multiple_values, 1); } else if (!strcmp("-p", argv[i])) { - partial = 1; + yajl_config(hand, yajl_allow_partial_values, 1); } else { fprintf(stderr, "invalid command line option: '%s'\n", argv[i]); @@ -230,17 +232,12 @@ main(int argc, char ** argv) fprintf(stderr, "failed to allocate read buffer of %zu bytes, exiting.", bufSize); + yajl_free(hand); exit(2); } fileName = argv[argc-1]; - /* ok. open file. let's read and parse */ - hand = yajl_alloc(&callbacks, &cfg, &allocFuncs, NULL); - if (trailing) yajl_forbid_trailing_garbage(hand); - if (multiple) yajl_allow_multiple_values(hand); - if (partial) yajl_forbid_partial_values(hand); - for (;;) { rd = fread((void *) fileData, 1, bufSize, stdin); diff --git a/verify/json_verify.c b/verify/json_verify.c index dbb6525..ea09cdf 100644 --- a/verify/json_verify.c +++ b/verify/json_verify.c @@ -40,9 +40,10 @@ main(int argc, char ** argv) yajl_handle hand; static unsigned char fileData[65536]; int quiet = 0; - int retval = 0, done = 0; - yajl_parser_config cfg = { 0, 1 }; - + int retval = 0; + int allowComments = 0; + int checkUTF8 = 1; + /* check arguments.*/ int a = 1; while ((a < argc) && (argv[a][0] == '-') && (strlen(argv[a]) > 1)) { @@ -53,10 +54,10 @@ main(int argc, char ** argv) quiet = 1; break; case 'c': - cfg.allowComments = 1; + allowComments = 1; break; case 'u': - cfg.checkUTF8 = 0; + checkUTF8 = 0; break; default: fprintf(stderr, "unrecognized option: '%c'\n\n", argv[a][i]); @@ -70,9 +71,11 @@ main(int argc, char ** argv) } /* allocate a parser */ - hand = yajl_alloc(NULL, &cfg, NULL, NULL); + hand = yajl_alloc(NULL, NULL, NULL); + yajl_config(hand, yajl_allow_comments, allowComments); + yajl_config(hand, yajl_dont_validate_strings, !checkUTF8); - while (!done) { + for (;;) { rd = fread((void *) fileData, 1, sizeof(fileData) - 1, stdin); retval = 0; @@ -83,30 +86,28 @@ main(int argc, char ** argv) fprintf(stderr, "error encountered on file read\n"); } retval = 1; - break; } - done = 1; + break; } fileData[rd] = 0; - if (done) - /* parse any remaining buffered data */ - stat = yajl_parse_complete(hand); - else - /* read file data, pass to parser */ - stat = yajl_parse(hand, fileData, rd); + /* read file data, pass to parser */ + stat = yajl_parse(hand, fileData, rd); - if (stat != yajl_status_ok && - stat != yajl_status_insufficient_data) - { - if (!quiet) { - unsigned char * str = yajl_get_error(hand, 1, fileData, rd); - fprintf(stderr, "%s", (const char *) str); - yajl_free_error(hand, str); - } - retval = 1; - break; + if (stat != yajl_status_ok) break; + } + + /* parse any remaining buffered data */ + stat = yajl_parse_complete(hand); + + if (stat != yajl_status_ok) + { + if (!quiet) { + unsigned char * str = yajl_get_error(hand, 1, fileData, rd); + fprintf(stderr, "%s", (const char *) str); + yajl_free_error(hand, str); } + retval = 1; } yajl_free(hand); |