refactor: C++ify tr_torrentPeers() (#8253)

* refactor: tr_torrentPeers() now returns a std::vector<tr_peer_stat>

* refactor: make tr_peer_stat.client a std::string_view

* refactor: make tr_peer_stat.flag_str a std::string

* refactor: make tr_peer_stat.addr a std::string

* refactor: rename tr_peer_stat field names to camel_case

* refactor: use Speed type for tr_peer_stat speed fields

* refactor: add default initializers to tr_peer_stat fields

* refactor: order tr_peer_stat fields to reduce padding

* fix: typo in [Torrent peers] getter

* refactor: make tr_peer_stat.user_agent a std::string

* refactor: pass by reference instead of by pointer in DetailsDialog

* fixup! refactor: add default initializers to tr_peer_stat fields
This commit is contained in:
Charles Kerr
2026-01-28 15:16:12 -06:00
committed by GitHub
parent 63d6d670fd
commit 7559f7ffca
10 changed files with 176 additions and 219 deletions

View File

@@ -1088,17 +1088,17 @@ public:
Gtk::TreeModelColumn<Glib::ustring> upload_rate_string;
Gtk::TreeModelColumn<Glib::ustring> client;
Gtk::TreeModelColumn<int> progress;
Gtk::TreeModelColumn<decltype(tr_peer_stat::activeReqsToClient)> upload_request_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::active_reqs_to_client)> upload_request_count_number;
Gtk::TreeModelColumn<Glib::ustring> upload_request_count_string;
Gtk::TreeModelColumn<decltype(tr_peer_stat::activeReqsToPeer)> download_request_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::active_reqs_to_peer)> download_request_count_number;
Gtk::TreeModelColumn<Glib::ustring> download_request_count_string;
Gtk::TreeModelColumn<decltype(tr_peer_stat::blocksToClient)> blocks_downloaded_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::blocks_to_client)> blocks_downloaded_count_number;
Gtk::TreeModelColumn<Glib::ustring> blocks_downloaded_count_string;
Gtk::TreeModelColumn<decltype(tr_peer_stat::blocksToPeer)> blocks_uploaded_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::blocks_to_peer)> blocks_uploaded_count_number;
Gtk::TreeModelColumn<Glib::ustring> blocks_uploaded_count_string;
Gtk::TreeModelColumn<decltype(tr_peer_stat::cancelsToPeer)> reqs_cancelled_by_client_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::cancels_to_peer)> reqs_cancelled_by_client_count_number;
Gtk::TreeModelColumn<Glib::ustring> reqs_cancelled_by_client_count_string;
Gtk::TreeModelColumn<decltype(tr_peer_stat::cancelsToClient)> reqs_cancelled_by_peer_count_number;
Gtk::TreeModelColumn<decltype(tr_peer_stat::cancels_to_client)> reqs_cancelled_by_peer_count_number;
Gtk::TreeModelColumn<Glib::ustring> reqs_cancelled_by_peer_count_string;
Gtk::TreeModelColumn<Glib::ustring> encryption_stock_id;
Gtk::TreeModelColumn<Glib::ustring> flags;
@@ -1111,21 +1111,13 @@ void initPeerRow(
Gtk::TreeModel::iterator const& iter,
std::string_view const key,
std::string_view const torrent_name,
tr_peer_stat const* peer)
tr_peer_stat const& peer)
{
g_return_if_fail(peer != nullptr);
char const* client = peer->client;
if (client == nullptr || g_strcmp0(client, "Unknown Client") == 0)
{
client = "";
}
auto peer_addr4 = in_addr();
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
auto const* const peer_addr4_octets = reinterpret_cast<uint8_t const*>(&peer_addr4.s_addr);
auto const collated_name = inet_pton(AF_INET, std::data(peer->addr), &peer_addr4) != 1 ?
std::data(peer->addr) :
auto const collated_name = inet_pton(AF_INET, peer.addr.c_str(), &peer_addr4) != 1 ?
peer.addr :
fmt::format(
"{:03}",
fmt::join(
@@ -1134,20 +1126,18 @@ void initPeerRow(
peer_addr4_octets + sizeof(peer_addr4.s_addr), // TODO(C++20): Use std::span
"."));
(*iter)[peer_cols.address] = std::data(peer->addr);
(*iter)[peer_cols.address] = peer.addr;
(*iter)[peer_cols.address_collated] = collated_name;
(*iter)[peer_cols.client] = client;
(*iter)[peer_cols.encryption_stock_id] = peer->isEncrypted ? "lock" : "";
(*iter)[peer_cols.client] = peer.user_agent;
(*iter)[peer_cols.encryption_stock_id] = peer.is_encrypted ? "lock" : "";
(*iter)[peer_cols.key] = std::string(key);
(*iter)[peer_cols.torrent_name] = std::string(torrent_name);
}
void refreshPeerRow(Gtk::TreeModel::iterator const& iter, tr_peer_stat const* peer)
void refreshPeerRow(Gtk::TreeModel::iterator const& iter, tr_peer_stat const& peer)
{
g_return_if_fail(peer != nullptr);
auto const down_speed = Speed{ peer->rateToClient_KBps, Speed::Units::KByps };
auto const up_speed = Speed{ peer->rateToPeer_KBps, Speed::Units::KByps };
auto const down_speed = peer.rate_to_client;
auto const up_speed = peer.rate_to_peer;
auto blocks_to_client = std::string{};
auto blocks_to_peer = std::string{};
@@ -1158,64 +1148,64 @@ void refreshPeerRow(Gtk::TreeModel::iterator const& iter, tr_peer_stat const* pe
auto up_count = std::string{};
auto up_speed_string = std::string{};
if (peer->rateToPeer_KBps > 0.01)
if (peer.rate_to_peer.base_quantity() > 0U)
{
up_speed_string = up_speed.to_string();
}
if (peer->rateToClient_KBps > 0)
if (peer.rate_to_client.base_quantity() > 0U)
{
down_speed_string = down_speed.to_string();
}
if (peer->activeReqsToPeer > 0)
if (peer.active_reqs_to_peer > 0)
{
down_count = std::to_string(peer->activeReqsToPeer);
down_count = std::to_string(peer.active_reqs_to_peer);
}
if (peer->activeReqsToClient > 0)
if (peer.active_reqs_to_client > 0)
{
up_count = std::to_string(peer->activeReqsToClient);
up_count = std::to_string(peer.active_reqs_to_client);
}
if (peer->blocksToPeer > 0)
if (peer.blocks_to_peer > 0)
{
blocks_to_peer = std::to_string(peer->blocksToPeer);
blocks_to_peer = std::to_string(peer.blocks_to_peer);
}
if (peer->blocksToClient > 0)
if (peer.blocks_to_client > 0)
{
blocks_to_client = std::to_string(peer->blocksToClient);
blocks_to_client = std::to_string(peer.blocks_to_client);
}
if (peer->cancelsToPeer > 0)
if (peer.cancels_to_peer > 0)
{
cancelled_by_client = std::to_string(peer->cancelsToPeer);
cancelled_by_client = std::to_string(peer.cancels_to_peer);
}
if (peer->cancelsToClient > 0)
if (peer.cancels_to_client > 0)
{
cancelled_by_peer = std::to_string(peer->cancelsToClient);
cancelled_by_peer = std::to_string(peer.cancels_to_client);
}
(*iter)[peer_cols.progress] = static_cast<int>(100.0 * peer->progress);
(*iter)[peer_cols.upload_request_count_number] = peer->activeReqsToClient;
(*iter)[peer_cols.progress] = static_cast<int>(100.0 * peer.progress);
(*iter)[peer_cols.upload_request_count_number] = peer.active_reqs_to_client;
(*iter)[peer_cols.upload_request_count_string] = up_count;
(*iter)[peer_cols.download_request_count_number] = peer->activeReqsToPeer;
(*iter)[peer_cols.download_request_count_number] = peer.active_reqs_to_peer;
(*iter)[peer_cols.download_request_count_string] = down_count;
(*iter)[peer_cols.download_rate_speed] = down_speed;
(*iter)[peer_cols.download_rate_string] = down_speed_string;
(*iter)[peer_cols.upload_rate_speed] = up_speed;
(*iter)[peer_cols.upload_rate_string] = up_speed_string;
(*iter)[peer_cols.flags] = std::data(peer->flagStr);
(*iter)[peer_cols.flags] = peer.flag_str;
(*iter)[peer_cols.was_updated] = true;
(*iter)[peer_cols.blocks_downloaded_count_number] = peer->blocksToClient;
(*iter)[peer_cols.blocks_downloaded_count_number] = peer.blocks_to_client;
(*iter)[peer_cols.blocks_downloaded_count_string] = blocks_to_client;
(*iter)[peer_cols.blocks_uploaded_count_number] = peer->blocksToPeer;
(*iter)[peer_cols.blocks_uploaded_count_number] = peer.blocks_to_peer;
(*iter)[peer_cols.blocks_uploaded_count_string] = blocks_to_peer;
(*iter)[peer_cols.reqs_cancelled_by_client_count_number] = peer->cancelsToPeer;
(*iter)[peer_cols.reqs_cancelled_by_client_count_number] = peer.cancels_to_peer;
(*iter)[peer_cols.reqs_cancelled_by_client_count_string] = cancelled_by_client;
(*iter)[peer_cols.reqs_cancelled_by_peer_count_number] = peer->cancelsToClient;
(*iter)[peer_cols.reqs_cancelled_by_peer_count_number] = peer.cancels_to_client;
(*iter)[peer_cols.reqs_cancelled_by_peer_count_string] = cancelled_by_peer;
}
@@ -1227,16 +1217,12 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
auto const& store = peer_store_;
/* step 1: get all the peers */
std::vector<tr_peer_stat*> peers;
std::vector<size_t> peerCount;
std::vector<std::vector<tr_peer_stat>> peers;
peers.reserve(torrents.size());
peerCount.reserve(torrents.size());
for (auto const* const torrent : torrents)
{
size_t count = 0;
peers.push_back(tr_torrentPeers(torrent, &count));
peerCount.push_back(count);
peers.push_back(tr_torrentPeers(torrent));
}
/* step 2: mark all the peers in the list as not-updated */
@@ -1245,25 +1231,25 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
row[peer_cols.was_updated] = false;
}
auto make_key = [](tr_torrent const* tor, tr_peer_stat const* ps)
auto make_key = [](tr_torrent const* tor, tr_peer_stat const& ps)
{
return fmt::format("{:d}.{:s}", tr_torrentId(tor), ps->addr);
return fmt::format("{:d}.{:s}", tr_torrentId(tor), ps.addr);
};
/* step 3: add any new peers */
for (size_t i = 0; i < torrents.size(); ++i)
{
auto const* tor = torrents.at(i);
auto const& torrent_peers = peers.at(i);
for (size_t j = 0; j < peerCount[i]; ++j)
for (auto const& peer : torrent_peers)
{
auto const* s = &peers.at(i)[j];
auto const key = make_key(tor, s);
auto const key = make_key(tor, peer);
if (hash.find(key) == hash.end())
{
auto const iter = store->append();
initPeerRow(iter, key, tr_torrentName(tor), s);
initPeerRow(iter, key, tr_torrentName(tor), peer);
hash.try_emplace(key, Gtk::TreeRowReference(store, store->get_path(iter)));
}
}
@@ -1273,12 +1259,12 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
for (size_t i = 0; i < torrents.size(); ++i)
{
auto const* tor = torrents.at(i);
auto const& torrent_peers = peers.at(i);
for (size_t j = 0; j < peerCount[i]; ++j)
for (auto const& peer : torrent_peers)
{
auto const* s = &peers.at(i)[j];
auto const key = make_key(tor, s);
refreshPeerRow(store->get_iter(hash.at(key).get_path()), s);
auto const key = make_key(tor, peer);
refreshPeerRow(store->get_iter(hash.at(key).get_path()), peer);
}
}
@@ -1299,12 +1285,6 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
}
}
}
/* step 6: cleanup */
for (size_t i = 0; i < peers.size(); ++i)
{
tr_torrentPeersFree(peers[i], peerCount[i]);
}
}
void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& torrents)

