Files
simdjson-simdjson/include/simdjson/inline/document.h
T
John Keiser 99667f7c55 Create top level simdjson.h (#515)
- Allows everyone to #include the same way, singleheader or not.
2020-03-04 10:12:27 -08:00

700 lines
26 KiB
C++

#ifndef SIMDJSON_INLINE_DOCUMENT_H
#define SIMDJSON_INLINE_DOCUMENT_H
#include "simdjson/document.h"
// 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"
#include <iostream>
namespace simdjson {
//
// document::element_result<T> inline implementation
//
template<typename T>
inline document::element_result<T>::element_result(T _value) noexcept : value(_value), error{SUCCESS} {}
template<typename T>
inline document::element_result<T>::element_result(error_code _error) noexcept : value(), error{_error} {}
template<>
inline document::element_result<std::string_view>::operator std::string_view() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
template<>
inline document::element_result<const char *>::operator const char *() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
template<>
inline document::element_result<bool>::operator bool() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
template<>
inline document::element_result<uint64_t>::operator uint64_t() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
template<>
inline document::element_result<int64_t>::operator int64_t() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
template<>
inline document::element_result<double>::operator double() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
//
// document::element_result<document::array> inline implementation
//
inline document::element_result<document::array>::element_result(document::array _value) noexcept : value(_value), error{SUCCESS} {}
inline document::element_result<document::array>::element_result(error_code _error) noexcept : value(), error{_error} {}
inline document::element_result<document::array>::operator document::array() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
inline document::array::iterator document::element_result<document::array>::begin() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value.begin();
}
inline document::array::iterator document::element_result<document::array>::end() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value.end();
}
//
// document::element_result<document::object> inline implementation
//
inline document::element_result<document::object>::element_result(document::object _value) noexcept : value(_value), error{SUCCESS} {}
inline document::element_result<document::object>::element_result(error_code _error) noexcept : value(), error{_error} {}
inline document::element_result<document::object>::operator document::object() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value;
}
inline document::element_result<document::element> document::element_result<document::object>::operator[](const std::string_view &key) const noexcept {
if (error) { return error; }
return value[key];
}
inline document::element_result<document::element> document::element_result<document::object>::operator[](const char *key) const noexcept {
if (error) { return error; }
return value[key];
}
inline document::object::iterator document::element_result<document::object>::begin() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value.begin();
}
inline document::object::iterator document::element_result<document::object>::end() const noexcept(false) {
if (error) { throw invalid_json(error); }
return value.end();
}
//
// document::element_result<document::element> inline implementation
//
inline document::element_result<document::element>::element_result(document::element _value) noexcept : value(_value), error{SUCCESS} {}
inline document::element_result<document::element>::element_result(error_code _error) noexcept : value(), error{_error} {}
inline document::element_result<bool> document::element_result<document::element>::is_null() const noexcept {
if (error) { return error; }
return value.is_null();
}
inline document::element_result<bool> document::element_result<document::element>::as_bool() const noexcept {
if (error) { return error; }
return value.as_bool();
}
inline document::element_result<const char*> document::element_result<document::element>::as_c_str() const noexcept {
if (error) { return error; }
return value.as_c_str();
}
inline document::element_result<std::string_view> document::element_result<document::element>::as_string() const noexcept {
if (error) { return error; }
return value.as_string();
}
inline document::element_result<uint64_t> document::element_result<document::element>::as_uint64_t() const noexcept {
if (error) { return error; }
return value.as_uint64_t();
}
inline document::element_result<int64_t> document::element_result<document::element>::as_int64_t() const noexcept {
if (error) { return error; }
return value.as_int64_t();
}
inline document::element_result<double> document::element_result<document::element>::as_double() const noexcept {
if (error) { return error; }
return value.as_double();
}
inline document::element_result<document::array> document::element_result<document::element>::as_array() const noexcept {
if (error) { return error; }
return value.as_array();
}
inline document::element_result<document::object> document::element_result<document::element>::as_object() const noexcept {
if (error) { return error; }
return value.as_object();
}
inline document::element_result<document::element>::operator bool() const noexcept(false) {
return as_bool();
}
inline document::element_result<document::element>::operator const char *() const noexcept(false) {
return as_c_str();
}
inline document::element_result<document::element>::operator std::string_view() const noexcept(false) {
return as_string();
}
inline document::element_result<document::element>::operator uint64_t() const noexcept(false) {
return as_uint64_t();
}
inline document::element_result<document::element>::operator int64_t() const noexcept(false) {
return as_int64_t();
}
inline document::element_result<document::element>::operator double() const noexcept(false) {
return as_double();
}
inline document::element_result<document::element>::operator document::array() const noexcept(false) {
return as_array();
}
inline document::element_result<document::element>::operator document::object() const noexcept(false) {
return as_object();
}
inline document::element_result<document::element> document::element_result<document::element>::operator[](const std::string_view &key) const noexcept {
if (error) { return *this; }
return value[key];
}
inline document::element_result<document::element> document::element_result<document::element>::operator[](const char *key) const noexcept {
if (error) { return *this; }
return value[key];
}
//
// document inline implementation
//
inline document::element document::root() const noexcept {
return document::element(this, 1);
}
inline document::element_result<document::array> document::as_array() const noexcept {
return root().as_array();
}
inline document::element_result<document::object> document::as_object() const noexcept {
return root().as_object();
}
inline document::operator document::element() const noexcept {
return root();
}
inline document::operator document::array() const noexcept(false) {
return root();
}
inline document::operator document::object() const noexcept(false) {
return root();
}
inline document::element_result<document::element> document::operator[](const std::string_view &key) const noexcept {
return root()[key];
}
inline document::element_result<document::element> document::operator[](const char *key) const noexcept {
return root()[key];
}
//
// document::doc_ref_result inline implementation
//
inline document::doc_ref_result::doc_ref_result(document &_doc, error_code _error) noexcept : doc(_doc), error(_error) { }
inline document::doc_ref_result::operator document&() noexcept(false) {
if (error) {
throw invalid_json(error);
}
return doc;
}
inline const std::string &document::doc_ref_result::get_error_message() const noexcept {
return error_message(error);
}
//
// document::doc_result inline implementation
//
inline document::doc_result::doc_result(document &&_doc, error_code _error) noexcept : doc(std::move(_doc)), error(_error) { }
inline document::doc_result::doc_result(document &&_doc) noexcept : doc(std::move(_doc)), error(SUCCESS) { }
inline document::doc_result::doc_result(error_code _error) noexcept : doc(), error(_error) { }
inline document::doc_result::operator document() noexcept(false) {
if (error) {
throw invalid_json(error);
}
return std::move(doc);
}
inline const std::string &document::doc_result::get_error_message() const noexcept {
return error_message(error);
}
//
// document::parser inline implementation
//
inline bool document::parser::is_valid() const noexcept { return valid; }
inline int document::parser::get_error_code() const noexcept { return error; }
inline std::string document::parser::get_error_message() const noexcept { return error_message(error); }
inline bool document::parser::print_json(std::ostream &os) const noexcept {
return is_valid() ? doc.print_json(os) : false;
}
inline bool document::parser::dump_raw_tape(std::ostream &os) const noexcept {
return is_valid() ? doc.dump_raw_tape(os) : false;
}
inline const document &document::parser::get_document() const noexcept(false) {
if (!is_valid()) {
throw invalid_json(error);
}
return doc;
}
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) noexcept {
return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
}
really_inline document::doc_ref_result document::parser::parse(const padded_string &s) noexcept {
return parse(s.data(), s.length(), false);
}
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) noexcept {
return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
}
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) noexcept {
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() noexcept {
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) noexcept {
error = new_error_code;
return new_error_code;
}
really_inline error_code document::parser::on_success(error_code success_code) noexcept {
error = success_code;
valid = true;
return success_code;
}
really_inline bool document::parser::on_start_document(uint32_t depth) noexcept {
containing_scope_offset[depth] = current_loc;
write_tape(0, tape_type::ROOT);
return true;
}
really_inline bool document::parser::on_start_object(uint32_t depth) noexcept {
containing_scope_offset[depth] = current_loc;
write_tape(0, tape_type::START_OBJECT);
return true;
}
really_inline bool document::parser::on_start_array(uint32_t depth) noexcept {
containing_scope_offset[depth] = current_loc;
write_tape(0, tape_type::START_ARRAY);
return true;
}
// TODO we're not checking this bool
really_inline bool document::parser::on_end_document(uint32_t depth) noexcept {
// 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], tape_type::ROOT);
return true;
}
really_inline bool document::parser::on_end_object(uint32_t depth) noexcept {
// write our doc.tape location to the header scope
write_tape(containing_scope_offset[depth], tape_type::END_OBJECT);
annotate_previous_loc(containing_scope_offset[depth], current_loc);
return true;
}
really_inline bool document::parser::on_end_array(uint32_t depth) noexcept {
// write our doc.tape location to the header scope
write_tape(containing_scope_offset[depth], tape_type::END_ARRAY);
annotate_previous_loc(containing_scope_offset[depth], current_loc);
return true;
}
really_inline bool document::parser::on_true_atom() noexcept {
write_tape(0, tape_type::TRUE_VALUE);
return true;
}
really_inline bool document::parser::on_false_atom() noexcept {
write_tape(0, tape_type::FALSE_VALUE);
return true;
}
really_inline bool document::parser::on_null_atom() noexcept {
write_tape(0, tape_type::NULL_VALUE);
return true;
}
really_inline uint8_t *document::parser::on_start_string() noexcept {
/* we advance the point, accounting for the fact that we have a NULL
* termination */
write_tape(current_string_buf_loc - doc.string_buf.get(), tape_type::STRING);
return current_string_buf_loc + sizeof(uint32_t);
}
really_inline bool document::parser::on_end_string(uint8_t *dst) noexcept {
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) noexcept {
write_tape(0, tape_type::INT64);
std::memcpy(&doc.tape[current_loc], &value, sizeof(value));
++current_loc;
return true;
}
really_inline bool document::parser::on_number_u64(uint64_t value) noexcept {
write_tape(0, tape_type::UINT64);
doc.tape[current_loc++] = value;
return true;
}
really_inline bool document::parser::on_number_double(double value) noexcept {
write_tape(0, tape_type::DOUBLE);
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;
}
really_inline void document::parser::write_tape(uint64_t val, document::tape_type t) noexcept {
doc.tape[current_loc++] = val | ((static_cast<uint64_t>(static_cast<char>(t))) << 56);
}
really_inline void document::parser::annotate_previous_loc(uint32_t saved_loc, uint64_t val) noexcept {
doc.tape[saved_loc] |= val;
}
//
// document::tape_ref inline implementation
//
really_inline document::tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {}
really_inline document::tape_ref::tape_ref(const document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {}
inline size_t document::tape_ref::after_element() const noexcept {
switch (type()) {
case tape_type::START_ARRAY:
case tape_type::START_OBJECT:
return tape_value();
case tape_type::UINT64:
case tape_type::INT64:
case tape_type::DOUBLE:
return json_index + 2;
default:
return json_index + 1;
}
}
really_inline document::tape_type document::tape_ref::type() const noexcept {
return static_cast<tape_type>(doc->tape[json_index] >> 56);
}
really_inline uint64_t document::tape_ref::tape_value() const noexcept {
return doc->tape[json_index] & JSON_VALUE_MASK;
}
template<typename T>
really_inline T document::tape_ref::next_tape_value() const noexcept {
static_assert(sizeof(T) == sizeof(uint64_t));
return *reinterpret_cast<const T*>(&doc->tape[json_index + 1]);
}
//
// document::array inline implementation
//
really_inline document::array::array() noexcept : tape_ref() {}
really_inline document::array::array(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) {}
inline document::array::iterator document::array::begin() const noexcept {
return iterator(doc, json_index + 1);
}
inline document::array::iterator document::array::end() const noexcept {
return iterator(doc, after_element() - 1);
}
//
// document::array::iterator inline implementation
//
really_inline document::array::iterator::iterator(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { }
inline document::element document::array::iterator::operator*() const noexcept {
return element(doc, json_index);
}
inline bool document::array::iterator::operator!=(const document::array::iterator& other) const noexcept {
return json_index != other.json_index;
}
inline void document::array::iterator::operator++() noexcept {
json_index = after_element();
}
//
// document::object inline implementation
//
really_inline document::object::object() noexcept : tape_ref() {}
really_inline document::object::object(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { };
inline document::object::iterator document::object::begin() const noexcept {
return iterator(doc, json_index + 1);
}
inline document::object::iterator document::object::end() const noexcept {
return iterator(doc, after_element() - 1);
}
inline document::element_result<document::element> document::object::operator[](const std::string_view &key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
if (key == field.key()) {
return field.value();
}
}
return NO_SUCH_FIELD;
}
inline document::element_result<document::element> document::object::operator[](const char *key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
if (!strcmp(key, field.key_c_str())) {
return field.value();
}
}
return NO_SUCH_FIELD;
}
//
// document::object::iterator inline implementation
//
really_inline document::object::iterator::iterator(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { }
inline const document::key_value_pair document::object::iterator::operator*() const noexcept {
return key_value_pair(key(), value());
}
inline bool document::object::iterator::operator!=(const document::object::iterator& other) const noexcept {
return json_index != other.json_index;
}
inline void document::object::iterator::operator++() noexcept {
json_index++;
json_index = after_element();
}
inline std::string_view document::object::iterator::key() const noexcept {
size_t string_buf_index = tape_value();
uint32_t len;
memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len));
return std::string_view(
reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]),
len
);
}
inline const char* document::object::iterator::key_c_str() const noexcept {
return reinterpret_cast<const char *>(&doc->string_buf[tape_value() + sizeof(uint32_t)]);
}
inline document::element document::object::iterator::value() const noexcept {
return element(doc, json_index + 1);
}
//
// document::key_value_pair inline implementation
//
inline document::key_value_pair::key_value_pair(std::string_view _key, document::element _value) noexcept :
key(_key), value(_value) {}
//
// document::element inline implementation
//
really_inline document::element::element() noexcept : tape_ref() {}
really_inline document::element::element(const document *_doc, size_t _json_index) noexcept : tape_ref(_doc, _json_index) { }
really_inline bool document::element::is_null() const noexcept {
return type() == tape_type::NULL_VALUE;
}
really_inline bool document::element::is_bool() const noexcept {
return type() == tape_type::TRUE_VALUE || type() == tape_type::FALSE_VALUE;
}
really_inline bool document::element::is_number() const noexcept {
return type() == tape_type::UINT64 || type() == tape_type::INT64 || type() == tape_type::DOUBLE;
}
really_inline bool document::element::is_integer() const noexcept {
return type() == tape_type::UINT64 || type() == tape_type::INT64;
}
really_inline bool document::element::is_string() const noexcept {
return type() == tape_type::STRING;
}
really_inline bool document::element::is_array() const noexcept {
return type() == tape_type::START_ARRAY;
}
really_inline bool document::element::is_object() const noexcept {
return type() == tape_type::START_OBJECT;
}
inline document::element::operator bool() const noexcept(false) { return as_bool(); }
inline document::element::operator const char*() const noexcept(false) { return as_c_str(); }
inline document::element::operator std::string_view() const noexcept(false) { return as_string(); }
inline document::element::operator uint64_t() const noexcept(false) { return as_uint64_t(); }
inline document::element::operator int64_t() const noexcept(false) { return as_int64_t(); }
inline document::element::operator double() const noexcept(false) { return as_double(); }
inline document::element::operator document::array() const noexcept(false) { return as_array(); }
inline document::element::operator document::object() const noexcept(false) { return as_object(); }
inline document::element_result<bool> document::element::as_bool() const noexcept {
switch (type()) {
case tape_type::TRUE_VALUE:
return true;
case tape_type::FALSE_VALUE:
return false;
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<const char *> document::element::as_c_str() const noexcept {
switch (type()) {
case tape_type::STRING: {
size_t string_buf_index = tape_value();
return reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]);
}
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<std::string_view> document::element::as_string() const noexcept {
switch (type()) {
case tape_type::STRING: {
size_t string_buf_index = tape_value();
uint32_t len;
memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len));
return std::string_view(
reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]),
len
);
}
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<uint64_t> document::element::as_uint64_t() const noexcept {
switch (type()) {
case tape_type::UINT64:
return next_tape_value<uint64_t>();
case tape_type::INT64: {
int64_t result = next_tape_value<int64_t>();
if (result < 0) {
return NUMBER_OUT_OF_RANGE;
}
return static_cast<uint64_t>(result);
}
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<int64_t> document::element::as_int64_t() const noexcept {
switch (type()) {
case tape_type::UINT64: {
uint64_t result = next_tape_value<uint64_t>();
// Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std
if (result > (std::numeric_limits<uint64_t>::max)()) {
return NUMBER_OUT_OF_RANGE;
}
return static_cast<int64_t>(result);
}
case tape_type::INT64:
return next_tape_value<int64_t>();
default:
std::cout << "Incorrect " << json_index << " = " << char(type()) << std::endl;
return INCORRECT_TYPE;
}
}
inline document::element_result<double> document::element::as_double() const noexcept {
switch (type()) {
case tape_type::UINT64:
return next_tape_value<uint64_t>();
case tape_type::INT64: {
return next_tape_value<int64_t>();
int64_t result = tape_value();
if (result < 0) {
return NUMBER_OUT_OF_RANGE;
}
return result;
}
case tape_type::DOUBLE:
return next_tape_value<double>();
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<document::array> document::element::as_array() const noexcept {
switch (type()) {
case tape_type::START_ARRAY:
return array(doc, json_index);
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<document::object> document::element::as_object() const noexcept {
switch (type()) {
case tape_type::START_OBJECT:
return object(doc, json_index);
default:
return INCORRECT_TYPE;
}
}
inline document::element_result<document::element> document::element::operator[](const std::string_view &key) const noexcept {
auto [obj, error] = as_object();
if (error) { return error; }
return obj[key];
}
inline document::element_result<document::element> document::element::operator[](const char *key) const noexcept {
auto [obj, error] = as_object();
if (error) { return error; }
return obj[key];
}
} // namespace simdjson
#endif // SIMDJSON_INLINE_DOCUMENT_H