refactor: add tr_compare_3way() (#5742)

* refactor: add tr_compare_3way()

This is a small templated utility function to make libtransmission's
sorting / comparison code more consistent and easier to read.
This commit is contained in:
Charles Kerr 2023-07-06 18:51:08 -05:00 committed by GitHub
parent c364abcb6f
commit fdf042d32c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 90 deletions

View File

@ -64,35 +64,22 @@ struct StopsCompare
// primary key: volume of data transferred
auto const ax = one.up + one.down;
auto const bx = two.up + two.down;
if (ax < bx)
if (auto const val = tr_compare_3way(ax, bx); val != 0)
{
return -1;
}
if (ax > bx)
{
return 1;
return val;
}
// secondary key: the torrent's info_hash
for (size_t i = 0, n = sizeof(tr_sha1_digest_t); i < n; ++i)
{
if (one.info_hash[i] != two.info_hash[i])
if (auto const val = tr_compare_3way(one.info_hash[i], two.info_hash[i]); val != 0)
{
return one.info_hash[i] < two.info_hash[i] ? -1 : 1;
return val;
}
}
// tertiary key: the tracker's announce url
if (one.announce_url < two.announce_url)
{
return -1;
}
if (one.announce_url > two.announce_url)
{
return 1;
}
return 0;
return tr_compare_3way(one.announce_url, two.announce_url);
}
[[nodiscard]] constexpr auto operator()(tr_announce_request const& one, tr_announce_request const& two) const noexcept
@ -1478,15 +1465,15 @@ namespace upkeep_helpers
int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
{
/* prefer higher-priority events */
if (auto const priority_a = a->announce_event_priority, priority_b = b->announce_event_priority; priority_a != priority_b)
if (auto const val = tr_compare_3way(a->announce_event_priority, b->announce_event_priority); val != 0)
{
return priority_a > priority_b ? -1 : 1;
return -val;
}
/* prefer swarms where we might upload */
if (auto const leechers_a = a->countDownloaders(), leechers_b = b->countDownloaders(); leechers_a != leechers_b)
if (auto const val = tr_compare_3way(a->countDownloaders(), b->countDownloaders()); val != 0)
{
return leechers_a > leechers_b ? -1 : 1;
return -val;
}
/* prefer swarms where we might download */
@ -1496,17 +1483,18 @@ int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
}
/* prefer larger stats, to help ensure stats get recorded when stopping on shutdown */
if (auto const xa = a->byteCounts[TR_ANN_UP] + a->byteCounts[TR_ANN_DOWN],
xb = b->byteCounts[TR_ANN_UP] + b->byteCounts[TR_ANN_DOWN];
xa != xb)
if (auto const val = tr_compare_3way(
a->byteCounts[TR_ANN_UP] + a->byteCounts[TR_ANN_DOWN],
b->byteCounts[TR_ANN_UP] + b->byteCounts[TR_ANN_DOWN]);
val != 0)
{
return xa > xb ? -1 : 1;
return -val;
}
// announcements that have been waiting longer go first
if (a->announceAt != b->announceAt)
if (auto const val = tr_compare_3way(a->announceAt, b->announceAt); val != 0)
{
return a->announceAt < b->announceAt ? -1 : 1;
return val;
}
// the tiers are effectively equal priority, but add an arbitrary

View File

@ -590,9 +590,9 @@ std::pair<sockaddr_storage, socklen_t> tr_address::to_sockaddr(tr_port port) con
int tr_address::compare(tr_address const& that) const noexcept // <=>
{
// IPv6 addresses are always "greater than" IPv4
if (this->type != that.type)
if (auto const val = tr_compare_3way(this->type, that.type); val != 0)
{
return this->is_ipv4() ? 1 : -1;
return val;
}
return this->is_ipv4() ? memcmp(&this->addr.addr4, &that.addr.addr4, sizeof(this->addr.addr4)) :

View File

@ -34,26 +34,21 @@ struct Candidate
{
}
[[nodiscard]] int compare(Candidate const& that) const // <=>
[[nodiscard]] constexpr auto compare(Candidate const& that) const noexcept // <=>
{
// prefer pieces closer to completion
if (n_blocks_missing != that.n_blocks_missing)
if (auto const val = tr_compare_3way(n_blocks_missing, that.n_blocks_missing); val != 0)
{
return n_blocks_missing < that.n_blocks_missing ? -1 : 1;
return val;
}
// prefer higher priority
if (priority != that.priority)
if (auto const val = tr_compare_3way(priority, that.priority); val != 0)
{
return priority > that.priority ? -1 : 1;
return -val;
}
if (salt != that.salt)
{
return salt < that.salt ? -1 : 1;
}
return 0;
return tr_compare_3way(salt, that.salt);
}
bool operator<(Candidate const& that) const // less than

View File

@ -1317,22 +1317,17 @@ constexpr struct
{
[[nodiscard]] constexpr static int compare(peer_atom const& a, peer_atom const& b) noexcept // <=>
{
if (a.piece_data_time != b.piece_data_time)
if (auto const val = tr_compare_3way(a.piece_data_time, b.piece_data_time); val != 0)
{
return a.piece_data_time > b.piece_data_time ? -1 : 1;
return -val;
}
if (a.fromBest != b.fromBest)
if (auto const val = tr_compare_3way(a.fromBest, b.fromBest); val != 0)
{
return a.fromBest < b.fromBest ? -1 : 1;
return val;
}
if (a.num_fails != b.num_fails)
{
return a.num_fails < b.num_fails ? -1 : 1;
}
return 0;
return tr_compare_3way(a.num_fails, b.num_fails);
}
[[nodiscard]] constexpr bool operator()(peer_atom const& a, peer_atom const& b) const noexcept
@ -1775,9 +1770,10 @@ struct ChokeData
[[nodiscard]] constexpr auto compare(ChokeData const& that) const noexcept // <=>
{
if (this->rate != that.rate) // prefer higher overall speeds
// prefer higher overall speeds
if (auto const val = tr_compare_3way(this->rate, that.rate); val != 0)
{
return this->rate > that.rate ? -1 : 1;
return -val;
}
if (this->was_choked != that.was_choked) // prefer unchoked
@ -1785,12 +1781,7 @@ struct ChokeData
return this->was_choked ? 1 : -1;
}
if (this->salt != that.salt) // random order
{
return this->salt < that.salt ? -1 : 1;
}
return 0;
return tr_compare_3way(this->salt, that.salt);
}
[[nodiscard]] constexpr auto operator<(ChokeData const& that) const noexcept
@ -2058,18 +2049,13 @@ constexpr struct
}
/* the one to give us data more recently goes first */
if (a->atom->piece_data_time != b->atom->piece_data_time)
if (auto const val = tr_compare_3way(a->atom->piece_data_time, b->atom->piece_data_time); val != 0)
{
return a->atom->piece_data_time > b->atom->piece_data_time ? -1 : 1;
return -val;
}
/* the one we connected to most recently goes first */
if (a->atom->time != b->atom->time)
{
return a->atom->time > b->atom->time ? -1 : 1;
}
return 0;
return -tr_compare_3way(a->atom->time, b->atom->time);
}
[[nodiscard]] constexpr bool operator()(tr_peer const* a, tr_peer const* b) const // less than

