#ifndef SIMDJSON_INLINE_DOCUMENT_STREAM_H #define SIMDJSON_INLINE_DOCUMENT_STREAM_H #include "simdjson/dom/document_stream.h" #include #include #include namespace simdjson { namespace dom { #ifdef SIMDJSON_THREADS_ENABLED inline void stage1_worker::finish() { std::unique_lock lock(locking_mutex); cond_var.wait(lock, [this]{return has_work == false;}); } inline stage1_worker::~stage1_worker() { stop_thread(); } inline void stage1_worker::start_thread() { std::unique_lock lock(locking_mutex); if(thread.joinable()) { return; // This should never happen but we never want to create more than one thread. } thread = std::thread([this]{ while(can_work) { std::unique_lock thread_lock(locking_mutex); cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); if(!can_work) { break; } this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, this->_next_batch_start); this->has_work = false; thread_lock.unlock(); cond_var.notify_one(); // will notify "finish" } } ); } inline void stage1_worker::stop_thread() { std::unique_lock lock(locking_mutex); // We have to make sure that all locks can be released. can_work = false; has_work = false; lock.unlock(); cond_var.notify_all(); if(thread.joinable()) { thread.join(); } } inline void stage1_worker::run(document_stream * ds, dom::parser * stage1, size_t next_batch_start) { std::unique_lock lock(locking_mutex); owner = ds; _next_batch_start = next_batch_start; stage1_thread_parser = stage1; has_work = true; lock.unlock(); cond_var.notify_one();// will notify the thread lock } #endif really_inline document_stream::document_stream( dom::parser &_parser, const uint8_t *_buf, size_t _len, size_t _batch_size ) noexcept : parser{&_parser}, buf{_buf}, len{_len}, batch_size{_batch_size}, error{SUCCESS} { #ifdef SIMDJSON_THREADS_ENABLED if(worker.get() == nullptr) { error = MEMALLOC; } #endif } really_inline document_stream::document_stream() noexcept : parser{nullptr}, buf{nullptr}, len{0}, batch_size{0}, error{UNINITIALIZED} { } really_inline document_stream::~document_stream() noexcept { } really_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. return iterator(*this, error == EMPTY); } really_inline document_stream::iterator document_stream::end() noexcept { return iterator(*this, true); } really_inline document_stream::iterator::iterator(document_stream& _stream, bool is_end) noexcept : stream{_stream}, finished{is_end} { } really_inline simdjson_result document_stream::iterator::operator*() noexcept { // Once we have yielded any errors, we're finished. if (stream.error) { finished = true; return stream.error; } return stream.parser->doc.root(); } really_inline document_stream::iterator& document_stream::iterator::operator++() noexcept { stream.next(); // If that was the last document, we're finished. if (stream.error == EMPTY) { finished = true; } return *this; } really_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } inline void document_stream::start() noexcept { if (error) { return; } error = parser->ensure_capacity(batch_size); if (error) { return; } // Always run the first stage 1 parse immediately batch_start = 0; error = run_stage1(*parser, batch_start); if (error) { return; } #ifdef SIMDJSON_THREADS_ENABLED if (next_batch_start() < len) { // Kick off the first thread if needed error = stage1_thread_parser.ensure_capacity(batch_size); if (error) { return; } worker->start_thread(); start_stage1_thread(); if (error) { return; } } #endif // SIMDJSON_THREADS_ENABLED next(); } really_inline size_t document_stream::iterator::current_index() noexcept { return stream.doc_index; } inline void document_stream::next() noexcept { if (error) { return; } // Load the next document from the batch doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; error = parser->implementation->stage2_next(parser->doc); // If that was the last document in the batch, load another batch (if available) while (error == EMPTY) { batch_start = next_batch_start(); if (batch_start >= len) { break; } #ifdef SIMDJSON_THREADS_ENABLED load_from_stage1_thread(); #else error = run_stage1(*parser, batch_start); #endif if (error) { continue; } // If the error was EMPTY, we may want to load another batch. // Run stage 2 on the first document in the batch doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; error = parser->implementation->stage2_next(parser->doc); } } inline size_t document_stream::next_batch_start() const noexcept { return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; } inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept { // If this is the final batch, pass partial = false size_t remaining = len - _batch_start; if (remaining <= batch_size) { return p.implementation->stage1(&buf[_batch_start], remaining, false); } else { return p.implementation->stage1(&buf[_batch_start], batch_size, true); } } #ifdef SIMDJSON_THREADS_ENABLED inline void document_stream::load_from_stage1_thread() noexcept { worker->finish(); // Swap to the parser that was loaded up in the thread. Make sure the parser has // enough memory to swap to, as well. std::swap(*parser, stage1_thread_parser); error = stage1_thread_error; if (error) { return; } // If there's anything left, start the stage 1 thread! if (next_batch_start() < len) { start_stage1_thread(); } } inline void document_stream::start_stage1_thread() noexcept { // we call the thread on a lambda that will update // this->stage1_thread_error // there is only one thread that may write to this value // TODO this is NOT exception-safe. this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error size_t _next_batch_start = this->next_batch_start(); worker->run(this, & this->stage1_thread_parser, _next_batch_start); } #endif // SIMDJSON_THREADS_ENABLED } // namespace dom really_inline simdjson_result::simdjson_result() noexcept : simdjson_result_base() { } really_inline simdjson_result::simdjson_result(error_code error) noexcept : simdjson_result_base(error) { } really_inline simdjson_result::simdjson_result(dom::document_stream &&value) noexcept : simdjson_result_base(std::forward(value)) { } #if SIMDJSON_EXCEPTIONS really_inline dom::document_stream::iterator simdjson_result::begin() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first.begin(); } really_inline dom::document_stream::iterator simdjson_result::end() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first.end(); } #else // SIMDJSON_EXCEPTIONS really_inline dom::document_stream::iterator simdjson_result::begin() noexcept { first.error = error(); return first.begin(); } really_inline dom::document_stream::iterator simdjson_result::end() noexcept { first.error = error(); return first.end(); } #endif // SIMDJSON_EXCEPTIONS } // namespace simdjson #endif // SIMDJSON_INLINE_DOCUMENT_STREAM_H