mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Document stream: truncate final unfinished document and give access to the number of truncated bytes. (#1534)
* Truncate final unclosed string. * Adding more precise remarks. * Better documentation and more robust code. * ARM + PPC corrections. * Patching ARM implementation with new stage1_mode parameter. * Fixed most problems. * Correcting white spaces and adding a remark. * This adds the truncated_bytes() method to the stream instances.
This commit is contained in:
@@ -13,7 +13,7 @@ namespace stage1 {
|
||||
class structural_scanner {
|
||||
public:
|
||||
|
||||
simdjson_really_inline structural_scanner(dom_parser_implementation &_parser, bool _partial)
|
||||
simdjson_really_inline structural_scanner(dom_parser_implementation &_parser, stage1_mode _partial)
|
||||
: buf{_parser.buf},
|
||||
next_structural_index{_parser.structural_indexes.get()},
|
||||
parser{_parser},
|
||||
@@ -43,7 +43,7 @@ simdjson_really_inline void validate_utf8_character() {
|
||||
if ((buf[idx] & 0b00100000) == 0) {
|
||||
// missing continuation
|
||||
if (simdjson_unlikely(idx+1 > len || !is_continuation(buf[idx+1]))) {
|
||||
if (idx+1 > len && partial) { idx = len; return; }
|
||||
if (idx+1 > len && is_streaming(partial)) { idx = len; return; }
|
||||
error = UTF8_ERROR;
|
||||
idx++;
|
||||
return;
|
||||
@@ -58,7 +58,7 @@ simdjson_really_inline void validate_utf8_character() {
|
||||
if ((buf[idx] & 0b00010000) == 0) {
|
||||
// missing continuation
|
||||
if (simdjson_unlikely(idx+2 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]))) {
|
||||
if (idx+2 > len && partial) { idx = len; return; }
|
||||
if (idx+2 > len && is_streaming(partial)) { idx = len; return; }
|
||||
error = UTF8_ERROR;
|
||||
idx++;
|
||||
return;
|
||||
@@ -74,7 +74,7 @@ simdjson_really_inline void validate_utf8_character() {
|
||||
// 4-byte
|
||||
// missing continuation
|
||||
if (simdjson_unlikely(idx+3 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]) || !is_continuation(buf[idx+3]))) {
|
||||
if (idx+2 > len && partial) { idx = len; return; }
|
||||
if (idx+2 > len && is_streaming(partial)) { idx = len; return; }
|
||||
error = UTF8_ERROR;
|
||||
idx++;
|
||||
return;
|
||||
@@ -147,24 +147,47 @@ simdjson_really_inline error_code scan() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*next_structural_index = len;
|
||||
// We pad beyond.
|
||||
// https://github.com/simdjson/simdjson/issues/906
|
||||
// See json_structural_indexer.h for an explanation.
|
||||
*next_structural_index = len; // assumed later in partial == stage1_mode::streaming_final
|
||||
next_structural_index[1] = len;
|
||||
next_structural_index[2] = 0;
|
||||
parser.n_structural_indexes = uint32_t(next_structural_index - parser.structural_indexes.get());
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; }
|
||||
parser.next_structural_index = 0;
|
||||
if (partial) {
|
||||
if (partial == stage1_mode::streaming_partial) {
|
||||
if(unclosed_string) {
|
||||
parser.n_structural_indexes--;
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
|
||||
}
|
||||
// We truncate the input to the end of the last complete document (or zero).
|
||||
auto new_structural_indexes = find_next_document_index(parser);
|
||||
if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) {
|
||||
return CAPACITY; // If the buffer is partial but the document is incomplete, it's too big to parse.
|
||||
}
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
} else if(partial == stage1_mode::streaming_final) {
|
||||
if(unclosed_string) { parser.n_structural_indexes--; }
|
||||
// We truncate the input to the end of the last complete document (or zero).
|
||||
// Because partial == stage1_mode::streaming_final, it means that we may
|
||||
// silently ignore trailing garbage. Though it sounds bad, we do it
|
||||
// deliberately because many people who have streams of JSON documents
|
||||
// will truncate them for processing. E.g., imagine that you are uncompressing
|
||||
// the data from a size file or receiving it in chunks from the network. You
|
||||
// may not know where exactly the last document will be. Meanwhile the
|
||||
// document_stream instances allow people to know the JSON documents they are
|
||||
// parsing (see the iterator.source() method).
|
||||
parser.n_structural_indexes = find_next_document_index(parser);
|
||||
// We store the initial n_structural_indexes so that the client can see
|
||||
// whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes,
|
||||
// then this will query parser.structural_indexes[parser.n_structural_indexes] which is len,
|
||||
// otherwise, it will copy some prior index.
|
||||
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||
// This next line is critical, do not change it unless you understand what you are
|
||||
// doing.
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (parser.n_structural_indexes == 0) { return EMPTY; }
|
||||
} else if(unclosed_string) { error = UNCLOSED_STRING; }
|
||||
return error;
|
||||
}
|
||||
@@ -176,13 +199,13 @@ private:
|
||||
uint32_t len;
|
||||
uint32_t idx{0};
|
||||
error_code error{SUCCESS};
|
||||
bool partial;
|
||||
stage1_mode partial;
|
||||
}; // structural_scanner
|
||||
|
||||
} // namespace stage1
|
||||
} // unnamed namespace
|
||||
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, bool partial) noexcept {
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode partial) noexcept {
|
||||
this->buf = _buf;
|
||||
this->len = _len;
|
||||
stage1::structural_scanner scanner(*this, partial);
|
||||
@@ -328,7 +351,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu
|
||||
}
|
||||
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, false);
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user