mirror of
https://github.com/transmission/transmission
synced 2025-03-16 00:39:34 +00:00
refactor: add file-piece-map (#2246)
* refactor: add file-piece-map * refactor: use tr_file_priorities * refactor: use tr_files_wanted
This commit is contained in:
parent
ef154e48ae
commit
35fe175f2a
17 changed files with 785 additions and 402 deletions
|
@ -9,6 +9,8 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
0A6169A70FE5C9A200C66CE6 /* bitfield.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0A6169A50FE5C9A200C66CE6 /* bitfield.cc */; };
|
||||
0A6169A80FE5C9A200C66CE6 /* bitfield.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A6169A60FE5C9A200C66CE6 /* bitfield.h */; };
|
||||
1BB44E07B1B52E28291B4E32 /* file-piece-map.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1BB44E07B1B52E28291B4E30 /* file-piece-map.cc */; };
|
||||
1BB44E07B1B52E28291B4E33 /* file-piece-map.h in Headers */ = {isa = PBXBuildFile; fileRef = 1BB44E07B1B52E28291B4E31 /* file-piece-map.h */; };
|
||||
35F373030C2DA89000DAA8F2 /* FilePriorityCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */; };
|
||||
3C7A11970D0B2EE300B5701F /* getgateway.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C7A11910D0B2EE300B5701F /* getgateway.c */; };
|
||||
3C7A11980D0B2EE300B5701F /* getgateway.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7A11920D0B2EE300B5701F /* getgateway.h */; };
|
||||
|
@ -492,6 +494,8 @@
|
|||
/* Begin PBXFileReference section */
|
||||
0A6169A50FE5C9A200C66CE6 /* bitfield.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = bitfield.cc; sourceTree = "<group>"; };
|
||||
0A6169A60FE5C9A200C66CE6 /* bitfield.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitfield.h; sourceTree = "<group>"; };
|
||||
1BB44E07B1B52E28291B4E30 /* file-piece-map.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = file-piece-map.cc; sourceTree = "<group>"; };
|
||||
1BB44E07B1B52E28291B4E31 /* file-piece-map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = file-piece-map.h; sourceTree = "<group>"; };
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
29B97316FDCFA39411CA2CEA /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
|
||||
|
@ -1396,6 +1400,8 @@
|
|||
4D8017E910BBC073008A4AF2 /* torrent-magnet.h */,
|
||||
0A6169A50FE5C9A200C66CE6 /* bitfield.cc */,
|
||||
0A6169A60FE5C9A200C66CE6 /* bitfield.h */,
|
||||
1BB44E07B1B52E28291B4E30 /* file-piece-map.cc */,
|
||||
1BB44E07B1B52E28291B4E31 /* file-piece-map.h */,
|
||||
C1425B321EE9C5EA001DB85F /* tr-assert.cc */,
|
||||
C1425B331EE9C5EA001DB85F /* tr-assert.h */,
|
||||
A22CFCA60FC24ED80009BD3E /* tr-dht.cc */,
|
||||
|
@ -1883,6 +1889,7 @@
|
|||
A21FBBAB0EDA78C300BC3C51 /* bandwidth.h in Headers */,
|
||||
A22CFCA90FC24ED80009BD3E /* tr-dht.h in Headers */,
|
||||
0A6169A80FE5C9A200C66CE6 /* bitfield.h in Headers */,
|
||||
1BB44E07B1B52E28291B4E33 /* file-piece-map.h in Headers */,
|
||||
A25964A7106D73A800453B31 /* announcer.h in Headers */,
|
||||
ED8A16412735A8AA000D61F9 /* peer-mgr-wishlist.h in Headers */,
|
||||
4D8017EB10BBC073008A4AF2 /* torrent-magnet.h in Headers */,
|
||||
|
@ -2479,6 +2486,7 @@
|
|||
C1033E081A3279B800EF44D8 /* crypto-utils-ccrypto.cc in Sources */,
|
||||
A22CFCA80FC24ED80009BD3E /* tr-dht.cc in Sources */,
|
||||
0A6169A70FE5C9A200C66CE6 /* bitfield.cc in Sources */,
|
||||
1BB44E07B1B52E28291B4E32 /* file-piece-map.cc in Sources */,
|
||||
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
|
||||
4D8017EA10BBC073008A4AF2 /* torrent-magnet.cc in Sources */,
|
||||
4D80185910BBC0B0008A4AF2 /* magnet-metainfo.cc in Sources */,
|
||||
|
|
|
@ -25,6 +25,7 @@ set(PROJECT_FILES
|
|||
crypto.cc
|
||||
error.cc
|
||||
fdlimit.cc
|
||||
file-piece-map.cc
|
||||
file-posix.cc
|
||||
file-win32.cc
|
||||
file.cc
|
||||
|
@ -156,6 +157,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
|
|||
crypto-utils.h
|
||||
crypto.h
|
||||
fdlimit.h
|
||||
file-piece-map.h
|
||||
handshake.h
|
||||
history.h
|
||||
inout.h
|
||||
|
|
|
@ -59,7 +59,7 @@ uint64_t tr_completion::computeSizeWhenDone() const
|
|||
auto size = size_t{ 0 };
|
||||
for (tr_piece_index_t piece = 0; piece < block_info_->n_pieces; ++piece)
|
||||
{
|
||||
if (!tor_->pieceIsDnd(piece))
|
||||
if (tor_->pieceIsWanted(piece))
|
||||
{
|
||||
size += block_info_->pieceSize(piece);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ struct tr_completion
|
|||
{
|
||||
struct torrent_view
|
||||
{
|
||||
virtual bool pieceIsDnd(tr_piece_index_t piece) const = 0;
|
||||
virtual bool pieceIsWanted(tr_piece_index_t piece) const = 0;
|
||||
|
||||
virtual ~torrent_view() = default;
|
||||
};
|
||||
|
|
180
libtransmission/file-piece-map.cc
Normal file
180
libtransmission/file-piece-map.cc
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "block-info.h"
|
||||
#include "file-piece-map.h"
|
||||
|
||||
void tr_file_piece_map::reset(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files)
|
||||
{
|
||||
files_.resize(n_files);
|
||||
files_.shrink_to_fit();
|
||||
|
||||
uint64_t offset = 0;
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
auto const file_size = file_sizes[i];
|
||||
auto const begin_piece = block_info.pieceOf(offset);
|
||||
tr_piece_index_t end_piece = 0;
|
||||
if (file_size != 0)
|
||||
{
|
||||
auto const last_byte = offset + file_size - 1;
|
||||
auto const final_piece = block_info.pieceOf(last_byte);
|
||||
end_piece = final_piece + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
end_piece = begin_piece + 1;
|
||||
}
|
||||
files_[i] = piece_span_t{ begin_piece, end_piece };
|
||||
offset += file_size;
|
||||
}
|
||||
}
|
||||
|
||||
void tr_file_piece_map::reset(tr_info const& info)
|
||||
{
|
||||
tr_file_index_t const n = info.fileCount;
|
||||
auto file_sizes = std::vector<uint64_t>(n);
|
||||
std::transform(info.files, info.files + n, std::begin(file_sizes), [](tr_file const& file) { return file.length; });
|
||||
reset({ info.totalSize, info.pieceSize }, std::data(file_sizes), std::size(file_sizes));
|
||||
}
|
||||
|
||||
tr_file_piece_map::piece_span_t tr_file_piece_map::pieceSpan(tr_file_index_t file) const
|
||||
{
|
||||
return files_[file];
|
||||
}
|
||||
|
||||
tr_file_piece_map::file_span_t tr_file_piece_map::fileSpan(tr_piece_index_t piece) const
|
||||
{
|
||||
struct Compare
|
||||
{
|
||||
int compare(tr_piece_index_t piece, piece_span_t span) const // <=>
|
||||
{
|
||||
if (piece < span.begin)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (piece >= span.end)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator()(tr_piece_index_t piece, piece_span_t span) const // <
|
||||
{
|
||||
return compare(piece, span) < 0;
|
||||
}
|
||||
|
||||
int compare(piece_span_t span, tr_piece_index_t piece) const // <=>
|
||||
{
|
||||
return -compare(piece, span);
|
||||
}
|
||||
|
||||
bool operator()(piece_span_t span, tr_piece_index_t piece) const // <
|
||||
{
|
||||
return compare(span, piece) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
auto const begin = std::begin(files_);
|
||||
auto const pair = std::equal_range(begin, std::end(files_), piece, Compare{});
|
||||
return { tr_piece_index_t(std::distance(begin, pair.first)), tr_piece_index_t(std::distance(begin, pair.second)) };
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
tr_file_priorities::tr_file_priorities(tr_file_piece_map const* fpm)
|
||||
{
|
||||
reset(fpm);
|
||||
}
|
||||
|
||||
void tr_file_priorities::reset(tr_file_piece_map const* fpm)
|
||||
{
|
||||
fpm_ = fpm;
|
||||
|
||||
auto const n = std::size(*fpm_);
|
||||
priorities_.resize(n);
|
||||
priorities_.shrink_to_fit();
|
||||
std::fill_n(std::begin(priorities_), n, TR_PRI_NORMAL);
|
||||
}
|
||||
|
||||
void tr_file_priorities::set(tr_file_index_t file, tr_priority_t priority)
|
||||
{
|
||||
priorities_[file] = priority;
|
||||
}
|
||||
|
||||
void tr_file_priorities::set(tr_file_index_t const* files, size_t n, tr_priority_t priority)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
set(files[i], priority);
|
||||
}
|
||||
}
|
||||
|
||||
tr_priority_t tr_file_priorities::filePriority(tr_file_index_t file) const
|
||||
{
|
||||
return priorities_[file];
|
||||
}
|
||||
|
||||
tr_priority_t tr_file_priorities::piecePriority(tr_piece_index_t piece) const
|
||||
{
|
||||
auto const [begin_idx, end_idx] = fpm_->fileSpan(piece);
|
||||
auto const begin = std::begin(priorities_) + begin_idx;
|
||||
auto const end = std::begin(priorities_) + end_idx;
|
||||
auto const it = std::max_element(begin, end);
|
||||
if (it == end)
|
||||
{
|
||||
return TR_PRI_NORMAL;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void tr_files_wanted::reset(tr_file_piece_map const* fpm)
|
||||
{
|
||||
fpm_ = fpm;
|
||||
wanted_ = tr_bitfield{ std::size(*fpm) };
|
||||
wanted_.setHasAll(); // by default we want all files
|
||||
}
|
||||
|
||||
void tr_files_wanted::set(tr_file_index_t file, bool wanted)
|
||||
{
|
||||
wanted_.set(file, wanted);
|
||||
}
|
||||
|
||||
void tr_files_wanted::set(tr_file_index_t const* files, size_t n, bool wanted)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
set(files[i], wanted);
|
||||
}
|
||||
}
|
||||
|
||||
bool tr_files_wanted::fileWanted(tr_file_index_t file) const
|
||||
{
|
||||
return wanted_.test(file);
|
||||
}
|
||||
|
||||
bool tr_files_wanted::pieceWanted(tr_piece_index_t piece) const
|
||||
{
|
||||
auto const [begin, end] = fpm_->fileSpan(piece);
|
||||
return wanted_.count(begin, end) != 0;
|
||||
}
|
92
libtransmission/file-piece-map.h
Normal file
92
libtransmission/file-piece-map.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __TRANSMISSION__
|
||||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "bitfield.h"
|
||||
|
||||
struct tr_block_info;
|
||||
|
||||
class tr_file_piece_map
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
struct index_span_t
|
||||
{
|
||||
T begin;
|
||||
T end;
|
||||
};
|
||||
using file_span_t = index_span_t<tr_file_index_t>;
|
||||
using piece_span_t = index_span_t<tr_piece_index_t>;
|
||||
|
||||
tr_file_piece_map(tr_info const& info)
|
||||
{
|
||||
reset(info);
|
||||
}
|
||||
tr_file_piece_map(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files)
|
||||
{
|
||||
reset(block_info, file_sizes, n_files);
|
||||
}
|
||||
void reset(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files);
|
||||
void reset(tr_info const& info);
|
||||
|
||||
[[nodiscard]] piece_span_t pieceSpan(tr_file_index_t file) const;
|
||||
[[nodiscard]] file_span_t fileSpan(tr_piece_index_t piece) const;
|
||||
[[nodiscard]] size_t size() const
|
||||
{
|
||||
return std::size(files_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<piece_span_t> files_;
|
||||
};
|
||||
|
||||
class tr_file_priorities
|
||||
{
|
||||
public:
|
||||
explicit tr_file_priorities(tr_file_piece_map const* fpm);
|
||||
void reset(tr_file_piece_map const*);
|
||||
void set(tr_file_index_t file, tr_priority_t priority);
|
||||
void set(tr_file_index_t const* files, size_t n, tr_priority_t priority);
|
||||
|
||||
[[nodiscard]] tr_priority_t filePriority(tr_file_index_t file) const;
|
||||
[[nodiscard]] tr_priority_t piecePriority(tr_piece_index_t piece) const;
|
||||
|
||||
private:
|
||||
tr_file_piece_map const* fpm_;
|
||||
std::vector<tr_priority_t> priorities_;
|
||||
};
|
||||
|
||||
class tr_files_wanted
|
||||
{
|
||||
public:
|
||||
explicit tr_files_wanted(tr_file_piece_map const* fpm)
|
||||
: wanted_(std::size(*fpm))
|
||||
{
|
||||
reset(fpm);
|
||||
}
|
||||
void reset(tr_file_piece_map const* fpm);
|
||||
|
||||
void set(tr_file_index_t file, bool wanted);
|
||||
void set(tr_file_index_t const* files, size_t n, bool wanted);
|
||||
|
||||
[[nodiscard]] bool fileWanted(tr_file_index_t file) const;
|
||||
[[nodiscard]] bool pieceWanted(tr_piece_index_t piece) const;
|
||||
|
||||
private:
|
||||
tr_file_piece_map const* fpm_;
|
||||
tr_bitfield wanted_;
|
||||
};
|
|
@ -53,7 +53,7 @@ static int readOrWriteBytes(
|
|||
bool const doWrite = ioMode >= TR_IO_WRITE;
|
||||
|
||||
TR_ASSERT(fileIndex < tr_torrentFileCount(tor));
|
||||
auto const file = tr_torrentFile(tor, fileIndex);
|
||||
auto const& file = tor->info.files[fileIndex];
|
||||
TR_ASSERT(file.length == 0 || fileOffset < file.length);
|
||||
TR_ASSERT(fileOffset + buflen <= file.length);
|
||||
|
||||
|
@ -91,8 +91,9 @@ static int readOrWriteBytes(
|
|||
{
|
||||
/* open (and maybe create) the file */
|
||||
auto const filename = tr_strvPath(base, subpath);
|
||||
tr_preallocation_mode const prealloc = (!file.wanted || !doWrite) ? TR_PREALLOCATE_NONE :
|
||||
tor->session->preallocationMode;
|
||||
tr_preallocation_mode const prealloc = (!doWrite || !tor->fileIsWanted(fileIndex)) ?
|
||||
TR_PREALLOCATE_NONE :
|
||||
tor->session->preallocationMode;
|
||||
|
||||
fd = tr_fdFileCheckout(session, tor->uniqueId, fileIndex, filename.c_str(), doWrite, prealloc, file.length);
|
||||
if (fd == TR_BAD_SYS_FILE)
|
||||
|
|
|
@ -577,7 +577,7 @@ std::vector<tr_block_span_t> tr_peerMgrGetNextRequests(tr_torrent* torrent, tr_p
|
|||
|
||||
bool clientCanRequestPiece(tr_piece_index_t piece) const override
|
||||
{
|
||||
return !torrent_->pieceIsDnd(piece) && peer_->have.test(piece);
|
||||
return torrent_->pieceIsWanted(piece) && peer_->have.test(piece);
|
||||
}
|
||||
|
||||
bool isEndgame() const override
|
||||
|
@ -1694,7 +1694,7 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
|
|||
|
||||
for (size_t i = 0; i < n_pieces; ++i)
|
||||
{
|
||||
if (!tor->pieceIsDnd(i) && have.at(i))
|
||||
if (tor->pieceIsWanted(i) && have.at(i))
|
||||
{
|
||||
desired_available += tor->countMissingBytesInPiece(i);
|
||||
}
|
||||
|
@ -2022,7 +2022,7 @@ static void rechokeDownloads(tr_swarm* s)
|
|||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
piece_is_interesting[i] = !tor->pieceIsDnd(i) && !tor->hasPiece(i);
|
||||
piece_is_interesting[i] = tor->pieceIsWanted(i) && !tor->hasPiece(i);
|
||||
}
|
||||
|
||||
/* decide WHICH peers to be interested in (based on their cancel-to-block ratio) */
|
||||
|
@ -2703,7 +2703,7 @@ static void bandwidthPulse(evutil_socket_t /*fd*/, short /*what*/, void* vmgr)
|
|||
if (tor->swarm->needsCompletenessCheck)
|
||||
{
|
||||
tor->swarm->needsCompletenessCheck = false;
|
||||
tr_torrentRecheckCompleteness(tor);
|
||||
tor->recheckCompleteness();
|
||||
}
|
||||
|
||||
/* stop torrents that are ready to stop, but couldn't be stopped
|
||||
|
|
|
@ -156,38 +156,27 @@ static uint64_t loadDND(tr_variant* dict, tr_torrent* tor)
|
|||
|
||||
if (tr_variantDictFindList(dict, TR_KEY_dnd, &list) && tr_variantListSize(list) == n)
|
||||
{
|
||||
tr_file_index_t* dl = tr_new(tr_file_index_t, n);
|
||||
tr_file_index_t* dnd = tr_new(tr_file_index_t, n);
|
||||
tr_file_index_t dlCount = 0;
|
||||
tr_file_index_t dndCount = 0;
|
||||
auto wanted = std::vector<tr_file_index_t>{};
|
||||
auto unwanted = std::vector<tr_file_index_t>{};
|
||||
wanted.reserve(n);
|
||||
unwanted.reserve(n);
|
||||
|
||||
for (tr_file_index_t i = 0; i < n; ++i)
|
||||
{
|
||||
auto tmp = false;
|
||||
if (tr_variantGetBool(tr_variantListChild(list, i), &tmp) && tmp)
|
||||
{
|
||||
dnd[dndCount++] = i;
|
||||
unwanted.push_back(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
dl[dlCount++] = i;
|
||||
wanted.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (dndCount != 0)
|
||||
{
|
||||
tr_torrentInitFileDLs(tor, dnd, dndCount, false);
|
||||
tr_logAddTorDbg(tor, "Resume file found %d files listed as dnd", dndCount);
|
||||
}
|
||||
tor->initFilesWanted(std::data(unwanted), std::size(unwanted), false);
|
||||
tor->initFilesWanted(std::data(wanted), std::size(wanted), true);
|
||||
|
||||
if (dlCount != 0)
|
||||
{
|
||||
tr_torrentInitFileDLs(tor, dl, dlCount, true);
|
||||
tr_logAddTorDbg(tor, "Resume file found %d files marked for download", dlCount);
|
||||
}
|
||||
|
||||
tr_free(dnd);
|
||||
tr_free(dl);
|
||||
ret = TR_FR_DND;
|
||||
}
|
||||
else
|
||||
|
@ -232,7 +221,7 @@ static uint64_t loadFilePriorities(tr_variant* dict, tr_torrent* tor)
|
|||
auto priority = int64_t{};
|
||||
if (tr_variantGetInt(tr_variantListChild(list, i), &priority))
|
||||
{
|
||||
tr_torrentInitFilePriority(tor, i, priority);
|
||||
tor->setFilePriority(i, priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,7 +559,6 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
for (tr_file_index_t fi = 0; fi < n_files; ++fi)
|
||||
{
|
||||
tr_variant* const b = tr_variantListChild(l, fi);
|
||||
tr_file* const f = &inf->files[fi];
|
||||
auto time_checked = time_t{};
|
||||
|
||||
if (tr_variantIsInt(b))
|
||||
|
@ -585,8 +573,8 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
tr_variantGetInt(tr_variantListChild(b, 0), &offset);
|
||||
|
||||
time_checked = tr_time();
|
||||
size_t const pieces = f->priv.lastPiece + 1 - f->priv.firstPiece;
|
||||
for (size_t i = 0; i < pieces; ++i)
|
||||
auto const [begin, end] = tor->piecesInFile(fi);
|
||||
for (size_t i = 0, n = end - begin; i < n; ++i)
|
||||
{
|
||||
int64_t piece_time = 0;
|
||||
tr_variantGetInt(tr_variantListChild(b, i + 1), &piece_time);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <cstdlib> /* strtol */
|
||||
#include <cstring> /* strcmp */
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
@ -983,11 +984,11 @@ static char const* setLabels(tr_torrent* tor, tr_variant* list)
|
|||
|
||||
static char const* setFilePriorities(tr_torrent* tor, int priority, tr_variant* list)
|
||||
{
|
||||
int fileCount = 0;
|
||||
size_t const n = tr_variantListSize(list);
|
||||
char const* errmsg = nullptr;
|
||||
tr_file_index_t* files = tr_new0(tr_file_index_t, tor->info.fileCount);
|
||||
auto files = std::vector<tr_file_index_t>{};
|
||||
files.reserve(tr_torrentFileCount(tor));
|
||||
|
||||
size_t const n = tr_variantListSize(list);
|
||||
if (n != 0)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
|
@ -997,7 +998,7 @@ static char const* setFilePriorities(tr_torrent* tor, int priority, tr_variant*
|
|||
{
|
||||
if (0 <= tmp && tmp < tor->info.fileCount)
|
||||
{
|
||||
files[fileCount++] = tmp;
|
||||
files.push_back(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1006,41 +1007,39 @@ static char const* setFilePriorities(tr_torrent* tor, int priority, tr_variant*
|
|||
}
|
||||
}
|
||||
}
|
||||
else /* if empty set, apply to all */
|
||||
else // if empty set, apply to all
|
||||
{
|
||||
for (tr_file_index_t t = 0; t < tor->info.fileCount; ++t)
|
||||
{
|
||||
files[fileCount++] = t;
|
||||
files.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCount != 0)
|
||||
{
|
||||
tr_torrentSetFilePriorities(tor, files, fileCount, priority);
|
||||
}
|
||||
tor->setFilePriorities(std::data(files), std::size(files), priority);
|
||||
|
||||
tr_free(files);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
static char const* setFileDLs(tr_torrent* tor, bool do_download, tr_variant* list)
|
||||
static char const* setFileDLs(tr_torrent* tor, bool wanted, tr_variant* list)
|
||||
{
|
||||
char const* errmsg = nullptr;
|
||||
|
||||
int fileCount = 0;
|
||||
size_t const n = tr_variantListSize(list);
|
||||
tr_file_index_t* files = tr_new0(tr_file_index_t, tor->info.fileCount);
|
||||
auto const n_files = tr_torrentFileCount(tor);
|
||||
size_t const n_items = tr_variantListSize(list);
|
||||
|
||||
if (n != 0) /* if argument list, process them */
|
||||
auto files = std::vector<tr_file_index_t>{};
|
||||
files.reserve(n_files);
|
||||
|
||||
if (n_items != 0) // if argument list, process them
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
for (size_t i = 0; i < n_items; ++i)
|
||||
{
|
||||
auto tmp = int64_t{};
|
||||
if (tr_variantGetInt(tr_variantListChild(list, i), &tmp))
|
||||
{
|
||||
if (0 <= tmp && tmp < tor->info.fileCount)
|
||||
{
|
||||
files[fileCount++] = tmp;
|
||||
files.push_back(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1049,20 +1048,14 @@ static char const* setFileDLs(tr_torrent* tor, bool do_download, tr_variant* lis
|
|||
}
|
||||
}
|
||||
}
|
||||
else /* if empty set, apply to all */
|
||||
else // if empty set, apply to all
|
||||
{
|
||||
for (tr_file_index_t t = 0; t < tor->info.fileCount; ++t)
|
||||
{
|
||||
files[fileCount++] = t;
|
||||
}
|
||||
files.resize(n_files);
|
||||
std::iota(std::begin(files), std::end(files), 0);
|
||||
}
|
||||
|
||||
if (fileCount != 0)
|
||||
{
|
||||
tr_torrentSetFileDLs(tor, files, fileCount, do_download);
|
||||
}
|
||||
tor->setFilesWanted(std::data(files), std::size(files), wanted);
|
||||
|
||||
tr_free(files);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ struct tr_ctor
|
|||
|
||||
std::string incomplete_dir;
|
||||
|
||||
std::vector<tr_file_index_t> want;
|
||||
std::vector<tr_file_index_t> not_want;
|
||||
std::vector<tr_file_index_t> wanted;
|
||||
std::vector<tr_file_index_t> unwanted;
|
||||
std::vector<tr_file_index_t> low;
|
||||
std::vector<tr_file_index_t> normal;
|
||||
std::vector<tr_file_index_t> high;
|
||||
|
@ -188,32 +188,21 @@ void tr_ctorSetFilePriorities(tr_ctor* ctor, tr_file_index_t const* files, tr_fi
|
|||
|
||||
void tr_ctorInitTorrentPriorities(tr_ctor const* ctor, tr_torrent* tor)
|
||||
{
|
||||
for (auto file_index : ctor->low)
|
||||
{
|
||||
tr_torrentInitFilePriority(tor, file_index, TR_PRI_LOW);
|
||||
}
|
||||
|
||||
for (auto file_index : ctor->normal)
|
||||
{
|
||||
tr_torrentInitFilePriority(tor, file_index, TR_PRI_NORMAL);
|
||||
}
|
||||
|
||||
for (auto file_index : ctor->high)
|
||||
{
|
||||
tr_torrentInitFilePriority(tor, file_index, TR_PRI_HIGH);
|
||||
}
|
||||
tor->setFilePriorities(std::data(ctor->low), std::size(ctor->low), TR_PRI_LOW);
|
||||
tor->setFilePriorities(std::data(ctor->normal), std::size(ctor->normal), TR_PRI_NORMAL);
|
||||
tor->setFilePriorities(std::data(ctor->high), std::size(ctor->high), TR_PRI_HIGH);
|
||||
}
|
||||
|
||||
void tr_ctorSetFilesWanted(tr_ctor* ctor, tr_file_index_t const* files, tr_file_index_t fileCount, bool wanted)
|
||||
{
|
||||
auto& indices = wanted ? ctor->want : ctor->not_want;
|
||||
auto& indices = wanted ? ctor->wanted : ctor->unwanted;
|
||||
indices.assign(files, files + fileCount);
|
||||
}
|
||||
|
||||
void tr_ctorInitTorrentWanted(tr_ctor const* ctor, tr_torrent* tor)
|
||||
{
|
||||
tr_torrentInitFileDLs(tor, std::data(ctor->not_want), std::size(ctor->not_want), false);
|
||||
tr_torrentInitFileDLs(tor, std::data(ctor->want), std::size(ctor->want), true);
|
||||
tor->initFilesWanted(std::data(ctor->unwanted), std::size(ctor->unwanted), false);
|
||||
tor->initFilesWanted(std::data(ctor->wanted), std::size(ctor->wanted), true);
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
|
@ -579,61 +579,6 @@ static void onTrackerResponse(tr_torrent* tor, tr_tracker_event const* event, vo
|
|||
****
|
||||
***/
|
||||
|
||||
static constexpr void initFilePieces(tr_torrent* tor, tr_file_index_t fileIndex)
|
||||
{
|
||||
TR_ASSERT(tor != nullptr);
|
||||
TR_ASSERT(fileIndex < tor->info.fileCount);
|
||||
|
||||
tr_file& file = tor->info.files[fileIndex];
|
||||
uint64_t first_byte = file.priv.offset;
|
||||
uint64_t last_byte = first_byte + (file.length != 0 ? file.length - 1 : 0);
|
||||
|
||||
file.priv.firstPiece = tor->pieceOf(first_byte);
|
||||
file.priv.lastPiece = tor->pieceOf(last_byte);
|
||||
}
|
||||
|
||||
static constexpr bool pieceHasFile(tr_piece_index_t piece, tr_file const* file)
|
||||
{
|
||||
return file->priv.firstPiece <= piece && piece <= file->priv.lastPiece;
|
||||
}
|
||||
|
||||
static tr_priority_t calculatePiecePriority(tr_info const& info, tr_piece_index_t piece, tr_file_index_t file_hint)
|
||||
{
|
||||
// safeguard against a bad arg
|
||||
file_hint = std::min(file_hint, info.fileCount - 1);
|
||||
|
||||
// find the first file with data in this piece
|
||||
tr_file_index_t first = file_hint;
|
||||
while (first > 0 && pieceHasFile(piece, &info.files[first - 1]))
|
||||
{
|
||||
--first;
|
||||
}
|
||||
|
||||
// the priority is the max of all the file priorities in the piece
|
||||
tr_priority_t priority = TR_PRI_LOW;
|
||||
for (tr_file_index_t i = first; i < info.fileCount; ++i)
|
||||
{
|
||||
tr_file const* file = &info.files[i];
|
||||
|
||||
if (!pieceHasFile(piece, file))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
priority = std::max(priority, file->priv.priority);
|
||||
|
||||
/* When dealing with multimedia files, getting the first and
|
||||
last pieces can sometimes allow you to preview it a bit
|
||||
before it's fully downloaded... */
|
||||
if ((file->priv.priority >= TR_PRI_NORMAL) && (file->priv.firstPiece == piece || file->priv.lastPiece == piece))
|
||||
{
|
||||
priority = TR_PRI_HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
static void tr_torrentInitFilePieces(tr_torrent* tor)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
|
@ -644,44 +589,9 @@ static void tr_torrentInitFilePieces(tr_torrent* tor)
|
|||
{
|
||||
inf->files[f].priv.offset = offset;
|
||||
offset += inf->files[f].length;
|
||||
initFilePieces(tor, f);
|
||||
}
|
||||
}
|
||||
|
||||
static void tr_torrentInitPiecePriorities(tr_torrent* tor)
|
||||
{
|
||||
tor->piece_priorities_.clear();
|
||||
|
||||
// throw away file prorities once we're done downloading,
|
||||
// they just waste time & space
|
||||
if (tr_torrentIsSeed(tor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* build the array of first-file hints to give calculatePiecePriority */
|
||||
tr_info* inf = &tor->info;
|
||||
tr_file_index_t* firstFiles = tr_new(tr_file_index_t, inf->pieceCount);
|
||||
tr_file_index_t f = 0;
|
||||
|
||||
for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
|
||||
{
|
||||
while (inf->files[f].priv.lastPiece < p)
|
||||
{
|
||||
++f;
|
||||
}
|
||||
|
||||
firstFiles[p] = f;
|
||||
}
|
||||
|
||||
for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
|
||||
{
|
||||
tor->setPiecePriority(p, calculatePiecePriority(*inf, p, firstFiles[p]));
|
||||
}
|
||||
|
||||
tr_free(firstFiles);
|
||||
}
|
||||
|
||||
static void torrentStart(tr_torrent* tor, bool bypass_queue);
|
||||
|
||||
static void tr_torrentFireMetadataCompleted(tr_torrent* tor);
|
||||
|
@ -752,6 +662,10 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
|||
|
||||
static int nextUniqueId = 1;
|
||||
|
||||
tor->fpm_.reset(tor->info);
|
||||
tor->file_priorities_.reset(&tor->fpm_);
|
||||
tor->files_wanted_.reset(&tor->fpm_);
|
||||
|
||||
tor->session = session;
|
||||
tor->uniqueId = nextUniqueId++;
|
||||
tor->queuePosition = tr_sessionCountTorrents(session);
|
||||
|
@ -814,7 +728,6 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
|||
|
||||
tr_ctorInitTorrentPriorities(ctor, tor);
|
||||
tr_ctorInitTorrentWanted(ctor, tor);
|
||||
tr_torrentInitPiecePriorities(tor);
|
||||
|
||||
refreshCurrentDir(tor);
|
||||
|
||||
|
@ -1315,8 +1228,8 @@ tr_file_view tr_torrentFile(tr_torrent const* torrent, tr_file_index_t i)
|
|||
|
||||
auto const& file = torrent->info.files[i];
|
||||
auto const* const name = file.name;
|
||||
auto const priority = file.priv.priority;
|
||||
auto const wanted = !file.priv.dnd;
|
||||
auto const priority = torrent->file_priorities_.filePriority(i);
|
||||
auto const wanted = torrent->files_wanted_.fileWanted(i);
|
||||
auto const length = file.length;
|
||||
|
||||
if (torrent->completeness == TR_SEED || length == 0)
|
||||
|
@ -1461,7 +1374,7 @@ static void torrentStartImpl(void* vtor)
|
|||
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
tr_torrentRecheckCompleteness(tor);
|
||||
tor->recheckCompleteness();
|
||||
torrentSetQueued(tor, false);
|
||||
|
||||
time_t const now = tr_time();
|
||||
|
@ -1603,7 +1516,7 @@ static void onVerifyDoneThreadFunc(void* vdata)
|
|||
{
|
||||
if (!data->aborted)
|
||||
{
|
||||
tr_torrentRecheckCompleteness(tor);
|
||||
tor->recheckCompleteness();
|
||||
}
|
||||
|
||||
if (data->callback_func != nullptr)
|
||||
|
@ -1973,65 +1886,65 @@ static void torrentCallScript(tr_torrent const* tor, char const* script)
|
|||
tr_free(torrent_dir);
|
||||
}
|
||||
|
||||
void tr_torrentRecheckCompleteness(tr_torrent* tor)
|
||||
void tr_torrent::recheckCompleteness()
|
||||
{
|
||||
auto const lock = tor->unique_lock();
|
||||
auto const lock = unique_lock();
|
||||
|
||||
auto const completeness = tor->completion.status();
|
||||
auto const new_completeness = completion.status();
|
||||
|
||||
if (completeness != tor->completeness)
|
||||
if (new_completeness != completeness)
|
||||
{
|
||||
bool const recentChange = tor->downloadedCur != 0;
|
||||
bool const wasLeeching = !tr_torrentIsSeed(tor);
|
||||
bool const wasRunning = tor->isRunning;
|
||||
bool const recentChange = downloadedCur != 0;
|
||||
bool const wasLeeching = !tr_torrentIsSeed(this);
|
||||
bool const wasRunning = isRunning;
|
||||
|
||||
if (recentChange)
|
||||
{
|
||||
tr_logAddTorInfo(
|
||||
tor,
|
||||
this,
|
||||
_("State changed from \"%1$s\" to \"%2$s\""),
|
||||
getCompletionString(tor->completeness),
|
||||
getCompletionString(this->completeness),
|
||||
getCompletionString(completeness));
|
||||
}
|
||||
|
||||
tor->completeness = completeness;
|
||||
tr_fdTorrentClose(tor->session, tor->uniqueId);
|
||||
this->completeness = new_completeness;
|
||||
tr_fdTorrentClose(this->session, this->uniqueId);
|
||||
|
||||
if (tr_torrentIsSeed(tor))
|
||||
if (tr_torrentIsSeed(this))
|
||||
{
|
||||
if (recentChange)
|
||||
{
|
||||
tr_announcerTorrentCompleted(tor);
|
||||
tor->doneDate = tor->anyDate = tr_time();
|
||||
tr_announcerTorrentCompleted(this);
|
||||
this->doneDate = this->anyDate = tr_time();
|
||||
}
|
||||
|
||||
if (wasLeeching && wasRunning)
|
||||
{
|
||||
/* clear interested flag on all peers */
|
||||
tr_peerMgrClearInterest(tor);
|
||||
tr_peerMgrClearInterest(this);
|
||||
}
|
||||
|
||||
if (tor->currentDir == tor->incompleteDir)
|
||||
if (this->currentDir == this->incompleteDir)
|
||||
{
|
||||
tor->setLocation(tor->downloadDir, true, nullptr, nullptr);
|
||||
this->setLocation(this->downloadDir, true, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
fireCompletenessChange(tor, completeness, wasRunning);
|
||||
fireCompletenessChange(this, completeness, wasRunning);
|
||||
|
||||
if (tr_torrentIsSeed(tor) && wasLeeching && wasRunning)
|
||||
if (tr_torrentIsSeed(this) && wasLeeching && wasRunning)
|
||||
{
|
||||
/* if completeness was TR_LEECH, the seed limit check
|
||||
will have been skipped in bandwidthPulse */
|
||||
tr_torrentCheckSeedLimit(tor);
|
||||
tr_torrentCheckSeedLimit(this);
|
||||
}
|
||||
|
||||
tr_torrentSetDirty(tor);
|
||||
this->setDirty();
|
||||
|
||||
if (tr_torrentIsSeed(tor))
|
||||
if (tr_torrentIsSeed(this))
|
||||
{
|
||||
tr_torrentSave(tor);
|
||||
callScriptIfEnabled(tor, TR_SCRIPT_ON_TORRENT_DONE);
|
||||
tr_torrentSave(this);
|
||||
callScriptIfEnabled(this, TR_SCRIPT_ON_TORRENT_DONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2058,151 +1971,15 @@ void tr_torrentSetMetadataCallback(tr_torrent* tor, tr_torrent_metadata_func fun
|
|||
tor->metadata_func_user_data = user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
*** File priorities
|
||||
**/
|
||||
|
||||
void tr_torrentInitFilePriority(tr_torrent* tor, tr_file_index_t fileIndex, tr_priority_t priority)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(fileIndex < tor->info.fileCount);
|
||||
TR_ASSERT(tr_isPriority(priority));
|
||||
|
||||
auto& info = tor->info;
|
||||
tr_file* file = &info.files[fileIndex];
|
||||
|
||||
file->priv.priority = priority;
|
||||
|
||||
for (tr_piece_index_t i = file->priv.firstPiece; i <= file->priv.lastPiece; ++i)
|
||||
{
|
||||
tor->setPiecePriority(i, calculatePiecePriority(info, i, fileIndex));
|
||||
}
|
||||
}
|
||||
|
||||
void tr_torrentSetFilePriorities(
|
||||
tr_torrent* tor,
|
||||
tr_file_index_t const* files,
|
||||
tr_file_index_t fileCount,
|
||||
tr_priority_t priority)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
auto const lock = tor->unique_lock();
|
||||
|
||||
for (tr_file_index_t i = 0; i < fileCount; ++i)
|
||||
{
|
||||
if (files[i] < tor->info.fileCount)
|
||||
{
|
||||
tr_torrentInitFilePriority(tor, files[i], priority);
|
||||
}
|
||||
}
|
||||
|
||||
tr_torrentSetDirty(tor);
|
||||
}
|
||||
|
||||
tr_priority_t* tr_torrentGetFilePriorities(tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
tr_priority_t* p = tr_new0(tr_priority_t, tor->info.fileCount);
|
||||
|
||||
for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
|
||||
{
|
||||
p[i] = tor->info.files[i].priv.priority;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
*** File DND
|
||||
**/
|
||||
|
||||
static void setFileDND(tr_torrent* tor, tr_file_index_t fileIndex, bool doDownload)
|
||||
{
|
||||
bool const dnd = !doDownload;
|
||||
tr_file* file = &tor->info.files[fileIndex];
|
||||
|
||||
file->priv.dnd = dnd;
|
||||
auto const firstPiece = file->priv.firstPiece;
|
||||
auto const lastPiece = file->priv.lastPiece;
|
||||
|
||||
/* can't set the first piece to DND unless
|
||||
every file using that piece is DND */
|
||||
auto firstPieceDND = dnd;
|
||||
|
||||
if (fileIndex > 0)
|
||||
{
|
||||
for (tr_file_index_t i = fileIndex - 1; firstPieceDND; --i)
|
||||
{
|
||||
if (tor->info.files[i].priv.lastPiece != firstPiece)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
firstPieceDND = tor->info.files[i].priv.dnd;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* can't set the last piece to DND unless
|
||||
every file using that piece is DND */
|
||||
auto lastPieceDND = dnd;
|
||||
|
||||
for (tr_file_index_t i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
|
||||
{
|
||||
if (tor->info.files[i].priv.firstPiece != lastPiece)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
lastPieceDND = tor->info.files[i].priv.dnd;
|
||||
}
|
||||
|
||||
// update dnd_pieces_
|
||||
|
||||
if (firstPiece == lastPiece)
|
||||
{
|
||||
tor->dnd_pieces_.set(firstPiece, firstPieceDND && lastPieceDND);
|
||||
}
|
||||
else
|
||||
{
|
||||
tor->dnd_pieces_.set(firstPiece, firstPieceDND);
|
||||
tor->dnd_pieces_.set(lastPiece, lastPieceDND);
|
||||
for (tr_piece_index_t pp = firstPiece + 1; pp < lastPiece; ++pp)
|
||||
{
|
||||
tor->dnd_pieces_.set(pp, dnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_torrentInitFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
|
||||
void tr_torrentSetFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t n_files, bool wanted)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
auto const lock = tor->unique_lock();
|
||||
|
||||
for (tr_file_index_t i = 0; i < fileCount; ++i)
|
||||
{
|
||||
if (files[i] < tor->info.fileCount)
|
||||
{
|
||||
setFileDND(tor, files[i], doDownload);
|
||||
}
|
||||
}
|
||||
|
||||
tor->completion.invalidateSizeWhenDone();
|
||||
}
|
||||
|
||||
void tr_torrentSetFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
auto const lock = tor->unique_lock();
|
||||
|
||||
tr_torrentInitFileDLs(tor, files, fileCount, doDownload);
|
||||
tr_torrentSetDirty(tor);
|
||||
tr_torrentRecheckCompleteness(tor);
|
||||
tor->setFilesWanted(files, n_files, wanted);
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -2528,16 +2305,18 @@ uint64_t tr_torrentGetBytesLeftToAllocate(tr_torrent const* tor)
|
|||
|
||||
uint64_t bytesLeft = 0;
|
||||
|
||||
for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
|
||||
for (tr_file_index_t i = 0, n = tr_torrentFileCount(tor); i < n; ++i)
|
||||
{
|
||||
if (!tor->info.files[i].priv.dnd)
|
||||
auto const file = tr_torrentFile(tor, i);
|
||||
|
||||
if (file.wanted)
|
||||
{
|
||||
tr_sys_path_info info;
|
||||
uint64_t const length = tor->info.files[i].length;
|
||||
uint64_t const length = file.length;
|
||||
char* path = tr_torrentFindFile(tor, i);
|
||||
|
||||
bytesLeft += length;
|
||||
|
||||
tr_sys_path_info info;
|
||||
if (path != nullptr && tr_sys_path_get_info(path, 0, &info, nullptr) && info.type == TR_SYS_PATH_IS_FILE &&
|
||||
info.size <= length)
|
||||
{
|
||||
|
@ -3008,15 +2787,13 @@ static void tr_torrentPieceCompleted(tr_torrent* tor, tr_piece_index_t pieceInde
|
|||
{
|
||||
tr_peerMgrPieceCompleted(tor, pieceIndex);
|
||||
|
||||
/* if this piece completes any file, invoke the fileCompleted func for it */
|
||||
for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
|
||||
// if this piece completes any file, invoke the fileCompleted func for it
|
||||
auto const [begin, end] = tor->fpm_.fileSpan(pieceIndex);
|
||||
for (tr_file_index_t file = begin; file < end; ++file)
|
||||
{
|
||||
tr_file const* file = &tor->info.files[i];
|
||||
|
||||
if ((file->priv.firstPiece <= pieceIndex) && (pieceIndex <= file->priv.lastPiece) &&
|
||||
tor->completion.hasBlocks(tr_torGetFileBlockSpan(tor, i)))
|
||||
if (tor->completion.hasBlocks(tr_torGetFileBlockSpan(tor, file)))
|
||||
{
|
||||
tr_torrentFileCompleted(tor, i);
|
||||
tr_torrentFileCompleted(tor, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3567,3 +3344,12 @@ void tr_torrent::swapMetainfo(tr_metainfo_parsed& parsed)
|
|||
std::swap(this->piece_checksums_, parsed.pieces);
|
||||
std::swap(this->infoDictLength, parsed.info_dict_length);
|
||||
}
|
||||
|
||||
void tr_torrentSetFilePriorities(
|
||||
tr_torrent* tor,
|
||||
tr_file_index_t const* files,
|
||||
tr_file_index_t fileCount,
|
||||
tr_priority_t priority)
|
||||
{
|
||||
tor->setFilePriorities(files, fileCount, priority);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
|
@ -26,6 +25,7 @@
|
|||
#include "block-info.h"
|
||||
#include "completion.h"
|
||||
#include "file.h"
|
||||
#include "file-piece-map.h"
|
||||
#include "quark.h"
|
||||
#include "session.h"
|
||||
#include "tr-assert.h"
|
||||
|
@ -56,15 +56,10 @@ void tr_ctorInitTorrentWanted(tr_ctor const* ctor, tr_torrent* tor);
|
|||
***
|
||||
**/
|
||||
|
||||
/* just like tr_torrentSetFileDLs but doesn't trigger a fastresume save */
|
||||
void tr_torrentInitFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool do_download);
|
||||
|
||||
using tr_labels_t = std::unordered_set<std::string>;
|
||||
|
||||
void tr_torrentSetLabels(tr_torrent* tor, tr_labels_t&& labels);
|
||||
|
||||
void tr_torrentRecheckCompleteness(tr_torrent*);
|
||||
|
||||
void tr_torrentChangeMyPort(tr_torrent* session);
|
||||
|
||||
tr_sha1_digest_t tr_torrentInfoHash(tr_torrent const* torrent);
|
||||
|
@ -90,8 +85,6 @@ void tr_torrentGetBlockLocation(
|
|||
|
||||
tr_block_span_t tr_torGetFileBlockSpan(tr_torrent const* tor, tr_file_index_t const file);
|
||||
|
||||
void tr_torrentInitFilePriority(tr_torrent* tor, tr_file_index_t fileIndex, tr_priority_t priority);
|
||||
|
||||
void tr_torrentCheckSeedLimit(tr_torrent* tor);
|
||||
|
||||
/** save a torrent's .resume file if it's changed since the last time it was saved */
|
||||
|
@ -238,41 +231,54 @@ public:
|
|||
completion.setHasPiece(piece, has);
|
||||
}
|
||||
|
||||
bool pieceIsDnd(tr_piece_index_t piece) const final
|
||||
/// FILE <-> PIECE
|
||||
|
||||
auto piecesInFile(tr_file_index_t file) const
|
||||
{
|
||||
return dnd_pieces_.test(piece);
|
||||
return fpm_.pieceSpan(file);
|
||||
}
|
||||
|
||||
/// WANTED
|
||||
|
||||
bool pieceIsWanted(tr_piece_index_t piece) const final
|
||||
{
|
||||
return files_wanted_.pieceWanted(piece);
|
||||
}
|
||||
|
||||
bool fileIsWanted(tr_file_index_t file) const
|
||||
{
|
||||
return files_wanted_.fileWanted(file);
|
||||
}
|
||||
|
||||
void initFilesWanted(tr_file_index_t const* files, size_t n_files, bool wanted)
|
||||
{
|
||||
setFilesWanted(files, n_files, wanted, /*is_bootstrapping*/ true);
|
||||
}
|
||||
|
||||
void setFilesWanted(tr_file_index_t const* files, size_t n_files, bool wanted)
|
||||
{
|
||||
setFilesWanted(files, n_files, wanted, /*is_bootstrapping*/ false);
|
||||
}
|
||||
|
||||
void recheckCompleteness(); // TODO(ckerr): should be private
|
||||
|
||||
/// PRIORITIES
|
||||
|
||||
void setPiecePriority(tr_piece_index_t piece, tr_priority_t priority)
|
||||
{
|
||||
// since 'TR_PRI_NORMAL' is by far the most common, save some
|
||||
// space by treating anything not in the map as normal
|
||||
if (priority == TR_PRI_NORMAL)
|
||||
{
|
||||
piece_priorities_.erase(piece);
|
||||
|
||||
if (std::empty(piece_priorities_))
|
||||
{
|
||||
// ensure we release piece_priorities_' internal memory
|
||||
piece_priorities_ = decltype(piece_priorities_){};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
piece_priorities_[piece] = priority;
|
||||
}
|
||||
}
|
||||
|
||||
tr_priority_t piecePriority(tr_piece_index_t piece) const
|
||||
{
|
||||
auto const it = piece_priorities_.find(piece);
|
||||
if (it == std::end(piece_priorities_))
|
||||
{
|
||||
return TR_PRI_NORMAL;
|
||||
}
|
||||
return it->second;
|
||||
return file_priorities_.piecePriority(piece);
|
||||
}
|
||||
|
||||
void setFilePriorities(tr_file_index_t const* files, tr_file_index_t fileCount, tr_priority_t priority)
|
||||
{
|
||||
file_priorities_.set(files, fileCount, priority);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
void setFilePriority(tr_file_index_t file, tr_priority_t priority)
|
||||
{
|
||||
file_priorities_.set(file, priority);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
/// CHECKSUMS
|
||||
|
@ -310,8 +316,7 @@ public:
|
|||
// if a file has changed, mark its pieces as unchecked
|
||||
if (mtime == 0 || mtime != mtimes[i])
|
||||
{
|
||||
auto const begin = info.files[i].priv.firstPiece;
|
||||
auto const end = info.files[i].priv.lastPiece + 1;
|
||||
auto const [begin, end] = piecesInFile(i);
|
||||
checked_pieces_.unsetSpan(begin, end);
|
||||
}
|
||||
}
|
||||
|
@ -360,8 +365,6 @@ public:
|
|||
|
||||
std::optional<double> verify_progress;
|
||||
|
||||
std::unordered_map<tr_piece_index_t, tr_priority_t> piece_priorities_;
|
||||
|
||||
tr_stat_errtype error = TR_STAT_OK;
|
||||
char errorString[128] = {};
|
||||
tr_quark error_announce_url = TR_KEY_NONE;
|
||||
|
@ -491,7 +494,25 @@ public:
|
|||
|
||||
static auto constexpr MagicNumber = int{ 95549 };
|
||||
|
||||
tr_file_piece_map fpm_ = tr_file_piece_map{ info };
|
||||
tr_file_priorities file_priorities_{ &fpm_ };
|
||||
tr_files_wanted files_wanted_{ &fpm_ };
|
||||
|
||||
private:
|
||||
void setFilesWanted(tr_file_index_t const* files, size_t n_files, bool wanted, bool is_bootstrapping)
|
||||
{
|
||||
auto const lock = unique_lock();
|
||||
|
||||
files_wanted_.set(files, n_files, wanted);
|
||||
completion.invalidateSizeWhenDone();
|
||||
|
||||
if (!is_bootstrapping)
|
||||
{
|
||||
setDirty();
|
||||
recheckCompleteness();
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::vector<tr_sha1_digest_t> piece_checksums_;
|
||||
};
|
||||
|
||||
|
|
|
@ -1188,15 +1188,6 @@ void tr_torrentSetFilePriorities(
|
|||
tr_file_index_t fileCount,
|
||||
tr_priority_t priority);
|
||||
|
||||
/**
|
||||
* @brief Get this torrent's file priorities.
|
||||
*
|
||||
* @return A malloc()ed array of tor->info.fileCount items,
|
||||
* each holding a TR_PRI_NORMAL, TR_PRI_HIGH, or TR_PRI_LOW.
|
||||
* It's the caller's responsibility to free() this.
|
||||
*/
|
||||
tr_priority_t* tr_torrentGetFilePriorities(tr_torrent const* torrent);
|
||||
|
||||
/** @brief Set a batch of files to be downloaded or not. */
|
||||
void tr_torrentSetFileDLs(tr_torrent* torrent, tr_file_index_t const* files, tr_file_index_t fileCount, bool do_download);
|
||||
|
||||
|
@ -1603,10 +1594,6 @@ struct tr_file_priv
|
|||
{
|
||||
uint64_t offset; // file begins at the torrent's nth byte
|
||||
time_t mtime;
|
||||
tr_piece_index_t firstPiece; // We need pieces [firstPiece...
|
||||
tr_piece_index_t lastPiece; // ...lastPiece] to dl this file
|
||||
int8_t priority; // TR_PRI_HIGH, _NORMAL, or _LOW
|
||||
bool dnd; // "do not download" flag
|
||||
bool is_renamed; // true if we're using a different path from the one in the metainfo; ie, if the user has renamed it */
|
||||
};
|
||||
/** @brief a part of tr_info that represents a single file of the torrent's content */
|
||||
|
|
|
@ -9,6 +9,7 @@ add_executable(libtransmission-test
|
|||
crypto-test.cc
|
||||
error-test.cc
|
||||
file-test.cc
|
||||
file-piece-map-test.cc
|
||||
getopt-test.cc
|
||||
history-test.cc
|
||||
json-test.cc
|
||||
|
|
|
@ -27,9 +27,9 @@ struct TestTorrent : public tr_completion::torrent_view
|
|||
{
|
||||
std::set<tr_piece_index_t> dnd_pieces;
|
||||
|
||||
[[nodiscard]] bool pieceIsDnd(tr_piece_index_t piece) const final
|
||||
[[nodiscard]] bool pieceIsWanted(tr_piece_index_t piece) const final
|
||||
{
|
||||
return dnd_pieces.count(piece) != 0;
|
||||
return dnd_pieces.count(piece) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
335
tests/libtransmission/file-piece-map-test.cc
Normal file
335
tests/libtransmission/file-piece-map-test.cc
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <cstdint>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "block-info.h"
|
||||
#include "file-piece-map.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class FilePieceMapTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static constexpr size_t TotalSize{ 1001 };
|
||||
static constexpr size_t PieceSize{ 100 };
|
||||
tr_block_info const block_info_{ TotalSize, PieceSize };
|
||||
|
||||
static constexpr std::array<uint64_t, 17> FileSizes{
|
||||
500, // [offset 0] begins and ends on a piece boundary
|
||||
0, // [offset 500] zero-sized files
|
||||
0, 0, 0,
|
||||
50, // [offset 500] begins on a piece boundary
|
||||
100, // [offset 550] neither begins nor ends on a piece boundary, spans >1 piece
|
||||
10, // [offset 650] small files all contained in a single piece
|
||||
9, 8, 7, 6,
|
||||
311, // [offset 690] ends end-of-torrent
|
||||
0, // [offset 1001] zero-sized files at the end-of-torrent
|
||||
0, 0, 0,
|
||||
// sum is 1001 == TotalSize
|
||||
};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
EXPECT_EQ(11, block_info_.n_pieces);
|
||||
EXPECT_EQ(PieceSize, block_info_.piece_size);
|
||||
EXPECT_EQ(TotalSize, block_info_.total_size);
|
||||
EXPECT_EQ(TotalSize, std::accumulate(std::begin(FileSizes), std::end(FileSizes), uint64_t{ 0 }));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FilePieceMapTest, pieceSpan)
|
||||
{
|
||||
// Note to reviewers: it's easy to see a nonexistent fencepost error here.
|
||||
// Remember everything is zero-indexed, so the 11 valid pieces are [0..10]
|
||||
// and that last piece #10 has one byte in it. Piece #11 is the 'end' iterator position.
|
||||
auto constexpr ExpectedPieceSpans = std::array<tr_file_piece_map::piece_span_t, 17>{ {
|
||||
{ 0, 5 },
|
||||
{ 5, 6 },
|
||||
{ 5, 6 },
|
||||
{ 5, 6 },
|
||||
{ 5, 6 },
|
||||
{ 5, 6 },
|
||||
{ 5, 7 },
|
||||
{ 6, 7 },
|
||||
{ 6, 7 },
|
||||
{ 6, 7 },
|
||||
{ 6, 7 },
|
||||
{ 6, 7 },
|
||||
{ 6, 11 },
|
||||
{ 10, 11 },
|
||||
{ 10, 11 },
|
||||
{ 10, 11 },
|
||||
{ 10, 11 },
|
||||
} };
|
||||
EXPECT_EQ(std::size(FileSizes), std::size(ExpectedPieceSpans));
|
||||
|
||||
auto const fpm = tr_file_piece_map{ block_info_, std::data(FileSizes), std::size(FileSizes) };
|
||||
tr_file_index_t const n = std::size(fpm);
|
||||
EXPECT_EQ(std::size(FileSizes), n);
|
||||
uint64_t offset = 0;
|
||||
for (tr_file_index_t file = 0; file < n; ++file)
|
||||
{
|
||||
EXPECT_EQ(ExpectedPieceSpans[file].begin, fpm.pieceSpan(file).begin);
|
||||
EXPECT_EQ(ExpectedPieceSpans[file].end, fpm.pieceSpan(file).end);
|
||||
offset += FileSizes[file];
|
||||
}
|
||||
EXPECT_EQ(TotalSize, offset);
|
||||
EXPECT_EQ(block_info_.n_pieces, fpm.pieceSpan(std::size(FileSizes) - 1).end);
|
||||
}
|
||||
|
||||
TEST_F(FilePieceMapTest, priorities)
|
||||
{
|
||||
auto const fpm = tr_file_piece_map{ block_info_, std::data(FileSizes), std::size(FileSizes) };
|
||||
auto file_priorities = tr_file_priorities(&fpm);
|
||||
tr_file_index_t const n_files = std::size(FileSizes);
|
||||
|
||||
// make a helper to compare file & piece priorities
|
||||
auto expected_file_priorities = std::vector<tr_priority_t>(n_files, TR_PRI_NORMAL);
|
||||
auto expected_piece_priorities = std::vector<tr_priority_t>(block_info_.n_pieces, TR_PRI_NORMAL);
|
||||
auto const compare_to_expected = [&, this]()
|
||||
{
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
EXPECT_EQ(int(expected_file_priorities[i]), int(file_priorities.filePriority(i)));
|
||||
}
|
||||
for (tr_piece_index_t i = 0; i < block_info_.n_pieces; ++i)
|
||||
{
|
||||
EXPECT_EQ(int(expected_piece_priorities[i]), int(file_priorities.piecePriority(i)));
|
||||
}
|
||||
};
|
||||
|
||||
// check default priority is normal
|
||||
compare_to_expected();
|
||||
|
||||
// set the first file as high priority.
|
||||
// since this begins and ends on a piece boundary,
|
||||
// this shouldn't affect any other files' pieces
|
||||
auto pri = TR_PRI_HIGH;
|
||||
file_priorities.set(0, pri);
|
||||
expected_file_priorities[0] = pri;
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
expected_piece_priorities[i] = pri;
|
||||
}
|
||||
compare_to_expected();
|
||||
|
||||
// This file shares a piece with another file.
|
||||
// If _either_ is set to high, the piece's priority should be high.
|
||||
// file #5: byte [500..550) piece [5, 6)
|
||||
// file #6: byte [550..650) piece [5, 7)
|
||||
//
|
||||
// first test setting file #5...
|
||||
pri = TR_PRI_HIGH;
|
||||
file_priorities.set(5, pri);
|
||||
expected_file_priorities[5] = pri;
|
||||
expected_piece_priorities[5] = pri;
|
||||
compare_to_expected();
|
||||
// ...and that shared piece should still be the same when both are high...
|
||||
file_priorities.set(6, pri);
|
||||
expected_file_priorities[6] = pri;
|
||||
expected_piece_priorities[5] = pri;
|
||||
expected_piece_priorities[6] = pri;
|
||||
compare_to_expected();
|
||||
// ...and that shared piece should still be the same when only 6 is high...
|
||||
pri = TR_PRI_NORMAL;
|
||||
file_priorities.set(5, pri);
|
||||
expected_file_priorities[5] = pri;
|
||||
compare_to_expected();
|
||||
|
||||
// setup for the next test: set all files to low priority
|
||||
pri = TR_PRI_LOW;
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
file_priorities.set(i, pri);
|
||||
}
|
||||
std::fill(std::begin(expected_file_priorities), std::end(expected_file_priorities), pri);
|
||||
std::fill(std::begin(expected_piece_priorities), std::end(expected_piece_priorities), pri);
|
||||
compare_to_expected();
|
||||
|
||||
// Raise the priority of a small 1-piece file.
|
||||
// Since it's the highest priority in the piece, piecePriority() should return its value.
|
||||
// file #8: byte [650, 659) piece [6, 7)
|
||||
pri = TR_PRI_NORMAL;
|
||||
file_priorities.set(8, pri);
|
||||
expected_file_priorities[8] = pri;
|
||||
expected_piece_priorities[6] = pri;
|
||||
compare_to_expected();
|
||||
// Raise the priority of another small 1-piece file in the same piece.
|
||||
// Since _it_ now has the highest priority in the piece, piecePriority should return _its_ value.
|
||||
// file #9: byte [659, 667) piece [6, 7)
|
||||
pri = TR_PRI_HIGH;
|
||||
file_priorities.set(9, pri);
|
||||
expected_file_priorities[9] = pri;
|
||||
expected_piece_priorities[6] = pri;
|
||||
compare_to_expected();
|
||||
|
||||
// Prep for the next test: set all files to normal priority
|
||||
pri = TR_PRI_NORMAL;
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
file_priorities.set(i, pri);
|
||||
}
|
||||
std::fill(std::begin(expected_file_priorities), std::end(expected_file_priorities), pri);
|
||||
std::fill(std::begin(expected_piece_priorities), std::end(expected_piece_priorities), pri);
|
||||
compare_to_expected();
|
||||
|
||||
// *Sigh* OK what happens to piece priorities if you set the priority
|
||||
// of a zero-byte file. Arguably nothing should happen since you can't
|
||||
// download an empty file. But that would complicate the code for a
|
||||
// pretty stupid use case, and treating 0-sized files the same as any
|
||||
// other does no real harm. Let's KISS.
|
||||
//
|
||||
// Check that even zero-sized files can change a piece's priority
|
||||
// file #1: byte [500, 500) piece [5, 6)
|
||||
pri = TR_PRI_HIGH;
|
||||
file_priorities.set(1, pri);
|
||||
expected_file_priorities[1] = pri;
|
||||
expected_piece_priorities[5] = pri;
|
||||
compare_to_expected();
|
||||
// Check that zero-sized files at the end of a torrent change the last piece's priority.
|
||||
// file #16 byte [1001, 1001) piece [10, 11)
|
||||
file_priorities.set(16, pri);
|
||||
expected_file_priorities[16] = pri;
|
||||
expected_piece_priorities[10] = pri;
|
||||
compare_to_expected();
|
||||
|
||||
// test the batch API
|
||||
auto file_indices = std::vector<tr_file_index_t>(n_files);
|
||||
std::iota(std::begin(file_indices), std::end(file_indices), 0);
|
||||
pri = TR_PRI_HIGH;
|
||||
file_priorities.set(std::data(file_indices), std::size(file_indices), pri);
|
||||
std::fill(std::begin(expected_file_priorities), std::end(expected_file_priorities), pri);
|
||||
std::fill(std::begin(expected_piece_priorities), std::end(expected_piece_priorities), pri);
|
||||
compare_to_expected();
|
||||
pri = TR_PRI_LOW;
|
||||
file_priorities.set(std::data(file_indices), std::size(file_indices), pri);
|
||||
std::fill(std::begin(expected_file_priorities), std::end(expected_file_priorities), pri);
|
||||
std::fill(std::begin(expected_piece_priorities), std::end(expected_piece_priorities), pri);
|
||||
compare_to_expected();
|
||||
}
|
||||
|
||||
TEST_F(FilePieceMapTest, wanted)
|
||||
{
|
||||
auto const fpm = tr_file_piece_map{ block_info_, std::data(FileSizes), std::size(FileSizes) };
|
||||
auto files_wanted = tr_files_wanted(&fpm);
|
||||
tr_file_index_t const n_files = std::size(FileSizes);
|
||||
|
||||
// make a helper to compare file & piece priorities
|
||||
auto expected_files_wanted = tr_bitfield(n_files);
|
||||
auto expected_pieces_wanted = tr_bitfield(block_info_.n_pieces);
|
||||
auto const compare_to_expected = [&, this]()
|
||||
{
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
EXPECT_EQ(int(expected_files_wanted.test(i)), int(files_wanted.fileWanted(i)));
|
||||
}
|
||||
for (tr_piece_index_t i = 0; i < block_info_.n_pieces; ++i)
|
||||
{
|
||||
EXPECT_EQ(int(expected_pieces_wanted.test(i)), int(files_wanted.pieceWanted(i)));
|
||||
}
|
||||
};
|
||||
|
||||
// check everything is wanted by default
|
||||
expected_files_wanted.setHasAll();
|
||||
expected_pieces_wanted.setHasAll();
|
||||
compare_to_expected();
|
||||
|
||||
// set the first file as not wanted.
|
||||
// since this begins and ends on a piece boundary,
|
||||
// this shouldn't affect any other files' pieces
|
||||
bool wanted = false;
|
||||
files_wanted.set(0, wanted);
|
||||
expected_files_wanted.set(0, wanted);
|
||||
expected_pieces_wanted.setSpan(0, 5, wanted);
|
||||
compare_to_expected();
|
||||
|
||||
// now test when a piece has >1 file.
|
||||
// if *any* file in that piece is wanted, then we want the piece too.
|
||||
// file #1: byte [100..100) piece [5, 6) (zero-byte file)
|
||||
// file #2: byte [100..100) piece [5, 6) (zero-byte file)
|
||||
// file #3: byte [100..100) piece [5, 6) (zero-byte file)
|
||||
// file #4: byte [100..100) piece [5, 6) (zero-byte file)
|
||||
// file #5: byte [500..550) piece [5, 6)
|
||||
// file #6: byte [550..650) piece [5, 7)
|
||||
//
|
||||
// first test setting file #5...
|
||||
files_wanted.set(5, false);
|
||||
expected_files_wanted.unset(5);
|
||||
compare_to_expected();
|
||||
// marking all the files in the piece as unwanted
|
||||
// should cause the piece to become unwanted
|
||||
files_wanted.set(1, false);
|
||||
files_wanted.set(2, false);
|
||||
files_wanted.set(3, false);
|
||||
files_wanted.set(4, false);
|
||||
files_wanted.set(5, false);
|
||||
files_wanted.set(6, false);
|
||||
expected_files_wanted.setSpan(1, 7, false);
|
||||
expected_pieces_wanted.unset(5);
|
||||
compare_to_expected();
|
||||
// but as soon as any of them is turned back to wanted,
|
||||
// the piece should pop back.
|
||||
files_wanted.set(6, true);
|
||||
expected_files_wanted.set(6, true);
|
||||
expected_pieces_wanted.set(5);
|
||||
compare_to_expected();
|
||||
files_wanted.set(5, true);
|
||||
files_wanted.set(6, false);
|
||||
expected_files_wanted.set(5);
|
||||
expected_files_wanted.unset(6);
|
||||
compare_to_expected();
|
||||
files_wanted.set(4, true);
|
||||
files_wanted.set(5, false);
|
||||
expected_files_wanted.set(4);
|
||||
expected_files_wanted.unset(5);
|
||||
compare_to_expected();
|
||||
|
||||
// Prep for the next test: set all files to unwanted priority
|
||||
for (tr_file_index_t i = 0; i < n_files; ++i)
|
||||
{
|
||||
files_wanted.set(i, false);
|
||||
}
|
||||
expected_files_wanted.setHasNone();
|
||||
expected_pieces_wanted.setHasNone();
|
||||
compare_to_expected();
|
||||
|
||||
// *Sigh* OK what happens to files_wanted if you say the only
|
||||
// file you want is a zero-byte file? Arguably nothing should happen
|
||||
// since you can't download a zero-byte file. But that would complicate
|
||||
// the coe for a stupid use case, so let's KISS.
|
||||
//
|
||||
// Check that even zero-sized files can change a file's 'wanted' state
|
||||
// file #1: byte [500, 500) piece [5, 6)
|
||||
files_wanted.set(1, true);
|
||||
expected_files_wanted.set(1);
|
||||
expected_pieces_wanted.set(5);
|
||||
compare_to_expected();
|
||||
// Check that zero-sized files at the end of a torrent change the last piece's state.
|
||||
// file #16 byte [1001, 1001) piece [10, 11)
|
||||
files_wanted.set(16, true);
|
||||
expected_files_wanted.set(16);
|
||||
expected_pieces_wanted.set(10);
|
||||
compare_to_expected();
|
||||
|
||||
// test the batch API
|
||||
auto file_indices = std::vector<tr_file_index_t>(n_files);
|
||||
std::iota(std::begin(file_indices), std::end(file_indices), 0);
|
||||
files_wanted.set(std::data(file_indices), std::size(file_indices), true);
|
||||
expected_files_wanted.setHasAll();
|
||||
expected_pieces_wanted.setHasAll();
|
||||
compare_to_expected();
|
||||
files_wanted.set(std::data(file_indices), std::size(file_indices), false);
|
||||
expected_files_wanted.setHasNone();
|
||||
expected_pieces_wanted.setHasNone();
|
||||
compare_to_expected();
|
||||
}
|
Loading…
Add table
Reference in a new issue