refactor: tr_torrents (#2722)

* refactor: add tr_torrents container
This commit is contained in:
Charles Kerr 2022-03-01 15:06:29 -08:00 committed by GitHub
parent e7272fc340
commit 60ef1abadf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 466 additions and 116 deletions

View File

@ -205,6 +205,7 @@
A29DF8B90DB2544C00D04E5A /* resume.cc in Sources */ = {isa = PBXBuildFile; fileRef = A29DF8B60DB2544C00D04E5A /* resume.cc */; };
A29DF8BA0DB2544C00D04E5A /* resume.h in Headers */ = {isa = PBXBuildFile; fileRef = A29DF8B70DB2544C00D04E5A /* resume.h */; };
A29DF8BB0DB2544C00D04E5A /* torrent.h in Headers */ = {isa = PBXBuildFile; fileRef = A29DF8B80DB2544C00D04E5A /* torrent.h */; };
2B9BA6C508B488FE586A0AB2 /* torrents.h in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB3 /* torrents.h */; };
A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */ = {isa = PBXBuildFile; fileRef = A2D22A110D65EED100007D5F /* verify.h */; };
A29E653613F1603100048D71 /* evutil_rand.c in Sources */ = {isa = PBXBuildFile; fileRef = A29E653513F1603100048D71 /* evutil_rand.c */; };
A2A1CB7A0BF29D5500AE959F /* PeerProgressIndicatorCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2A1CB780BF29D5500AE959F /* PeerProgressIndicatorCell.mm */; };
@ -308,6 +309,7 @@
BEFC1E2D0C07861A00B0BB3C /* upnp.cc in Sources */ = {isa = PBXBuildFile; fileRef = BEFC1DF40C07861A00B0BB3C /* upnp.cc */; };
BEFC1E2F0C07861A00B0BB3C /* session.cc in Sources */ = {isa = PBXBuildFile; fileRef = BEFC1DF60C07861A00B0BB3C /* session.cc */; };
BEFC1E320C07861A00B0BB3C /* torrent.cc in Sources */ = {isa = PBXBuildFile; fileRef = BEFC1DF90C07861A00B0BB3C /* torrent.cc */; };
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB1 /* torrents.cc */; };
BEFC1E350C07861A00B0BB3C /* port-forwarding.h in Headers */ = {isa = PBXBuildFile; fileRef = BEFC1DFC0C07861A00B0BB3C /* port-forwarding.h */; };
BEFC1E360C07861A00B0BB3C /* port-forwarding.cc in Sources */ = {isa = PBXBuildFile; fileRef = BEFC1DFD0C07861A00B0BB3C /* port-forwarding.cc */; };
BEFC1E3B0C07861A00B0BB3C /* platform.h in Headers */ = {isa = PBXBuildFile; fileRef = BEFC1E020C07861A00B0BB3C /* platform.h */; };
@ -886,6 +888,7 @@
A29DF8B60DB2544C00D04E5A /* resume.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = resume.cc; sourceTree = "<group>"; };
A29DF8B70DB2544C00D04E5A /* resume.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = resume.h; sourceTree = "<group>"; };
A29DF8B80DB2544C00D04E5A /* torrent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = torrent.h; sourceTree = "<group>"; };
2B9BA6C508B488FE586A0AB3 /* torrents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = torrents.h; sourceTree = "<group>"; };
A29E653513F1603100048D71 /* evutil_rand.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = evutil_rand.c; sourceTree = "<group>"; };
A29EBE520DC01FC9006CEE80 /* web.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = web.cc; sourceTree = "<group>"; };
A29EBE530DC01FC9006CEE80 /* web.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = web.h; sourceTree = "<group>"; };
@ -1032,6 +1035,7 @@
BEFC1DF50C07861A00B0BB3C /* transmission.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = transmission.h; sourceTree = "<group>"; };
BEFC1DF60C07861A00B0BB3C /* session.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = session.cc; sourceTree = "<group>"; };
BEFC1DF90C07861A00B0BB3C /* torrent.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = torrent.cc; sourceTree = "<group>"; };
2B9BA6C508B488FE586A0AB1 /* torrents.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = torrents.cc; sourceTree = "<group>"; };
BEFC1DFC0C07861A00B0BB3C /* port-forwarding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "port-forwarding.h"; sourceTree = "<group>"; };
BEFC1DFD0C07861A00B0BB3C /* port-forwarding.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "port-forwarding.cc"; sourceTree = "<group>"; };
BEFC1E020C07861A00B0BB3C /* platform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = platform.h; sourceTree = "<group>"; };
@ -1552,6 +1556,7 @@
A29DF8B60DB2544C00D04E5A /* resume.cc */,
A29DF8B70DB2544C00D04E5A /* resume.h */,
A29DF8B80DB2544C00D04E5A /* torrent.h */,
2B9BA6C508B488FE586A0AB3 /* torrents.h */,
C1033E031A3279B800EF44D8 /* crypto-utils-fallback.cc */,
C1033E041A3279B800EF44D8 /* crypto-utils-ccrypto.cc */,
C1033E051A3279B800EF44D8 /* crypto-utils.cc */,
@ -1598,6 +1603,7 @@
A23F29A0132A447400E9A83B /* announcer-http.cc */,
A2AA9BE0132CAC8D00FA131E /* announcer-udp.cc */,
BEFC1DF90C07861A00B0BB3C /* torrent.cc */,
2B9BA6C508B488FE586A0AB1 /* torrents.cc */,
BEFC1DFC0C07861A00B0BB3C /* port-forwarding.h */,
BEFC1DFD0C07861A00B0BB3C /* port-forwarding.cc */,
A21FBBA90EDA78C300BC3C51 /* bandwidth.h */,
@ -2085,6 +2091,7 @@
C17740D6273A002C00E455D2 /* web-utils.h in Headers */,
A29DF8BA0DB2544C00D04E5A /* resume.h in Headers */,
A29DF8BB0DB2544C00D04E5A /* torrent.h in Headers */,
2B9BA6C508B488FE586A0AB2 /* torrents.h in Headers */,
A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */,
C1FEE57B1C3223CC00D62832 /* watchdir.h in Headers */,
A2AAB6650DE0D08B00E04DDA /* blocklist.h in Headers */,
@ -2760,6 +2767,7 @@
ED8A16402735A8AA000D61F9 /* peer-mgr-active-requests.cc in Sources */,
BEFC1E2F0C07861A00B0BB3C /* session.cc in Sources */,
BEFC1E320C07861A00B0BB3C /* torrent.cc in Sources */,
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */,
BEFC1E360C07861A00B0BB3C /* port-forwarding.cc in Sources */,
BEFC1E3C0C07861A00B0BB3C /* platform.cc in Sources */,
BEFC1E460C07861A00B0BB3C /* net.cc in Sources */,

