mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 17:49:40 +00:00
AK: Implement UTF-16 string-to-number conversions
This commit is contained in:
parent
6e0290ecaa
commit
d40e3af697
Notes:
github-actions[bot]
2025-07-18 16:47:05 +00:00
Author: https://github.com/trflynn89
Commit: d40e3af697
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5388
Reviewed-by: https://github.com/shannonbooth ✅
6 changed files with 163 additions and 45 deletions
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <AK/StringConversions.h>
|
#include <AK/StringConversions.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
|
#include <AK/Utf16View.h>
|
||||||
|
|
||||||
#include <fast_float/fast_float.h>
|
#include <fast_float/fast_float.h>
|
||||||
|
|
||||||
|
@ -29,27 +30,22 @@ namespace AK {
|
||||||
__ENUMERATE_TYPE(float) \
|
__ENUMERATE_TYPE(float) \
|
||||||
__ENUMERATE_TYPE(double)
|
__ENUMERATE_TYPE(double)
|
||||||
|
|
||||||
template<Arithmetic T>
|
template<typename CharType, Arithmetic ValueType>
|
||||||
Optional<ParseFirstNumberResult<T>> parse_first_number(StringView string, TrimWhitespace trim_whitespace, int base)
|
static constexpr Optional<ParseFirstNumberResult<ValueType>> from_chars(CharType const* string, size_t length, int base)
|
||||||
{
|
{
|
||||||
if (trim_whitespace == TrimWhitespace::Yes)
|
ValueType value { 0 };
|
||||||
string = StringUtils::trim_whitespace(string, TrimMode::Both);
|
|
||||||
|
|
||||||
auto const* begin = string.characters_without_null_termination();
|
fast_float::parse_options_t<CharType> options;
|
||||||
auto const* end = begin + string.length();
|
|
||||||
T value { 0 };
|
|
||||||
|
|
||||||
fast_float::parse_options_t<char> options;
|
|
||||||
options.base = base;
|
options.base = base;
|
||||||
options.format |= fast_float::chars_format::no_infnan;
|
options.format |= fast_float::chars_format::no_infnan;
|
||||||
|
|
||||||
if constexpr (IsSigned<T> || IsFloatingPoint<T>) {
|
if constexpr (IsSigned<ValueType> || IsFloatingPoint<ValueType>) {
|
||||||
options.format |= fast_float::chars_format::allow_leading_plus;
|
options.format |= fast_float::chars_format::allow_leading_plus;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = fast_float::from_chars_advanced(begin, end, value, options);
|
auto result = fast_float::from_chars_advanced(string, string + length, value, options);
|
||||||
|
|
||||||
if constexpr (IsFloatingPoint<T>) {
|
if constexpr (IsFloatingPoint<ValueType>) {
|
||||||
if (result.ec == std::errc::result_out_of_range && (__builtin_isinf(value) || value == 0))
|
if (result.ec == std::errc::result_out_of_range && (__builtin_isinf(value) || value == 0))
|
||||||
result.ec = {};
|
result.ec = {};
|
||||||
}
|
}
|
||||||
|
@ -57,7 +53,26 @@ Optional<ParseFirstNumberResult<T>> parse_first_number(StringView string, TrimWh
|
||||||
if (result.ec != std::errc {})
|
if (result.ec != std::errc {})
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return ParseFirstNumberResult { value, static_cast<size_t>(result.ptr - begin) };
|
return ParseFirstNumberResult { value, static_cast<size_t>(result.ptr - string) };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
Optional<ParseFirstNumberResult<T>> parse_first_number(StringView string, TrimWhitespace trim_whitespace, int base)
|
||||||
|
{
|
||||||
|
if (trim_whitespace == TrimWhitespace::Yes)
|
||||||
|
string = StringUtils::trim_whitespace(string, TrimMode::Both);
|
||||||
|
|
||||||
|
return from_chars<char, T>(string.characters_without_null_termination(), string.length(), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
Optional<ParseFirstNumberResult<T>> parse_first_number(Utf16View const& string, TrimWhitespace trim_whitespace, int base)
|
||||||
|
{
|
||||||
|
if (string.has_ascii_storage())
|
||||||
|
return parse_first_number<T>(string.bytes(), trim_whitespace, base);
|
||||||
|
|
||||||
|
auto trimmed_string = trim_whitespace == TrimWhitespace::Yes ? string.trim_whitespace() : string;
|
||||||
|
return from_chars<char16_t, T>(trimmed_string.utf16_span().data(), trimmed_string.length_in_code_units(), base);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define __ENUMERATE_TYPE(type) \
|
#define __ENUMERATE_TYPE(type) \
|
||||||
|
@ -65,6 +80,11 @@ Optional<ParseFirstNumberResult<T>> parse_first_number(StringView string, TrimWh
|
||||||
ENUMERATE_ARITHMETIC_TYPES
|
ENUMERATE_ARITHMETIC_TYPES
|
||||||
#undef __ENUMERATE_TYPE
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
|
#define __ENUMERATE_TYPE(type) \
|
||||||
|
template Optional<ParseFirstNumberResult<type>> parse_first_number(Utf16View const&, TrimWhitespace, int);
|
||||||
|
ENUMERATE_ARITHMETIC_TYPES
|
||||||
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
template<Arithmetic T>
|
template<Arithmetic T>
|
||||||
Optional<T> parse_number(StringView string, TrimWhitespace trim_whitespace, int base)
|
Optional<T> parse_number(StringView string, TrimWhitespace trim_whitespace, int base)
|
||||||
{
|
{
|
||||||
|
@ -81,20 +101,54 @@ Optional<T> parse_number(StringView string, TrimWhitespace trim_whitespace, int
|
||||||
return result->value;
|
return result->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
Optional<T> parse_number(Utf16View const& string, TrimWhitespace trim_whitespace, int base)
|
||||||
|
{
|
||||||
|
if (string.has_ascii_storage())
|
||||||
|
return parse_number<T>(string.bytes(), trim_whitespace, base);
|
||||||
|
|
||||||
|
auto trimmed_string = trim_whitespace == TrimWhitespace::Yes ? string.trim_whitespace() : string;
|
||||||
|
|
||||||
|
auto result = parse_first_number<T>(trimmed_string, TrimWhitespace::No, base);
|
||||||
|
if (!result.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (result->characters_parsed != trimmed_string.length_in_code_units())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return result->value;
|
||||||
|
}
|
||||||
|
|
||||||
#define __ENUMERATE_TYPE(type) \
|
#define __ENUMERATE_TYPE(type) \
|
||||||
template Optional<type> parse_number(StringView, TrimWhitespace, int);
|
template Optional<type> parse_number(StringView, TrimWhitespace, int);
|
||||||
ENUMERATE_ARITHMETIC_TYPES
|
ENUMERATE_ARITHMETIC_TYPES
|
||||||
#undef __ENUMERATE_TYPE
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
|
#define __ENUMERATE_TYPE(type) \
|
||||||
|
template Optional<type> parse_number(Utf16View const&, TrimWhitespace, int);
|
||||||
|
ENUMERATE_ARITHMETIC_TYPES
|
||||||
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
template<Integral T>
|
template<Integral T>
|
||||||
Optional<T> parse_hexadecimal_number(StringView string, TrimWhitespace trim_whitespace)
|
Optional<T> parse_hexadecimal_number(StringView string, TrimWhitespace trim_whitespace)
|
||||||
{
|
{
|
||||||
return parse_number<T>(string, trim_whitespace, 16);
|
return parse_number<T>(string, trim_whitespace, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Integral T>
|
||||||
|
Optional<T> parse_hexadecimal_number(Utf16View const& string, TrimWhitespace trim_whitespace)
|
||||||
|
{
|
||||||
|
return parse_number<T>(string, trim_whitespace, 16);
|
||||||
|
}
|
||||||
|
|
||||||
#define __ENUMERATE_TYPE(type) \
|
#define __ENUMERATE_TYPE(type) \
|
||||||
template Optional<type> parse_hexadecimal_number(StringView, TrimWhitespace);
|
template Optional<type> parse_hexadecimal_number(StringView, TrimWhitespace);
|
||||||
ENUMERATE_INTEGRAL_TYPES
|
ENUMERATE_INTEGRAL_TYPES
|
||||||
#undef __ENUMERATE_TYPE
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
|
#define __ENUMERATE_TYPE(type) \
|
||||||
|
template Optional<type> parse_hexadecimal_number(Utf16View const&, TrimWhitespace);
|
||||||
|
ENUMERATE_INTEGRAL_TYPES
|
||||||
|
#undef __ENUMERATE_TYPE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,19 @@ struct ParseFirstNumberResult {
|
||||||
template<Arithmetic T>
|
template<Arithmetic T>
|
||||||
Optional<ParseFirstNumberResult<T>> parse_first_number(StringView, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
Optional<ParseFirstNumberResult<T>> parse_first_number(StringView, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
Optional<ParseFirstNumberResult<T>> parse_first_number(Utf16View const&, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
||||||
|
|
||||||
template<Arithmetic T>
|
template<Arithmetic T>
|
||||||
Optional<T> parse_number(StringView, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
Optional<T> parse_number(StringView, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
Optional<T> parse_number(Utf16View const&, TrimWhitespace = TrimWhitespace::Yes, int base = 10);
|
||||||
|
|
||||||
template<Integral T>
|
template<Integral T>
|
||||||
Optional<T> parse_hexadecimal_number(StringView, TrimWhitespace = TrimWhitespace::Yes);
|
Optional<T> parse_hexadecimal_number(StringView, TrimWhitespace = TrimWhitespace::Yes);
|
||||||
|
|
||||||
|
template<Integral T>
|
||||||
|
Optional<T> parse_hexadecimal_number(Utf16View const&, TrimWhitespace = TrimWhitespace::Yes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,12 @@ public:
|
||||||
return view().to_ascii_titlecase();
|
return view().to_ascii_titlecase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
ALWAYS_INLINE Optional<T> to_number(TrimWhitespace trim_whitespace = TrimWhitespace::Yes) const
|
||||||
|
{
|
||||||
|
return m_data.to_number<T>(trim_whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE Utf16FlyString& operator=(Utf16String const& string)
|
ALWAYS_INLINE Utf16FlyString& operator=(Utf16String const& string)
|
||||||
{
|
{
|
||||||
*this = Utf16FlyString { string };
|
*this = Utf16FlyString { string };
|
||||||
|
|
|
@ -88,6 +88,12 @@ public:
|
||||||
StringView ascii_view() const&& = delete;
|
StringView ascii_view() const&& = delete;
|
||||||
Utf16View utf16_view() const&& = delete;
|
Utf16View utf16_view() const&& = delete;
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
ALWAYS_INLINE Optional<T> to_number(TrimWhitespace trim_whitespace = TrimWhitespace::Yes) const
|
||||||
|
{
|
||||||
|
return utf16_view().to_number<T>(trim_whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE Utf16StringBase& operator=(Utf16StringBase const& other)
|
ALWAYS_INLINE Utf16StringBase& operator=(Utf16StringBase const& other)
|
||||||
{
|
{
|
||||||
if (&other != this) {
|
if (&other != this) {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/Span.h>
|
#include <AK/Span.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <AK/StringConversions.h>
|
||||||
#include <AK/StringHash.h>
|
#include <AK/StringHash.h>
|
||||||
#include <AK/Traits.h>
|
#include <AK/Traits.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
@ -210,6 +211,14 @@ public:
|
||||||
return { m_string.utf16, length_in_code_units() };
|
return { m_string.utf16, length_in_code_units() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Arithmetic T>
|
||||||
|
ALWAYS_INLINE Optional<T> to_number(TrimWhitespace trim_whitespace = TrimWhitespace::Yes) const
|
||||||
|
{
|
||||||
|
if (has_ascii_storage())
|
||||||
|
return parse_number<T>(bytes(), trim_whitespace);
|
||||||
|
return parse_number<T>(*this, trim_whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator==(Utf16View const& other) const
|
[[nodiscard]] constexpr bool operator==(Utf16View const& other) const
|
||||||
{
|
{
|
||||||
if (length_in_code_units() != other.length_in_code_units())
|
if (length_in_code_units() != other.length_in_code_units())
|
||||||
|
|
|
@ -7,13 +7,16 @@
|
||||||
#include <LibTest/TestCase.h>
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
#include <AK/StringConversions.h>
|
#include <AK/StringConversions.h>
|
||||||
|
#include <AK/Utf16View.h>
|
||||||
|
|
||||||
static double parse_complete_double(StringView view)
|
template<typename ViewType>
|
||||||
|
static double parse_complete_double(ViewType const& view)
|
||||||
{
|
{
|
||||||
return AK::parse_number<double>(view, TrimWhitespace::No).release_value();
|
return AK::parse_number<double>(view, TrimWhitespace::No).release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
static float parse_complete_float(StringView view)
|
template<typename ViewType>
|
||||||
|
static float parse_complete_float(ViewType const& view)
|
||||||
{
|
{
|
||||||
return AK::parse_number<float>(view, TrimWhitespace::No).release_value();
|
return AK::parse_number<float>(view, TrimWhitespace::No).release_value();
|
||||||
}
|
}
|
||||||
|
@ -21,22 +24,31 @@ static float parse_complete_float(StringView view)
|
||||||
TEST_CASE(simple_cases)
|
TEST_CASE(simple_cases)
|
||||||
{
|
{
|
||||||
|
|
||||||
#define DOES_PARSE_DOUBLE_LIKE_CPP(value) \
|
#define DOES_PARSE_DOUBLE_LIKE_CPP(value) \
|
||||||
do { \
|
do { \
|
||||||
EXPECT_EQ(static_cast<double>(value), parse_complete_double(#value##sv)); \
|
EXPECT_EQ(static_cast<double>(value), parse_complete_double(#value##sv)); \
|
||||||
EXPECT_EQ(-static_cast<double>(value), parse_complete_double("-" #value##sv)); \
|
EXPECT_EQ(-static_cast<double>(value), parse_complete_double("-" #value##sv)); \
|
||||||
|
\
|
||||||
|
EXPECT_EQ(static_cast<double>(value), parse_complete_double(u"" #value##sv)); \
|
||||||
|
EXPECT_EQ(-static_cast<double>(value), parse_complete_double(u"-" #value##sv)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define DOES_PARSE_FLOAT_LIKE_CPP(value) \
|
#define DOES_PARSE_FLOAT_LIKE_CPP(value) \
|
||||||
do { \
|
do { \
|
||||||
float val = parse_complete_float(#value##sv); \
|
float val = parse_complete_float(#value##sv); \
|
||||||
EXPECT_EQ(static_cast<float>(value##f), val); \
|
EXPECT_EQ(static_cast<float>(value##f), val); \
|
||||||
|
EXPECT_EQ(-static_cast<float>(value##f), parse_complete_float("-" #value##sv)); \
|
||||||
|
\
|
||||||
|
val = parse_complete_float(u"" #value##sv); \
|
||||||
|
EXPECT_EQ(static_cast<float>(value##f), val); \
|
||||||
EXPECT_EQ(-static_cast<float>(value##f), parse_complete_float("-" #value##sv)); \
|
EXPECT_EQ(-static_cast<float>(value##f), parse_complete_float("-" #value##sv)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(value) \
|
#define DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(value) \
|
||||||
DOES_PARSE_DOUBLE_LIKE_CPP(value); \
|
do { \
|
||||||
DOES_PARSE_FLOAT_LIKE_CPP(value);
|
DOES_PARSE_DOUBLE_LIKE_CPP(value); \
|
||||||
|
DOES_PARSE_FLOAT_LIKE_CPP(value); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
DOES_PARSE_DOUBLE_LIKE_CPP(2.22507385850720138309e-308);
|
DOES_PARSE_DOUBLE_LIKE_CPP(2.22507385850720138309e-308);
|
||||||
|
|
||||||
|
@ -212,10 +224,13 @@ TEST_CASE(simple_cases)
|
||||||
DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(8.589934335999999523162841796875e+09);
|
DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(8.589934335999999523162841796875e+09);
|
||||||
DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(0.09289376810193062);
|
DOES_PARSE_FLOAT_AND_DOUBLE_LIKE_CPP(0.09289376810193062);
|
||||||
|
|
||||||
#define DOES_PARSE_INT_LIKE_VALUE_LIKE_CPP(value) \
|
#define DOES_PARSE_INT_LIKE_VALUE_LIKE_CPP(value) \
|
||||||
do { \
|
do { \
|
||||||
EXPECT_EQ(static_cast<double>(value##.), parse_complete_double(#value##sv)); \
|
EXPECT_EQ(static_cast<double>(value##.), parse_complete_double(#value##sv)); \
|
||||||
EXPECT_EQ(-static_cast<double>(value##.), parse_complete_double("-" #value##sv)); \
|
EXPECT_EQ(-static_cast<double>(value##.), parse_complete_double("-" #value##sv)); \
|
||||||
|
\
|
||||||
|
EXPECT_EQ(static_cast<double>(value##.), parse_complete_double(u"" #value##sv)); \
|
||||||
|
EXPECT_EQ(-static_cast<double>(value##.), parse_complete_double(u"-" #value##sv)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
DOES_PARSE_INT_LIKE_VALUE_LIKE_CPP(0);
|
DOES_PARSE_INT_LIKE_VALUE_LIKE_CPP(0);
|
||||||
|
@ -230,8 +245,11 @@ TEST_CASE(simple_cases)
|
||||||
EXPECT_EQ(0., parse_complete_double("2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324"sv));
|
EXPECT_EQ(0., parse_complete_double("2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324"sv));
|
||||||
EXPECT_EQ(0., parse_complete_double("2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324"sv));
|
EXPECT_EQ(0., parse_complete_double("2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324"sv));
|
||||||
|
|
||||||
#define EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(expected_val, str) \
|
#define EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(expected_val, str) \
|
||||||
EXPECT_EQ(bit_cast<u64>(expected_val), bit_cast<u64>(parse_complete_double(str##sv)));
|
do { \
|
||||||
|
EXPECT_EQ(bit_cast<u64>(expected_val), bit_cast<u64>(parse_complete_double(str##sv))); \
|
||||||
|
EXPECT_EQ(bit_cast<u64>(expected_val), bit_cast<u64>(parse_complete_double(u##str##sv))); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(0., "1e-324");
|
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(0., "1e-324");
|
||||||
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(-0., "-1e-324");
|
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(-0., "-1e-324");
|
||||||
|
@ -240,13 +258,22 @@ TEST_CASE(simple_cases)
|
||||||
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(0., "+.0e10");
|
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(0., "+.0e10");
|
||||||
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(-0., "-.0e10");
|
EXPECT_TO_PARSE_TO_VALUE_EQUAL_TO(-0., "-.0e10");
|
||||||
|
|
||||||
#define EXPECT_TO_PARSE_TO_INFINITY(str) \
|
#define EXPECT_TO_PARSE_TO_INFINITY(str) \
|
||||||
EXPECT_EQ(__builtin_huge_val(), parse_complete_double(str##sv)); \
|
do { \
|
||||||
EXPECT_EQ(__builtin_huge_val(), parse_complete_double("+" str##sv)); \
|
EXPECT_EQ(__builtin_huge_val(), parse_complete_double(str##sv)); \
|
||||||
EXPECT_EQ(-__builtin_huge_val(), parse_complete_double("-" str##sv)); \
|
EXPECT_EQ(__builtin_huge_val(), parse_complete_double("+" str##sv)); \
|
||||||
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float(str##sv)); \
|
EXPECT_EQ(-__builtin_huge_val(), parse_complete_double("-" str##sv)); \
|
||||||
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float("+" str##sv)); \
|
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float(str##sv)); \
|
||||||
EXPECT_EQ(static_cast<float>(-__builtin_huge_valf()), parse_complete_float("-" str##sv))
|
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float("+" str##sv)); \
|
||||||
|
EXPECT_EQ(static_cast<float>(-__builtin_huge_valf()), parse_complete_float("-" str##sv)); \
|
||||||
|
\
|
||||||
|
EXPECT_EQ(__builtin_huge_val(), parse_complete_double(u##str##sv)); \
|
||||||
|
EXPECT_EQ(__builtin_huge_val(), parse_complete_double(u"+" str##sv)); \
|
||||||
|
EXPECT_EQ(-__builtin_huge_val(), parse_complete_double(u"-" str##sv)); \
|
||||||
|
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float(u##str##sv)); \
|
||||||
|
EXPECT_EQ(static_cast<float>(__builtin_huge_valf()), parse_complete_float(u"+" str##sv)); \
|
||||||
|
EXPECT_EQ(static_cast<float>(-__builtin_huge_valf()), parse_complete_float(u"-" str##sv)); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
EXPECT_TO_PARSE_TO_INFINITY("123.456e789");
|
EXPECT_TO_PARSE_TO_INFINITY("123.456e789");
|
||||||
EXPECT_TO_PARSE_TO_INFINITY("123456.456789e789");
|
EXPECT_TO_PARSE_TO_INFINITY("123456.456789e789");
|
||||||
|
@ -267,6 +294,11 @@ TEST_CASE(partial_parse_stops_at_right_spot)
|
||||||
auto result = AK::parse_first_number<double>(string_value##sv); \
|
auto result = AK::parse_first_number<double>(string_value##sv); \
|
||||||
VERIFY(result.has_value()); \
|
VERIFY(result.has_value()); \
|
||||||
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||||
|
EXPECT_EQ(result->characters_parsed, chars_parsed##uz); \
|
||||||
|
\
|
||||||
|
result = AK::parse_first_number<double>(u##string_value##sv); \
|
||||||
|
VERIFY(result.has_value()); \
|
||||||
|
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||||
EXPECT_EQ(result->characters_parsed, chars_parsed##uz); \
|
EXPECT_EQ(result->characters_parsed, chars_parsed##uz); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
@ -285,10 +317,10 @@ TEST_CASE(partial_parse_stops_at_right_spot)
|
||||||
|
|
||||||
TEST_CASE(invalid_parse)
|
TEST_CASE(invalid_parse)
|
||||||
{
|
{
|
||||||
#define EXPECT_PARSE_TO_FAIL(string_value) \
|
#define EXPECT_PARSE_TO_FAIL(string_value) \
|
||||||
do { \
|
do { \
|
||||||
auto result = AK::parse_first_number<double>(string_value##sv); \
|
EXPECT(!AK::parse_first_number<double>(string_value##sv).has_value()); \
|
||||||
EXPECT(!result.has_value()); \
|
EXPECT(!AK::parse_first_number<double>(u##string_value##sv).has_value()); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
EXPECT_PARSE_TO_FAIL("");
|
EXPECT_PARSE_TO_FAIL("");
|
||||||
|
@ -331,6 +363,10 @@ TEST_CASE(detect_out_of_range_values)
|
||||||
do { \
|
do { \
|
||||||
auto result = AK::parse_first_number<double>(string_value##sv); \
|
auto result = AK::parse_first_number<double>(string_value##sv); \
|
||||||
VERIFY(result.has_value()); \
|
VERIFY(result.has_value()); \
|
||||||
|
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||||
|
\
|
||||||
|
result = AK::parse_first_number<double>(u##string_value##sv); \
|
||||||
|
VERIFY(result.has_value()); \
|
||||||
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
EXPECT_EQ(bit_cast<u64>(result->value), bit_cast<u64>(static_cast<double>(double_value))); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
@ -340,15 +376,13 @@ TEST_CASE(detect_out_of_range_values)
|
||||||
EXPECT_PARSE_TO_HAVE_ERROR("-10e10000", -INFINITY);
|
EXPECT_PARSE_TO_HAVE_ERROR("-10e10000", -INFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool parse_completely_passes(StringView view)
|
|
||||||
{
|
|
||||||
return AK::parse_number<double>(view, TrimWhitespace::No).has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE(parse_completely_must_be_just_floating_point)
|
TEST_CASE(parse_completely_must_be_just_floating_point)
|
||||||
{
|
{
|
||||||
#define EXPECT_PARSE_COMPLETELY_TO_FAIL(value) \
|
#define EXPECT_PARSE_COMPLETELY_TO_FAIL(string_value) \
|
||||||
EXPECT(!parse_completely_passes(value##sv))
|
do { \
|
||||||
|
EXPECT(!AK::parse_number<double>(string_value##sv, TrimWhitespace::No).has_value()); \
|
||||||
|
EXPECT(!AK::parse_number<double>(u##string_value##sv, TrimWhitespace::No).has_value()); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
EXPECT_PARSE_COMPLETELY_TO_FAIL("");
|
EXPECT_PARSE_COMPLETELY_TO_FAIL("");
|
||||||
EXPECT_PARSE_COMPLETELY_TO_FAIL("-");
|
EXPECT_PARSE_COMPLETELY_TO_FAIL("-");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue