mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 16:56:57 +08:00
When calling ch_to_digit() with a UTF-16 or UTF-32 code unit, it simply
truncates away any data stored in the non-low byte(s) of the code unit.
It then uses a lookup table to determine whether the low byte
corresponds to an ASCII digit. This is incorrect because as soon as any
bit outside the low byte is set, the number will never correspond to a
ASCII digit anymore.
To fix this, we produce a mask that is all zeroes if any bit outside the
low byte is set in the code unit, all ones otherwise. Anding this mask
with the original code unit forces the table lookup to return the
sentinel value from the zero-index if any high bit was set and causes
the code unit not to be parsed as integer.
This bug was discovered when loading Mastodon posts inside the Ladybird
browser where some of Mastodon's JavaScript would trigger the code path
that erroneously parsed the emoji as integer. It had the visible effect
that some digits inside the posts would get rendered as one of the
emojis that parsed to that digit. For more details see this issue:
https://github.com/LadybirdBrowser/ladybird/issues/6205
The emojis in the test case are simply all the emojis used on Mastodon
that caused the bug. They can be found here:
06803422da/app/javascript/mastodon/features/emoji/emoji_map.json
1115 lines
36 KiB
C++
1115 lines
36 KiB
C++
#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 <cstdlib>
|
||
#include <iostream>
|
||
#include <vector>
|
||
#include <string_view>
|
||
#include <cstring>
|
||
#include "fast_float/fast_float.h"
|
||
#include <cstdint>
|
||
|
||
/*
|
||
all tests conducted are to check fast_float::from_chars functionality with int
|
||
and unsigned test cases include: int basic tests - numbers only, numbers with
|
||
strings behind, decimals, negative numbers unsigned basic tests - numbers only,
|
||
numbers with strings behind, decimals int invalid tests - strings only, strings
|
||
with numbers behind, space in front of number, plus sign in front of number
|
||
unsigned invalid tests - strings only, strings with numbers behind, space in
|
||
front of number, plus/minus sign in front of number int out of range tests -
|
||
numbers exceeding int bit size for 8, 16, 32, and 64 bits unsigned out of range
|
||
tests - numbers exceeding unsigned bit size 8, 16, 32, and 64 bits int pointer
|
||
tests - points to first character that is not recognized as int unsigned pointer
|
||
tests - points to first character that is not recognized as unsigned
|
||
int/unsigned base 2 tests - numbers are converted from binary to decimal
|
||
octal tests - numbers are converted from octal to decimal
|
||
hex tests - numbers are converted from hex to decimal (Note: 0x and 0X are
|
||
considered invalid) invalid base tests - any base not within 2-36 is invalid out
|
||
of range base tests - numbers exceeding int/unsigned bit size after converted
|
||
from base (Note: only 64 bit int and unsigned are tested) within range base
|
||
tests - max/min numbers are still within int/unsigned bit size after converted
|
||
from base (Note: only 64 bit int and unsigned are tested) leading zeros tests -
|
||
ignores all zeroes in front of valid number after converted from base
|
||
*/
|
||
|
||
int main() {
|
||
// int basic test
|
||
std::vector<int> const int_basic_test_expected{0, 10, -40, 1001, 9};
|
||
std::vector<std::string_view> const int_basic_test{"0", "10 ", "-40",
|
||
"1001 with text", "9.999"};
|
||
|
||
for (std::size_t i = 0; i < int_basic_test.size(); ++i) {
|
||
auto const 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: \"" << f
|
||
<< "\" because of invalid argument" << std::endl;
|
||
} else if (answer.ec == std::errc::result_out_of_range) {
|
||
std::cerr << "could not convert to int for input: \"" << f
|
||
<< "\" because it's out of range" << std::endl;
|
||
} else {
|
||
std::cerr << "could not convert to int for input: \"" << f
|
||
<< "\" because of an unknown error" << std::endl;
|
||
}
|
||
return EXIT_FAILURE;
|
||
} else if (result != int_basic_test_expected[i]) {
|
||
std::cerr << "result \"" << f << "\" did not match with expected int: "
|
||
<< int_basic_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned basic test
|
||
std::vector<unsigned> const unsigned_basic_test_expected{0, 10, 1001, 9};
|
||
std::vector<std::string_view> const unsigned_basic_test{
|
||
"0", "10 ", "1001 with text", "9.999"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != unsigned_basic_test_expected[i]) {
|
||
std::cerr << "result \"" << f
|
||
<< "\" did not match with expected unsigned: "
|
||
<< unsigned_basic_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int invalid error test
|
||
std::vector<std::string_view> const int_invalid_argument_test{
|
||
"text", "text with 1002", "+50", " 50"};
|
||
|
||
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned invalid error test
|
||
std::vector<std::string_view> const unsigned_invalid_argument_test{
|
||
"text", "text with 1002", "+50", " 50", "-50"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int out of range error test #1 (8 bit)
|
||
std::vector<std::string_view> const int_out_of_range_test_1{
|
||
"2000000000000000000000", "128", "-129"};
|
||
|
||
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int out of range error test #2 (16 bit)
|
||
std::vector<std::string_view> const int_out_of_range_test_2{
|
||
"2000000000000000000000", "32768", "-32769"};
|
||
|
||
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int out of range error test #3 (32 bit)
|
||
std::vector<std::string_view> const int_out_of_range_test_3{
|
||
"2000000000000000000000", "2147483648", "-2147483649"};
|
||
|
||
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int out of range error test #4 (64 bit)
|
||
std::vector<std::string_view> const int_out_of_range_test_4{
|
||
"2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
|
||
|
||
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned out of range error test #1 (8 bit)
|
||
std::vector<std::string_view> const unsigned_out_of_range_test_1{
|
||
"2000000000000000000000", "256"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned out of range error test #2 (16 bit)
|
||
std::vector<std::string_view> const unsigned_out_of_range_test_2{
|
||
"2000000000000000000000", "65536"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned out of range error test #3 (32 bit)
|
||
std::vector<std::string_view> const unsigned_out_of_range_test_3{
|
||
"2000000000000000000000", "4294967296"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned out of range error test #4 (64 bit)
|
||
std::vector<std::string_view> const unsigned_out_of_range_test_4{
|
||
"2000000000000000000000", "18446744073709551616"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int pointer test #1 (only numbers)
|
||
std::vector<std::string_view> const int_pointer_test_1{"0", "010", "-40"};
|
||
|
||
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (strcmp(answer.ptr, "") != 0) {
|
||
std::cerr << "ptr of result " << f
|
||
<< " did not match with expected ptr: \"\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int pointer test #2 (string behind numbers)
|
||
std::string_view const int_pointer_test_2 = "1001 with text";
|
||
|
||
auto const &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 " << f2
|
||
<< " did not match with expected ptr: \"with text\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// int pointer test #3 (string with newline behind numbers)
|
||
std::string_view const int_pointer_test_3 = "1001 with text\n";
|
||
|
||
auto const &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 " << f3
|
||
<< " did not match with expected ptr: with text" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// int pointer test #4 (float)
|
||
std::string_view const int_pointer_test_4 = "9.999";
|
||
|
||
auto const &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 " << f4
|
||
<< " did not match with expected ptr: .999" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// int pointer test #5 (invalid int)
|
||
std::string_view const int_pointer_test_5 = "+50";
|
||
|
||
auto const &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 " << f5
|
||
<< " did not match with expected ptr: +50" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// unsigned pointer test #2 (string behind numbers)
|
||
std::string_view const unsigned_pointer_test_1 = "1001 with text";
|
||
|
||
auto const &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 " << f6
|
||
<< " did not match with expected ptr: with text" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// unsigned pointer test #2 (invalid unsigned)
|
||
std::string_view const unsigned_pointer_test_2 = "-50";
|
||
|
||
auto const &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 " << f7
|
||
<< " did not match with expected ptr: -50" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// int base 2 test
|
||
std::vector<int> const int_base_2_test_expected{0, 1, 4, 2, -1};
|
||
std::vector<std::string_view> const int_base_2_test{"0", "1", "100", "010",
|
||
"-1"};
|
||
|
||
for (std::size_t i = 0; i < int_base_2_test.size(); ++i) {
|
||
auto const 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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != int_base_2_test_expected[i]) {
|
||
std::cerr << "result " << f << " did not match with expected int: "
|
||
<< int_base_2_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned base 2 test
|
||
std::vector<unsigned> const unsigned_base_2_test_expected{0, 1, 4, 2};
|
||
std::vector<std::string_view> const unsigned_base_2_test{"0", "1", "100",
|
||
"010"};
|
||
|
||
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != unsigned_base_2_test_expected[i]) {
|
||
std::cerr << "result " << f << " did not match with expected unsigned: "
|
||
<< unsigned_base_2_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int invalid error base 2 test
|
||
std::vector<std::string_view> const int_invalid_argument_base_2_test{"2", "A",
|
||
"-2"};
|
||
|
||
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned invalid error base 2 test
|
||
std::vector<std::string_view> const 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) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// octal test
|
||
std::vector<int> const base_octal_test_expected{0, 1, 7, 8, 9};
|
||
std::vector<std::string_view> const base_octal_test{"0", "1", "07", "010",
|
||
"0011"};
|
||
|
||
for (std::size_t i = 0; i < base_octal_test.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != base_octal_test_expected[i]) {
|
||
std::cerr << "result " << f << " did not match with expected int: "
|
||
<< base_octal_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// hex test
|
||
std::vector<int> const base_hex_test_expected{0, 1, 15, 31, 0, 16};
|
||
std::vector<std::string_view> const base_hex_test{"0", "1", "F",
|
||
"01f", "0x11", "10X11"};
|
||
|
||
for (std::size_t i = 0; i < base_hex_test.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != base_hex_test_expected[i]) {
|
||
std::cerr << "result " << f << " did not match with expected int: "
|
||
<< base_hex_test_expected[i] << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// invalid base test #1 (-1)
|
||
std::vector<std::string_view> const invalid_base_test_1{"0", "1", "-1", "F",
|
||
"10Z"};
|
||
|
||
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// invalid base test #2 (37)
|
||
std::vector<std::string_view> const invalid_base_test_2{"0", "1", "F", "Z",
|
||
"10Z"};
|
||
|
||
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) {
|
||
auto const &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: \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// int out of range error base test (64 bit)
|
||
std::vector<std::string_view> const int_out_of_range_base_test{
|
||
"1000000000000000000000000000000000000000000000000000000000000000",
|
||
"-1000000000000000000000000000000000000000000000000000000000000001",
|
||
"2021110011022210012102010021220101220222",
|
||
"-2021110011022210012102010021220101221000",
|
||
"20000000000000000000000000000000",
|
||
"-20000000000000000000000000000001",
|
||
"1104332401304422434310311213",
|
||
"-1104332401304422434310311214",
|
||
"1540241003031030222122212",
|
||
"-1540241003031030222122213",
|
||
"22341010611245052052301",
|
||
"-22341010611245052052302",
|
||
"1000000000000000000000",
|
||
"-1000000000000000000001",
|
||
"67404283172107811828",
|
||
"-67404283172107811830",
|
||
"9223372036854775808",
|
||
"-9223372036854775809",
|
||
"1728002635214590698",
|
||
"-1728002635214590699",
|
||
"41A792678515120368",
|
||
"-41A792678515120369",
|
||
"10B269549075433C38",
|
||
"-10B269549075433C39",
|
||
"4340724C6C71DC7A8",
|
||
"-4340724C6C71DC7A9",
|
||
"160E2AD3246366808",
|
||
"-160E2AD3246366809",
|
||
"8000000000000000",
|
||
"-8000000000000001",
|
||
"33D3D8307B214009",
|
||
"-33D3D8307B21400A",
|
||
"16AGH595DF825FA8",
|
||
"-16AGH595DF825FA9",
|
||
"BA643DCI0FFEEHI",
|
||
"-BA643DCI0FFEEI0",
|
||
"5CBFJIA3FH26JA8",
|
||
"-5CBFJIA3FH26JA9",
|
||
"2HEICIIIE82DH98",
|
||
"-2HEICIIIE82DH99",
|
||
"1ADAIBB21DCKFA8",
|
||
"-1ADAIBB21DCKFA9",
|
||
"I6K448CF4192C3",
|
||
"-I6K448CF4192C4",
|
||
"ACD772JNC9L0L8",
|
||
"-ACD772JNC9L0L9",
|
||
"64IE1FOCNN5G78",
|
||
"-64IE1FOCNN5G79",
|
||
"3IGOECJBMCA688",
|
||
"-3IGOECJBMCA689",
|
||
"27C48L5B37OAOQ",
|
||
"-27C48L5B37OAP0",
|
||
"1BK39F3AH3DMQ8",
|
||
"-1BK39F3AH3DMQ9",
|
||
"Q1SE8F0M04ISC",
|
||
"-Q1SE8F0M04ISD",
|
||
"HAJPPBC1FC208",
|
||
"-HAJPPBC1FC209",
|
||
"BM03I95HIA438",
|
||
"-BM03I95HIA439",
|
||
"8000000000000",
|
||
"-8000000000001",
|
||
"5HG4CK9JD4U38",
|
||
"-5HG4CK9JD4U39",
|
||
"3TDTK1V8J6TPQ",
|
||
"-3TDTK1V8J6TPR",
|
||
"2PIJMIKEXRXP8",
|
||
"-2PIJMIKEXRXP9",
|
||
"1Y2P0IJ32E8E8",
|
||
"-1Y2P0IJ32E8E9"};
|
||
|
||
for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned out of range error base test (64 bit)
|
||
std::vector<std::string_view> const unsigned_out_of_range_base_test{
|
||
"10000000000000000000000000000000000000000000000000000000000000000",
|
||
"11112220022122120101211020120210210211221",
|
||
"100000000000000000000000000000000",
|
||
"2214220303114400424121122431",
|
||
"3520522010102100444244424",
|
||
"45012021522523134134602",
|
||
"2000000000000000000000",
|
||
"145808576354216723757",
|
||
"18446744073709551616",
|
||
"335500516A429071285",
|
||
"839365134A2A240714",
|
||
"219505A9511A867B73",
|
||
"8681049ADB03DB172",
|
||
"2C1D56B648C6CD111",
|
||
"10000000000000000",
|
||
"67979G60F5428011",
|
||
"2D3FGB0B9CG4BD2G",
|
||
"141C8786H1CCAAGH",
|
||
"B53BJH07BE4DJ0G",
|
||
"5E8G4GGG7G56DIG",
|
||
"2L4LF104353J8KG",
|
||
"1DDH88H2782I516",
|
||
"L12EE5FN0JI1IG",
|
||
"C9C336O0MLB7EG",
|
||
"7B7N2PCNIOKCGG",
|
||
"4EO8HFAM6FLLMP",
|
||
"2NC6J26L66RHOG",
|
||
"1N3RSH11F098RO",
|
||
"14L9LKMO30O40G",
|
||
"ND075IB45K86G",
|
||
"G000000000000",
|
||
"B1W8P7J5Q9R6G",
|
||
"7ORP63SH4DPHI",
|
||
"5G24A25TWKWFG",
|
||
"3W5E11264SGSG"};
|
||
int base_unsigned = 2;
|
||
for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) {
|
||
auto const &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': \"" << f
|
||
<< "\"" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
++base_unsigned;
|
||
}
|
||
|
||
// just within range base test (64 bit)
|
||
std::vector<std::string_view> const int_within_range_base_test{
|
||
"111111111111111111111111111111111111111111111111111111111111111",
|
||
"-1000000000000000000000000000000000000000000000000000000000000000",
|
||
"2021110011022210012102010021220101220221",
|
||
"-2021110011022210012102010021220101220222",
|
||
"13333333333333333333333333333333",
|
||
"-20000000000000000000000000000000",
|
||
"1104332401304422434310311212",
|
||
"-1104332401304422434310311213",
|
||
"1540241003031030222122211",
|
||
"-1540241003031030222122212",
|
||
"22341010611245052052300",
|
||
"-22341010611245052052301",
|
||
"777777777777777777777",
|
||
"-1000000000000000000000",
|
||
"67404283172107811827",
|
||
"-67404283172107811828",
|
||
"9223372036854775807",
|
||
"-9223372036854775808",
|
||
"1728002635214590697",
|
||
"-1728002635214590698",
|
||
"41A792678515120367",
|
||
"-41A792678515120368",
|
||
"10B269549075433C37",
|
||
"-10B269549075433C38",
|
||
"4340724C6C71DC7A7",
|
||
"-4340724C6C71DC7A8",
|
||
"160E2AD3246366807",
|
||
"-160E2AD3246366808",
|
||
"7FFFFFFFFFFFFFFF",
|
||
"-8000000000000000",
|
||
"33D3D8307B214008",
|
||
"-33D3D8307B214009",
|
||
"16AGH595DF825FA7",
|
||
"-16AGH595DF825FA8",
|
||
"BA643DCI0FFEEHH",
|
||
"-BA643DCI0FFEEHI",
|
||
"5CBFJIA3FH26JA7",
|
||
"-5CBFJIA3FH26JA8",
|
||
"2HEICIIIE82DH97",
|
||
"-2HEICIIIE82DH98",
|
||
"1ADAIBB21DCKFA7",
|
||
"-1ADAIBB21DCKFA8",
|
||
"I6K448CF4192C2",
|
||
"-I6K448CF4192C3",
|
||
"ACD772JNC9L0L7",
|
||
"-ACD772JNC9L0L8",
|
||
"64IE1FOCNN5G77",
|
||
"-64IE1FOCNN5G78",
|
||
"3IGOECJBMCA687",
|
||
"-3IGOECJBMCA688",
|
||
"27C48L5B37OAOP",
|
||
"-27C48L5B37OAOQ",
|
||
"1BK39F3AH3DMQ7",
|
||
"-1BK39F3AH3DMQ8",
|
||
"Q1SE8F0M04ISB",
|
||
"-Q1SE8F0M04ISC",
|
||
"HAJPPBC1FC207",
|
||
"-HAJPPBC1FC208",
|
||
"BM03I95HIA437",
|
||
"-BM03I95HIA438",
|
||
"7VVVVVVVVVVVV",
|
||
"-8000000000000",
|
||
"5HG4CK9JD4U37",
|
||
"-5HG4CK9JD4U38",
|
||
"3TDTK1V8J6TPP",
|
||
"-3TDTK1V8J6TPQ",
|
||
"2PIJMIKEXRXP7",
|
||
"-2PIJMIKEXRXP8",
|
||
"1Y2P0IJ32E8E7",
|
||
"-1Y2P0IJ32E8E8"};
|
||
|
||
for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) {
|
||
auto const &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 " << f
|
||
<< " to int failed (most likely out of range)" << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
// unsigned within range base test (64 bit)
|
||
std::vector<std::string_view> const unsigned_within_range_base_test{
|
||
"1111111111111111111111111111111111111111111111111111111111111111",
|
||
"11112220022122120101211020120210210211220",
|
||
"33333333333333333333333333333333",
|
||
"2214220303114400424121122430",
|
||
"3520522010102100444244423",
|
||
"45012021522523134134601",
|
||
"1777777777777777777777",
|
||
"145808576354216723756",
|
||
"18446744073709551615",
|
||
"335500516A429071284",
|
||
"839365134A2A240713",
|
||
"219505A9511A867B72",
|
||
"8681049ADB03DB171",
|
||
"2C1D56B648C6CD110",
|
||
"FFFFFFFFFFFFFFFF",
|
||
"67979G60F5428010",
|
||
"2D3FGB0B9CG4BD2F",
|
||
"141C8786H1CCAAGG",
|
||
"B53BJH07BE4DJ0F",
|
||
"5E8G4GGG7G56DIF",
|
||
"2L4LF104353J8KF",
|
||
"1DDH88H2782I515",
|
||
"L12EE5FN0JI1IF",
|
||
"C9C336O0MLB7EF",
|
||
"7B7N2PCNIOKCGF",
|
||
"4EO8HFAM6FLLMO",
|
||
"2NC6J26L66RHOF",
|
||
"1N3RSH11F098RN",
|
||
"14L9LKMO30O40F",
|
||
"ND075IB45K86F",
|
||
"FVVVVVVVVVVVV",
|
||
"B1W8P7J5Q9R6F",
|
||
"7ORP63SH4DPHH",
|
||
"5G24A25TWKWFF",
|
||
"3W5E11264SGSF"};
|
||
int base_unsigned2 = 2;
|
||
for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) {
|
||
auto const &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 " << f
|
||
<< " to unsigned failed (most likely out of range)"
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
++base_unsigned2;
|
||
}
|
||
|
||
// int leading zeros test
|
||
std::vector<std::string_view> const int_leading_zeros_test{
|
||
"000000000000000000000000000000000000000000000000000000000000000000000011"
|
||
"11110111",
|
||
"000000000000000000000000000000000000000000000000001101121",
|
||
"000000000000000000000000000000000000000033313",
|
||
"00000000000000000000000000000013030",
|
||
"0000000000000000000000000000004411",
|
||
"0000000000000000000000000000002650",
|
||
"0000000000000000000000000000001767",
|
||
"0000000000000000000000000000001347",
|
||
"0000000000000000000000000000001015",
|
||
"00000000000000000000843",
|
||
"00000000000000000000707",
|
||
"00000000000000000000601",
|
||
"00000000000000000000527",
|
||
"0000000000000000000047A",
|
||
"000000000000000000003F7",
|
||
"0000000000000000000038C",
|
||
"00000000000000000000327",
|
||
"000000000000000000002F8",
|
||
"000000000000000000002AF",
|
||
"00000000000000000000267",
|
||
"00000000000000000000223",
|
||
"000000000000000000001L3",
|
||
"000000000000000000001I7",
|
||
"000000000000000000001FF",
|
||
"000000000000000000001D1",
|
||
"000000000000000000001AG",
|
||
"00000000000000000000187",
|
||
"00000000000000000000160",
|
||
"0000000000000000000013P",
|
||
"0000000000000000000011N",
|
||
"00000000000000000000VN",
|
||
"00000000000000000000UP",
|
||
"00000000000000000000TT",
|
||
"00000000000000000000T0",
|
||
"00000000000000000000S7"};
|
||
|
||
for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) {
|
||
auto const &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: \"" << f << "\""
|
||
<< std::endl;
|
||
return EXIT_FAILURE;
|
||
} else if (result != 1015) {
|
||
std::cerr << "result " << f
|
||
<< " did not match with expected int: " << 1015 << std::endl;
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
// issue 235
|
||
{
|
||
std::vector<char> 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;
|
||
}
|
||
}
|
||
// dont parse UTF-16 code units of emojis as int if low byte is ascii digit
|
||
{
|
||
const std::u16string emojis[] = {
|
||
u"ℹ", u"ℹ️", u"☸", u"☸️", u"☹", u"☹️", u"✳", u"✳️",
|
||
u"✴", u"✴️", u"⤴", u"⤴️", u"⤵", u"⤵️", u"〰", u"〰️",
|
||
};
|
||
bool failed = false;
|
||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||
for (size_t i = 0; i < array_size; i++) {
|
||
auto e = emojis[i];
|
||
int foo;
|
||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||
if (answer.ec == std::errc()) {
|
||
failed = true;
|
||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||
<< "." << std::endl;
|
||
}
|
||
}
|
||
|
||
if (failed) {
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
// dont parse UTF-32 code points of emojis as int if low byte is ascii digit
|
||
{
|
||
const std::u32string emojis[] = {
|
||
U"ℹ",
|
||
U"ℹ️",
|
||
U"☸",
|
||
U"☸️",
|
||
U"☹",
|
||
U"☹️",
|
||
U"✳",
|
||
U"✳️",
|
||
U"✴",
|
||
U"✴️",
|
||
U"⤴",
|
||
U"⤴️",
|
||
U"⤵",
|
||
U"⤵️",
|
||
U"〰",
|
||
U"〰️",
|
||
U"🈲",
|
||
U"🈳",
|
||
U"🈴",
|
||
U"🈵",
|
||
U"🈶",
|
||
U"🈷",
|
||
U"🈷️",
|
||
U"🈸",
|
||
U"🈹",
|
||
U"🌰",
|
||
U"🌱",
|
||
U"🌲",
|
||
U"🌳",
|
||
U"🌴",
|
||
U"🌵",
|
||
U"🌶",
|
||
U"🌶️",
|
||
U"🌷",
|
||
U"🌸",
|
||
U"🌹",
|
||
U"🐰",
|
||
U"🐱",
|
||
U"🐲",
|
||
U"🐳",
|
||
U"🐴",
|
||
U"🐵",
|
||
U"🐶",
|
||
U"🐷",
|
||
U"🐸",
|
||
U"🐹",
|
||
U"🔰",
|
||
U"🔱",
|
||
U"🔲",
|
||
U"🔳",
|
||
U"🔴",
|
||
U"🔵",
|
||
U"🔶",
|
||
U"🔷",
|
||
U"🔸",
|
||
U"🔹",
|
||
U"😰",
|
||
U"😱",
|
||
U"😲",
|
||
U"😳",
|
||
U"😴",
|
||
U"😵",
|
||
U"😵💫",
|
||
U"😶",
|
||
U"😶🌫",
|
||
U"😶🌫️",
|
||
U"😷",
|
||
U"😸",
|
||
U"😹",
|
||
U"🤰",
|
||
U"🤰🏻",
|
||
U"🤰🏼",
|
||
U"🤰🏽",
|
||
U"🤰🏾",
|
||
U"🤰🏿",
|
||
U"🤱",
|
||
U"🤱🏻",
|
||
U"🤱🏼",
|
||
U"🤱🏽",
|
||
U"🤱🏾",
|
||
U"🤱🏿",
|
||
U"🤲",
|
||
U"🤲🏻",
|
||
U"🤲🏼",
|
||
U"🤲🏽",
|
||
U"🤲🏾",
|
||
U"🤲🏿",
|
||
U"🤳",
|
||
U"🤳🏻",
|
||
U"🤳🏼",
|
||
U"🤳🏽",
|
||
U"🤳🏾",
|
||
U"🤳🏿",
|
||
U"🤴",
|
||
U"🤴🏻",
|
||
U"🤴🏼",
|
||
U"🤴🏽",
|
||
U"🤴🏾",
|
||
U"🤴🏿",
|
||
U"🤵",
|
||
U"🤵♀",
|
||
U"🤵♀️",
|
||
U"🤵♂",
|
||
U"🤵♂️",
|
||
U"🤵🏻",
|
||
U"🤵🏻♀",
|
||
U"🤵🏻♀️",
|
||
U"🤵🏻♂",
|
||
U"🤵🏻♂️",
|
||
U"🤵🏼",
|
||
U"🤵🏼♀",
|
||
U"🤵🏼♀️",
|
||
U"🤵🏼♂",
|
||
U"🤵🏼♂️",
|
||
U"🤵🏽",
|
||
U"🤵🏽♀",
|
||
U"🤵🏽♀️",
|
||
U"🤵🏽♂",
|
||
U"🤵🏽♂️",
|
||
U"🤵🏾",
|
||
U"🤵🏾♀",
|
||
U"🤵🏾♀️",
|
||
U"🤵🏾♂",
|
||
U"🤵🏾♂️",
|
||
U"🤵🏿",
|
||
U"🤵🏿♀",
|
||
U"🤵🏿♀️",
|
||
U"🤵🏿♂",
|
||
U"🤵🏿♂️",
|
||
U"🤶",
|
||
U"🤶🏻",
|
||
U"🤶🏼",
|
||
U"🤶🏽",
|
||
U"🤶🏾",
|
||
U"🤶🏿",
|
||
U"🤷",
|
||
U"🤷♀",
|
||
U"🤷♀️",
|
||
U"🤷♂",
|
||
U"🤷♂️",
|
||
U"🤷🏻",
|
||
U"🤷🏻♀",
|
||
U"🤷🏻♀️",
|
||
U"🤷🏻♂",
|
||
U"🤷🏻♂️",
|
||
U"🤷🏼",
|
||
U"🤷🏼♀",
|
||
U"🤷🏼♀️",
|
||
U"🤷🏼♂",
|
||
U"🤷🏼♂️",
|
||
U"🤷🏽",
|
||
U"🤷🏽♀",
|
||
U"🤷🏽♀️",
|
||
U"🤷🏽♂",
|
||
U"🤷🏽♂️",
|
||
U"🤷🏾",
|
||
U"🤷🏾♀",
|
||
U"🤷🏾♀️",
|
||
U"🤷🏾♂",
|
||
U"🤷🏾♂️",
|
||
U"🤷🏿",
|
||
U"🤷🏿♀",
|
||
U"🤷🏿♀️",
|
||
U"🤷🏿♂",
|
||
U"🤷🏿♂️",
|
||
U"🤸",
|
||
U"🤸♀",
|
||
U"🤸♀️",
|
||
U"🤸♂",
|
||
U"🤸♂️",
|
||
U"🤸🏻",
|
||
U"🤸🏻♀",
|
||
U"🤸🏻♀️",
|
||
U"🤸🏻♂",
|
||
U"🤸🏻♂️",
|
||
U"🤸🏼",
|
||
U"🤸🏼♀",
|
||
U"🤸🏼♀️",
|
||
U"🤸🏼♂",
|
||
U"🤸🏼♂️",
|
||
U"🤸🏽",
|
||
U"🤸🏽♀",
|
||
U"🤸🏽♀️",
|
||
U"🤸🏽♂",
|
||
U"🤸🏽♂️",
|
||
U"🤸🏾",
|
||
U"🤸🏾♀",
|
||
U"🤸🏾♀️",
|
||
U"🤸🏾♂",
|
||
U"🤸🏾♂️",
|
||
U"🤸🏿",
|
||
U"🤸🏿♀",
|
||
U"🤸🏿♀️",
|
||
U"🤸🏿♂",
|
||
U"🤸🏿♂️",
|
||
U"🤹",
|
||
U"🤹♀",
|
||
U"🤹♀️",
|
||
U"🤹♂",
|
||
U"🤹♂️",
|
||
U"🤹🏻",
|
||
U"🤹🏻♀",
|
||
U"🤹🏻♀️",
|
||
U"🤹🏻♂",
|
||
U"🤹🏻♂️",
|
||
U"🤹🏼",
|
||
U"🤹🏼♀",
|
||
U"🤹🏼♀️",
|
||
U"🤹🏼♂",
|
||
U"🤹🏼♂️",
|
||
U"🤹🏽",
|
||
U"🤹🏽♀",
|
||
U"🤹🏽♀️",
|
||
U"🤹🏽♂",
|
||
U"🤹🏽♂️",
|
||
U"🤹🏾",
|
||
U"🤹🏾♀",
|
||
U"🤹🏾♀️",
|
||
U"🤹🏾♂",
|
||
U"🤹🏾♂️",
|
||
U"🤹🏿",
|
||
U"🤹🏿♀",
|
||
U"🤹🏿♀️",
|
||
U"🤹🏿♂",
|
||
U"🤹🏿♂️",
|
||
};
|
||
bool failed = false;
|
||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||
for (size_t i = 0; i < array_size; i++) {
|
||
auto e = emojis[i];
|
||
int foo;
|
||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||
if (answer.ec == std::errc()) {
|
||
failed = true;
|
||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||
<< "." << std::endl;
|
||
}
|
||
}
|
||
|
||
if (failed) {
|
||
return EXIT_FAILURE;
|
||
}
|
||
}
|
||
|
||
return EXIT_SUCCESS;
|
||
}
|
||
#else
|
||
#include <iostream>
|
||
#include <cstdlib>
|
||
|
||
int main() {
|
||
std::cerr << "The test requires C++17." << std::endl;
|
||
return EXIT_SUCCESS;
|
||
}
|
||
#endif
|