Add value.type()

This commit is contained in:
John Keiser 2021-03-02 13:22:26 -08:00
parent 29fe1866ef
commit 2ed24666b5
20 changed files with 331 additions and 28 deletions

View File

@ -43,12 +43,14 @@ SIMDJSON_DISABLE_UNDESIRED_WARNINGS
// Public API // Public API
#include "simdjson/simdjson_version.h" #include "simdjson/simdjson_version.h"
#include "simdjson/error.h" #include "simdjson/error.h"
#include "simdjson/json_type.h"
#include "simdjson/minify.h" #include "simdjson/minify.h"
#include "simdjson/padded_string.h" #include "simdjson/padded_string.h"
#include "simdjson/implementation.h" #include "simdjson/implementation.h"
// Inline functions // Inline functions
#include "simdjson/error-inl.h" #include "simdjson/error-inl.h"
#include "simdjson/json_type-inl.h"
#include "simdjson/padded_string-inl.h" #include "simdjson/padded_string-inl.h"
// DOM // DOM

View File

@ -96,7 +96,7 @@ simdjson_really_inline const T& simdjson_result_base<T>::value_unsafe() const& n
template<typename T> template<typename T>
simdjson_really_inline T&& simdjson_result_base<T>::value_unsafe() && noexcept { simdjson_really_inline T&& simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);; return std::forward<T>(this->first);
} }
template<typename T> template<typename T>

View File

@ -135,6 +135,13 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
*/ */
simdjson_really_inline error_code get(T &value) && noexcept; simdjson_really_inline error_code get(T &value) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_really_inline const T &value(error_code &error) const & noexcept;
/** /**
* The error. * The error.
*/ */

View File

@ -49,6 +49,16 @@ simdjson_really_inline implementation_simdjson_result_base<T>::operator T&&() &&
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value(); return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
} }
template<typename T>
simdjson_really_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_really_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
#endif // SIMDJSON_EXCEPTIONS #endif // SIMDJSON_EXCEPTIONS
template<typename T> template<typename T>

View File

@ -97,6 +97,18 @@ struct implementation_simdjson_result_base {
*/ */
simdjson_really_inline operator T&&() && noexcept(false); simdjson_really_inline operator T&&() && noexcept(false);
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evoluates to false.
*/
simdjson_really_inline const T& value_unsafe() const& noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evoluates to false.
*/
simdjson_really_inline T&& value_unsafe() && noexcept;
#endif // SIMDJSON_EXCEPTIONS #endif // SIMDJSON_EXCEPTIONS
T first{}; T first{};

View File

@ -115,6 +115,10 @@ simdjson_really_inline simdjson_result<value> document::operator[](const char *k
return resume_value()[key]; return resume_value()[key];
} }
simdjson_really_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
} // namespace ondemand } // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
@ -239,6 +243,11 @@ template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTA
return SUCCESS; return SUCCESS;
} }
simdjson_really_inline simdjson_result<json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) { simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); } if (error()) { throw simdjson_error(error()); }

View File

@ -263,6 +263,17 @@ public:
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */ /** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) & noexcept; simdjson_really_inline simdjson_result<value> operator[](const char *key) & noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
protected: protected:
simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept; simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept;
@ -337,6 +348,8 @@ public:
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) & noexcept; simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept; simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept; simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_really_inline simdjson_result<json_type> type() noexcept;
}; };
} // namespace simdjson } // namespace simdjson

View File

@ -117,6 +117,10 @@ simdjson_really_inline simdjson_result<value> value::operator[](const char *key)
return start_or_resume_object()[key]; return start_or_resume_object()[key];
} }
simdjson_really_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
} // namespace ondemand } // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION } // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson } // namespace simdjson
@ -230,6 +234,11 @@ template<> simdjson_really_inline error_code simdjson_result<SIMDJSON_IMPLEMENTA
return SUCCESS; return SUCCESS;
} }
simdjson_really_inline simdjson_result<json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) { simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); } if (error()) { throw simdjson_error(error()); }

View File

@ -260,6 +260,17 @@ public:
/** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */ /** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<value> operator[](const char *key) noexcept; simdjson_really_inline simdjson_result<value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
protected: protected:
/** /**
* Create a value. * Create a value.
@ -394,6 +405,15 @@ public:
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) noexcept; simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept; */ /** @overload simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) noexcept; simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](const char *key) noexcept;
/**
* Get the type of this JSON value.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
}; };
} // namespace simdjson } // namespace simdjson

View File

@ -568,7 +568,29 @@ simdjson_really_inline void value_iterator::assert_is_valid() const noexcept {
} }
simdjson_really_inline bool value_iterator::is_valid() const noexcept { simdjson_really_inline bool value_iterator::is_valid() const noexcept {
return _json_iter; return _json_iter != nullptr;
}
simdjson_really_inline simdjson_result<json_type> value_iterator::type() noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return TAPE_ERROR;
}
} }
} // namespace ondemand } // namespace ondemand

View File

@ -80,6 +80,13 @@ public:
*/ */
simdjson_really_inline depth_t depth() const noexcept; simdjson_really_inline depth_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_really_inline simdjson_result<json_type> type() noexcept;
/** /**
* @addtogroup object Object iteration * @addtogroup object Object iteration
* *

View File

@ -0,0 +1,29 @@
#ifndef SIMDJSON_JSON_TYPE_INL_H
#define SIMDJSON_JSON_TYPE_INL_H
#include "simdjson/json_type.h"
namespace simdjson {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
} // namespace simdjson
#endif // SIMDJSON_JSON_TYPE_INL_H

View File

@ -0,0 +1,44 @@
#ifndef SIMDJSON_JSON_TYPE_H
#define SIMDJSON_JSON_TYPE_H
#include "simdjson/common_defs.h"
namespace simdjson {
/**
* The type of a JSON value.
*/
enum class json_type {
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "å" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace simdjson
#endif // SIMDJSON_JSON_TYPE_H

View File

@ -6,13 +6,14 @@ using namespace simdjson;
namespace array_tests { namespace array_tests {
using namespace std; using namespace std;
bool iterate_array() { bool iterate_document_array() {
TEST_START(); TEST_START();
const auto json = R"([ 1, 10, 100 ])"_padded; const auto json = R"([ 1, 10, 100 ])"_padded;
const uint64_t expected_value[] = { 1, 10, 100 }; const uint64_t expected_value[] = { 1, 10, 100 };
SUBTEST("ondemand::array", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST("ondemand::array", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::array array; ondemand::array array;
ASSERT_RESULT( doc_result.type(), json_type::array );
ASSERT_SUCCESS( doc_result.get(array) ); ASSERT_SUCCESS( doc_result.get(array) );
size_t i=0; size_t i=0;
@ -25,16 +26,20 @@ namespace array_tests {
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true; return true;
})); }));
SUBTEST("simdjson_result<ondemand::array>", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST("simdjson_result<ondemand::array>", test_ondemand_doc(json, [&](auto doc_result) {
simdjson_result<ondemand::array> array = doc_result.get_array(); simdjson_result<ondemand::array> array = doc_result.get_array();
ASSERT_RESULT( doc_result.type(), json_type::array );
size_t i=0; size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; } for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true; return true;
})); }));
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::document doc; ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) ); ASSERT_SUCCESS( std::move(doc_result).get(doc) );
ASSERT_RESULT( doc.type(), json_type::array );
size_t i=0; size_t i=0;
for (simdjson_unused auto value : doc) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; } for (simdjson_unused auto value : doc) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
@ -42,6 +47,7 @@ namespace array_tests {
})); }));
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
size_t i=0; size_t i=0;
ASSERT_RESULT( doc_result.type(), json_type::array );
for (simdjson_unused auto value : doc_result) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; } for (simdjson_unused auto value : doc_result) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
return true; return true;
@ -49,6 +55,81 @@ namespace array_tests {
TEST_SUCCEED(); TEST_SUCCEED();
} }
bool iterate_array() {
TEST_START();
const auto json = R"( [ [ 1, 10, 100 ] ] )"_padded;
const uint64_t expected_value[] = { 1, 10, 100 };
SUBTEST("ondemand::array", test_ondemand_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<ondemand::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
ondemand::array array;
ASSERT_RESULT( array_result.type(), json_type::array );
ASSERT_SUCCESS( array_result.get(array) );
size_t i=0;
for (auto value : array) {
int64_t actual;
ASSERT_SUCCESS( value.get(actual) );
ASSERT_EQUAL(actual, expected_value[i]);
i++;
}
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("simdjson_result<ondemand::array>", test_ondemand_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<ondemand::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
ondemand::array array;
ASSERT_RESULT( array_result.type(), json_type::array );
ASSERT_SUCCESS( array_result.get(array) );
size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<ondemand::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
ondemand::value array;
ASSERT_SUCCESS( std::move(array_result).get(array) );
ASSERT_RESULT( array.type(), json_type::array );
size_t i=0;
for (simdjson_unused auto value : array) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
bool found = false;
for (simdjson_result<ondemand::value> array_result : doc_result) {
ASSERT_TRUE(!found); found = true;
size_t i=0;
ASSERT_RESULT( array_result.type(), json_type::array );
for (simdjson_unused auto value : array_result) { int64_t actual; ASSERT_SUCCESS( value.get(actual) ); ASSERT_EQUAL(actual, expected_value[i]); i++; }
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
}
ASSERT_TRUE(found);
return true;
}));
TEST_SUCCEED();
}
bool iterate_array_partial_children() { bool iterate_array_partial_children() {
TEST_START(); TEST_START();
auto json = R"( auto json = R"(
@ -260,6 +341,7 @@ namespace array_tests {
bool run() { bool run() {
return return
iterate_array() && iterate_array() &&
iterate_document_array() &&
iterate_empty_array() && iterate_empty_array() &&
iterate_array_partial_children() && iterate_array_partial_children() &&
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS

View File

@ -181,11 +181,22 @@ namespace error_tests {
TEST_SUCCEED(); TEST_SUCCEED();
} }
bool invalid_type() {
TEST_START();
ONDEMAND_SUBTEST("]", "]", doc.type().error() == TAPE_ERROR);
ONDEMAND_SUBTEST("}", "}", doc.type().error() == TAPE_ERROR);
ONDEMAND_SUBTEST(":", ":", doc.type().error() == TAPE_ERROR);
ONDEMAND_SUBTEST(",", ",", doc.type().error() == TAPE_ERROR);
ONDEMAND_SUBTEST("+", "+", doc.type().error() == TAPE_ERROR);
TEST_SUCCEED();
}
bool run() { bool run() {
return return
empty_document_error() && empty_document_error() &&
get_fail_then_succeed_bool() && get_fail_then_succeed_bool() &&
get_fail_then_succeed_null() && get_fail_then_succeed_null() &&
invalid_type() &&
true; true;
} }
} }

View File

@ -122,7 +122,7 @@ namespace object_error_tests {
TEST_START(); TEST_START();
ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", ([&]() { ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", ([&]() {
auto obj = doc.get_object(); auto obj = doc.get_object();
return assert_result<int64_t>(obj["a"], 1) && assert_error(obj["b"], TAPE_ERROR); return assert_result(obj["a"], int64_t(1)) && assert_error(obj["b"], TAPE_ERROR);
})()); })());
TEST_SUCCEED(); TEST_SUCCEED();
} }

