Class: JSON::ResumableParser
Instance Method Summary collapse
-
#<<(string) ⇒ self
Appends the given string to the parser’s buffer.
-
#clear ⇒ self
Entirely reset the parser state and buffer.
-
#value? ⇒ Boolean
Returns whether the internal buffer has been entirely consumed.
-
#parse ⇒ Boolean
Attemps to parse a JSON document from the internal buffer.
-
#parsed_bytes ⇒ Integer
Returns the number of bytes parsed since the start of the current partial value.
-
#partial_value ⇒ Object
Returns the Ruby objects parsed up to this point: parser << ‘[1, [2, 3,’ parser.parse # => false parser.value # ArgumentError no ready value parser.partial_value # => [1, [2, 3]].
-
#rest ⇒ String
Returns a string containing what remains to be parsed in the buffer parser << ‘{ “message”: “unterminated message’ parser.parse # => false parser.rest # => ‘”unterminated message“’.
-
#value ⇒ Object
Returns and consume the last parsed value.
-
#value? ⇒ Boolean
Returns whether a parsed value is available.
Instance Method Details
#<<(string) ⇒ self
Appends the given string to the parser’s buffer.
2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 |
# File 'ext/json/ext/parser/parser.c', line 2363
static VALUE cResumableParser_feed(VALUE self, VALUE str)
{
rb_check_frozen(self);
JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
str = convert_encoding(str);
if (!RSTRING_LEN(str)) {
return self;
}
size_t offset = parser->state.cursor - parser->state.start;
const size_t remaining = parser->state.end - parser->state.cursor;
if (!remaining) {
if (parser->buffer) {
json_str_clear(parser->buffer);
}
parser->buffer = RB_OBJ_FROZEN_RAW(str) ? str : rb_obj_hide(rb_str_new_shared(str));
offset = 0;
} else {
JSON_ASSERT(parser->buffer);
const size_t size = parser->state.end - parser->state.start;
const size_t consumed = size - remaining;
if (RB_OBJ_FROZEN_RAW(parser->buffer)) {
VALUE new_buffer = rb_obj_hide(rb_str_buf_new(remaining + RSTRING_LEN(str)));
rb_enc_associate_index(new_buffer, utf8_encindex);
char *old_ptr = RSTRING_PTR(parser->buffer);
memcpy(RSTRING_PTR(new_buffer), old_ptr + consumed, remaining);
rb_str_set_len(new_buffer, remaining);
offset = 0;
parser->buffer = new_buffer;
} else if (consumed > (size / 2) && size >= 512) {
rb_str_modify(parser->buffer);
char *old_ptr = RSTRING_PTR(parser->buffer);
memmove(old_ptr, old_ptr + consumed, remaining);
rb_str_set_len(parser->buffer, remaining);
offset = 0;
}
rb_str_append(parser->buffer, str);
}
long len;
const char *start;
RSTRING_GETMEM(parser->buffer, start, len);
parser->state.start = start;
parser->state.end = start + len;
parser->state.cursor = parser->state.start + offset;
return self;
}
|
#clear ⇒ self
Entirely reset the parser state and buffer.
2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 |
# File 'ext/json/ext/parser/parser.c', line 2583
static VALUE cResumableParser_clear(VALUE self)
{
JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
parser->buffer = 0;
parser->complete = true;
parser->parsed_bytes = 0;
parser->incomplete_bytes = 0;
parser->frames.head = 0;
parser->value_stack.head = 0;
parser->state.name_cache.length = 0;
parser->state.current_nesting = 0;
parser->state.in_array = 1;
parser->state.emitted_deprecations = 0;
parser->state.start = parser->state.cursor = parser->state.end = NULL;
return self;
}
|
#value? ⇒ Boolean
Returns whether the internal buffer has been entirely consumed.
2719 2720 2721 2722 2723 |
# File 'ext/json/ext/parser/parser.c', line 2719
static VALUE cResumableParser_eos_p(VALUE self)
{
JSON_ResumableParser *parser = cResumableParser_get(self);
return eos(&parser->state) ? Qtrue : Qfalse;
}
|
#parse ⇒ Boolean
Attemps to parse a JSON document from the internal buffer. Returns whether a complete document could be parsed.
It does raise JSON::ParserError when encountering invalid JSON syntax.
The parsed object can be retrieved by calling #value
2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 |
# File 'ext/json/ext/parser/parser.c', line 2468
static VALUE cResumableParser_parse(VALUE self)
{
JSON_ResumableParser *parser = ResumableParser_acquire(self, true);
if (parser->complete) {
parser->parsed_bytes = 0;
parser->incomplete_bytes = 0;
parser->complete = false;
}
if (!parser->buffer) {
parser->in_use = false;
return Qfalse;
}
if (parser->frames.head == 0) {
json_frame_stack_push(&parser->state, (json_frame){
.type = JSON_FRAME_ROOT,
.phase = JSON_PHASE_VALUE,
});
}
VALUE Vsource = parser->buffer; // Prevent compaction
json_frame *frame = json_frame_stack_peek(&parser->frames);
if (frame->phase == JSON_PHASE_DONE) {
JSON_ASSERT(parser->value_stack.head == 1);
JSON_ASSERT(parser->frames.head == 1);
frame->phase = JSON_PHASE_VALUE;
rvalue_stack_pop(parser->state.value_stack, 1);
}
struct json_parse_any_args args = {
.state = &parser->state,
.config = &parser->config,
.parser = self,
};
int status;
const char *initial_cursor = parser->state.cursor;
parser->complete = rb_protect(json_parse_any_resumable_safe, (VALUE)&args, &status);
if (status) {
parser->complete = true; // a parse error is considered complete
}
parser->parsed_bytes += parser->state.cursor - initial_cursor;
parser->incomplete_bytes = parser->complete ? 0 : parser->state.end - parser->state.cursor;
json_eat_whitespace(&parser->state, &parser->config, false);
if (eos(&parser->state)) {
json_str_clear(parser->buffer);
parser->buffer = Qfalse;
}
parser->in_use = false;
if (status) {
rb_jump_tag(status); // reraise
}
RB_GC_GUARD(Vsource);
return parser->complete ? Qtrue : Qfalse;
}
|
#parsed_bytes ⇒ Integer
Returns the number of bytes parsed since the start of the current partial value. This is intended to be used for securing against untrusted input:
loop do
if parser.parsed_bytes > DOCUMENT_MAX_SIZE
raise "document too large"
end
parser << read_chunk
while parser.parse
process(parser.value)
end
end
2742 2743 2744 2745 2746 |
# File 'ext/json/ext/parser/parser.c', line 2742
static VALUE cResumableParser_parsed_bytes(VALUE self)
{
JSON_ResumableParser *parser = cResumableParser_get(self);
return ULL2NUM(parser->parsed_bytes + parser->incomplete_bytes);
}
|
#partial_value ⇒ Object
Returns the Ruby objects parsed up to this point:
parser << '[1, [2, 3,'
parser.parse # => false
parser.value # ArgumentError no ready value
parser.partial_value # => [1, [2, 3]]
2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 |
# File 'ext/json/ext/parser/parser.c', line 2678
static VALUE cResumableParser_partial_value(VALUE self)
{
JSON_ResumableParser *parser = ResumableParser_acquire(self, true);
int status;
VALUE result = rb_protect(cResumableParser_partial_value_body, self, &status);
parser->in_use = false;
if (status) {
rb_jump_tag(status);
}
return result;
}
|
#rest ⇒ String
Returns a string containing what remains to be parsed in the buffer
parser << '{ "message": "unterminated message'
parser.parse # => false
parser.rest # => '"unterminated message"'
2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 |
# File 'ext/json/ext/parser/parser.c', line 2699
static VALUE cResumableParser_rest(VALUE self)
{
JSON_ResumableParser *parser = cResumableParser_get(self);
if (!parser->buffer) {
return rb_utf8_str_new("", 0);
}
size_t offset = parser->state.cursor - parser->state.start;
const char *ptr;
long len;
RSTRING_GETMEM(parser->buffer, ptr, len);
return rb_utf8_str_new(ptr + offset, len - offset);
}
|
#value ⇒ Object
Returns and consume the last parsed value. Raises ArgumentError if there is no parsed value or if it was already retrieved:
parser << '[1][2]'
parser.value # ArgumentError no ready value
parser.parse # => true
parser.value # => [1]
parser.value # ArgumentError no ready value
2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 |
# File 'ext/json/ext/parser/parser.c', line 2561
static VALUE cResumableParser_value(VALUE self)
{
JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
if (parser->frames.head > 0) {
json_frame *frame = json_frame_stack_peek(&parser->frames);
if (frame->phase == JSON_PHASE_DONE) {
VALUE result = *rvalue_stack_peek(parser->state.value_stack, 1);
rvalue_stack_pop(parser->state.value_stack, 1);
json_frame_stack_pop(parser->state.frames);
return result;
}
}
rb_raise(rb_eArgError, "no ready value");
}
|
#value? ⇒ Boolean
Returns whether a parsed value is available.
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 |
# File 'ext/json/ext/parser/parser.c', line 2537
static VALUE cResumableParser_value_p(VALUE self)
{
JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
if (parser->value_stack.head > 0) {
json_frame *frame = json_frame_stack_peek(&parser->frames);
if (frame->phase == JSON_PHASE_DONE) {
return Qtrue;
}
}
return Qfalse;
}
|