From 16e8db1f17cec85dfb2b21dc6b7580c27d050321 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 6 Jun 2021 17:56:00 -0400 Subject: [PATCH] Adding 'count_elements' method. (#1577) * Adding 'count_elements' method. * Actually reporting errors. * removing white space. * Removing white space again. * Adding an extra example. * Prettier. * Making the functionality more error-proof. * Avoiding exceptions. * Various fixes including extending count_elements to value types. * Various fixes. * Minor fixes. * Correcting comment. * Trimming white spaces. --- doc/basics.md | 26 +++++ include/simdjson/error.h | 1 - include/simdjson/generic/ondemand/array-inl.h | 26 ++++- include/simdjson/generic/ondemand/array.h | 14 ++- .../simdjson/generic/ondemand/document-inl.h | 12 ++- include/simdjson/generic/ondemand/document.h | 15 ++- .../simdjson/generic/ondemand/json_iterator.h | 6 +- include/simdjson/generic/ondemand/value-inl.h | 15 ++- include/simdjson/generic/ondemand/value.h | 15 ++- .../generic/ondemand/value_iterator-inl.h | 11 +++ .../generic/ondemand/value_iterator.h | 12 ++- .../compilation_failure_tests/CMakeLists.txt | 1 + .../bad_array_count.cpp | 21 ++++ tests/ondemand/ondemand_array_tests.cpp | 96 +++++++++++++++++++ tests/ondemand/ondemand_readme_examples.cpp | 86 +++++++++++++++++ tests/test_macros.h | 4 +- 16 files changed, 343 insertions(+), 18 deletions(-) create mode 100644 tests/compilation_failure_tests/bad_array_count.cpp diff --git a/doc/basics.md b/doc/basics.md index a90923255..d47310f1d 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -311,7 +311,33 @@ cout << doc["str"]["123"]["abc"].get_double() << endl; // Prints 3.14 cout << value << endl; // Prints 3.14 ``` +Sometimes it is useful to scan an array to determine its length prior to parsing it. +For this purpose, `array` instances have a `count_elements` method. Users should be +aware that the `count_elements` method can be costly since it requires scanning the +whole array. You may use it as follows if your document is itself an array: +```C++ + auto cars_json = R"( [ 40.1, 39.9, 37.7, 40.4 ] )"_padded; + auto doc = parser.iterate(cars_json); + size_t count = doc.count_elements(); + std::vector values(count); + size_t index = 0; + for(double x : doc) { values[index++] = x; } +``` + +If you access an array inside a document, you can use the `count_elements` method as follow. +You should not let the array instance go out of scope before consuming it after calling the `count_elements` method: +``` C++ + ondemand::parser parser; + auto cars_json = R"( { "test":[ { "val1":1, "val2":2 }, { "val1":1, "val2":2 } ] } )"_padded; + auto doc = parser.iterate(cars_json); + auto test_array = doc.find_field("test").get_array(); + size_t count = test_array.count_elements(); + std::cout << "Number of elements: " << count << std::endl; + for(ondemand::object elem: test_array) { + std::cout << simdjson::to_string(elem); + } +``` Tree Walking and JSON Element Types: Sometimes you don't necessarily have a document with a known type, and are trying to generically inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON: diff --git a/include/simdjson/error.h b/include/simdjson/error.h index 1d09be804..d2583cd7b 100644 --- a/include/simdjson/error.h +++ b/include/simdjson/error.h @@ -287,7 +287,6 @@ struct simdjson_result : public internal::simdjson_result_base { template inline std::ostream& operator<<(std::ostream& out, simdjson_result value) noexcept { return out << value.value(); } - #endif // SIMDJSON_EXCEPTIONS #ifndef SIMDJSON_DISABLE_DEPRECATED_API diff --git a/include/simdjson/generic/ondemand/array-inl.h b/include/simdjson/generic/ondemand/array-inl.h index 4b78851d7..c0fc42ce6 100644 --- a/include/simdjson/generic/ondemand/array-inl.h +++ b/include/simdjson/generic/ondemand/array-inl.h @@ -72,6 +72,27 @@ simdjson_really_inline simdjson_result array::end() noexcept { return array_iterator(iter); } +simdjson_really_inline simdjson_result array::count_elements() & noexcept { + // The count_elements method should always be called before you have begun + // iterating through the array. + // To open a new array you need to be at a `[`. +#ifdef SIMDJSON_DEVELOPMENT_CHECKS + // array::begin() makes the same check. + if(!iter.is_at_container_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + // Otherwise, we need to iterate through the array. + iter.enter_at_container_start(); // sets the depth to indicate that we are inside the container and accesses the first element + size_t count{0}; + // Important: we do not consume any of the values. + for(simdjson_unused auto v : *this) { count++; } + // The above loop will always succeed, but we want to report errors. + if(iter.error()) { return iter.error(); } + // We need to move back at the start because we expect users to iterator through + // the array after counting the number of elements. + iter.enter_at_container_start(); // sets the depth to indicate that we are inside the container and accesses the first element + return count; +} + } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson @@ -101,5 +122,8 @@ simdjson_really_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/array.h b/include/simdjson/generic/ondemand/array.h index 656c321e9..3336ccfd3 100644 --- a/include/simdjson/generic/ondemand/array.h +++ b/include/simdjson/generic/ondemand/array.h @@ -31,7 +31,18 @@ public: * Part of the std::iterable interface. */ simdjson_really_inline simdjson_result end() noexcept; - + /** + * This method scans the array and counts the number of elements. + * The count_elements method should always be called before you have begun + * iterating through the array: it is expected that you are pointing at + * the beginning of the array. + * The runtime complexity is linear in the size of the array. After + * calling this function, if successful, the array is 'rewinded' at its + * beginning as if it had never been accessed. If the JSON is malformed (e.g., + * there is a missing comma), then an error is returned and it is no longer + * safe to continue. + */ + simdjson_really_inline simdjson_result count_elements() & noexcept; protected: /** * Begin array iteration. @@ -98,6 +109,7 @@ public: simdjson_really_inline simdjson_result begin() noexcept; simdjson_really_inline simdjson_result end() noexcept; + simdjson_really_inline simdjson_result count_elements() & noexcept; }; } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index b0086c66a..a5c0877b7 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -92,7 +92,13 @@ simdjson_really_inline document::operator std::string_view() noexcept(false) { r simdjson_really_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } simdjson_really_inline document::operator bool() noexcept(false) { return get_bool(); } #endif - +simdjson_really_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { iter._depth -= 1 ; /* undoing the increment so we go back at the doc depth.*/ } + return answer; +} simdjson_really_inline simdjson_result document::begin() & noexcept { return get_array().begin(); } @@ -150,6 +156,10 @@ simdjson_really_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} simdjson_really_inline error_code simdjson_result::rewind() noexcept { if (error()) { return error(); } first.rewind(); diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index b3dd052d0..581d8a7ce 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -198,7 +198,18 @@ public: */ simdjson_really_inline operator bool() noexcept(false); #endif - + /** + * This method scans the array and counts the number of elements. + * The count_elements method should always be called before you have begun + * iterating through the array: it is expected that you are pointing at + * the beginning of the array. + * The runtime complexity is linear in the size of the array. After + * calling this function, if successful, the array is 'rewinded' at its + * beginning as if it had never been accessed. If the JSON is malformed (e.g., + * there is a missing comma), then an error is returned and it is no longer + * safe to continue. + */ + simdjson_really_inline simdjson_result count_elements() & noexcept; /** * Begin array iteration. * @@ -371,7 +382,7 @@ public: simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); simdjson_really_inline operator bool() noexcept(false); #endif - + simdjson_really_inline simdjson_result count_elements() & noexcept; simdjson_really_inline simdjson_result begin() & noexcept; simdjson_really_inline simdjson_result end() & noexcept; simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; diff --git a/include/simdjson/generic/ondemand/json_iterator.h b/include/simdjson/generic/ondemand/json_iterator.h index 6d5401fbd..16d9e5b13 100644 --- a/include/simdjson/generic/ondemand/json_iterator.h +++ b/include/simdjson/generic/ondemand/json_iterator.h @@ -51,7 +51,6 @@ public: simdjson_really_inline json_iterator &operator=(json_iterator &&other) noexcept; simdjson_really_inline explicit json_iterator(const json_iterator &other) noexcept = default; simdjson_really_inline json_iterator &operator=(const json_iterator &other) noexcept = default; - /** * Skips a JSON value, whether it is a scalar, array or object. */ @@ -194,14 +193,13 @@ public: simdjson_really_inline token_position start_position(depth_t depth) const noexcept; simdjson_really_inline void set_start_position(depth_t depth, token_position position) noexcept; #endif + /* Useful for debugging and logging purposes. */ + inline std::string to_string() const noexcept; /** * Updates this json iterator so that it is back at the beginning of the document, * as if it had just been created. */ inline void rewind() noexcept; - /* Useful for debugging and logging purposes. */ - inline std::string to_string() const noexcept; - protected: simdjson_really_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept; simdjson_really_inline token_position last_document_position() const noexcept; diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h index 59f3a1781..91ed6bac9 100644 --- a/include/simdjson/generic/ondemand/value-inl.h +++ b/include/simdjson/generic/ondemand/value-inl.h @@ -95,6 +95,16 @@ simdjson_really_inline simdjson_result value::begin() & noexcept simdjson_really_inline simdjson_result value::end() & noexcept { return {}; } +simdjson_really_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; + auto a = get_array(); + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); + return answer; +} simdjson_really_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); @@ -145,7 +155,10 @@ simdjson_really_inline simdjson_result implementation_simdjson_result_base(error) { } - +simdjson_really_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} simdjson_really_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index 03c138555..a8a5f48d6 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -208,7 +208,18 @@ public: * Part of the std::iterable interface. */ simdjson_really_inline simdjson_result end() & noexcept; - + /** + * This method scans the array and counts the number of elements. + * The count_elements method should always be called before you have begun + * iterating through the array: it is expected that you are pointing at + * the beginning of the array. + * The runtime complexity is linear in the size of the array. After + * calling this function, if successful, the array is 'rewinded' at its + * beginning as if it had never been accessed. If the JSON is malformed (e.g., + * there is a missing comma), then an error is returned and it is no longer + * safe to continue. + */ + simdjson_really_inline simdjson_result count_elements() & noexcept; /** * Look up a field by name on an object (order-sensitive). * @@ -382,7 +393,7 @@ public: simdjson_really_inline operator SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); simdjson_really_inline operator bool() noexcept(false); #endif - + simdjson_really_inline simdjson_result count_elements() & noexcept; simdjson_really_inline simdjson_result begin() & noexcept; simdjson_really_inline simdjson_result end() & noexcept; diff --git a/include/simdjson/generic/ondemand/value_iterator-inl.h b/include/simdjson/generic/ondemand/value_iterator-inl.h index 614a35141..c8ca15874 100644 --- a/include/simdjson/generic/ondemand/value_iterator-inl.h +++ b/include/simdjson/generic/ondemand/value_iterator-inl.h @@ -631,6 +631,17 @@ inline void value_iterator::assert_at_next() const noexcept { SIMDJSON_ASSUME( _depth > 0 ); } + +simdjson_really_inline void value_iterator::enter_at_container_start() noexcept { + _json_iter->_depth = _depth + 1; + _json_iter->token.index = _start_position + 1; +} + +simdjson_really_inline void value_iterator::move_at_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.index = _start_position; +} + inline void value_iterator::assert_at_child() const noexcept { SIMDJSON_ASSUME( _json_iter->token.index > _start_position ); SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); diff --git a/include/simdjson/generic/ondemand/value_iterator.h b/include/simdjson/generic/ondemand/value_iterator.h index 3980b7490..61494be61 100644 --- a/include/simdjson/generic/ondemand/value_iterator.h +++ b/include/simdjson/generic/ondemand/value_iterator.h @@ -280,11 +280,17 @@ public: simdjson_really_inline bool is_valid() const noexcept; /** @} */ - protected: - + /* updates the index so that at_start() is true and syncs the depth. */ + simdjson_really_inline void move_at_start() noexcept; + /** + * enter_at_container_start is similar to move_at_start() + * except that it sets the depth to indicate that we are inside the + * container and then it accesses the first element + **/ + simdjson_really_inline void enter_at_container_start() noexcept; /* Useful for debugging and logging purposes. */ - inline std::string to_string() const noexcept; + inline std::string to_string() const noexcept; simdjson_really_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept; simdjson_really_inline bool parse_null(const uint8_t *json) const noexcept; diff --git a/tests/compilation_failure_tests/CMakeLists.txt b/tests/compilation_failure_tests/CMakeLists.txt index 713f2fd11..d42da00b8 100644 --- a/tests/compilation_failure_tests/CMakeLists.txt +++ b/tests/compilation_failure_tests/CMakeLists.txt @@ -17,6 +17,7 @@ endfunction(add_dual_compile_test) add_dual_compile_test(example_compiletest) # These don't compile with exceptions off if (SIMDJSON_EXCEPTIONS) + add_dual_compile_test(bad_array_count) add_dual_compile_test(dangling_parser_load) add_dual_compile_test(dangling_parser_parse_uint8) add_dual_compile_test(dangling_parser_parse_uchar) diff --git a/tests/compilation_failure_tests/bad_array_count.cpp b/tests/compilation_failure_tests/bad_array_count.cpp new file mode 100644 index 000000000..b17286dbe --- /dev/null +++ b/tests/compilation_failure_tests/bad_array_count.cpp @@ -0,0 +1,21 @@ +#include "simdjson.h" +#include + +using namespace simdjson; + +int main(void) { + ondemand::parser parser; + auto cars_json = R"( { "test":[ { "val1":1, "val2":2 }, { "val1":1, "val2":2 } ] } )"_padded; + auto doc = parser.iterate(cars_json); + auto testArray = doc.find_field("test"); +#if COMPILATION_TEST_USE_FAILING_CODE + size_t count = testArray.get_array().count_elements(); + std::cout << "Number of elements: " << count << std::endl; + for(simdjson_unused ondemand::object elem : testArray) {} +#else + ondemand::array a = testArray.get_array(); + size_t count = a.count_elements(); + std::cout << "Number of elements: " << count << std::endl; + for(simdjson_unused ondemand::object elem : a) {} +#endif +} \ No newline at end of file diff --git a/tests/ondemand/ondemand_array_tests.cpp b/tests/ondemand/ondemand_array_tests.cpp index 4939f31d0..cba64a302 100644 --- a/tests/ondemand/ondemand_array_tests.cpp +++ b/tests/ondemand/ondemand_array_tests.cpp @@ -136,6 +136,98 @@ namespace array_tests { TEST_SUCCEED(); } + bool iterate_complex_array_count() { + TEST_START(); + ondemand::parser parser; + auto cars_json = R"( { "test":[ { "val1":1, "val2":2 }, { "val1":1, "val2":2 } ] } )"_padded; + ondemand::document doc; + ASSERT_SUCCESS(parser.iterate(cars_json).get(doc)); + ondemand::array myarray; + ASSERT_SUCCESS(doc.find_field("test").get_array().get(myarray)); + size_t count; + ASSERT_SUCCESS(myarray.count_elements().get(count)); + size_t new_count = 0; + for(simdjson_unused auto elem: myarray) { new_count++; } + ASSERT_EQUAL(count, new_count); + TEST_SUCCEED(); + } + + bool iterate_sub_array_count() { + TEST_START(); + ondemand::parser parser; + auto key_value_json = R"( { "test":[ 1,2,3], "joe": [1,2] } )"_padded; + ondemand::document doc; + ASSERT_SUCCESS(parser.iterate(key_value_json).get(doc)); + ondemand::object obj; + ASSERT_SUCCESS(doc.get_object().get(obj)); + ondemand::value v; + ASSERT_SUCCESS(doc.find_field("test").get(v)); + size_t count; + ASSERT_SUCCESS(v.count_elements().get(count)); + ASSERT_EQUAL(count, 3); + ASSERT_SUCCESS(doc.find_field("joe").get(v)); + ASSERT_SUCCESS(v.count_elements().get(count)); + ASSERT_EQUAL(count, 2); + TEST_SUCCEED(); + } + + bool iterate_array_count() { + TEST_START(); + const auto json = R"([ 1, 10, 100 ])"_padded; + const auto badjson = R"([ 1, 10 100 ])"_padded; + const vector expected_value = { 1, 10, 100 }; + + SUBTEST("ondemand::count_elements", 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 count; + ASSERT_SUCCESS( array.count_elements().get(count) ); + ASSERT_EQUAL(count, expected_value.size()); + return true; + })); + SUBTEST("ondemand::count_elements_and_decode", 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 count; + ASSERT_SUCCESS( array.count_elements().get(count) ); + ASSERT_EQUAL(count, expected_value.size()); + size_t i = 0; + std::vector receiver(count); + for (auto value : array) { + uint64_t actual; + ASSERT_SUCCESS( value.get(actual) ); + ASSERT_EQUAL(actual, expected_value[i]); + receiver[i] = actual; + i++; + } + return true; + })); + TEST_SUCCEED(); + } + + bool iterate_bad_array_count() { + TEST_START(); + const auto badjson = R"([ 1, 10 100 ])"_padded; + + + SUBTEST("ondemand::count_elements", test_ondemand_doc(badjson, [&](auto doc_result) { + ondemand::array array; + ASSERT_RESULT( doc_result.type(), json_type::array ); + ASSERT_SUCCESS( doc_result.get(array) ); + size_t count; + auto e = array.count_elements().get(count); + if( e != TAPE_ERROR) { + std::cout << e << "\n"; + std::cout << "expected: " << TAPE_ERROR << "\n"; + std::cout << "count = " << count << "\n"; + return false; + } + return true; + })); + TEST_SUCCEED(); + } bool iterate_document_array() { TEST_START(); const auto json = R"([ 1, 10, 100 ])"_padded; @@ -513,6 +605,10 @@ namespace array_tests { bool run() { return + iterate_sub_array_count() && + iterate_complex_array_count() && + iterate_bad_array_count() && + iterate_array_count() && issue1588() && iterate_array() && iterate_document_array() && diff --git a/tests/ondemand/ondemand_readme_examples.cpp b/tests/ondemand/ondemand_readme_examples.cpp index f152b4fe8..f4497aeb9 100644 --- a/tests/ondemand/ondemand_readme_examples.cpp +++ b/tests/ondemand/ondemand_readme_examples.cpp @@ -44,6 +44,88 @@ bool basics_3() { TEST_SUCCEED(); } + +bool json_array_with_array_count() { + TEST_START(); + ondemand::parser parser; + auto cars_json = R"( [ 40.1, 39.9, 37.7, 40.4 ] )"_padded; + auto doc = parser.iterate(cars_json); + auto arr = doc.get_array(); + size_t count = arr.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + // We deliberately do it twice: + count = arr.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + // Next, we check whether we can iterate normally: + std::vector values(count); + size_t index = 0; + for(double x : arr) { values[index++] = x; } + ASSERT_EQUAL(index, count); + TEST_SUCCEED(); +} + +bool json_value_with_array_count() { + TEST_START(); + ondemand::parser parser; + auto cars_json = R"( {"array":[ 40.1, 39.9, 37.7, 40.4 ]} )"_padded; + auto doc = parser.iterate(cars_json); + auto val = doc["array"]; + size_t count = val.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + // We deliberately do it twice: + count = val.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + std::vector values(count); + // Next, we check whether we can iterate normally: + size_t index = 0; + for(double x : val) { values[index++] = x; } + ASSERT_EQUAL(index, count); + TEST_SUCCEED(); +} + + +bool json_array_count() { + TEST_START(); + ondemand::parser parser; + auto cars_json = R"( [ 40.1, 39.9, 37.7, 40.4 ] )"_padded; + auto doc = parser.iterate(cars_json); + size_t count = doc.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + // We deliberately do it twice: + count = doc.count_elements(); + ASSERT_EQUAL(4, count); + std::cout << count << std::endl; + std::vector values(count); + size_t index = 0; + for(double x : doc) { values[index++] = x; } + ASSERT_EQUAL(index, count); + TEST_SUCCEED(); +} + +bool json_array_count_complex() { + TEST_START(); + ondemand::parser parser; + auto cars_json = R"( { "test":[ { "val1":1, "val2":2 }, { "val1":1, "val2":2 }, { "val1":1, "val2":2 } ] } )"_padded; + auto doc = parser.iterate(cars_json); + auto test_array = doc.find_field("test").get_array(); + size_t count = test_array.count_elements(); + std::cout << "Number of elements: " << count << std::endl; + size_t c = 0; + for(ondemand::object elem : test_array) { + std::cout << simdjson::to_string(elem); + c++; + } + std::cout << std::endl; + ASSERT_EQUAL(c, count); + TEST_SUCCEED(); + +} + bool using_the_parsed_json_1() { TEST_START(); @@ -215,6 +297,10 @@ int main() { true #if SIMDJSON_EXCEPTIONS // && basics_1() // Fails because twitter.json isn't in current directory. Compile test only. + && json_value_with_array_count() + && json_array_with_array_count() + && json_array_count_complex() + && json_array_count() && using_the_parsed_json_rewind() && basics_2() && using_the_parsed_json_1() diff --git a/tests/test_macros.h b/tests/test_macros.h index 5c0394dec..29a500ccc 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -100,8 +100,8 @@ simdjson_really_inline bool assert_iterate_error(T &arr, simdjson::error_code ex } return assert_equal( count, 1, operation ); } -#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 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_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0); #define ASSERT_SUCCESS(ACTUAL) do { if (!::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0);