diff --git a/.circleci/config.yml b/.circleci/config.yml index 332f0a297..8397f6d77 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,7 +92,16 @@ jobs: executor: clang6 environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_ENABLE_THREADS=ON } steps: [ init_clang6, cmake_test ] - + gcc-avx-noexcept: + description: Build, run tests and check performance on GCC 7 and AVX 2 *without* threads + executor: gcc7 + environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_DISABLE_EXCEPTIONS=ON } + steps: [ cmake_test ] + clang-avx-noexcept: + description: Build, run tests and check performance on Clang 6 and AVX 2 with threads + executor: clang6 + environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_DISABLE_EXCEPTIONS=ON } + steps: [ init_clang6, cmake_test ] gcc9-avx: description: Build, run tests and check performance on GCC 9 and AVX 2 executor: gcc9 @@ -197,6 +206,8 @@ workflows: version: 2.1 build_and_test: jobs: + - gcc-avx-noexcept + - clang-avx-noexcept - gcc9-avx - gcc8-avx - gcc-avx diff --git a/benchmark/distinctuseridcompetition.cpp b/benchmark/distinctuseridcompetition.cpp index 54f40f51d..66cae9acd 100644 --- a/benchmark/distinctuseridcompetition.cpp +++ b/benchmark/distinctuseridcompetition.cpp @@ -65,23 +65,30 @@ void print_vec(const std::vector &v) { void simdjson_recurse(std::vector & v, simdjson::dom::element element) { if (element.is()) { - auto array = element.get(); + simdjson::dom::array array; + simdjson::error_code error; + element.get().tie(array, error); for (auto child : array) { if (child.is() || child.is()) { simdjson_recurse(v, child); } } } else if (element.is()) { - auto object = element.get(); + simdjson::dom::object object; + simdjson::error_code error; + element.get().tie(object, error); for (auto [key, value] : object) { if((key.size() == 4) && (memcmp(key.data(), "user", 4) == 0)) { // we are in an object under the key "user" if(value.is()) { - auto child_object = value.get(); + simdjson::dom::object child_object; + value.get().tie(child_object, error); for (auto [child_key, child_value] : child_object) { if((child_key.size() == 2) && (memcmp(child_key.data(), "id", 2) == 0)) { if(child_value.is()) { - v.push_back(child_value.get()); + int64_t x; + child_value.get().tie(x, error); + v.push_back(x); } } if (child_value.is() || child_value.is()) { @@ -111,9 +118,13 @@ __attribute__((noinline)) std::vector simdjson_compute_stats(const simdjson::padded_string &p) { std::vector answer; simdjson::dom::parser parser; - simdjson::dom::element doc = parser.parse(p); - simdjson_recurse(answer, doc); - remove_duplicates(answer); + simdjson::dom::element doc; + simdjson::error_code error; + parser.parse(p).tie(doc, error); + if(!error) { + simdjson_recurse(answer, doc); + remove_duplicates(answer); + } return answer; } @@ -373,7 +384,8 @@ int main(int argc, char *argv[]) { BEST_TIME("sasjon (just parse) ", sasjon_just_parse(p), false, , repeat, volume, !just_data); simdjson::dom::parser parser; - simdjson::dom::element doc = parser.parse(p); + simdjson::dom::element doc; + parser.parse(p).tie(doc, error); BEST_TIME("simdjson (just dom) ", simdjson_just_dom(doc).size(), size, , repeat, volume, !just_data); char *buffer = (char *)malloc(p.size() + 1); diff --git a/benchmark/parseandstatcompetition.cpp b/benchmark/parseandstatcompetition.cpp index 088d69781..7016d4a29 100644 --- a/benchmark/parseandstatcompetition.cpp +++ b/benchmark/parseandstatcompetition.cpp @@ -54,7 +54,10 @@ really_inline void simdjson_process_atom(stat_t &s, if (element.is()) { s.number_count++; } else if (element.is()) { - if (element.get()) { + simdjson::error_code err; + bool v; + element.get().tie(v,err); + if (v) { s.true_count++; } else { s.false_count++; diff --git a/benchmark/perfdiff.cpp b/benchmark/perfdiff.cpp index 63a010b82..f936950d7 100644 --- a/benchmark/perfdiff.cpp +++ b/benchmark/perfdiff.cpp @@ -29,7 +29,8 @@ std::string exec(const char* cmd) { std::string result; std::unique_ptr pipe(popen(cmd, "r"), closepipe); if (!pipe) { - throw std::runtime_error("popen() failed!"); + std::cerr << "popen() failed!" << std::endl; + abort(); } while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { result += buffer.data(); diff --git a/benchmark/statisticalmodel.cpp b/benchmark/statisticalmodel.cpp index 38e575ccd..c666a5d7e 100644 --- a/benchmark/statisticalmodel.cpp +++ b/benchmark/statisticalmodel.cpp @@ -52,7 +52,10 @@ really_inline void simdjson_process_atom(stat_t &s, } else if(element.is()) { s.float_count++; } else if (element.is()) { - if (element.get()) { + simdjson::error_code err; + bool v; + element.get().tie(v,err); + if (v) { s.true_count++; } else { s.false_count++; diff --git a/doc/basics.md b/doc/basics.md index 655d3cc9e..958e1dec3 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -190,6 +190,29 @@ behavior. > parser.parse(json).tie(doc, error); // <-- Assigns to doc and error just like "auto [doc, error]" > ``` + +We can write a "quick start" example where we attempt to parse a file and access some data, without triggering exceptions: + +```C++ +#include "simdjson.h" + +int main(void) { + simdjson::dom::parser parser; + simdjson::dom::element tweets; + simdjson::error_code error; + parser.load("twitter.json").tie(tweets,error); + if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; } + simdjson::dom::element res; + + tweets["search_metadata"]["count"].tie(res,error); + if(error) { + std::cerr << "could not access keys" << std::endl; + return EXIT_FAILURE; + } + std::cout << res << " results." << std::endl; +} +``` + ### Error Handling Example This is how the example in "Using the Parsed JSON" could be written using only error code checking: diff --git a/examples/quickstart/CMakeLists.txt b/examples/quickstart/CMakeLists.txt index 7b7ff855a..ced2aa31c 100644 --- a/examples/quickstart/CMakeLists.txt +++ b/examples/quickstart/CMakeLists.txt @@ -1,2 +1,6 @@ -add_executable(quickstart quickstart.cpp) -target_link_libraries(quickstart PRIVATE simdjson) +if(SIMDJSON_EXCEPTIONS) + add_executable(quickstart quickstart.cpp) + target_link_libraries(quickstart PRIVATE simdjson) +endif() +add_executable(quickstart_noexceptions quickstart_noexceptions.cpp) +target_link_libraries(quickstart_noexceptions PRIVATE simdjson) diff --git a/examples/quickstart/quickstart.cpp b/examples/quickstart/quickstart.cpp index 818a3d229..ed66159f2 100644 --- a/examples/quickstart/quickstart.cpp +++ b/examples/quickstart/quickstart.cpp @@ -1,6 +1,7 @@ #include "simdjson.h" + int main(void) { simdjson::dom::parser parser; simdjson::dom::element tweets = parser.load("twitter.json"); std::cout << tweets["search_metadata"]["count"] << " results." << std::endl; -} +} \ No newline at end of file diff --git a/examples/quickstart/quickstart_noexceptions.cpp b/examples/quickstart/quickstart_noexceptions.cpp new file mode 100644 index 000000000..b6ca8aebf --- /dev/null +++ b/examples/quickstart/quickstart_noexceptions.cpp @@ -0,0 +1,18 @@ +#include "simdjson.h" + +int main(void) { + simdjson::dom::parser parser; + simdjson::dom::element tweets; + simdjson::error_code error; + parser.load("twitter.json").tie(tweets,error); + if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; } + simdjson::dom::element res; + + tweets["search_metadata"]["count"].tie(res,error); + if(error) { + std::cerr << "could not access keys" << std::endl; + return EXIT_FAILURE; + } + std::cout << res << " results." << std::endl; +} + diff --git a/fuzz/fuzz_dump.cpp b/fuzz/fuzz_dump.cpp index b6f30992a..868a79677 100644 --- a/fuzz/fuzz_dump.cpp +++ b/fuzz/fuzz_dump.cpp @@ -9,6 +9,7 @@ // example from doc/basics.md#tree-walking-and-json-element-types static void print_json(std::ostream& os, simdjson::dom::element element) { +#if SIMDJSON_EXCEPTIONS const char endl='\n'; switch (element.type()) { case simdjson::dom::element_type::ARRAY: @@ -46,9 +47,10 @@ static void print_json(std::ostream& os, simdjson::dom::element element) { os << "null" << endl; break; } +#endif } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - +#if SIMDJSON_EXCEPTIONS try { simdjson::dom::parser pj; auto elem=pj.parse(Data, Size); @@ -58,6 +60,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { print_json(os,v); } catch (...) { } +#endif return 0; - } diff --git a/fuzz/fuzz_dump_raw_tape.cpp b/fuzz/fuzz_dump_raw_tape.cpp index 5b150a263..5de7654a9 100644 --- a/fuzz/fuzz_dump_raw_tape.cpp +++ b/fuzz/fuzz_dump_raw_tape.cpp @@ -7,7 +7,7 @@ #include "NullBuffer.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - +#if SIMDJSON_EXCEPTIONS try { simdjson::dom::parser pj; auto elem=pj.parse(Data, Size); @@ -16,5 +16,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { UNUSED auto dumpstatus=v.dump_raw_tape(os); } catch (...) { } +#endif return 0; } diff --git a/fuzz/fuzz_minify.cpp b/fuzz/fuzz_minify.cpp index 9916bd1db..19223a944 100644 --- a/fuzz/fuzz_minify.cpp +++ b/fuzz/fuzz_minify.cpp @@ -8,7 +8,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { auto end = begin + Size; std::string str(begin, end); - +#if SIMDJSON_EXCEPTIONS try { simdjson::dom::parser parser; simdjson::dom::element doc = parser.parse(str); @@ -17,5 +17,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { } catch (...) { } +#endif return 0; } diff --git a/fuzz/fuzz_parser.cpp b/fuzz/fuzz_parser.cpp index e45e08b2d..25a880d8b 100644 --- a/fuzz/fuzz_parser.cpp +++ b/fuzz/fuzz_parser.cpp @@ -3,12 +3,13 @@ #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - +#if SIMDJSON_EXCEPTIONS try { simdjson::dom::parser pj; auto result=pj.parse(Data, Size); UNUSED auto v=result.value(); }catch(...) { } +#endif return 0; } diff --git a/fuzz/fuzz_print_json.cpp b/fuzz/fuzz_print_json.cpp index 6925dd8a7..9811f1554 100644 --- a/fuzz/fuzz_print_json.cpp +++ b/fuzz/fuzz_print_json.cpp @@ -7,7 +7,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - +#if SIMDJSON_EXCEPTIONS try { simdjson::dom::parser pj; auto elem=pj.parse(Data, Size); @@ -15,5 +15,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { os<(json) != newjson) { std::cout << "serialized json differs!" << std::endl; @@ -1280,7 +1287,6 @@ namespace type_tests { // T() == expected actual = T(result); ASSERT_EQUAL(actual, expected); - actual = T(element); ASSERT_EQUAL(actual, expected); @@ -1309,6 +1315,7 @@ namespace type_tests { return true; } + template bool test_cast(simdjson_result result) { std::cout << " test_cast<" << typeid(T).name() << "> expecting success" << std::endl; @@ -1369,7 +1376,6 @@ namespace type_tests { bool test_cast(simdjson_result result, simdjson::error_code expected_error) { std::cout << " test_cast<" << typeid(T).name() << "> expecting error '" << expected_error << "'" << std::endl; dom::element element = result.first; - // get() == expected T actual; simdjson::error_code error; @@ -1428,7 +1434,6 @@ namespace type_tests { bool test_type(simdjson_result result, dom::element_type expected_type) { std::cout << " test_type() expecting " << expected_type << std::endl; dom::element element = result.first; - dom::element_type actual_type; simdjson::error_code error; result.type().tie(actual_type, error); @@ -1460,7 +1465,6 @@ namespace type_tests { std::cout << " test_is_null() expecting " << expected_is_null << std::endl; // Grab the element out and check success dom::element element = result.first; - bool actual_is_null; simdjson::error_code error; result.is_null().tie(actual_is_null, error); @@ -1549,7 +1553,6 @@ namespace type_tests { dom::parser parser; simdjson_result result = parser.parse(ALL_TYPES_JSON)[key]; - return true && test_type(result, dom::element_type::INT64) && test_cast(result, INCORRECT_TYPE) @@ -1589,7 +1592,6 @@ namespace type_tests { dom::parser parser; simdjson_result result = parser.parse(ALL_TYPES_JSON)[key]; - return true && test_type(result, dom::element_type::DOUBLE) && test_cast(result, INCORRECT_TYPE) diff --git a/tools/jsonstats.cpp b/tools/jsonstats.cpp index c136ec1b7..4486c35ae 100644 --- a/tools/jsonstats.cpp +++ b/tools/jsonstats.cpp @@ -96,12 +96,15 @@ void recurse(simdjson::dom::element element, stat_t &s, size_t depth) { } } } else { + simdjson::error_code error; if (element.is()) { s.float_count++; } else if (element.is()) { s.integer_count++; } else if (element.is()) { - if (element.get()) { + bool v; + element.get().tie(v,error); + if (v) { s.true_count++; } else { s.false_count++; @@ -110,15 +113,19 @@ void recurse(simdjson::dom::element element, stat_t &s, size_t depth) { s.null_count++; } else if (element.is()) { s.string_count++; - if (is_ascii(element.get())) { + std::string_view v; + element.get().tie(v,error); + if (is_ascii(v)) { s.ascii_string_count++; } - const std::string_view strval = element.get(); + std::string_view strval; + element.get().tie(strval, error); if (strval.size() > s.string_maximum_length) { s.string_maximum_length = strval.size(); } } else { - throw std::runtime_error("unrecognized node."); + std::cerr << "unrecognized node." << std::endl; + abort(); } } }