convert IPAddress::AssignFromIPLiteral to constexpr
As part of making the DohProviderEntry list constinit, this commit converts AssignFromIPLiteral methods to constexpr as an intermediate step. Bug: 382015338 Change-Id: Ie8dc10b2ab9d398378c9a546dc3164e3aa7f94dd Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6218424 Reviewed-by: Adam Rice <ricea@chromium.org> Reviewed-by: Hayato Ito <hayato@chromium.org> Commit-Queue: Mayur Patil <patilmayur@microsoft.com> Cr-Commit-Position: refs/heads/main@{#1419691}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
6c2d66fc4a
commit
bda52c4ecf
chrome/browser/ash/printing
net/base
url
@ -77,7 +77,6 @@ PrinterDetector::DetectedPrinter MakeExpectedPrinter(const std::string& name,
|
||||
ServiceType service_type) {
|
||||
PrinterDetector::DetectedPrinter detected;
|
||||
chromeos::Printer& printer = detected.printer;
|
||||
net::IPAddress ip_address = GetIPAddressFor(name);
|
||||
int port = GetPortFor(name);
|
||||
std::string scheme;
|
||||
std::string rp = base::StrCat({name, "_rp"});
|
||||
|
@ -17,14 +17,12 @@
|
||||
#include "base/debug/crash_logging.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/trace_event/memory_usage_estimator.h"
|
||||
#include "base/values.h"
|
||||
#include "net/base/parse_number.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/url_canon_ip.h"
|
||||
|
||||
namespace net {
|
||||
namespace {
|
||||
@ -159,33 +157,8 @@ bool IsPubliclyRoutableIPv6(const IPAddressBytes& ip_address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseIPLiteralToBytes(std::string_view ip_literal, IPAddressBytes* bytes) {
|
||||
// |ip_literal| could be either an IPv4 or an IPv6 literal. If it contains
|
||||
// a colon however, it must be an IPv6 address.
|
||||
if (ip_literal.find(':') != std::string_view::npos) {
|
||||
// GURL expects IPv6 hostnames to be surrounded with brackets.
|
||||
std::string host_brackets = base::StrCat({"[", ip_literal, "]"});
|
||||
url::Component host_comp(0, host_brackets.size());
|
||||
|
||||
// Try parsing the hostname as an IPv6 literal.
|
||||
bytes->Resize(16); // 128 bits.
|
||||
return url::IPv6AddressToNumber(host_brackets.data(), host_comp,
|
||||
bytes->data());
|
||||
}
|
||||
|
||||
// Otherwise the string is an IPv4 address.
|
||||
bytes->Resize(4); // 32 bits.
|
||||
url::Component host_comp(0, ip_literal.size());
|
||||
int num_components;
|
||||
url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
|
||||
ip_literal.data(), host_comp, bytes->data(), &num_components);
|
||||
return family == url::CanonHostInfo::IPV4;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IPAddressBytes::~IPAddressBytes() = default;
|
||||
|
||||
bool IPAddressBytes::operator<(const IPAddressBytes& other) const {
|
||||
if (size_ == other.size_)
|
||||
return std::lexicographical_compare(begin(), end(), other.begin(),
|
||||
@ -230,21 +203,6 @@ std::optional<IPAddress> IPAddress::FromIPLiteral(std::string_view ip_literal) {
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
IPAddress::~IPAddress() = default;
|
||||
|
||||
bool IPAddress::IsIPv4() const {
|
||||
return ip_address_.size() == kIPv4AddressSize;
|
||||
}
|
||||
|
||||
bool IPAddress::IsIPv6() const {
|
||||
return ip_address_.size() == kIPv6AddressSize;
|
||||
}
|
||||
|
||||
bool IPAddress::IsValid() const {
|
||||
return IsIPv4() || IsIPv6();
|
||||
}
|
||||
|
||||
bool IPAddress::IsPubliclyRoutable() const {
|
||||
if (IsIPv4()) {
|
||||
return IsPubliclyRoutableIPv4(ip_address_);
|
||||
@ -305,13 +263,6 @@ bool IPAddress::IsUniqueLocalIPv6() const {
|
||||
return IsIPv6() && ((ip_address_[0] & 0xFE) == 0xFC);
|
||||
}
|
||||
|
||||
bool IPAddress::AssignFromIPLiteral(std::string_view ip_literal) {
|
||||
bool success = ParseIPLiteralToBytes(ip_literal, &ip_address_);
|
||||
if (!success)
|
||||
ip_address_.Resize(0);
|
||||
return success;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> IPAddress::CopyBytesToVector() const {
|
||||
return std::vector<uint8_t>(ip_address_.begin(), ip_address_.end());
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "base/containers/span.h"
|
||||
#include "base/values.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "url/url_canon_ip.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@ -28,12 +29,12 @@ namespace net {
|
||||
// IPAddressBytes uses a fixed size array.
|
||||
class NET_EXPORT IPAddressBytes {
|
||||
public:
|
||||
constexpr IPAddressBytes() : size_(0) {}
|
||||
constexpr IPAddressBytes() : bytes_{}, size_(0) {}
|
||||
constexpr explicit IPAddressBytes(base::span<const uint8_t> data) {
|
||||
Assign(data);
|
||||
}
|
||||
constexpr IPAddressBytes(const IPAddressBytes& other) = default;
|
||||
~IPAddressBytes();
|
||||
constexpr ~IPAddressBytes() = default;
|
||||
|
||||
// Copies elements from |data| into this object.
|
||||
constexpr void Assign(base::span<const uint8_t> data) {
|
||||
@ -43,42 +44,42 @@ class NET_EXPORT IPAddressBytes {
|
||||
}
|
||||
|
||||
// Returns the number of elements in the underlying array.
|
||||
size_t size() const { return size_; }
|
||||
constexpr size_t size() const { return size_; }
|
||||
|
||||
// Sets the size to be |size|. Does not actually change the size
|
||||
// of the underlying array or zero-initialize the bytes.
|
||||
void Resize(size_t size) {
|
||||
constexpr void Resize(size_t size) {
|
||||
DCHECK_LE(size, 16u);
|
||||
size_ = static_cast<uint8_t>(size);
|
||||
}
|
||||
|
||||
// Returns true if the underlying array is empty.
|
||||
bool empty() const { return size_ == 0; }
|
||||
constexpr bool empty() const { return size_ == 0; }
|
||||
|
||||
// Returns a pointer to the underlying array of bytes.
|
||||
const uint8_t* data() const { return bytes_.data(); }
|
||||
uint8_t* data() { return bytes_.data(); }
|
||||
constexpr const uint8_t* data() const { return bytes_.data(); }
|
||||
constexpr uint8_t* data() { return bytes_.data(); }
|
||||
|
||||
// Returns a pointer to the first element.
|
||||
const uint8_t* begin() const { return data(); }
|
||||
uint8_t* begin() { return data(); }
|
||||
constexpr const uint8_t* begin() const { return data(); }
|
||||
constexpr uint8_t* begin() { return data(); }
|
||||
|
||||
// Returns a pointer past the last element.
|
||||
const uint8_t* end() const { return UNSAFE_TODO(data() + size_); }
|
||||
uint8_t* end() { return UNSAFE_TODO(data() + size_); }
|
||||
constexpr const uint8_t* end() const { return UNSAFE_TODO(data() + size_); }
|
||||
constexpr uint8_t* end() { return UNSAFE_TODO(data() + size_); }
|
||||
|
||||
// Returns a reference to the last element.
|
||||
uint8_t& back() {
|
||||
constexpr uint8_t& back() {
|
||||
DCHECK(!empty());
|
||||
return bytes_[size_ - 1];
|
||||
}
|
||||
const uint8_t& back() const {
|
||||
constexpr const uint8_t& back() const {
|
||||
DCHECK(!empty());
|
||||
return bytes_[size_ - 1];
|
||||
}
|
||||
|
||||
// Appends |val| to the end and increments the size.
|
||||
void push_back(uint8_t val) {
|
||||
constexpr void push_back(uint8_t val) {
|
||||
DCHECK_GT(16, size_);
|
||||
bytes_[size_++] = val;
|
||||
}
|
||||
@ -87,11 +88,11 @@ class NET_EXPORT IPAddressBytes {
|
||||
void Append(base::span<const uint8_t> data);
|
||||
|
||||
// Returns a reference to the byte at index |pos|.
|
||||
uint8_t& operator[](size_t pos) {
|
||||
constexpr uint8_t& operator[](size_t pos) {
|
||||
DCHECK_LT(pos, size_);
|
||||
return bytes_[pos];
|
||||
}
|
||||
const uint8_t& operator[](size_t pos) const {
|
||||
constexpr const uint8_t& operator[](size_t pos) const {
|
||||
DCHECK_LT(pos, size_);
|
||||
return bytes_[pos];
|
||||
}
|
||||
@ -111,6 +112,39 @@ class NET_EXPORT IPAddressBytes {
|
||||
uint8_t size_;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
constexpr bool ParseIPLiteralToBytes(std::string_view ip_literal,
|
||||
IPAddressBytes* bytes) {
|
||||
// |ip_literal| could be either an IPv4 or an IPv6 literal. If it contains
|
||||
// a colon however, it must be an IPv6 address.
|
||||
if (ip_literal.find(':') != std::string_view::npos) {
|
||||
// GURL expects IPv6 hostnames to be surrounded with brackets.
|
||||
// Not using base::StrCat() because it is not constexpr.
|
||||
std::string host_with_brackets;
|
||||
host_with_brackets.reserve(ip_literal.size() + 2);
|
||||
host_with_brackets.push_back('[');
|
||||
host_with_brackets.append(ip_literal);
|
||||
host_with_brackets.push_back(']');
|
||||
url::Component host_comp(0, static_cast<int>(host_with_brackets.size()));
|
||||
|
||||
// Try parsing the hostname as an IPv6 literal.
|
||||
bytes->Resize(16); // 128 bits.
|
||||
return url::IPv6AddressToNumber(host_with_brackets.data(), host_comp,
|
||||
bytes->data());
|
||||
}
|
||||
|
||||
// Otherwise the string is an IPv4 address.
|
||||
bytes->Resize(4); // 32 bits.
|
||||
url::Component host_comp(0, static_cast<int>(ip_literal.size()));
|
||||
int num_components;
|
||||
url::CanonHostInfo::Family family = url::IPv4AddressToNumber(
|
||||
ip_literal.data(), host_comp, bytes->data(), &num_components);
|
||||
return family == url::CanonHostInfo::IPV4;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
class NET_EXPORT IPAddress {
|
||||
public:
|
||||
enum : size_t { kIPv4AddressSize = 4, kIPv6AddressSize = 16 };
|
||||
@ -165,17 +199,21 @@ class NET_EXPORT IPAddress {
|
||||
ip_address_.Assign(bytes);
|
||||
}
|
||||
|
||||
~IPAddress();
|
||||
constexpr ~IPAddress() = default;
|
||||
|
||||
// Returns true if the IP has |kIPv4AddressSize| elements.
|
||||
bool IsIPv4() const;
|
||||
constexpr bool IsIPv4() const {
|
||||
return ip_address_.size() == kIPv4AddressSize;
|
||||
}
|
||||
|
||||
// Returns true if the IP has |kIPv6AddressSize| elements.
|
||||
bool IsIPv6() const;
|
||||
constexpr bool IsIPv6() const {
|
||||
return ip_address_.size() == kIPv6AddressSize;
|
||||
}
|
||||
|
||||
// Returns true if the IP is either an IPv4 or IPv6 address. This function
|
||||
// only checks the address length.
|
||||
bool IsValid() const;
|
||||
constexpr bool IsValid() const { return IsIPv4() || IsIPv6(); }
|
||||
|
||||
// Returns true if the IP is not in a range reserved by the IANA for
|
||||
// local networks. Works with both IPv4 and IPv6 addresses.
|
||||
@ -199,10 +237,10 @@ class NET_EXPORT IPAddress {
|
||||
bool IsUniqueLocalIPv6() const;
|
||||
|
||||
// The size in bytes of |ip_address_|.
|
||||
size_t size() const { return ip_address_.size(); }
|
||||
constexpr size_t size() const { return ip_address_.size(); }
|
||||
|
||||
// Returns true if the IP is an empty, zero-sized (invalid) address.
|
||||
bool empty() const { return ip_address_.empty(); }
|
||||
constexpr bool empty() const { return ip_address_.empty(); }
|
||||
|
||||
// Returns the canonical string representation of an IP address.
|
||||
// For example: "192.168.0.1" or "::1". Returns the empty string when
|
||||
@ -214,10 +252,17 @@ class NET_EXPORT IPAddress {
|
||||
//
|
||||
// When parsing fails, the original value of |this| will be overwritten such
|
||||
// that |this->empty()| and |!this->IsValid()|.
|
||||
[[nodiscard]] bool AssignFromIPLiteral(std::string_view ip_literal);
|
||||
[[nodiscard]] constexpr bool AssignFromIPLiteral(
|
||||
std::string_view ip_literal) {
|
||||
bool success = internal::ParseIPLiteralToBytes(ip_literal, &ip_address_);
|
||||
if (!success) {
|
||||
ip_address_.Resize(0);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Returns the underlying bytes.
|
||||
const IPAddressBytes& bytes() const { return ip_address_; }
|
||||
constexpr const IPAddressBytes& bytes() const { return ip_address_; }
|
||||
|
||||
// Copies the bytes to a new vector. Generally callers should be using
|
||||
// |bytes()| and the IPAddressBytes abstraction. This method is provided as a
|
||||
@ -331,7 +376,8 @@ NET_EXPORT size_t MaskPrefixLength(const IPAddress& mask);
|
||||
// functionality as IPAddressMatchesPrefix() but doesn't perform automatic IPv4
|
||||
// to IPv4MappedIPv6 conversions and only checks against full bytes.
|
||||
template <size_t N>
|
||||
bool IPAddressStartsWith(const IPAddress& address, const uint8_t (&prefix)[N]) {
|
||||
constexpr bool IPAddressStartsWith(const IPAddress& address,
|
||||
const uint8_t (&prefix)[N]) {
|
||||
if (address.size() < N)
|
||||
return false;
|
||||
// SAFETY: N is size of `prefix` as inferred by the compiler.
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "base/format_macros.h"
|
||||
@ -943,6 +944,36 @@ TEST(IPAddressTest, IPv6Mask) {
|
||||
EXPECT_EQ("::", mask.ToString());
|
||||
}
|
||||
|
||||
// Test that IPAddress can be created at compile time.
|
||||
template <size_t N>
|
||||
constexpr bool VerifyIPBytes(const IPAddress& addr,
|
||||
const std::array<uint8_t, N> ip_bytes) {
|
||||
return std::ranges::equal(addr.bytes(), ip_bytes);
|
||||
}
|
||||
|
||||
constexpr IPAddress CreateIPAddress(std::string_view ip_address) {
|
||||
IPAddress addr;
|
||||
std::ignore = addr.AssignFromIPLiteral(ip_address);
|
||||
return addr;
|
||||
}
|
||||
|
||||
constexpr std::array<uint8_t, 4> ipv4_bytes = {192, 168, 2, 3};
|
||||
constexpr auto ipv4_address = CreateIPAddress("192.168.2.3");
|
||||
static_assert(VerifyIPBytes(ipv4_address, ipv4_bytes));
|
||||
|
||||
constexpr auto ipv6_address = CreateIPAddress("2001:0700:0300:1800::000f");
|
||||
constexpr std::array<uint8_t, 16> ipv6_bytes = {
|
||||
0x20, 0x01, 0x07, 0x00, 0x03, 0x00, 0x18, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f};
|
||||
static_assert(VerifyIPBytes(ipv6_address, ipv6_bytes));
|
||||
|
||||
// This test exists mainly to prevent the compiler from optimizing away
|
||||
// the compile-time checks above. All actual validation is done at compile time.
|
||||
TEST(IPAddressTest, VerifyIPAddressCreatedAtCompileTime) {
|
||||
EXPECT_TRUE(VerifyIPBytes(ipv4_address, ipv4_bytes));
|
||||
EXPECT_TRUE(VerifyIPBytes(ipv6_address, ipv6_bytes));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
} // namespace net
|
||||
|
@ -16,7 +16,6 @@
|
||||
// functions.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -176,19 +175,19 @@ inline constexpr uint8_t kSharedCharTypeTable[0x100] = {
|
||||
// clang-format on
|
||||
|
||||
// More readable wrappers around the character type lookup table.
|
||||
inline bool IsCharOfType(unsigned char c, SharedCharTypes type) {
|
||||
constexpr bool IsCharOfType(unsigned char c, SharedCharTypes type) {
|
||||
return !!(kSharedCharTypeTable[c] & type);
|
||||
}
|
||||
inline bool IsQueryChar(unsigned char c) {
|
||||
constexpr bool IsQueryChar(unsigned char c) {
|
||||
return IsCharOfType(c, CHAR_QUERY);
|
||||
}
|
||||
inline bool IsIPv4Char(unsigned char c) {
|
||||
constexpr bool IsIPv4Char(unsigned char c) {
|
||||
return IsCharOfType(c, CHAR_IPV4);
|
||||
}
|
||||
inline bool IsHexChar(unsigned char c) {
|
||||
constexpr bool IsHexChar(unsigned char c) {
|
||||
return IsCharOfType(c, CHAR_HEX);
|
||||
}
|
||||
inline bool IsComponentChar(unsigned char c) {
|
||||
constexpr bool IsComponentChar(unsigned char c) {
|
||||
return IsCharOfType(c, CHAR_COMPONENT);
|
||||
}
|
||||
|
||||
@ -554,6 +553,35 @@ int FindWindowsDriveLetter(const char* spec, int begin, int end);
|
||||
COMPONENT_EXPORT(URL)
|
||||
int FindWindowsDriveLetter(const char16_t* spec, int begin, int end);
|
||||
|
||||
// StringToUint64WithBase is implemented separately because std::strtoull (and
|
||||
// its variants like _stroui64 on Windows) are not guaranteed to be constexpr,
|
||||
// preventing their direct use in constant expressions. This custom
|
||||
// implementation provides a constexpr-friendly alternative for use in contexts
|
||||
// where constant evaluation is required.
|
||||
constexpr uint64_t StringToUint64WithBase(std::string_view str, uint8_t base) {
|
||||
uint64_t result = 0;
|
||||
|
||||
for (const char digit : str) {
|
||||
int value = -1;
|
||||
|
||||
if (digit >= '0' && digit <= '9') {
|
||||
value = digit - '0';
|
||||
} else if (digit >= 'A' && digit <= 'Z') {
|
||||
value = digit - 'A' + 10;
|
||||
} else if (digit >= 'a' && digit <= 'z') {
|
||||
value = digit - 'a' + 10;
|
||||
}
|
||||
|
||||
if (value < 0 || value >= base) {
|
||||
break; // Invalid character for the given base.
|
||||
}
|
||||
|
||||
result = result * base + static_cast<uint64_t>(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
// Implementations of Windows' int-to-string conversions
|
||||
@ -573,13 +601,6 @@ inline int _itow_s(int value, char16_t (&buffer)[N], int radix) {
|
||||
return _itow_s(value, buffer, N, radix);
|
||||
}
|
||||
|
||||
// _strtoui64 and strtoull behave the same
|
||||
inline unsigned long long _strtoui64(const char* nptr,
|
||||
char** endptr,
|
||||
int base) {
|
||||
return strtoull(nptr, endptr, base);
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
// The threshold we set to consider SIMD processing, in bytes; there is
|
||||
|
@ -21,7 +21,7 @@ namespace internal {
|
||||
|
||||
// Converts one of the character types that represent a numerical base to the
|
||||
// corresponding base.
|
||||
constexpr int BaseForType(SharedCharTypes type) {
|
||||
constexpr uint8_t BaseForType(SharedCharTypes type) {
|
||||
switch (type) {
|
||||
case CHAR_HEX:
|
||||
return 16;
|
||||
@ -92,7 +92,7 @@ constexpr CanonHostInfo::Family IPv4ComponentToNumber(
|
||||
|
||||
// We know the input is 7-bit, so convert to narrow (if this is the wide
|
||||
// version of the template) by casting.
|
||||
char input = static_cast<char>(spec[i]);
|
||||
auto input = static_cast<unsigned char>(spec[i]);
|
||||
|
||||
// Validate that this character is OK for the given base.
|
||||
if (!IsCharOfType(input, base)) {
|
||||
@ -108,7 +108,7 @@ constexpr CanonHostInfo::Family IPv4ComponentToNumber(
|
||||
// Fill the buffer, if there's space remaining. This check allows us to
|
||||
// verify that all characters are numeric, even those that don't fit.
|
||||
if (dest_i < kMaxComponentLen) {
|
||||
buf[dest_i++] = input;
|
||||
buf[dest_i++] = static_cast<char>(input);
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,9 +118,9 @@ constexpr CanonHostInfo::Family IPv4ComponentToNumber(
|
||||
|
||||
buf[dest_i] = '\0';
|
||||
|
||||
// Use the 64-bit strtoi so we get a big number (no hex, decimal, or octal
|
||||
// number can overflow a 64-bit number in <= 16 characters).
|
||||
uint64_t num = _strtoui64(buf, NULL, BaseForType(base));
|
||||
// Use the 64-bit StringToUint64WithBase so we get a big number (no hex,
|
||||
// decimal, or octal number can overflow a 64-bit number in <= 16 characters).
|
||||
uint64_t num = StringToUint64WithBase(buf, BaseForType(base));
|
||||
|
||||
// Check for 32-bit overflow.
|
||||
if (num > std::numeric_limits<uint32_t>::max()) {
|
||||
@ -443,7 +443,7 @@ constexpr uint16_t IPv6HexComponentToNumber(const CHAR* spec,
|
||||
|
||||
// Convert it to a number (overflow is not possible, since with 4 hex
|
||||
// characters we can at most have a 16 bit number).
|
||||
return static_cast<uint16_t>(_strtoui64(buf, NULL, 16));
|
||||
return static_cast<uint16_t>(StringToUint64WithBase(buf, 16));
|
||||
}
|
||||
|
||||
// Converts an IPv6 address to a 128-bit number (network byte order), returning
|
||||
|
@ -28,6 +28,13 @@ namespace url {
|
||||
|
||||
namespace {
|
||||
|
||||
// Struct to hold test cases for StringToUint64WithBase function.
|
||||
struct StringToUint64TestCase {
|
||||
std::string_view input;
|
||||
uint8_t base;
|
||||
uint64_t expected;
|
||||
};
|
||||
|
||||
struct ComponentCase {
|
||||
const char* input;
|
||||
const char* expected;
|
||||
@ -3246,4 +3253,52 @@ TEST_F(URLCanonTest, NonSpecialHostIPv6Address) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compile-time checks for StringToUint64WithBase function
|
||||
static_assert(StringToUint64WithBase("123", 10) == 123u);
|
||||
static_assert(StringToUint64WithBase("1A", 16) == 26u);
|
||||
|
||||
// Test cases for StringToUint64WithBase function with various bases.
|
||||
TEST_F(URLCanonTest, StringToUint64WithBase) {
|
||||
StringToUint64TestCase test_cases[] = {
|
||||
{"1", 10, 1u},
|
||||
{"-1", 10, 0u},
|
||||
{"a", 10, 0u},
|
||||
{"a", 16, 10u},
|
||||
{"123", 10, 123u},
|
||||
{"0", 10, 0u},
|
||||
{"18446744073709551615", 10, 18446744073709551615ULL}, // Max uint64_t
|
||||
{"1A", 16, 26u},
|
||||
{"FF", 16, 255u},
|
||||
{"FFFFFFFFFFFFFFFF", 16, 18446744073709551615ULL}, // Max uint64_t
|
||||
{"10", 8, 8u},
|
||||
{"77", 8, 63u},
|
||||
{"1010", 2, 10u},
|
||||
{"1111", 2, 15u},
|
||||
{"1000000000000000000000000000000000000000000000000000000000000000", 2,
|
||||
9223372036854775808ULL}, // 2^63
|
||||
{"123Z", 10, 123u}, // Stops at 'Z'
|
||||
{"1G", 16, 1u}, // Stops at 'G'
|
||||
{"08", 8, 0u}, // Stops at '8'
|
||||
{"", 10, 0u}, // Empty String
|
||||
{" 1", 10, 0u}, // Doesn't ignore leading spaces.
|
||||
{"+2", 10, 0u}, // Doesn't accept leading '+'.
|
||||
{"0644", 10, 644u}, // Doesn't automatically switch to octal.
|
||||
{"0xFF", 10, 0u}, // Doesn't automatically switch to hex.
|
||||
{"0xFF", 16, 0u}, // Doesn't accept 0x prefix.
|
||||
{"1e10", 10, 1u}, // Doesn't parse floating point.
|
||||
{"eF", 16, 239u}, // Supports mixed case.
|
||||
{"1z", 36, 71u}, // Supports base 36.
|
||||
{"1 001", 10, 1u}, // Stops at spaces.
|
||||
{"1,001", 10, 1u}, // Stops at commas.
|
||||
{"1_001", 10, 1u}, // Stops at underlines.
|
||||
{"1'001", 10, 1u}, // Stops at quote marks.
|
||||
{"1.001", 10, 1u}, // Stops at periods.
|
||||
{"1000000000000000F", 16, 15u}}; // Overflow is discarded.
|
||||
|
||||
for (const auto& test_case : test_cases) {
|
||||
EXPECT_EQ(StringToUint64WithBase(test_case.input, test_case.base),
|
||||
test_case.expected);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace url
|
||||
|
Reference in New Issue
Block a user