yajl_handle yajl_alloc(const yajl_callbacks * callbacks, const yajl_parser_config * config, void * ctx) { unsigned int allowComments = 0; unsigned int validateUTF8 = 0; yajl_handle hand = (yajl_handle) malloc(sizeof(struct yajl_handle_t)); if (config != NULL) { allowComments = config->allowComments; validateUTF8 = config->checkUTF8; } hand->callbacks = callbacks; hand->ctx = ctx; hand->lexer = yajl_lex_alloc(allowComments, validateUTF8); hand->errorOffset = 0; hand->decodeBuf = yajl_buf_alloc(); hand->stateBuf = yajl_buf_alloc(); yajl_state_push(hand, yajl_state_start); return hand; }
yajl_status yajl_do_parse(yajl_handle hand, unsigned int * offset, const unsigned char * jsonText, unsigned int jsonTextLen) { yajl_tok tok; const unsigned char * buf; unsigned int bufLen; around_again: switch (yajl_state_current(hand)) { case yajl_state_parse_complete: return yajl_status_ok; case yajl_state_lexical_error: case yajl_state_parse_error: hand->errorOffset = *offset; 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_state_set(hand, 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, (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((char *) buf, NULL, 10); if ((i == LONG_MIN || i == LONG_MAX) && errno == ERANGE) { yajl_state_set(hand, 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, (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_state_set(hand, 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_state_current(hand) == yajl_state_array_start) { if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } (void) yajl_state_pop(hand); goto around_again; } /* intentional fall-through */ } case yajl_tok_colon: case yajl_tok_comma: case yajl_tok_right_bracket: yajl_state_set(hand, yajl_state_parse_error); hand->parseError = "unallowed token at this point in JSON text"; goto around_again; default: yajl_state_set(hand, 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_state_current(hand); if (s == yajl_state_start) { yajl_state_set(hand, yajl_state_parse_complete); } else if (s == yajl_state_map_need_val) { yajl_state_set(hand, yajl_state_map_got_val); } else { yajl_state_set(hand, yajl_state_array_got_val); } } if (stateToPush != yajl_state_start) { yajl_state_push(hand, 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_state_set(hand, 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_state_set(hand, yajl_state_map_sep); goto around_again; case yajl_tok_right_bracket: if (yajl_state_current(hand) == yajl_state_map_start) { if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } (void) yajl_state_pop(hand); goto around_again; } default: yajl_state_set(hand, 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_state_set(hand, yajl_state_map_need_val); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_state_set(hand, yajl_state_lexical_error); goto around_again; default: yajl_state_set(hand, 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)); } (void) yajl_state_pop(hand); goto around_again; case yajl_tok_comma: yajl_state_set(hand, yajl_state_map_need_key); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_state_set(hand, yajl_state_lexical_error); goto around_again; default: yajl_state_set(hand, 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)); } (void) yajl_state_pop(hand); goto around_again; case yajl_tok_comma: yajl_state_set(hand, yajl_state_array_need_val); goto around_again; case yajl_tok_eof: return yajl_status_insufficient_data; case yajl_tok_error: yajl_state_set(hand, yajl_state_lexical_error); goto around_again; default: yajl_state_set(hand, yajl_state_parse_error); hand->parseError = "after array element, I expect ',' or ']'"; goto around_again; } } } abort(); return yajl_status_error; }