View File

@ -13,6 +13,7 @@ namespace object_tests {
const uint64_t expected_value[] = { 1, 2, 3 }; const uint64_t expected_value[] = { 1, 2, 3 };
SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) {
ondemand::object object; ondemand::object object;
ASSERT_RESULT( doc_result.type(), json_type::object );
ASSERT_SUCCESS( doc_result.get(object) ); ASSERT_SUCCESS( doc_result.get(object) );
size_t i = 0; size_t i = 0;
for (auto [ field, error ] : object) { for (auto [ field, error ] : object) {

View File

@ -5,18 +5,26 @@ using namespace simdjson;
namespace scalar_tests { namespace scalar_tests {
using namespace std; using namespace std;
template<typename T> json_type expected_json_type();
template<> json_type expected_json_type<std::string_view>() { return json_type::string; }
template<> json_type expected_json_type<double>() { return json_type::number; }
template<> json_type expected_json_type<uint64_t>() { return json_type::number; }
template<> json_type expected_json_type<int64_t>() { return json_type::number; }
template<> json_type expected_json_type<bool>() { return json_type::boolean; }
template<typename T> template<typename T>
bool test_scalar_value(const padded_string &json, const T &expected, bool test_twice=true) { bool test_scalar_value(const padded_string &json, const T &expected, bool test_twice=true) {
std::cout << "- JSON: " << json << endl; std::cout << "- JSON: " << json << endl;
SUBTEST( "simdjson_result<document>", test_ondemand_doc(json, [&](auto doc_result) { SUBTEST( "simdjson_result<document>", test_ondemand_doc(json, [&](auto doc_result) {
T actual; T actual;
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc_result.get(actual) ); ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( doc_result.get(actual) ); ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
} }
return true; return true;
})); }));
@ -24,12 +32,14 @@ namespace scalar_tests {
ondemand::document doc; ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) ); ASSERT_SUCCESS( std::move(doc_result).get(doc) );
T actual; T actual;
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc.get(actual) ); ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( doc.get(actual) ); ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
} }
return true; return true;
})); }));
@ -39,12 +49,14 @@ namespace scalar_tests {
std::cout << "- JSON: " << whitespace_json << endl; std::cout << "- JSON: " << whitespace_json << endl;
SUBTEST( "simdjson_result<document>", test_ondemand_doc(whitespace_json, [&](auto doc_result) { SUBTEST( "simdjson_result<document>", test_ondemand_doc(whitespace_json, [&](auto doc_result) {
T actual; T actual;
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc_result.get(actual) ); ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( doc_result.get(actual) ); ASSERT_SUCCESS( doc_result.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc_result.type(), expected_json_type<T>() );
} }
return true; return true;
})); }));
@ -52,12 +64,14 @@ namespace scalar_tests {
ondemand::document doc; ondemand::document doc;
ASSERT_SUCCESS( std::move(doc_result).get(doc) ); ASSERT_SUCCESS( std::move(doc_result).get(doc) );
T actual; T actual;
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
ASSERT_SUCCESS( doc.get(actual) ); ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( doc.get(actual) ); ASSERT_SUCCESS( doc.get(actual) );
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
ASSERT_RESULT( doc.type(), expected_json_type<T>() );
} }
return true; return true;
})); }));
@ -70,12 +84,14 @@ namespace scalar_tests {
int count = 0; int count = 0;
for (simdjson_result<ondemand::value> val_result : doc_result) { for (simdjson_result<ondemand::value> val_result : doc_result) {
T actual; T actual;
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val_result.get(actual) ); ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( val_result.get(actual) ); ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
} }
count++; count++;
} }
@ -88,12 +104,14 @@ namespace scalar_tests {
ondemand::value val; ondemand::value val;
ASSERT_SUCCESS( val_result.get(val) ); ASSERT_SUCCESS( val_result.get(val) );
T actual; T actual;
ASSERT_RESULT( val.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val.get(actual) ); ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( val.get(actual) ); ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val.type(), expected_json_type<T>() );
} }
count++; count++;
} }
@ -105,34 +123,40 @@ namespace scalar_tests {
{ {
padded_string whitespace_array_json = std::string("[") + std::string(json) + " ]"; padded_string whitespace_array_json = std::string("[") + std::string(json) + " ]";
std::cout << "- JSON: " << whitespace_array_json << endl; std::cout << "- JSON: " << whitespace_array_json << endl;
SUBTEST( "simdjson_result<value>", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) { SUBTEST( "simdjson_result<value>", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) {
int count = 0; int count = 0;
for (simdjson_result<ondemand::value> val_result : doc_result) { for (simdjson_result<ondemand::value> val_result : doc_result) {
T actual; T actual;
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val_result.get(actual) ); ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( val_result.get(actual) ); ASSERT_SUCCESS( val_result.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
} }
count++; count++;
} }
ASSERT_EQUAL(count, 1); ASSERT_EQUAL(count, 1);
return true; return true;
})); }));
SUBTEST( "value", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) { SUBTEST( "value", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) {
int count = 0; int count = 0;
for (simdjson_result<ondemand::value> val_result : doc_result) { for (simdjson_result<ondemand::value> val_result : doc_result) {
ondemand::value val; ondemand::value val;
ASSERT_SUCCESS( val_result.get(val) ); ASSERT_SUCCESS( val_result.get(val) );
T actual; T actual;
ASSERT_RESULT( val.type(), expected_json_type<T>() );
ASSERT_SUCCESS( val.get(actual) ); ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
// Test it twice (scalars can be retrieved more than once) // Test it twice (scalars can be retrieved more than once)
if (test_twice) { if (test_twice) {
ASSERT_SUCCESS( val.get(actual) ); ASSERT_SUCCESS( val.get(actual) );
ASSERT_EQUAL(expected, actual); ASSERT_EQUAL(actual, expected);
ASSERT_RESULT( val_result.type(), expected_json_type<T>() );
} }
count++; count++;
} }
@ -227,7 +251,7 @@ namespace scalar_tests {
SUBTEST( "value", test_ondemand_doc(array_json, [&](auto doc_result) { SUBTEST( "value", test_ondemand_doc(array_json, [&](auto doc_result) {
int count = 0; int count = 0;
for (T actual : doc_result) { for (T actual : doc_result) {
ASSERT_EQUAL( expected, actual ); ASSERT_EQUAL( actual, expected );
count++; count++;
} }
ASSERT_EQUAL(count, 1); ASSERT_EQUAL(count, 1);

View File

@ -31,7 +31,7 @@ bool test_ondemand_doc(const simdjson::padded_string &json, const F& f) {
#define ONDEMAND_SUBTEST(NAME, JSON, TEST) \ #define ONDEMAND_SUBTEST(NAME, JSON, TEST) \
{ \ { \
std::cout << "- Subtest " << (NAME) << " - JSON: " << (JSON) << " ..." << std::endl; \ std::cout << "- Subtest " << NAME << " - JSON: " << (JSON) << " ..." << std::endl; \
if (!test_ondemand_doc(JSON##_padded, [&](auto doc) { \ if (!test_ondemand_doc(JSON##_padded, [&](auto doc) { \
return (TEST); \ return (TEST); \
})) { \ })) { \

View File

@ -23,9 +23,9 @@ const char *SMALLDEMO_JSON = SIMDJSON_BENCHMARK_SMALLDATA_DIR "smalldemo.json";
const char *TRUENULL_JSON = SIMDJSON_BENCHMARK_SMALLDATA_DIR "truenull.json"; const char *TRUENULL_JSON = SIMDJSON_BENCHMARK_SMALLDATA_DIR "truenull.json";
// For the ASSERT_EQUAL macro // For the ASSERT_EQUAL macro
template<typename T, typename S> template<typename A, typename E>
simdjson_really_inline bool equals_expected(T actual, S expected) { simdjson_really_inline bool equals_expected(A actual, E expected) {
return actual == T(expected); return actual == A(expected);
} }
template<> template<>
simdjson_really_inline bool equals_expected<const char *, const char *>(const char *actual, const char *expected) { simdjson_really_inline bool equals_expected<const char *, const char *>(const char *actual, const char *expected) {
@ -76,10 +76,11 @@ simdjson_really_inline bool assert_error(const T &actual_result, simdjson::error
} }
return true; return true;
} }
template<typename E, typename A> template<typename A, typename E>
simdjson_really_inline bool assert_result(simdjson::simdjson_result<A> &&actual_result, const E &expected, const char *operation = "result") { simdjson_really_inline bool assert_result(simdjson::simdjson_result<A> &&actual_result, const E &expected, const char *operation = "result") {
E actual; E actual;
return assert_success(actual_result.get(actual), operation) && assert_equal(actual, expected, operation); return assert_success(std::forward<simdjson::simdjson_result<A>>(actual_result).get(actual))
&& assert_equal(actual, expected, operation);
} }
simdjson_really_inline bool assert_true(bool value, const char *operation = "result") { simdjson_really_inline bool assert_true(bool value, const char *operation = "result") {
if (!value) { if (!value) {
@ -102,7 +103,7 @@ simdjson_really_inline bool assert_iterate_error(T &arr, simdjson::error_code ex
#define TEST_START() do { std::cout << "Running " << __func__ << " ..." << std::endl; } while(0); #define TEST_START() do { std::cout << "Running " << __func__ << " ..." << std::endl; } while(0);
#define SUBTEST(NAME, TEST) do { std::cout << "- Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0); #define SUBTEST(NAME, TEST) do { std::cout << "- Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0); #define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0); #define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_SUCCESS(ACTUAL) do { if (!::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0); #define ASSERT_SUCCESS(ACTUAL) do { if (!::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0);
#define ASSERT_ERROR(ACTUAL, EXPECTED) do { if (!::assert_error ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0); #define ASSERT_ERROR(ACTUAL, EXPECTED) do { if (!::assert_error ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_TRUE(ACTUAL) do { if (!::assert_true ((ACTUAL), #ACTUAL)) { return false; } } while (0); #define ASSERT_TRUE(ACTUAL) do { if (!::assert_true ((ACTUAL), #ACTUAL)) { return false; } } while (0);