Chromium Code Reviews| Index: net/base/parse_number.cc |
| diff --git a/net/base/parse_number.cc b/net/base/parse_number.cc |
| index d315bec63a906dba00b5f27e56dd20161ace6266..907878f62b86c6f1cac16b13cc624f4169eee40a 100644 |
| --- a/net/base/parse_number.cc |
| +++ b/net/base/parse_number.cc |
| @@ -4,20 +4,131 @@ |
| #include "net/base/parse_number.h" |
| +#include "base/logging.h" |
| #include "base/strings/string_number_conversions.h" |
| namespace net { |
| -bool ParseNonNegativeDecimalInt(const base::StringPiece& input, int* output) { |
| - if (input.empty() || input[0] > '9' || input[0] < '0') |
| - return false; |
| +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(ParseIntegerError error, ParseIntegerError* optional_error) { |
| + if (optional_error) |
| + *optional_error = error; |
| + return false; |
| +} |
| + |
| +template <typename T> |
| +bool ParseIntegerBase10Helper(const base::StringPiece& input, |
| + ParseInteger policy, |
| + T* output, |
| + ParseIntegerError* optional_error) { |
| + if (input.empty()) |
| + return SetError(ParseIntegerError::FAILED_PARSE, optional_error); |
| + |
| + bool starts_with_negative = input[0] == '-'; |
| + bool starts_with_digit = input[0] >= '0' && input[0] <= '9'; |
| - int result; |
| - if (!base::StringToInt(input, &result)) |
| + // Numbers must start with either a digit or a negative sign |
|
mmenke
2016/03/25 22:10:56
nit: +.
|
| + // |
| + // Reject everything else to prevent the permissive behavior of |
| + // StringToIntXXX() when it comes to accepting a leading '+'. |
| + if (!starts_with_digit) { |
| + if (policy == ParseInteger::DISALLOW_NEGATIVE || !starts_with_negative) |
| + return SetError(ParseIntegerError::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; |
| + } |
| + |
| + // Set an error that distinguishes between parsing/underflow/overflow errors. |
| + // |
| + // Note that base::StringToXXX() functions have a magical API that modify the |
| + // output on failure to indicate whether underflow/overflow happened. |
| + // |
| + // You would think these can be used here, but unfortunately they return those |
| + // magic values in multiple cases making it impossible to distinguish |
| + // underflow/overflow from failed parsing due to leading/trailing whitespace. |
| + // |
| + // So instead look at the number to see if it was valid. |
| + |
| + // Optimization: If the error is not going to be inspected, don't bother |
| + // refining it. |
| + if (!optional_error) |
| return false; |
| - *output = result; |
| - return true; |
| + // 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 ? ParseIntegerError::FAILED_UNDERFLOW |
| + : ParseIntegerError::FAILED_OVERFLOW, |
| + optional_error); |
| + } |
| + |
| + // Otherwise it was a mundane parsing error. |
| + return SetError(ParseIntegerError::FAILED_PARSE, optional_error); |
| +} |
| + |
| +} // namespace |
| + |
| +bool ParseIntegerBase10(const base::StringPiece& input, |
| + ParseInteger policy, |
| + int32_t* output, |
| + ParseIntegerError* optional_error) { |
| + return ParseIntegerBase10Helper(input, policy, output, optional_error); |
| +} |
| + |
| +bool ParseIntegerBase10(const base::StringPiece& input, |
| + ParseInteger policy, |
| + int64_t* output, |
| + ParseIntegerError* optional_error) { |
| + return ParseIntegerBase10Helper(input, policy, output, optional_error); |
| +} |
| + |
| +bool ParseUnsignedIntegerBase10(const base::StringPiece& input, |
| + uint32_t* output, |
| + ParseIntegerError* optional_error) { |
| + return ParseIntegerBase10Helper(input, ParseInteger::DISALLOW_NEGATIVE, |
| + output, optional_error); |
| +} |
| + |
| +bool ParseUnsignedIntegerBase10(const base::StringPiece& input, |
| + uint64_t* output, |
| + ParseIntegerError* optional_error) { |
| + return ParseIntegerBase10Helper(input, ParseInteger::DISALLOW_NEGATIVE, |
| + output, optional_error); |
| } |
| } // namespace net |