| // Copyright 2020 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. |
| |
| #ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_ |
| #define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_ |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <wctype.h> |
| |
| #include <limits> |
| |
| #include "polyfills/base/check.h" |
| #include "polyfills/base/logging.h" |
| #include "base/numerics/safe_math.h" |
| #include "base/strings/string_util.h" |
| #include "base/third_party/double_conversion/double-conversion/double-conversion.h" |
| #include "absl/types/optional.h" |
| |
| namespace gurl_base { |
| |
| namespace internal { |
| |
| template <typename STR, typename INT> |
| static STR IntToStringT(INT value) { |
| // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. |
| // So round up to allocate 3 output characters per byte, plus 1 for '-'. |
| const size_t kOutputBufSize = |
| 3 * sizeof(INT) + std::numeric_limits<INT>::is_signed; |
| |
| // Create the string in a temporary buffer, write it back to front, and |
| // then return the substr of what we ended up using. |
| using CHR = typename STR::value_type; |
| CHR outbuf[kOutputBufSize]; |
| |
| // The ValueOrDie call below can never fail, because UnsignedAbs is valid |
| // for all valid inputs. |
| std::make_unsigned_t<INT> res = |
| CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie(); |
| |
| CHR* end = outbuf + kOutputBufSize; |
| CHR* i = end; |
| do { |
| --i; |
| GURL_DCHECK(i != outbuf); |
| *i = static_cast<CHR>((res % 10) + '0'); |
| res /= 10; |
| } while (res != 0); |
| if (IsValueNegative(value)) { |
| --i; |
| GURL_DCHECK(i != outbuf); |
| *i = static_cast<CHR>('-'); |
| } |
| return STR(i, end); |
| } |
| |
| // Utility to convert a character to a digit in a given base |
| template <int BASE, typename CHAR> |
| absl::optional<uint8_t> CharToDigit(CHAR c) { |
| static_assert(1 <= BASE && BASE <= 36, "BASE needs to be in [1, 36]"); |
| if (c >= '0' && c < '0' + std::min(BASE, 10)) |
| return c - '0'; |
| |
| if (c >= 'a' && c < 'a' + BASE - 10) |
| return c - 'a' + 10; |
| |
| if (c >= 'A' && c < 'A' + BASE - 10) |
| return c - 'A' + 10; |
| |
| return absl::nullopt; |
| } |
| |
| // There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it |
| // is locale independent, whereas the functions we are replacing were |
| // locale-dependent. TBD what is desired, but for the moment let's not |
| // introduce a change in behaviour. |
| template <typename CHAR> |
| class WhitespaceHelper {}; |
| |
| template <> |
| class WhitespaceHelper<char> { |
| public: |
| static bool Invoke(char c) { |
| return 0 != isspace(static_cast<unsigned char>(c)); |
| } |
| }; |
| |
| template <> |
| class WhitespaceHelper<char16_t> { |
| public: |
| static bool Invoke(char16_t c) { return 0 != iswspace(c); } |
| }; |
| |
| template <typename CHAR> |
| bool LocalIsWhitespace(CHAR c) { |
| return WhitespaceHelper<CHAR>::Invoke(c); |
| } |
| |
| template <typename Number, int kBase> |
| class StringToNumberParser { |
| public: |
| struct Result { |
| Number value = 0; |
| bool valid = false; |
| }; |
| |
| static constexpr Number kMin = std::numeric_limits<Number>::min(); |
| static constexpr Number kMax = std::numeric_limits<Number>::max(); |
| |
| // Sign provides: |
| // - a static function, CheckBounds, that determines whether the next digit |
| // causes an overflow/underflow |
| // - a static function, Increment, that appends the next digit appropriately |
| // according to the sign of the number being parsed. |
| template <typename Sign> |
| class Base { |
| public: |
| template <typename Iter> |
| static Result Invoke(Iter begin, Iter end) { |
| Number value = 0; |
| |
| if (begin == end) { |
| return {value, false}; |
| } |
| |
| // Note: no performance difference was found when using template |
| // specialization to remove this check in bases other than 16 |
| if (kBase == 16 && end - begin > 2 && *begin == '0' && |
| (*(begin + 1) == 'x' || *(begin + 1) == 'X')) { |
| begin += 2; |
| } |
| |
| for (Iter current = begin; current != end; ++current) { |
| absl::optional<uint8_t> new_digit = CharToDigit<kBase>(*current); |
| |
| if (!new_digit) { |
| return {value, false}; |
| } |
| |
| if (current != begin) { |
| Result result = Sign::CheckBounds(value, *new_digit); |
| if (!result.valid) |
| return result; |
| |
| value *= kBase; |
| } |
| |
| value = Sign::Increment(value, *new_digit); |
| } |
| return {value, true}; |
| } |
| }; |
| |
| class Positive : public Base<Positive> { |
| public: |
| static Result CheckBounds(Number value, uint8_t new_digit) { |
| if (value > static_cast<Number>(kMax / kBase) || |
| (value == static_cast<Number>(kMax / kBase) && |
| new_digit > kMax % kBase)) { |
| return {kMax, false}; |
| } |
| return {value, true}; |
| } |
| static Number Increment(Number lhs, uint8_t rhs) { return lhs + rhs; } |
| }; |
| |
| class Negative : public Base<Negative> { |
| public: |
| static Result CheckBounds(Number value, uint8_t new_digit) { |
| if (value < kMin / kBase || |
| (value == kMin / kBase && new_digit > 0 - kMin % kBase)) { |
| return {kMin, false}; |
| } |
| return {value, true}; |
| } |
| static Number Increment(Number lhs, uint8_t rhs) { return lhs - rhs; } |
| }; |
| }; |
| |
| template <typename Number, int kBase, typename CharT> |
| auto StringToNumber(BasicStringPiece<CharT> input) { |
| using Parser = StringToNumberParser<Number, kBase>; |
| using Result = typename Parser::Result; |
| |
| bool has_leading_whitespace = false; |
| auto begin = input.begin(); |
| auto end = input.end(); |
| |
| while (begin != end && LocalIsWhitespace(*begin)) { |
| has_leading_whitespace = true; |
| ++begin; |
| } |
| |
| if (begin != end && *begin == '-') { |
| if (!std::numeric_limits<Number>::is_signed) { |
| return Result{0, false}; |
| } |
| |
| Result result = Parser::Negative::Invoke(begin + 1, end); |
| result.valid &= !has_leading_whitespace; |
| return result; |
| } |
| |
| if (begin != end && *begin == '+') { |
| ++begin; |
| } |
| |
| Result result = Parser::Positive::Invoke(begin, end); |
| result.valid &= !has_leading_whitespace; |
| return result; |
| } |
| |
| template <typename T, typename VALUE, typename CharT = typename T::value_type> |
| bool StringToIntImpl(T input, VALUE& output) { |
| auto result = StringToNumber<VALUE, 10, CharT>(input); |
| output = result.value; |
| return result.valid; |
| } |
| |
| template <typename T, typename VALUE, typename CharT = typename T::value_type> |
| bool HexStringToIntImpl(T input, VALUE& output) { |
| auto result = StringToNumber<VALUE, 16, CharT>(input); |
| output = result.value; |
| return result.valid; |
| } |
| |
| static const double_conversion::DoubleToStringConverter* |
| GetDoubleToStringConverter() { |
| static double_conversion::DoubleToStringConverter converter( |
| double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, |
| nullptr, nullptr, 'e', -6, 12, 0, 0); |
| return &converter; |
| } |
| |
| // Converts a given (data, size) pair to a desired string type. For |
| // performance reasons, this dispatches to a different constructor if the |
| // passed-in data matches the string's value_type. |
| template <typename StringT> |
| StringT ToString(const typename StringT::value_type* data, size_t size) { |
| return StringT(data, size); |
| } |
| |
| template <typename StringT, typename CharT> |
| StringT ToString(const CharT* data, size_t size) { |
| return StringT(data, data + size); |
| } |
| |
| template <typename StringT> |
| StringT DoubleToStringT(double value) { |
| char buffer[32]; |
| double_conversion::StringBuilder builder(buffer, sizeof(buffer)); |
| GetDoubleToStringConverter()->ToShortest(value, &builder); |
| return ToString<StringT>(buffer, builder.position()); |
| } |
| |
| template <typename STRING, typename CHAR> |
| bool StringToDoubleImpl(STRING input, const CHAR* data, double& output) { |
| static double_conversion::StringToDoubleConverter converter( |
| double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | |
| double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK, |
| 0.0, 0, nullptr, nullptr); |
| |
| int processed_characters_count; |
| output = |
| converter.StringToDouble(data, input.size(), &processed_characters_count); |
| |
| // Cases to return false: |
| // - If the input string is empty, there was nothing to parse. |
| // - If the value saturated to HUGE_VAL. |
| // - If the entire string was not processed, there are either characters |
| // remaining in the string after a parsed number, or the string does not |
| // begin with a parseable number. |
| // - If the first character is a space, there was leading whitespace |
| return !input.empty() && output != HUGE_VAL && output != -HUGE_VAL && |
| static_cast<size_t>(processed_characters_count) == input.size() && |
| !IsUnicodeWhitespace(input[0]); |
| } |
| |
| template <typename OutIter> |
| static bool HexStringToByteContainer(StringPiece input, OutIter output) { |
| size_t count = input.size(); |
| if (count == 0 || (count % 2) != 0) |
| return false; |
| for (uintptr_t i = 0; i < count / 2; ++i) { |
| // most significant 4 bits |
| absl::optional<uint8_t> msb = CharToDigit<16>(input[i * 2]); |
| // least significant 4 bits |
| absl::optional<uint8_t> lsb = CharToDigit<16>(input[i * 2 + 1]); |
| if (!msb || !lsb) { |
| return false; |
| } |
| *(output++) = (*msb << 4) | *lsb; |
| } |
| return true; |
| } |
| |
| } // namespace internal |
| |
| } // namespace base |
| |
| #endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_ |