View File

@ -60,6 +60,7 @@ set(PROJECT_FILES
torrent-magnet.cc
torrent-metainfo.cc
torrent.cc
torrents.cc
tr-assert.cc
tr-assert.mm
tr-dht.cc
@ -194,6 +195,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
torrent-magnet.h
torrent-metainfo.h
torrent.h
torrents.h
tr-dht.h
tr-lpd.h
tr-udp.h

View File

@ -598,7 +598,7 @@ static tr_tier* getTier(tr_announcer* announcer, tr_sha1_digest_t const& info_ha
return nullptr;
}
auto* const tor = announcer->session->getTorrent(info_hash);
auto* const tor = announcer->session->torrents().get(info_hash);
if (tor == nullptr || tor->torrent_announcer == nullptr)
{
return nullptr;
@ -1305,7 +1305,7 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
for (int i = 0; i < response->row_count; ++i)
{
auto const& row = response->rows[i];
auto* const tor = session->getTorrent(row.info_hash);
auto* const tor = session->torrents().get(row.info_hash);
if (tor != nullptr)
{
@ -1535,7 +1535,7 @@ static void scrapeAndAnnounceMore(tr_announcer* announcer)
/* build a list of tiers that need to be announced */
auto announce_me = std::vector<tr_tier*>{};
auto scrape_me = std::vector<tr_tier*>{};
for (auto* tor : announcer->session->torrents)
for (auto* const tor : announcer->session->torrents())
{
for (auto& tier : tor->torrent_announcer->tiers)
{

View File

@ -175,7 +175,7 @@ static void setReadState(tr_handshake* handshake, handshake_state_t state)
static bool buildHandshakeMessage(tr_handshake* handshake, uint8_t* buf)
{
auto const torrent_hash = tr_cryptoGetTorrentHash(handshake->crypto);
auto* const tor = torrent_hash ? handshake->session->getTorrent(*torrent_hash) : nullptr;
auto* const tor = torrent_hash ? handshake->session->torrents().get(*torrent_hash) : nullptr;
bool const success = tor != nullptr;
if (success)
@ -257,7 +257,7 @@ static handshake_parse_err_t parseHandshake(tr_handshake* handshake, struct evbu
/* peer id */
dbgmsg(handshake, "peer-id is [%" TR_PRIsv "]", TR_PRIsv_ARG(peer_id));
if (auto* const tor = handshake->session->getTorrent(hash); peer_id == tr_torrentGetPeerId(tor))
if (auto* const tor = handshake->session->torrents().get(hash); peer_id == tr_torrentGetPeerId(tor))
{
dbgmsg(handshake, "streuth! we've connected to ourselves.");
return HANDSHAKE_PEER_IS_SELF;
@ -647,7 +647,7 @@ static ReadState readHandshake(tr_handshake* handshake, struct evbuffer* inbuf)
if (tr_peerIoIsIncoming(handshake->io))
{
if (!handshake->session->contains(hash))
if (!handshake->session->torrents().contains(hash))
{
dbgmsg(handshake, "peer is trying to connect to us for a torrent we don't have.");
return tr_handshakeDone(handshake, false);
@ -703,7 +703,7 @@ static ReadState readPeerId(tr_handshake* handshake, struct evbuffer* inbuf)
// if we've somehow connected to ourselves, don't keep the connection
auto const hash = tr_peerIoGetTorrentHash(handshake->io);
auto* const tor = hash ? handshake->session->getTorrent(*hash) : nullptr;
auto* const tor = hash ? handshake->session->torrents().get(*hash) : nullptr;
bool const connected_to_self = peer_id == tr_torrentGetPeerId(tor);
return tr_handshakeDone(handshake, !connected_to_self);
@ -1134,7 +1134,7 @@ static void gotError(tr_peerIo* io, short what, void* vhandshake)
/* This peer probably doesn't speak uTP. */
auto const hash = tr_peerIoGetTorrentHash(io);
auto* const tor = hash ? handshake->session->getTorrent(*hash) : nullptr;
auto* const tor = hash ? handshake->session->torrents().get(*hash) : nullptr;
/* Don't mark a peer as non-uTP unless it's really a connect failure. */
if ((errcode == ETIMEDOUT || errcode == ECONNREFUSED) && tr_isTorrent(tor))

View File

@ -294,7 +294,7 @@ tr_address const* tr_peerAddress(tr_peer const* peer)
static tr_swarm* getExistingSwarm(tr_peerMgr* manager, tr_sha1_digest_t const& hash)
{
tr_torrent* tor = manager->session->getTorrent(hash);
auto* const tor = manager->session->torrents().get(hash);
return tor == nullptr ? nullptr : tor->swarm;
}
@ -414,7 +414,7 @@ void tr_peerMgrOnBlocklistChanged(tr_peerMgr* mgr)
{
/* we cache whether or not a peer is blocklisted...
since the blocklist has changed, erase that cached value */
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
tr_swarm* s = tor->swarm;
@ -657,7 +657,7 @@ static void refillUpkeep(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
auto* mgr = static_cast<tr_peerMgr*>(vmgr);
auto const lock = mgr->unique_lock();
auto& torrents = mgr->session->torrents;
auto& torrents = mgr->session->torrents();
std::for_each(std::begin(torrents), std::end(torrents), [](auto* tor) { tr_swarmCancelOldRequests(tor->swarm); });
tr_timerAddMsec(mgr->refillUpkeepTimer, RefillUpkeepPeriodMsec);
@ -2224,7 +2224,7 @@ static void rechokePulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
auto const lock = mgr->unique_lock();
uint64_t const now = tr_time_msec();
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
if (tor->isRunning)
{
@ -2479,9 +2479,10 @@ static void enforceTorrentPeerLimit(tr_swarm* s)
static void enforceSessionPeerLimit(tr_session* session)
{
// do we have too many peers?
auto const& torrents = session->torrents();
size_t const n_peers = std::accumulate(
std::begin(session->torrents),
std::end(session->torrents),
std::begin(torrents),
std::end(torrents),
size_t{},
[](size_t sum, tr_torrent const* tor) { return sum + tr_ptrArraySize(&tor->swarm->peers); });
size_t const max = tr_sessionGetPeerLimit(session);
@ -2493,7 +2494,7 @@ static void enforceSessionPeerLimit(tr_session* session)
// make a list of all the peers
auto peers = std::vector<tr_peer*>{};
peers.reserve(n_peers);
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
size_t const n = tr_ptrArraySize(&tor->swarm->peers);
auto** base = (tr_peer**)tr_ptrArrayBase(&tor->swarm->peers);
@ -2513,7 +2514,7 @@ static void reconnectPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
time_t const now_sec = tr_time();
// remove crappy peers
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
if (!tor->swarm->isRunning)
{
@ -2526,7 +2527,7 @@ static void reconnectPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
}
// if we're over the per-torrent peer limits, cull some peers
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
if (tor->isRunning)
{
@ -2550,7 +2551,7 @@ static void reconnectPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
static void pumpAllPeers(tr_peerMgr* mgr)
{
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
tr_swarm* s = tor->swarm;
@ -2595,7 +2596,7 @@ static void bandwidthPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
session->bandwidth->allocate(TR_DOWN, BandwidthPeriodMsec);
/* torrent upkeep */
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
/* possibly stop torrents that have seeded enough */
tr_torrentCheckSeedLimit(tor);
@ -2693,7 +2694,7 @@ static void atomPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
auto* mgr = static_cast<tr_peerMgr*>(vmgr);
auto const lock = mgr->unique_lock();
for (auto* tor : mgr->session->torrents)
for (auto* const tor : mgr->session->torrents())
{
tr_swarm* s = tor->swarm;
int const maxAtomCount = getMaxAtomCount(tor);
@ -2920,7 +2921,7 @@ static std::vector<peer_candidate> getPeerCandidates(tr_session* session, size_t
/* count how many peers and atoms we've got */
int atomCount = 0;
int peerCount = 0;
for (auto const* tor : session->torrents)
for (auto const* tor : session->torrents())
{
atomCount += tr_ptrArraySize(&tor->swarm->pool);
peerCount += tr_ptrArraySize(&tor->swarm->peers);
@ -2936,7 +2937,7 @@ static std::vector<peer_candidate> getPeerCandidates(tr_session* session, size_t
candidates.reserve(atomCount);
/* populate the candidate array */
for (auto* tor : session->torrents)
for (auto* tor : session->torrents())
{
if (!tor->swarm->isRunning)
{

View File

@ -133,7 +133,7 @@ static auto getTorrents(tr_session* session, tr_variant* args)
}
else if (tr_variantGetStrView(node, &sv))
{
tor = session->getTorrent(sv);
tor = session->torrents().get(sv);
}
if (tor != nullptr)
@ -155,16 +155,16 @@ static auto getTorrents(tr_session* session, tr_variant* args)
{
time_t const cutoff = tr_time() - RecentlyActiveSeconds;
torrents.reserve(std::size(session->torrents));
torrents.reserve(std::size(session->torrents()));
std::copy_if(
std::begin(session->torrents),
std::end(session->torrents),
std::begin(session->torrents()),
std::end(session->torrents()),
std::back_inserter(torrents),
[&cutoff](auto const* tor) { return tor->anyDate >= cutoff; });
}
else
{
auto* const tor = session->getTorrent(sv);
auto* const tor = session->torrents().get(sv);
if (tor != nullptr)
{
torrents.push_back(tor);
@ -173,8 +173,8 @@ static auto getTorrents(tr_session* session, tr_variant* args)
}
else // all of them
{
torrents.reserve(std::size(session->torrents));
std::copy(std::begin(session->torrents), std::end(session->torrents), std::back_inserter(torrents));
torrents.reserve(std::size(session->torrents()));
std::copy(std::begin(session->torrents()), std::end(session->torrents()), std::back_inserter(torrents));
}
return torrents;
@ -1977,11 +1977,9 @@ static char const* sessionStats(
auto currentStats = tr_session_stats{};
auto cumulativeStats = tr_session_stats{};
int const total = std::size(session->torrents);
int const running = std::count_if(
std::begin(session->torrents),
std::end(session->torrents),
[](auto const* tor) { return tor->isRunning; });
auto const& torrents = session->torrents();
int const total = std::size(torrents);
int const running = std::count_if(std::begin(torrents), std::end(torrents), [](auto const* tor) { return tor->isRunning; });
tr_sessionGetStats(session, &currentStats);
tr_sessionGetCumulativeStats(session, &cumulativeStats);

View File

@ -144,17 +144,17 @@ std::optional<std::string> tr_session::WebMediator::publicAddress() const
unsigned int tr_session::WebMediator::clamp(int torrent_id, unsigned int byte_count) const
{
auto const lock = session_->unique_lock();
auto const it = session_->torrentsById.find(torrent_id);
return it == std::end(session_->torrentsById) ? 0U : it->second->bandwidth->clamp(TR_DOWN, byte_count);
auto const* const tor = session_->torrents().get(torrent_id);
return tor == nullptr ? 0U : tor->bandwidth->clamp(TR_DOWN, byte_count);
}
void tr_session::WebMediator::notifyBandwidthConsumed(int torrent_id, size_t byte_count)
{
auto const lock = session_->unique_lock();
auto const it = session_->torrentsById.find(torrent_id);
if (it != std::end(session_->torrentsById))
auto const* const tor = session_->torrents().get(torrent_id);
if (tor != nullptr)
{
it->second->bandwidth->notifyBandwidthConsumed(TR_DOWN, byte_count, true, tr_time_msec());
tor->bandwidth->notifyBandwidthConsumed(TR_DOWN, byte_count, true, tr_time_msec());
}
}
@ -574,7 +574,7 @@ static void onSaveTimer(evutil_socket_t /*fd*/, short /*what*/, void* vsession)
tr_logAddError("Error while flushing completed pieces from cache");
}
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
tr_torrentSave(tor);
}
@ -671,7 +671,7 @@ static void onNowTimer(evutil_socket_t /*fd*/, short /*what*/, void* vsession)
// TODO: this seems a little silly. Why do we increment this
// every second instead of computing the value as needed by
// subtracting the current time from a start time?
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
if (tor->isRunning)
{
@ -1233,7 +1233,7 @@ static void peerPortChanged(void* vsession)
open_incoming_peer_port(session);
tr_sharedPortChanged(session);
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
tr_torrentChangeMyPort(tor);
}
@ -1805,17 +1805,16 @@ double tr_sessionGetRawSpeed_KBps(tr_session const* session, tr_direction dir)
int tr_sessionCountTorrents(tr_session const* session)
{
return tr_isSession(session) ? std::size(session->torrents) : 0;
return tr_isSession(session) ? std::size(session->torrents()) : 0;
}
std::vector<tr_torrent*> tr_sessionGetTorrents(tr_session* session)
{
TR_ASSERT(tr_isSession(session));
auto const& src = session->torrents;
auto const n = std::size(src);
auto const n = std::size(session->torrents());
auto torrents = std::vector<tr_torrent*>{ n };
std::copy(std::begin(src), std::end(src), std::begin(torrents));
std::copy(std::begin(session->torrents()), std::end(session->torrents()), std::begin(torrents));
return torrents;
}
@ -2275,7 +2274,7 @@ void tr_session::setDefaultTrackers(std::string_view trackers)
// if the list changed, update all the public torrents
if (default_trackers_ != oldval)
{
for (auto* tor : torrents)
for (auto* const tor : torrents())
{
if (tor->isPublic())
{
@ -2808,7 +2807,7 @@ std::vector<tr_torrent*> tr_sessionGetNextQueuedTorrents(tr_session* session, tr
// build an array of the candidates
auto candidates = std::vector<tr_torrent*>{};
candidates.reserve(tr_sessionCountTorrents(session));
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
if (tor->isQueued() && (direction == tor->queueDirection()))
{
@ -2846,7 +2845,7 @@ int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction dir)
bool const stalled_enabled = tr_sessionGetQueueStalledEnabled(session);
int const stalled_if_idle_for_n_seconds = tr_sessionGetQueueStalledMinutes(session) * 60;
time_t const now = tr_time();
for (auto const* tor : session->torrents)
for (auto const* const tor : session->torrents())
{
/* is it the right activity? */
if (activity != tr_torrentGetActivity(tor))
@ -2875,23 +2874,3 @@ int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction dir)
return max - active_count;
}
void tr_sessionAddTorrent(tr_session* session, tr_torrent* tor)
{
session->torrents.insert(tor);
session->torrentsById.insert_or_assign(tor->uniqueId, tor);
session->torrentsByHash.insert_or_assign(tor->infoHash(), tor);
}
void tr_sessionRemoveTorrent(tr_session* session, tr_torrent* tor)
{
session->torrents.erase(tor);
session->torrentsById.erase(tor->uniqueId);
session->torrentsByHash.erase(tor->infoHash());
}
tr_torrent* tr_session::getTorrent(std::string_view info_dict_hash_string)
{
auto const info_hash = tr_sha1_from_string(info_dict_hash_string);
return info_hash ? this->getTorrent(*info_hash) : nullptr;
}

View File

@ -30,6 +30,7 @@
#include "announce-list.h"
#include "net.h" // tr_socket_t
#include "quark.h"
#include "torrents.h"
#include "web.h"
enum tr_auto_switch_state_t
@ -108,6 +109,16 @@ struct tr_turtle_info
struct tr_session
{
public:
[[nodiscard]] auto const& torrents() const
{
return torrents_;
}
[[nodiscard]] auto& torrents()
{
return torrents_;
}
[[nodiscard]] auto unique_lock() const
{
return std::unique_lock(session_mutex_);
@ -118,27 +129,6 @@ public:
return is_closing_;
}
[[nodiscard]] auto const* getTorrent(tr_sha1_digest_t const& info_dict_hash) const
{
auto& src = this->torrentsByHash;
auto it = src.find(info_dict_hash);
return it == std::end(src) ? nullptr : it->second;
}
[[nodiscard]] auto* getTorrent(tr_sha1_digest_t const& info_dict_hash)
{
auto& src = this->torrentsByHash;
auto it = src.find(info_dict_hash);
return it == std::end(src) ? nullptr : it->second;
}
[[nodiscard]] tr_torrent* getTorrent(std::string_view info_dict_hash_string);
[[nodiscard]] auto contains(tr_sha1_digest_t const& info_dict_hash) const
{
return getTorrent(info_dict_hash) != nullptr;
}
// download dir
std::string const& downloadDir() const
@ -348,10 +338,6 @@ public:
tr_port randomPortLow;
tr_port randomPortHigh;
std::unordered_set<tr_torrent*> torrents;
std::map<int, tr_torrent*> torrentsById;
std::map<tr_sha1_digest_t, tr_torrent*> torrentsByHash;
std::string config_dir;
std::string resume_dir;
std::string torrent_dir;
@ -423,6 +409,8 @@ public:
private:
static std::recursive_mutex session_mutex_;
tr_torrents torrents_;
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
std::string blocklist_url_;
std::string download_dir_;
@ -492,6 +480,3 @@ bool tr_sessionGetActiveSpeedLimit_Bps(tr_session const* session, tr_direction d
std::vector<tr_torrent*> tr_sessionGetNextQueuedTorrents(tr_session* session, tr_direction dir, size_t numwanted);
int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction);
void tr_sessionAddTorrent(tr_session* session, tr_torrent* tor);
void tr_sessionRemoveTorrent(tr_session* session, tr_torrent* tor);

View File

@ -88,14 +88,12 @@ int tr_torrentId(tr_torrent const* tor)
tr_torrent* tr_torrentFindFromId(tr_session* session, int id)
{
auto& src = session->torrentsById;
auto it = src.find(id);
return it == std::end(src) ? nullptr : it->second;
return session->torrents().get(id);
}
tr_torrent* tr_torrentFindFromHash(tr_session* session, tr_sha1_digest_t const* hash)
{
return hash == nullptr ? nullptr : session->getTorrent(*hash);
return hash == nullptr ? nullptr : session->torrents().get(*hash);
}
tr_torrent* tr_torrentFindFromMetainfo(tr_session* session, tr_torrent_metainfo const* metainfo)
@ -105,18 +103,17 @@ tr_torrent* tr_torrentFindFromMetainfo(tr_session* session, tr_torrent_metainfo
return nullptr;
}
return tr_torrentFindFromHash(session, &metainfo->infoHash());
return session->torrents().get(metainfo->infoHash());
}
tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet_link)
{
auto mm = tr_magnet_metainfo{};
return mm.parseMagnet(magnet_link != nullptr ? magnet_link : "") ? session->getTorrent(mm.infoHash()) : nullptr;
return magnet_link == nullptr ? nullptr : session->torrents().get(magnet_link);
}
tr_torrent* tr_torrentFindFromObfuscatedHash(tr_session* session, tr_sha1_digest_t const& obfuscated_hash)
{
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
if (tor->obfuscated_hash == obfuscated_hash)
{
@ -675,14 +672,12 @@ static void refreshCurrentDir(tr_torrent* tor);
static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
{
static auto next_unique_id = int{ 1 };
auto const lock = tor->unique_lock();
tr_session* session = tr_ctorGetSession(ctor);
TR_ASSERT(session != nullptr);
tor->session = session;
tor->uniqueId = next_unique_id++;
tor->queuePosition = tr_sessionCountTorrents(session);
torrentInitFromInfoDict(tor);
@ -765,7 +760,7 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
tr_torrentSetIdleLimit(tor, tr_sessionGetIdleLimit(tor->session));
}
tr_sessionAddTorrent(session, tor);
tor->uniqueId = session->torrents().add(tor);
// if we don't have a local .torrent or .magnet file already, assume the torrent is new
auto const filename = tor->hasMetadata() ? tor->torrentFile() : tor->magnetFile();
@ -845,7 +840,7 @@ tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of)
}
// is it a duplicate?
if (auto* const duplicate_of = session->getTorrent(metainfo.infoHash()); duplicate_of != nullptr)
if (auto* const duplicate_of = session->torrents().get(metainfo.infoHash()); duplicate_of != nullptr)
{
if (setme_duplicate_of != nullptr)
{
@ -1298,13 +1293,13 @@ static void freeTorrent(tr_torrent* tor)
tr_announcerRemoveTorrent(session->announcer, tor);
tr_sessionRemoveTorrent(session, tor);
session->torrents().remove(tor, tr_time());
if (!session->isClosing())
{
// "so you die, captain, and we all move up in rank."
// resequence the queue positions
for (auto* t : session->torrents)
for (auto* t : session->torrents())
{
if (t->queuePosition > tor->queuePosition)
{
@ -2768,7 +2763,7 @@ void tr_torrentSetQueuePosition(tr_torrent* tor, int pos)
tor->queuePosition = -1;
for (auto* walk : tor->session->torrents)
for (auto* const walk : tor->session->torrents())
{
if ((old_pos < pos) && (old_pos <= walk->queuePosition) && (walk->queuePosition <= pos))
{

141
libtransmission/torrents.cc Normal file
View File

@ -0,0 +1,141 @@
// This file Copyright © 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <set>
#include <string_view>
#include <vector>
#include "transmission.h"
#include "magnet-metainfo.h"
#include "torrent.h"
#include "torrents.h"
#include "tr-assert.h"
namespace
{
struct CompareTorrentByHash
{
bool operator()(tr_sha1_digest_t const& a, tr_sha1_digest_t const& b) const
{
return a < b;
}
bool operator()(tr_torrent const* a, tr_torrent const* b) const
{
return (*this)(a->infoHash(), b->infoHash());
}
bool operator()(tr_torrent const* a, tr_sha1_digest_t const& b) const
{
return (*this)(a->infoHash(), b);
}
bool operator()(tr_sha1_digest_t const& a, tr_torrent const* b) const
{
return (*this)(a, b->infoHash());
}
};
} // namespace
tr_torrents::tr_torrents()
// Insert an empty pointer at by_id_[0] to ensure that the first added
// torrent doesn't get an ID of 0; ie, that every torrent has a positive
// ID number. This constraint isn't needed by libtransmission code but
// the ID is exported in the RPC API to 3rd party clients that may be
// testing for >0 as a validity check.
: by_id_{ nullptr }
{
}
tr_torrent const* tr_torrents::get(int id) const
{
TR_ASSERT(0 <= id);
TR_ASSERT(static_cast<size_t>(id) < std::size(by_id_));
if (static_cast<size_t>(id) >= std::size(by_id_))
{
return nullptr;
}
auto const* tor = by_id_.at(id);
TR_ASSERT(tor == nullptr || tor->uniqueId == id);
TR_ASSERT(removed_.count(id) == (tor == nullptr ? 1 : 0));
return tor;
}
tr_torrent* tr_torrents::get(int id)
{
TR_ASSERT(0 <= id);
TR_ASSERT(static_cast<size_t>(id) < std::size(by_id_));
if (static_cast<size_t>(id) >= std::size(by_id_))
{
return nullptr;
}
auto* tor = by_id_.at(id);
TR_ASSERT(tor == nullptr || tor->uniqueId == id);
TR_ASSERT(removed_.count(id) == (tor == nullptr ? 1 : 0));
return tor;
}
tr_torrent const* tr_torrents::get(std::string_view magnet_link) const
{
auto magnet = tr_magnet_metainfo{};
return magnet.parseMagnet(magnet_link) ? get(magnet.infoHash()) : nullptr;
}
tr_torrent* tr_torrents::get(std::string_view magnet_link)
{
auto magnet = tr_magnet_metainfo{};
return magnet.parseMagnet(magnet_link) ? get(magnet.infoHash()) : nullptr;
}
tr_torrent* tr_torrents::get(tr_sha1_digest_t const& hash)
{
auto [begin, end] = std::equal_range(std::begin(by_hash_), std::end(by_hash_), hash, CompareTorrentByHash{});
return begin == end ? nullptr : *begin;
}
tr_torrent const* tr_torrents::get(tr_sha1_digest_t const& hash) const
{
auto [begin, end] = std::equal_range(std::cbegin(by_hash_), std::cend(by_hash_), hash, CompareTorrentByHash{});
return begin == end ? nullptr : *begin;
}
int tr_torrents::add(tr_torrent* tor)
{
int const id = static_cast<int>(std::size(by_id_));
by_id_.push_back(tor);
by_hash_.insert(std::lower_bound(std::begin(by_hash_), std::end(by_hash_), tor, CompareTorrentByHash{}), tor);
return id;
}
void tr_torrents::remove(tr_torrent const* tor, time_t timestamp)
{
TR_ASSERT(tor != nullptr);
TR_ASSERT(get(tor->uniqueId) == tor);
by_id_[tor->uniqueId] = nullptr;
auto const [begin, end] = std::equal_range(std::begin(by_hash_), std::end(by_hash_), tor, CompareTorrentByHash{});
by_hash_.erase(begin, end);
removed_.insert_or_assign(tor->uniqueId, timestamp);
}
std::set<int> tr_torrents::removedSince(time_t timestamp) const
{
auto ret = std::set<int>{};
for (auto const& [id, removed_at] : removed_)
{
if (removed_at >= timestamp)
{
ret.insert(id);
}
}
return ret;
}

118
libtransmission/torrents.h Normal file
View File

@ -0,0 +1,118 @@
// This file Copyright © 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#pragma once
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#endif
#include <ctime>
#include <map>
#include <set>
#include <string_view>
#include <vector>
#include "transmission.h"
#include "torrent-metainfo.h"
struct tr_torrent;
struct tr_torrent_metainfo;
// A helper class to manage tracking sets of tr_torrent objects.
class tr_torrents
{
public:
tr_torrents();
// returns a fast lookup id for `tor`
[[nodiscard]] int add(tr_torrent* tor);
void remove(tr_torrent const* tor, time_t current_time);
// O(1)
[[nodiscard]] tr_torrent* get(int id);
[[nodiscard]] tr_torrent const* get(int id) const;
// O(log n)
[[nodiscard]] tr_torrent const* get(tr_sha1_digest_t const& hash) const;
[[nodiscard]] tr_torrent* get(tr_sha1_digest_t const& hash);
[[nodiscard]] tr_torrent const* get(tr_torrent_metainfo const& metainfo) const
{
return get(metainfo.infoHash());
}
[[nodiscard]] tr_torrent* get(tr_torrent_metainfo const& metainfo)
{
return get(metainfo.infoHash());
}
// These convenience functions use get(tr_sha1_digest_t const&)
// after parsing the magnet link to get the info hash. If you have
// the info hash already, use get() instead to avoid excess parsing.
[[nodiscard]] tr_torrent const* get(std::string_view magnet_link) const;
[[nodiscard]] tr_torrent* get(std::string_view magnet_link);
template<typename T>
[[nodiscard]] bool contains(T const& key) const
{
return get(key) != nullptr;
}
[[nodiscard]] std::set<int> removedSince(time_t) const;
[[nodiscard]] auto cbegin() const
{
return std::cbegin(by_hash_);
}
[[nodiscard]] auto begin() const
{
return cbegin();
}
[[nodiscard]] auto begin()
{
return std::begin(by_hash_);
}
[[nodiscard]] auto cend() const
{
return std::cend(by_hash_);
}
[[nodiscard]] auto end() const
{
return cend();
}
[[nodiscard]] auto end()
{
return std::end(by_hash_);
}
[[nodiscard]] auto size() const
{
return std::size(by_hash_);
}
[[nodiscard]] auto empty() const
{
return std::empty(by_hash_);
}
private:
std::vector<tr_torrent*> by_hash_;
// This is a lookup table where by_id_[id]->uniqueId == id.
// There is a small tradeoff here -- lookup is O(1) at the cost
// of a wasted slot in the lookup table whenever a torrent is
// removed. This improves speed for all use cases at the cost of
// wasting a small amount of memory in uncommon use cases, e.g. a
// long-lived session where thousands of torrents are removed
std::vector<tr_torrent*> by_id_;
std::map<int, time_t> removed_;
};

View File

@ -600,7 +600,7 @@ static void callback(void* /*ignore*/, int event, unsigned char const* info_hash
auto hash = tr_sha1_digest_t{};
std::copy_n(reinterpret_cast<std::byte const*>(info_hash), std::size(hash), std::data(hash));
auto const lock = session_->unique_lock();
tr_torrent* const tor = session_->getTorrent(hash);
auto* const tor = session_->torrents().get(hash);
if (event == DHT_EVENT_VALUES || event == DHT_EVENT_VALUES6)
{
@ -700,7 +700,7 @@ void tr_dhtUpkeep(tr_session* session)
{
time_t const now = tr_time();
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
if (!tor->isRunning || !tor->allowsDht())
{

View File

@ -533,7 +533,7 @@ static int tr_lpdConsiderAnnounce(tr_pex* peer, char const* const msg)
return res;
}
tor = session->getTorrent(hashString);
tor = session->torrents().get(hashString);
if (tr_isTorrent(tor) && tor->allowsLpd())
{
@ -576,7 +576,7 @@ static int tr_lpdAnnounceMore(time_t const now, int const interval)
if (tr_sessionAllowsLPD(session))
{
for (auto* tor : session->torrents)
for (auto* const tor : session->torrents())
{
int announcePrio = 0;

View File

@ -30,6 +30,7 @@ add_executable(libtransmission-test
subprocess-test.cc
test-fixtures.h
torrent-metainfo-test.cc
torrents-test.cc
utils-test.cc
variant-test.cc
watchdir-test.cc

View File

@ -0,0 +1,122 @@
// This file Copyright (C) 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include "transmission.h"
#include "torrent.h"
#include "torrents.h"
#include "utils.h"
#include "gtest/gtest.h"
#include <cstring>
#include <set>
using namespace std::literals;
using TorrentsTest = ::testing::Test;
TEST_F(TorrentsTest, simpleTests)
{
auto constexpr* const TorrentFile = LIBTRANSMISSION_TEST_ASSETS_DIR "/Android-x86 8.1 r6 iso.torrent";
auto tm = tr_torrent_metainfo{};
EXPECT_TRUE(tm.parseTorrentFile(TorrentFile));
auto* tor = new tr_torrent(std::move(tm));
EXPECT_NE(nullptr, tor);
auto torrents = tr_torrents{};
EXPECT_TRUE(std::empty(torrents));
EXPECT_EQ(0U, std::size(torrents));
auto const id = torrents.add(tor);
EXPECT_GT(id, 0);
tor->uniqueId = id;
EXPECT_TRUE(std::empty(torrents.removedSince(0)));
EXPECT_FALSE(std::empty(torrents));
EXPECT_EQ(1U, std::size(torrents));
EXPECT_EQ(tor, torrents.get(id));
EXPECT_EQ(tor, torrents.get(tor->infoHash()));
EXPECT_EQ(tor, torrents.get(tor->magnet()));
tm = tr_torrent_metainfo{};
EXPECT_TRUE(tm.parseTorrentFile(TorrentFile));
EXPECT_EQ(tor, torrents.get(tm));
// cleanup
torrents.remove(tor, time(nullptr));
delete tor;
}
TEST_F(TorrentsTest, rangedLoop)
{
auto constexpr Filenames = std::array<std::string_view, 4>{ "Android-x86 8.1 r6 iso.torrent"sv,
"debian-11.2.0-amd64-DVD-1.iso.torrent"sv,
"ubuntu-18.04.6-desktop-amd64.iso.torrent"sv,
"ubuntu-20.04.4-desktop-amd64.iso.torrent"sv };
auto torrents = tr_torrents{};
auto torrents_set = std::set<tr_torrent const*>{};
for (auto const& name : Filenames)
{
auto const path = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/"sv, name);
auto tm = tr_torrent_metainfo{};
EXPECT_TRUE(tm.parseTorrentFile(path));
auto* const tor = new tr_torrent(std::move(tm));
tor->uniqueId = torrents.add(tor);
EXPECT_EQ(tor, torrents.get(tor->uniqueId));
torrents_set.insert(tor);
}
for (auto const* tor : torrents)
{
EXPECT_EQ(1U, torrents_set.erase(tor));
}
EXPECT_EQ(0U, std::size(torrents_set));
EXPECT_EQ(0U, std::size(torrents_set));
}
TEST_F(TorrentsTest, removedSince)
{
auto constexpr Filenames = std::array<std::string_view, 4>{ "Android-x86 8.1 r6 iso.torrent"sv,
"debian-11.2.0-amd64-DVD-1.iso.torrent"sv,
"ubuntu-18.04.6-desktop-amd64.iso.torrent"sv,
"ubuntu-20.04.4-desktop-amd64.iso.torrent"sv };
auto torrents = tr_torrents{};
auto torrents_v = std::vector<tr_torrent const*>{};
torrents_v.reserve(std::size(Filenames));
// setup: add the torrents
for (auto const& name : Filenames)
{
auto const path = tr_strvJoin(LIBTRANSMISSION_TEST_ASSETS_DIR, "/"sv, name);
auto tm = tr_torrent_metainfo{};
auto* const tor = new tr_torrent(std::move(tm));
tor->uniqueId = torrents.add(tor);
torrents_v.push_back(tor);
}
// setup: remove them at the given timestamp
auto constexpr TimeRemoved = std::array<time_t, 4>{ 100, 200, 200, 300 };
for (size_t i = 0; i < 4; ++i)
{
auto* const tor = torrents_v[i];
EXPECT_EQ(tor, torrents.get(tor->uniqueId));
torrents.remove(torrents_v[i], TimeRemoved[i]);
EXPECT_EQ(nullptr, torrents.get(tor->uniqueId));
}
auto remove = std::set<int>{};
remove = { torrents_v[3]->uniqueId };
EXPECT_EQ(remove, torrents.removedSince(300));
EXPECT_EQ(remove, torrents.removedSince(201));
remove = { torrents_v[1]->uniqueId, torrents_v[2]->uniqueId, torrents_v[3]->uniqueId };
EXPECT_EQ(remove, torrents.removedSince(200));
remove = { torrents_v[0]->uniqueId, torrents_v[1]->uniqueId, torrents_v[2]->uniqueId, torrents_v[3]->uniqueId };
EXPECT_EQ(remove, torrents.removedSince(50));
}