diff --git a/rtc_base/string_to_number.cc b/rtc_base/string_to_number.cc index 351610f31a..ab3f1abb8e 100644 --- a/rtc_base/string_to_number.cc +++ b/rtc_base/string_to_number.cc @@ -20,30 +20,41 @@ namespace rtc { namespace string_to_number_internal { -absl::optional ParseSigned(const char* str, int base) { - RTC_DCHECK(str); +absl::optional ParseSigned(absl::string_view str, int base) { + if (str.empty()) + return absl::nullopt; + if (isdigit(str[0]) || str[0] == '-') { + std::string str_str = std::string(str); char* end = nullptr; errno = 0; - const signed_type value = std::strtoll(str, &end, base); - if (end && *end == '\0' && errno == 0) { + const signed_type value = std::strtoll(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0) { return value; } } return absl::nullopt; } -absl::optional ParseUnsigned(const char* str, int base) { - RTC_DCHECK(str); +absl::optional ParseUnsigned(absl::string_view str, int base) { + if (str.empty()) + return absl::nullopt; + if (isdigit(str[0]) || str[0] == '-') { + std::string str_str = std::string(str); // Explicitly discard negative values. std::strtoull parsing causes unsigned // wraparound. We cannot just reject values that start with -, though, since // -0 is perfectly fine, as is -0000000000000000000000000000000. const bool is_negative = str[0] == '-'; char* end = nullptr; errno = 0; - const unsigned_type value = std::strtoull(str, &end, base); - if (end && *end == '\0' && errno == 0 && (value == 0 || !is_negative)) { + const unsigned_type value = std::strtoull(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0 && + (value == 0 || !is_negative)) { return value; } } @@ -69,22 +80,25 @@ inline long double StrToT(const char* str, char** str_end) { } template -absl::optional ParseFloatingPoint(const char* str) { - RTC_DCHECK(str); - if (*str == '\0') +absl::optional ParseFloatingPoint(absl::string_view str) { + if (str.empty()) return absl::nullopt; + + if (str[0] == '\0') + return absl::nullopt; + std::string str_str = std::string(str); char* end = nullptr; errno = 0; - const T value = StrToT(str, &end); - if (end && *end == '\0' && errno == 0) { + const T value = StrToT(str_str.c_str(), &end); + if (end == str_str.c_str() + str_str.size() && errno == 0) { return value; } return absl::nullopt; } -template absl::optional ParseFloatingPoint(const char* str); -template absl::optional ParseFloatingPoint(const char* str); -template absl::optional ParseFloatingPoint(const char* str); +template absl::optional ParseFloatingPoint(absl::string_view str); +template absl::optional ParseFloatingPoint(absl::string_view str); +template absl::optional ParseFloatingPoint(absl::string_view str); } // namespace string_to_number_internal } // namespace rtc diff --git a/rtc_base/string_to_number.h b/rtc_base/string_to_number.h index 4cb521595d..1d704ee464 100644 --- a/rtc_base/string_to_number.h +++ b/rtc_base/string_to_number.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" namespace rtc { @@ -25,10 +26,9 @@ namespace rtc { // functions (std::stoi, etc.) indicate errors by throwing exceptions, which // are disabled in WebRTC. // -// Integers are parsed using one of the following functions: -// absl::optional StringToNumber(const char* str, int base = 10); -// absl::optional StringToNumber(const std::string& str, -// int base = 10); +// Integers are parsed using: +// absl::optional StringToNumber(absl::string_view str, +// int base = 10); // // These functions parse a value from the beginning of a string into one of the // fundamental integer types, or returns an empty Optional if parsing @@ -38,26 +38,23 @@ namespace rtc { // By setting base to 0, one of octal, decimal or hexadecimal will be // detected from the string's prefix (0, nothing or 0x, respectively). // If non-zero, base can be set to a value between 2 and 36 inclusively. -// -// If desired, this interface could be extended with support for floating-point -// types. namespace string_to_number_internal { // These must be (unsigned) long long, to match the signature of strto(u)ll. using unsigned_type = unsigned long long; // NOLINT(runtime/int) using signed_type = long long; // NOLINT(runtime/int) -absl::optional ParseSigned(const char* str, int base); -absl::optional ParseUnsigned(const char* str, int base); +absl::optional ParseSigned(absl::string_view str, int base); +absl::optional ParseUnsigned(absl::string_view str, int base); template -absl::optional ParseFloatingPoint(const char* str); +absl::optional ParseFloatingPoint(absl::string_view str); } // namespace string_to_number_internal template typename std::enable_if::value && std::is_signed::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { using string_to_number_internal::signed_type; static_assert( std::numeric_limits::max() <= @@ -78,7 +75,7 @@ template typename std::enable_if::value && std::is_unsigned::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { using string_to_number_internal::unsigned_type; static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), @@ -95,7 +92,7 @@ StringToNumber(const char* str, int base = 10) { template typename std::enable_if::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { static_assert( std::numeric_limits::max() <= std::numeric_limits::max(), "StringToNumber only supports floating-point numbers as large " @@ -103,14 +100,6 @@ StringToNumber(const char* str, int base = 10) { return string_to_number_internal::ParseFloatingPoint(str); } -// The std::string overloads only exists if there is a matching const char* -// version. -template -auto StringToNumber(const std::string& str, int base = 10) - -> decltype(StringToNumber(str.c_str(), base)) { - return StringToNumber(str.c_str(), base); -} - } // namespace rtc #endif // RTC_BASE_STRING_TO_NUMBER_H_ diff --git a/rtc_base/string_to_number_unittest.cc b/rtc_base/string_to_number_unittest.cc index f460b15e58..2d4c8d0ce7 100644 --- a/rtc_base/string_to_number_unittest.cc +++ b/rtc_base/string_to_number_unittest.cc @@ -15,6 +15,8 @@ #include #include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "test/gtest.h" namespace rtc { @@ -80,6 +82,10 @@ TYPED_TEST_P(BasicNumberTest, TestInvalidInputs) { const char kInvalidCharArray[] = "Invalid string containing 47"; const char kPlusMinusCharArray[] = "+-100"; const char kNumberFollowedByCruft[] = "640x480"; + const char kEmbeddedNul[] = {'1', '2', '\0', '3', '4'}; + const char kBeginningEmbeddedNul[] = {'\0', '1', '2', '3', '4'}; + const char kTrailingEmbeddedNul[] = {'1', '2', '3', '4', '\0'}; + EXPECT_EQ(absl::nullopt, StringToNumber(kInvalidCharArray)); EXPECT_EQ(absl::nullopt, StringToNumber(std::string(kInvalidCharArray))); EXPECT_EQ(absl::nullopt, StringToNumber(kPlusMinusCharArray)); @@ -92,6 +98,23 @@ TYPED_TEST_P(BasicNumberTest, TestInvalidInputs) { EXPECT_EQ(absl::nullopt, StringToNumber("- 5")); EXPECT_EQ(absl::nullopt, StringToNumber(" -5")); EXPECT_EQ(absl::nullopt, StringToNumber("5 ")); + // Test various types of empty inputs + EXPECT_EQ(absl::nullopt, StringToNumber(nullptr)); + EXPECT_EQ(absl::nullopt, StringToNumber("")); + EXPECT_EQ(absl::nullopt, StringToNumber(std::string())); + EXPECT_EQ(absl::nullopt, StringToNumber(std::string(""))); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view())); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view(nullptr))); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view(""))); + // Test strings with embedded nuls. + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view( + kEmbeddedNul, sizeof(kEmbeddedNul)))); + EXPECT_EQ(absl::nullopt, + StringToNumber(absl::string_view( + kBeginningEmbeddedNul, sizeof(kBeginningEmbeddedNul)))); + EXPECT_EQ(absl::nullopt, + StringToNumber(absl::string_view(kTrailingEmbeddedNul, + sizeof(kTrailingEmbeddedNul)))); } REGISTER_TYPED_TEST_SUITE_P(BasicNumberTest,