Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions ext/json/ext/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -2625,11 +2625,16 @@ static VALUE cResumableParser_partial_value_body(VALUE self)
missing_object_value = 1;
}

// Copy the value stack as we need to mutate it.
// Copy the value stack as we need to mutate it. The collapse loop folds each
// open container by popping its entries and pushing the single result, so a
// parent always reclaims its child's slot; head exceeds its live size by at
// most one, either for the missing-value placeholder pushed below or for the
// result of folding an empty innermost container. That one spare slot keeps
// rvalue_stack_push from growing (reallocating) this ALLOCV buffer.
long capa = parser.value_stack.head;
parser.value_stack.capa = (capa + missing_object_value);
VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, capa + missing_object_value);
MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, parser.value_stack.capa);
parser.value_stack.capa = capa + 1;
VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, parser.value_stack.capa);
MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, capa);
parser.value_stack.ptr = value_stack_buffer;

JSON_ParserState *state = &parser.state;
Expand Down
11 changes: 11 additions & 0 deletions test/json/resumable_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,17 @@ def test_partial_value
assert_partial_value([1, { "a" => 1, "b" => { "c" => nil } }], '[1, { "a": 1, "b": { "c"')
end

def test_partial_value_collapses_nested_incomplete_containers
# partial_value rebuilds the open containers on a scratch value stack; folding
# an empty inner container pushes a value, so that stack must hold more than its
# live size or the push reallocates the scratch buffer.
assert_partial_value({ "abc" => {} }, '{"abc":{"d')
assert_partial_value({ "a" => { "b" => { "c" => {} } } }, '{"a":{"b":{"c":{"e')
assert_partial_value([1, { "a" => {} }], '[1,{"a":{"d')
assert_partial_value({ "a" => [1, { "b" => [2, { "c" => nil }] }] }, '{"a":[1,{"b":[2,{"c"')
assert_partial_value([1, [2, [3, { "x" => nil }]]], '[1,[2,[3,{"x":[')
end

def test_partial_value_issue_1005
data = <<~JSON
[
Expand Down
Loading