Files
simdjson-simdjson/include/simdjson/inline/document.h
T
John Keiser 910f272467 Add parser implementation interface and selection API (#501)
* Make architecture implementations virtual functions

- Easier to add new architectures (add implementation to implementation.cpp)
- Easier to add new algorithms / functions to architecture selection
(add to implementation.h, implement)
- Automatically select best implementation in static initialization
- Allow user to explicitly select implementation with a string (i.e.
parameter)
- Allow user to inspect current implementation name/description
- Allow user to list available implementations
- Eliminate architecture enum and architecture-based templating
- Add noexcept in non-inline functions

* Move implementation static methods to their own classes

* Detect best supported implementation on first use

* available_implementationsI() -> available_implementations
2020-02-21 16:34:27 -05:00

193 lines
6.5 KiB
C++

#ifndef SIMDJSON_INLINE_DOCUMENT_H
#define SIMDJSON_INLINE_DOCUMENT_H
#ifndef SIMDJSON_DOCUMENT_H
#error This is an internal file only. Include document.h instead.
#endif
// Inline implementations go in here if they aren't small enough to go in the class itself or if
// there are complex header file dependencies that need to be broken by externalizing the
// implementation.
#include "simdjson/implementation.h"
namespace simdjson {
// TODO inline?
document::doc_ref_result document::parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept {
error_code code = init_parse(len);
if (code) { return document::doc_ref_result(doc, code); }
if (realloc_if_needed) {
const uint8_t *tmp_buf = buf;
buf = (uint8_t *)allocate_padded_buffer(len);
if (buf == nullptr)
return document::doc_ref_result(doc, MEMALLOC);
memcpy((void *)buf, tmp_buf, len);
}
code = simdjson::active_implementation->parse(buf, len, *this);
// We're indicating validity via the doc_ref_result, so set the parse state back to invalid
valid = false;
error = UNINITIALIZED;
if (realloc_if_needed) {
aligned_free((void *)buf); // must free before we exit
}
return document::doc_ref_result(doc, code);
}
really_inline document::doc_ref_result document::parser::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept {
return parse((const uint8_t *)buf, len, realloc_if_needed);
}
really_inline document::doc_ref_result document::parser::parse(const std::string &s, bool realloc_if_needed) noexcept {
return parse(s.data(), s.length(), realloc_if_needed);
}
really_inline document::doc_ref_result document::parser::parse(const padded_string &s) noexcept {
return parse(s.data(), s.length(), false);
}
// TODO really_inline?
inline document::doc_result document::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) noexcept {
document::parser parser;
if (!parser.allocate_capacity(len)) {
return MEMALLOC;
}
auto [doc, error] = parser.parse(buf, len, realloc_if_needed);
return document::doc_result((document &&)doc, error);
}
really_inline document::doc_result document::parse(const char *buf, size_t len, bool realloc_if_needed) noexcept {
return parse((const uint8_t *)buf, len, realloc_if_needed);
}
really_inline document::doc_result document::parse(const std::string &s, bool realloc_if_needed) noexcept {
return parse(s.data(), s.length(), realloc_if_needed);
}
really_inline document::doc_result document::parse(const padded_string &s) noexcept {
return parse(s.data(), s.length(), false);
}
//
// Parser callbacks
//
WARN_UNUSED
inline error_code document::parser::init_parse(size_t len) {
if (len > capacity()) {
return error = CAPACITY;
}
// If the last doc was taken, we need to allocate a new one
if (!doc.tape) {
if (!doc.set_capacity(len)) {
return error = MEMALLOC;
}
}
return SUCCESS;
}
inline void document::parser::init_stage2() {
current_string_buf_loc = doc.string_buf.get();
current_loc = 0;
valid = false;
error = UNINITIALIZED;
}
really_inline error_code document::parser::on_error(error_code new_error_code) {
error = new_error_code;
return new_error_code;
}
really_inline error_code document::parser::on_success(error_code success_code) {
error = success_code;
valid = true;
return success_code;
}
really_inline bool document::parser::on_start_document(uint32_t depth) {
containing_scope_offset[depth] = current_loc;
write_tape(0, 'r');
return true;
}
really_inline bool document::parser::on_start_object(uint32_t depth) {
containing_scope_offset[depth] = current_loc;
write_tape(0, '{');
return true;
}
really_inline bool document::parser::on_start_array(uint32_t depth) {
containing_scope_offset[depth] = current_loc;
write_tape(0, '[');
return true;
}
// TODO we're not checking this bool
really_inline bool document::parser::on_end_document(uint32_t depth) {
// write our doc.tape location to the header scope
// The root scope gets written *at* the previous location.
annotate_previous_loc(containing_scope_offset[depth], current_loc);
write_tape(containing_scope_offset[depth], 'r');
return true;
}
really_inline bool document::parser::on_end_object(uint32_t depth) {
// write our doc.tape location to the header scope
write_tape(containing_scope_offset[depth], '}');
annotate_previous_loc(containing_scope_offset[depth], current_loc);
return true;
}
really_inline bool document::parser::on_end_array(uint32_t depth) {
// write our doc.tape location to the header scope
write_tape(containing_scope_offset[depth], ']');
annotate_previous_loc(containing_scope_offset[depth], current_loc);
return true;
}
really_inline bool document::parser::on_true_atom() {
write_tape(0, 't');
return true;
}
really_inline bool document::parser::on_false_atom() {
write_tape(0, 'f');
return true;
}
really_inline bool document::parser::on_null_atom() {
write_tape(0, 'n');
return true;
}
really_inline uint8_t *document::parser::on_start_string() {
/* we advance the point, accounting for the fact that we have a NULL
* termination */
write_tape(current_string_buf_loc - doc.string_buf.get(), '"');
return current_string_buf_loc + sizeof(uint32_t);
}
really_inline bool document::parser::on_end_string(uint8_t *dst) {
uint32_t str_length = dst - (current_string_buf_loc + sizeof(uint32_t));
// TODO check for overflow in case someone has a crazy string (>=4GB?)
// But only add the overflow check when the document itself exceeds 4GB
// Currently unneeded because we refuse to parse docs larger or equal to 4GB.
memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t));
// NULL termination is still handy if you expect all your strings to
// be NULL terminated? It comes at a small cost
*dst = 0;
current_string_buf_loc = dst + 1;
return true;
}
really_inline bool document::parser::on_number_s64(int64_t value) {
write_tape(0, 'l');
std::memcpy(&doc.tape[current_loc], &value, sizeof(value));
++current_loc;
return true;
}
really_inline bool document::parser::on_number_u64(uint64_t value) {
write_tape(0, 'u');
doc.tape[current_loc++] = value;
return true;
}
really_inline bool document::parser::on_number_double(double value) {
write_tape(0, 'd');
static_assert(sizeof(value) == sizeof(doc.tape[current_loc]), "mismatch size");
memcpy(&doc.tape[current_loc++], &value, sizeof(double));
// doc.tape[doc.current_loc++] = *((uint64_t *)&d);
return true;
}
} // namespace simdjson
#endif // SIMDJSON_INLINE_DOCUMENT_H