diff --git a/adb/adb_utils.h b/adb/adb_utils.h index 8712e1bfc..8253487a8 100644 --- a/adb/adb_utils.h +++ b/adb/adb_utils.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -107,3 +109,34 @@ inline std::string_view StripTrailingNulls(std::string_view str) { str.remove_suffix(n); return str; } + +// Base-10 stroll on a string_view. +template +inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) { + if (str.empty() || !isdigit(str[0])) { + return false; + } + + T value = 0; + std::string_view::iterator it; + constexpr T max = std::numeric_limits::max(); + for (it = str.begin(); it != str.end() && isdigit(*it); ++it) { + if (value > max / 10) { + return false; + } + + value *= 10; + + T digit = *it - '0'; + if (value > max - digit) { + return false; + } + + value += digit; + } + *result = value; + if (remaining) { + *remaining = str.substr(it - str.begin()); + } + return true; +} diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp index 870f6f048..bb094258c 100644 --- a/adb/adb_utils_test.cpp +++ b/adb/adb_utils_test.cpp @@ -181,3 +181,48 @@ TEST(adb_utils, test_forward_targets_are_valid) { EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error)); EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error)); } + +void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) { + // Standalone. + { + uint32_t value; + std::string_view remaining; + bool success = ParseUint(&value, string, &remaining); + EXPECT_EQ(success, expected_success); + if (expected_success) { + EXPECT_EQ(value, expected_value); + } + EXPECT_TRUE(remaining.empty()); + } + + // With trailing text. + { + std::string text = std::string(string) + "foo"; + uint32_t value; + std::string_view remaining; + bool success = ParseUint(&value, text, &remaining); + EXPECT_EQ(success, expected_success); + if (expected_success) { + EXPECT_EQ(value, expected_value); + EXPECT_EQ(remaining, "foo"); + } + } +} + +TEST(adb_utils, ParseUint) { + TestParseUint("", false); + TestParseUint("foo", false); + TestParseUint("foo123", false); + TestParseUint("-1", false); + + TestParseUint("123", true, 123); + TestParseUint("9999999999999999999999999", false); + TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX); + TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX); + TestParseUint(std::to_string(static_cast(UINT32_MAX) + 1), false); + TestParseUint("0" + std::to_string(static_cast(UINT32_MAX) + 1), false); + + std::string x = std::to_string(UINT32_MAX) + "123"; + std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size()); + TestParseUint(substr, true, UINT32_MAX); +}