tvl-depot/absl/strings/numbers_test.cc
Abseil Team 7c7754fb3e Export of internal Abseil changes.
--
89b5e681db1d4f0b039daebb86c49bda77c8931b by Tom Manshreck <shreck@google.com>:

Add clarification that absl::Hash does not produce stable values across instances.

PiperOrigin-RevId: 238316564

--
56dec1d1e37fb2a02aa10d7c81bcd78f8486c093 by Eric Fiselier <ericwf@google.com>:

Add SFINAE to absl::optional::optional(in_place_t, Args...)

This constructor previously didn't have SFINAE because the SFINAE
acted badly when Clang was trying to figure out what special
members to declare. Specifically, while considering copy
constructors it would attempt to call `optional(in_place_t) [ with Args = <>
]`, which evaluated `is_constructible<T>`, which shouldn't have been
evaluated.

This patch avoids the eager SFINAE bug by deducing the in_place_t tag
and short-circuting the SFINAE if the argument passed is not exactly
in_place_t.

I fixed the same bug in libc++ in the same way.

PiperOrigin-RevId: 238290810

--
ffe6d087df495f7f990c89b0a4e1f1664c2c4f9d by Jon Cohen <cohenjon@google.com>:

Remove absl_internal_blah names form CMake.  We are always creating the alias target now, since use of these targets still requires including a header with internal in the name. This simplifies target naming a bit, especially for installation where we have to generate non-prefixed target names to export in the absl:: namespace.

PiperOrigin-RevId: 238280135

--
9d8ae92ff8727fa49391f7f5386810ff81e80aa7 by Derek Mauro <dmauro@google.com>:

Use ABSL_TEST_COPTS for spinlock_benchmark_common and mutex_benchmark_common.
Despite being cc_library, these are really tests and the warning for the
used-but-marked-unused iterator in Google Benchmark needs to be supressed.

PiperOrigin-RevId: 238225200

--
fcde1a79420ce15c8925944c45b69f9fd5226f12 by Matt Armstrong <marmstrong@google.com>:

Qualify calls to certain functions from the cmath library.

PiperOrigin-RevId: 238163972

--
4b931e5ef4ba76961b0e2a9edab1e586ba12dfd4 by Tom Manshreck <shreck@google.com>:

Add clarification that absl::Hash does not produce stable values across instances.

PiperOrigin-RevId: 238125817

--
8963ea8c65cac1e396a72fe77d6eb6a7313d76db by Derek Mauro <dmauro@google.com>:

Fix -Wc++14-binary-literal warning.

PiperOrigin-RevId: 238069157
GitOrigin-RevId: 89b5e681db1d4f0b039daebb86c49bda77c8931b
Change-Id: Ib06f1ee8efcddb7e2f332bc5bf1c1325458e1073
2019-03-14 11:32:24 -04:00

1187 lines
42 KiB
C++

// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file tests string processing functions related to numeric values.
#include "absl/strings/numbers.h"
#include <sys/types.h>
#include <cfenv> // NOLINT(build/c++11)
#include <cinttypes>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <numeric>
#include <random>
#include <set>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/internal/numbers_test_common.h"
#include "absl/strings/internal/pow10_helper.h"
namespace {
using absl::numbers_internal::kSixDigitsToBufferSize;
using absl::numbers_internal::safe_strto32_base;
using absl::numbers_internal::safe_strto64_base;
using absl::numbers_internal::safe_strtou32_base;
using absl::numbers_internal::safe_strtou64_base;
using absl::numbers_internal::SixDigitsToBuffer;
using absl::strings_internal::Itoa;
using absl::strings_internal::strtouint32_test_cases;
using absl::strings_internal::strtouint64_test_cases;
using absl::SimpleAtoi;
using testing::Eq;
using testing::MatchesRegex;
// Number of floats to test with.
// 5,000,000 is a reasonable default for a test that only takes a few seconds.
// 1,000,000,000+ triggers checking for all possible mantissa values for
// double-precision tests. 2,000,000,000+ triggers checking for every possible
// single-precision float.
const int kFloatNumCases = 5000000;
// This is a slow, brute-force routine to compute the exact base-10
// representation of a double-precision floating-point number. It
// is useful for debugging only.
std::string PerfectDtoa(double d) {
if (d == 0) return "0";
if (d < 0) return "-" + PerfectDtoa(-d);
// Basic theory: decompose d into mantissa and exp, where
// d = mantissa * 2^exp, and exp is as close to zero as possible.
int64_t mantissa, exp = 0;
while (d >= 1ULL << 63) ++exp, d *= 0.5;
while ((mantissa = d) != d) --exp, d *= 2.0;
// Then convert mantissa to ASCII, and either double it (if
// exp > 0) or halve it (if exp < 0) repeatedly. "halve it"
// in this case means multiplying it by five and dividing by 10.
constexpr int maxlen = 1100; // worst case is actually 1030 or so.
char buf[maxlen + 5];
for (int64_t num = mantissa, pos = maxlen; --pos >= 0;) {
buf[pos] = '0' + (num % 10);
num /= 10;
}
char* begin = &buf[0];
char* end = buf + maxlen;
for (int i = 0; i != exp; i += (exp > 0) ? 1 : -1) {
int carry = 0;
for (char* p = end; --p != begin;) {
int dig = *p - '0';
dig = dig * (exp > 0 ? 2 : 5) + carry;
carry = dig / 10;
dig %= 10;
*p = '0' + dig;
}
}
if (exp < 0) {
// "dividing by 10" above means we have to add the decimal point.
memmove(end + 1 + exp, end + exp, 1 - exp);
end[exp] = '.';
++end;
}
while (*begin == '0' && begin[1] != '.') ++begin;
return {begin, end};
}
TEST(ToString, PerfectDtoa) {
EXPECT_THAT(PerfectDtoa(1), Eq("1"));
EXPECT_THAT(PerfectDtoa(0.1),
Eq("0.1000000000000000055511151231257827021181583404541015625"));
EXPECT_THAT(PerfectDtoa(1e24), Eq("999999999999999983222784"));
EXPECT_THAT(PerfectDtoa(5e-324), MatchesRegex("0.0000.*625"));
for (int i = 0; i < 100; ++i) {
for (double multiplier :
{1e-300, 1e-200, 1e-100, 0.1, 1.0, 10.0, 1e100, 1e300}) {
double d = multiplier * i;
std::string s = PerfectDtoa(d);
EXPECT_DOUBLE_EQ(d, strtod(s.c_str(), nullptr));
}
}
}
template <typename integer>
struct MyInteger {
integer i;
explicit constexpr MyInteger(integer i) : i(i) {}
constexpr operator integer() const { return i; }
constexpr MyInteger operator+(MyInteger other) const { return i + other.i; }
constexpr MyInteger operator-(MyInteger other) const { return i - other.i; }
constexpr MyInteger operator*(MyInteger other) const { return i * other.i; }
constexpr MyInteger operator/(MyInteger other) const { return i / other.i; }
constexpr bool operator<(MyInteger other) const { return i < other.i; }
constexpr bool operator<=(MyInteger other) const { return i <= other.i; }
constexpr bool operator==(MyInteger other) const { return i == other.i; }
constexpr bool operator>=(MyInteger other) const { return i >= other.i; }
constexpr bool operator>(MyInteger other) const { return i > other.i; }
constexpr bool operator!=(MyInteger other) const { return i != other.i; }
integer as_integer() const { return i; }
};
typedef MyInteger<int64_t> MyInt64;
typedef MyInteger<uint64_t> MyUInt64;
void CheckInt32(int32_t x) {
char buffer[absl::numbers_internal::kFastToBufferSize];
char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
std::string expected = std::to_string(x);
EXPECT_EQ(expected, std::string(buffer, actual)) << " Input " << x;
char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
EXPECT_EQ(expected, std::string(buffer, generic_actual)) << " Input " << x;
}
void CheckInt64(int64_t x) {
char buffer[absl::numbers_internal::kFastToBufferSize + 3];
buffer[0] = '*';
buffer[23] = '*';
buffer[24] = '*';
char* actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
std::string expected = std::to_string(x);
EXPECT_EQ(expected, std::string(&buffer[1], actual)) << " Input " << x;
EXPECT_EQ(buffer[0], '*');
EXPECT_EQ(buffer[23], '*');
EXPECT_EQ(buffer[24], '*');
char* my_actual =
absl::numbers_internal::FastIntToBuffer(MyInt64(x), &buffer[1]);
EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
}
void CheckUInt32(uint32_t x) {
char buffer[absl::numbers_internal::kFastToBufferSize];
char* actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
std::string expected = std::to_string(x);
EXPECT_EQ(expected, std::string(buffer, actual)) << " Input " << x;
char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, buffer);
EXPECT_EQ(expected, std::string(buffer, generic_actual)) << " Input " << x;
}
void CheckUInt64(uint64_t x) {
char buffer[absl::numbers_internal::kFastToBufferSize + 1];
char* actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
std::string expected = std::to_string(x);
EXPECT_EQ(expected, std::string(&buffer[1], actual)) << " Input " << x;
char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]);
EXPECT_EQ(expected, std::string(&buffer[1], generic_actual))
<< " Input " << x;
char* my_actual =
absl::numbers_internal::FastIntToBuffer(MyUInt64(x), &buffer[1]);
EXPECT_EQ(expected, std::string(&buffer[1], my_actual)) << " Input " << x;
}
void CheckHex64(uint64_t v) {
char expected[16 + 1];
std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16));
snprintf(expected, sizeof(expected), "%016" PRIx64, static_cast<uint64_t>(v));
EXPECT_EQ(expected, actual) << " Input " << v;
}
TEST(Numbers, TestFastPrints) {
for (int i = -100; i <= 100; i++) {
CheckInt32(i);
CheckInt64(i);
}
for (int i = 0; i <= 100; i++) {
CheckUInt32(i);
CheckUInt64(i);
}
// Test min int to make sure that works
CheckInt32(INT_MIN);
CheckInt32(INT_MAX);
CheckInt64(LONG_MIN);
CheckInt64(uint64_t{1000000000});
CheckInt64(uint64_t{9999999999});
CheckInt64(uint64_t{100000000000000});
CheckInt64(uint64_t{999999999999999});
CheckInt64(uint64_t{1000000000000000000});
CheckInt64(uint64_t{1199999999999999999});
CheckInt64(int64_t{-700000000000000000});
CheckInt64(LONG_MAX);
CheckUInt32(std::numeric_limits<uint32_t>::max());
CheckUInt64(uint64_t{1000000000});
CheckUInt64(uint64_t{9999999999});
CheckUInt64(uint64_t{100000000000000});
CheckUInt64(uint64_t{999999999999999});
CheckUInt64(uint64_t{1000000000000000000});
CheckUInt64(uint64_t{1199999999999999999});
CheckUInt64(std::numeric_limits<uint64_t>::max());
for (int i = 0; i < 10000; i++) {
CheckHex64(i);
}
CheckHex64(uint64_t{0x123456789abcdef0});
}
template <typename int_type, typename in_val_type>
void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) {
std::string s = absl::StrCat(in_value);
int_type x = static_cast<int_type>(~exp_value);
EXPECT_TRUE(SimpleAtoi(s, &x))
<< "in_value=" << in_value << " s=" << s << " x=" << x;
EXPECT_EQ(exp_value, x);
x = static_cast<int_type>(~exp_value);
EXPECT_TRUE(SimpleAtoi(s.c_str(), &x));
EXPECT_EQ(exp_value, x);
}
template <typename int_type, typename in_val_type>
void VerifySimpleAtoiBad(in_val_type in_value) {
std::string s = absl::StrCat(in_value);
int_type x;
EXPECT_FALSE(SimpleAtoi(s, &x));
EXPECT_FALSE(SimpleAtoi(s.c_str(), &x));
}
TEST(NumbersTest, Atoi) {
// SimpleAtoi(absl::string_view, int32_t)
VerifySimpleAtoiGood<int32_t>(0, 0);
VerifySimpleAtoiGood<int32_t>(42, 42);
VerifySimpleAtoiGood<int32_t>(-42, -42);
VerifySimpleAtoiGood<int32_t>(std::numeric_limits<int32_t>::min(),
std::numeric_limits<int32_t>::min());
VerifySimpleAtoiGood<int32_t>(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
// SimpleAtoi(absl::string_view, uint32_t)
VerifySimpleAtoiGood<uint32_t>(0, 0);
VerifySimpleAtoiGood<uint32_t>(42, 42);
VerifySimpleAtoiBad<uint32_t>(-42);
VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int32_t>::min());
VerifySimpleAtoiGood<uint32_t>(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
VerifySimpleAtoiGood<uint32_t>(std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max());
VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int64_t>::min());
VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<int64_t>::max());
VerifySimpleAtoiBad<uint32_t>(std::numeric_limits<uint64_t>::max());
// SimpleAtoi(absl::string_view, int64_t)
VerifySimpleAtoiGood<int64_t>(0, 0);
VerifySimpleAtoiGood<int64_t>(42, 42);
VerifySimpleAtoiGood<int64_t>(-42, -42);
VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int32_t>::min(),
std::numeric_limits<int32_t>::min());
VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
VerifySimpleAtoiGood<int64_t>(std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max());
VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int64_t>::min(),
std::numeric_limits<int64_t>::min());
VerifySimpleAtoiGood<int64_t>(std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::max());
VerifySimpleAtoiBad<int64_t>(std::numeric_limits<uint64_t>::max());
// SimpleAtoi(absl::string_view, uint64_t)
VerifySimpleAtoiGood<uint64_t>(0, 0);
VerifySimpleAtoiGood<uint64_t>(42, 42);
VerifySimpleAtoiBad<uint64_t>(-42);
VerifySimpleAtoiBad<uint64_t>(std::numeric_limits<int32_t>::min());
VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max());
VerifySimpleAtoiBad<uint64_t>(std::numeric_limits<int64_t>::min());
VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::max());
VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max());
// Some other types
VerifySimpleAtoiGood<int>(-42, -42);
VerifySimpleAtoiGood<int32_t>(-42, -42);
VerifySimpleAtoiGood<uint32_t>(42, 42);
VerifySimpleAtoiGood<unsigned int>(42, 42);
VerifySimpleAtoiGood<int64_t>(-42, -42);
VerifySimpleAtoiGood<long>(-42, -42); // NOLINT(runtime/int)
VerifySimpleAtoiGood<uint64_t>(42, 42);
VerifySimpleAtoiGood<size_t>(42, 42);
VerifySimpleAtoiGood<std::string::size_type>(42, 42);
}
TEST(NumbersTest, Atoenum) {
enum E01 {
E01_zero = 0,
E01_one = 1,
};
VerifySimpleAtoiGood<E01>(E01_zero, E01_zero);
VerifySimpleAtoiGood<E01>(E01_one, E01_one);
enum E_101 {
E_101_minusone = -1,
E_101_zero = 0,
E_101_one = 1,
};
VerifySimpleAtoiGood<E_101>(E_101_minusone, E_101_minusone);
VerifySimpleAtoiGood<E_101>(E_101_zero, E_101_zero);
VerifySimpleAtoiGood<E_101>(E_101_one, E_101_one);
enum E_bigint {
E_bigint_zero = 0,
E_bigint_one = 1,
E_bigint_max31 = static_cast<int32_t>(0x7FFFFFFF),
};
VerifySimpleAtoiGood<E_bigint>(E_bigint_zero, E_bigint_zero);
VerifySimpleAtoiGood<E_bigint>(E_bigint_one, E_bigint_one);
VerifySimpleAtoiGood<E_bigint>(E_bigint_max31, E_bigint_max31);
enum E_fullint {
E_fullint_zero = 0,
E_fullint_one = 1,
E_fullint_max31 = static_cast<int32_t>(0x7FFFFFFF),
E_fullint_min32 = INT32_MIN,
};
VerifySimpleAtoiGood<E_fullint>(E_fullint_zero, E_fullint_zero);
VerifySimpleAtoiGood<E_fullint>(E_fullint_one, E_fullint_one);
VerifySimpleAtoiGood<E_fullint>(E_fullint_max31, E_fullint_max31);
VerifySimpleAtoiGood<E_fullint>(E_fullint_min32, E_fullint_min32);
enum E_biguint {
E_biguint_zero = 0,
E_biguint_one = 1,
E_biguint_max31 = static_cast<uint32_t>(0x7FFFFFFF),
E_biguint_max32 = static_cast<uint32_t>(0xFFFFFFFF),
};
VerifySimpleAtoiGood<E_biguint>(E_biguint_zero, E_biguint_zero);
VerifySimpleAtoiGood<E_biguint>(E_biguint_one, E_biguint_one);
VerifySimpleAtoiGood<E_biguint>(E_biguint_max31, E_biguint_max31);
VerifySimpleAtoiGood<E_biguint>(E_biguint_max32, E_biguint_max32);
}
TEST(stringtest, safe_strto32_base) {
int32_t value;
EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16));
EXPECT_EQ(0x34234324, value);
EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16));
EXPECT_EQ(0x34234324, value);
EXPECT_TRUE(safe_strto32_base("34234324", &value, 16));
EXPECT_EQ(0x34234324, value);
EXPECT_TRUE(safe_strto32_base("0", &value, 16));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto32_base(" \t\n -0x34234324", &value, 16));
EXPECT_EQ(-0x34234324, value);
EXPECT_TRUE(safe_strto32_base(" \t\n -34234324", &value, 16));
EXPECT_EQ(-0x34234324, value);
EXPECT_TRUE(safe_strto32_base("7654321", &value, 8));
EXPECT_EQ(07654321, value);
EXPECT_TRUE(safe_strto32_base("-01234", &value, 8));
EXPECT_EQ(-01234, value);
EXPECT_FALSE(safe_strto32_base("1834", &value, 8));
// Autodetect base.
EXPECT_TRUE(safe_strto32_base("0", &value, 0));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto32_base("077", &value, 0));
EXPECT_EQ(077, value); // Octal interpretation
// Leading zero indicates octal, but then followed by invalid digit.
EXPECT_FALSE(safe_strto32_base("088", &value, 0));
// Leading 0x indicated hex, but then followed by invalid digit.
EXPECT_FALSE(safe_strto32_base("0xG", &value, 0));
// Base-10 version.
EXPECT_TRUE(safe_strto32_base("34234324", &value, 10));
EXPECT_EQ(34234324, value);
EXPECT_TRUE(safe_strto32_base("0", &value, 10));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto32_base(" \t\n -34234324", &value, 10));
EXPECT_EQ(-34234324, value);
EXPECT_TRUE(safe_strto32_base("34234324 \n\t ", &value, 10));
EXPECT_EQ(34234324, value);
// Invalid ints.
EXPECT_FALSE(safe_strto32_base("", &value, 10));
EXPECT_FALSE(safe_strto32_base(" ", &value, 10));
EXPECT_FALSE(safe_strto32_base("abc", &value, 10));
EXPECT_FALSE(safe_strto32_base("34234324a", &value, 10));
EXPECT_FALSE(safe_strto32_base("34234.3", &value, 10));
// Out of bounds.
EXPECT_FALSE(safe_strto32_base("2147483648", &value, 10));
EXPECT_FALSE(safe_strto32_base("-2147483649", &value, 10));
// String version.
EXPECT_TRUE(safe_strto32_base(std::string("0x1234"), &value, 16));
EXPECT_EQ(0x1234, value);
// Base-10 std::string version.
EXPECT_TRUE(safe_strto32_base("1234", &value, 10));
EXPECT_EQ(1234, value);
}
TEST(stringtest, safe_strto32_range) {
// These tests verify underflow/overflow behaviour.
int32_t value;
EXPECT_FALSE(safe_strto32_base("2147483648", &value, 10));
EXPECT_EQ(std::numeric_limits<int32_t>::max(), value);
EXPECT_TRUE(safe_strto32_base("-2147483648", &value, 10));
EXPECT_EQ(std::numeric_limits<int32_t>::min(), value);
EXPECT_FALSE(safe_strto32_base("-2147483649", &value, 10));
EXPECT_EQ(std::numeric_limits<int32_t>::min(), value);
}
TEST(stringtest, safe_strto64_range) {
// These tests verify underflow/overflow behaviour.
int64_t value;
EXPECT_FALSE(safe_strto64_base("9223372036854775808", &value, 10));
EXPECT_EQ(std::numeric_limits<int64_t>::max(), value);
EXPECT_TRUE(safe_strto64_base("-9223372036854775808", &value, 10));
EXPECT_EQ(std::numeric_limits<int64_t>::min(), value);
EXPECT_FALSE(safe_strto64_base("-9223372036854775809", &value, 10));
EXPECT_EQ(std::numeric_limits<int64_t>::min(), value);
}
TEST(stringtest, safe_strto32_leading_substring) {
// These tests verify this comment in numbers.h:
// On error, returns false, and sets *value to: [...]
// conversion of leading substring if available ("123@@@" -> 123)
// 0 if no leading substring available
int32_t value;
EXPECT_FALSE(safe_strto32_base("04069@@@", &value, 10));
EXPECT_EQ(4069, value);
EXPECT_FALSE(safe_strto32_base("04069@@@", &value, 8));
EXPECT_EQ(0406, value);
EXPECT_FALSE(safe_strto32_base("04069balloons", &value, 10));
EXPECT_EQ(4069, value);
EXPECT_FALSE(safe_strto32_base("04069balloons", &value, 16));
EXPECT_EQ(0x4069ba, value);
EXPECT_FALSE(safe_strto32_base("@@@", &value, 10));
EXPECT_EQ(0, value); // there was no leading substring
}
TEST(stringtest, safe_strto64_leading_substring) {
// These tests verify this comment in numbers.h:
// On error, returns false, and sets *value to: [...]
// conversion of leading substring if available ("123@@@" -> 123)
// 0 if no leading substring available
int64_t value;
EXPECT_FALSE(safe_strto64_base("04069@@@", &value, 10));
EXPECT_EQ(4069, value);
EXPECT_FALSE(safe_strto64_base("04069@@@", &value, 8));
EXPECT_EQ(0406, value);
EXPECT_FALSE(safe_strto64_base("04069balloons", &value, 10));
EXPECT_EQ(4069, value);
EXPECT_FALSE(safe_strto64_base("04069balloons", &value, 16));
EXPECT_EQ(0x4069ba, value);
EXPECT_FALSE(safe_strto64_base("@@@", &value, 10));
EXPECT_EQ(0, value); // there was no leading substring
}
TEST(stringtest, safe_strto64_base) {
int64_t value;
EXPECT_TRUE(safe_strto64_base("0x3423432448783446", &value, 16));
EXPECT_EQ(int64_t{0x3423432448783446}, value);
EXPECT_TRUE(safe_strto64_base("3423432448783446", &value, 16));
EXPECT_EQ(int64_t{0x3423432448783446}, value);
EXPECT_TRUE(safe_strto64_base("0", &value, 16));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto64_base(" \t\n -0x3423432448783446", &value, 16));
EXPECT_EQ(int64_t{-0x3423432448783446}, value);
EXPECT_TRUE(safe_strto64_base(" \t\n -3423432448783446", &value, 16));
EXPECT_EQ(int64_t{-0x3423432448783446}, value);
EXPECT_TRUE(safe_strto64_base("123456701234567012", &value, 8));
EXPECT_EQ(int64_t{0123456701234567012}, value);
EXPECT_TRUE(safe_strto64_base("-017777777777777", &value, 8));
EXPECT_EQ(int64_t{-017777777777777}, value);
EXPECT_FALSE(safe_strto64_base("19777777777777", &value, 8));
// Autodetect base.
EXPECT_TRUE(safe_strto64_base("0", &value, 0));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto64_base("077", &value, 0));
EXPECT_EQ(077, value); // Octal interpretation
// Leading zero indicates octal, but then followed by invalid digit.
EXPECT_FALSE(safe_strto64_base("088", &value, 0));
// Leading 0x indicated hex, but then followed by invalid digit.
EXPECT_FALSE(safe_strto64_base("0xG", &value, 0));
// Base-10 version.
EXPECT_TRUE(safe_strto64_base("34234324487834466", &value, 10));
EXPECT_EQ(int64_t{34234324487834466}, value);
EXPECT_TRUE(safe_strto64_base("0", &value, 10));
EXPECT_EQ(0, value);
EXPECT_TRUE(safe_strto64_base(" \t\n -34234324487834466", &value, 10));
EXPECT_EQ(int64_t{-34234324487834466}, value);
EXPECT_TRUE(safe_strto64_base("34234324487834466 \n\t ", &value, 10));
EXPECT_EQ(int64_t{34234324487834466}, value);
// Invalid ints.
EXPECT_FALSE(safe_strto64_base("", &value, 10));
EXPECT_FALSE(safe_strto64_base(" ", &value, 10));
EXPECT_FALSE(safe_strto64_base("abc", &value, 10));
EXPECT_FALSE(safe_strto64_base("34234324487834466a", &value, 10));
EXPECT_FALSE(safe_strto64_base("34234487834466.3", &value, 10));
// Out of bounds.
EXPECT_FALSE(safe_strto64_base("9223372036854775808", &value, 10));
EXPECT_FALSE(safe_strto64_base("-9223372036854775809", &value, 10));
// String version.
EXPECT_TRUE(safe_strto64_base(std::string("0x1234"), &value, 16));
EXPECT_EQ(0x1234, value);
// Base-10 std::string version.
EXPECT_TRUE(safe_strto64_base("1234", &value, 10));
EXPECT_EQ(1234, value);
}
const size_t kNumRandomTests = 10000;
template <typename IntType>
void test_random_integer_parse_base(bool (*parse_func)(absl::string_view,
IntType* value,
int base)) {
using RandomEngine = std::minstd_rand0;
std::random_device rd;
RandomEngine rng(rd());
std::uniform_int_distribution<IntType> random_int(
std::numeric_limits<IntType>::min());
std::uniform_int_distribution<int> random_base(2, 35);
for (size_t i = 0; i < kNumRandomTests; i++) {
IntType value = random_int(rng);
int base = random_base(rng);
std::string str_value;
EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
IntType parsed_value;
// Test successful parse
EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
EXPECT_EQ(parsed_value, value);
// Test overflow
EXPECT_FALSE(
parse_func(absl::StrCat(std::numeric_limits<IntType>::max(), value),
&parsed_value, base));
// Test underflow
if (std::numeric_limits<IntType>::min() < 0) {
EXPECT_FALSE(
parse_func(absl::StrCat(std::numeric_limits<IntType>::min(), value),
&parsed_value, base));
} else {
EXPECT_FALSE(parse_func(absl::StrCat("-", value), &parsed_value, base));
}
}
}
TEST(stringtest, safe_strto32_random) {
test_random_integer_parse_base<int32_t>(&safe_strto32_base);
}
TEST(stringtest, safe_strto64_random) {
test_random_integer_parse_base<int64_t>(&safe_strto64_base);
}
TEST(stringtest, safe_strtou32_random) {
test_random_integer_parse_base<uint32_t>(&safe_strtou32_base);
}
TEST(stringtest, safe_strtou64_random) {
test_random_integer_parse_base<uint64_t>(&safe_strtou64_base);
}
TEST(stringtest, safe_strtou32_base) {
for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) {
const auto& e = strtouint32_test_cases()[i];
uint32_t value;
EXPECT_EQ(e.expect_ok, safe_strtou32_base(e.str, &value, e.base))
<< "str=\"" << e.str << "\" base=" << e.base;
if (e.expect_ok) {
EXPECT_EQ(e.expected, value) << "i=" << i << " str=\"" << e.str
<< "\" base=" << e.base;
}
}
}
TEST(stringtest, safe_strtou32_base_length_delimited) {
for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) {
const auto& e = strtouint32_test_cases()[i];
std::string tmp(e.str);
tmp.append("12"); // Adds garbage at the end.
uint32_t value;
EXPECT_EQ(e.expect_ok,
safe_strtou32_base(absl::string_view(tmp.data(), strlen(e.str)),
&value, e.base))
<< "str=\"" << e.str << "\" base=" << e.base;
if (e.expect_ok) {
EXPECT_EQ(e.expected, value) << "i=" << i << " str=" << e.str
<< " base=" << e.base;
}
}
}
TEST(stringtest, safe_strtou64_base) {
for (int i = 0; strtouint64_test_cases()[i].str != nullptr; ++i) {
const auto& e = strtouint64_test_cases()[i];
uint64_t value;
EXPECT_EQ(e.expect_ok, safe_strtou64_base(e.str, &value, e.base))
<< "str=\"" << e.str << "\" base=" << e.base;
if (e.expect_ok) {
EXPECT_EQ(e.expected, value) << "str=" << e.str << " base=" << e.base;
}
}
}
TEST(stringtest, safe_strtou64_base_length_delimited) {
for (int i = 0; strtouint64_test_cases()[i].str != nullptr; ++i) {
const auto& e = strtouint64_test_cases()[i];
std::string tmp(e.str);
tmp.append("12"); // Adds garbage at the end.
uint64_t value;
EXPECT_EQ(e.expect_ok,
safe_strtou64_base(absl::string_view(tmp.data(), strlen(e.str)),
&value, e.base))
<< "str=\"" << e.str << "\" base=" << e.base;
if (e.expect_ok) {
EXPECT_EQ(e.expected, value) << "str=\"" << e.str << "\" base=" << e.base;
}
}
}
// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC,
// and WebAssembly.
#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
#define ABSL_MISSING_FEENABLEEXCEPT 1
#define ABSL_MISSING_FEDISABLEEXCEPT 1
#endif
class SimpleDtoaTest : public testing::Test {
protected:
void SetUp() override {
// Store the current floating point env & clear away any pending exceptions.
feholdexcept(&fp_env_);
#ifndef ABSL_MISSING_FEENABLEEXCEPT
// Turn on floating point exceptions.
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
}
void TearDown() override {
// Restore the floating point environment to the original state.
// In theory fedisableexcept is unnecessary; fesetenv will also do it.
// In practice, our toolchains have subtle bugs.
#ifndef ABSL_MISSING_FEDISABLEEXCEPT
fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
fesetenv(&fp_env_);
}
std::string ToNineDigits(double value) {
char buffer[16]; // more than enough for %.9g
snprintf(buffer, sizeof(buffer), "%.9g", value);
return buffer;
}
fenv_t fp_env_;
};
// Run the given runnable functor for "cases" test cases, chosen over the
// available range of float. pi and e and 1/e are seeded, and then all
// available integer powers of 2 and 10 are multiplied against them. In
// addition to trying all those values, we try the next higher and next lower
// float, and then we add additional test cases evenly distributed between them.
// Each test case is passed to runnable as both a positive and negative value.
template <typename R>
void ExhaustiveFloat(uint32_t cases, R&& runnable) {
runnable(0.0f);
runnable(-0.0f);
if (cases >= 2e9) { // more than 2 billion? Might as well run them all.
for (float f = 0; f < std::numeric_limits<float>::max(); ) {
f = nextafterf(f, std::numeric_limits<float>::max());
runnable(-f);
runnable(f);
}
return;
}
std::set<float> floats = {3.4028234e38f};
for (float f : {1.0, 3.14159265, 2.718281828, 1 / 2.718281828}) {
for (float testf = f; testf != 0; testf *= 0.1f) floats.insert(testf);
for (float testf = f; testf != 0; testf *= 0.5f) floats.insert(testf);
for (float testf = f; testf < 3e38f / 2; testf *= 2.0f)
floats.insert(testf);
for (float testf = f; testf < 3e38f / 10; testf *= 10) floats.insert(testf);
}
float last = *floats.begin();
runnable(last);
runnable(-last);
int iters_per_float = cases / floats.size();
if (iters_per_float == 0) iters_per_float = 1;
for (float f : floats) {
if (f == last) continue;
float testf = std::nextafter(last, std::numeric_limits<float>::max());
runnable(testf);
runnable(-testf);
last = testf;
if (f == last) continue;
double step = (double{f} - last) / iters_per_float;
for (double d = last + step; d < f; d += step) {
testf = d;
if (testf != last) {
runnable(testf);
runnable(-testf);
last = testf;
}
}
testf = std::nextafter(f, 0.0f);
if (testf > last) {
runnable(testf);
runnable(-testf);
last = testf;
}
if (f != last) {
runnable(f);
runnable(-f);
last = f;
}
}
}
TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) {
uint64_t test_count = 0;
std::vector<double> mismatches;
auto checker = [&](double d) {
if (d != d) return; // rule out NaNs
++test_count;
char sixdigitsbuf[kSixDigitsToBufferSize] = {0};
SixDigitsToBuffer(d, sixdigitsbuf);
char snprintfbuf[kSixDigitsToBufferSize] = {0};
snprintf(snprintfbuf, kSixDigitsToBufferSize, "%g", d);
if (strcmp(sixdigitsbuf, snprintfbuf) != 0) {
mismatches.push_back(d);
if (mismatches.size() < 10) {
ABSL_RAW_LOG(ERROR, "%s",
absl::StrCat("Six-digit failure with double. ", "d=", d,
"=", d, " sixdigits=", sixdigitsbuf,
" printf(%g)=", snprintfbuf)
.c_str());
}
}
};
// Some quick sanity checks...
checker(5e-324);
checker(1e-308);
checker(1.0);
checker(1.000005);
checker(1.7976931348623157e308);
checker(0.00390625);
#ifndef _MSC_VER
// on MSVC, snprintf() rounds it to 0.00195313. SixDigitsToBuffer() rounds it
// to 0.00195312 (round half to even).
checker(0.001953125);
#endif
checker(0.005859375);
// Some cases where the rounding is very very close
checker(1.089095e-15);
checker(3.274195e-55);
checker(6.534355e-146);
checker(2.920845e+234);
if (mismatches.empty()) {
test_count = 0;
ExhaustiveFloat(kFloatNumCases, checker);
test_count = 0;
std::vector<int> digit_testcases{
100000, 100001, 100002, 100005, 100010, 100020, 100050, 100100, // misc
195312, 195313, // 1.953125 is a case where we round down, just barely.
200000, 500000, 800000, // misc mid-range cases
585937, 585938, // 5.859375 is a case where we round up, just barely.
900000, 990000, 999000, 999900, 999990, 999996, 999997, 999998, 999999};
if (kFloatNumCases >= 1e9) {
// If at least 1 billion test cases were requested, user wants an
// exhaustive test. So let's test all mantissas, too.
constexpr int min_mantissa = 100000, max_mantissa = 999999;
digit_testcases.resize(max_mantissa - min_mantissa + 1);
std::iota(digit_testcases.begin(), digit_testcases.end(), min_mantissa);
}
for (int exponent = -324; exponent <= 308; ++exponent) {
double powten = absl::strings_internal::Pow10(exponent);
if (powten == 0) powten = 5e-324;
if (kFloatNumCases >= 1e9) {
// The exhaustive test takes a very long time, so log progress.
char buf[kSixDigitsToBufferSize];
ABSL_RAW_LOG(
INFO, "%s",
absl::StrCat("Exp ", exponent, " powten=", powten, "(", powten,
") (",
std::string(buf, SixDigitsToBuffer(powten, buf)), ")")
.c_str());
}
for (int digits : digit_testcases) {
if (exponent == 308 && digits >= 179769) break; // don't overflow!
double digiform = (digits + 0.5) * 0.00001;
double testval = digiform * powten;
double pretestval = nextafter(testval, 0);
double posttestval = nextafter(testval, 1.7976931348623157e308);
checker(testval);
checker(pretestval);
checker(posttestval);
}
}
} else {
EXPECT_EQ(mismatches.size(), 0);
for (size_t i = 0; i < mismatches.size(); ++i) {
if (i > 100) i = mismatches.size() - 1;
double d = mismatches[i];
char sixdigitsbuf[kSixDigitsToBufferSize] = {0};
SixDigitsToBuffer(d, sixdigitsbuf);
char snprintfbuf[kSixDigitsToBufferSize] = {0};
snprintf(snprintfbuf, kSixDigitsToBufferSize, "%g", d);
double before = nextafter(d, 0.0);
double after = nextafter(d, 1.7976931348623157e308);
char b1[32], b2[kSixDigitsToBufferSize];
ABSL_RAW_LOG(
ERROR, "%s",
absl::StrCat(
"Mismatch #", i, " d=", d, " (", ToNineDigits(d), ")",
" sixdigits='", sixdigitsbuf, "'", " snprintf='", snprintfbuf,
"'", " Before.=", PerfectDtoa(before), " ",
(SixDigitsToBuffer(before, b2), b2),
" vs snprintf=", (snprintf(b1, sizeof(b1), "%g", before), b1),
" Perfect=", PerfectDtoa(d), " ", (SixDigitsToBuffer(d, b2), b2),
" vs snprintf=", (snprintf(b1, sizeof(b1), "%g", d), b1),
" After.=.", PerfectDtoa(after), " ",
(SixDigitsToBuffer(after, b2), b2),
" vs snprintf=", (snprintf(b1, sizeof(b1), "%g", after), b1))
.c_str());
}
}
}
TEST(StrToInt32, Partial) {
struct Int32TestLine {
std::string input;
bool status;
int32_t value;
};
const int32_t int32_min = std::numeric_limits<int32_t>::min();
const int32_t int32_max = std::numeric_limits<int32_t>::max();
Int32TestLine int32_test_line[] = {
{"", false, 0},
{" ", false, 0},
{"-", false, 0},
{"123@@@", false, 123},
{absl::StrCat(int32_min, int32_max), false, int32_min},
{absl::StrCat(int32_max, int32_max), false, int32_max},
};
for (const Int32TestLine& test_line : int32_test_line) {
int32_t value = -2;
bool status = safe_strto32_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = -2;
status = safe_strto32_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = -2;
status = safe_strto32_base(absl::string_view(test_line.input), &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
}
}
TEST(StrToUint32, Partial) {
struct Uint32TestLine {
std::string input;
bool status;
uint32_t value;
};
const uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
Uint32TestLine uint32_test_line[] = {
{"", false, 0},
{" ", false, 0},
{"-", false, 0},
{"123@@@", false, 123},
{absl::StrCat(uint32_max, uint32_max), false, uint32_max},
};
for (const Uint32TestLine& test_line : uint32_test_line) {
uint32_t value = 2;
bool status = safe_strtou32_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = 2;
status = safe_strtou32_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = 2;
status = safe_strtou32_base(absl::string_view(test_line.input), &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
}
}
TEST(StrToInt64, Partial) {
struct Int64TestLine {
std::string input;
bool status;
int64_t value;
};
const int64_t int64_min = std::numeric_limits<int64_t>::min();
const int64_t int64_max = std::numeric_limits<int64_t>::max();
Int64TestLine int64_test_line[] = {
{"", false, 0},
{" ", false, 0},
{"-", false, 0},
{"123@@@", false, 123},
{absl::StrCat(int64_min, int64_max), false, int64_min},
{absl::StrCat(int64_max, int64_max), false, int64_max},
};
for (const Int64TestLine& test_line : int64_test_line) {
int64_t value = -2;
bool status = safe_strto64_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = -2;
status = safe_strto64_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = -2;
status = safe_strto64_base(absl::string_view(test_line.input), &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
}
}
TEST(StrToUint64, Partial) {
struct Uint64TestLine {
std::string input;
bool status;
uint64_t value;
};
const uint64_t uint64_max = std::numeric_limits<uint64_t>::max();
Uint64TestLine uint64_test_line[] = {
{"", false, 0},
{" ", false, 0},
{"-", false, 0},
{"123@@@", false, 123},
{absl::StrCat(uint64_max, uint64_max), false, uint64_max},
};
for (const Uint64TestLine& test_line : uint64_test_line) {
uint64_t value = 2;
bool status = safe_strtou64_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = 2;
status = safe_strtou64_base(test_line.input, &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
value = 2;
status = safe_strtou64_base(absl::string_view(test_line.input), &value, 10);
EXPECT_EQ(test_line.status, status) << test_line.input;
EXPECT_EQ(test_line.value, value) << test_line.input;
}
}
TEST(StrToInt32Base, PrefixOnly) {
struct Int32TestLine {
std::string input;
bool status;
int32_t value;
};
Int32TestLine int32_test_line[] = {
{ "", false, 0 },
{ "-", false, 0 },
{ "-0", true, 0 },
{ "0", true, 0 },
{ "0x", false, 0 },
{ "-0x", false, 0 },
};
const int base_array[] = { 0, 2, 8, 10, 16 };
for (const Int32TestLine& line : int32_test_line) {
for (const int base : base_array) {
int32_t value = 2;
bool status = safe_strto32_base(line.input.c_str(), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strto32_base(line.input, &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strto32_base(absl::string_view(line.input), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
}
}
}
TEST(StrToUint32Base, PrefixOnly) {
struct Uint32TestLine {
std::string input;
bool status;
uint32_t value;
};
Uint32TestLine uint32_test_line[] = {
{ "", false, 0 },
{ "0", true, 0 },
{ "0x", false, 0 },
};
const int base_array[] = { 0, 2, 8, 10, 16 };
for (const Uint32TestLine& line : uint32_test_line) {
for (const int base : base_array) {
uint32_t value = 2;
bool status = safe_strtou32_base(line.input.c_str(), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strtou32_base(line.input, &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strtou32_base(absl::string_view(line.input), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
}
}
}
TEST(StrToInt64Base, PrefixOnly) {
struct Int64TestLine {
std::string input;
bool status;
int64_t value;
};
Int64TestLine int64_test_line[] = {
{ "", false, 0 },
{ "-", false, 0 },
{ "-0", true, 0 },
{ "0", true, 0 },
{ "0x", false, 0 },
{ "-0x", false, 0 },
};
const int base_array[] = { 0, 2, 8, 10, 16 };
for (const Int64TestLine& line : int64_test_line) {
for (const int base : base_array) {
int64_t value = 2;
bool status = safe_strto64_base(line.input.c_str(), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strto64_base(line.input, &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strto64_base(absl::string_view(line.input), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
}
}
}
TEST(StrToUint64Base, PrefixOnly) {
struct Uint64TestLine {
std::string input;
bool status;
uint64_t value;
};
Uint64TestLine uint64_test_line[] = {
{ "", false, 0 },
{ "0", true, 0 },
{ "0x", false, 0 },
};
const int base_array[] = { 0, 2, 8, 10, 16 };
for (const Uint64TestLine& line : uint64_test_line) {
for (const int base : base_array) {
uint64_t value = 2;
bool status = safe_strtou64_base(line.input.c_str(), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strtou64_base(line.input, &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
value = 2;
status = safe_strtou64_base(absl::string_view(line.input), &value, base);
EXPECT_EQ(line.status, status) << line.input << " " << base;
EXPECT_EQ(line.value, value) << line.input << " " << base;
}
}
}
} // namespace