refactor: make piece checksums private (#2130)
* refactor: remove tr_info.pieces
This commit is contained in:
parent
82b3da0a54
commit
d1f8c28fcd
|
@ -290,5 +290,5 @@ static std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece
|
|||
bool tr_ioTestPiece(tr_torrent* tor, tr_piece_index_t piece)
|
||||
{
|
||||
auto const hash = recalculateHash(tor, piece);
|
||||
return hash && *hash == tor->info.pieces[piece];
|
||||
return hash && *hash == tor->pieceHash(piece);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,16 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring> /* strlen() */
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "crypto-utils.h" /* tr_sha1 */
|
||||
#include "error.h"
|
||||
#include "error-types.h"
|
||||
#include "file.h"
|
||||
#include "log.h"
|
||||
#include "metainfo.h"
|
||||
|
@ -467,8 +470,8 @@ static void geturllist(tr_info* inf, tr_variant* meta)
|
|||
static char const* tr_metainfoParseImpl(
|
||||
tr_session const* session,
|
||||
tr_info* inf,
|
||||
bool* hasInfoDict,
|
||||
size_t* infoDictLength,
|
||||
std::vector<tr_sha1_digest_t>* pieces,
|
||||
uint64_t* infoDictLength,
|
||||
tr_variant const* meta_in)
|
||||
{
|
||||
int64_t i = 0;
|
||||
|
@ -482,11 +485,6 @@ static char const* tr_metainfoParseImpl(
|
|||
tr_variant* infoDict = nullptr;
|
||||
bool b = tr_variantDictFindDict(meta, TR_KEY_info, &infoDict);
|
||||
|
||||
if (hasInfoDict != nullptr)
|
||||
{
|
||||
*hasInfoDict = b;
|
||||
}
|
||||
|
||||
if (!b)
|
||||
{
|
||||
/* no info dictionary... is this a magnet link? */
|
||||
|
@ -633,9 +631,10 @@ static char const* tr_metainfoParseImpl(
|
|||
return "pieces";
|
||||
}
|
||||
|
||||
inf->pieceCount = std::size(sv) / SHA_DIGEST_LENGTH;
|
||||
inf->pieces = tr_new0(tr_sha1_digest_t, inf->pieceCount);
|
||||
std::copy_n(std::data(sv), std::size(sv), (uint8_t*)(inf->pieces));
|
||||
auto const n_pieces = std::size(sv) / SHA_DIGEST_LENGTH;
|
||||
inf->pieceCount = n_pieces;
|
||||
pieces->resize(n_pieces);
|
||||
std::copy_n(std::data(sv), std::size(sv), reinterpret_cast<uint8_t*>(std::data(*pieces)));
|
||||
|
||||
auto const* const errstr = parseFiles(
|
||||
inf,
|
||||
|
@ -674,23 +673,19 @@ static char const* tr_metainfoParseImpl(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool tr_metainfoParse(
|
||||
tr_session const* session,
|
||||
tr_variant const* meta_in,
|
||||
tr_info* inf,
|
||||
bool* hasInfoDict,
|
||||
size_t* infoDictLength)
|
||||
std::optional<tr_metainfo_parsed> tr_metainfoParse(tr_session const* session, tr_variant const* meta_in, tr_error** error)
|
||||
{
|
||||
char const* badTag = tr_metainfoParseImpl(session, inf, hasInfoDict, infoDictLength, meta_in);
|
||||
bool const success = badTag == nullptr;
|
||||
auto out = tr_metainfo_parsed{};
|
||||
|
||||
if (badTag != nullptr)
|
||||
char const* bad_tag = tr_metainfoParseImpl(session, &out.info, &out.pieces, &out.info_dict_length, meta_in);
|
||||
if (bad_tag != nullptr)
|
||||
{
|
||||
tr_logAddNamedError(inf->name, _("Invalid metadata entry \"%s\""), badTag);
|
||||
tr_metainfoFree(inf);
|
||||
tr_error_set(error, TR_ERROR_EINVAL, _("Error parsing metainfo: %s"), bad_tag);
|
||||
tr_metainfoFree(&out.info);
|
||||
return {};
|
||||
}
|
||||
|
||||
return success;
|
||||
return std::optional<tr_metainfo_parsed>{ std::move(out) };
|
||||
}
|
||||
|
||||
void tr_metainfoFree(tr_info* inf)
|
||||
|
@ -706,7 +701,6 @@ void tr_metainfoFree(tr_info* inf)
|
|||
}
|
||||
|
||||
tr_free(inf->webseeds);
|
||||
tr_free(inf->pieces);
|
||||
tr_free(inf->files);
|
||||
tr_free(inf->comment);
|
||||
tr_free(inf->creator);
|
||||
|
|
|
@ -12,11 +12,15 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
struct tr_error;
|
||||
struct tr_variant;
|
||||
|
||||
enum tr_metainfo_basename_format
|
||||
|
@ -25,12 +29,32 @@ enum tr_metainfo_basename_format
|
|||
TR_METAINFO_BASENAME_HASH
|
||||
};
|
||||
|
||||
bool tr_metainfoParse(
|
||||
tr_session const* session,
|
||||
tr_variant const* variant,
|
||||
tr_info* setmeInfo,
|
||||
bool* setmeHasInfoDict,
|
||||
size_t* setmeInfoDictLength);
|
||||
struct tr_metainfo_parsed
|
||||
{
|
||||
tr_info info = {};
|
||||
uint64_t info_dict_length = 0;
|
||||
std::vector<tr_sha1_digest_t> pieces;
|
||||
|
||||
tr_metainfo_parsed() = default;
|
||||
|
||||
tr_metainfo_parsed(tr_metainfo_parsed&& that)
|
||||
{
|
||||
std::swap(this->info, that.info);
|
||||
std::swap(this->pieces, that.pieces);
|
||||
std::swap(this->info_dict_length, that.info_dict_length);
|
||||
}
|
||||
|
||||
tr_metainfo_parsed(tr_metainfo_parsed const&) = delete;
|
||||
|
||||
tr_metainfo_parsed& operator=(tr_metainfo_parsed const&) = delete;
|
||||
|
||||
~tr_metainfo_parsed()
|
||||
{
|
||||
tr_metainfoFree(&info);
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<tr_metainfo_parsed> tr_metainfoParse(tr_session const* session, tr_variant const* variant, tr_error** error);
|
||||
|
||||
void tr_metainfoRemoveSaved(tr_session const* session, tr_info const* info);
|
||||
|
||||
|
|
|
@ -296,23 +296,18 @@ void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, in
|
|||
dbgmsg(tor, "Saving completed metadata to \"%s\"", path);
|
||||
tr_variantMergeDicts(tr_variantDictAddDict(&newMetainfo, TR_KEY_info, 0), &infoDict);
|
||||
|
||||
auto hasInfo = bool{};
|
||||
auto info = tr_info{};
|
||||
auto infoDictLength = size_t{};
|
||||
success = tr_metainfoParse(tor->session, &newMetainfo, &info, &hasInfo, &infoDictLength);
|
||||
|
||||
if (success && tr_getBlockSize(info.pieceSize) == 0)
|
||||
auto info = tr_metainfoParse(tor->session, &newMetainfo, nullptr);
|
||||
if (info && tr_getBlockSize(info->info.pieceSize) == 0)
|
||||
{
|
||||
tr_torrentSetLocalError(tor, "%s", _("Magnet torrent's metadata is not usable"));
|
||||
tr_metainfoFree(&info);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
/* keep the new info */
|
||||
tor->info = info;
|
||||
tor->infoDictLength = infoDictLength;
|
||||
std::swap(tor->info, info->info);
|
||||
std::swap(tor->infoDictLength, info->info_dict_length);
|
||||
|
||||
/* save the new .torrent file */
|
||||
tr_variantToFile(&newMetainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
|
||||
|
|
|
@ -978,114 +978,66 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
|||
tr_sessionUnlock(session);
|
||||
}
|
||||
|
||||
static tr_parse_result torrentParseImpl(
|
||||
tr_ctor const* ctor,
|
||||
tr_info* setmeInfo,
|
||||
bool* setmeHasInfo,
|
||||
size_t* dictLength,
|
||||
int* setme_duplicate_id)
|
||||
tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setmeInfo)
|
||||
{
|
||||
tr_session* session = tr_ctorGetSession(ctor);
|
||||
tr_parse_result result = TR_PARSE_OK;
|
||||
|
||||
tr_info tmp;
|
||||
if (setmeInfo == nullptr)
|
||||
{
|
||||
setmeInfo = &tmp;
|
||||
}
|
||||
|
||||
*setmeInfo = {};
|
||||
|
||||
tr_variant const* metainfo = nullptr;
|
||||
if (!tr_ctorGetMetainfo(ctor, &metainfo))
|
||||
{
|
||||
return TR_PARSE_ERR;
|
||||
}
|
||||
|
||||
auto hasInfo = bool{};
|
||||
bool const didParse = tr_metainfoParse(session, metainfo, setmeInfo, &hasInfo, dictLength);
|
||||
bool const doFree = didParse && (setmeInfo == &tmp);
|
||||
|
||||
if (!didParse)
|
||||
auto parsed = tr_metainfoParse(tr_ctorGetSession(ctor), metainfo, nullptr);
|
||||
if (!parsed)
|
||||
{
|
||||
result = TR_PARSE_ERR;
|
||||
return TR_PARSE_ERR;
|
||||
}
|
||||
|
||||
if (didParse && hasInfo && tr_getBlockSize(setmeInfo->pieceSize) == 0)
|
||||
if (setmeInfo != nullptr)
|
||||
{
|
||||
result = TR_PARSE_ERR;
|
||||
std::swap(*setmeInfo, parsed->info);
|
||||
}
|
||||
|
||||
if (didParse && session != nullptr && result == TR_PARSE_OK)
|
||||
{
|
||||
tr_torrent const* const tor = tr_torrentFindFromHash(session, setmeInfo->hash);
|
||||
|
||||
if (tor != nullptr)
|
||||
{
|
||||
result = TR_PARSE_DUPLICATE;
|
||||
|
||||
if (setme_duplicate_id != nullptr)
|
||||
{
|
||||
*setme_duplicate_id = tr_torrentId(tor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doFree)
|
||||
{
|
||||
tr_metainfoFree(setmeInfo);
|
||||
}
|
||||
|
||||
if (setmeHasInfo != nullptr)
|
||||
{
|
||||
*setmeHasInfo = hasInfo;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setmeInfo)
|
||||
{
|
||||
return torrentParseImpl(ctor, setmeInfo, nullptr, nullptr, nullptr);
|
||||
return TR_PARSE_OK;
|
||||
}
|
||||
|
||||
tr_torrent* tr_torrentNew(tr_ctor const* ctor, int* setme_error, int* setme_duplicate_id)
|
||||
{
|
||||
tr_torrent* tor = nullptr;
|
||||
|
||||
TR_ASSERT(ctor != nullptr);
|
||||
TR_ASSERT(tr_isSession(tr_ctorGetSession(ctor)));
|
||||
auto* const session = tr_ctorGetSession(ctor);
|
||||
TR_ASSERT(tr_isSession(session));
|
||||
|
||||
auto tmpInfo = tr_info{};
|
||||
auto hasInfo = bool{};
|
||||
auto len = size_t{};
|
||||
tr_parse_result const r = torrentParseImpl(ctor, &tmpInfo, &hasInfo, &len, setme_duplicate_id);
|
||||
|
||||
if (r == TR_PARSE_OK)
|
||||
tr_variant const* metainfo = nullptr;
|
||||
tr_ctorGetMetainfo(ctor, &metainfo);
|
||||
auto parsed = tr_metainfoParse(session, metainfo, nullptr);
|
||||
if (!parsed)
|
||||
{
|
||||
tor = new tr_torrent{};
|
||||
tor->info = tmpInfo;
|
||||
|
||||
if (hasInfo)
|
||||
if (setme_error != nullptr)
|
||||
{
|
||||
tor->infoDictLength = len;
|
||||
*setme_error = TR_PARSE_ERR;
|
||||
}
|
||||
|
||||
torrentInit(tor, ctor);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
|
||||
tr_torrent const* const dupe = tr_torrentFindFromHash(session, parsed->info.hash);
|
||||
if (dupe != nullptr)
|
||||
{
|
||||
if (r == TR_PARSE_DUPLICATE)
|
||||
if (setme_duplicate_id != nullptr)
|
||||
{
|
||||
tr_metainfoFree(&tmpInfo);
|
||||
*setme_duplicate_id = tr_torrentId(dupe);
|
||||
}
|
||||
|
||||
if (setme_error != nullptr)
|
||||
{
|
||||
*setme_error = r;
|
||||
*setme_error = TR_PARSE_DUPLICATE;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* tor = new tr_torrent{};
|
||||
tor->takeMetainfo(std::move(*parsed));
|
||||
torrentInit(tor, ctor);
|
||||
return tor;
|
||||
}
|
||||
|
||||
|
@ -2644,21 +2596,13 @@ bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_
|
|||
}
|
||||
|
||||
/* try to parse it back again, to make sure it's good */
|
||||
auto tmpInfo = tr_info{};
|
||||
auto hasInfo = bool{};
|
||||
if (tr_metainfoParse(tor->session, &metainfo, &tmpInfo, &hasInfo, &tor->infoDictLength))
|
||||
auto parsed = tr_metainfoParse(tor->session, &metainfo, nullptr);
|
||||
if (parsed)
|
||||
{
|
||||
/* it's good, so keep these new trackers and free the old ones */
|
||||
tr_info swap;
|
||||
swap.trackers = tor->info.trackers;
|
||||
swap.trackerCount = tor->info.trackerCount;
|
||||
tor->info.trackers = tmpInfo.trackers;
|
||||
tor->info.trackerCount = tmpInfo.trackerCount;
|
||||
tmpInfo.trackers = swap.trackers;
|
||||
tmpInfo.trackerCount = swap.trackerCount;
|
||||
std::swap(tor->info.trackers, parsed->info.trackers);
|
||||
std::swap(tor->info.trackerCount, parsed->info.trackerCount);
|
||||
tr_torrentMarkEdited(tor);
|
||||
|
||||
tr_metainfoFree(&tmpInfo);
|
||||
tr_variantToFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
|
||||
}
|
||||
|
||||
|
@ -3771,3 +3715,10 @@ void tr_torrentRenamePath(
|
|||
|
||||
tr_runInEventThread(tor->session, torrentRenamePath, data);
|
||||
}
|
||||
|
||||
void tr_torrent::takeMetainfo(tr_metainfo_parsed&& parsed)
|
||||
{
|
||||
std::swap(this->info, parsed.info);
|
||||
std::swap(this->piece_checksums_, parsed.pieces);
|
||||
std::swap(this->infoDictLength, parsed.info_dict_length);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
class tr_swarm;
|
||||
struct tr_magnet_info;
|
||||
struct tr_metainfo_parsed;
|
||||
struct tr_session;
|
||||
struct tr_torrent;
|
||||
struct tr_torrent_tiers;
|
||||
|
@ -141,7 +142,14 @@ struct tr_torrent
|
|||
int magicNumber;
|
||||
|
||||
std::optional<double> verify_progress;
|
||||
std::vector<tr_sha1_digest_t> piece_checksums;
|
||||
|
||||
tr_sha1_digest_t pieceHash(tr_piece_index_t i) const
|
||||
{
|
||||
TR_ASSERT(i < std::size(this->piece_checksums_));
|
||||
return this->piece_checksums_[i];
|
||||
}
|
||||
|
||||
void takeMetainfo(tr_metainfo_parsed&& parsed);
|
||||
|
||||
tr_stat_errtype error;
|
||||
char errorString[128];
|
||||
|
@ -280,7 +288,7 @@ struct tr_torrent
|
|||
char* incompleteDir;
|
||||
|
||||
/* Length, in bytes, of the "info" dict in the .torrent file. */
|
||||
size_t infoDictLength;
|
||||
uint64_t infoDictLength;
|
||||
|
||||
/* Offset, in bytes, of the beginning of the "info" dict in the .torrent file.
|
||||
*
|
||||
|
@ -395,6 +403,9 @@ struct tr_torrent
|
|||
bool finishedSeedingByIdle;
|
||||
|
||||
tr_labels_t labels;
|
||||
|
||||
private:
|
||||
mutable std::vector<tr_sha1_digest_t> piece_checksums_;
|
||||
};
|
||||
|
||||
/* what piece index is this block in? */
|
||||
|
|
|
@ -1612,7 +1612,6 @@ struct tr_info
|
|||
char* source;
|
||||
|
||||
tr_file* files;
|
||||
tr_sha1_digest_t* pieces;
|
||||
|
||||
/* these trackers are sorted by tier */
|
||||
tr_tracker_info* trackers;
|
||||
|
|
|
@ -96,7 +96,7 @@ static bool verifyTorrent(tr_torrent* tor, bool* stopFlag)
|
|||
if (leftInPiece == 0)
|
||||
{
|
||||
auto hash = tr_sha1_final(sha);
|
||||
auto const hasPiece = hash && *hash == tor->info.pieces[pieceIndex];
|
||||
auto const hasPiece = hash && *hash == tor->pieceHash(pieceIndex);
|
||||
|
||||
if (hasPiece || hadPiece)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <libtransmission/variant.h>
|
||||
|
||||
#include "AddData.h"
|
||||
#include "FileTreeModel.h"
|
||||
#include "FreeSpaceLabel.h"
|
||||
#include "OptionsDialog.h"
|
||||
#include "Prefs.h"
|
||||
|
@ -34,7 +35,6 @@ using ::trqt::variant_helpers::listAdd;
|
|||
OptionsDialog::OptionsDialog(Session& session, Prefs const& prefs, AddData addme, QWidget* parent)
|
||||
: BaseDialog(parent)
|
||||
, add_(std::move(addme))
|
||||
, verify_button_(new QPushButton(tr("&Verify Local Data"), this))
|
||||
, session_(session)
|
||||
, is_local_(session_.isLocal())
|
||||
{
|
||||
|
@ -100,15 +100,11 @@ OptionsDialog::OptionsDialog(Session& session, Prefs const& prefs, AddData addme
|
|||
connect(ui_.destinationEdit, &QLineEdit::editingFinished, this, &OptionsDialog::onDestinationChanged);
|
||||
|
||||
ui_.filesView->setEditable(false);
|
||||
|
||||
ui_.priorityCombo->addItem(tr("High"), TR_PRI_HIGH);
|
||||
ui_.priorityCombo->addItem(tr("Normal"), TR_PRI_NORMAL);
|
||||
ui_.priorityCombo->addItem(tr("Low"), TR_PRI_LOW);
|
||||
ui_.priorityCombo->setCurrentIndex(1); // Normal
|
||||
|
||||
ui_.dialogButtons->addButton(verify_button_, QDialogButtonBox::ActionRole);
|
||||
connect(verify_button_, &QAbstractButton::clicked, this, &OptionsDialog::onVerify);
|
||||
|
||||
ui_.startCheck->setChecked(prefs.getBool(Prefs::START));
|
||||
ui_.trashCheck->setChecked(prefs.getBool(Prefs::TRASH_ORIGINAL));
|
||||
|
||||
|
@ -118,8 +114,6 @@ OptionsDialog::OptionsDialog(Session& session, Prefs const& prefs, AddData addme
|
|||
connect(ui_.filesView, &FileTreeView::priorityChanged, this, &OptionsDialog::onPriorityChanged);
|
||||
connect(ui_.filesView, &FileTreeView::wantedChanged, this, &OptionsDialog::onWantedChanged);
|
||||
|
||||
connect(&verify_timer_, &QTimer::timeout, this, &OptionsDialog::onTimeout);
|
||||
|
||||
connect(&session_, &Session::sessionUpdated, this, &OptionsDialog::onSessionUpdated);
|
||||
|
||||
updateWidgetsLocality();
|
||||
|
@ -149,7 +143,6 @@ void OptionsDialog::clearInfo()
|
|||
void OptionsDialog::reload()
|
||||
{
|
||||
clearInfo();
|
||||
clearVerify();
|
||||
|
||||
tr_ctor* ctor = tr_ctorNew(nullptr);
|
||||
|
||||
|
@ -183,7 +176,6 @@ void OptionsDialog::reload()
|
|||
bool const have_files_to_show = have_info_ && info_.fileCount > 0;
|
||||
|
||||
ui_.filesView->setVisible(have_files_to_show);
|
||||
verify_button_->setEnabled(have_files_to_show);
|
||||
layout()->setSizeConstraint(have_files_to_show ? QLayout::SetDefaultConstraint : QLayout::SetFixedSize);
|
||||
|
||||
if (have_info_)
|
||||
|
@ -205,6 +197,7 @@ void OptionsDialog::reload()
|
|||
}
|
||||
|
||||
ui_.filesView->update(files_);
|
||||
ui_.filesView->hideColumn(FileTreeModel::COL_PROGRESS);
|
||||
}
|
||||
|
||||
void OptionsDialog::updateWidgetsLocality()
|
||||
|
@ -212,11 +205,6 @@ void OptionsDialog::updateWidgetsLocality()
|
|||
ui_.destinationStack->setCurrentWidget(is_local_ ? static_cast<QWidget*>(ui_.destinationButton) : ui_.destinationEdit);
|
||||
ui_.destinationStack->setFixedHeight(ui_.destinationStack->currentWidget()->sizeHint().height());
|
||||
ui_.destinationLabel->setBuddy(ui_.destinationStack->currentWidget());
|
||||
|
||||
// hide the % done when non-local, since we've no way of knowing
|
||||
(ui_.filesView->*(is_local_ ? &QTreeView::showColumn : &QTreeView::hideColumn))(2);
|
||||
|
||||
verify_button_->setVisible(is_local_);
|
||||
}
|
||||
|
||||
void OptionsDialog::onSessionUpdated()
|
||||
|
@ -353,154 +341,3 @@ void OptionsDialog::onDestinationChanged()
|
|||
ui_.freeSpaceLabel->setPath(ui_.destinationEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
**** VERIFY
|
||||
****
|
||||
***/
|
||||
|
||||
void OptionsDialog::clearVerify()
|
||||
{
|
||||
verify_hash_.reset();
|
||||
verify_file_.close();
|
||||
verify_file_pos_ = 0;
|
||||
verify_flags_.clear();
|
||||
verify_file_index_ = 0;
|
||||
verify_piece_index_ = 0;
|
||||
verify_piece_pos_ = 0;
|
||||
verify_timer_.stop();
|
||||
|
||||
for (TorrentFile& f : files_)
|
||||
{
|
||||
f.have = 0;
|
||||
}
|
||||
|
||||
ui_.filesView->update(files_);
|
||||
}
|
||||
|
||||
void OptionsDialog::onVerify()
|
||||
{
|
||||
clearVerify();
|
||||
verify_flags_.assign(info_.pieceCount, false);
|
||||
verify_timer_.setSingleShot(false);
|
||||
verify_timer_.start(0);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
uint64_t getPieceSize(tr_info const* info, tr_piece_index_t piece_index)
|
||||
{
|
||||
if (piece_index != info->pieceCount - 1)
|
||||
{
|
||||
return info->pieceSize;
|
||||
}
|
||||
|
||||
return info->totalSize % info->pieceSize;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void OptionsDialog::onTimeout()
|
||||
{
|
||||
if (files_.empty())
|
||||
{
|
||||
verify_timer_.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
tr_file const* file = &info_.files[verify_file_index_];
|
||||
|
||||
if (verify_file_pos_ == 0 && !verify_file_.isOpen())
|
||||
{
|
||||
QFileInfo const file_info(local_destination_, QString::fromUtf8(file->name));
|
||||
verify_file_.setFileName(file_info.absoluteFilePath());
|
||||
verify_file_.open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
int64_t left_in_piece = getPieceSize(&info_, verify_piece_index_) - verify_piece_pos_;
|
||||
int64_t left_in_file = file->length - verify_file_pos_;
|
||||
int64_t bytes_this_pass = qMin(left_in_file, left_in_piece);
|
||||
bytes_this_pass = qMin(bytes_this_pass, static_cast<int64_t>(sizeof(verify_buf_)));
|
||||
|
||||
if (verify_file_.isOpen() && verify_file_.seek(verify_file_pos_))
|
||||
{
|
||||
int64_t num_read = verify_file_.read(verify_buf_, bytes_this_pass);
|
||||
|
||||
if (num_read == bytes_this_pass)
|
||||
{
|
||||
verify_hash_.addData(verify_buf_, num_read);
|
||||
}
|
||||
}
|
||||
|
||||
left_in_piece -= bytes_this_pass;
|
||||
left_in_file -= bytes_this_pass;
|
||||
verify_piece_pos_ += bytes_this_pass;
|
||||
verify_file_pos_ += bytes_this_pass;
|
||||
|
||||
verify_bins_[verify_file_index_] += bytes_this_pass;
|
||||
|
||||
if (left_in_piece == 0)
|
||||
{
|
||||
QByteArray const result(verify_hash_.result());
|
||||
bool const matches = memcmp(result.constData(), std::data(info_.pieces[verify_piece_index_]), SHA_DIGEST_LENGTH) == 0;
|
||||
verify_flags_[verify_piece_index_] = matches;
|
||||
verify_piece_pos_ = 0;
|
||||
++verify_piece_index_;
|
||||
verify_hash_.reset();
|
||||
|
||||
FileList changed_files;
|
||||
|
||||
if (matches)
|
||||
{
|
||||
for (auto i = verify_bins_.begin(), end = verify_bins_.end(); i != end; ++i)
|
||||
{
|
||||
TorrentFile& f(files_[i.key()]);
|
||||
f.have += i.value();
|
||||
changed_files.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
ui_.filesView->update(changed_files);
|
||||
verify_bins_.clear();
|
||||
}
|
||||
|
||||
if (left_in_file == 0)
|
||||
{
|
||||
verify_file_.close();
|
||||
++verify_file_index_;
|
||||
verify_file_pos_ = 0;
|
||||
}
|
||||
|
||||
bool done = verify_piece_index_ >= info_.pieceCount;
|
||||
|
||||
if (done)
|
||||
{
|
||||
uint64_t have = 0;
|
||||
|
||||
for (TorrentFile const& f : files_)
|
||||
{
|
||||
have += f.have;
|
||||
}
|
||||
|
||||
if (have == 0) // everything failed
|
||||
{
|
||||
// did the user accidentally specify the child directory instead of the parent?
|
||||
QStringList const tokens = QString::fromUtf8(file->name).split(QLatin1Char('/'));
|
||||
|
||||
if (!tokens.empty() && local_destination_.dirName() == tokens.at(0))
|
||||
{
|
||||
// move up one directory and try again
|
||||
local_destination_.cdUp();
|
||||
onVerify();
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
verify_timer_.stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMap>
|
||||
|
@ -44,8 +43,6 @@ private slots:
|
|||
void onAccepted();
|
||||
void onPriorityChanged(QSet<int> const& file_indices, int);
|
||||
void onWantedChanged(QSet<int> const& file_indices, bool);
|
||||
void onVerify();
|
||||
void onTimeout();
|
||||
|
||||
void onSourceChanged();
|
||||
void onDestinationChanged();
|
||||
|
@ -58,29 +55,17 @@ private:
|
|||
void reload();
|
||||
void updateWidgetsLocality();
|
||||
void clearInfo();
|
||||
void clearVerify();
|
||||
|
||||
AddData add_;
|
||||
FileList files_;
|
||||
QCryptographicHash verify_hash_ = QCryptographicHash(QCryptographicHash::Sha1);
|
||||
|
||||
QDir local_destination_;
|
||||
QFile verify_file_;
|
||||
QPushButton* verify_button_ = {};
|
||||
QTimer edit_timer_;
|
||||
QTimer verify_timer_;
|
||||
std::vector<bool> verify_flags_;
|
||||
std::vector<bool> wanted_;
|
||||
std::vector<int> priorities_;
|
||||
Session& session_;
|
||||
Ui::OptionsDialog ui_ = {};
|
||||
mybins_t verify_bins_;
|
||||
tr_info info_ = {};
|
||||
uint64_t verify_file_pos_ = {};
|
||||
uint32_t verify_piece_index_ = {};
|
||||
uint32_t verify_piece_pos_ = {};
|
||||
int verify_file_index_ = {};
|
||||
char verify_buf_[2048 * 4] = {};
|
||||
bool have_info_ = {};
|
||||
bool is_local_ = {};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue