Let us try to check with the exceptions disabled. (#707)

* Tweaking code so that we can run all tests with exceptions off.
* Removing SIMDJSON_DISABLE_EXCEPTIONS
This commit is contained in:
Daniel Lemire 2020-04-15 16:45:36 -04:00 committed by GitHub
parent efd706528b
commit 6d7c77ddc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 133 additions and 33 deletions

View File

@ -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

View File

@ -65,23 +65,30 @@ void print_vec(const std::vector<int64_t> &v) {
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
if (element.is<simdjson::dom::array>()) {
auto array = element.get<simdjson::dom::array>();
simdjson::dom::array array;
simdjson::error_code error;
element.get<simdjson::dom::array>().tie(array, error);
for (auto child : array) {
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
simdjson_recurse(v, child);
}
}
} else if (element.is<simdjson::dom::object>()) {
auto object = element.get<simdjson::dom::object>();
simdjson::dom::object object;
simdjson::error_code error;
element.get<simdjson::dom::object>().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<simdjson::dom::object>()) {
auto child_object = value.get<simdjson::dom::object>();
simdjson::dom::object child_object;
value.get<simdjson::dom::object>().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<int64_t>()) {
v.push_back(child_value.get<int64_t>());
int64_t x;
child_value.get<int64_t>().tie(x, error);
v.push_back(x);
}
}
if (child_value.is<simdjson::dom::array>() || child_value.is<simdjson::dom::object>()) {
@ -111,9 +118,13 @@ __attribute__((noinline)) std::vector<int64_t>
simdjson_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> 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);

View File

@ -54,7 +54,10 @@ really_inline void simdjson_process_atom(stat_t &s,
if (element.is<double>()) {
s.number_count++;
} else if (element.is<bool>()) {
if (element.get<bool>()) {
simdjson::error_code err;
bool v;
element.get<bool>().tie(v,err);
if (v) {
s.true_count++;
} else {
s.false_count++;

View File

@ -29,7 +29,8 @@ std::string exec(const char* cmd) {
std::string result;
std::unique_ptr<FILE, decltype(&closepipe)> 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();

View File

@ -52,7 +52,10 @@ really_inline void simdjson_process_atom(stat_t &s,
} else if(element.is<double>()) {
s.float_count++;
} else if (element.is<bool>()) {
if (element.get<bool>()) {
simdjson::error_code err;
bool v;
element.get<bool>().tie(v,err);
if (v) {
s.true_count++;
} else {
s.false_count++;

View File

@ -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:

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -3,12 +3,13 @@
#include <cstdint>
#include <string>
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;
}

View File

@ -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<<elem;
} catch (...) {
}
#endif
return 0;
}

View File

@ -41,6 +41,9 @@ option(SIMDJSON_EXCEPTIONS "Enable simdjson's exception-throwing interface" ON)
if(NOT SIMDJSON_EXCEPTIONS)
message(STATUS "simdjson exception interface turned off. Code that does not check error codes will not compile.")
target_compile_definitions(simdjson-headers INTERFACE SIMDJSON_EXCEPTIONS=0)
if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
endif(UNIX)
endif()
option(SIMDJSON_ENABLE_THREADS "Enable threaded operation" ON)

View File

@ -73,18 +73,24 @@ endfunction(add_compile_test)
# Don't add the tests if we're on VS2017 or older; they don't succeed.
if(NOT (MSVC AND MSVC_VERSION LESS 1920))
if(SIMDJSON_EXCEPTIONS)
add_compile_test(readme_examples readme_examples.cpp quicktests)
set_property(
TEST readme_examples
APPEND PROPERTY LABELS quicktests
)
endif()
add_compile_test(readme_examples_noexceptions readme_examples_noexceptions.cpp quicktests)
# Compile tests that *should fail*
add_compile_test(readme_examples_will_fail_with_exceptions_off readme_examples.cpp)
target_compile_definitions(readme_examples_will_fail_with_exceptions_off PRIVATE SIMDJSON_EXCEPTIONS=0)
set_tests_properties(readme_examples_will_fail_with_exceptions_off PROPERTIES WILL_FAIL TRUE)
set_property(
TEST readme_examples readme_examples_noexceptions readme_examples_will_fail_with_exceptions_off
TEST readme_examples_noexceptions readme_examples_will_fail_with_exceptions_off
APPEND PROPERTY LABELS quicktests
)
endif()

View File

@ -258,7 +258,14 @@ namespace document_tests {
"}"_padded;
simdjson::dom::parser parser;
std::ostringstream myStream;
#if SIMDJSON_EXCEPTIONS
myStream << parser.parse(json);
#else
simdjson::dom::element doc;
simdjson::error_code error;
parser.parse(json).tie(doc, error);
myStream << doc;
#endif
std::string newjson = myStream.str();
if(static_cast<std::string>(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<typename T>
bool test_cast(simdjson_result<dom::element> result) {
std::cout << " test_cast<" << typeid(T).name() << "> expecting success" << std::endl;
@ -1369,7 +1376,6 @@ namespace type_tests {
bool test_cast(simdjson_result<dom::element> 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<T>() == expected
T actual;
simdjson::error_code error;
@ -1428,7 +1434,6 @@ namespace type_tests {
bool test_type(simdjson_result<dom::element> 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<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
return true
&& test_type(result, dom::element_type::INT64)
&& test_cast<dom::array>(result, INCORRECT_TYPE)
@ -1589,7 +1592,6 @@ namespace type_tests {
dom::parser parser;
simdjson_result<dom::element> result = parser.parse(ALL_TYPES_JSON)[key];
return true
&& test_type(result, dom::element_type::DOUBLE)
&& test_cast<dom::array>(result, INCORRECT_TYPE)

View File

@ -96,12 +96,15 @@ void recurse(simdjson::dom::element element, stat_t &s, size_t depth) {
}
}
} else {
simdjson::error_code error;
if (element.is<double>()) {
s.float_count++;
} else if (element.is<int64_t>()) {
s.integer_count++;
} else if (element.is<bool>()) {
if (element.get<bool>()) {
bool v;
element.get<bool>().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<std::string_view>()) {
s.string_count++;
if (is_ascii(element.get<std::string_view>())) {
std::string_view v;
element.get<std::string_view>().tie(v,error);
if (is_ascii(v)) {
s.ascii_string_count++;
}
const std::string_view strval = element.get<std::string_view>();
std::string_view strval;
element.get<std::string_view>().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();
}
}
}