From 3e7b8eb7bbeaeeb796c823e95c5605eb0fea0225 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 15 Oct 2021 17:15:33 -0500 Subject: [PATCH] refactor: tr_parseNumberRange() takes a std::string_view (#1962) * tr_parseNumberRange() now takes a std::string_view --- libtransmission/rpcimpl.cc | 7 ++++++- libtransmission/utils.cc | 30 ++++++++++++++++------------- libtransmission/utils.h | 2 +- tests/libtransmission/utils-test.cc | 15 +++++++++------ utils/remote.cc | 4 ++-- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index 74dbc3c92..528181bce 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -2840,7 +2840,12 @@ void tr_rpc_request_exec_json( */ void tr_rpc_parse_list_str(tr_variant* setme, char const* str, size_t len) { - auto const values = tr_parseNumberRange(str, len); + if (len == TR_BAD_SIZE) + { + len = strlen(str); + } + + auto const values = tr_parseNumberRange(std::string_view{ str, len }); auto const valueCount = std::size(values); if (valueCount == 0) diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index 3f98ab0c6..5c3b12598 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -1371,27 +1371,30 @@ struct number_range * This should be a single number (ex. "6") or a range (ex. "6-9"). * Anything else is an error and will return failure. */ -static bool parseNumberSection(char const* str, char const* const end, number_range& range) +static bool parseNumberSection(std::string_view str, number_range& range) { bool success; auto const error = errno; #if defined(HAVE_CHARCONV) - auto result = std::from_chars(str, end, range.low); + // wants char*, so string_view::iterator don't work. make our own begin/end + auto const* const begin_ch = std::data(str); + auto const* const end_ch = begin_ch + std::size(str); + auto result = std::from_chars(begin_ch, end_ch, range.low); success = result.ec == std::errc{}; if (success) { range.high = range.low; - if (result.ptr != end && *result.ptr == '-') + if (result.ptr < end_ch && *result.ptr == '-') { - result = std::from_chars(result.ptr + 1, end, range.high); + result = std::from_chars(result.ptr + 1, end_ch, range.high); success = result.ec == std::errc{}; } } #else try { - auto tmp = std::string(str, end); + auto tmp = std::string(str); auto pos = size_t{}; range.low = range.high = std::stoi(tmp, &pos); if (pos != std::size(tmp) && tmp[pos] == '-') @@ -1418,26 +1421,27 @@ static bool parseNumberSection(char const* str, char const* const end, number_ra * It's the caller's responsibility to call tr_free () on the returned array. * If a fragment of the string can't be parsed, nullptr is returned. */ -std::vector tr_parseNumberRange(char const* str, size_t len) // TODO: string_view +std::vector tr_parseNumberRange(std::string_view str) { auto values = std::set{}; - auto const* const end = str + (len != TR_BAD_SIZE ? len : strlen(str)); - for (auto const* walk = str; walk < end;) + for (;;) { - auto delim = std::find(walk, end, ','); + auto const delim = str.find(','); auto range = number_range{}; - if (!parseNumberSection(walk, delim, range)) + if (!parseNumberSection(str.substr(0, delim), range)) { break; } - for (auto i = range.low; i <= range.high; ++i) { values.insert(i); } - - walk = delim + 1; + if (delim == std::string_view::npos) + { + break; + } + str.remove_prefix(delim + 1); } return { std::begin(values), std::end(values) }; diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 912185dd8..78dbda194 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -286,7 +286,7 @@ double tr_getRatio(uint64_t numerator, uint64_t denominator); * * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4. */ -std::vector tr_parseNumberRange(char const* str, size_t str_len) TR_GNUC_NONNULL(1); +std::vector tr_parseNumberRange(std::string_view str); /** * @brief truncate a double value at a given number of decimal places. diff --git a/tests/libtransmission/utils-test.cc b/tests/libtransmission/utils-test.cc index 5c73ff258..bd02dfa1d 100644 --- a/tests/libtransmission/utils-test.cc +++ b/tests/libtransmission/utils-test.cc @@ -6,6 +6,9 @@ * */ +#include +#include + #ifdef _WIN32 #include #define setenv(key, value, unused) SetEnvironmentVariableA(key, value) @@ -30,8 +33,8 @@ #include using ::libtransmission::test::makeString; - using UtilsTest = ::testing::Test; +using namespace std::literals; TEST_F(UtilsTest, trStripPositionalArgs) { @@ -125,20 +128,20 @@ TEST_F(UtilsTest, numbers) return ss.str(); }; - auto numbers = tr_parseNumberRange("1-10,13,16-19", TR_BAD_SIZE); + auto numbers = tr_parseNumberRange("1-10,13,16-19"sv); EXPECT_EQ(std::string("1 2 3 4 5 6 7 8 9 10 13 16 17 18 19 "), tostring(numbers)); - numbers = tr_parseNumberRange("1-5,3-7,2-6", TR_BAD_SIZE); + numbers = tr_parseNumberRange("1-5,3-7,2-6"sv); EXPECT_EQ(std::string("1 2 3 4 5 6 7 "), tostring(numbers)); - numbers = tr_parseNumberRange("1-Hello", TR_BAD_SIZE); + numbers = tr_parseNumberRange("1-Hello"sv); auto const empty_string = std::string{}; EXPECT_EQ(empty_string, tostring(numbers)); - numbers = tr_parseNumberRange("1-", TR_BAD_SIZE); + numbers = tr_parseNumberRange("1-"sv); EXPECT_EQ(empty_string, tostring(numbers)); - numbers = tr_parseNumberRange("Hello", TR_BAD_SIZE); + numbers = tr_parseNumberRange("Hello"sv); EXPECT_EQ(empty_string, tostring(numbers)); } diff --git a/utils/remote.cc b/utils/remote.cc index 96866e284..08acf0ddc 100644 --- a/utils/remote.cc +++ b/utils/remote.cc @@ -645,7 +645,7 @@ static void addDays(tr_variant* args, tr_quark const key, char const* arg) if (arg != nullptr) { - for (int& day : tr_parseNumberRange(arg, TR_BAD_SIZE)) + for (int& day : tr_parseNumberRange(arg)) { if (day < 0 || day > 7) { @@ -706,7 +706,7 @@ static void addFiles(tr_variant* args, tr_quark const key, char const* arg) if (strcmp(arg, "all") != 0) { - for (auto const& idx : tr_parseNumberRange(arg, TR_BAD_SIZE)) + for (auto const& idx : tr_parseNumberRange(arg)) { tr_variantListAddInt(files, idx); }