// 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 "net/base/parse_number.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" namespace net { namespace { // The string to number conversion functions in //base include the type in the // name (like StringToInt64()). The following wrapper methods create a // consistent interface to StringToXXX() that calls the appropriate //base // version. This simplifies writing generic code with a template. bool StringToNumber(const base::StringPiece& input, int32_t* output) { // This assumes ints are 32-bits (will fail compile if that ever changes). return base::StringToInt(input, output); } bool StringToNumber(const base::StringPiece& input, uint32_t* output) { // This assumes ints are 32-bits (will fail compile if that ever changes). return base::StringToUint(input, output); } bool StringToNumber(const base::StringPiece& input, int64_t* output) { return base::StringToInt64(input, output); } bool StringToNumber(const base::StringPiece& input, uint64_t* output) { return base::StringToUint64(input, output); } bool SetError(ParseIntError error, ParseIntError* optional_error) { if (optional_error) *optional_error = error; return false; } template bool ParseIntHelper(const base::StringPiece& input, ParseIntFormat format, T* output, ParseIntError* optional_error) { // Check that the input matches the format before calling StringToNumber(). // Numbers must start with either a digit or a negative sign. if (input.empty()) return SetError(ParseIntError::FAILED_PARSE, optional_error); bool starts_with_negative = input[0] == '-'; bool starts_with_digit = base::IsAsciiDigit(input[0]); if (!starts_with_digit) { if (format == ParseIntFormat::NON_NEGATIVE || !starts_with_negative) return SetError(ParseIntError::FAILED_PARSE, optional_error); } // Dispatch to the appropriate flavor of base::StringToXXX() by calling one of // the type-specific overloads. T result; if (StringToNumber(input, &result)) { *output = result; return true; } // Optimization: If the error is not going to be inspected, don't bother // calculating it. if (!optional_error) return false; // Set an error that distinguishes between parsing/underflow/overflow errors. // // Note that the output set by base::StringToXXX() on failure cannot be used // as it has ambiguity with parse errors. // Strip any leading negative sign off the number. base::StringPiece numeric_portion = starts_with_negative ? input.substr(1) : input; // Test if |numeric_portion| is a valid non-negative integer. if (!numeric_portion.empty() && numeric_portion.find_first_not_of("0123456789") == std::string::npos) { // If it was, the failure must have been due to underflow/overflow. return SetError(starts_with_negative ? ParseIntError::FAILED_UNDERFLOW : ParseIntError::FAILED_OVERFLOW, optional_error); } // Otherwise it was a mundane parsing error. return SetError(ParseIntError::FAILED_PARSE, optional_error); } } // namespace bool ParseInt32(const base::StringPiece& input, ParseIntFormat format, int32_t* output, ParseIntError* optional_error) { return ParseIntHelper(input, format, output, optional_error); } bool ParseInt64(const base::StringPiece& input, ParseIntFormat format, int64_t* output, ParseIntError* optional_error) { return ParseIntHelper(input, format, output, optional_error); } bool ParseUint32(const base::StringPiece& input, uint32_t* output, ParseIntError* optional_error) { return ParseIntHelper(input, ParseIntFormat::NON_NEGATIVE, output, optional_error); } bool ParseUint64(const base::StringPiece& input, uint64_t* output, ParseIntError* optional_error) { return ParseIntHelper(input, ParseIntFormat::NON_NEGATIVE, output, optional_error); } } // namespace net