View File

@@ -234,16 +234,14 @@ void announce_url_new(tr_urlbuf& url, tr_session const* session, tr_announce_req
if (auto ipv4_addr = session->global_address(TR_AF_INET); ipv4_addr)
{
auto buf = std::array<char, INET_ADDRSTRLEN>{};
auto const display_name = ipv4_addr->display_name(std::data(buf), std::size(buf));
auto const display_name = ipv4_addr->display_name();
fmt::format_to(out, "&ipv4=");
tr_urlPercentEncode(out, display_name);
}
if (auto ipv6_addr = session->global_address(TR_AF_INET6); ipv6_addr)
{
auto buf = std::array<char, INET6_ADDRSTRLEN>{};
auto const display_name = ipv6_addr->display_name(std::data(buf), std::size(buf));
auto const display_name = ipv6_addr->display_name();
fmt::format_to(out, "&ipv6=");
tr_urlPercentEncode(out, display_name);
}

View File

@@ -472,20 +472,15 @@ std::optional<tr_address> tr_address::from_string(std::string_view address_sv)
}
}
std::string_view tr_address::display_name(char* out, size_t outlen) const
{
TR_ASSERT(is_valid());
if (auto* name = evutil_inet_ntop(tr_ip_protocol_to_af(type), &addr, out, outlen))
{
return name;
}
return "Invalid address"sv;
}
[[nodiscard]] std::string tr_address::display_name() const
{
auto buf = std::array<char, std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)>{};
return std::string{ display_name(std::data(buf), std::size(buf)) };
TR_ASSERT(is_valid());
if (auto* name = evutil_inet_ntop(tr_ip_protocol_to_af(type), &addr, std::data(buf), std::size(buf)))
{
return std::string{ name };
}
return std::string{ "Invalid address" };
}
std::pair<tr_address, std::byte const*> tr_address::from_compact_ipv4(std::byte const* compact) noexcept