View File

@ -166,6 +166,22 @@ template<typename T>
return !std::empty(sv) && sv.back() == key;
}
template<typename T>
[[nodiscard]] constexpr int tr_compare_3way(T const& left, T const& right)
{
if (left < right)
{
return -1;
}
if (right < left)
{
return 1;
}
return 0;
}
constexpr std::string_view tr_strv_sep(std::string_view* sv, char delim)
{
auto pos = sv->find(delim);

View File

@ -37,24 +37,19 @@ int tr_verify_worker::Node::compare(tr_verify_worker::Node const& that) const
// higher priority comes before lower priority
auto const pa = tr_torrentGetPriority(torrent);
auto const pb = tr_torrentGetPriority(that.torrent);
if (pa != pb)
if (auto const val = tr_compare_3way(pa, pb); val != 0)
{
return pa > pb ? -1 : 1;
return -val;
}
// smaller torrents come before larger ones because they verify faster
if (current_size != that.current_size)
if (auto const val = tr_compare_3way(current_size, that.current_size); val != 0)
{
return current_size < that.current_size ? -1 : 1;
return val;
}
// tertiary compare just to ensure they don't compare equal
if (torrent->id() != that.torrent->id())
{
return torrent->id() < that.torrent->id() ? -1 : 1;
}
return 0;
return tr_compare_3way(torrent->id(), that.torrent->id());
}
bool tr_verify_worker::verify_torrent(tr_torrent* tor, std::atomic<bool> const& stop_flag)

View File

@ -183,25 +183,23 @@ TEST_F(NetTest, isGlobalUnicastAddress)
TEST_F(NetTest, ipCompare)
{
static auto constexpr IpPairs = std::array{ std::tuple{ "223.18.245.229"sv, "8.8.8.8"sv, 1 },
static constexpr auto IpPairs = std::array{ std::tuple{ "223.18.245.229"sv, "8.8.8.8"sv, 1 },
std::tuple{ "0.0.0.0"sv, "255.255.255.255"sv, -1 },
std::tuple{ "8.8.8.8"sv, "8.8.8.8"sv, 0 },
std::tuple{ "8.8.8.8"sv, "2001:0:0eab:dead::a0:abcd:4e"sv, 1 },
std::tuple{ "8.8.8.8"sv, "2001:0:0eab:dead::a0:abcd:4e"sv, -1 },
std::tuple{ "2001:1890:1112:1::20"sv, "2001:0:0eab:dead::a0:abcd:4e"sv, 1 },
std::tuple{ "2001:1890:1112:1::20"sv, "2001:1890:1112:1::20"sv, 0 } };
for (auto const& [ip_str1, ip_str2, res] : IpPairs)
for (auto const& [sv1, sv2, res] : IpPairs)
{
auto const ip1 = *tr_address::from_string(ip_str1);
auto const ip2 = *tr_address::from_string(ip_str2);
auto const ip1 = *tr_address::from_string(sv1);
auto const ip2 = *tr_address::from_string(sv2);
std::cerr << ip_str1 << " Vs " << ip_str2 << std::endl;
EXPECT_EQ(ip1.compare(ip2) < 0, res < 0);
EXPECT_EQ(ip1.compare(ip2) > 0, res > 0);
EXPECT_EQ(ip1.compare(ip2) == 0, res == 0);
EXPECT_EQ(ip1 < ip2, res < 0);
EXPECT_EQ(ip1 > ip2, res > 0);
EXPECT_EQ(ip1 == ip2, res == 0);
EXPECT_EQ(ip1.compare(ip2) < 0, res < 0) << sv1 << ' ' << sv2;
EXPECT_EQ(ip1.compare(ip2) > 0, res > 0) << sv1 << ' ' << sv2;
EXPECT_EQ(ip1.compare(ip2) == 0, res == 0) << sv1 << ' ' << sv2;
EXPECT_EQ(ip1 < ip2, res < 0) << sv1 << ' ' << sv2;
EXPECT_EQ(ip1 > ip2, res > 0) << sv1 << ' ' << sv2;
EXPECT_EQ(ip1 == ip2, res == 0) << sv1 << ' ' << sv2;
}
}