mirror of
https://github.com/aphrody-labs/terminal.git
synced 2026-10-13 22:05:20 +00:00
Minor improvements to til::parse_(un)signed (#20778)
* Allow an explicit `+` prefix like other parsers * Reject invalid bases * Fix UB during `uint64_t` -> `T` cast in `parse_signed`
This commit is contained in:
1 parent
35f29bd316
commit
2d417e4eda
2 files changed
+39
-6
No files matched your search
+12
-6
@@ -382,6 +382,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
namespace details
|
||||
{
|
||||
// Just like std::wcstoul, but without annoying locales and null-terminating strings.
|
||||
// Don't use this, use parse_unsigned and parse_signed instead.
|
||||
template<typename T, typename Traits>
|
||||
_TIL_INLINEPREFIX constexpr std::optional<uint64_t> parse_u64(const std::basic_string_view<T, Traits>& str, int base = 0) noexcept
|
||||
{
|
||||
@@ -397,7 +398,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
uint64_t accumulator = 0;
|
||||
uint64_t base_uint64 = base;
|
||||
|
||||
if (base <= 0)
|
||||
if (base == 0)
|
||||
{
|
||||
base_uint64 = 10;
|
||||
|
||||
@@ -422,7 +423,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr == end || base_uint64 > 36)
|
||||
if (ptr == end || base_uint64 < 2 || base_uint64 > 36)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -467,8 +468,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
}
|
||||
|
||||
template<std::unsigned_integral R, typename T, typename Traits>
|
||||
constexpr std::optional<R> parse_unsigned(const std::basic_string_view<T, Traits>& str, int base = 0) noexcept
|
||||
constexpr std::optional<R> parse_unsigned(std::basic_string_view<T, Traits> str, int base = 0) noexcept
|
||||
{
|
||||
if (str.starts_with('+'))
|
||||
{
|
||||
str = str.substr(1);
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<R, uint64_t>)
|
||||
{
|
||||
return details::parse_u64<>(str, base);
|
||||
@@ -488,7 +494,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
constexpr std::optional<R> parse_signed(std::basic_string_view<T, Traits> str, int base = 0) noexcept
|
||||
{
|
||||
const bool hasSign = str.starts_with(L'-');
|
||||
if (hasSign)
|
||||
if (hasSign || str.starts_with('+'))
|
||||
{
|
||||
str = str.substr(1);
|
||||
}
|
||||
@@ -500,8 +506,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto r = gsl::narrow_cast<R>(*opt);
|
||||
return hasSign ? -r : r;
|
||||
const auto value = hasSign ? uint64_t{ 0 } - *opt : *opt;
|
||||
return gsl::narrow_cast<R>(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,11 @@ class StringTests
|
||||
|
||||
TEST_METHOD(parse_unsigned)
|
||||
{
|
||||
for (const auto text : { "+", "++1", "+-1", "-+1", "-0", "+ 1", " +1", "+1 ", "+0x", "+0b", "+08", "+4294967296" })
|
||||
{
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint32_t>(text));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint64_t>(L"++1"));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint32_t>(""));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint32_t>("0x"));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint32_t>("Z"));
|
||||
@@ -139,17 +144,30 @@ class StringTests
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint32_t>("0x100000000"));
|
||||
VERIFY_ARE_EQUAL(0u, til::parse_unsigned<uint32_t>("0"));
|
||||
VERIFY_ARE_EQUAL(0u, til::parse_unsigned<uint32_t>("0x0"));
|
||||
VERIFY_ARE_EQUAL(0u, til::parse_unsigned<uint32_t>("+0"));
|
||||
VERIFY_ARE_EQUAL(123u, til::parse_unsigned<uint32_t>("0b1111011"));
|
||||
VERIFY_ARE_EQUAL(0123u, til::parse_unsigned<uint32_t>("0123"));
|
||||
VERIFY_ARE_EQUAL(123u, til::parse_unsigned<uint32_t>("123"));
|
||||
VERIFY_ARE_EQUAL(0x123u, til::parse_unsigned<uint32_t>("0x123"));
|
||||
VERIFY_ARE_EQUAL(123u, til::parse_unsigned<uint32_t>("+0B1111011"));
|
||||
VERIFY_ARE_EQUAL(0123u, til::parse_unsigned<uint32_t>("+0123"));
|
||||
VERIFY_ARE_EQUAL(123u, til::parse_unsigned<uint32_t>("+123"));
|
||||
VERIFY_ARE_EQUAL(0x123u, til::parse_unsigned<uint32_t>("+0X123"));
|
||||
VERIFY_ARE_EQUAL(0x123abcu, til::parse_unsigned<uint32_t>("0x123abc"));
|
||||
VERIFY_ARE_EQUAL(0X123ABCu, til::parse_unsigned<uint32_t>("0X123ABC"));
|
||||
VERIFY_ARE_EQUAL(UINT32_MAX, til::parse_unsigned<uint32_t>("0xffffffff"));
|
||||
VERIFY_ARE_EQUAL(UINT32_MAX, til::parse_unsigned<uint32_t>("4294967295"));
|
||||
VERIFY_ARE_EQUAL(UINT32_MAX, til::parse_unsigned<uint32_t>("+4294967295"));
|
||||
VERIFY_ARE_EQUAL(UINT64_MAX, til::parse_unsigned<uint64_t>(L"+18446744073709551615"));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_unsigned<uint64_t>(L"+18446744073709551616"));
|
||||
}
|
||||
|
||||
TEST_METHOD(parse_signed)
|
||||
{
|
||||
for (const auto text : { "+", "++1", "+-1", "-+1", "--1", "+ 1", " +1", "+1 ", "+0x", "+0b", "+08", "+2147483648" })
|
||||
{
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>(text));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>(""));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>("-"));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>("--"));
|
||||
@@ -160,13 +178,20 @@ class StringTests
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>("0x80000000"));
|
||||
VERIFY_ARE_EQUAL(std::nullopt, til::parse_signed<int32_t>("-0x80000001"));
|
||||
VERIFY_ARE_EQUAL(0, til::parse_signed<int32_t>("0"));
|
||||
VERIFY_ARE_EQUAL(0, til::parse_signed<int32_t>("+0"));
|
||||
VERIFY_ARE_EQUAL(0, til::parse_signed<int32_t>("-0"));
|
||||
VERIFY_ARE_EQUAL(0, til::parse_signed<int32_t>("-0x0"));
|
||||
VERIFY_ARE_EQUAL(123, til::parse_signed<int32_t>("0b1111011"));
|
||||
VERIFY_ARE_EQUAL(0123, til::parse_signed<int32_t>("0123"));
|
||||
VERIFY_ARE_EQUAL(123, til::parse_signed<int32_t>("123"));
|
||||
VERIFY_ARE_EQUAL(0x123, til::parse_signed<int32_t>("0x123"));
|
||||
VERIFY_ARE_EQUAL(123, til::parse_signed<int32_t>("+0b1111011"));
|
||||
VERIFY_ARE_EQUAL(-123, til::parse_signed<int32_t>("-0b1111011"));
|
||||
VERIFY_ARE_EQUAL(0123, til::parse_signed<int32_t>("+0123"));
|
||||
VERIFY_ARE_EQUAL(-0123, til::parse_signed<int32_t>("-0123"));
|
||||
VERIFY_ARE_EQUAL(123, til::parse_signed<int32_t>("+123"));
|
||||
VERIFY_ARE_EQUAL(-123, til::parse_signed<int32_t>("-123"));
|
||||
VERIFY_ARE_EQUAL(0x123, til::parse_signed<int32_t>("+0X123"));
|
||||
VERIFY_ARE_EQUAL(-0x123, til::parse_signed<int32_t>("-0x123"));
|
||||
VERIFY_ARE_EQUAL(-0x123abc, til::parse_signed<int32_t>("-0x123abc"));
|
||||
VERIFY_ARE_EQUAL(-0X123ABC, til::parse_signed<int32_t>("-0X123ABC"));
|
||||
@@ -174,6 +199,8 @@ class StringTests
|
||||
VERIFY_ARE_EQUAL(INT32_MIN, til::parse_signed<int32_t>("-2147483648"));
|
||||
VERIFY_ARE_EQUAL(INT32_MAX, til::parse_signed<int32_t>("0x7fffffff"));
|
||||
VERIFY_ARE_EQUAL(INT32_MAX, til::parse_signed<int32_t>("2147483647"));
|
||||
VERIFY_ARE_EQUAL(INT32_MAX, til::parse_signed<int32_t>("+2147483647"));
|
||||
VERIFY_ARE_EQUAL(INT64_MAX, til::parse_signed<int64_t>(L"+9223372036854775807"));
|
||||
}
|
||||
|
||||
TEST_METHOD(tolower_ascii)
|
||||
|
||||
Reference in new issue
Block a user