pax_global_header00006660000000000000000000000064125221124050014504gustar00rootroot0000000000000052 comment=9f55366a50c31c3be33705a3ee21d52eb77cb4ab liblaxjson-1.0.5/000077500000000000000000000000001252211240500136545ustar00rootroot00000000000000liblaxjson-1.0.5/.gitignore000066400000000000000000000000071252211240500156410ustar00rootroot00000000000000/build liblaxjson-1.0.5/CHANGELOG.md000066400000000000000000000006001252211240500154610ustar00rootroot00000000000000### 1.0.5 * fix decimal numbers causing parse error ### 1.0.4 * fix escapes silently breaking parser ### 1.0.3 * build: remove -fPIC ### 1.0.2 * add CHANGELOG.md file ### 1.0.1 * build: install laxjson.h on install target * fix reallocing the wrong size * ability to abort from callback methods * h file: add extern "C" if __cplusplus ### 1.0.0 * Initial release. liblaxjson-1.0.5/CMakeLists.txt000066400000000000000000000026301252211240500164150ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(laxjson C) set(VERSION_MAJOR 1) set(VERSION_MINOR 0) set(VERSION_PATCH 5) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") message("Configuring laxjson version ${VERSION}") file(GLOB_RECURSE SOURCES ${PROJECT_SOURCE_DIR}/src/*.c) file(GLOB_RECURSE HEADERS ${PROJECT_SOURCE_DIR}/src/*.h) set(LIB_CFLAGS "-pedantic -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes") set(EXAMPLE_CFLAGS "-pedantic -Werror -Wall") include_directories("${PROJECT_SOURCE_DIR}/include") add_library(laxjson_static STATIC ${SOURCES} ${HEADERS}) set_target_properties(laxjson_static PROPERTIES OUTPUT_NAME laxjson COMPILE_FLAGS ${LIB_CFLAGS}) add_library(laxjson SHARED ${SOURCES} ${HEADERS}) set_target_properties(laxjson PROPERTIES SOVERSION ${VERSION_MAJOR} VERSION ${VERSION} COMPILE_FLAGS ${LIB_CFLAGS}) add_executable(token_list example/token_list.c) set_target_properties(token_list PROPERTIES COMPILE_FLAGS ${EXAMPLE_CFLAGS}) target_link_libraries(token_list laxjson) enable_testing() add_executable(primitives_test test/primitives.c) set_target_properties(primitives_test PROPERTIES COMPILE_FLAGS ${EXAMPLE_CFLAGS}) target_link_libraries(primitives_test laxjson) add_test(DetectPrimitives primitives_test) install(FILES "include/laxjson.h" DESTINATION include) install(TARGETS laxjson laxjson_static DESTINATION lib) liblaxjson-1.0.5/COPYING000066400000000000000000000020701252211240500147060ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013 Andrew Kelley 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. liblaxjson-1.0.5/README.md000066400000000000000000000077311252211240500151430ustar00rootroot00000000000000# Relaxed Streaming JSON Parser C Library ## Differences from RFC 4627 * unquoted keys * single quotes `'` * `//` and `/* */` style comments * extra commas `,` in arrays and objects ## Why? [Official JSON](http://json.org/) is *almost* human-readable and human-writable. If we disable a few of the strict rules, we can make it significantly more so. You would use this library when parsing user input, such as a config file. You would *not* use this library when serializing or deserializing, or as a format for computer-to-computer communication. I could not find another JSON parser that fit all of these requirements: * C library * Relaxed parsing rules as outlined above. As a rule of thumb the parser should be compatible with [GYP](https://code.google.com/p/gyp/). * Streaming - ability to not buffer the entire JSON string in memory before parsing it. * In Debian/Ubuntu's package repository or at least scheduled to be in it. So I wrote one that satisfies all these requirements. It has a [test suite](test) and is already in use by [another project](https://github.com/andrewrk/rucksack). It has been uploaded to Debian and is scheduled to be released in "jessie" and Ubuntu 14.10 Utopic Unicorn. ## Usage See include/laxjson.h for more details. ```c #include #include static int on_string(struct LaxJsonContext *context, enum LaxJsonType type, const char *value, int length) { const char *type_name = type == LaxJsonTypeProperty ? "property" : "string"; printf("%s: %s\n", type_name, value); return 0; } static int on_number(struct LaxJsonContext *context, double x) { printf("number: %f\n", x); return 0; } static int on_primitive(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name; if (type == LaxJsonTypeTrue) type_name = "true"; else if (type == LaxJsonTypeFalse) type_name = "false"; else type_name = "null"; printf("primitive: %s\n", type_name); return 0; } static int on_begin(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name = (type == LaxJsonTypeArray) ? "array" : "object"; printf("begin %s\n", type_name); return 0; } static int on_end(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name = (type == LaxJsonTypeArray) ? "array" : "object"; printf("end %s\n", type_name); return 0; } int main() { char buf[1024]; struct LaxJsonContext *context; FILE *f; int amt_read; enum LaxJsonError err; context = lax_json_create(); context->userdata = NULL; /* can set this to whatever you want */ context->string = on_string; context->number = on_number; context->primitive = on_primitive; context->begin = on_begin; context->end = on_end; f = fopen("file.json", "rb"); while ((amt_read = fread(buf, 1, sizeof(buf), f))) { if ((err = lax_json_feed(context, amt_read, buf))) { fprintf(stderr, "Line %d, column %d: %s\n", context->line, context->column, lax_json_str_err(err)); return -1; } lax_json_feed(context, amt_read, buf); } if ((err = lax_json_eof(context))) { fprintf(stderr, "Line %d, column %d: %s\n", context->line, context->column, lax_json_str_err(err)); return -1; } lax_json_destroy(context); return 0; } ``` ## Installation ### Pre-Built Packages * [Ubuntu PPA](https://launchpad.net/~andrewrk/+archive/rucksack) ``` sudo apt-add-repository ppa:andrewrk/rucksack sudo apt-get update sudo apt-get install liblaxjson-dev ``` ### From Source ```sh mkdir build cd build cmake .. make sudo make install ``` To run the tests, use `make test`. ## Projects Using liblaxjson Feel free to make a pull request adding to this list. * [rucksack](https://github.com/andrewrk/rucksack) - a texture packer and resource bundler * [Genesis](https://github.com/andrewrk/genesis) - digital audio workstation liblaxjson-1.0.5/example/000077500000000000000000000000001252211240500153075ustar00rootroot00000000000000liblaxjson-1.0.5/example/token_list.c000066400000000000000000000043221252211240500176270ustar00rootroot00000000000000/* * Copyright (c) 2013 Andrew Kelley * * This file is part of liblaxjson, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include #include static int on_string(struct LaxJsonContext *context, enum LaxJsonType type, const char *value, int length) { const char *type_name = type == LaxJsonTypeProperty ? "property" : "string"; printf("%s: %s\n", type_name, value); return 0; } static int on_number(struct LaxJsonContext *context, double x) { printf("number: %f\n", x); return 0; } static int on_primitive(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name; if (type == LaxJsonTypeTrue) type_name = "true"; else if (type == LaxJsonTypeFalse) type_name = "false"; else type_name = "null"; printf("primitive: %s\n", type_name); return 0; } static int on_begin(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name = (type == LaxJsonTypeArray) ? "array" : "object"; printf("begin %s\n", type_name); return 0; } static int on_end(struct LaxJsonContext *context, enum LaxJsonType type) { const char *type_name = (type == LaxJsonTypeArray) ? "array" : "object"; printf("end %s\n", type_name); return 0; } int main() { char buf[1024]; struct LaxJsonContext *context; FILE *f; int amt_read; enum LaxJsonError err; context = lax_json_create(); context->userdata = NULL; /* can set this to whatever you want */ context->string = on_string; context->number = on_number; context->primitive = on_primitive; context->begin = on_begin; context->end = on_end; f = fopen("file.json", "rb"); while ((amt_read = fread(buf, 1, sizeof(buf), f))) { if ((err = lax_json_feed(context, amt_read, buf))) { fprintf(stderr, "Line %d, column %d: %s\n", context->line, context->column, lax_json_str_err(err)); return -1; } } if ((err = lax_json_eof(context))) { fprintf(stderr, "Line %d, column %d: %s\n", context->line, context->column, lax_json_str_err(err)); return -1; } lax_json_destroy(context); return 0; } liblaxjson-1.0.5/include/000077500000000000000000000000001252211240500152775ustar00rootroot00000000000000liblaxjson-1.0.5/include/laxjson.h000066400000000000000000000055021252211240500171300ustar00rootroot00000000000000/* * Copyright (c) 2013 Andrew Kelley * * This file is part of liblaxjson, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #ifndef LAXJSON_H_INCLUDED #define LAXJSON_H_INCLUDED #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ enum LaxJsonType { LaxJsonTypeString, LaxJsonTypeProperty, LaxJsonTypeNumber, LaxJsonTypeObject, LaxJsonTypeArray, LaxJsonTypeTrue, LaxJsonTypeFalse, LaxJsonTypeNull }; enum LaxJsonState { LaxJsonStateValue, LaxJsonStateObject, LaxJsonStateArray, LaxJsonStateString, LaxJsonStateStringEscape, LaxJsonStateUnicodeEscape, LaxJsonStateBareProp, LaxJsonStateCommentBegin, LaxJsonStateCommentLine, LaxJsonStateCommentMultiLine, LaxJsonStateCommentMultiLineStar, LaxJsonStateExpect, LaxJsonStateEnd, LaxJsonStateColon, LaxJsonStateNumber, LaxJsonStateNumberDecimal, LaxJsonStateNumberExponent, LaxJsonStateNumberExponentSign }; enum LaxJsonError { LaxJsonErrorNone, LaxJsonErrorUnexpectedChar, LaxJsonErrorExpectedEof, LaxJsonErrorExceededMaxStack, LaxJsonErrorNoMem, LaxJsonErrorExceededMaxValueSize, LaxJsonErrorInvalidHexDigit, LaxJsonErrorInvalidUnicodePoint, LaxJsonErrorExpectedColon, LaxJsonErrorUnexpectedEof, LaxJsonErrorAborted }; /* All callbacks must be provided. Return nonzero to abort the ongoing feed operation. */ struct LaxJsonContext { void *userdata; /* type can be property or string */ int (*string)(struct LaxJsonContext *, enum LaxJsonType type, const char *value, int length); /* type is always number */ int (*number)(struct LaxJsonContext *, double x); /* type can be true, false, or null */ int (*primitive)(struct LaxJsonContext *, enum LaxJsonType type); /* type can be array or object */ int (*begin)(struct LaxJsonContext *, enum LaxJsonType type); /* type can be array or object */ int (*end)(struct LaxJsonContext *, enum LaxJsonType type); int line; int column; int max_state_stack_size; int max_value_buffer_size; /* private members */ enum LaxJsonState state; enum LaxJsonState *state_stack; int state_stack_index; int state_stack_size; char *value_buffer; int value_buffer_index; int value_buffer_size; unsigned int unicode_point; unsigned int unicode_digit_index; char *expected; char delim; enum LaxJsonType string_type; }; struct LaxJsonContext *lax_json_create(void); void lax_json_destroy(struct LaxJsonContext *context); enum LaxJsonError lax_json_feed(struct LaxJsonContext *context, int size, const char *data); enum LaxJsonError lax_json_eof(struct LaxJsonContext *context); const char *lax_json_str_err(enum LaxJsonError err); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* LAXJSON_H_INCLUDED */ liblaxjson-1.0.5/src/000077500000000000000000000000001252211240500144435ustar00rootroot00000000000000liblaxjson-1.0.5/src/laxjson.c000066400000000000000000000641741252211240500163010ustar00rootroot00000000000000/* * Copyright (c) 2013 Andrew Kelley * * This file is part of liblaxjson, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "laxjson.h" #include #include #define WHITESPACE \ ' ': \ case '\t': \ case '\n': \ case '\f': \ case '\r': \ case 0xb #define DIGIT \ '0': \ case '1': \ case '2': \ case '3': \ case '4': \ case '5': \ case '6': \ case '7': \ case '8': \ case '9' #define ALPHANUMERIC \ 'a': \ case 'b': \ case 'c': \ case 'd': \ case 'e': \ case 'f': \ case 'g': \ case 'h': \ case 'i': \ case 'j': \ case 'k': \ case 'l': \ case 'm': \ case 'n': \ case 'o': \ case 'p': \ case 'q': \ case 'r': \ case 's': \ case 't': \ case 'u': \ case 'v': \ case 'w': \ case 'x': \ case 'y': \ case 'z': \ case 'A': \ case 'B': \ case 'C': \ case 'D': \ case 'E': \ case 'F': \ case 'G': \ case 'H': \ case 'I': \ case 'J': \ case 'K': \ case 'L': \ case 'M': \ case 'N': \ case 'O': \ case 'P': \ case 'Q': \ case 'R': \ case 'S': \ case 'T': \ case 'U': \ case 'V': \ case 'W': \ case 'X': \ case 'Y': \ case 'Z': \ case DIGIT #define VALID_UNQUOTED \ '-': \ case '_': \ case '#': \ case '$': \ case '%': \ case '&': \ case '<': \ case '>': \ case '=': \ case '~': \ case '|': \ case '@': \ case '?': \ case ';': \ case '.': \ case '+': \ case '*': \ case '(': \ case ')': \ case ALPHANUMERIC #define NUMBER_TERMINATOR \ ',': \ case WHITESPACE: \ case ']': \ case '}': \ case '/' static const int HEX_MULT[] = {4096, 256, 16, 1}; /* static const char *STATE_NAMES[] = { "LaxJsonStateValue", "LaxJsonStateObject", "LaxJsonStateArray", "LaxJsonStateString", "LaxJsonStateStringEscape", "LaxJsonStateUnicodeEscape", "LaxJsonStateBareProp", "LaxJsonStateCommentBegin", "LaxJsonStateCommentLine", "LaxJsonStateCommentMultiLine", "LaxJsonStateCommentMultiLineStar", "LaxJsonStateExpect", "LaxJsonStateEnd", "LaxJsonStateColon", "LaxJsonStateNumber", "LaxJsonStateNumberDecimal", "LaxJsonStateNumberExponent", "LaxJsonStateNumberExponentSign" }; */ static enum LaxJsonError push_state(struct LaxJsonContext *context, enum LaxJsonState state) { enum LaxJsonState *new_ptr; /* fprintf(stderr, "push state %s\n", STATE_NAMES[state]); */ if (context->state_stack_index >= context->state_stack_size) { context->state_stack_size += 1024; if (context->state_stack_size > context->max_state_stack_size) return LaxJsonErrorExceededMaxStack; new_ptr = realloc(context->state_stack, context->state_stack_size * sizeof(enum LaxJsonState)); if (!new_ptr) return LaxJsonErrorNoMem; context->state_stack = new_ptr; } context->state_stack[context->state_stack_index] = state; context->state_stack_index += 1; return LaxJsonErrorNone; } struct LaxJsonContext *lax_json_create(void) { struct LaxJsonContext *context = calloc(1, sizeof(struct LaxJsonContext)); if (!context) return NULL; context->value_buffer_size = 1024; context->value_buffer = malloc(context->value_buffer_size); if (!context->value_buffer) { lax_json_destroy(context); return NULL; } context->state_stack_size = 1024; context->state_stack = malloc(context->state_stack_size * sizeof(enum LaxJsonState)); if (!context->state_stack) { lax_json_destroy(context); return NULL; } context->line = 1; context->max_state_stack_size = 16384; context->max_value_buffer_size = 1048576; /* 1 MB */ push_state(context, LaxJsonStateEnd); return context; } void lax_json_destroy(struct LaxJsonContext *context) { free(context->state_stack); free(context->value_buffer); free(context); } static void pop_state(struct LaxJsonContext *context) { context->state_stack_index -= 1; context->state = context->state_stack[context->state_stack_index]; assert(context->state_stack_index >= 0); } static enum LaxJsonError buffer_char(struct LaxJsonContext *context, char c) { char *new_ptr; if (context->value_buffer_index >= context->value_buffer_size) { context->value_buffer_size += 16384; if (context->value_buffer_size > context->max_value_buffer_size) return LaxJsonErrorExceededMaxValueSize; new_ptr = realloc(context->value_buffer, context->value_buffer_size); if (!new_ptr) return LaxJsonErrorNoMem; context->value_buffer = new_ptr; } context->value_buffer[context->value_buffer_index] = c; context->value_buffer_index += 1; return LaxJsonErrorNone; } enum LaxJsonError lax_json_feed(struct LaxJsonContext *context, int size, const char *data) { #define PUSH_STATE(state) \ err = push_state(context, state); \ if (err) return err; #define BUFFER_CHAR(c) \ err = buffer_char(context, c); \ if (err) return err; enum LaxJsonError err = LaxJsonErrorNone; int x; const char *end; char c; unsigned char byte; for (end = data + size; data < end; data += 1) { c = *data; if (c == '\n') { context->line += 1; context->column = 0; } else { context->column += 1; } /* fprintf(stderr, "line %d col %d state %s char %c\n", context->line, context->column, STATE_NAMES[context->state], c); */ switch (context->state) { case LaxJsonStateEnd: switch (c) { case WHITESPACE: /* ignore */ break; case '/': context->state = LaxJsonStateCommentBegin; PUSH_STATE(LaxJsonStateEnd); break; default: return LaxJsonErrorExpectedEof; } break; case LaxJsonStateObject: switch (c) { case WHITESPACE: case ',': /* do nothing except eat these characters */ break; case '/': context->state = LaxJsonStateCommentBegin; PUSH_STATE(LaxJsonStateObject); break; case '"': case '\'': context->state = LaxJsonStateString; context->value_buffer_index = 0; context->delim = c; context->string_type = LaxJsonTypeProperty; PUSH_STATE(LaxJsonStateColon); break; case VALID_UNQUOTED: context->state = LaxJsonStateBareProp; context->value_buffer[0] = c; context->value_buffer_index = 1; context->delim = 0; break; case '}': if (context->end(context, LaxJsonTypeObject)) return LaxJsonErrorAborted; pop_state(context); break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateBareProp: switch (c) { case VALID_UNQUOTED: BUFFER_CHAR(c); break; case WHITESPACE: BUFFER_CHAR('\0'); if (context->string(context, LaxJsonTypeProperty, context->value_buffer, context->value_buffer_index - 1)) { return LaxJsonErrorAborted; } context->state = LaxJsonStateColon; break; case ':': BUFFER_CHAR('\0'); if (context->string(context, LaxJsonTypeProperty, context->value_buffer, context->value_buffer_index - 1)) { return LaxJsonErrorAborted; } context->state = LaxJsonStateValue; context->string_type = LaxJsonTypeString; PUSH_STATE(LaxJsonStateObject); break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateString: if (c == context->delim) { BUFFER_CHAR('\0'); if (context->string(context, context->string_type, context->value_buffer, context->value_buffer_index - 1)) { return LaxJsonErrorAborted; } pop_state(context); } else if (c == '\\') { context->state = LaxJsonStateStringEscape; } else { BUFFER_CHAR(c); } break; case LaxJsonStateStringEscape: switch (c) { case '\'': case '"': case '/': case '\\': BUFFER_CHAR(c); context->state = LaxJsonStateString; break; case 'b': BUFFER_CHAR('\b'); context->state = LaxJsonStateString; break; case 'f': BUFFER_CHAR('\f'); context->state = LaxJsonStateString; break; case 'n': BUFFER_CHAR('\n'); context->state = LaxJsonStateString; break; case 'r': BUFFER_CHAR('\r'); context->state = LaxJsonStateString; break; case 't': BUFFER_CHAR('\t'); context->state = LaxJsonStateString; break; case 'u': context->state = LaxJsonStateUnicodeEscape; context->unicode_digit_index = 0; context->unicode_point = 0; break; } break; case LaxJsonStateUnicodeEscape: switch (c) { case '0': x = 0; break; case '1': x = 1; break; case '2': x = 2; break; case '3': x = 3; break; case '4': x = 4; break; case '5': x = 5; break; case '6': x = 6; break; case '7': x = 7; break; case '8': x = 8; break; case '9': x = 9; break; case 'a': case 'A': x = 10; break; case 'b': case 'B': x = 11; break; case 'c': case 'C': x = 12; break; case 'd': case 'D': x = 13; break; case 'e': case 'E': x = 14; break; case 'f': case 'F': x = 15; break; default: return LaxJsonErrorInvalidHexDigit; } context->unicode_point += x * HEX_MULT[context->unicode_digit_index]; context->unicode_digit_index += 1; if (context->unicode_digit_index == 4) { if (context->unicode_point <= 0x007f) { /* 1 byte */ BUFFER_CHAR((char)context->unicode_point); context->state = LaxJsonStateString; } else if (context->unicode_point <= 0x07ff) { /* 2 bytes */ byte = (0xc0 | (context->unicode_point >> 6)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); } else if (context->unicode_point <= 0xffff) { /* 3 bytes */ byte = (0xe0 | (context->unicode_point >> 12)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 6) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); } else if (context->unicode_point <= 0x1fffff) { /* 4 bytes */ byte = (0xf0 | (context->unicode_point >> 18)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 12) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 6) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); } else if (context->unicode_point <= 0x3ffffff) { /* 5 bytes */ byte = (0xf8 | (context->unicode_point >> 24)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point >> 18)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 12) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 6) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); } else if (context->unicode_point <= 0x7fffffff) { /* 6 bytes */ byte = (0xfc | (context->unicode_point >> 30)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 24) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 18) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 12) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | ((context->unicode_point >> 6) & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); byte = (0x80 | (context->unicode_point & 0x3f)); BUFFER_CHAR(*(char *)(&byte)); } else { return LaxJsonErrorInvalidUnicodePoint; } context->state = LaxJsonStateString; } break; case LaxJsonStateColon: switch (c) { case WHITESPACE: /* ignore it */ break; case '/': context->state = LaxJsonStateCommentBegin; PUSH_STATE(LaxJsonStateColon); break; case ':': context->state = LaxJsonStateValue; context->string_type = LaxJsonTypeString; PUSH_STATE(LaxJsonStateObject); break; default: return LaxJsonErrorExpectedColon; } break; case LaxJsonStateValue: switch (c) { case WHITESPACE: /* ignore */ break; case '/': context->state = LaxJsonStateCommentBegin; PUSH_STATE(LaxJsonStateValue); break; case '{': if (context->begin(context, LaxJsonTypeObject)) return LaxJsonErrorAborted; context->state = LaxJsonStateObject; break; case '[': if (context->begin(context, LaxJsonTypeArray)) return LaxJsonErrorAborted; context->state = LaxJsonStateArray; break; case '\'': case '"': context->state = LaxJsonStateString; context->delim = c; context->value_buffer_index = 0; break; case '-': context->state = LaxJsonStateNumber; context->value_buffer[0] = c; context->value_buffer_index = 1; break; case '+': context->state = LaxJsonStateNumber; context->value_buffer_index = 0; break; case DIGIT: context->state = LaxJsonStateNumber; context->value_buffer_index = 1; context->value_buffer[0] = c; break; case 't': if (context->primitive(context, LaxJsonTypeTrue)) return LaxJsonErrorAborted; context->state = LaxJsonStateExpect; context->expected = "rue"; break; case 'f': if (context->primitive(context, LaxJsonTypeFalse)) return LaxJsonErrorAborted; context->state = LaxJsonStateExpect; context->expected = "alse"; break; case 'n': if (context->primitive(context, LaxJsonTypeNull)) return LaxJsonErrorAborted; context->state = LaxJsonStateExpect; context->expected = "ull"; break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateArray: switch (c) { case WHITESPACE: case ',': /* ignore */ break; case '/': context->state = LaxJsonStateCommentBegin; PUSH_STATE(LaxJsonStateArray); break; case ']': if (context->end(context, LaxJsonTypeArray)) return LaxJsonErrorAborted; pop_state(context); break; default: context->state = LaxJsonStateValue; PUSH_STATE(LaxJsonStateArray); /* rewind 1 character */ data -= 1; context->column -= 1; continue; } break; case LaxJsonStateNumber: switch (c) { case DIGIT: BUFFER_CHAR(c); break; case '.': BUFFER_CHAR(c); context->state = LaxJsonStateNumberDecimal; break; case NUMBER_TERMINATOR: BUFFER_CHAR('\0'); if (context->number(context, atof(context->value_buffer))) return LaxJsonErrorAborted; pop_state(context); /* rewind 1 */ data -= 1; context->column -= 1; continue; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateNumberDecimal: switch (c) { case DIGIT: BUFFER_CHAR(c); break; case 'e': case 'E': BUFFER_CHAR('e'); context->state = LaxJsonStateNumberExponentSign; break; case NUMBER_TERMINATOR: context->state = LaxJsonStateNumber; /* rewind 1 */ data -= 1; context->column -= 1; break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateNumberExponentSign: switch (c) { case '+': case '-': BUFFER_CHAR(c); context->state = LaxJsonStateNumberExponent; break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateNumberExponent: switch (c) { case DIGIT: BUFFER_CHAR(c); break; case ',': case WHITESPACE: case ']': case '}': case '/': BUFFER_CHAR('\0'); if (context->number(context, atof(context->value_buffer))) return LaxJsonErrorAborted; pop_state(context); /* rewind 1 */ data -= 1; context->column -= 1; continue; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateExpect: if (c == *context->expected) { context->expected += 1; if (*context->expected == 0) { pop_state(context); } } else { return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateCommentBegin: switch (c) { case '/': context->state = LaxJsonStateCommentLine; break; case '*': context->state = LaxJsonStateCommentMultiLine; break; default: return LaxJsonErrorUnexpectedChar; } break; case LaxJsonStateCommentLine: if (c == '\n') pop_state(context); break; case LaxJsonStateCommentMultiLine: if (c == '*') context->state = LaxJsonStateCommentMultiLineStar; break; case LaxJsonStateCommentMultiLineStar: if (c == '/') pop_state(context); else context->state = LaxJsonStateCommentMultiLine; break; } } return err; } enum LaxJsonError lax_json_eof(struct LaxJsonContext *context) { for (;;) { switch (context->state) { case LaxJsonStateEnd: return LaxJsonErrorNone; case LaxJsonStateCommentLine: pop_state(context); continue; default: return LaxJsonErrorUnexpectedEof; } } } const char *lax_json_str_err(enum LaxJsonError err) { switch (err) { case LaxJsonErrorNone: return "none"; case LaxJsonErrorUnexpectedChar: return "unexpected character"; case LaxJsonErrorExpectedEof: return "expected end of file"; case LaxJsonErrorExceededMaxStack: return "exceeded max stack"; case LaxJsonErrorNoMem: return "out of memory"; case LaxJsonErrorExceededMaxValueSize: return "exceeded maximum value size"; case LaxJsonErrorInvalidHexDigit: return "invalid hex digit"; case LaxJsonErrorInvalidUnicodePoint: return "invalid unicode point"; case LaxJsonErrorExpectedColon: return "expected colon"; case LaxJsonErrorUnexpectedEof: return "unexpected end of file"; case LaxJsonErrorAborted: return "aborted"; } return "invalid error code"; } liblaxjson-1.0.5/test/000077500000000000000000000000001252211240500146335ustar00rootroot00000000000000liblaxjson-1.0.5/test/primitives.c000066400000000000000000000252151252211240500171770ustar00rootroot00000000000000#include #include #include #include static char out_buf[16384]; static int out_buf_index; static void add_buf(const char *str, int len) { if (len == 0) len = strlen(str); memcpy(&out_buf[out_buf_index], str, len); out_buf_index += len; } static const char *type_to_str(enum LaxJsonType type) { switch (type) { case LaxJsonTypeString: return "string"; case LaxJsonTypeProperty: return "property"; case LaxJsonTypeNumber: return "number"; case LaxJsonTypeObject: return "object"; case LaxJsonTypeArray: return "array"; case LaxJsonTypeTrue: return "true"; case LaxJsonTypeFalse: return "false"; case LaxJsonTypeNull: return "null"; } exit(1); } static void feed(struct LaxJsonContext *context, const char *data) { int size = strlen(data); enum LaxJsonError err = lax_json_feed(context, size, data); if (!err) return; fprintf(stderr, "line %d column %d parse error: %s\n", context->line, context->column, lax_json_str_err(err)); exit(1); } static int on_string_build(struct LaxJsonContext *context, enum LaxJsonType type, const char *value, int length) { add_buf(type_to_str(type), 0); add_buf("\n", 0); add_buf(value, length); add_buf("\n", 0); return 0; } static int on_number_build(struct LaxJsonContext *context, double x) { out_buf_index += snprintf(&out_buf[out_buf_index], 30, "number %g\n", x); return 0; } static int on_primitive_build(struct LaxJsonContext *context, enum LaxJsonType type) { out_buf_index += snprintf(&out_buf[out_buf_index], 50, "%s\n", type_to_str(type)); return 0; } static int on_begin_build(struct LaxJsonContext *context, enum LaxJsonType type) { out_buf_index += snprintf(&out_buf[out_buf_index], 50, "begin %s\n", type_to_str(type)); return 0; } static int on_end_build(struct LaxJsonContext *context, enum LaxJsonType type) { out_buf_index += snprintf(&out_buf[out_buf_index], 50, "end %s\n", type_to_str(type)); return 0; } static void check_build(struct LaxJsonContext *context, const char *output) { int expected_len = strlen(output); enum LaxJsonError err = lax_json_eof(context); if (err != LaxJsonErrorNone) { fprintf(stderr, "%s\n", lax_json_str_err(err)); exit(1); } if (out_buf_index != expected_len) { fprintf(stderr, "\n" "EXPECTED:\n" "---------\n" "%s\n" "RECEIVED:\n" "---------\n" "%s\n", output, out_buf); exit(1); } if (memcmp(output, out_buf, expected_len) != 0) { fprintf(stderr, "EXPECTED:\n" "---------\n" "%s\n" "RECEIVED:\n" "---------\n" "%s\n", output, out_buf); exit(1); } lax_json_destroy(context); } static struct LaxJsonContext *init_for_build(void) { struct LaxJsonContext *context = lax_json_create(); if (!context) exit(1); out_buf_index = 0; context->userdata = NULL; context->string = on_string_build; context->number = on_number_build; context->primitive = on_primitive_build; context->begin = on_begin_build; context->end = on_end_build; return context; } static void check_error(const char *input, enum LaxJsonError error, int line, int col) { struct LaxJsonContext *context = init_for_build(); int size = strlen(input); enum LaxJsonError err = lax_json_feed(context, size, input); if (err == LaxJsonErrorNone) err = lax_json_eof(context); if (err != error) { fprintf(stderr, "Expected %s, received %s\n", lax_json_str_err(error), lax_json_str_err(err)); exit(1); } if (context->line != line) { fprintf(stderr, "Expected error line %d, received error line %d\n", line, context->line); exit(1); } if (context->column != col) { fprintf(stderr, "Expected error column %d, received error column %d\n", col, context->column); exit(1); } lax_json_destroy(context); } static void test_false(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "// this is a comment\n" " false" ); check_build(context, "false\n" ); } static void test_true(void) { struct LaxJsonContext *context = init_for_build(); feed(context, " /* before comment */true" ); check_build(context, "true\n" ); } static void test_null(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "null/* after comment*/ // line comment" ); check_build(context, "null\n" ); } static void test_string(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "\"foo\"" ); check_build(context, "string\n" "foo\n" ); } static void test_basic_json(void) { struct LaxJsonContext *context; context = init_for_build(); feed(context, "// comments are OK :)\n" "// single quotes, double quotes, and no quotes are OK\n" "{\n" " textures: {\n" " cockpit: {\n" " images: {\n" " arrow: {\n" " path: \"img/arrow.png\",\n" " anchor: \"left\"\n" " }," " 'radar-circle': {\n" " path: \"img/radar-circle.png\",\n" " anchor: \"center\"\n" " }\n" " }\n" " }\n" " }\n" "}\n" ); check_build(context, "begin object\n" "property\n" "textures\n" "begin object\n" "property\n" "cockpit\n" "begin object\n" "property\n" "images\n" "begin object\n" "property\n" "arrow\n" "begin object\n" "property\n" "path\n" "string\n" "img/arrow.png\n" "property\n" "anchor\n" "string\n" "left\n" "end object\n" "property\n" "radar-circle\n" "begin object\n" "property\n" "path\n" "string\n" "img/radar-circle.png\n" "property\n" "anchor\n" "string\n" "center\n" "end object\n" "end object\n" "end object\n" "end object\n" "end object\n" ); } static void test_empty_object(void) { struct LaxJsonContext *context; context = init_for_build(); feed(context, "{}"); check_build(context, "begin object\n" "end object\n" ); } static void test_float_value(void) { struct LaxJsonContext *context; context = init_for_build(); feed(context, "{\n" "\"PI\": 3.141E-10" "}" ); check_build(context, "begin object\n" "property\n" "PI\n" "number 3.141e-10\n" "end object\n" ); } static void test_simple_digit_array(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "[ 1,2,3,4]" ); check_build(context, "begin array\n" "number 1\n" "number 2\n" "number 3\n" "number 4\n" "end array\n" ); } static void test_simple_string_array(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "[ \"1\",\"2\",\"3\",\"4\"]" ); check_build(context, "begin array\n" "string\n" "1\n" "string\n" "2\n" "string\n" "3\n" "string\n" "4\n" "end array\n" ); } static void test_array_of_empty_object(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "[ { }, { }, []]" ); check_build(context, "begin array\n" "begin object\n" "end object\n" "begin object\n" "end object\n" "begin array\n" "end array\n" "end array\n" ); } static void test_unclosed_value(void) { check_error( "{ foo: \"value\n" "}\n" , LaxJsonErrorUnexpectedEof, 3, 0); } static void test_unicode_text(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "{ \"v\":\"\\u2000\\u20ff\"}" ); check_build(context, "begin object\n" "property\n" "v\n" "string\n" "\xe2\x80\x80\xe2\x83\xbf\n" "end object\n" ); } static void test_escapes(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "{ prop: \"\\b\\n\\\"\\\\\\t\\f\\r\" }" ); check_build(context, "begin object\n" "property\n" "prop\n" "string\n" "\b\n\"\\\t\f\r\n" "end object\n" ); } static void test_decimals(void) { struct LaxJsonContext *context = init_for_build(); feed(context, "{n: 0.3}"); check_build(context, "begin object\n" "property\n" "n\n" "number 0.3\n" "end object\n" ); } struct Test { const char *name; void (*fn)(void); }; static struct Test tests[] = { {"false primitive", test_false}, {"true primitive", test_true}, {"null primitive", test_null}, {"string primitive", test_string}, {"basic json", test_basic_json}, {"empty object", test_empty_object}, {"float value", test_float_value}, {"simple digit array", test_simple_digit_array}, {"simple string array", test_simple_string_array}, {"array of empty object", test_array_of_empty_object}, {"unclosed value", test_unclosed_value}, {"unicode text", test_unicode_text}, {"escapes", test_escapes}, {"decimal", test_decimals}, {NULL, NULL}, }; int main(int argc, char *argv[]) { struct Test *test = &tests[0]; while (test->name) { fprintf(stderr, "testing %s...", test->name); test->fn(); fprintf(stderr, "OK\n"); test += 1; } return 0; }