1#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
3#ifndef SIMDJSON_CONDITIONAL_INCLUDE
4#define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
5#include "simdjson/generic/ondemand/base.h"
6#include "simdjson/generic/ondemand/document_stream.h"
7#include "simdjson/generic/ondemand/document-inl.h"
8#include "simdjson/generic/implementation_simdjson_result_base-inl.h"
16namespace SIMDJSON_IMPLEMENTATION {
19#ifdef SIMDJSON_THREADS_ENABLED
21inline void stage1_worker::finish() {
26 std::unique_lock<std::mutex> lock(locking_mutex);
27 cond_var.wait(lock, [
this]{
return has_work ==
false;});
30inline stage1_worker::~stage1_worker() {
37inline void stage1_worker::start_thread() {
38 std::unique_lock<std::mutex> lock(locking_mutex);
39 if(thread.joinable()) {
42 thread = std::thread([
this]{
44 std::unique_lock<std::mutex> thread_lock(locking_mutex);
46 cond_var.wait(thread_lock, [
this]{
return has_work || !can_work;});
53 this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
54 this->_next_batch_start);
55 this->has_work =
false;
59 cond_var.notify_one();
67inline void stage1_worker::stop_thread() {
68 std::unique_lock<std::mutex> lock(locking_mutex);
72 cond_var.notify_all();
74 if(thread.joinable()) {
79inline void stage1_worker::run(document_stream * ds, parser * stage1,
size_t next_batch_start) {
80 std::unique_lock<std::mutex> lock(locking_mutex);
82 _next_batch_start = next_batch_start;
83 stage1_thread_parser = stage1;
88 cond_var.notify_one();
95 ondemand::parser &_parser,
99 bool _allow_comma_separated,
105 batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
106 allow_comma_separated{_allow_comma_separated},
109 #ifdef SIMDJSON_THREADS_ENABLED
110 , use_thread(_parser.threaded)
120 allow_comma_separated{
false},
123 #ifdef SIMDJSON_THREADS_ENABLED
129simdjson_inline document_stream::~document_stream() noexcept
131 #ifdef SIMDJSON_THREADS_ENABLED
143 if (len == 0) {
return 0; }
144 if(error ==
CAPACITY) {
return len - batch_start; }
145 return parser->implementation->structural_indexes[
parser->implementation->n_structural_indexes] -
parser->implementation->structural_indexes[
parser->implementation->n_structural_indexes + 1];
149 : stream{
nullptr}, finished{
true} {
153 : stream{_stream}, finished{is_end} {
171 if (stream->error) { finished =
true; }
180 if (stream->error ==
EMPTY) { finished =
true; }
193 return finished != other.finished;
197 return finished == other.finished;
210inline void document_stream::start() noexcept {
211 if (error) {
return; }
212 error = parser->allocate(batch_size);
213 if (error) {
return; }
216 error = run_stage1(*parser, batch_start);
217 while(error ==
EMPTY) {
219 batch_start = next_batch_start();
220 if (batch_start >= len) {
return; }
221 error = run_stage1(*parser, batch_start);
223 if (error) {
return; }
227 doc_index = batch_start + parser->implementation->structural_indexes[0];
228 doc = document(json_iterator(&buf[batch_start], parser));
229 doc.
iter._streaming =
true;
231 #ifdef SIMDJSON_THREADS_ENABLED
232 if (use_thread && next_batch_start() < len) {
234 if (worker.get() ==
nullptr) {
235 worker.reset(
new(std::nothrow) stage1_worker());
236 if (worker.get() ==
nullptr) { error =
MEMALLOC;
return; }
238 error = stage1_thread_parser.allocate(batch_size);
239 if (error) {
return; }
240 worker->start_thread();
241 start_stage1_thread();
242 if (error) {
return; }
247inline void document_stream::next() noexcept {
249 if (error) {
return; }
251 if (error) {
return; }
252 auto cur_struct_index = doc.
iter._root - parser->implementation->structural_indexes.get();
253 doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
256 if(cur_struct_index >=
static_cast<int64_t
>(parser->implementation->n_structural_indexes)) {
259 while (error ==
EMPTY) {
260 batch_start = next_batch_start();
261 if (batch_start >= len) {
break; }
262 #ifdef SIMDJSON_THREADS_ENABLED
264 load_from_stage1_thread();
266 error = run_stage1(*parser, batch_start);
269 error = run_stage1(*parser, batch_start);
305 doc.
iter = json_iterator(&buf[batch_start], parser);
306 doc.
iter._streaming =
true;
311 if (error) {
continue; }
312 doc_index = batch_start + parser->implementation->structural_indexes[0];
317simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
325simdjson_inline
bool document_stream::skip_to_delimiter(uint8_t delimiter)
noexcept {
326 const uint8_t *
const base = &buf[batch_start];
327 const token_position pos = doc.
iter.position();
328 const token_position
end = doc.
iter.end_position();
329 if (pos >=
end) {
return false; }
330 const size_t here = size_t(doc.
iter.token.peek(pos) - base);
331 const size_t batch_len =
332 (len - batch_start < batch_size) ? len - batch_start : batch_size;
333 if (here >= batch_len) {
return false; }
334 const uint8_t *
const found =
static_cast<const uint8_t *
>(
335 std::memchr(base + here, delimiter, batch_len - here));
336 if (found ==
nullptr) {
return false; }
338 const uint32_t boundary = uint32_t(found - base);
342 token_position lo = pos;
344 while (lo + hop <
end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
345 token_position hi = (lo + hop <
end) ? lo + hop :
end;
347 const token_position mid = lo + ((hi - lo) >> 1);
348 if (*mid < boundary) { lo = mid + 1; }
else { hi = mid; }
350 doc.
iter.token.set_position(lo);
354inline void document_stream::next_document() noexcept {
367 const uint8_t delimiter = document_delimiter();
368 if (delimiter != 0 && !error && doc.
iter.depth() > 0 &&
369 skip_to_delimiter(delimiter)) {
371 doc.
iter._string_buf_loc = parser->string_buf.get();
372 doc.
iter._root = doc.
iter.position();
376 error = doc.
iter.skip_child(0);
377 if (error) {
return; }
381 if (allow_comma_separated) {
383 static_cast<void>(ignored);
386 doc.
iter._string_buf_loc = parser->string_buf.get();
387 doc.
iter._root = doc.
iter.position();
390inline size_t document_stream::next_batch_start() const noexcept {
391 return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
394inline error_code document_stream::run_stage1(ondemand::parser &p,
size_t _batch_start)
noexcept {
397 size_t remaining = len - _batch_start;
399 if (remaining <= batch_size) {
403 mode = stage1_mode::json_sequence_final;
406 mode = stage1_mode::comma_delimited_final;
409 mode = stage1_mode::streaming_final;
412 return p.implementation->stage1(&buf[_batch_start], remaining, mode);
417 mode = stage1_mode::json_sequence_partial;
420 mode = stage1_mode::comma_delimited_partial;
423 mode = stage1_mode::streaming_partial;
426 return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
430simdjson_inline
size_t document_stream::iterator::current_index() const noexcept {
431 return stream->doc_index;
434simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
438 return std::string_view(
reinterpret_cast<const char*
>(stream->buf) + current_index(), stream->len - current_index());
444 auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
448 switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
457 auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
459 size_t svlen = next_index - current_index();
460 const char *start =
reinterpret_cast<const char*
>(stream->buf) + current_index();
464 size_t token_len = 0;
467 while (token_len < svlen) {
468 char c = start[token_len++];
471 }
else if (c ==
'"') {
476 while (token_len < svlen) {
477 char c = start[token_len];
478 if (std::isspace(
static_cast<unsigned char>(c)) || c ==
',' || c ==
'{' || c ==
'[' || c ==
'\0' ||
static_cast<uint8_t
>(c) == 0x1E) {
484 if (token_len > 0 && token_len < svlen) {
495 while(svlen > 1 && (std::isspace(
static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] ==
'\0' ||
static_cast<uint8_t
>(start[svlen-1]) == 0x1E || (stream->format ==
stream_format::comma_delimited && start[svlen-1] ==
','))) {
498 return std::string_view(start, svlen);
504 while (cur_struct_index <=
static_cast<int64_t
>(stream->parser->implementation->n_structural_indexes)) {
505 switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
513 if (depth == 0) {
break; }
517 return std::string_view(
reinterpret_cast<const char*
>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
521 return stream->error;
524#ifdef SIMDJSON_THREADS_ENABLED
526inline void document_stream::load_from_stage1_thread() noexcept {
530 std::swap(stage1_thread_parser,*
parser);
531 error = stage1_thread_error;
532 if (error) {
return; }
535 if (next_batch_start() < len) {
536 start_stage1_thread();
540inline void document_stream::start_stage1_thread() noexcept {
546 size_t _next_batch_start = this->next_batch_start();
548 worker->run(
this, & this->stage1_thread_parser, _next_batch_start);
559simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_stream>::simdjson_result(
562 implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::document_stream>(error)
565simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_stream>::simdjson_result(
566 SIMDJSON_IMPLEMENTATION::ondemand::document_stream &&value
568 implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::document_stream>(
569 std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_stream>(value)
iterator & operator++() noexcept
Advance to the next document (prefix).
simdjson_inline bool operator!=(const iterator &other) const noexcept
Check if we're at the end yet.
simdjson_inline iterator() noexcept
Default constructor.
error_code error() const noexcept
Returns error of the stream (if any).
simdjson_inline reference operator*() noexcept
Get the current document (or error).
bool at_end() const noexcept
Returns whether the iterator is at the end.
simdjson_inline iterator end() noexcept
The end of the stream, for iterator comparison purposes.
size_t size_in_bytes() const noexcept
Returns the input size in bytes.
size_t truncated_bytes() const noexcept
After iterating through the stream, this method returns the number of bytes that were not parsed at t...
simdjson_inline iterator begin() noexcept
Start iterating the documents in the stream.
simdjson_inline document_stream() noexcept
Construct an uninitialized document_stream.
json_iterator iter
Current position in the document.
A JSON fragment iterator.
int32_t depth_t
Represents the depth of a JSON value (number of nested arrays/objects).
The top level simdjson namespace, containing everything the library provides.
stream_format
Stream format for parse_many/iterate_many.
@ comma_delimited
Comma-separated JSON documents (e.g., {...},{...},{...})
@ whitespace_delimited
Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
@ newline_delimited
NDJSON/JSON Lines where each document occupies exactly one line: documents are separated by line feed...
@ json_sequence
RFC 7464 JSON text sequences (RS-delimited)
error_code
All possible errors returned by simdjson.
@ CAPACITY
This parser can't support a document that big.
@ EMPTY
no structural element found
@ MEMALLOC
Error allocating memory, most likely out of memory.
@ UNINITIALIZED
unknown error, or uninitialized document
stage1_mode
This enum is used with the dom_parser_implementation::stage1 function.
The result of a simdjson operation that could fail.