| // 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 "wtf/text/StringToNumber.h" |
| |
| #include "wtf/ASCIICType.h" |
| #include "wtf/dtoa.h" |
| #include "wtf/text/StringImpl.h" |
| |
| 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 const IntegralType integralMax = |
| std::numeric_limits<IntegralType>::max(); |
| static const bool isSigned = std::numeric_limits<IntegralType>::is_signed; |
| const IntegralType maxMultiplier = integralMax / base; |
| |
| IntegralType value = 0; |
| bool isOk = false; |
| bool isNegative = false; |
| |
| if (!data) |
| goto bye; |
| |
| // skip leading whitespace |
| while (length && isSpaceOrNewline(*data)) { |
| --length; |
| ++data; |
| } |
| |
| if (isSigned && length && *data == '-') { |
| --length; |
| ++data; |
| isNegative = true; |
| } else if (length && *data == '+') { |
| --length; |
| ++data; |
| } |
| |
| if (!length || !isCharacterAllowedInBase(*data, base)) |
| goto bye; |
| |
| while (length && isCharacterAllowedInBase(*data, base)) { |
| --length; |
| IntegralType digitValue; |
| CharType c = *data; |
| if (isASCIIDigit(c)) |
| digitValue = c - '0'; |
| else if (c >= 'a') |
| digitValue = c - 'a' + 10; |
| else |
| digitValue = c - 'A' + 10; |
| |
| if (value > maxMultiplier || |
| (value == maxMultiplier && |
| digitValue > (integralMax % base) + isNegative)) |
| goto bye; |
| |
| value = base * value + digitValue; |
| ++data; |
| } |
| |
| #if COMPILER(MSVC) |
| #pragma warning(push, 0) |
| #pragma warning(disable : 4146) |
| #endif |
| |
| if (isNegative) |
| value = -value; |
| |
| #if COMPILER(MSVC) |
| #pragma warning(pop) |
| #endif |
| |
| // skip trailing space |
| while (length && isSpaceOrNewline(*data)) { |
| --length; |
| ++data; |
| } |
| |
| if (!length) |
| isOk = true; |
| bye: |
| if (ok) |
| *ok = isOk; |
| return isOk ? 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 { DisallowTrailingJunk, AllowTrailingJunk }; |
| |
| template <typename CharType, TrailingJunkPolicy policy> |
| static inline double toDoubleType(const CharType* data, |
| size_t length, |
| bool* ok, |
| size_t& parsedLength) { |
| size_t leadingSpacesLength = 0; |
| while (leadingSpacesLength < length && |
| isASCIISpace(data[leadingSpacesLength])) |
| ++leadingSpacesLength; |
| |
| double number = parseDouble(data + leadingSpacesLength, |
| length - leadingSpacesLength, parsedLength); |
| if (!parsedLength) { |
| if (ok) |
| *ok = false; |
| return 0.0; |
| } |
| |
| parsedLength += leadingSpacesLength; |
| if (ok) |
| *ok = policy == AllowTrailingJunk || parsedLength == length; |
| return number; |
| } |
| |
| double charactersToDouble(const LChar* data, size_t length, bool* ok) { |
| size_t parsedLength; |
| return toDoubleType<LChar, DisallowTrailingJunk>(data, length, ok, |
| parsedLength); |
| } |
| |
| double charactersToDouble(const UChar* data, size_t length, bool* ok) { |
| size_t parsedLength; |
| return toDoubleType<UChar, DisallowTrailingJunk>(data, length, ok, |
| parsedLength); |
| } |
| |
| double charactersToDouble(const LChar* data, |
| size_t length, |
| size_t& parsedLength) { |
| return toDoubleType<LChar, AllowTrailingJunk>(data, length, nullptr, |
| parsedLength); |
| } |
| |
| double charactersToDouble(const UChar* data, |
| size_t length, |
| size_t& parsedLength) { |
| return toDoubleType<UChar, AllowTrailingJunk>(data, length, nullptr, |
| parsedLength); |
| } |
| |
| 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 parsedLength; |
| return static_cast<float>(toDoubleType<LChar, DisallowTrailingJunk>( |
| data, length, ok, parsedLength)); |
| } |
| |
| 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 parsedLength; |
| return static_cast<float>(toDoubleType<UChar, DisallowTrailingJunk>( |
| data, length, ok, parsedLength)); |
| } |
| |
| float charactersToFloat(const LChar* data, |
| size_t length, |
| size_t& parsedLength) { |
| // FIXME: This will return ok even when the string fits into a double but |
| // not a float. |
| return static_cast<float>( |
| toDoubleType<LChar, AllowTrailingJunk>(data, length, 0, parsedLength)); |
| } |
| |
| float charactersToFloat(const UChar* data, |
| size_t length, |
| size_t& parsedLength) { |
| // FIXME: This will return ok even when the string fits into a double but |
| // not a float. |
| return static_cast<float>( |
| toDoubleType<UChar, AllowTrailingJunk>(data, length, 0, parsedLength)); |
| } |
| |
| } // namespace WTF |