mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:47:34 +00:00
LibC: Implement strtoull correctly
This fixes the behavior for several inputs: - '-0' (shouldn't work but was accepted) - '+3' (shouldn't work but was accepted) - '13835058055282163712' (should work but returned 9223372036854775807 with errno=ERANGE)
This commit is contained in:
parent
4bff3defa8
commit
71fd752289
1 changed files with 68 additions and 6 deletions
|
@ -107,7 +107,7 @@ public:
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (digit >= m_base)
|
if (static_cast<T>(digit) >= m_base)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return digit;
|
return digit;
|
||||||
|
@ -161,6 +161,7 @@ private:
|
||||||
|
|
||||||
typedef NumParser<int, INT_MIN, INT_MAX> IntParser;
|
typedef NumParser<int, INT_MIN, INT_MAX> IntParser;
|
||||||
typedef NumParser<long long, LONG_LONG_MIN, LONG_LONG_MAX> LongLongParser;
|
typedef NumParser<long long, LONG_LONG_MIN, LONG_LONG_MAX> LongLongParser;
|
||||||
|
typedef NumParser<unsigned long long, 0ULL, ULONG_LONG_MAX> ULongLongParser;
|
||||||
|
|
||||||
static bool is_either(char* str, int offset, char lower, char upper)
|
static bool is_either(char* str, int offset, char lower, char upper)
|
||||||
{
|
{
|
||||||
|
@ -942,11 +943,72 @@ long long strtoll(const char* str, char** endptr, int base)
|
||||||
|
|
||||||
unsigned long long strtoull(const char* str, char** endptr, int base)
|
unsigned long long strtoull(const char* str, char** endptr, int base)
|
||||||
{
|
{
|
||||||
auto value = strtoll(str, endptr, base);
|
// Parse spaces and sign
|
||||||
// TODO: strtoull should not accept a sign character at all,
|
char* parse_ptr = const_cast<char*>(str);
|
||||||
// not in `-0` and not even `+3`.
|
strtons(parse_ptr, &parse_ptr);
|
||||||
ASSERT(value >= 0);
|
|
||||||
return value;
|
// Parse base
|
||||||
|
if (base == 0) {
|
||||||
|
if (*parse_ptr == '0') {
|
||||||
|
parse_ptr += 1;
|
||||||
|
if (*parse_ptr == 'x' || *parse_ptr == 'X') {
|
||||||
|
base = 16;
|
||||||
|
parse_ptr += 2;
|
||||||
|
} else {
|
||||||
|
base = 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse actual digits.
|
||||||
|
ULongLongParser digits { Sign::Positive, base };
|
||||||
|
bool digits_usable = false;
|
||||||
|
bool should_continue = true;
|
||||||
|
bool overflow = false;
|
||||||
|
do {
|
||||||
|
bool is_a_digit;
|
||||||
|
if (overflow) {
|
||||||
|
is_a_digit = digits.parse_digit(*parse_ptr) >= 0;
|
||||||
|
} else {
|
||||||
|
DigitConsumeDecision decision = digits.consume(*parse_ptr);
|
||||||
|
switch (decision) {
|
||||||
|
case DigitConsumeDecision::Consumed:
|
||||||
|
is_a_digit = true;
|
||||||
|
// The very first actual digit must pass here:
|
||||||
|
digits_usable = true;
|
||||||
|
break;
|
||||||
|
case DigitConsumeDecision::PosOverflow: // fall-through
|
||||||
|
case DigitConsumeDecision::NegOverflow:
|
||||||
|
is_a_digit = true;
|
||||||
|
overflow = true;
|
||||||
|
break;
|
||||||
|
case DigitConsumeDecision::Invalid:
|
||||||
|
is_a_digit = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
should_continue = is_a_digit;
|
||||||
|
parse_ptr += should_continue;
|
||||||
|
} while (should_continue);
|
||||||
|
|
||||||
|
if (!digits_usable) {
|
||||||
|
// No actual number value available.
|
||||||
|
if (endptr)
|
||||||
|
*endptr = const_cast<char*>(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overflow) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return LONG_LONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return digits.number();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serenity's PRNG is not cryptographically secure. Do not rely on this for
|
// Serenity's PRNG is not cryptographically secure. Do not rely on this for
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue