From 1d97749791b507cd2908bc2762a6d0d3103a0259 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 28 Jan 2024 10:47:21 -0500 Subject: [PATCH 1/4] Verify and fix issue 235 --- include/fast_float/ascii_number.h | 7 +- include/fast_float/parse_number.h | 3 +- tests/CMakeLists.txt | 2 +- tests/fast_int.cpp | 252 +++++++++++++++--------------- 4 files changed, 128 insertions(+), 136 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 5d3eac9d..219c7d3c 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -442,8 +442,7 @@ parsed_number_string_t parse_number_string(UC const *p, UC const * pend, par template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 -from_chars_result_t parse_int_string(UC const* p, UC const* pend, T& value, int base) -{ +from_chars_result_t parse_int_string(UC const* p, UC const* pend, T& value, int base) { from_chars_result_t answer; UC const* const first = p; @@ -463,9 +462,11 @@ from_chars_result_t parse_int_string(UC const* p, UC const* pend, T& value, } UC const* const start_num = p; - while (*p == UC('0')) { + + while (p!= pend && *p == UC('0')) { ++p; } + const bool has_leading_zeros = p > start_num; UC const* const start_digits = p; diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 1fd587bb..de70898b 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -232,8 +232,7 @@ from_chars_result_t from_chars_advanced(UC const * first, UC const * last, template FASTFLOAT_CONSTEXPR20 -from_chars_result_t from_chars(UC const* first, UC const* last, T& value, int base) noexcept -{ +from_chars_result_t from_chars(UC const* first, UC const* last, T& value, int base) noexcept { static_assert (is_supported_char_type(), "only char, wchar_t, char16_t and char32_t are supported"); from_chars_result_t answer; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2cf649c5..26e4368e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ option(SYSTEM_DOCTEST "Use system copy of doctest" OFF) if (NOT SYSTEM_DOCTEST) FetchContent_Declare(doctest GIT_REPOSITORY https://github.com/onqtam/doctest.git - GIT_TAG v2.4.10) + GIT_TAG v2.4.11) endif() FetchContent_Declare(supplemental_test_files GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git diff --git a/tests/fast_int.cpp b/tests/fast_int.cpp index 85dbca88..0783371a 100644 --- a/tests/fast_int.cpp +++ b/tests/fast_int.cpp @@ -1,11 +1,16 @@ #include #include #include -#include +#include #include #include "fast_float/fast_float.h" #include +template +std::string quoted(T& s) { + return "\""+std::string(s)+"\""; +} + /* all tests conducted are to check fast_float::from_chars functionality with int and unsigned test cases include: @@ -26,414 +31,391 @@ within range base tests - max/min numbers are still within int/unsigned bit size leading zeros tests - ignores all zeroes in front of valid number after converted from base */ -int main() -{ - +int main() { // int basic test const std::vector int_basic_test_expected { 0, 10, -40, 1001, 9 }; - const std::vector int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" }; + const std::vector int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" }; - for (std::size_t i = 0; i < int_basic_test.size(); ++i) - { + for (std::size_t i = 0; i < int_basic_test.size(); ++i) { const auto& f = int_basic_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { if (answer.ec == std::errc::invalid_argument) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << " because of invalid arguement" << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << " because of invalid arguement" << std::endl; } else if (answer.ec == std::errc::result_out_of_range) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << " because it's out of range" << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << " because it's out of range" << std::endl; } else { - std::cerr << "could not convert to int for input: " << std::quoted(f) << " because of an unknown error" << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << " because of an unknown error" << std::endl; } return EXIT_FAILURE; } else if (result != int_basic_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_basic_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected int: " << int_basic_test_expected[i] << std::endl; return EXIT_FAILURE; } } // unsigned basic test const std::vector unsigned_basic_test_expected { 0, 10, 1001, 9 }; - const std::vector unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" }; + const std::vector unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" }; - for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) { const auto& f = unsigned_basic_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { - std::cerr << "could not convert to unsigned for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to unsigned for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != unsigned_basic_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl; return EXIT_FAILURE; } } // int invalid error test - const std::vector int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" }; + const std::vector int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" }; - for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) - { + for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) { const auto& f = int_invalid_argument_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned invalid error test - const std::vector unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" }; + const std::vector unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" }; - for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) { const auto& f = unsigned_invalid_argument_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int out of range error test #1 (8 bit) - const std::vector int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"}; + const std::vector int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"}; - for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) - { + for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) { const auto& f = int_out_of_range_test_1[i]; int8_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int out of range error test #2 (16 bit) - const std::vector int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"}; + const std::vector int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"}; - for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) - { + for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) { const auto& f = int_out_of_range_test_2[i]; int16_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int out of range error test #3 (32 bit) - const std::vector int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"}; + const std::vector int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"}; - for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) - { + for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) { const auto& f = int_out_of_range_test_3[i]; int32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int out of range error test #4 (64 bit) - const std::vector int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; + const std::vector int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; - for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) - { + for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) { const auto& f = int_out_of_range_test_4[i]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #1 (8 bit) - const std::vector unsigned_out_of_range_test_1{ "2000000000000000000000", "256" }; + const std::vector unsigned_out_of_range_test_1{ "2000000000000000000000", "256" }; - for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) { const auto& f = unsigned_out_of_range_test_1[i]; uint8_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #2 (16 bit) - const std::vector unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" }; + const std::vector unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" }; - for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) { const auto& f = unsigned_out_of_range_test_2[i]; uint16_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #3 (32 bit) - const std::vector unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" }; + const std::vector unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" }; - for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) { const auto& f = unsigned_out_of_range_test_3[i]; uint32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #4 (64 bit) - const std::vector unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" }; + const std::vector unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" }; - for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) { const auto& f = unsigned_out_of_range_test_4[i]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int pointer test #1 (only numbers) - const std::vector int_pointer_test_1 { "0", "010", "-40" }; + const std::vector int_pointer_test_1 { "0", "010", "-40" }; - for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) - { + for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) { const auto& f = int_pointer_test_1[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (strcmp(answer.ptr, "") != 0) { - std::cerr << "ptr of result " << std::quoted(f) << " did not match with expected ptr: " << std::quoted("") << std::endl; + std::cerr << "ptr of result " << quoted(f) << " did not match with expected ptr: " << quoted("") << std::endl; return EXIT_FAILURE; } } // int pointer test #2 (string behind numbers) - const std::string int_pointer_test_2 = "1001 with text"; + const std::string_view int_pointer_test_2 = "1001 with text"; const auto& f2 = int_pointer_test_2; int result2; auto answer2 = fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); if (strcmp(answer2.ptr, " with text") != 0) { - std::cerr << "ptr of result " << std::quoted(f2) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; + std::cerr << "ptr of result " << quoted(f2) << " did not match with expected ptr: " << quoted(" with text") << std::endl; return EXIT_FAILURE; } // int pointer test #3 (string with newline behind numbers) - const std::string int_pointer_test_3 = "1001 with text\n"; + const std::string_view int_pointer_test_3 = "1001 with text\n"; const auto& f3 = int_pointer_test_3; int result3; auto answer3 = fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); if (strcmp(answer3.ptr, " with text\n") != 0) { - std::cerr << "ptr of result " << std::quoted(f3) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; + std::cerr << "ptr of result " << quoted(f3) << " did not match with expected ptr: " << quoted(" with text") << std::endl; return EXIT_FAILURE; } // int pointer test #4 (float) - const std::string int_pointer_test_4 = "9.999"; + const std::string_view int_pointer_test_4 = "9.999"; const auto& f4 = int_pointer_test_4; int result4; auto answer4 = fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); if (strcmp(answer4.ptr, ".999") != 0) { - std::cerr << "ptr of result " << std::quoted(f4) << " did not match with expected ptr: " << std::quoted(".999") << std::endl; + std::cerr << "ptr of result " << quoted(f4) << " did not match with expected ptr: " << quoted(".999") << std::endl; return EXIT_FAILURE; } // int pointer test #5 (invalid int) - const std::string int_pointer_test_5 = "+50"; + const std::string_view int_pointer_test_5 = "+50"; const auto& f5 = int_pointer_test_5; int result5; auto answer5 = fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); if (strcmp(answer5.ptr, "+50") != 0) { - std::cerr << "ptr of result " << std::quoted(f5) << " did not match with expected ptr: " << std::quoted("+50") << std::endl; + std::cerr << "ptr of result " << quoted(f5) << " did not match with expected ptr: " << quoted("+50") << std::endl; return EXIT_FAILURE; } // unsigned pointer test #2 (string behind numbers) - const std::string unsigned_pointer_test_1 = "1001 with text"; + const std::string_view unsigned_pointer_test_1 = "1001 with text"; const auto& f6 = unsigned_pointer_test_1; unsigned result6; auto answer6 = fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); if (strcmp(answer6.ptr, " with text") != 0) { - std::cerr << "ptr of result " << std::quoted(f6) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; + std::cerr << "ptr of result " << quoted(f6) << " did not match with expected ptr: " << quoted(" with text") << std::endl; return EXIT_FAILURE; } // unsigned pointer test #2 (invalid unsigned) - const std::string unsigned_pointer_test_2 = "-50"; + const std::string_view unsigned_pointer_test_2 = "-50"; const auto& f7 = unsigned_pointer_test_2; unsigned result7; auto answer7 = fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); if (strcmp(answer7.ptr, "-50") != 0) { - std::cerr << "ptr of result " << std::quoted(f7) << " did not match with expected ptr: " << std::quoted("-50") << std::endl; + std::cerr << "ptr of result " << quoted(f7) << " did not match with expected ptr: " << quoted("-50") << std::endl; return EXIT_FAILURE; } // int base 2 test const std::vector int_base_2_test_expected { 0, 1, 4, 2, -1 }; - const std::vector int_base_2_test { "0", "1", "100", "010", "-1" }; + const std::vector int_base_2_test { "0", "1", "100", "010", "-1" }; - for (std::size_t i = 0; i < int_base_2_test.size(); ++i) - { + for (std::size_t i = 0; i < int_base_2_test.size(); ++i) { const auto& f = int_base_2_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc()) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != int_base_2_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl; return EXIT_FAILURE; } } // unsigned base 2 test const std::vector unsigned_base_2_test_expected { 0, 1, 4, 2 }; - const std::vector unsigned_base_2_test { "0", "1", "100", "010" }; + const std::vector unsigned_base_2_test { "0", "1", "100", "010" }; - for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) { const auto& f = unsigned_base_2_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc()) { - std::cerr << "could not convert to unsigned for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to unsigned for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != unsigned_base_2_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl; return EXIT_FAILURE; } } // int invalid error base 2 test - const std::vector int_invalid_argument_base_2_test{ "2", "A", "-2" }; + const std::vector int_invalid_argument_base_2_test{ "2", "A", "-2" }; - for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) - { + for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) { const auto& f = int_invalid_argument_base_2_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned invalid error base 2 test - const std::vector unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2" }; + const std::vector unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2" }; - for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) { const auto& f = unsigned_invalid_argument_base_2_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // octal test const std::vector base_octal_test_expected {0, 1, 7, 8, 9}; - const std::vector base_octal_test { "0", "1", "07", "010", "0011" }; + const std::vector base_octal_test { "0", "1", "07", "010", "0011" }; - for (std::size_t i = 0; i < base_octal_test.size(); ++i) - { + for (std::size_t i = 0; i < base_octal_test.size(); ++i) { const auto& f = base_octal_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); if (answer.ec != std::errc()) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != base_octal_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_octal_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected int: " << base_octal_test_expected[i] << std::endl; return EXIT_FAILURE; } } // hex test const std::vector base_hex_test_expected { 0, 1, 15, 31, 0, 16}; - const std::vector base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" }; + const std::vector base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" }; - for (std::size_t i = 0; i < base_hex_test.size(); ++i) - { + for (std::size_t i = 0; i < base_hex_test.size(); ++i) { const auto& f = base_hex_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); if (answer.ec != std::errc()) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != base_hex_test_expected[i]) { - std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_hex_test_expected[i] << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected int: " << base_hex_test_expected[i] << std::endl; return EXIT_FAILURE; } } // invalid base test #1 (-1) - const std::vector invalid_base_test_1 { "0", "1", "-1", "F", "10Z" }; + const std::vector invalid_base_test_1 { "0", "1", "-1", "F", "10Z" }; - for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) - { + for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) { const auto& f = invalid_base_test_1[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // invalid base test #2 (37) - const std::vector invalid_base_test_2 { "0", "1", "F", "Z", "10Z" }; + const std::vector invalid_base_test_2 { "0", "1", "F", "Z", "10Z" }; - for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) - { + for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) { const auto& f = invalid_base_test_2[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); if (answer.ec != std::errc::invalid_argument) { - std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; + std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl; return EXIT_FAILURE; } } // int out of range error base test (64 bit) - const std::vector int_out_of_range_base_test { "1000000000000000000000000000000000000000000000000000000000000000", + const std::vector int_out_of_range_base_test { "1000000000000000000000000000000000000000000000000000000000000000", "-1000000000000000000000000000000000000000000000000000000000000001", "2021110011022210012102010021220101220222", "-2021110011022210012102010021220101221000", @@ -504,19 +486,18 @@ int main() "1Y2P0IJ32E8E8", "-1Y2P0IJ32E8E9" }; - for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) - { + for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) { const auto& f = int_out_of_range_base_test[i]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } } // unsigned out of range error base test (64 bit) - const std::vector unsigned_out_of_range_base_test { "10000000000000000000000000000000000000000000000000000000000000000", + const std::vector unsigned_out_of_range_base_test { "10000000000000000000000000000000000000000000000000000000000000000", "11112220022122120101211020120210210211221", "100000000000000000000000000000000", "2214220303114400424121122431", @@ -552,20 +533,19 @@ int main() "5G24A25TWKWFG", "3W5E11264SGSG" }; int base_unsigned = 2; - for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) { const auto& f = unsigned_out_of_range_base_test[i]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned); if (answer.ec != std::errc::result_out_of_range) { - std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; + std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl; return EXIT_FAILURE; } ++base_unsigned; } // just within range base test (64 bit) - const std::vector int_within_range_base_test { "111111111111111111111111111111111111111111111111111111111111111", + const std::vector int_within_range_base_test { "111111111111111111111111111111111111111111111111111111111111111", "-1000000000000000000000000000000000000000000000000000000000000000", "2021110011022210012102010021220101220221", "-2021110011022210012102010021220101220222", @@ -636,19 +616,18 @@ int main() "1Y2P0IJ32E8E7", "-1Y2P0IJ32E8E8" }; - for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) - { + for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) { const auto& f = int_within_range_base_test[i]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); if (answer.ec != std::errc()) { - std::cerr << "converting " << std::quoted(f) << " to int failed (most likely out of range)" << std::endl; + std::cerr << "converting " << quoted(f) << " to int failed (most likely out of range)" << std::endl; return EXIT_FAILURE; } } // unsigned within range base test (64 bit) - const std::vector unsigned_within_range_base_test { "1111111111111111111111111111111111111111111111111111111111111111", + const std::vector unsigned_within_range_base_test { "1111111111111111111111111111111111111111111111111111111111111111", "11112220022122120101211020120210210211220", "33333333333333333333333333333333", "2214220303114400424121122430", @@ -684,20 +663,19 @@ int main() "5G24A25TWKWFF", "3W5E11264SGSF" }; int base_unsigned2 = 2; - for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) - { + for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) { const auto& f = unsigned_within_range_base_test[i]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned2); if (answer.ec != std::errc()) { - std::cerr << "converting " << std::quoted(f) << " to unsigned failed (most likely out of range)" << std::endl; + std::cerr << "converting " << quoted(f) << " to unsigned failed (most likely out of range)" << std::endl; return EXIT_FAILURE; } ++base_unsigned2; } // int leading zeros test - const std::vector int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111", + const std::vector int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111", "000000000000000000000000000000000000000000000000001101121", "000000000000000000000000000000000000000033313", "00000000000000000000000000000013030", @@ -733,17 +711,31 @@ int main() "00000000000000000000T0", "00000000000000000000S7" }; - for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) - { + for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) { const auto& f = int_leading_zeros_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(i + 2)); if (answer.ec != std::errc()) { - std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; + std::cerr << "could not convert to int for input: " << quoted(f) << std::endl; return EXIT_FAILURE; } else if (result != 1015) { - std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << 1015 << std::endl; + std::cerr << "result " << quoted(f) << " did not match with expected int: " << 1015 << std::endl; + return EXIT_FAILURE; + } + } + // issue 235 + { + std::vector s = {'0'}; + s.shrink_to_fit(); + int foo; + auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), foo); + if (answer.ec != std::errc()) { + std::cerr << "could not convert to int for input: '0'" << std::endl; + return EXIT_FAILURE; + } + else if (foo != 0) { + std::cerr << "expected zero: " << foo << std::endl; return EXIT_FAILURE; } } From 5334e2ba947e5f606cab63446730198e31d6038d Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 28 Jan 2024 10:55:02 -0500 Subject: [PATCH 2/4] fix --- tests/CMakeLists.txt | 1 + tests/fast_int.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 26e4368e..a7c83420 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -72,6 +72,7 @@ fast_float_add_cpp_test(long_test) fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(string_test) fast_float_add_cpp_test(fast_int) +target_compile_features(fast_int PRIVATE cxx_std_17) fast_float_add_cpp_test(json_fmt) fast_float_add_cpp_test(fortran) diff --git a/tests/fast_int.cpp b/tests/fast_int.cpp index 0783371a..8ef7784a 100644 --- a/tests/fast_int.cpp +++ b/tests/fast_int.cpp @@ -1,3 +1,4 @@ +#if defined(__cplusplus) && (__cplusplus >= 201703L) #include #include #include @@ -741,4 +742,10 @@ int main() { } return EXIT_SUCCESS; -} \ No newline at end of file +} +#else +int main() { + std::cerr << "The test requires C++17." << std::endl; + return EXIT_SUCCESS; +} +#endif \ No newline at end of file From 9be7de59985818d58e603deb9e4904a49bbc16c4 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 28 Jan 2024 10:56:20 -0500 Subject: [PATCH 3/4] adding sanitized tests --- .github/workflows/ubuntu22-sanitize.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/ubuntu22-sanitize.yml diff --git a/.github/workflows/ubuntu22-sanitize.yml b/.github/workflows/ubuntu22-sanitize.yml new file mode 100644 index 00000000..6275961f --- /dev/null +++ b/.github/workflows/ubuntu22-sanitize.yml @@ -0,0 +1,16 @@ +name: Ubuntu 22.04 CI Sanitized (GCC 11) + +on: [push, pull_request] + +jobs: + ubuntu-build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Use cmake + run: | + mkdir build && + cd build && + cmake -DFASTFLOAT_TEST=ON -D FASTFLOAT_SANITIZE=ON .. && + cmake --build . && + ctest --output-on-failure \ No newline at end of file From 7977ec60540c81c9e95da85f83983e4167d9dbdd Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 28 Jan 2024 11:11:19 -0500 Subject: [PATCH 4/4] fix --- fuzz/from_chars.cc | 6 +++++- tests/fast_int.cpp | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/fuzz/from_chars.cc b/fuzz/from_chars.cc index 4517d4a0..f47466f1 100644 --- a/fuzz/from_chars.cc +++ b/fuzz/from_chars.cc @@ -27,8 +27,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { auto answer = fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format); std::string input_f = fdp.ConsumeRandomLengthString(128); - double result_f = 0.0; + float result_f = 0.0; answer = fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format); + int result_i = 0; + std::string input_i = fdp.ConsumeRandomLengthString(128); + answer = + fast_float::from_chars(input_i.data(), input_i.data() + input_i.size(), result_i); return 0; } \ No newline at end of file diff --git a/tests/fast_int.cpp b/tests/fast_int.cpp index 8ef7784a..e12deeac 100644 --- a/tests/fast_int.cpp +++ b/tests/fast_int.cpp @@ -1,4 +1,20 @@ -#if defined(__cplusplus) && (__cplusplus >= 201703L) +#ifndef __cplusplus +#error fastfloat requires a C++ compiler +#endif + +// We want to enable the tests only for C++17 and above. +#ifndef FASTFLOAT_CPLUSPLUS +#if defined(_MSVC_LANG) && !defined(__clang__) +#define FASTFLOAT_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) +#else +#define FASTFLOAT_CPLUSPLUS __cplusplus +#endif +#endif + + +#if FASTFLOAT_CPLUSPLUS >= 201703L + + #include #include #include @@ -744,6 +760,9 @@ int main() { return EXIT_SUCCESS; } #else +#include +#include + int main() { std::cerr << "The test requires C++17." << std::endl; return EXIT_SUCCESS;