View File

@@ -159,7 +159,6 @@ struct tr_address
[[nodiscard]] static std::pair<tr_address, std::byte const*> from_compact_ipv6(std::byte const* compact) noexcept;
// --- write the text form of the address, e.g. inet_ntop()
std::string_view display_name(char* out, size_t outlen) const;
[[nodiscard]] std::string display_name() const;
// ---

View File

@@ -1831,121 +1831,118 @@ namespace peer_stat_helpers
auto const [addr, port] = peer->socket_address();
addr.display_name(stats.addr, sizeof(stats.addr));
stats.client = peer->user_agent().c_str();
stats.addr = addr.display_name();
stats.user_agent = peer->user_agent();
stats.peer_id = peer->peer_id();
stats.port = port.host();
stats.from = peer->peer_info->from_first();
stats.progress = peer->percent_done();
stats.isUTP = peer->is_utp_connection();
stats.isEncrypted = peer->is_encrypted();
stats.rateToPeer_KBps = peer->get_piece_speed(now_msec, tr_direction::ClientToPeer).count(Speed::Units::KByps);
stats.rateToClient_KBps = peer->get_piece_speed(now_msec, tr_direction::PeerToClient).count(Speed::Units::KByps);
stats.peerIsChoked = peer->peer_is_choked();
stats.peerIsInterested = peer->peer_is_interested();
stats.clientIsChoked = peer->client_is_choked();
stats.clientIsInterested = peer->client_is_interested();
stats.isIncoming = peer->is_incoming_connection();
stats.isDownloadingFrom = peer->is_active(tr_direction::PeerToClient);
stats.isUploadingTo = peer->is_active(tr_direction::ClientToPeer);
stats.isSeed = peer->is_seed();
stats.is_utp = peer->is_utp_connection();
stats.is_encrypted = peer->is_encrypted();
stats.rate_to_peer = peer->get_piece_speed(now_msec, tr_direction::ClientToPeer);
stats.rate_to_client = peer->get_piece_speed(now_msec, tr_direction::PeerToClient);
stats.peer_is_choked = peer->peer_is_choked();
stats.peer_is_interested = peer->peer_is_interested();
stats.client_is_choked = peer->client_is_choked();
stats.client_is_interested = peer->client_is_interested();
stats.is_incoming = peer->is_incoming_connection();
stats.is_downloading_from = peer->is_active(tr_direction::PeerToClient);
stats.is_uploading_to = peer->is_active(tr_direction::ClientToPeer);
stats.is_seed = peer->is_seed();
stats.blocksToPeer = peer->blocks_sent_to_peer.count(now, CancelHistorySec);
stats.blocksToClient = peer->blocks_sent_to_client.count(now, CancelHistorySec);
stats.cancelsToPeer = peer->cancels_sent_to_peer.count(now, CancelHistorySec);
stats.cancelsToClient = peer->cancels_sent_to_client.count(now, CancelHistorySec);
stats.blocks_to_peer = peer->blocks_sent_to_peer.count(now, CancelHistorySec);
stats.blocks_to_client = peer->blocks_sent_to_client.count(now, CancelHistorySec);
stats.cancels_to_peer = peer->cancels_sent_to_peer.count(now, CancelHistorySec);
stats.cancels_to_client = peer->cancels_sent_to_client.count(now, CancelHistorySec);
stats.bytes_to_peer = peer->bytes_sent_to_peer.count(now, CancelHistorySec);
stats.bytes_to_client = peer->bytes_sent_to_client.count(now, CancelHistorySec);
stats.activeReqsToPeer = peer->active_req_count(tr_direction::ClientToPeer);
stats.activeReqsToClient = peer->active_req_count(tr_direction::PeerToClient);
stats.active_reqs_to_peer = peer->active_req_count(tr_direction::ClientToPeer);
stats.active_reqs_to_client = peer->active_req_count(tr_direction::PeerToClient);
char* pch = stats.flagStr;
stats.flag_str.clear();
stats.flag_str.reserve(9);
if (stats.isUTP)
if (stats.is_utp)
{
*pch++ = 'T';
stats.flag_str.push_back('T');
}
if (peer->swarm->optimistic == peer)
{
*pch++ = 'O';
stats.flag_str.push_back('O');
}
if (stats.isDownloadingFrom)
if (stats.is_downloading_from)
{
*pch++ = 'D';
stats.flag_str.push_back('D');
}
else if (stats.clientIsInterested)
else if (stats.client_is_interested)
{
*pch++ = 'd';
stats.flag_str.push_back('d');
}
if (stats.isUploadingTo)
if (stats.is_uploading_to)
{
*pch++ = 'U';
stats.flag_str.push_back('U');
}
else if (stats.peerIsInterested)
else if (stats.peer_is_interested)
{
*pch++ = 'u';
stats.flag_str.push_back('u');
}
if (!stats.clientIsChoked && !stats.clientIsInterested)
if (!stats.client_is_choked && !stats.client_is_interested)
{
*pch++ = 'K';
stats.flag_str.push_back('K');
}
if (!stats.peerIsChoked && !stats.peerIsInterested)
if (!stats.peer_is_choked && !stats.peer_is_interested)
{
*pch++ = '?';
stats.flag_str.push_back('?');
}
if (stats.isEncrypted)
if (stats.is_encrypted)
{
*pch++ = 'E';
stats.flag_str.push_back('E');
}
if (stats.from == TR_PEER_FROM_DHT)
{
*pch++ = 'H';
stats.flag_str.push_back('H');
}
else if (stats.from == TR_PEER_FROM_PEX)
{
*pch++ = 'X';
stats.flag_str.push_back('X');
}
if (stats.isIncoming)
if (stats.is_incoming)
{
*pch++ = 'I';
stats.flag_str.push_back('I');
}
*pch = '\0';
return stats;
}
} // namespace peer_stat_helpers
} // namespace
tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, size_t* setme_count)
std::vector<tr_peer_stat> tr_peerMgrPeerStats(tr_torrent const* tor)
{
TR_ASSERT(tr_isTorrent(tor));
TR_ASSERT(tor->swarm->manager != nullptr);
auto const peers = tor->swarm->peers;
auto const n = std::size(peers);
auto* const ret = new tr_peer_stat[n];
auto ret = std::vector<tr_peer_stat>(std::size(peers));
auto const lock = tor->unique_lock();
auto const now = tr_time();
auto const now_msec = tr_time_msec();
std::ranges::transform(
peers,
ret,
std::data(ret),
[&now, &now_msec](auto const& peer) { return peer_stat_helpers::get_peer_stats(peer.get(), now, now_msec); });
*setme_count = n;
return ret;
}

