yajl_status yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, unsigned int jsonTextLen) { yajl_tok tok; const unsigned char * buf; unsigned int bufLen; unsigned int * offset = &(hand->bytesConsumed); *offset = 0; around_again: switch (yajl_bs_current(hand->stateStack)) { case yajl_state_parse_complete: return yajl_status_ok; case yajl_state_lexical_error: case yajl_state_parse_error: return yajl_status_error; case yajl_state_start: case yajl_state_map_need_val: case yajl_state_array_need_val: case yajl_state_array_start: { /* for arrays and maps, we advance the state for this * depth, then push the state of the next depth. * If an error occurs during the parsing of the nesting * enitity, the state at this level will not matter. * a state that needs pushing will be anything other * than state_start */ yajl_state stateToPush = yajl_state_start; tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_string) { _CC_CHK(hand->callbacks->yajl_string(hand->ctx, buf, bufLen)); } break; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_string) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); _CC_CHK(hand->callbacks->yajl_string( hand->ctx, yajl_buf_data(hand->decodeBuf), yajl_buf_len(hand->decodeBuf))); } break; case yajl_tok_bool: if (hand->callbacks && hand->callbacks->yajl_boolean) { _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, *buf == 't')); } break; case yajl_tok_null: if (hand->callbacks && hand->callbacks->yajl_null) { _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); } break; case yajl_tok_left_bracket: if (hand->callbacks && hand->callbacks->yajl_start_map) { _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); } stateToPush = yajl_state_map_start; break; case yajl_tok_left_brace: if (hand->callbacks && hand->callbacks->yajl_start_array) { _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); } stateToPush = yajl_state_array_start; break; case yajl_tok_integer: /* * note. strtol does not respect the length of * the lexical token. in a corner case where the * lexed number is a integer with a trailing zero, * immediately followed by the end of buffer, * sscanf could run off into oblivion and cause a * crash. for this reason we copy the integer * (and doubles), into our parse buffer (the same * one used for unescaping strings), before * calling strtol. yajl_buf ensures null padding, * so we're safe. */ if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx,(const char *) buf, bufLen)); } else if (hand->callbacks->yajl_integer) { long int i = 0; yajl_buf_clear(hand->decodeBuf); yajl_buf_append(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); i = strtol((const char *) buf, NULL, 10); if ((i == LONG_MIN || i == LONG_MAX) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "integer overflow" ; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, i)); } } break; case yajl_tok_double: if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx, (const char *) buf, bufLen)); } else if (hand->callbacks->yajl_double) { double d = 0.0; yajl_buf_clear(hand->decodeBuf); yajl_buf_append(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); d = strtod((char *) buf, NULL); if ((d == HUGE_VAL || d == -HUGE_VAL) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "numeric (floating point) " "overflow"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_double(hand->ctx, d)); } } break; case yajl_tok_right_brace: { if (yajl_bs_current(hand->stateStack) == yajl_state_array_start) { if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } /* intentional fall-through */ } case yajl_tok_colon: case yajl_tok_comma: case yajl_tok_right_bracket: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "unallowed token at this point in JSON text"; goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid token, internal error"; goto around_again; } /* got a value. transition depends on the state we're in. */ { yajl_state s = yajl_bs_current(hand->stateStack); if (s == yajl_state_start) { yajl_bs_set(hand->stateStack, yajl_state_parse_complete); } else if (s == yajl_state_map_need_val) { yajl_bs_set(hand->stateStack, yajl_state_map_got_val); } else { yajl_bs_set(hand->stateStack, yajl_state_array_got_val); } } if (stateToPush != yajl_state_start) { yajl_bs_push(hand->stateStack, stateToPush); } goto around_again; } case yajl_state_map_start: case yajl_state_map_need_key: { /* only difference between these two states is that in * start '}' is valid, whereas in need_key, we've parsed * a comma, and a string key _must_ follow */ tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_map_key) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); bufLen = yajl_buf_len(hand->decodeBuf); } /* intentional fall-through */ case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_map_key) { _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, bufLen)); } yajl_bs_set(hand->stateStack, yajl_state_map_sep); goto around_again; case yajl_tok_right_bracket: if (yajl_bs_current(hand->stateStack) == yajl_state_map_start) { if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid object key (must be a string)"; goto around_again; } } case yajl_state_map_sep: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_colon: yajl_bs_set(hand->stateStack, yajl_state_map_need_val); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "object key and value must " "be separated by a colon (':')"; goto around_again; } } case yajl_state_map_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_bracket: if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_map_need_key); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after key and value, inside map, " "I expect ',' or '}'"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } } case yajl_state_array_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_brace: if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_array_need_val); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after array element, I expect ',' or ']'"; goto around_again; } } } abort(); return yajl_status_error; }
yajl_status yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen) { yajl_tok tok; const unsigned char * buf; size_t bufLen; size_t * offset = &(hand->bytesConsumed); *offset = 0; around_again: switch (yajl_bs_current(hand->stateStack)) { case yajl_state_parse_complete: if (hand->flags & yajl_allow_multiple_values) { yajl_bs_set(hand->stateStack, yajl_state_got_value); goto around_again; } if (!(hand->flags & yajl_allow_trailing_garbage)) { if (*offset != jsonTextLen) { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); if (tok != yajl_tok_eof) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "trailing garbage"; } goto around_again; } } return yajl_status_ok; case yajl_state_lexical_error: case yajl_state_parse_error: return yajl_status_error; case yajl_state_start: case yajl_state_got_value: case yajl_state_map_need_val: case yajl_state_array_need_val: case yajl_state_array_start: { /* for arrays and maps, we advance the state for this * depth, then push the state of the next depth. * If an error occurs during the parsing of the nesting * enitity, the state at this level will not matter. * a state that needs pushing will be anything other * than state_start */ yajl_state stateToPush = yajl_state_start; tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_string) { _CC_CHK(hand->callbacks->yajl_string(hand->ctx, buf, bufLen)); } break; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_string) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); _CC_CHK(hand->callbacks->yajl_string( hand->ctx, yajl_buf_data(hand->decodeBuf), yajl_buf_len(hand->decodeBuf))); } break; case yajl_tok_bool: if (hand->callbacks && hand->callbacks->yajl_boolean) { _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, *buf == 't')); } break; case yajl_tok_null: if (hand->callbacks && hand->callbacks->yajl_null) { _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); } break; case yajl_tok_left_bracket: if (hand->callbacks && hand->callbacks->yajl_start_map) { _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); } stateToPush = yajl_state_map_start; break; case yajl_tok_left_brace: if (hand->callbacks && hand->callbacks->yajl_start_array) { _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); } stateToPush = yajl_state_array_start; break; case yajl_tok_integer: if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx,(const char *) buf, bufLen)); } else if (hand->callbacks->yajl_integer) { long long int i = 0; i = yajl_parse_integer(buf, bufLen); if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) { unsigned long long ui = 0; int parsing_failed = 1; if (hand->callbacks->yajl_uinteger) { errno = 0; ui = yajl_parse_uinteger(buf, bufLen); if ((ui == 0 || ui == ULLONG_MAX) && errno == ERANGE) parsing_failed = 1; else parsing_failed = 0; } if (parsing_failed) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "integer overflow" ; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } else { _CC_CHK(hand->callbacks->yajl_uinteger(hand->ctx, ui)); } } else { _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, i)); } } else if (hand->callbacks->yajl_uinteger) { unsigned long long ui = 0; ui = yajl_parse_uinteger(buf, bufLen); if ((ui == 0 || ui == ULLONG_MAX) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "integer overflow" ; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_uinteger(hand->ctx, ui)); } } break; case yajl_tok_double: if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx, (const char *) buf, bufLen)); } else if (hand->callbacks->yajl_double) { double d = 0.0; yajl_buf_clear(hand->decodeBuf); yajl_buf_append(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); d = strtod((char *) buf, NULL); if ((d == HUGE_VAL || d == -HUGE_VAL) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "numeric (floating point) " "overflow"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_double(hand->ctx, d)); } } break; case yajl_tok_right_brace: { if (yajl_bs_current(hand->stateStack) == yajl_state_array_start) { if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } /* intentional fall-through */ } case yajl_tok_colon: case yajl_tok_comma: case yajl_tok_right_bracket: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "unallowed token at this point in JSON text"; goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid token, internal error"; goto around_again; } /* got a value. transition depends on the state we're in. */ { yajl_state s = yajl_bs_current(hand->stateStack); if (s == yajl_state_start || s == yajl_state_got_value) { yajl_bs_set(hand->stateStack, yajl_state_parse_complete); } else if (s == yajl_state_map_need_val) { yajl_bs_set(hand->stateStack, yajl_state_map_got_val); } else { yajl_bs_set(hand->stateStack, yajl_state_array_got_val); } } if (stateToPush != yajl_state_start) { yajl_bs_push(hand->stateStack, stateToPush); } goto around_again; } case yajl_state_map_start: case yajl_state_map_need_key: { /* only difference between these two states is that in * start '}' is valid, whereas in need_key, we've parsed * a comma, and a string key _must_ follow */ tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_map_key) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); bufLen = yajl_buf_len(hand->decodeBuf); } /* intentional fall-through */ case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_map_key) { _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, bufLen)); } yajl_bs_set(hand->stateStack, yajl_state_map_sep); goto around_again; case yajl_tok_right_bracket: if (yajl_bs_current(hand->stateStack) == yajl_state_map_start) { if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid object key (must be a string)"; goto around_again; } } case yajl_state_map_sep: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_colon: yajl_bs_set(hand->stateStack, yajl_state_map_need_val); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "object key and value must " "be separated by a colon (':')"; goto around_again; } } case yajl_state_map_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_bracket: if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_map_need_key); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after key and value, inside map, " "I expect ',' or '}'"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } } case yajl_state_array_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_brace: if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_array_need_val); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after array element, I expect ',' or ']'"; goto around_again; } } } abort(); return yajl_status_error; }