Skip to content

Commit 11ed694

Browse files
committed
ResumableParser#<<: call rb_str_modify before shrinking the buffer
Fix: #1013 The string may be shared.
1 parent cdad7df commit 11ed694

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

ext/json/ext/parser/parser.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,16 +2300,19 @@ static VALUE cResumableParser_feed(VALUE self, VALUE str)
23002300

23012301
const size_t size = parser->state.end - parser->state.start;
23022302
const size_t consumed = size - remaining;
2303-
char *old_ptr = RSTRING_PTR(parser->buffer);
23042303

23052304
if (RB_OBJ_FROZEN_RAW(parser->buffer)) {
23062305
VALUE new_buffer = rb_obj_hide(rb_str_buf_new(remaining + RSTRING_LEN(str)));
2307-
MEMCPY(RSTRING_PTR(new_buffer), old_ptr + consumed, char, remaining);
2306+
2307+
char *old_ptr = RSTRING_PTR(parser->buffer);
2308+
memcpy(RSTRING_PTR(new_buffer), old_ptr + consumed, remaining);
23082309
rb_str_set_len(new_buffer, remaining);
23092310
offset = 0;
23102311
parser->buffer = new_buffer;
23112312
} else if (consumed > (size / 2) && size >= 512) {
2312-
MEMMOVE(old_ptr, old_ptr + consumed, char, remaining);
2313+
rb_str_modify(parser->buffer);
2314+
char *old_ptr = RSTRING_PTR(parser->buffer);
2315+
memmove(old_ptr, old_ptr + consumed, remaining);
23132316
rb_str_set_len(parser->buffer, remaining);
23142317
offset = 0;
23152318
}

test/json/resumable_parser_test.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,21 @@ def test_spill_frames_stack
244244
assert_equal expected, @parser.value
245245
end
246246

247+
def test_buffer_shrink
248+
doc1 = '{"a":"' + ("x" * 800) + '"} {' # >= 512 bytes
249+
doc2 = '"b":1} '
250+
251+
parser = JSON::ResumableParser.new({})
252+
253+
parser << doc1 # internal buffer becomes a *shared* string here
254+
parser.parse # consume doc1 -> >50% of a >=512B buffer is now consumed
255+
parser.value
256+
257+
parser << doc2 # buffer is shrinked
258+
parser.parse
259+
parser.value
260+
end
261+
247262
private
248263

249264
def assert_partial_value(expected, json)

0 commit comments

Comments
 (0)