diff --git a/libtransmission/rpcimpl.cc b/libtransmission/rpcimpl.cc index a7281f486..0ff07bc91 100644 --- a/libtransmission/rpcimpl.cc +++ b/libtransmission/rpcimpl.cc @@ -1382,7 +1382,7 @@ static char const* removeTrackers(tr_torrent* tor, tr_variant* ids) } /* sort trackerIds and remove from largest to smallest so there is no need to recalculate array indicies */ - qsort(tids, t, sizeof(int), compareInt); + std::sort(tids, tids + t); bool changed = false; int dup = -1; @@ -2917,8 +2917,8 @@ void tr_rpc_request_exec_json( */ void tr_rpc_parse_list_str(tr_variant* setme, char const* str, size_t len) { - int valueCount; - int* values = tr_parseNumberRange(str, len, &valueCount); + auto const values = tr_parseNumberRange(str, len); + auto const valueCount = std::size(values); if (valueCount == 0) { @@ -2932,13 +2932,11 @@ void tr_rpc_parse_list_str(tr_variant* setme, char const* str, size_t len) { tr_variantInitList(setme, valueCount); - for (int i = 0; i < valueCount; ++i) + for (auto const& value : values) { - tr_variantListAddInt(setme, values[i]); + tr_variantListAddInt(setme, value); } } - - tr_free(values); } void tr_rpc_request_exec_uri( diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index 6e5dd08b4..6d00e228c 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -12,6 +12,7 @@ #endif #endif +#include // std::sort #include // std::array #include /* isdigit(), tolower() */ #include @@ -23,6 +24,17 @@ #include /* getenv() */ #include /* strerror(), memset(), memmem() */ #include /* nanosleep() */ +#include +#include +#include +#include + +#if defined(__GNUC__) && !__has_include() +#undef HAVE_CHARCONV +#else +#define HAVE_CHARCONV 1 +#include // std::from_chars() +#endif #ifdef _WIN32 #include /* WSAStartup() */ @@ -1393,61 +1405,46 @@ 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, size_t len, struct number_range* setme) +static bool parseNumberSection(char const* str, char const* const end, number_range& range) { - long a; - long b; bool success; - char* end; - int const error = errno; - char* tmp = tr_strndup(str, len); + auto const error = errno; - errno = 0; - a = b = strtol(tmp, &end, 10); - - if (errno != 0 || end == tmp) +#if defined(HAVE_CHARCONV) + auto result = std::from_chars(str, end, range.low); + success = result.ec == std::errc{}; + if (success) + { + range.high = range.low; + if (result.ptr != end && *result.ptr == '-') + { + result = std::from_chars(result.ptr + 1, end, range.high); + success = result.ec == std::errc{}; + } + } +#else + try + { + auto tmp = std::string(str, end); + auto pos = size_t{}; + range.low = range.high = std::stoi(tmp, &pos); + if (pos != std::size(tmp) && tmp[pos] == '-') + { + tmp.erase(0, pos + 1); + range.high = std::stoi(tmp, &pos); + } + success = true; + } + catch (std::exception&) { success = false; } - else if (*end != '-') - { - success = true; - } - else - { - char const* pch = end + 1; - b = strtol(pch, &end, 10); - - if (errno != 0 || pch == end) - { - success = false; - } - else if (*end != '\0') /* trailing data */ - { - success = false; - } - else - { - success = true; - } - } - - tr_free(tmp); - - setme->low = (int)std::min(a, b); // FIXME: narrowing long to int - setme->high = (int)std::max(a, b); +#endif errno = error; return success; } -int compareInt(void const* va, void const* vb) -{ - int const a = *(int const*)va; - int const b = *(int const*)vb; - return a - b; -} - /** * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an * array of setmeCount ints of all the values in the array. @@ -1455,106 +1452,29 @@ int compareInt(void const* va, void const* vb) * It's the caller's responsibility to call tr_free () on the returned array. * If a fragment of the string can't be parsed, NULL is returned. */ -int* tr_parseNumberRange(char const* str_in, size_t len, int* setmeCount) +std::vector tr_parseNumberRange(char const* str, size_t len) // TODO: string_view { - int n = 0; - int* uniq = nullptr; - char* str = tr_strndup(str_in, len); - char const* walk; - tr_list* ranges = nullptr; - bool success = true; + auto values = std::set{}; - walk = str; - - while (!tr_str_is_empty(walk) && success) + auto const* const end = str + (len != TR_BAD_SIZE ? len : strlen(str)); + for (auto const* walk = str; walk < end;) { - struct number_range range; - char const* pch = strchr(walk, ','); - - if (pch != nullptr) + auto delim = std::find(walk, end, ','); + auto range = number_range{}; + if (!parseNumberSection(walk, delim, range)) { - success = parseNumberSection(walk, (size_t)(pch - walk), &range); - walk = pch + 1; - } - else - { - success = parseNumberSection(walk, strlen(walk), &range); - walk += strlen(walk); + break; } - if (success) + for (auto i = range.low; i <= range.high; ++i) { - tr_list_append(&ranges, tr_memdup(&range, sizeof(struct number_range))); + values.insert(i); } + + walk = delim + 1; } - if (!success) - { - *setmeCount = 0; - uniq = nullptr; - } - else - { - int n2; - int* sorted = nullptr; - - /* build a sorted number array */ - n = n2 = 0; - - for (tr_list* l = ranges; l != nullptr; l = l->next) - { - auto const* r = static_cast(l->data); - n += r->high + 1 - r->low; - } - - sorted = tr_new(int, n); - - if (sorted == nullptr) - { - n = 0; - uniq = nullptr; - } - else - { - for (tr_list* l = ranges; l != nullptr; l = l->next) - { - auto const* r = static_cast(l->data); - - for (int i = r->low; i <= r->high; ++i) - { - sorted[n2++] = i; - } - } - - qsort(sorted, n, sizeof(int), compareInt); - TR_ASSERT(n == n2); - - /* remove duplicates */ - uniq = tr_new(int, n); - n = 0; - - if (uniq != nullptr) - { - for (int i = 0; i < n2; ++i) - { - if (n == 0 || uniq[n - 1] != sorted[i]) - { - uniq[n++] = sorted[i]; - } - } - } - - tr_free(sorted); - } - } - - /* cleanup */ - tr_list_free(&ranges, tr_free); - tr_free(str); - - /* return the result */ - *setmeCount = n; - return uniq; + return { std::begin(values), std::end(values) }; } /*** diff --git a/libtransmission/utils.h b/libtransmission/utils.h index d643bb6e6..732321c0a 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -12,6 +12,7 @@ #include #include /* size_t */ #include /* time_t */ +#include #include "tr-macros.h" @@ -256,8 +257,6 @@ char* tr_strjoin(char const* const* arr, size_t len, char const* delim); **** ***/ -int compareInt(void const* va, void const* vb); - void tr_binary_to_hex(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2); void tr_hex_to_binary(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2); @@ -287,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. */ -int* tr_parseNumberRange(char const* str, size_t str_len, int* setmeCount) TR_GNUC_MALLOC TR_GNUC_NONNULL(1); +std::vector tr_parseNumberRange(char const* str, size_t str_len) TR_GNUC_NONNULL(1); /** * @brief truncate a double value at a given number of decimal places. diff --git a/libtransmission/wildmat.c b/libtransmission/wildmat.c index 0ba5661b7..82f9e14e8 100644 --- a/libtransmission/wildmat.c +++ b/libtransmission/wildmat.c @@ -37,8 +37,7 @@ ** on. */ -#include "transmission.h" -#include "utils.h" +#include #define ABORT -1 diff --git a/tests/libtransmission/utils-test.cc b/tests/libtransmission/utils-test.cc index 27d47460a..98bab36de 100644 --- a/tests/libtransmission/utils-test.cc +++ b/tests/libtransmission/utils-test.cc @@ -25,6 +25,8 @@ #include #include // sqrt() #include // setenv(), unsetenv() +#include +#include #include using ::libtransmission::test::makeString; @@ -136,38 +138,31 @@ TEST_F(UtilsTest, trUtf8clean) TEST_F(UtilsTest, numbers) { - auto count = int{}; - auto* numbers = tr_parseNumberRange("1-10,13,16-19", TR_BAD_SIZE, &count); - EXPECT_EQ(15, count); - EXPECT_EQ(1, numbers[0]); - EXPECT_EQ(6, numbers[5]); - EXPECT_EQ(10, numbers[9]); - EXPECT_EQ(13, numbers[10]); - EXPECT_EQ(16, numbers[11]); - EXPECT_EQ(19, numbers[14]); - tr_free(numbers); - - numbers = tr_parseNumberRange("1-5,3-7,2-6", TR_BAD_SIZE, &count); - EXPECT_EQ(7, count); - EXPECT_NE(nullptr, numbers); - for (int i = 0; i < count; ++i) + auto const tostring = [](std::vector const& v) { - EXPECT_EQ(i + 1, numbers[i]); - } + std::stringstream ss; + for (auto const& i : v) + { + ss << i << ' '; + } + return ss.str(); + }; - tr_free(numbers); + auto numbers = tr_parseNumberRange("1-10,13,16-19", TR_BAD_SIZE); + 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-Hello", TR_BAD_SIZE, &count); - EXPECT_EQ(0, count); - EXPECT_EQ(nullptr, numbers); + numbers = tr_parseNumberRange("1-5,3-7,2-6", TR_BAD_SIZE); + EXPECT_EQ(std::string("1 2 3 4 5 6 7 "), tostring(numbers)); - numbers = tr_parseNumberRange("1-", TR_BAD_SIZE, &count); - EXPECT_EQ(0, count); - EXPECT_EQ(nullptr, numbers); + numbers = tr_parseNumberRange("1-Hello", TR_BAD_SIZE); + auto const empty_string = std::string{}; + EXPECT_EQ(empty_string, tostring(numbers)); - numbers = tr_parseNumberRange("Hello", TR_BAD_SIZE, &count); - EXPECT_EQ(0, count); - EXPECT_EQ(nullptr, numbers); + numbers = tr_parseNumberRange("1-", TR_BAD_SIZE); + EXPECT_EQ(empty_string, tostring(numbers)); + + numbers = tr_parseNumberRange("Hello", TR_BAD_SIZE); + EXPECT_EQ(empty_string, tostring(numbers)); } namespace diff --git a/utils/remote.cc b/utils/remote.cc index 41af0fd98..de4bd9221 100644 --- a/utils/remote.cc +++ b/utils/remote.cc @@ -640,27 +640,20 @@ static void addDays(tr_variant* args, tr_quark const key, char const* arg) if (arg != NULL) { - int valueCount; - int* values; - - values = tr_parseNumberRange(arg, TR_BAD_SIZE, &valueCount); - - for (int i = 0; i < valueCount; ++i) + for (int& day : tr_parseNumberRange(arg, TR_BAD_SIZE)) { - if (values[i] < 0 || values[i] > 7) + if (day < 0 || day > 7) { continue; } - if (values[i] == 7) + if (day == 7) { - values[i] = 0; + day = 0; } - days |= 1 << values[i]; + days |= 1 << day; } - - tr_free(values); } if (days != 0) @@ -708,15 +701,10 @@ static void addFiles(tr_variant* args, tr_quark const key, char const* arg) if (strcmp(arg, "all") != 0) { - int valueCount; - int* values = tr_parseNumberRange(arg, TR_BAD_SIZE, &valueCount); - - for (int i = 0; i < valueCount; ++i) + for (auto const& idx : tr_parseNumberRange(arg, TR_BAD_SIZE)) { - tr_variantListAddInt(files, values[i]); + tr_variantListAddInt(files, idx); } - - tr_free(values); } }