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

View File

@ -60,6 +60,7 @@ set(PROJECT_FILES
torrent-magnet.cc torrent-magnet.cc
torrent-metainfo.cc torrent-metainfo.cc
torrent.cc torrent.cc
torrents.cc
tr-assert.cc tr-assert.cc
tr-assert.mm tr-assert.mm
tr-dht.cc tr-dht.cc
@ -194,6 +195,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
torrent-magnet.h torrent-magnet.h
torrent-metainfo.h torrent-metainfo.h
torrent.h torrent.h
torrents.h
tr-dht.h tr-dht.h
tr-lpd.h tr-lpd.h
tr-udp.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; 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) if (tor == nullptr || tor->torrent_announcer == nullptr)
{ {
return 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) for (int i = 0; i < response->row_count; ++i)
{ {
auto const& row = response->rows[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) if (tor != nullptr)
{ {
@ -1535,7 +1535,7 @@ static void scrapeAndAnnounceMore(tr_announcer* announcer)
/* build a list of tiers that need to be announced */ /* build a list of tiers that need to be announced */
auto announce_me = std::vector<tr_tier*>{}; auto announce_me = std::vector<tr_tier*>{};
auto scrape_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) 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) static bool buildHandshakeMessage(tr_handshake* handshake, uint8_t* buf)
{ {
auto const torrent_hash = tr_cryptoGetTorrentHash(handshake->crypto); 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; bool const success = tor != nullptr;
if (success) if (success)
@ -257,7 +257,7 @@ static handshake_parse_err_t parseHandshake(tr_handshake* handshake, struct evbu
/* peer id */ /* peer id */
dbgmsg(handshake, "peer-id is [%" TR_PRIsv "]", TR_PRIsv_ARG(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."); dbgmsg(handshake, "streuth! we've connected to ourselves.");
return HANDSHAKE_PEER_IS_SELF; return HANDSHAKE_PEER_IS_SELF;
@ -647,7 +647,7 @@ static ReadState readHandshake(tr_handshake* handshake, struct evbuffer* inbuf)
if (tr_peerIoIsIncoming(handshake->io)) 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."); dbgmsg(handshake, "peer is trying to connect to us for a torrent we don't have.");
return tr_handshakeDone(handshake, false); 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 // if we've somehow connected to ourselves, don't keep the connection
auto const hash = tr_peerIoGetTorrentHash(handshake->io); 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); bool const connected_to_self = peer_id == tr_torrentGetPeerId(tor);
return tr_handshakeDone(handshake, !connected_to_self); 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. */ /* This peer probably doesn't speak uTP. */
auto const hash = tr_peerIoGetTorrentHash(io); 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. */ /* Don't mark a peer as non-uTP unless it's really a connect failure. */
if ((errcode == ETIMEDOUT || errcode == ECONNREFUSED) && tr_isTorrent(tor)) 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) 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; 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... /* we cache whether or not a peer is blocklisted...
since the blocklist has changed, erase that cached value */ 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; 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* mgr = static_cast<tr_peerMgr*>(vmgr);
auto const lock = mgr->unique_lock(); 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); }); std::for_each(std::begin(torrents), std::end(torrents), [](auto* tor) { tr_swarmCancelOldRequests(tor->swarm); });
tr_timerAddMsec(mgr->refillUpkeepTimer, RefillUpkeepPeriodMsec); 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(); auto const lock = mgr->unique_lock();
uint64_t const now = tr_time_msec(); uint64_t const now = tr_time_msec();
for (auto* tor : mgr->session->torrents) for (auto* const tor : mgr->session->torrents())
{ {
if (tor->isRunning) if (tor->isRunning)
{ {
@ -2479,9 +2479,10 @@ static void enforceTorrentPeerLimit(tr_swarm* s)
static void enforceSessionPeerLimit(tr_session* session) static void enforceSessionPeerLimit(tr_session* session)
{ {
// do we have too many peers? // do we have too many peers?
auto const& torrents = session->torrents();
size_t const n_peers = std::accumulate( size_t const n_peers = std::accumulate(
std::begin(session->torrents), std::begin(torrents),
std::end(session->torrents), std::end(torrents),
size_t{}, size_t{},
[](size_t sum, tr_torrent const* tor) { return sum + tr_ptrArraySize(&tor->swarm->peers); }); [](size_t sum, tr_torrent const* tor) { return sum + tr_ptrArraySize(&tor->swarm->peers); });
size_t const max = tr_sessionGetPeerLimit(session); size_t const max = tr_sessionGetPeerLimit(session);
@ -2493,7 +2494,7 @@ static void enforceSessionPeerLimit(tr_session* session)
// make a list of all the peers // make a list of all the peers
auto peers = std::vector<tr_peer*>{}; auto peers = std::vector<tr_peer*>{};
peers.reserve(n_peers); peers.reserve(n_peers);
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
size_t const n = tr_ptrArraySize(&tor->swarm->peers); size_t const n = tr_ptrArraySize(&tor->swarm->peers);
auto** base = (tr_peer**)tr_ptrArrayBase(&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(); time_t const now_sec = tr_time();
// remove crappy peers // remove crappy peers
for (auto* tor : mgr->session->torrents) for (auto* const tor : mgr->session->torrents())
{ {
if (!tor->swarm->isRunning) 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 // 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) if (tor->isRunning)
{ {
@ -2550,7 +2551,7 @@ static void reconnectPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
static void pumpAllPeers(tr_peerMgr* mgr) static void pumpAllPeers(tr_peerMgr* mgr)
{ {
for (auto* tor : mgr->session->torrents) for (auto* const tor : mgr->session->torrents())
{ {
tr_swarm* s = tor->swarm; 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); session->bandwidth->allocate(TR_DOWN, BandwidthPeriodMsec);
/* torrent upkeep */ /* torrent upkeep */
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
/* possibly stop torrents that have seeded enough */ /* possibly stop torrents that have seeded enough */
tr_torrentCheckSeedLimit(tor); 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* mgr = static_cast<tr_peerMgr*>(vmgr);
auto const lock = mgr->unique_lock(); auto const lock = mgr->unique_lock();
for (auto* tor : mgr->session->torrents) for (auto* const tor : mgr->session->torrents())
{ {
tr_swarm* s = tor->swarm; tr_swarm* s = tor->swarm;
int const maxAtomCount = getMaxAtomCount(tor); 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 */ /* count how many peers and atoms we've got */
int atomCount = 0; int atomCount = 0;
int peerCount = 0; int peerCount = 0;
for (auto const* tor : session->torrents) for (auto const* tor : session->torrents())
{ {
atomCount += tr_ptrArraySize(&tor->swarm->pool); atomCount += tr_ptrArraySize(&tor->swarm->pool);
peerCount += tr_ptrArraySize(&tor->swarm->peers); peerCount += tr_ptrArraySize(&tor->swarm->peers);
@ -2936,7 +2937,7 @@ static std::vector<peer_candidate> getPeerCandidates(tr_session* session, size_t
candidates.reserve(atomCount); candidates.reserve(atomCount);
/* populate the candidate array */ /* populate the candidate array */
for (auto* tor : session->torrents) for (auto* tor : session->torrents())
{ {
if (!tor->swarm->isRunning) 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)) else if (tr_variantGetStrView(node, &sv))
{ {
tor = session->getTorrent(sv); tor = session->torrents().get(sv);
} }
if (tor != nullptr) if (tor != nullptr)
@ -155,16 +155,16 @@ static auto getTorrents(tr_session* session, tr_variant* args)
{ {
time_t const cutoff = tr_time() - RecentlyActiveSeconds; time_t const cutoff = tr_time() - RecentlyActiveSeconds;
torrents.reserve(std::size(session->torrents)); torrents.reserve(std::size(session->torrents()));
std::copy_if( std::copy_if(
std::begin(session->torrents), std::begin(session->torrents()),
std::end(session->torrents), std::end(session->torrents()),
std::back_inserter(torrents), std::back_inserter(torrents),
[&cutoff](auto const* tor) { return tor->anyDate >= cutoff; }); [&cutoff](auto const* tor) { return tor->anyDate >= cutoff; });
} }
else else
{ {
auto* const tor = session->getTorrent(sv); auto* const tor = session->torrents().get(sv);
if (tor != nullptr) if (tor != nullptr)
{ {
torrents.push_back(tor); torrents.push_back(tor);
@ -173,8 +173,8 @@ static auto getTorrents(tr_session* session, tr_variant* args)
} }
else // all of them else // all of them
{ {
torrents.reserve(std::size(session->torrents)); torrents.reserve(std::size(session->torrents()));
std::copy(std::begin(session->torrents), std::end(session->torrents), std::back_inserter(torrents)); std::copy(std::begin(session->torrents()), std::end(session->torrents()), std::back_inserter(torrents));
} }
return torrents; return torrents;
@ -1977,11 +1977,9 @@ static char const* sessionStats(
auto currentStats = tr_session_stats{}; auto currentStats = tr_session_stats{};
auto cumulativeStats = tr_session_stats{}; auto cumulativeStats = tr_session_stats{};
int const total = std::size(session->torrents); auto const& torrents = session->torrents();
int const running = std::count_if( int const total = std::size(torrents);
std::begin(session->torrents), int const running = std::count_if(std::begin(torrents), std::end(torrents), [](auto const* tor) { return tor->isRunning; });
std::end(session->torrents),
[](auto const* tor) { return tor->isRunning; });
tr_sessionGetStats(session, &currentStats); tr_sessionGetStats(session, &currentStats);
tr_sessionGetCumulativeStats(session, &cumulativeStats); 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 unsigned int tr_session::WebMediator::clamp(int torrent_id, unsigned int byte_count) const
{ {
auto const lock = session_->unique_lock(); auto const lock = session_->unique_lock();
auto const it = session_->torrentsById.find(torrent_id); auto const* const tor = session_->torrents().get(torrent_id);
return it == std::end(session_->torrentsById) ? 0U : it->second->bandwidth->clamp(TR_DOWN, byte_count); return tor == nullptr ? 0U : tor->bandwidth->clamp(TR_DOWN, byte_count);
} }
void tr_session::WebMediator::notifyBandwidthConsumed(int torrent_id, size_t byte_count) void tr_session::WebMediator::notifyBandwidthConsumed(int torrent_id, size_t byte_count)
{ {
auto const lock = session_->unique_lock(); auto const lock = session_->unique_lock();
auto const it = session_->torrentsById.find(torrent_id); auto const* const tor = session_->torrents().get(torrent_id);
if (it != std::end(session_->torrentsById)) 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"); tr_logAddError("Error while flushing completed pieces from cache");
} }
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
tr_torrentSave(tor); 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 // TODO: this seems a little silly. Why do we increment this
// every second instead of computing the value as needed by // every second instead of computing the value as needed by
// subtracting the current time from a start time? // subtracting the current time from a start time?
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
if (tor->isRunning) if (tor->isRunning)
{ {
@ -1233,7 +1233,7 @@ static void peerPortChanged(void* vsession)
open_incoming_peer_port(session); open_incoming_peer_port(session);
tr_sharedPortChanged(session); tr_sharedPortChanged(session);
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
tr_torrentChangeMyPort(tor); 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) 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) std::vector<tr_torrent*> tr_sessionGetTorrents(tr_session* session)
{ {
TR_ASSERT(tr_isSession(session)); TR_ASSERT(tr_isSession(session));
auto const& src = session->torrents; auto const n = std::size(session->torrents());
auto const n = std::size(src);
auto torrents = std::vector<tr_torrent*>{ n }; 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; return torrents;
} }
@ -2275,7 +2274,7 @@ void tr_session::setDefaultTrackers(std::string_view trackers)
// if the list changed, update all the public torrents // if the list changed, update all the public torrents
if (default_trackers_ != oldval) if (default_trackers_ != oldval)
{ {
for (auto* tor : torrents) for (auto* const tor : torrents())
{ {
if (tor->isPublic()) if (tor->isPublic())
{ {
@ -2808,7 +2807,7 @@ std::vector<tr_torrent*> tr_sessionGetNextQueuedTorrents(tr_session* session, tr
// build an array of the candidates // build an array of the candidates
auto candidates = std::vector<tr_torrent*>{}; auto candidates = std::vector<tr_torrent*>{};
candidates.reserve(tr_sessionCountTorrents(session)); candidates.reserve(tr_sessionCountTorrents(session));
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
if (tor->isQueued() && (direction == tor->queueDirection())) 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); bool const stalled_enabled = tr_sessionGetQueueStalledEnabled(session);
int const stalled_if_idle_for_n_seconds = tr_sessionGetQueueStalledMinutes(session) * 60; int const stalled_if_idle_for_n_seconds = tr_sessionGetQueueStalledMinutes(session) * 60;
time_t const now = tr_time(); time_t const now = tr_time();
for (auto const* tor : session->torrents) for (auto const* const tor : session->torrents())
{ {
/* is it the right activity? */ /* is it the right activity? */
if (activity != tr_torrentGetActivity(tor)) if (activity != tr_torrentGetActivity(tor))
@ -2875,23 +2874,3 @@ int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction dir)
return max - active_count; 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 "announce-list.h"
#include "net.h" // tr_socket_t #include "net.h" // tr_socket_t
#include "quark.h" #include "quark.h"
#include "torrents.h"
#include "web.h" #include "web.h"
enum tr_auto_switch_state_t enum tr_auto_switch_state_t
@ -108,6 +109,16 @@ struct tr_turtle_info
struct tr_session struct tr_session
{ {
public: public:
[[nodiscard]] auto const& torrents() const
{
return torrents_;
}
[[nodiscard]] auto& torrents()
{
return torrents_;
}
[[nodiscard]] auto unique_lock() const [[nodiscard]] auto unique_lock() const
{ {
return std::unique_lock(session_mutex_); return std::unique_lock(session_mutex_);
@ -118,27 +129,6 @@ public:
return is_closing_; 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 // download dir
std::string const& downloadDir() const std::string const& downloadDir() const
@ -348,10 +338,6 @@ public:
tr_port randomPortLow; tr_port randomPortLow;
tr_port randomPortHigh; 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 config_dir;
std::string resume_dir; std::string resume_dir;
std::string torrent_dir; std::string torrent_dir;
@ -423,6 +409,8 @@ public:
private: private:
static std::recursive_mutex session_mutex_; static std::recursive_mutex session_mutex_;
tr_torrents torrents_;
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_; std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
std::string blocklist_url_; std::string blocklist_url_;
std::string download_dir_; 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); std::vector<tr_torrent*> tr_sessionGetNextQueuedTorrents(tr_session* session, tr_direction dir, size_t numwanted);
int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction); 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) tr_torrent* tr_torrentFindFromId(tr_session* session, int id)
{ {
auto& src = session->torrentsById; return session->torrents().get(id);
auto it = src.find(id);
return it == std::end(src) ? nullptr : it->second;
} }
tr_torrent* tr_torrentFindFromHash(tr_session* session, tr_sha1_digest_t const* hash) 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) 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 nullptr;
} }
return tr_torrentFindFromHash(session, &metainfo->infoHash()); return session->torrents().get(metainfo->infoHash());
} }
tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet_link) tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet_link)
{ {
auto mm = tr_magnet_metainfo{}; return magnet_link == nullptr ? nullptr : session->torrents().get(magnet_link);
return mm.parseMagnet(magnet_link != nullptr ? magnet_link : "") ? session->getTorrent(mm.infoHash()) : nullptr;
} }
tr_torrent* tr_torrentFindFromObfuscatedHash(tr_session* session, tr_sha1_digest_t const& obfuscated_hash) 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) 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 void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
{ {
static auto next_unique_id = int{ 1 };
auto const lock = tor->unique_lock(); auto const lock = tor->unique_lock();
tr_session* session = tr_ctorGetSession(ctor); tr_session* session = tr_ctorGetSession(ctor);
TR_ASSERT(session != nullptr); TR_ASSERT(session != nullptr);
tor->session = session; tor->session = session;
tor->uniqueId = next_unique_id++;
tor->queuePosition = tr_sessionCountTorrents(session); tor->queuePosition = tr_sessionCountTorrents(session);
torrentInitFromInfoDict(tor); torrentInitFromInfoDict(tor);
@ -765,7 +760,7 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
tr_torrentSetIdleLimit(tor, tr_sessionGetIdleLimit(tor->session)); 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 // 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(); 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? // 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) if (setme_duplicate_of != nullptr)
{ {
@ -1298,13 +1293,13 @@ static void freeTorrent(tr_torrent* tor)
tr_announcerRemoveTorrent(session->announcer, tor); tr_announcerRemoveTorrent(session->announcer, tor);
tr_sessionRemoveTorrent(session, tor); session->torrents().remove(tor, tr_time());
if (!session->isClosing()) if (!session->isClosing())
{ {
// "so you die, captain, and we all move up in rank." // "so you die, captain, and we all move up in rank."
// resequence the queue positions // resequence the queue positions
for (auto* t : session->torrents) for (auto* t : session->torrents())
{ {
if (t->queuePosition > tor->queuePosition) if (t->queuePosition > tor->queuePosition)
{ {
@ -2768,7 +2763,7 @@ void tr_torrentSetQueuePosition(tr_torrent* tor, int pos)
tor->queuePosition = -1; 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)) 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{}; auto hash = tr_sha1_digest_t{};
std::copy_n(reinterpret_cast<std::byte const*>(info_hash), std::size(hash), std::data(hash)); std::copy_n(reinterpret_cast<std::byte const*>(info_hash), std::size(hash), std::data(hash));
auto const lock = session_->unique_lock(); 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) 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(); time_t const now = tr_time();
for (auto* tor : session->torrents) for (auto* const tor : session->torrents())
{ {
if (!tor->isRunning || !tor->allowsDht()) if (!tor->isRunning || !tor->allowsDht())
{ {

View File

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

View File

@ -30,6 +30,7 @@ add_executable(libtransmission-test
subprocess-test.cc subprocess-test.cc
test-fixtures.h test-fixtures.h
torrent-metainfo-test.cc torrent-metainfo-test.cc
torrents-test.cc
utils-test.cc utils-test.cc
variant-test.cc variant-test.cc
watchdir-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));
}