mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
910f272467
* 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
193 lines
6.5 KiB
C++
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
|