1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// Copyright 2016 The Chromium Authors
// 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/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(std::string_view input, int32_t* output) {
// This assumes ints are 32-bits (will fail compile if that ever changes).
return base::StringToInt(input, output);
}
bool StringToNumber(std::string_view input, uint32_t* output) {
// This assumes ints are 32-bits (will fail compile if that ever changes).
return base::StringToUint(input, output);
}
bool StringToNumber(std::string_view input, int64_t* output) {
return base::StringToInt64(input, output);
}
bool StringToNumber(std::string_view 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 <typename T>
bool ParseIntHelper(std::string_view 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 is_non_negative = (format == ParseIntFormat::NON_NEGATIVE ||
format == ParseIntFormat::STRICT_NON_NEGATIVE);
bool is_strict = (format == ParseIntFormat::STRICT_NON_NEGATIVE ||
format == ParseIntFormat::STRICT_OPTIONALLY_NEGATIVE);
bool starts_with_negative = input[0] == '-';
bool starts_with_digit = base::IsAsciiDigit(input[0]);
if (!starts_with_digit) {
// The length() < 2 check catches "-". It's needed here to prevent reading
// beyond the end of the array on line 70.
if (is_non_negative || !starts_with_negative || input.length() < 2) {
return SetError(ParseIntError::FAILED_PARSE, optional_error);
}
// If the first digit after the negative is a 0, then either the number is
// -0 or it has an unnecessary leading 0. Either way, it violates the
// requirements of being "strict", so fail if strict.
if (is_strict && input[1] == '0') {
return SetError(ParseIntError::FAILED_PARSE, optional_error);
}
} else {
// Fail if the first character is a zero and the string has more than 1
// digit.
if (is_strict && input[0] == '0' && input.length() > 1) {
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.
std::string_view 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(std::string_view input,
ParseIntFormat format,
int32_t* output,
ParseIntError* optional_error) {
return ParseIntHelper(input, format, output, optional_error);
}
bool ParseInt64(std::string_view input,
ParseIntFormat format,
int64_t* output,
ParseIntError* optional_error) {
return ParseIntHelper(input, format, output, optional_error);
}
bool ParseUint32(std::string_view input,
ParseIntFormat format,
uint32_t* output,
ParseIntError* optional_error) {
CHECK(format == ParseIntFormat::NON_NEGATIVE ||
format == ParseIntFormat::STRICT_NON_NEGATIVE);
return ParseIntHelper(input, format, output, optional_error);
}
bool ParseUint64(std::string_view input,
ParseIntFormat format,
uint64_t* output,
ParseIntError* optional_error) {
CHECK(format == ParseIntFormat::NON_NEGATIVE ||
format == ParseIntFormat::STRICT_NON_NEGATIVE);
return ParseIntHelper(input, format, output, optional_error);
}
} // namespace net
|