JSON parsing library by Andrii Mamchur https://github.com/amamchur/jsonlite
Dependents: M2X_dev MTS_M2x_Example1 MTS_M2x_Example m2x-demo-all ... more
Diff: jsonlite.c
- Revision:
- 0:01a2f8de46c8
- Child:
- 1:807034181e02
diff -r 000000000000 -r 01a2f8de46c8 jsonlite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jsonlite.c Thu Oct 24 12:20:56 2013 +0000 @@ -0,0 +1,1687 @@ +#define JSONLITE_AMALGAMATED +#include "jsonlite.h" +// +// Copyright 2012-2013, Andrii Mamchur +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#ifndef JSONLITE_AMALGAMATED +#include "jsonlite_parser.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef _MSC_VER + +#include <intrin.h> + +static uint32_t __inline jsonlite_clz(uint32_t x) { + unsigned long r = 0; + _BitScanForward(&r, x); + return r; +} + +#else + +#define jsonlite_clz(x) __builtin_clz((x)) + +#endif + +#define CALL_VALUE_CALLBACK(cbs, type, token) (cbs.type(&cbs.context, token)) +#define CALL_STATE_CALLBACK(cbs, type) (cbs.type(&cbs.context)) + +#define CHECK_HEX(c) \ +if ((c) < 48) goto error_escape; \ +if ((c) > 102) goto error_escape; \ +if (57 < (c) && (c) < 65) goto error_escape; \ +if (70 < (c) && (c) < 97) goto error_escape + +#define CASE_NUMBER_TOKEN_END \ +case 9: goto found_token; \ +case 10: goto found_token; \ +case 13: goto found_token; \ +case 32: goto found_token; \ +case 44: goto found_token; \ +case 93: goto found_token; \ +case 125: goto found_token + +enum { + state_start, + state_object_key, + state_object_key_end, + state_colon, + state_object_comma_end, + state_array_value_end, + state_array_comma_end, + state_key, + state_value, + state_end, + + state_suspend = 1 << 7 +}; + +typedef uint8_t parse_state; +struct jsonlite_parser_struct { + const uint8_t *cursor; + const uint8_t *limit; + const uint8_t *token_start; + const uint8_t *buffer; + + uint8_t *buffer_own; + uint8_t *rest; + size_t rest_size; + + parse_state *current; + const parse_state *last; + + jsonlite_result result; + jsonlite_parser_callbacks callbacks; +} jsonlite_parser_struct; + +static void jsonlite_do_parse(jsonlite_parser parser); +static void jsonlite_finish_parse(jsonlite_parser parser); +static void empty_value_callback(jsonlite_callback_context *ctx, jsonlite_token *t) {} +static void empty_state_callback(jsonlite_callback_context *ctx) {} + +const jsonlite_parser_callbacks jsonlite_default_callbacks = { + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_state_callback, + &empty_value_callback, + &empty_value_callback, + &empty_value_callback, + {NULL, NULL} +}; + +size_t jsonlite_parser_estimate_size(size_t depth) { + depth = depth < 2 ? 32 : depth; + return sizeof(jsonlite_parser_struct) + depth * sizeof(parse_state); +} + +jsonlite_parser jsonlite_parser_init(size_t depth) { + depth = depth < 2 ? 32 : depth; + + jsonlite_parser parser = (jsonlite_parser)calloc(1, jsonlite_parser_estimate_size(depth)); + parser->result = jsonlite_result_unknown; + parser->callbacks = jsonlite_default_callbacks; + parser->current = (parse_state *)((uint8_t *)parser + sizeof(jsonlite_parser_struct)); + parser->current[0] = state_end; + parser->current[1] = state_start; + parser->last = parser->current + depth; + parser->current++; + return parser; +} + +jsonlite_result jsonlite_parser_set_callback(jsonlite_parser parser, const jsonlite_parser_callbacks *cbs) { + if (parser == NULL || cbs == NULL) { + return jsonlite_result_invalid_argument; + } + + parser->callbacks = *cbs; + parser->callbacks.context.parser = parser; + return jsonlite_result_ok; +} + +jsonlite_result jsonlite_parser_get_result(jsonlite_parser parser) { + if (parser == NULL) { + return jsonlite_result_invalid_argument; + } + + return parser->result; +} + +jsonlite_result jsonlite_parser_tokenize(jsonlite_parser parser, const void *buffer, size_t size) { + if (parser == NULL || buffer == NULL || size == 0) { + return jsonlite_result_invalid_argument; + } + + if (parser->rest != NULL) { + size_t total_size = size + parser->rest_size; + uint8_t *b = (uint8_t *)malloc(total_size); + memcpy(b, parser->rest, parser->rest_size); // LCOV_EXCL_LINE + memcpy(b + parser->rest_size, buffer, size); // LCOV_EXCL_LINE + + free(parser->buffer_own); + free(parser->rest); + + parser->buffer = b; + parser->buffer_own = b; + parser->cursor = parser->buffer; + parser->limit = parser->buffer + total_size; + parser->rest = NULL; + parser->rest_size = 0; + } else { + parser->buffer = buffer; + parser->cursor = parser->buffer; + parser->limit = parser->buffer + size; + } + + jsonlite_do_parse(parser); + jsonlite_finish_parse(parser); + return parser->result; +} + +jsonlite_result jsonlite_parser_resume(jsonlite_parser parser) { + if (parser == NULL) { + return jsonlite_result_invalid_argument; + } + + if (parser->result != jsonlite_result_suspended) { + return jsonlite_result_not_allowed; + } + + jsonlite_do_parse(parser); + jsonlite_finish_parse(parser); + return parser->result; +} + +jsonlite_result jsonlite_parser_suspend(jsonlite_parser parser) { + if (parser == NULL) { + return jsonlite_result_invalid_argument; + } + + if (parser->result != jsonlite_result_unknown) { + return jsonlite_result_not_allowed; + } + + parser->result = jsonlite_result_suspended; + return jsonlite_result_ok; +} + +void jsonlite_parser_release(jsonlite_parser parser) { + if (parser == NULL) { + return; + } + + free(parser->buffer_own); + free(parser->rest); + free(parser); +} + +static void jsonlite_do_parse(jsonlite_parser parser) { + const uint8_t *c = parser->cursor; + const uint8_t *l = parser->limit; + const uint8_t *token_start = NULL; + const parse_state *last = parser->last; + parse_state *state = parser->current; + jsonlite_token token; + parser->result = jsonlite_result_unknown; + goto state_selection; + +structure_finished: + if (*--state == state_end) goto success; +skip_char_and_spaces: + c++; +state_selection: + if (c == l) goto end_of_stream_space; + if (*c == ' ') goto skip_char_and_spaces; + if (*c == '\n') goto skip_char_and_spaces; + if (*c == '\r') goto skip_char_and_spaces; + if (*c == '\t') goto skip_char_and_spaces; + token_start = c; + + if (parser->result != jsonlite_result_unknown) goto end; + switch (*state) { + case state_value: goto value_type_detection; + case state_key: goto string_token_parsing; + case state_object_key: goto key_checking; + case state_object_key_end: goto key_end_checking; + case state_colon: goto colon_checking; + case state_object_comma_end: goto object_comma_end_checking; + case state_array_value_end: goto array_value_end_checking; + case state_array_comma_end: goto array_comma_end_checking; + case state_start: + if (*c == '{') goto object_state_machine; + if (*c == '[') goto array_state_machine; + goto error_exp_ooa; + default: + goto suspend; + } + +object_state_machine: + *state = state_object_key_end; + CALL_STATE_CALLBACK(parser->callbacks, object_start); + goto skip_char_and_spaces; + +array_state_machine: + *state = state_array_value_end; + CALL_STATE_CALLBACK(parser->callbacks, array_start); + goto skip_char_and_spaces; + +key_checking: + if (*c != '"') goto error_exp_key; + *state = state_colon; + *++state = state_key; + goto string_token_parsing; + +colon_checking: + if (*c != ':') goto error_exp_colon; + *state = state_object_comma_end; + *++state = state_value; + goto skip_char_and_spaces; + +key_end_checking: + switch (*c) { + case '"': + *state = state_colon; + if (++state == last) goto error_depth; + *state = state_key; + goto string_token_parsing; + case '}': + CALL_STATE_CALLBACK(parser->callbacks, object_end); + goto structure_finished; + } + goto error_exp_koe; + +object_comma_end_checking: + switch (*c) { + case ',': + *state = state_object_key; + goto skip_char_and_spaces; + case '}': + CALL_STATE_CALLBACK(parser->callbacks, object_end); + goto structure_finished; + } + goto error_exp_coe; + +array_value_end_checking: + switch (*c) { + case ']': + CALL_STATE_CALLBACK(parser->callbacks, array_end); + goto structure_finished; + default: + *state = state_array_comma_end; + if (++state == last) goto error_depth; + *state = state_value; + goto value_type_detection; + } + +array_comma_end_checking: + switch (*c) { + case ',': + *++state = state_value; + goto skip_char_and_spaces; + case ']': + CALL_STATE_CALLBACK(parser->callbacks, array_end); + goto structure_finished; + } + goto error_exp_coe; + +value_type_detection: + if (*c == '-' || ('0' <= *c && *c <= '9')) goto number_parsing; + if (*c == '"') goto string_token_parsing; + if (*c == 't') goto true_token_parsing; + if (*c == 'f') goto false_token_paring; + if (*c == 'n') goto null_token_parsing; + if (*c == '{') goto object_state_machine; + if (*c == '[') goto array_state_machine; + goto error_exp_value; + +// State machine for number token parsing. +number_parsing: + { + jsonlite_number_type type = jsonlite_number_int; + token.start = c; + switch (*c) { + case 45: type |= jsonlite_number_negative; goto test_zero_leading; + case 48: type |= jsonlite_number_zero_leading; goto take_exp_frac; + default: type |= jsonlite_number_digit_leading; goto take_digits; + } + + test_zero_leading: + if (++c == l) goto end_of_stream; + if (49 <= *c && *c <= 57) goto take_digits; + if (*c == 48) goto take_exp_frac; + goto error_number; + take_exp_frac: + if (++c == l) goto end_of_stream; + switch (*c) { + CASE_NUMBER_TOKEN_END; + case 46: goto found_fraction; + case 69: goto take_exponent; + case 101: goto take_exponent; + default: goto error_number; + } + found_fraction: + type |= jsonlite_number_frac; + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_frac_number; + goto error_number; + take_frac_number: + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_frac_number; + switch (*c) { + CASE_NUMBER_TOKEN_END; + case 69: goto take_exponent; + case 101: goto take_exponent; + default: goto error_number; + } + take_exponent: + type |= jsonlite_number_exp; + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_exponent_number; + switch(*c) { + case 43: goto take_exponent_sign; + case 45: goto take_exponent_sign; + default: goto error_number; + } + take_exponent_sign: + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_exponent_number; + goto error_number; + take_exponent_number: + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_exponent_number; + switch(*c) { + CASE_NUMBER_TOKEN_END; + default: goto error_number; + } + take_digits: + if (++c == l) goto end_of_stream; + if (48 <= *c && *c <= 57) goto take_digits; + switch(*c) { + CASE_NUMBER_TOKEN_END; + case 46: goto found_fraction; + case 69: goto take_exponent; + case 101: goto take_exponent; + default: goto error_number; + } + found_token: + token.end = c; + token.number_type = type; + CALL_VALUE_CALLBACK(parser->callbacks, number_found, &token); + state--; + goto state_selection; + } + +// State machine for string token parsing. +string_token_parsing: + { + jsonlite_string_type type = jsonlite_string_ascii; + uint32_t value, utf32; + token.start = c + 1; + next_char: + if (++c == l) goto end_of_stream; + if (*c == '"') goto string_parsed; + if (*c == '\\') goto escaped; + if (*c >= 0x80) goto utf8; + if (*c < 0x20) goto error_token; + goto next_char; + escaped: + type |= jsonlite_string_escape; + if (++c == l) goto end_of_stream; + switch (*c) { + case 34: goto next_char; + case 47: goto next_char; + case 92: goto next_char; + case 98: goto next_char; + case 102: goto next_char; + case 110: goto next_char; + case 114: goto next_char; + case 116: goto next_char; + case 117: goto hex; + } + goto error_escape; + hex: + type |= jsonlite_string_unicode_escape; + if (c++ + 4 >= l) goto end_of_stream; + CHECK_HEX(*c); value = jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c); + + if ((value & 0xFFFFu) >= 0xFFFEu) goto error_token; + if (value >= 0xFDD0u && value <= 0xFDEFu) goto error_token; + if (0xD800 > value || value > 0xDBFF) goto next_char; + + // UTF-16 Surrogate + utf32 = (value - 0xD800) << 10; + if (c++ + 6 >= l) goto end_of_stream; + if (*c++ != '\\') goto error_escape; + if (*c++ != 'u') goto error_escape; + CHECK_HEX(*c); value = jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c++); + CHECK_HEX(*c); value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*c); + + if (value < 0xDC00 || value > 0xDFFF) goto error_escape; + utf32 += value - 0xDC00 + 0x10000; + if ((utf32 & 0x0FFFFu) >= 0x0FFFEu) goto error_token; + goto next_char; + utf8: + type |= jsonlite_string_utf8; + int res = jsonlite_clz(((*c) ^ 0xFF) << 0x19); + utf32 = (*c & (0xFF >> (res + 1))); + value = 0xAAAAAAAA; // == 1010... + if (c + res >= l) goto end_of_stream; + switch (res) { + case 3: value = (value << 2) | (*++c >> 6); utf32 = (utf32 << 6) | (*c & 0x3F); + case 2: value = (value << 2) | (*++c >> 6); utf32 = (utf32 << 6) | (*c & 0x3F); + case 1: value = (value << 2) | (*++c >> 6); utf32 = (utf32 << 6) | (*c & 0x3F); + if (value != 0xAAAAAAAA) goto error_utf8; + if ((utf32 & 0xFFFFu) >= 0xFFFEu) goto error_utf8; + if (utf32 >= 0xFDD0u && utf32 <= 0xFDEFu) goto error_utf8; + } + goto next_char; + string_parsed: + token.string_type = type; + token.end = c; + parser->cursor = c + 1; + if (*state == state_value) { + CALL_VALUE_CALLBACK(parser->callbacks, string_found, &token); + } else { + CALL_VALUE_CALLBACK(parser->callbacks, key_found, &token); + } + state--; + goto skip_char_and_spaces; + } + +// Primitive tokens states. +true_token_parsing: + if (c++ + 3 >= l) goto end_of_stream; + if (*c++ != 'r') goto error_token; + if (*c++ != 'u') goto error_token; + if (*c++ != 'e') goto error_token; + CALL_STATE_CALLBACK(parser->callbacks, true_found); + state--; + goto state_selection; +false_token_paring: + if (c++ + 4 >= l) goto end_of_stream; + if (*c++ != 'a') goto error_token; + if (*c++ != 'l') goto error_token; + if (*c++ != 's') goto error_token; + if (*c++ != 'e') goto error_token; + CALL_STATE_CALLBACK(parser->callbacks, false_found); + state--; + goto state_selection; +null_token_parsing: + if (c++ + 3 >= l) goto end_of_stream; + if (*c++ != 'u') goto error_token; + if (*c++ != 'l') goto error_token; + if (*c++ != 'l') goto error_token; + CALL_STATE_CALLBACK(parser->callbacks, null_found); + state--; + goto state_selection; + +// Error handling states. +error_depth: parser->result = jsonlite_result_depth_limit; goto end; +error_exp_ooa: parser->result = jsonlite_result_expected_object_or_array; goto end; +error_exp_value: parser->result = jsonlite_result_expected_value; goto end; +error_exp_koe: parser->result = jsonlite_result_expected_key_or_end; goto end; +error_exp_key: parser->result = jsonlite_result_expected_key; goto end; +error_exp_colon: parser->result = jsonlite_result_expected_colon; goto end; +error_exp_coe: parser->result = jsonlite_result_expected_comma_or_end; goto end; +error_escape: parser->result = jsonlite_result_invalid_escape; goto end; +error_number: parser->result = jsonlite_result_invalid_number; goto end; +error_token: parser->result = jsonlite_result_invalid_token; goto end; +error_utf8: parser->result = jsonlite_result_invalid_utf8; goto end; +suspend: parser->result = jsonlite_result_suspended; goto end; +// End of stream states. +end_of_stream_space: + token_start = l; +end_of_stream: + parser->result = jsonlite_result_end_of_stream; + goto end; + +success: + parser->result = jsonlite_result_ok; +end: + parser->token_start = token_start; + parser->current = state; + parser->cursor = c; + parser->callbacks.parse_finished(&parser->callbacks.context); +} + +static void jsonlite_finish_parse(jsonlite_parser parser) { + if (parser->result == jsonlite_result_suspended) { + return; + } + + if (*parser->current == state_end) { + return; + } + + parser->rest_size = parser->limit - parser->token_start; + if (parser->rest_size > 0) { + parser->rest = malloc(parser->rest_size); + memcpy(parser->rest, parser->token_start, parser->rest_size); // LCOV_EXCL_LINE + } +} +// +// Copyright 2012-2013, Andrii Mamchur +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#ifndef JSONLITE_AMALGAMATED +#include "../include/jsonlite_builder.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <stdint.h> + +#define jsonlite_builder_check_depth() \ +do { \ + if (builder->state - builder->stack >= builder->stack_depth - 1) { \ + return jsonlite_result_depth_limit; \ + } \ +} while (0) + +typedef enum { + jsonlite_accept_object_begin = 0x0001, + jsonlite_accept_object_end = 0x0002, + jsonlite_accept_array_begin = 0x0004, + jsonlite_accept_array_end = 0x0008, + + jsonlite_accept_key = 0x0010, + jsonlite_accept_string = 0x0020, + jsonlite_accept_number = 0x0040, + jsonlite_accept_boolean = 0x0080, + jsonlite_accept_null = 0x0100, + jsonlite_accept_values_only = 0x0200, + jsonlite_accept_next = 0x0400, + + jsonlite_accept_value = 0 + | jsonlite_accept_object_begin + | jsonlite_accept_array_begin + | jsonlite_accept_string + | jsonlite_accept_number + | jsonlite_accept_boolean + | jsonlite_accept_null, + + jsonlite_accept_continue_object = 0 + | jsonlite_accept_next + | jsonlite_accept_key + | jsonlite_accept_object_end, + jsonlite_accept_continue_array = 0 + | jsonlite_accept_next + | jsonlite_accept_values_only + | jsonlite_accept_value + | jsonlite_accept_array_end + +} jsonlite_accept; + +typedef struct jsonlite_write_state { + int accept; +} jsonlite_write_state; + +typedef struct jsonlite_builder_buffer { + char data[2048]; + char *cursor; + char *limit; + struct jsonlite_builder_buffer *next; +} jsonlite_builder_buffer; + +typedef struct jsonlite_builder_struct { + jsonlite_builder_buffer *first; + jsonlite_builder_buffer *buffer; + + jsonlite_write_state *stack; + jsonlite_write_state *state; + ptrdiff_t stack_depth; + + char *doubleFormat; + + size_t indentation; +} jsonlite_builder_struct; + +static int jsonlite_builder_accept(jsonlite_builder builder, jsonlite_accept a); +static void jsonlite_builder_pop_state(jsonlite_builder builder); +static void jsonlite_builder_push_buffer(jsonlite_builder builder); +static void jsonlite_builder_prepare_value_writing(jsonlite_builder builder); +static void jsonlite_builder_raw_char(jsonlite_builder builder, char data); +static void jsonlite_builder_write_uft8(jsonlite_builder builder, const char *data, size_t length); +static void jsonlite_builder_raw(jsonlite_builder builder, const void *data, size_t length); +static void jsonlite_builder_repeat(jsonlite_builder builder, const char ch, size_t count); + +jsonlite_builder jsonlite_builder_init(size_t depth) { + jsonlite_builder builder; + + depth = depth < 2 ? 2 : depth; + + builder = (jsonlite_builder)calloc(1, sizeof(jsonlite_builder_struct) + depth * sizeof(jsonlite_write_state)); + builder->first = (jsonlite_builder_buffer *)malloc(sizeof(jsonlite_builder_buffer)); + builder->buffer = builder->first; + builder->buffer->cursor = builder->buffer->data; + builder->buffer->limit = builder->buffer->data + sizeof(builder->buffer->data); + builder->buffer->next = NULL; + + builder->stack = (jsonlite_write_state *)((uint8_t *)builder + sizeof(jsonlite_builder_struct)); + builder->stack_depth = depth; + builder->state = builder->stack; + builder->state->accept = jsonlite_accept_object_begin | jsonlite_accept_array_begin; + + builder->indentation = 0; + jsonlite_builder_set_double_format(builder, "%.16g"); + return builder; +} + +jsonlite_result jsonlite_builder_release(jsonlite_builder builder) { + jsonlite_builder_buffer *b = NULL; + void *prev; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + for (b = builder->first; b != NULL;) { + prev = b; + b = b->next; + free(prev); + } + + free(builder->doubleFormat); + free(builder); + return jsonlite_result_ok; +} + +jsonlite_result jsonlite_builder_set_indentation(jsonlite_builder builder, size_t indentation) { + if (builder != NULL) { + builder->indentation = indentation; + return jsonlite_result_ok; + } + return jsonlite_result_invalid_argument; +} + +jsonlite_result jsonlite_builder_set_double_format(jsonlite_builder builder, const char *format) { + if (builder != NULL && format != NULL) { + builder->doubleFormat = strdup(format); + return jsonlite_result_ok; + } + return jsonlite_result_invalid_argument; +} + +static int jsonlite_builder_accept(jsonlite_builder builder, jsonlite_accept a) { + return (builder->state->accept & a) == a; +} + +static void jsonlite_builder_push_state(jsonlite_builder builder) { + builder->state++; +} + +static void jsonlite_builder_pop_state(jsonlite_builder builder) { + jsonlite_write_state *ws = --builder->state; + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } +} + +static void jsonlite_builder_push_buffer(jsonlite_builder builder) { + jsonlite_builder_buffer *buffer = builder->buffer; + buffer->next = malloc(sizeof(jsonlite_builder_buffer)); + buffer = builder->buffer = buffer->next; + + buffer->cursor = buffer->data; + buffer->limit = buffer->data + sizeof(buffer->data); + buffer->next = NULL; +} + +static void jsonlite_builder_prepare_value_writing(jsonlite_builder builder) { + jsonlite_write_state *ws = builder->state; + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + if (jsonlite_builder_accept(builder, jsonlite_accept_next) ) { + jsonlite_builder_raw_char(builder, ','); + } + if (builder->indentation != 0) { + jsonlite_builder_raw_char(builder, '\r'); + jsonlite_builder_repeat(builder, ' ', (builder->state - builder->stack) * builder->indentation); + } + } else { + ws->accept &= ~jsonlite_accept_value; + ws->accept |= jsonlite_accept_key; + } + ws->accept |= jsonlite_accept_next; +} + +jsonlite_result jsonlite_builder_object_begin(jsonlite_builder builder) { + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + jsonlite_builder_check_depth(); + + if (jsonlite_builder_accept(builder, jsonlite_accept_object_begin)) { + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_push_state(builder); + builder->state->accept = jsonlite_accept_object_end | jsonlite_accept_key; + jsonlite_builder_raw_char(builder, '{'); + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_object_end(jsonlite_builder builder) { + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + if (jsonlite_builder_accept(builder, jsonlite_accept_object_end)) { + jsonlite_builder_pop_state(builder); + if (builder->indentation != 0) { + jsonlite_builder_raw_char(builder, '\r'); + jsonlite_builder_repeat(builder, ' ', (builder->state - builder->stack) * builder->indentation); + } + jsonlite_builder_raw_char(builder, '}'); + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_array_begin(jsonlite_builder builder) { + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + jsonlite_builder_check_depth(); + + if (jsonlite_builder_accept(builder, jsonlite_accept_array_begin)) { + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_push_state(builder); + builder->state->accept = jsonlite_accept_array_end + | jsonlite_accept_value + | jsonlite_accept_values_only; + jsonlite_builder_raw_char(builder, '['); + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_array_end(jsonlite_builder builder) { + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + if (jsonlite_builder_accept(builder, jsonlite_accept_array_end)) { + jsonlite_builder_pop_state(builder); + if (builder->indentation != 0) { + jsonlite_builder_raw_char(builder, '\r'); + jsonlite_builder_repeat(builder, ' ', (builder->state - builder->stack) * builder->indentation); + } + jsonlite_builder_raw_char(builder, ']'); + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +static void jsonlite_builder_write_uft8(jsonlite_builder builder, const char *data, size_t length) { + size_t i; + jsonlite_builder_raw_char(builder, '\"'); + for (i = 0; i < length; i++) { + switch (data[i]) { + case '"': + jsonlite_builder_raw(builder, "\\\"", 2); + break; + case '\\': + jsonlite_builder_raw(builder, "\\\\", 2); + break; + case '\b': + jsonlite_builder_raw(builder, "\\b", 2); + break; + case '\f': + jsonlite_builder_raw(builder, "\\f", 2); + break; + case '\n': + jsonlite_builder_raw(builder, "\\n", 2); + break; + case '\r': + jsonlite_builder_raw(builder, "\\r", 2); + break; + case '\t': + jsonlite_builder_raw(builder, "\\t", 2); + break; + default: + jsonlite_builder_raw_char(builder, data[i]); + break; + } + } + jsonlite_builder_raw_char(builder, '\"'); +} + +jsonlite_result jsonlite_builder_key(jsonlite_builder builder, const char *data, size_t length) { + jsonlite_write_state *ws; + + if (builder == NULL || data == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_key) ) { + if (jsonlite_builder_accept(builder, jsonlite_accept_next) ) { + jsonlite_builder_raw_char(builder, ','); + } + if (builder->indentation != 0) { + jsonlite_builder_raw_char(builder, '\r'); + jsonlite_builder_repeat(builder, ' ', (builder->state - builder->stack) * builder->indentation); + } + jsonlite_builder_write_uft8(builder, data, length); + if (builder->indentation != 0) { + jsonlite_builder_raw(builder, ": ", 2); + } else { + jsonlite_builder_raw_char(builder, ':'); + } + ws->accept = jsonlite_accept_value; + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_string(jsonlite_builder builder, const char *data, size_t length) { + jsonlite_write_state *ws; + + if (builder == NULL || data == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_value) ) { + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_write_uft8(builder, data, length); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_int(jsonlite_builder builder, long long value) { + jsonlite_write_state *ws; + char buff[128]; + int size = 0; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_value) ) { + jsonlite_builder_prepare_value_writing(builder); + size = sprintf(buff, "%lld", value); + jsonlite_builder_raw(builder, buff, size); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_double(jsonlite_builder builder, double value) { + jsonlite_write_state *ws; + char buff[128]; + int size = 0; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_value) ) { + jsonlite_builder_prepare_value_writing(builder); + size = sprintf(buff, builder->doubleFormat, value); + jsonlite_builder_raw(builder, buff, size); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_true(jsonlite_builder builder) { + static const char value[] = "true"; + jsonlite_write_state *ws; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + if (!(jsonlite_builder_accept(builder, jsonlite_accept_value) )) { + + return jsonlite_result_not_allowed; + } + + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_raw(builder, (char *)value, sizeof(value) - 1); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; +} + +jsonlite_result jsonlite_builder_false(jsonlite_builder builder) { + static const char value[] = "false"; + jsonlite_write_state *ws; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + if (!(jsonlite_builder_accept(builder, jsonlite_accept_value) )) { + + return jsonlite_result_not_allowed; + } + + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_raw(builder, (char *)value, sizeof(value) - 1); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; +} + +jsonlite_result jsonlite_builder_null(jsonlite_builder builder) { + static const char value[] = "null"; + jsonlite_write_state *ws; + + if (builder == NULL) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + if (!(jsonlite_builder_accept(builder, jsonlite_accept_value) )) { + + return jsonlite_result_not_allowed; + } + + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_raw(builder, (char *)value, sizeof(value) - 1); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; +} + +static void jsonlite_builder_raw(jsonlite_builder builder, const void *data, size_t length) { + jsonlite_builder_buffer *buffer = builder->buffer; + size_t write_limit = buffer->limit - buffer->cursor; + if (write_limit >= length) { + memcpy(buffer->cursor, data, length); // LCOV_EXCL_LINE + buffer->cursor += length; + } else { + memcpy(buffer->cursor, data, write_limit); // LCOV_EXCL_LINE + buffer->cursor += write_limit; + + jsonlite_builder_push_buffer(builder); + jsonlite_builder_raw(builder, (char *)data + write_limit, length - write_limit); + } +} + +static void jsonlite_builder_repeat(jsonlite_builder builder, const char ch, size_t count) { + jsonlite_builder_buffer *buffer = builder->buffer; + size_t write_limit = buffer->limit - buffer->cursor; + if (write_limit >= count) { + memset(buffer->cursor, ch, count); // LCOV_EXCL_LINE + buffer->cursor += count; + } else { + memset(buffer->cursor, ch, write_limit); // LCOV_EXCL_LINE + buffer->cursor += write_limit; + + jsonlite_builder_push_buffer(builder); + jsonlite_builder_repeat(builder, ch, count - write_limit); + } +} + +static void jsonlite_builder_raw_char(jsonlite_builder builder, char data) { + jsonlite_builder_buffer *buffer = builder->buffer; + if (buffer->cursor >= buffer->limit) { + jsonlite_builder_push_buffer(builder); + } + *builder->buffer->cursor++ = data; +} + +jsonlite_result jsonlite_builder_raw_key(jsonlite_builder builder, const void *data, size_t length) { + jsonlite_write_state *ws; + + if (builder == NULL || data == NULL || length == 0) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + if (jsonlite_builder_accept(builder, jsonlite_accept_key) ) { + if (jsonlite_builder_accept(builder, jsonlite_accept_next) ) { + jsonlite_builder_raw(builder, ",", 1); + } + + if (builder->indentation != 0) { + jsonlite_builder_raw_char(builder, '\r'); + jsonlite_builder_repeat(builder, ' ', (builder->state - builder->stack) * builder->indentation); + } + jsonlite_builder_raw_char(builder, '\"'); + jsonlite_builder_raw(builder, data, length); + jsonlite_builder_raw_char(builder, '\"'); + if (builder->indentation != 0) { + jsonlite_builder_raw(builder, ": ", 2); + } else { + jsonlite_builder_raw_char(builder, ':'); + } + ws->accept = jsonlite_accept_value; + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_raw_string(jsonlite_builder builder, const void *data, size_t length) { + jsonlite_write_state *ws; + + if (builder == NULL || data == NULL || length == 0) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_value) ) { + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_raw_char(builder, '\"'); + jsonlite_builder_raw(builder, data, length); + jsonlite_builder_raw_char(builder, '\"'); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_raw_value(jsonlite_builder builder, const void *data, size_t length) { + jsonlite_write_state *ws; + + if (builder == NULL || data == NULL || length == 0) { + return jsonlite_result_invalid_argument; + } + + ws = builder->state; + + if (jsonlite_builder_accept(builder, jsonlite_accept_value) ) { + jsonlite_builder_prepare_value_writing(builder); + jsonlite_builder_raw(builder, data, length); + if (jsonlite_builder_accept(builder, jsonlite_accept_values_only) ) { + ws->accept = jsonlite_accept_continue_array; + } else { + ws->accept = jsonlite_accept_continue_object; + } + return jsonlite_result_ok; + } + + return jsonlite_result_not_allowed; +} + +jsonlite_result jsonlite_builder_data(jsonlite_builder builder, char **data, size_t *size) { + jsonlite_builder_buffer *b; + char *buff = NULL; + + if (builder == NULL || data == NULL || size == NULL) { + return jsonlite_result_invalid_argument; + } + + *size = 0; + for (b = builder->first; b != NULL; b = b->next) { + *size += b->cursor - b->data; + } + + if (*size == 0) { + return jsonlite_result_not_allowed; + } + + *data = (char*)calloc(*size, 1); + buff = *data; + for (b = builder->first; b != NULL; b = b->next) { + size_t s = b->cursor - b->data; + memcpy(buff, b->data, s); // LCOV_EXCL_LINE + buff += s; + } + return jsonlite_result_ok; +} +// +// Copyright 2012-2013, Andrii Mamchur +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#ifndef JSONLITE_AMALGAMATED +#include "../include/jsonlite_token.h" +#endif + +#include <stdlib.h> + +#ifdef _MSC_VER +#include <intrin.h> + +static uint32_t __inline jsonlite_clz( uint32_t x ) { + unsigned long r = 0; + _BitScanForward(&r, x); + return r; +} + +#else + +#define jsonlite_clz(x) __builtin_clz((x)) + +#endif + +uint8_t jsonlite_hex_char_to_uint8(uint8_t c) { + uint8_t res = 0xFF; + if (c >= '0' && c <= '9') { + res = c - '0'; + } else if (c >= 'a' && c <= 'f') { + res = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + res = c - 'A' + 10; + } + return res; +} + +static int unicode_char_to_utf16(uint32_t ch, uint16_t *utf16) { + uint32_t v = ch - 0x10000; + uint32_t vh = v >> 10; + uint32_t vl = v & 0x3FF; + if (ch <= 0xFFFF) { + *utf16 = (uint16_t)ch; + return 1; + } + + *utf16++ = (uint16_t)(0xD800 + vh); + *utf16 = (uint16_t)(0xDC00 + vl); + return 2; +} + +size_t jsonlite_token_decode_size_for_uft8(jsonlite_token *ts) { + if (ts == NULL) { + return 0; + } + + return ts->end - ts->start + 1; +} + +size_t jsonlite_token_decode_to_uft8(jsonlite_token *ts, uint8_t **buffer) { + size_t size = jsonlite_token_decode_size_for_uft8(ts); + if (size == 0 || buffer == NULL) { + return 0; + } + + const uint8_t *p = ts->start; + const uint8_t *l = ts->end; + uint32_t value, utf32; + uint8_t *c = *buffer = (uint8_t *)malloc(size); + int res; +step: + if (p == l) goto done; + if (*p == '\\') goto escaped; + if (*p >= 0x80) goto utf8; + *c++ = *p++; + goto step; +escaped: + switch (*++p) { + case 34: *c++ = '"'; p++; goto step; + case 47: *c++ = '/'; p++; goto step; + case 92: *c++ = '\\'; p++; goto step; + case 98: *c++ = '\b'; p++; goto step; + case 102: *c++ = '\f'; p++; goto step; + case 110: *c++ = '\n'; p++; goto step; + case 114: *c++ = '\r'; p++; goto step; + case 116: *c++ = '\t'; p++; goto step; + } + + // UTF-16 + p++; + utf32 = jsonlite_hex_char_to_uint8(*p++); + utf32 = (uint32_t)(utf32 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf32 = (uint32_t)(utf32 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf32 = (uint32_t)(utf32 << 4) | jsonlite_hex_char_to_uint8(*p++); + if (0xD800 > utf32 || utf32 > 0xDBFF) goto encode; + + // UTF-16 Surrogate + p += 2; + utf32 = (utf32 - 0xD800) << 10; + value = jsonlite_hex_char_to_uint8(*p++); + value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*p++); + value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*p++); + value = (uint32_t)(value << 4) | jsonlite_hex_char_to_uint8(*p++); + utf32 += value - 0xDC00 + 0x10000; +encode: + if (utf32 < 0x80) { + *c++ = (uint8_t)utf32; + } else if (utf32 < 0x0800) { + c[1] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[0] = (uint8_t)utf32 | 0xC0; + c += 2; + } else if (utf32 < 0x10000) { + c[2] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[1] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[0] = (uint8_t)utf32 | 0xE0; + c += 3; + } else { + c[3] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[2] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[1] = (uint8_t)(utf32 & 0x3F) | 0x80; + utf32 = utf32 >> 6; + c[0] = (uint8_t)utf32 | 0xF0; + c += 4; + } + goto step; +utf8: + res = jsonlite_clz(((*p) ^ 0xFF) << 0x19); + *c++ = *p++; + switch (res) { + case 3: *c++ = *p++; + case 2: *c++ = *p++; + case 1: *c++ = *p++; + } + goto step; +done: + *c = 0; + return c - *buffer; +} + +size_t jsonlite_token_decode_size_for_uft16(jsonlite_token *ts) { + if (ts == NULL) { + return 0; + } + + return (ts->end - ts->start + 1) * sizeof(uint16_t); +} + +size_t jsonlite_token_decode_to_uft16(jsonlite_token *ts, uint16_t **buffer) { + size_t size = jsonlite_token_decode_size_for_uft16(ts); + if (size == 0 || buffer == NULL) { + return 0; + } + + const uint8_t *p = ts->start; + const uint8_t *l = ts->end; + uint16_t utf16; + uint16_t *c = *buffer = (uint16_t *)malloc(size); + int res; +step: + if (p == l) goto done; + if (*p == '\\') goto escaped; + if (*p >= 0x80) goto utf8; + *c++ = *p++; + goto step; +escaped: + switch (*++p) { + case 34: *c++ = '"'; p++; goto step; + case 47: *c++ = '/'; p++; goto step; + case 92: *c++ = '\\'; p++; goto step; + case 98: *c++ = '\b'; p++; goto step; + case 102: *c++ = '\f'; p++; goto step; + case 110: *c++ = '\n'; p++; goto step; + case 114: *c++ = '\r'; p++; goto step; + case 116: *c++ = '\t'; p++; goto step; + } + + // UTF-16 + p++; + utf16 = jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + *c++ = utf16; + if (0xD800 > utf16 || utf16 > 0xDBFF) goto step; + + // UTF-16 Surrogate + p += 2; + utf16 = jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + utf16 = (uint16_t)(utf16 << 4) | jsonlite_hex_char_to_uint8(*p++); + *c++ = utf16; + goto step; +utf8: + res = jsonlite_clz(((*p) ^ 0xFF) << 0x19); + uint32_t code = (*p & (0xFF >> (res + 1))); + switch (res) { + case 3: code = (code << 6) | (*++p & 0x3F); + case 2: code = (code << 6) | (*++p & 0x3F); + case 1: code = (code << 6) | (*++p & 0x3F); + case 0: ++p; + } + + c += unicode_char_to_utf16(code, c); + goto step; +done: + *c = 0; + return (c - *buffer) * sizeof(uint16_t); +} +// +// Copyright 2012-2013, Andrii Mamchur +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#ifndef JSONLITE_AMALGAMATED +#include "../include/jsonlite_token_pool.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#define JSONLITE_TOKEN_POOL_FRONT 0x80 +#define JSONLITE_TOKEN_POOL_FRONT_MASK (JSONLITE_TOKEN_POOL_FRONT - 1) + +typedef struct content_pool_size { + jsonlite_token_bucket *buckets[JSONLITE_TOKEN_POOL_FRONT]; + size_t buckets_length[JSONLITE_TOKEN_POOL_FRONT]; + size_t buckets_capacity[JSONLITE_TOKEN_POOL_FRONT]; + + uint8_t *content_pool; + size_t content_pool_size; + + jsonlite_token_pool_release_value_fn release_fn; + +} jsonlite_token_pool_struct; + +static void jsonlite_extend_capacity(jsonlite_token_pool pool, int index); +static int jsonlite_bucket_not_copied(jsonlite_token_pool pool, jsonlite_token_bucket *b); +static int jsonlite_token_compare(const uint8_t *t1, const uint8_t *t2, size_t length); +static uint32_t jsonlite_hash(const uint8_t *data, size_t len); + +jsonlite_token_pool jsonlite_token_pool_create(jsonlite_token_pool_release_value_fn release_fn) { + jsonlite_token_pool pool = (jsonlite_token_pool)calloc(1, sizeof(jsonlite_token_pool_struct)); + pool->release_fn = release_fn; + return pool; +} + +void jsonlite_token_pool_copy_tokens(jsonlite_token_pool pool) { + jsonlite_token_bucket *b; + size_t size = pool->content_pool_size; + int i; + + for (i = 0; i < JSONLITE_TOKEN_POOL_FRONT; i++) { + b = pool->buckets[i]; + if (jsonlite_bucket_not_copied(pool, b)) { + size += b->end - b->start; + } + } + + if (size == pool->content_pool_size) { + return; + } + + uint8_t *buffer = (uint8_t *)malloc(size); + ptrdiff_t offset = 0; + if (pool->content_pool != NULL) { + offset = buffer - pool->content_pool; + memcpy(buffer, pool->content_pool, pool->content_pool_size); // LCOV_EXCL_LINE + } + + uint8_t *p = buffer + pool->content_pool_size; + for (i = 0; i < JSONLITE_TOKEN_POOL_FRONT; i++) { + b = pool->buckets[i]; + if (b == NULL) { + continue; + } + + if (jsonlite_bucket_not_copied(pool, b)) { + size_t length = b->end - b->start; + memcpy(p, b->start, length); // LCOV_EXCL_LINE + b->start = p, + b->end = p + length, + p += length; + } else { + b->start += offset; + b->end += offset; + } + } + + free(pool->content_pool); + pool->content_pool = buffer; + pool->content_pool_size = size; +} + +void jsonlite_token_pool_release(jsonlite_token_pool pool) { + int i, j; + if (pool == NULL) { + return; + } + + for (i = 0; i < JSONLITE_TOKEN_POOL_FRONT; i++) { + jsonlite_token_bucket *bucket = pool->buckets[i]; + if (bucket == NULL) { + continue; + } + + if (pool->release_fn != NULL) { + size_t count = pool->buckets_length[i]; + for (j = 0; j < count; j++, bucket++) { + pool->release_fn((void *)bucket->value); + } + } + + free(pool->buckets[i]); + } + + free(pool->content_pool); + free(pool); +} + +jsonlite_token_bucket* jsonlite_token_pool_get_bucket(jsonlite_token_pool pool, jsonlite_token *token) { + if (pool == NULL || token == NULL) { + return NULL; + } + + if (token->start == NULL || token->end == NULL) { + return NULL; + } + + size_t length = token->end - token->start; + uint32_t hash = jsonlite_hash(token->start, length); + uint32_t index = hash & JSONLITE_TOKEN_POOL_FRONT_MASK; + jsonlite_token_bucket *bucket = pool->buckets[index]; + size_t count = pool->buckets_length[index]; + for (; count > 0; count--, bucket++) { + if (bucket->hash != hash) { + continue; + } + + if (length != bucket->end - bucket->start) { + continue; + } + + if (jsonlite_token_compare(token->start, bucket->start, length)) { + return bucket; + } + } + + if (pool->buckets_length[index] >= pool->buckets_capacity[index]) { + jsonlite_extend_capacity(pool, index); + } + + bucket = pool->buckets[index] + pool->buckets_length[index]++; + bucket->hash = hash; + bucket->start = token->start; + bucket->end = token->end; + bucket->value = NULL; + return bucket; +} + +static int jsonlite_token_compare(const uint8_t *t1, const uint8_t *t2, size_t length) { + return memcmp(t1, t2, length) == 0 ? 1 : 0; +} + +static void jsonlite_extend_capacity(jsonlite_token_pool pool, int index) { + size_t capacity = pool->buckets_capacity[index]; + if (capacity == 0) { + capacity = 0x10; + } + + size_t size = capacity * sizeof(jsonlite_token_bucket); + jsonlite_token_bucket *b = pool->buckets[index]; + jsonlite_token_bucket *extended = (jsonlite_token_bucket *)malloc(2 * size); + + if (b != NULL) { + memcpy(extended, b, size); // LCOV_EXCL_LINE + free(b); + } + + pool->buckets[index] = extended; + pool->buckets_capacity[index] = 2 * capacity; +} + +static int jsonlite_bucket_not_copied(jsonlite_token_pool pool, jsonlite_token_bucket *b) { + if (b == NULL) { + return 0; + } + + int res = b->start < pool->content_pool; + res |= b->start >= pool->content_pool + pool->content_pool_size; + return res; +} + +// Used MurmurHash2 function by Austin Appleby +// http://code.google.com/p/smhasher/ revision 147 + +//----------------------------------------------------------------------------- +// MurmurHash2 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - This code makes a few assumptions about how your machine behaves - + +// 1. We can read a 4-byte value from any address without crashing +// 2. sizeof(int) == 4 + +// And it has a few limitations - + +// 1. It will not work incrementally. +// 2. It will not produce the same results on little-endian and big-endian +// machines. + +static uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const uint32_t m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + uint32_t h = seed ^ len; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + uint32_t k = *(uint32_t*)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +//----------------------------------------------------------------------------- + +static uint32_t jsonlite_hash(const uint8_t *data, size_t len) { + return MurmurHash2(data, (int)len, 0); +} +