static int json_skip_string(struct json_parser *parser) { for (; parser->data != parser->end; parser->data++) { if (*parser->data == '"') { parser->data++; json_parser_update_input_pos(parser); return 1; } if (*parser->data == '\\') { switch (*++parser->data) { case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': break; case 'u': if (parser->end - parser->data < 4) return -1; parser->data += 3; break; default: return -1; } } } json_parser_update_input_pos(parser); return 0; }
static int json_try_parse_stream_start(struct json_parser *parser, struct istream **input_r) { if (!json_parse_whitespace(parser)) return -1; if (parser->state == JSON_STATE_OBJECT_COLON) { if (*parser->data != ':') { parser->error = "Expected ':' after key"; return -1; } parser->data++; parser->state = JSON_STATE_OBJECT_VALUE; if (!json_parse_whitespace(parser)) return -1; } if (*parser->data != '"') return -1; parser->data++; json_parser_update_input_pos(parser); parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; parser->strinput = i_stream_create_jsonstr(parser->input); i_stream_add_destroy_callback(parser->strinput, json_strinput_destroyed, parser); *input_r = parser->strinput; return 1; }
static int json_parse_denest(struct json_parser *parser) { const enum json_state *nested_states; unsigned count; parser->data++; json_parser_update_input_pos(parser); nested_states = array_get(&parser->nesting, &count); if (count == 0) { /* closing root */ parser->state = JSON_STATE_DONE; return 0; } /* closing a nested object */ if (count == 1) { /* we're back to root */ parser->state = JSON_STATE_OBJECT_NEXT; } else { /* back to previous nested object */ parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; } array_delete(&parser->nesting, count-1, 1); if (parser->nested_skip_count > 0) { parser->nested_skip_count--; return 0; } return 1; }
static void json_parser_object_open(struct json_parser *parser) { parser->data++; parser->state = JSON_STATE_OBJECT_OPEN; array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); }
static int json_parse_denest(struct json_parser *parser) { const enum json_state *nested_states; unsigned count; parser->data++; json_parser_update_input_pos(parser); nested_states = array_get(&parser->nesting, &count); i_assert(count > 0); if (count == 1) { /* closing root */ parser->state = JSON_STATE_DONE; if ((parser->flags & JSON_PARSER_NO_ROOT_OBJECT) == 0) return 0; /* we want to return the ending "]" or "}" to caller */ return 1; } /* closing a nested object */ parser->state = nested_states[count-2] == JSON_STATE_OBJECT_OPEN ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; array_delete(&parser->nesting, count-1, 1); if (parser->nested_skip_count > 0) { parser->nested_skip_count--; return 0; } return 1; }
static bool json_parse_whitespace(struct json_parser *parser) { for (; parser->data != parser->end; parser->data++) { switch (*parser->data) { case ' ': case '\t': case '\r': case '\n': break; default: json_parser_update_input_pos(parser); return TRUE; } } json_parser_update_input_pos(parser); return FALSE; }
static int json_try_parse_next(struct json_parser *parser, enum json_type *type_r, const char **value_r) { bool skipping = parser->skipping; int ret; if (!json_parse_whitespace(parser)) return -1; switch (parser->state) { case JSON_STATE_ROOT: if (*parser->data != '{') { parser->error = "Object doesn't begin with '{'"; return -1; } parser->data++; parser->state = JSON_STATE_OBJECT_OPEN; json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_VALUE: case JSON_STATE_ARRAY_VALUE: if (*parser->data == '{') { parser->data++; parser->state = JSON_STATE_OBJECT_OPEN; array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); if (parser->skipping) { parser->nested_skip_count++; return 0; } *type_r = JSON_TYPE_OBJECT; return 1; } else if (*parser->data == '[') { parser->data++; parser->state = JSON_STATE_ARRAY_OPEN; array_append(&parser->nesting, &parser->state, 1); json_parser_update_input_pos(parser); if (parser->skipping) { parser->nested_skip_count++; return 0; } *type_r = JSON_TYPE_ARRAY; return 1; } if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) { *type_r = JSON_TYPE_STRING; } else if ((ret = json_parse_number(parser, value_r)) >= 0) { *type_r = JSON_TYPE_NUMBER; } else if ((ret = json_parse_atom(parser, "true")) >= 0) { *type_r = JSON_TYPE_TRUE; *value_r = "true"; } else if ((ret = json_parse_atom(parser, "false")) >= 0) { *type_r = JSON_TYPE_FALSE; *value_r = "false"; } else if ((ret = json_parse_atom(parser, "null")) >= 0) { *type_r = JSON_TYPE_NULL; *value_r = NULL; } else { parser->error = "Invalid data as value"; return -1; } if (ret == 0) { i_assert(parser->data == parser->end); if (parser->skipping && *type_r == JSON_TYPE_STRING) { /* a large string that we want to skip over. */ json_parser_update_input_pos(parser); parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? JSON_STATE_OBJECT_SKIP_STRING : JSON_STATE_ARRAY_SKIP_STRING; return 0; } return -1; } parser->state = parser->state == JSON_STATE_OBJECT_VALUE ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; break; case JSON_STATE_OBJECT_OPEN: if (*parser->data == '}') return json_parse_close_object(parser, type_r); parser->state = JSON_STATE_OBJECT_KEY; /* fall through */ case JSON_STATE_OBJECT_KEY: if (json_parse_string(parser, FALSE, value_r) <= 0) { parser->error = "Expected string as object key"; return -1; } *type_r = JSON_TYPE_OBJECT_KEY; parser->state = JSON_STATE_OBJECT_COLON; break; case JSON_STATE_OBJECT_COLON: if (*parser->data != ':') { parser->error = "Expected ':' after key"; return -1; } parser->data++; parser->state = JSON_STATE_OBJECT_VALUE; json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_NEXT: if (parser->skipping && parser->nested_skip_count == 0) { /* we skipped over the previous value */ parser->skipping = FALSE; } if (*parser->data == '}') return json_parse_close_object(parser, type_r); if (*parser->data != ',') { parser->error = "Expected ',' or '}' after object value"; return -1; } parser->state = JSON_STATE_OBJECT_KEY; parser->data++; json_parser_update_input_pos(parser); return 0; case JSON_STATE_ARRAY_OPEN: if (*parser->data == ']') return json_parse_close_array(parser, type_r); parser->state = JSON_STATE_ARRAY_VALUE; return 0; case JSON_STATE_ARRAY_NEXT: if (parser->skipping && parser->nested_skip_count == 0) { /* we skipped over the previous value */ parser->skipping = FALSE; } if (*parser->data == ']') return json_parse_close_array(parser, type_r); if (*parser->data != ',') { parser->error = "Expected ',' or '}' after array value"; return -1; } parser->state = JSON_STATE_ARRAY_VALUE; parser->data++; json_parser_update_input_pos(parser); return 0; case JSON_STATE_OBJECT_SKIP_STRING: case JSON_STATE_ARRAY_SKIP_STRING: if (json_skip_string(parser) <= 0) return -1; parser->state = parser->state == JSON_STATE_OBJECT_SKIP_STRING ? JSON_STATE_OBJECT_NEXT : JSON_STATE_ARRAY_NEXT; return 0; case JSON_STATE_DONE: parser->error = "Unexpected data at the end"; return -1; } json_parser_update_input_pos(parser); return skipping ? 0 : 1; }