Skip to content

Commit c2867d4

Browse files
committed
Implement ResumableParser#partial_value
1 parent 6d09636 commit c2867d4

2 files changed

Lines changed: 109 additions & 11 deletions

File tree

ext/json/ext/parser/parser.c

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,9 +1805,10 @@ ALWAYS_INLINE(static) bool json_parse_any(JSON_ParserState *state, JSON_ParserCo
18051805
state->current_nesting--;
18061806
state->in_array--;
18071807

1808-
json_frame_stack_pop(state->frames);
18091808
json_push_value(state, config, json_decode_array(state, config, count));
1809+
json_frame_stack_pop(state->frames);
18101810
frame = json_frame_stack_peek(state->frames);
1811+
18111812
json_value_completed(frame);
18121813

18131814
switch (frame->phase) {
@@ -2315,28 +2316,37 @@ static VALUE json_parse_any_resumable_safe(VALUE _args)
23152316
return (VALUE)json_parse_any(args->state, args->config, true);
23162317
}
23172318

2319+
static JSON_ResumableParser *ResumableParser_acquire(VALUE self)
2320+
{
2321+
JSON_ResumableParser *parser = cResumableParser_get(self);
2322+
// self may have moved, so we need to update all pointers
2323+
// Investigate: We might be better off keeping JSON_ParserState on the stack
2324+
// and only persist what we need.
2325+
parser->state.value_stack_handle = &self;
2326+
parser->state.frame_stack_handle = &self;
2327+
parser->state.value_stack = &parser->value_stack;
2328+
parser->state.frames = &parser->frames;
2329+
2330+
return parser;
2331+
}
2332+
23182333
// TODO: doc
23192334
static VALUE cResumableParser_parse(VALUE self)
23202335
{
2321-
JSON_ResumableParser *parser = cResumableParser_get(self);
2336+
JSON_ResumableParser *parser = ResumableParser_acquire(self);
23222337
if (!parser->buffer) {
23232338
return Qfalse;
23242339
}
23252340
VALUE Vsource = parser->buffer; // Prevent compaction
23262341

23272342
// TODO: prevent reentrancy
23282343

2329-
// self may have moved, so we need to update all pointers
2330-
// We might be better off keeping JSON_ParserState on the stack
2331-
// and only persist what we need.
2332-
parser->state.value_stack_handle = &self;
2333-
parser->state.frame_stack_handle = &self;
2334-
parser->state.value_stack = &parser->value_stack;
2335-
parser->state.frames = &parser->frames;
2336-
23372344
json_frame *frame = json_frame_stack_peek(&parser->frames);
23382345

23392346
if (frame->phase == JSON_PHASE_DONE) {
2347+
JSON_ASSERT(parser->value_stack.head == 1);
2348+
JSON_ASSERT(parser->frames.head == 1);
2349+
23402350
frame->phase = JSON_PHASE_VALUE;
23412351
rvalue_stack_pop(parser->state.value_stack, 1);
23422352
}
@@ -2362,7 +2372,7 @@ static VALUE cResumableParser_parse(VALUE self)
23622372
// TODO: doc
23632373
static VALUE cResumableParser_value(VALUE self)
23642374
{
2365-
JSON_ResumableParser *parser = cResumableParser_get(self);
2375+
JSON_ResumableParser *parser = ResumableParser_acquire(self);
23662376
json_frame *frame = json_frame_stack_peek(&parser->frames);
23672377

23682378
if (frame->phase == JSON_PHASE_DONE) {
@@ -2372,6 +2382,73 @@ static VALUE cResumableParser_value(VALUE self)
23722382
}
23732383
}
23742384

2385+
// TODO: doc
2386+
static VALUE cResumableParser_partial_value(VALUE self)
2387+
{
2388+
JSON_ResumableParser *original_parser = ResumableParser_acquire(self);
2389+
JSON_ResumableParser parser = *original_parser;
2390+
2391+
if (parser.value_stack.head == 0) {
2392+
return Qnil;
2393+
}
2394+
2395+
json_frame *frame = json_frame_stack_peek(parser.state.frames);
2396+
long missing_object_value = 0;
2397+
if (frame->type == JSON_FRAME_OBJECT && (frame->phase == JSON_PHASE_VALUE || frame->phase == JSON_PHASE_OBJECT_COLON)) {
2398+
missing_object_value = 1;
2399+
}
2400+
2401+
// Copy the value stack as we need to mutate it.
2402+
long capa = parser.value_stack.head;
2403+
parser.value_stack.capa = (capa + missing_object_value);
2404+
VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, capa + missing_object_value);
2405+
MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, parser.value_stack.capa);
2406+
parser.value_stack.ptr = value_stack_buffer;
2407+
2408+
JSON_ParserState *state = &parser.state;
2409+
JSON_ParserConfig *config = &parser.config;
2410+
2411+
if (missing_object_value) {
2412+
rvalue_stack_push(state->value_stack, Qnil, NULL, &state->value_stack);
2413+
}
2414+
2415+
VALUE partial_result = Qundef;
2416+
2417+
while (UNDEF_P(partial_result)) {
2418+
frame = json_frame_stack_peek(state->frames);
2419+
2420+
switch (frame->type) {
2421+
case JSON_FRAME_ROOT: {
2422+
partial_result = *rvalue_stack_peek(state->value_stack, 1);
2423+
break;
2424+
}
2425+
2426+
case JSON_FRAME_ARRAY: {
2427+
long count = json_frame_entry_count(frame, state->value_stack);
2428+
json_push_value(state, config, json_decode_array(state, config, count));
2429+
json_frame_stack_pop(state->frames);
2430+
2431+
break;
2432+
}
2433+
2434+
case JSON_FRAME_OBJECT: {
2435+
long count = json_frame_entry_count(frame, state->value_stack);
2436+
json_push_value(state, config, json_decode_object(state, config, count));
2437+
json_frame_stack_pop(state->frames);
2438+
break;
2439+
}
2440+
2441+
default: {
2442+
JSON_UNREACHABLE_RETURN(Qundef);
2443+
break;
2444+
}
2445+
}
2446+
}
2447+
2448+
ALLOCV_END(tmpbuf);
2449+
return partial_result;
2450+
}
2451+
23752452
// TODO: doc
23762453
static VALUE cResumableParser_rest(VALUE self)
23772454
{
@@ -2419,6 +2496,7 @@ void Init_parser(void)
24192496
rb_define_method(cResumableParser, "<<", cResumableParser_feed, 1);
24202497
rb_define_method(cResumableParser, "parse", cResumableParser_parse, 0);
24212498
rb_define_method(cResumableParser, "value", cResumableParser_value, 0);
2499+
rb_define_method(cResumableParser, "partial_value", cResumableParser_partial_value, 0);
24222500
rb_define_method(cResumableParser, "rest", cResumableParser_rest, 0);
24232501

24242502
rb_global_variable(&CNaN);

test/json/resumable_parser_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,28 @@ def test_rest
7272
assert_equal '"unterminated string', @parser.rest
7373
end
7474

75+
def test_partial_value
76+
assert_partial_value [1, 2, 3], '[1, 2, 3, "unterminated string'
77+
assert_partial_value({ "a" => 1, "b" => { "c" => nil } }, '{ "a": 1, "b": { "c": "unterminated string')
78+
assert_partial_value({ "a" => 1, "b" => { "c" => nil } }, '{ "a": 1, "b": { "c"')
79+
assert_partial_value([1, { "a" => 1, "b" => { "c" => nil } }], '[1, { "a": 1, "b": { "c"')
80+
end
81+
82+
def test_partial_value_missing
83+
assert_nil @parser.partial_value
84+
end
85+
7586
private
7687

88+
def assert_partial_value(expected, json)
89+
parser = new_parser
90+
parser << json
91+
refute parser.parse
92+
2.times do
93+
assert_equal expected, parser.partial_value
94+
end
95+
end
96+
7797
def assert_resumed_parsing(json, parser = @parser)
7898
expected = JSON.parse(json)
7999

0 commit comments

Comments
 (0)