blob: cb6e05ca332ca2b5bebd1987cea3e00bb17f9be6 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/wtf/text/StringToNumber.h"
#include "platform/wtf/ASCIICType.h"
#include "platform/wtf/dtoa.h"
#include "platform/wtf/text/StringImpl.h"
#include <type_traits>
namespace WTF {
static bool IsCharacterAllowedInBase(UChar c, int base) {
if (c > 0x7F)
return false;
if (IsASCIIDigit(c))
return c - '0' < base;
if (IsASCIIAlpha(c)) {
if (base > 36)
base = 36;
return (c >= 'a' && c < 'a' + base - 10) ||
(c >= 'A' && c < 'A' + base - 10);
}
return false;
}
template <typename IntegralType, typename CharType>
static inline IntegralType ToIntegralType(const CharType* data,
size_t length,
bool* ok,
int base) {
static_assert(std::is_integral<IntegralType>::value,
"IntegralType must be an integral type.");
static constexpr IntegralType kIntegralMax =
std::numeric_limits<IntegralType>::max();
static constexpr IntegralType kIntegralMin =
std::numeric_limits<IntegralType>::min();
static constexpr bool kIsSigned =
std::numeric_limits<IntegralType>::is_signed;
IntegralType value = 0;
bool is_ok = false;
bool is_negative = false;
if (!data)
goto bye;
// skip leading whitespace
while (length && IsSpaceOrNewline(*data)) {
--length;
++data;
}
if (kIsSigned && length && *data == '-') {
--length;
++data;
is_negative = true;
} else if (length && *data == '+') {
--length;
++data;
}
if (!length || !IsCharacterAllowedInBase(*data, base))
goto bye;
while (length && IsCharacterAllowedInBase(*data, base)) {
--length;
IntegralType digit_value;
CharType c = *data;
if (IsASCIIDigit(c))
digit_value = c - '0';
else if (c >= 'a')
digit_value = c - 'a' + 10;
else
digit_value = c - 'A' + 10;
bool overflow;
if (is_negative) {
// Overflow condition:
// value * base - digitValue < integralMin
// <=> value < (integralMin + digitValue) / base
// We must be careful of rounding errors here, but the default rounding
// mode (round to zero) works well, so we can use this formula as-is.
overflow = value < (kIntegralMin + digit_value) / base;
} else {
// Overflow condition:
// value * base + digitValue > integralMax
// <=> value > (integralMax + digitValue) / base
// Ditto regarding rounding errors.
overflow = value > (kIntegralMax - digit_value) / base;
}
if (overflow)
goto bye;
if (is_negative)
value = base * value - digit_value;
else
value = base * value + digit_value;
++data;
}
// skip trailing space
while (length && IsSpaceOrNewline(*data)) {
--length;
++data;
}
if (!length)
is_ok = true;
bye:
if (ok)
*ok = is_ok;
return is_ok ? value : 0;
}
template <typename CharType>
static unsigned LengthOfCharactersAsInteger(const CharType* data,
size_t length) {
size_t i = 0;
// Allow leading spaces.
for (; i != length; ++i) {
if (!IsSpaceOrNewline(data[i]))
break;
}
// Allow sign.
if (i != length && (data[i] == '+' || data[i] == '-'))
++i;
// Allow digits.
for (; i != length; ++i) {
if (!IsASCIIDigit(data[i]))
break;
}
return i;
}
int CharactersToIntStrict(const LChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<int, LChar>(data, length, ok, base);
}
int CharactersToIntStrict(const UChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<int, UChar>(data, length, ok, base);
}
unsigned CharactersToUIntStrict(const LChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<unsigned, LChar>(data, length, ok, base);
}
unsigned CharactersToUIntStrict(const UChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<unsigned, UChar>(data, length, ok, base);
}
int64_t CharactersToInt64Strict(const LChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<int64_t, LChar>(data, length, ok, base);
}
int64_t CharactersToInt64Strict(const UChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<int64_t, UChar>(data, length, ok, base);
}
uint64_t CharactersToUInt64Strict(const LChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<uint64_t, LChar>(data, length, ok, base);
}
uint64_t CharactersToUInt64Strict(const UChar* data,
size_t length,
bool* ok,
int base) {
return ToIntegralType<uint64_t, UChar>(data, length, ok, base);
}
int CharactersToInt(const LChar* data, size_t length, bool* ok) {
return ToIntegralType<int, LChar>(
data, LengthOfCharactersAsInteger<LChar>(data, length), ok, 10);
}
int CharactersToInt(const UChar* data, size_t length, bool* ok) {
return ToIntegralType<int, UChar>(
data, LengthOfCharactersAsInteger(data, length), ok, 10);
}
unsigned CharactersToUInt(const LChar* data, size_t length, bool* ok) {
return ToIntegralType<unsigned, LChar>(
data, LengthOfCharactersAsInteger<LChar>(data, length), ok, 10);
}
unsigned CharactersToUInt(const UChar* data, size_t length, bool* ok) {
return ToIntegralType<unsigned, UChar>(
data, LengthOfCharactersAsInteger<UChar>(data, length), ok, 10);
}
int64_t CharactersToInt64(const LChar* data, size_t length, bool* ok) {
return ToIntegralType<int64_t, LChar>(
data, LengthOfCharactersAsInteger<LChar>(data, length), ok, 10);
}
int64_t CharactersToInt64(const UChar* data, size_t length, bool* ok) {
return ToIntegralType<int64_t, UChar>(
data, LengthOfCharactersAsInteger<UChar>(data, length), ok, 10);
}
uint64_t CharactersToUInt64(const LChar* data, size_t length, bool* ok) {
return ToIntegralType<uint64_t, LChar>(
data, LengthOfCharactersAsInteger<LChar>(data, length), ok, 10);
}
uint64_t CharactersToUInt64(const UChar* data, size_t length, bool* ok) {
return ToIntegralType<uint64_t, UChar>(
data, LengthOfCharactersAsInteger<UChar>(data, length), ok, 10);
}
enum TrailingJunkPolicy { kDisallowTrailingJunk, kAllowTrailingJunk };
template <typename CharType, TrailingJunkPolicy policy>
static inline double ToDoubleType(const CharType* data,
size_t length,
bool* ok,
size_t& parsed_length) {
size_t leading_spaces_length = 0;
while (leading_spaces_length < length &&
IsASCIISpace(data[leading_spaces_length]))
++leading_spaces_length;
double number = ParseDouble(data + leading_spaces_length,
length - leading_spaces_length, parsed_length);
if (!parsed_length) {
if (ok)
*ok = false;
return 0.0;
}
parsed_length += leading_spaces_length;
if (ok)
*ok = policy == kAllowTrailingJunk || parsed_length == length;
return number;
}
double CharactersToDouble(const LChar* data, size_t length, bool* ok) {
size_t parsed_length;
return ToDoubleType<LChar, kDisallowTrailingJunk>(data, length, ok,
parsed_length);
}
double CharactersToDouble(const UChar* data, size_t length, bool* ok) {
size_t parsed_length;
return ToDoubleType<UChar, kDisallowTrailingJunk>(data, length, ok,
parsed_length);
}
double CharactersToDouble(const LChar* data,
size_t length,
size_t& parsed_length) {
return ToDoubleType<LChar, kAllowTrailingJunk>(data, length, nullptr,
parsed_length);
}
double CharactersToDouble(const UChar* data,
size_t length,
size_t& parsed_length) {
return ToDoubleType<UChar, kAllowTrailingJunk>(data, length, nullptr,
parsed_length);
}
float CharactersToFloat(const LChar* data, size_t length, bool* ok) {
// FIXME: This will return ok even when the string fits into a double but
// not a float.
size_t parsed_length;
return static_cast<float>(ToDoubleType<LChar, kDisallowTrailingJunk>(
data, length, ok, parsed_length));
}
float CharactersToFloat(const UChar* data, size_t length, bool* ok) {
// FIXME: This will return ok even when the string fits into a double but
// not a float.
size_t parsed_length;
return static_cast<float>(ToDoubleType<UChar, kDisallowTrailingJunk>(
data, length, ok, parsed_length));
}
float CharactersToFloat(const LChar* data,
size_t length,
size_t& parsed_length) {
// FIXME: This will return ok even when the string fits into a double but
// not a float.
return static_cast<float>(
ToDoubleType<LChar, kAllowTrailingJunk>(data, length, 0, parsed_length));
}
float CharactersToFloat(const UChar* data,
size_t length,
size_t& parsed_length) {
// FIXME: This will return ok even when the string fits into a double but
// not a float.
return static_cast<float>(
ToDoubleType<UChar, kAllowTrailingJunk>(data, length, 0, parsed_length));
}
} // namespace WTF