View File

@@ -716,7 +716,7 @@ void tr_peerMgrTorrentAvailability(tr_torrent const* tor, int8_t* tab, unsigned
[[nodiscard]] uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor);
[[nodiscard]] struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, size_t* setme_count);
[[nodiscard]] std::vector<tr_peer_stat> tr_peerMgrPeerStats(tr_torrent const* tor);
[[nodiscard]] tr_webseed_view tr_peerMgrWebseed(tr_torrent const* tor, size_t i);

View File

@@ -613,36 +613,33 @@ namespace make_torrent_field_helpers
[[nodiscard]] auto make_peer_vec(tr_torrent const& tor)
{
auto n_peers = size_t{};
auto* const peers = tr_torrentPeers(&tor, &n_peers);
auto const peers = tr_torrentPeers(&tor);
auto peers_vec = tr_variant::Vector{};
peers_vec.reserve(n_peers);
for (size_t idx = 0U; idx != n_peers; ++idx)
peers_vec.reserve(std::size(peers));
for (auto const& peer : peers)
{
auto const& peer = peers[idx];
auto peer_map = tr_variant::Map{ 19U };
peer_map.try_emplace(TR_KEY_address, peer.addr);
peer_map.try_emplace(TR_KEY_client_is_choked, peer.clientIsChoked);
peer_map.try_emplace(TR_KEY_client_is_interested, peer.clientIsInterested);
peer_map.try_emplace(TR_KEY_client_name, peer.client);
peer_map.try_emplace(TR_KEY_client_is_choked, peer.client_is_choked);
peer_map.try_emplace(TR_KEY_client_is_interested, peer.client_is_interested);
peer_map.try_emplace(TR_KEY_client_name, peer.user_agent);
peer_map.try_emplace(TR_KEY_peer_id, tr_base64_encode(std::string_view{ peer.peer_id.data(), peer.peer_id.size() }));
peer_map.try_emplace(TR_KEY_flag_str, peer.flagStr);
peer_map.try_emplace(TR_KEY_is_downloading_from, peer.isDownloadingFrom);
peer_map.try_emplace(TR_KEY_is_encrypted, peer.isEncrypted);
peer_map.try_emplace(TR_KEY_is_incoming, peer.isIncoming);
peer_map.try_emplace(TR_KEY_is_utp, peer.isUTP);
peer_map.try_emplace(TR_KEY_is_uploading_to, peer.isUploadingTo);
peer_map.try_emplace(TR_KEY_peer_is_choked, peer.peerIsChoked);
peer_map.try_emplace(TR_KEY_peer_is_interested, peer.peerIsInterested);
peer_map.try_emplace(TR_KEY_flag_str, peer.flag_str);
peer_map.try_emplace(TR_KEY_is_downloading_from, peer.is_downloading_from);
peer_map.try_emplace(TR_KEY_is_encrypted, peer.is_encrypted);
peer_map.try_emplace(TR_KEY_is_incoming, peer.is_incoming);
peer_map.try_emplace(TR_KEY_is_utp, peer.is_utp);
peer_map.try_emplace(TR_KEY_is_uploading_to, peer.is_uploading_to);
peer_map.try_emplace(TR_KEY_peer_is_choked, peer.peer_is_choked);
peer_map.try_emplace(TR_KEY_peer_is_interested, peer.peer_is_interested);
peer_map.try_emplace(TR_KEY_port, peer.port);
peer_map.try_emplace(TR_KEY_progress, peer.progress);
peer_map.try_emplace(TR_KEY_rate_to_client, Speed{ peer.rateToClient_KBps, Speed::Units::KByps }.base_quantity());
peer_map.try_emplace(TR_KEY_rate_to_peer, Speed{ peer.rateToPeer_KBps, Speed::Units::KByps }.base_quantity());
peer_map.try_emplace(TR_KEY_rate_to_client, peer.rate_to_client.base_quantity());
peer_map.try_emplace(TR_KEY_rate_to_peer, peer.rate_to_peer.base_quantity());
peer_map.try_emplace(TR_KEY_bytes_to_peer, peer.bytes_to_peer);
peer_map.try_emplace(TR_KEY_bytes_to_client, peer.bytes_to_client);
peers_vec.emplace_back(std::move(peer_map));
}
tr_torrentPeersFree(peers, n_peers);
return tr_variant{ std::move(peers_vec) };
}

View File

@@ -1491,16 +1491,11 @@ std::string tr_torrentFilename(tr_torrent const* tor)
// ---
tr_peer_stat* tr_torrentPeers(tr_torrent const* tor, size_t* peer_count)
std::vector<tr_peer_stat> tr_torrentPeers(tr_torrent const* tor)
{
TR_ASSERT(tr_isTorrent(tor));
return tr_peerMgrPeerStats(tor, peer_count);
}
void tr_torrentPeersFree(tr_peer_stat* peer_stats, size_t /*peer_count*/)
{
delete[] peer_stats;
return tr_peerMgrPeerStats(tor);
}
void tr_torrentAvailability(tr_torrent const* tor, int8_t* tab, int size)

View File

@@ -20,6 +20,7 @@
#include <vector>
#include "libtransmission/tr-macros.h"
#include "libtransmission/values.h"
using tr_file_index_t = size_t;
using tr_piece_index_t = uint32_t;
@@ -1139,60 +1140,58 @@ bool tr_torrentCanManualUpdate(tr_torrent const* torrent);
// --- tr_peer_stat
// NOLINTBEGIN(modernize-avoid-c-arrays)
struct tr_peer_stat
{
bool isUTP;
std::string addr;
std::string flag_str;
bool isEncrypted;
bool isDownloadingFrom;
bool isUploadingTo;
bool isSeed;
// The user agent, e.g. `BitTorrent 7.9.1`.
// Will be an empty string if the agent cannot be determined.
std::string user_agent;
bool peerIsChoked;
bool peerIsInterested;
bool clientIsChoked;
bool clientIsInterested;
bool isIncoming;
libtransmission::Values::Speed rate_to_peer;
libtransmission::Values::Speed rate_to_client;
uint8_t from;
uint16_t port;
// how many requests the peer has made that we haven't responded to yet
size_t active_reqs_to_client = {};
char addr[TrInet6AddrStrlen];
char flagStr[32];
char const* client;
// how many requests we've made and are currently awaiting a response for
size_t active_reqs_to_peer = {};
tr_peer_id_t peer_id;
size_t bytes_to_peer = {};
size_t bytes_to_client = {};
float progress;
double rateToPeer_KBps;
double rateToClient_KBps;
tr_peer_id_t peer_id = {};
float progress = {};
// THESE NEXT FOUR FIELDS ARE EXPERIMENTAL.
// Don't rely on them; they'll probably go away
/* how many blocks we've sent to this peer in the last 120 seconds */
uint32_t blocksToPeer;
/* how many blocks this client's sent to us in the last 120 seconds */
uint32_t blocksToClient;
/* how many requests to this peer that we've cancelled in the last 120 seconds */
uint32_t cancelsToPeer;
/* how many requests this peer made of us, then cancelled, in the last 120 seconds */
uint32_t cancelsToClient;
// how many blocks we've sent to this peer in the last 120 seconds
uint32_t blocks_to_peer = {};
// how many blocks this client's sent to us in the last 120 seconds
uint32_t blocks_to_client = {};
// how many requests to this peer that we've cancelled in the last 120 seconds
uint32_t cancels_to_peer = {};
// how many requests this peer made of us, then cancelled, in the last 120 seconds
uint32_t cancels_to_client = {};
/* how many requests the peer has made that we haven't responded to yet */
size_t activeReqsToClient;
uint16_t port = {};
uint8_t from = {};
/* how many requests we've made and are currently awaiting a response for */
size_t activeReqsToPeer;
size_t bytes_to_peer;
size_t bytes_to_client;
bool client_is_choked = {};
bool client_is_interested = {};
bool is_downloading_from = {};
bool is_encrypted = {};
bool is_incoming = {};
bool is_seed = {};
bool is_uploading_to = {};
bool is_utp = {};
bool peer_is_choked = {};
bool peer_is_interested = {};
};
// NOLINTEND(modernize-avoid-c-arrays)
tr_peer_stat* tr_torrentPeers(tr_torrent const* torrent, size_t* peer_count);
void tr_torrentPeersFree(tr_peer_stat* peer_stats, size_t peer_count);
std::vector<tr_peer_stat> tr_torrentPeers(tr_torrent const* torrent);
// --- tr_tracker_stat

View File

@@ -1006,41 +1006,38 @@ bool trashDataFile(std::string_view const filename, tr_error* error)
- (NSArray<NSDictionary*>*)peers
{
size_t totalPeers;
tr_peer_stat* peers = tr_torrentPeers(self.fHandle, &totalPeers);
auto const peers = tr_torrentPeers(self.fHandle);
size_t const totalPeers = peers.size();
NSMutableArray* peerDicts = [NSMutableArray arrayWithCapacity:totalPeers];
for (size_t i = 0; i < totalPeers; i++)
for (auto const& peer : peers)
{
tr_peer_stat* peer = &peers[i];
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:12];
dict[@"Name"] = self.name;
dict[@"From"] = @(peer->from);
dict[@"IP"] = @(peer->addr);
dict[@"Port"] = @(peer->port);
dict[@"Progress"] = @(peer->progress);
dict[@"Seed"] = @(peer->isSeed);
dict[@"Encryption"] = @(peer->isEncrypted);
dict[@"uTP"] = @(peer->isUTP);
dict[@"Client"] = @(peer->client);
dict[@"Flags"] = @(peer->flagStr);
dict[@"From"] = @(peer.from);
dict[@"IP"] = tr_strv_to_utf8_nsstring(peer.addr);
dict[@"Port"] = @(peer.port);
dict[@"Progress"] = @(peer.progress);
dict[@"Seed"] = @(peer.is_seed);
dict[@"Encryption"] = @(peer.is_encrypted);
dict[@"uTP"] = @(peer.is_utp);
dict[@"Client"] = tr_strv_to_utf8_nsstring(peer.user_agent);
dict[@"Flags"] = tr_strv_to_utf8_nsstring(peer.flag_str);
if (peer->isUploadingTo)
if (peer.is_uploading_to)
{
dict[@"UL To Rate"] = @(peer->rateToPeer_KBps);
dict[@"UL To Rate"] = @(peer.rate_to_peer.count(libtransmission::Values::Speed::Units::KByps));
}
if (peer->isDownloadingFrom)
if (peer.is_downloading_from)
{
dict[@"DL From Rate"] = @(peer->rateToClient_KBps);
dict[@"DL From Rate"] = @(peer.rate_to_client.count(libtransmission::Values::Speed::Units::KByps));
}
[peerDicts addObject:dict];
}
tr_torrentPeersFree(peers, totalPeers);
return peerDicts;
}