diff --git a/src/base/bittorrent/infohash.cpp b/src/base/bittorrent/infohash.cpp index 907b05fe4..35c2622da 100644 --- a/src/base/bittorrent/infohash.cpp +++ b/src/base/bittorrent/infohash.cpp @@ -36,6 +36,13 @@ BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash) { } +#ifdef QBT_USES_LIBTORRENT2 +BitTorrent::InfoHash::InfoHash(const SHA1Hash &v1, const SHA256Hash &v2) + : InfoHash {WrappedType(v1, v2)} +{ +} +#endif + bool BitTorrent::InfoHash::isValid() const { return m_valid; diff --git a/src/base/bittorrent/infohash.h b/src/base/bittorrent/infohash.h index d7848a151..dc1ac6a67 100644 --- a/src/base/bittorrent/infohash.h +++ b/src/base/bittorrent/infohash.h @@ -65,6 +65,9 @@ namespace BitTorrent InfoHash() = default; InfoHash(const WrappedType &nativeHash); +#ifdef QBT_USES_LIBTORRENT2 + InfoHash(const SHA1Hash &v1, const SHA256Hash &v2); +#endif bool isValid() const; SHA1Hash v1() const; @@ -85,3 +88,6 @@ namespace BitTorrent } Q_DECLARE_METATYPE(BitTorrent::TorrentID) +// We can declare it as Q_MOVABLE_TYPE to improve performance +// since base type uses QSharedDataPointer as the only member +Q_DECLARE_TYPEINFO(BitTorrent::TorrentID, Q_MOVABLE_TYPE); diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index cd8264d0d..366a62d0f 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -4346,53 +4346,114 @@ void Session::startUpTorrents() const QVector torrents = startupStorage->registeredTorrents(); int resumedTorrentsCount = 0; QVector queue; - for (const TorrentID &torrentID : torrents) +#ifdef QBT_USES_LIBTORRENT2 + const QSet indexedTorrents {torrents.cbegin(), torrents.cend()}; + QSet skippedIDs; +#endif + for (TorrentID torrentID : torrents) { +#ifdef QBT_USES_LIBTORRENT2 + if (skippedIDs.contains(torrentID)) + continue; +#endif + const std::optional loadResumeDataResult = startupStorage->load(torrentID); - if (loadResumeDataResult) - { - LoadTorrentParams resumeData = *loadResumeDataResult; - bool needStore = false; - - if (m_resumeDataStorage != startupStorage) - { - needStore = true; - if (isQueueingSystemEnabled() && !resumeData.hasSeedStatus) - queue.append(torrentID); - } - - // TODO: Remove the following upgrade code in v4.5 - // == BEGIN UPGRADE CODE == - if (m_needUpgradeDownloadPath && isDownloadPathEnabled()) - { - if (!resumeData.useAutoTMM) - { - resumeData.downloadPath = downloadPath(); - needStore = true; - } - } - // == END UPGRADE CODE == - - if (needStore) - m_resumeDataStorage->store(torrentID, resumeData); - - qDebug() << "Starting up torrent" << torrentID.toString() << "..."; - if (!loadTorrent(resumeData)) - { - LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") - .arg(torrentID.toString()), Log::CRITICAL); - } - - // process add torrent messages before message queue overflow - if ((resumedTorrentsCount % 100) == 0) readAlerts(); - - ++resumedTorrentsCount; - } - else + if (!loadResumeDataResult) { LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") .arg(torrentID.toString()), Log::CRITICAL); + continue; } + + LoadTorrentParams resumeData = *loadResumeDataResult; + bool needStore = false; + +#ifdef QBT_USES_LIBTORRENT2 + const lt::info_hash_t infoHash = (resumeData.ltAddTorrentParams.ti + ? resumeData.ltAddTorrentParams.ti->info_hashes() + : resumeData.ltAddTorrentParams.info_hashes); + if (infoHash.has_v1() && infoHash.has_v2()) + { + const auto torrentIDv1 = TorrentID::fromInfoHash(lt::info_hash_t(infoHash.v1)); + const auto torrentIDv2 = TorrentID::fromInfoHash(infoHash); + if (torrentID == torrentIDv2) + { + if (indexedTorrents.contains(torrentIDv1)) + { + // if we has no metadata trying to find it in alternative "resume data" + if (!resumeData.ltAddTorrentParams.ti) + { + const std::optional loadAltResumeDataResult = startupStorage->load(torrentIDv1); + if (loadAltResumeDataResult) + { + LoadTorrentParams altResumeData = *loadAltResumeDataResult; + resumeData.ltAddTorrentParams.ti = altResumeData.ltAddTorrentParams.ti; + } + } + + // remove alternative "resume data" and skip the attempt to load it + m_resumeDataStorage->remove(torrentIDv1); + skippedIDs.insert(torrentIDv1); + } + } + else + { + torrentID = torrentIDv2; + needStore = true; + m_resumeDataStorage->remove(torrentIDv1); + + if (indexedTorrents.contains(torrentID)) + { + skippedIDs.insert(torrentID); + + const std::optional loadPreferredResumeDataResult = startupStorage->load(torrentID); + if (loadPreferredResumeDataResult) + { + LoadTorrentParams preferredResumeData = *loadPreferredResumeDataResult; + std::shared_ptr ti = resumeData.ltAddTorrentParams.ti; + if (!preferredResumeData.ltAddTorrentParams.ti) + preferredResumeData.ltAddTorrentParams.ti = ti; + + resumeData = preferredResumeData; + } + } + } + } +#endif + + if (m_resumeDataStorage != startupStorage) + { + needStore = true; + if (isQueueingSystemEnabled() && !resumeData.hasSeedStatus) + queue.append(torrentID); + } + + // TODO: Remove the following upgrade code in v4.6 + // == BEGIN UPGRADE CODE == + if (m_needUpgradeDownloadPath && isDownloadPathEnabled()) + { + if (!resumeData.useAutoTMM) + { + resumeData.downloadPath = downloadPath(); + needStore = true; + } + } + // == END UPGRADE CODE == + + if (needStore) + m_resumeDataStorage->store(torrentID, resumeData); + + qDebug() << "Starting up torrent" << torrentID.toString() << "..."; + if (!loadTorrent(resumeData)) + { + LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") + .arg(torrentID.toString()), Log::CRITICAL); + } + + // process add torrent messages before message queue overflow + if ((resumedTorrentsCount % 100) == 0) readAlerts(); + + ++resumedTorrentsCount; } if (m_resumeDataStorage != startupStorage) diff --git a/src/base/digest32.h b/src/base/digest32.h index 2241ef691..f24b448f3 100644 --- a/src/base/digest32.h +++ b/src/base/digest32.h @@ -32,6 +32,8 @@ #include #include +#include +#include #include template @@ -41,13 +43,15 @@ public: using UnderlyingType = lt::digest32; Digest32() = default; + Digest32(const Digest32 &other) = default; + Digest32(Digest32 &&other) = default; Digest32(const UnderlyingType &nativeDigest) - : m_valid {true} - , m_nativeDigest {nativeDigest} { + m_dataPtr->valid = true; + m_dataPtr->nativeDigest = nativeDigest; const QByteArray raw = QByteArray::fromRawData(nativeDigest.data(), length()); - m_hashString = QString::fromLatin1(raw.toHex()); + m_dataPtr->hashString = QString::fromLatin1(raw.toHex()); } static constexpr int length() @@ -57,12 +61,15 @@ public: bool isValid() const { - return m_valid; + return m_dataPtr->valid; } + Digest32 &operator=(const Digest32 &other) = default; + Digest32 &operator=(Digest32 &&other) = default; + operator UnderlyingType() const { - return m_nativeDigest; + return m_dataPtr->nativeDigest; } static Digest32 fromString(const QString &digestString) @@ -75,22 +82,27 @@ public: return {}; Digest32 result; - result.m_valid = true; - result.m_hashString = digestString; - result.m_nativeDigest.assign(raw.constData()); + result.m_dataPtr->valid = true; + result.m_dataPtr->hashString = digestString; + result.m_dataPtr->nativeDigest.assign(raw.constData()); return result; } QString toString() const { - return m_hashString; + return m_dataPtr->hashString; } private: - bool m_valid = false; - UnderlyingType m_nativeDigest; - QString m_hashString; + struct Data : public QSharedData + { + bool valid = false; + UnderlyingType nativeDigest; + QString hashString; + }; + + QSharedDataPointer m_dataPtr {new Data}; }; template