Switch all string-number conversion to use fundamental types, and add long to the list.

Change-Id: I9244df09415f9d46262e2b8d04b64d7c4f786436
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1565287
Commit-Queue: Eric Astor <epastor@google.com>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Eric Astor 2019-04-12 13:42:44 -04:00 committed by Commit Bot
parent ad49fcfad6
commit e50676dcf2
3 changed files with 248 additions and 129 deletions

View File

@ -97,15 +97,34 @@ struct StringToUnsignedIntTraits
}
};
struct StringToInt64Traits
: public StringToSignedIntegerTraits<int64_t, int64_t> {
struct StringToLongTraits
: public StringToSignedIntegerTraits<long, long long> {
static LongType Convert(const char* str, char** end, int base) {
return strtoll(str, end, base);
}
};
struct StringToUnsignedInt64Traits
: public StringToUnsignedIntegerTraits<uint64_t, uint64_t> {
struct StringToUnsignedLongTraits
: public StringToUnsignedIntegerTraits<unsigned long, unsigned long long> {
static LongType Convert(const char* str, char** end, int base) {
if (str[0] == '-') {
*end = const_cast<char*>(str);
return 0;
}
return strtoull(str, end, base);
}
};
struct StringToLongLongTraits
: public StringToSignedIntegerTraits<long long, long long> {
static LongType Convert(const char* str, char** end, int base) {
return strtoll(str, end, base);
}
};
struct StringToUnsignedLongLongTraits
: public StringToUnsignedIntegerTraits<unsigned long long,
unsigned long long> {
static LongType Convert(const char* str, char** end, int base) {
if (str[0] == '-') {
*end = const_cast<char*>(str);
@ -136,7 +155,7 @@ bool StringToIntegerInternal(const std::string& string,
end != string.data() + string.length()) {
return false;
}
*number = result;
*number = static_cast<IntType>(result);
return true;
}
@ -152,12 +171,21 @@ bool StringToNumber(const std::string& string, unsigned int* number) {
return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
}
bool StringToNumber(const std::string& string, int64_t* number) {
return StringToIntegerInternal<StringToInt64Traits>(string, number);
bool StringToNumber(const std::string& string, long* number) {
return StringToIntegerInternal<StringToLongTraits>(string, number);
}
bool StringToNumber(const std::string& string, uint64_t* number) {
return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
bool StringToNumber(const std::string& string, unsigned long* number) {
return StringToIntegerInternal<StringToUnsignedLongTraits>(string, number);
}
bool StringToNumber(const std::string& string, long long* number) {
return StringToIntegerInternal<StringToLongLongTraits>(string, number);
}
bool StringToNumber(const std::string& string, unsigned long long* number) {
return StringToIntegerInternal<StringToUnsignedLongLongTraits>(string,
number);
}
} // namespace crashpad

View File

@ -56,8 +56,10 @@ namespace crashpad {
//! where such prefix recognition is desirable.
bool StringToNumber(const std::string& string, int* number);
bool StringToNumber(const std::string& string, unsigned int* number);
bool StringToNumber(const std::string& string, int64_t* number);
bool StringToNumber(const std::string& string, uint64_t* number);
bool StringToNumber(const std::string& string, long* number);
bool StringToNumber(const std::string& string, unsigned long* number);
bool StringToNumber(const std::string& string, long long* number);
bool StringToNumber(const std::string& string, unsigned long long* number);
//! \}
} // namespace crashpad

View File

@ -16,30 +16,39 @@
#include <sys/types.h>
#include <array>
#include <limits>
#include <type_traits>
#include "base/stl_util.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
#define STRINGIFY(a) STR(a)
#define STR(a) #a
TEST(StringNumberConversion, StringToInt) {
static constexpr struct {
const char* string;
bool valid;
int value;
} kTestData[] = {
template <typename TValueType>
struct TestSpecification {
const char* string;
bool valid;
TValueType value;
};
// Signed 32-bit test data
template <typename TIntType,
typename std::enable_if<std::is_integral<TIntType>::value &&
std::is_signed<TIntType>::value &&
(sizeof(TIntType) == 4)>::type* = nullptr>
static constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {
return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, std::numeric_limits<int>::max()},
{"2147483647", true, std::numeric_limits<TIntType>::max()},
{"2147483648", false, 0},
{"4294967295", false, 0},
{"4294967296", false, 0},
{"-1", true, -1},
{"-2147483648", true, std::numeric_limits<int>::min()},
{"-2147483648", true, std::numeric_limits<TIntType>::min()},
{"-2147483649", false, 0},
{"00", true, 0},
{"01", true, 1},
@ -50,12 +59,12 @@ TEST(StringNumberConversion, StringToInt) {
{"+0x20", true, 32},
{"0xf", true, 15},
{"0xg", false, 0},
{"0x7fffffff", true, std::numeric_limits<int>::max()},
{"0x7FfFfFfF", true, std::numeric_limits<int>::max()},
{"0x7fffffff", true, std::numeric_limits<TIntType>::max()},
{"0x7FfFfFfF", true, std::numeric_limits<TIntType>::max()},
{"0x80000000", false, 0},
{"0xFFFFFFFF", false, 0},
{"-0x7fffffff", true, -2147483647},
{"-0x80000000", true, std::numeric_limits<int>::min()},
{"-0x80000000", true, std::numeric_limits<TIntType>::min()},
{"-0x80000001", false, 0},
{"-0xffffffff", false, 0},
{"0x100000000", false, 0},
@ -92,45 +101,22 @@ TEST(StringNumberConversion, StringToInt) {
{"9223372036854775809", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
};
for (size_t index = 0; index < base::size(kTestData); ++index) {
int value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(value, kTestData[index].value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
// Ensure that embedded NUL characters are treated as bad input. The string
// is split to avoid MSVC warning:
// "decimal digit terminates octal escape sequence".
static constexpr char input[] = "6\000" "6";
std::string input_string(input, base::size(input) - 1);
int output;
EXPECT_FALSE(StringToNumber(input_string, &output));
}};
}
TEST(StringNumberConversion, StringToUnsignedInt) {
static constexpr struct {
const char* string;
bool valid;
unsigned int value;
} kTestData[] = {
// Unsigned 32-bit test data
template <typename TIntType,
typename std::enable_if<std::is_integral<TIntType>::value &&
!std::is_signed<TIntType>::value &&
(sizeof(TIntType) == 4)>::type* = nullptr>
static constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {
return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, std::numeric_limits<unsigned int>::max()},
{"4294967295", true, std::numeric_limits<TIntType>::max()},
{"4294967296", false, 0},
{"-1", false, 0},
{"-2147483648", false, 0},
@ -186,9 +172,121 @@ TEST(StringNumberConversion, StringToUnsignedInt) {
{"9223372036854775809", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
};
}};
}
for (size_t index = 0; index < base::size(kTestData); ++index) {
// Signed 64-bit test data
template <typename TIntType,
typename std::enable_if<std::is_integral<TIntType>::value &&
std::is_signed<TIntType>::value &&
(sizeof(TIntType) == 8)>::type* = nullptr>
static constexpr std::array<TestSpecification<TIntType>, 24> kTestDataFunc() {
return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, std::numeric_limits<TIntType>::max()},
{"9223372036854775808", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
{"-1", true, -1},
{"-2147483648", true, INT64_C(-2147483648)},
{"-2147483649", true, INT64_C(-2147483649)},
{"-9223372036854775808", true, std::numeric_limits<TIntType>::min()},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, std::numeric_limits<TIntType>::max()},
{"0x8000000000000000", false, 0},
{"0xffffffffffffffff", false, 0},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", true, -9223372036854775807},
{"-0x8000000000000000", true, std::numeric_limits<TIntType>::min()},
{"-0x8000000000000001", false, 0},
{"0x7Fffffffffffffff", true, std::numeric_limits<TIntType>::max()},
}};
}
// Unsigned 64-bit test data
template <typename TIntType,
typename std::enable_if<std::is_integral<TIntType>::value &&
!std::is_signed<TIntType>::value &&
(sizeof(TIntType) == 8)>::type* = nullptr>
static constexpr std::array<TestSpecification<TIntType>, 25> kTestDataFunc() {
return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, 9223372036854775807},
{"9223372036854775808", true, 9223372036854775808u},
{"18446744073709551615", true, std::numeric_limits<TIntType>::max()},
{"18446744073709551616", false, 0},
{"-1", false, 0},
{"-2147483648", false, 0},
{"-2147483649", false, 0},
{"-2147483648", false, 0},
{"-9223372036854775808", false, 0},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, 9223372036854775807},
{"0x8000000000000000", true, 9223372036854775808u},
{"0xffffffffffffffff", true, std::numeric_limits<TIntType>::max()},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", false, 0},
{"-0x8000000000000000", false, 0},
{"-0x8000000000000001", false, 0},
{"0xFfffffffffffffff", true, std::numeric_limits<TIntType>::max()},
}};
}
// This string is split to avoid MSVC warning:
// "decimal digit terminates octal escape sequence".
static constexpr char kEmbeddedNullInputRaw[] = "6\000" "6";
namespace crashpad {
namespace test {
namespace {
TEST(StringNumberConversion, StringToInt) {
static_assert(sizeof(int) == 4, "Test only configured for 32-bit int.");
static constexpr auto kTestData = kTestDataFunc<int>();
for (size_t index = 0; index < kTestData.size(); ++index) {
int value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(value, kTestData[index].value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
// Ensure that embedded NUL characters are treated as bad input. The string
// is split to avoid MSVC warning:
// "decimal digit terminates octal escape sequence".
int output;
std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,
base::size(kEmbeddedNullInputRaw) - 1);
EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));
}
TEST(StringNumberConversion, StringToUnsignedInt) {
static_assert(sizeof(unsigned int) == 4,
"Test only configured for 32-bit unsigned int.");
static constexpr auto kTestData = kTestDataFunc<unsigned int>();
for (size_t index = 0; index < kTestData.size(); ++index) {
unsigned int value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
@ -207,46 +305,20 @@ TEST(StringNumberConversion, StringToUnsignedInt) {
// Ensure that embedded NUL characters are treated as bad input. The string
// is split to avoid MSVC warning:
// "decimal digit terminates octal escape sequence".
static constexpr char input[] = "6\000" "6";
std::string input_string(input, base::size(input) - 1);
unsigned int output;
EXPECT_FALSE(StringToNumber(input_string, &output));
std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,
base::size(kEmbeddedNullInputRaw) - 1);
EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));
}
TEST(StringNumberConversion, StringToInt64) {
static constexpr struct {
const char* string;
bool valid;
int64_t value;
} kTestData[] = {
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, std::numeric_limits<int64_t>::max()},
{"9223372036854775808", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
{"-1", true, -1},
{"-2147483648", true, INT64_C(-2147483648)},
{"-2147483649", true, INT64_C(-2147483649)},
{"-9223372036854775808", true, std::numeric_limits<int64_t>::min()},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
{"0x8000000000000000", false, 0},
{"0xffffffffffffffff", false, 0},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", true, -9223372036854775807},
{"-0x8000000000000000", true, std::numeric_limits<int64_t>::min()},
{"-0x8000000000000001", false, 0},
{"0x7Fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
};
TEST(StringNumberConversion, StringToLong) {
static_assert(
sizeof(long) == 4 || sizeof(long) == 8,
"Test not configured for " STRINGIFY(__SIZEOF_LONG__) "-byte long");
static constexpr auto kTestData = kTestDataFunc<long>();
for (size_t index = 0; index < base::size(kTestData); ++index) {
int64_t value;
for (size_t index = 0; index < kTestData.size(); ++index) {
long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
@ -262,41 +334,58 @@ TEST(StringNumberConversion, StringToInt64) {
}
}
TEST(StringNumberConversion, StringToUnsignedInt64) {
static constexpr struct {
const char* string;
bool valid;
uint64_t value;
} kTestData[] = {
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, 9223372036854775807},
{"9223372036854775808", true, 9223372036854775808u},
{"18446744073709551615", true, std::numeric_limits<uint64_t>::max()},
{"18446744073709551616", false, 0},
{"-1", false, 0},
{"-2147483648", false, 0},
{"-2147483649", false, 0},
{"-2147483648", false, 0},
{"-9223372036854775808", false, 0},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, 9223372036854775807},
{"0x8000000000000000", true, 9223372036854775808u},
{"0xffffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", false, 0},
{"-0x8000000000000000", false, 0},
{"-0x8000000000000001", false, 0},
{"0xFfffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
};
TEST(StringNumberConversion, StringToUnsignedLong) {
static_assert(
sizeof(long) == 4 || sizeof(long) == 8,
"Test not configured for " STRINGIFY(__SIZEOF_LONG__) "-byte long");
static constexpr auto kTestData = kTestDataFunc<unsigned long>();
for (size_t index = 0; index < base::size(kTestData); ++index) {
uint64_t value;
for (size_t index = 0; index < kTestData.size(); ++index) {
unsigned long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(value, kTestData[index].value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
}
TEST(StringNumberConversion, StringToLongLong) {
static_assert(sizeof(long long) == 8,
"Test only configured for 64-bit long long.");
static constexpr auto kTestData = kTestDataFunc<long long>();
for (size_t index = 0; index < kTestData.size(); ++index) {
long long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(value, kTestData[index].value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
}
TEST(StringNumberConversion, StringToUnsignedLongLong) {
static_assert(sizeof(unsigned long long) == 8,
"Test only configured for 64-bit unsigned long long.");
static constexpr auto kTestData = kTestDataFunc<unsigned long long>();
for (size_t index = 0; index < kTestData.size(); ++index) {
unsigned long long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "