From 2ed24666b5b5d0b5ff50fa5873932ce57737b650 Mon Sep 17 00:00:00 2001 From: John Keiser Date: Tue, 2 Mar 2021 13:22:26 -0800 Subject: [PATCH] Add value.type() --- include/simdjson.h | 2 + include/simdjson/error-inl.h | 2 +- include/simdjson/error.h | 7 ++ .../implementation_simdjson_result_base-inl.h | 10 +++ .../implementation_simdjson_result_base.h | 12 +++ .../simdjson/generic/ondemand/document-inl.h | 9 ++ include/simdjson/generic/ondemand/document.h | 13 +++ include/simdjson/generic/ondemand/value-inl.h | 9 ++ include/simdjson/generic/ondemand/value.h | 20 +++++ .../generic/ondemand/value_iterator-inl.h | 24 +++++- .../generic/ondemand/value_iterator.h | 7 ++ include/simdjson/json_type-inl.h | 29 +++++++ include/simdjson/json_type.h | 44 ++++++++++ tests/ondemand/ondemand_array_tests.cpp | 84 ++++++++++++++++++- tests/ondemand/ondemand_error_tests.cpp | 11 +++ .../ondemand/ondemand_object_error_tests.cpp | 2 +- tests/ondemand/ondemand_object_tests.cpp | 1 + tests/ondemand/ondemand_scalar_tests.cpp | 58 +++++++++---- tests/ondemand/test_ondemand.h | 2 +- tests/test_macros.h | 13 +-- 20 files changed, 331 insertions(+), 28 deletions(-) create mode 100644 include/simdjson/json_type-inl.h create mode 100644 include/simdjson/json_type.h diff --git a/include/simdjson.h b/include/simdjson.h index a1a9426a..3d94962b 100644 --- a/include/simdjson.h +++ b/include/simdjson.h @@ -43,12 +43,14 @@ SIMDJSON_DISABLE_UNDESIRED_WARNINGS // Public API #include "simdjson/simdjson_version.h" #include "simdjson/error.h" +#include "simdjson/json_type.h" #include "simdjson/minify.h" #include "simdjson/padded_string.h" #include "simdjson/implementation.h" // Inline functions #include "simdjson/error-inl.h" +#include "simdjson/json_type-inl.h" #include "simdjson/padded_string-inl.h" // DOM diff --git a/include/simdjson/error-inl.h b/include/simdjson/error-inl.h index 2dc94a8a..09ee893d 100644 --- a/include/simdjson/error-inl.h +++ b/include/simdjson/error-inl.h @@ -96,7 +96,7 @@ simdjson_really_inline const T& simdjson_result_base::value_unsafe() const& n template simdjson_really_inline T&& simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first);; + return std::forward(this->first); } template diff --git a/include/simdjson/error.h b/include/simdjson/error.h index 6030cbcc..94d4d3d5 100644 --- a/include/simdjson/error.h +++ b/include/simdjson/error.h @@ -135,6 +135,13 @@ struct simdjson_result_base : protected std::pair { */ 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. */ diff --git a/include/simdjson/generic/implementation_simdjson_result_base-inl.h b/include/simdjson/generic/implementation_simdjson_result_base-inl.h index 71a8bb21..13129492 100644 --- a/include/simdjson/generic/implementation_simdjson_result_base-inl.h +++ b/include/simdjson/generic/implementation_simdjson_result_base-inl.h @@ -49,6 +49,16 @@ simdjson_really_inline implementation_simdjson_result_base::operator T&&() && return std::forward>(*this).take_value(); } +template +simdjson_really_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} + +template +simdjson_really_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} + #endif // SIMDJSON_EXCEPTIONS template diff --git a/include/simdjson/generic/implementation_simdjson_result_base.h b/include/simdjson/generic/implementation_simdjson_result_base.h index a4e39456..375d093f 100644 --- a/include/simdjson/generic/implementation_simdjson_result_base.h +++ b/include/simdjson/generic/implementation_simdjson_result_base.h @@ -97,6 +97,18 @@ struct implementation_simdjson_result_base { */ 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 T first{}; diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index f532fdf9..271551b9 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -115,6 +115,10 @@ simdjson_really_inline simdjson_result document::operator[](const char *k return resume_value()[key]; } +simdjson_really_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson @@ -239,6 +243,11 @@ template<> simdjson_really_inline error_code simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + #if SIMDJSON_EXCEPTIONS simdjson_really_inline simdjson_result::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index 908ad0b6..5943247e 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -263,6 +263,17 @@ public: /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result 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 type() noexcept; + protected: simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -337,6 +348,8 @@ public: simdjson_really_inline simdjson_result operator[](const char *key) & noexcept; simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_really_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_really_inline simdjson_result type() noexcept; }; } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h index df5a5d12..16764ce6 100644 --- a/include/simdjson/generic/ondemand/value-inl.h +++ b/include/simdjson/generic/ondemand/value-inl.h @@ -117,6 +117,10 @@ simdjson_really_inline simdjson_result value::operator[](const char *key) return start_or_resume_object()[key]; } +simdjson_really_inline simdjson_result value::type() noexcept { + return iter.type(); +} + } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson @@ -230,6 +234,11 @@ template<> simdjson_really_inline error_code simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + #if SIMDJSON_EXCEPTIONS simdjson_really_inline simdjson_result::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index 2c50f139..c5877b1e 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -260,6 +260,17 @@ public: /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_really_inline simdjson_result 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 type() noexcept; + protected: /** * Create a value. @@ -394,6 +405,15 @@ public: simdjson_really_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_really_inline simdjson_result 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 type() noexcept; }; } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/value_iterator-inl.h b/include/simdjson/generic/ondemand/value_iterator-inl.h index 5b84e142..409ca6fa 100644 --- a/include/simdjson/generic/ondemand/value_iterator-inl.h +++ b/include/simdjson/generic/ondemand/value_iterator-inl.h @@ -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 { - return _json_iter; + return _json_iter != nullptr; +} + + +simdjson_really_inline simdjson_result 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 diff --git a/include/simdjson/generic/ondemand/value_iterator.h b/include/simdjson/generic/ondemand/value_iterator.h index 1bb0e602..527aa6a5 100644 --- a/include/simdjson/generic/ondemand/value_iterator.h +++ b/include/simdjson/generic/ondemand/value_iterator.h @@ -80,6 +80,13 @@ public: */ 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 type() noexcept; + /** * @addtogroup object Object iteration * diff --git a/include/simdjson/json_type-inl.h b/include/simdjson/json_type-inl.h new file mode 100644 index 00000000..20363707 --- /dev/null +++ b/include/simdjson/json_type-inl.h @@ -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 &type) noexcept(false) { + return out << type.value(); +} +#endif + +} // namespace simdjson + +#endif // SIMDJSON_JSON_TYPE_INL_H diff --git a/include/simdjson/json_type.h b/include/simdjson/json_type.h new file mode 100644 index 00000000..2c1bbd57 --- /dev/null +++ b/include/simdjson/json_type.h @@ -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 &type) noexcept(false); +#endif + +} // namespace simdjson + +#endif // SIMDJSON_JSON_TYPE_H diff --git a/tests/ondemand/ondemand_array_tests.cpp b/tests/ondemand/ondemand_array_tests.cpp index a1ad495a..245d5411 100644 --- a/tests/ondemand/ondemand_array_tests.cpp +++ b/tests/ondemand/ondemand_array_tests.cpp @@ -6,13 +6,14 @@ using namespace simdjson; namespace array_tests { using namespace std; - bool iterate_array() { + bool iterate_document_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) { ondemand::array array; + ASSERT_RESULT( doc_result.type(), json_type::array ); ASSERT_SUCCESS( doc_result.get(array) ); size_t i=0; @@ -25,16 +26,20 @@ namespace array_tests { ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); return true; })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { simdjson_result array = doc_result.get_array(); + ASSERT_RESULT( doc_result.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)); return true; })); + SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) { ondemand::document doc; ASSERT_SUCCESS( std::move(doc_result).get(doc) ); + ASSERT_RESULT( doc.type(), json_type::array ); 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++; } ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); @@ -42,6 +47,7 @@ namespace array_tests { })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { 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++; } ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value)); return true; @@ -49,6 +55,81 @@ namespace array_tests { 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 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", test_ondemand_doc(json, [&](auto doc_result) { + bool found = false; + for (simdjson_result 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 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", test_ondemand_doc(json, [&](auto doc_result) { + bool found = false; + for (simdjson_result 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() { TEST_START(); auto json = R"( @@ -260,6 +341,7 @@ namespace array_tests { bool run() { return iterate_array() && + iterate_document_array() && iterate_empty_array() && iterate_array_partial_children() && #if SIMDJSON_EXCEPTIONS diff --git a/tests/ondemand/ondemand_error_tests.cpp b/tests/ondemand/ondemand_error_tests.cpp index da13a27b..4c9fea6a 100644 --- a/tests/ondemand/ondemand_error_tests.cpp +++ b/tests/ondemand/ondemand_error_tests.cpp @@ -181,11 +181,22 @@ namespace error_tests { 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() { return empty_document_error() && get_fail_then_succeed_bool() && get_fail_then_succeed_null() && + invalid_type() && true; } } diff --git a/tests/ondemand/ondemand_object_error_tests.cpp b/tests/ondemand/ondemand_object_error_tests.cpp index 12062036..c97b78d0 100644 --- a/tests/ondemand/ondemand_object_error_tests.cpp +++ b/tests/ondemand/ondemand_object_error_tests.cpp @@ -122,7 +122,7 @@ namespace object_error_tests { TEST_START(); ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", ([&]() { auto obj = doc.get_object(); - return assert_result(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(); } diff --git a/tests/ondemand/ondemand_object_tests.cpp b/tests/ondemand/ondemand_object_tests.cpp index 72235845..4d1c448d 100644 --- a/tests/ondemand/ondemand_object_tests.cpp +++ b/tests/ondemand/ondemand_object_tests.cpp @@ -13,6 +13,7 @@ namespace object_tests { const uint64_t expected_value[] = { 1, 2, 3 }; SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) { ondemand::object object; + ASSERT_RESULT( doc_result.type(), json_type::object ); ASSERT_SUCCESS( doc_result.get(object) ); size_t i = 0; for (auto [ field, error ] : object) { diff --git a/tests/ondemand/ondemand_scalar_tests.cpp b/tests/ondemand/ondemand_scalar_tests.cpp index c7805be5..5e2bb4ab 100644 --- a/tests/ondemand/ondemand_scalar_tests.cpp +++ b/tests/ondemand/ondemand_scalar_tests.cpp @@ -5,18 +5,26 @@ using namespace simdjson; namespace scalar_tests { using namespace std; + template json_type expected_json_type(); + template<> json_type expected_json_type() { return json_type::string; } + template<> json_type expected_json_type() { return json_type::number; } + template<> json_type expected_json_type() { return json_type::number; } + template<> json_type expected_json_type() { return json_type::number; } + template<> json_type expected_json_type() { return json_type::boolean; } template bool test_scalar_value(const padded_string &json, const T &expected, bool test_twice=true) { std::cout << "- JSON: " << json << endl; SUBTEST( "simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { T actual; + ASSERT_RESULT( doc_result.type(), expected_json_type() ); ASSERT_SUCCESS( doc_result.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( doc_result.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); + ASSERT_RESULT( doc_result.type(), expected_json_type() ); } return true; })); @@ -24,12 +32,14 @@ namespace scalar_tests { ondemand::document doc; ASSERT_SUCCESS( std::move(doc_result).get(doc) ); T actual; + ASSERT_RESULT( doc.type(), expected_json_type() ); ASSERT_SUCCESS( doc.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( doc.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); + ASSERT_RESULT( doc.type(), expected_json_type() ); } return true; })); @@ -39,12 +49,14 @@ namespace scalar_tests { std::cout << "- JSON: " << whitespace_json << endl; SUBTEST( "simdjson_result", test_ondemand_doc(whitespace_json, [&](auto doc_result) { T actual; + ASSERT_RESULT( doc_result.type(), expected_json_type() ); ASSERT_SUCCESS( doc_result.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( doc_result.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); + ASSERT_RESULT( doc_result.type(), expected_json_type() ); } return true; })); @@ -52,12 +64,14 @@ namespace scalar_tests { ondemand::document doc; ASSERT_SUCCESS( std::move(doc_result).get(doc) ); T actual; + ASSERT_RESULT( doc.type(), expected_json_type() ); ASSERT_SUCCESS( doc.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( doc.get(actual) ); - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); + ASSERT_RESULT( doc.type(), expected_json_type() ); } return true; })); @@ -70,12 +84,14 @@ namespace scalar_tests { int count = 0; for (simdjson_result val_result : doc_result) { T actual; + ASSERT_RESULT( val_result.type(), expected_json_type() ); ASSERT_SUCCESS( val_result.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( val_result.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); + ASSERT_RESULT( val_result.type(), expected_json_type() ); } count++; } @@ -88,12 +104,14 @@ namespace scalar_tests { ondemand::value val; ASSERT_SUCCESS( val_result.get(val) ); T actual; + ASSERT_RESULT( val.type(), expected_json_type() ); ASSERT_SUCCESS( val.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( val.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); + ASSERT_RESULT( val.type(), expected_json_type() ); } count++; } @@ -105,34 +123,40 @@ namespace scalar_tests { { padded_string whitespace_array_json = std::string("[") + std::string(json) + " ]"; std::cout << "- JSON: " << whitespace_array_json << endl; + SUBTEST( "simdjson_result", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) { int count = 0; for (simdjson_result val_result : doc_result) { T actual; + ASSERT_RESULT( val_result.type(), expected_json_type() ); ASSERT_SUCCESS( val_result.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( val_result.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); + ASSERT_RESULT( val_result.type(), expected_json_type() ); } count++; } ASSERT_EQUAL(count, 1); return true; })); + SUBTEST( "value", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) { int count = 0; for (simdjson_result val_result : doc_result) { ondemand::value val; ASSERT_SUCCESS( val_result.get(val) ); T actual; + ASSERT_RESULT( val.type(), expected_json_type() ); ASSERT_SUCCESS( val.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); // Test it twice (scalars can be retrieved more than once) if (test_twice) { ASSERT_SUCCESS( val.get(actual) ); - ASSERT_EQUAL(expected, actual); + ASSERT_EQUAL(actual, expected); + ASSERT_RESULT( val_result.type(), expected_json_type() ); } count++; } @@ -227,7 +251,7 @@ namespace scalar_tests { SUBTEST( "value", test_ondemand_doc(array_json, [&](auto doc_result) { int count = 0; for (T actual : doc_result) { - ASSERT_EQUAL( expected, actual ); + ASSERT_EQUAL( actual, expected ); count++; } ASSERT_EQUAL(count, 1); diff --git a/tests/ondemand/test_ondemand.h b/tests/ondemand/test_ondemand.h index c00548b5..acdd325e 100644 --- a/tests/ondemand/test_ondemand.h +++ b/tests/ondemand/test_ondemand.h @@ -31,7 +31,7 @@ bool test_ondemand_doc(const simdjson::padded_string &json, const F& f) { #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) { \ return (TEST); \ })) { \ diff --git a/tests/test_macros.h b/tests/test_macros.h index 8b5ecd11..5c0394de 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -23,9 +23,9 @@ const char *SMALLDEMO_JSON = SIMDJSON_BENCHMARK_SMALLDATA_DIR "smalldemo.json"; const char *TRUENULL_JSON = SIMDJSON_BENCHMARK_SMALLDATA_DIR "truenull.json"; // For the ASSERT_EQUAL macro -template -simdjson_really_inline bool equals_expected(T actual, S expected) { - return actual == T(expected); +template +simdjson_really_inline bool equals_expected(A actual, E expected) { + return actual == A(expected); } template<> simdjson_really_inline bool equals_expected(const char *actual, const char *expected) { @@ -76,10 +76,11 @@ simdjson_really_inline bool assert_error(const T &actual_result, simdjson::error } return true; } -template +template simdjson_really_inline bool assert_result(simdjson::simdjson_result &&actual_result, const E &expected, const char *operation = "result") { E actual; - return assert_success(actual_result.get(actual), operation) && assert_equal(actual, expected, operation); + return assert_success(std::forward>(actual_result).get(actual)) + && assert_equal(actual, expected, operation); } simdjson_really_inline bool assert_true(bool value, const char *operation = "result") { 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 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_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_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);