perf: replace tr_torrentFindFile2() with tr_torrrent.findFile() (#2839)

This commit is contained in:
Charles Kerr 2022-03-30 13:25:41 -05:00 committed by GitHub
parent 4dd25d8112
commit f4afb76695
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 188 deletions

View File

@ -87,7 +87,7 @@ int readOrWriteBytes(
TR_ASSERT(file_index < tor->fileCount());
int err = 0;
bool const doWrite = io_mode == IoMode::Write;
bool const do_write = io_mode == IoMode::Write;
auto const file_size = tor->fileSize(file_index);
TR_ASSERT(file_size == 0 || file_offset < file_size);
TR_ASSERT(file_offset + buflen <= file_size);
@ -101,35 +101,32 @@ int readOrWriteBytes(
**** Find the fd
***/
auto fd = tr_fdFileGetCached(session, tr_torrentId(tor), file_index, doWrite);
auto fd = tr_fdFileGetCached(session, tr_torrentId(tor), file_index, do_write);
if (fd == TR_BAD_SYS_FILE) /* it's not cached, so open/create it now */
if (fd == TR_BAD_SYS_FILE) // it's not cached, so open/create it now
{
/* see if the file exists... */
char const* base = nullptr;
char* subpath = nullptr;
if (!tr_torrentFindFile2(tor, file_index, &base, &subpath, nullptr))
auto found = tor->findFile(file_index); // see if the file exists...
if (!found)
{
/* we can't read a file that doesn't exist... */
if (!doWrite)
if (!do_write)
{
err = ENOENT;
}
/* figure out where the file should go, so we can create it */
base = tr_torrentGetCurrentDir(tor);
subpath = tr_sessionIsIncompleteFileNamingEnabled(tor->session) ? tr_torrentBuildPartial(tor, file_index) :
tr_strvDup(tor->fileSubpath(file_index));
// We didn't find the file that we want to write to.
// Let's figure out where it goes so that we can create it.
auto const base = tor->currentDir();
auto const suffix = tor->session->isIncompleteFileNamingEnabled ? tr_torrent::PartialFileSuffix : ""sv;
found = { {}, tr_pathbuf{ base, "/"sv, tor->fileSubpath(file_index), suffix }, std::size(base) };
}
if (err == 0)
{
/* open (and maybe create) the file */
auto const filename = tr_strvPath(base, subpath);
auto const prealloc = (!doWrite || !tor->fileIsWanted(file_index)) ? TR_PREALLOCATE_NONE :
tor->session->preallocationMode;
// open (and maybe create) the file
auto const prealloc = (!do_write || !tor->fileIsWanted(file_index)) ? TR_PREALLOCATE_NONE :
tor->session->preallocationMode;
fd = tr_fdFileCheckout(session, tor->uniqueId, file_index, filename.c_str(), doWrite, prealloc, file_size);
fd = tr_fdFileCheckout(session, tor->uniqueId, file_index, found->filename, do_write, prealloc, file_size);
if (fd == TR_BAD_SYS_FILE)
{
err = errno;
@ -137,18 +134,16 @@ int readOrWriteBytes(
tor,
fmt::format(
_("Couldn't get '{path}': {error} ({error_code})"),
fmt::arg("path", filename),
fmt::arg("path", found->filename),
fmt::arg("error", tr_strerror(err)),
fmt::arg("error_code", err)));
}
else if (doWrite)
else if (do_write)
{
/* make a note that we just created a file */
tr_statsFileCreated(tor->session);
}
}
tr_free(subpath);
}
if (err != 0)

View File

@ -601,11 +601,9 @@ void tr_torrent::setMetainfo(tr_torrent_metainfo const& tm)
static bool hasAnyLocalData(tr_torrent const* tor)
{
auto filename = std::string{};
for (tr_file_index_t i = 0, n = tor->fileCount(); i < n; ++i)
{
if (tor->findFile(filename, i))
if (tor->findFile(i))
{
return true;
}
@ -642,18 +640,17 @@ static bool isNewTorrentASeed(tr_torrent* tor)
return false;
}
auto filename_buf = std::string{};
for (tr_file_index_t i = 0, n = tor->fileCount(); i < n; ++i)
{
// it's not a new seed if a file is missing
auto const found = tor->findFile(filename_buf, i);
auto const found = tor->findFile(i);
if (!found)
{
return false;
}
// it's not a new seed if a file is partial
if (tr_strvEndsWith(found->filename, ".part"sv))
if (tr_strvEndsWith(found->filename, tr_torrent::PartialFileSuffix))
{
return false;
}
@ -2154,31 +2151,26 @@ uint64_t tr_torrentGetBytesLeftToAllocate(tr_torrent const* tor)
{
TR_ASSERT(tr_isTorrent(tor));
uint64_t bytesLeft = 0;
uint64_t bytes_left = 0;
for (tr_file_index_t i = 0, n = tor->fileCount(); i < n; ++i)
{
auto const file = tr_torrentFile(tor, i);
if (file.wanted)
if (auto const wanted = tor->files_wanted_.fileWanted(i); !wanted)
{
uint64_t const length = file.length;
char* path = tr_torrentFindFile(tor, i);
continue;
}
bytesLeft += length;
auto const length = tor->fileSize(i);
bytes_left += length;
tr_sys_path_info info;
if (path != nullptr && tr_sys_path_get_info(path, 0, &info) && info.type == TR_SYS_PATH_IS_FILE &&
info.size <= length)
{
bytesLeft -= info.size;
}
tr_free(path);
auto const found = tor->findFile(i);
if (found)
{
bytes_left -= found->size;
}
}
return bytesLeft;
return bytes_left;
}
/****
@ -2276,7 +2268,7 @@ static void deleteLocalData(tr_torrent const* tor, tr_fileFunc func)
if (!tr_sys_path_exists(filename.c_str()))
{
filename += ".part"sv;
filename += tr_torrent::PartialFileSuffix;
if (!tr_sys_path_exists(filename.c_str()))
{
@ -2436,22 +2428,20 @@ static void setLocationImpl(struct LocationData* const data)
{
auto const file_size = tor->fileSize(i);
char const* oldbase = nullptr;
if (char* sub = nullptr; tr_torrentFindFile2(tor, i, &oldbase, &sub, nullptr))
if (auto found = tor->findFile(i); found)
{
auto const oldpath = tr_strvPath(oldbase, sub);
auto const newpath = tr_strvPath(location, sub);
auto const& oldpath = found->filename;
auto const newpath = tr_pathbuf{ location, "/"sv, found->subpath };
tr_logAddTraceTor(tor, fmt::format("Found file #{}: '{}'", i, oldpath));
if (do_move && !tr_sys_path_is_same(oldpath.c_str(), newpath.c_str()))
if (do_move && !tr_sys_path_is_same(oldpath, newpath))
{
tr_error* error = nullptr;
tr_logAddTraceTor(tor, fmt::format("moving '{}' to '{}'", oldpath, newpath));
if (!tr_moveFile(oldpath.c_str(), newpath.c_str(), &error))
if (!tr_moveFile(oldpath, newpath, &error))
{
err = true;
tr_logAddErrorTor(
@ -2465,8 +2455,6 @@ static void setLocationImpl(struct LocationData* const data)
tr_error_free(error);
}
}
tr_free(sub);
}
if (data->setme_progress != nullptr)
@ -2588,17 +2576,15 @@ static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t i)
/* if the torrent's current filename isn't the same as the one in the
* metadata -- for example, if it had the ".part" suffix appended to
* it until now -- then rename it to match the one in the metadata */
char const* base = nullptr;
char* sub = nullptr;
if (tr_torrentFindFile2(tor, i, &base, &sub, nullptr))
if (auto found = tor->findFile(i); found)
{
if (auto const& file_subpath = tor->fileSubpath(i); file_subpath != sub)
if (auto const& file_subpath = tor->fileSubpath(i); file_subpath != found->subpath)
{
auto const oldpath = tr_strvPath(base, sub);
auto const newpath = tr_strvPath(base, file_subpath);
auto const oldpath = tr_pathbuf{ found->base, "/"sv, found->subpath };
auto const newpath = tr_pathbuf{ found->base, "/"sv, file_subpath };
tr_error* error = nullptr;
if (!tr_sys_path_rename(oldpath.c_str(), newpath.c_str(), &error))
if (!tr_sys_path_rename(oldpath, newpath, &error))
{
tr_logAddErrorTor(
tor,
@ -2611,8 +2597,6 @@ static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t i)
tr_error_free(error);
}
}
tr_free(sub);
}
}
@ -2673,8 +2657,9 @@ void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block)
****
***/
std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(std::string& filename, tr_file_index_t i) const
std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(tr_file_index_t i) const
{
auto filename = tr_pathbuf{};
auto const subpath = std::string_view{ this->fileSubpath(i) };
auto file_info = tr_sys_path_info{};
@ -2682,16 +2667,16 @@ std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(std::string& fil
{
auto const base = this->downloadDir();
tr_buildBuf(filename, base, "/"sv, subpath);
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
filename.assign(base, "/"sv, subpath);
if (tr_sys_path_get_info(filename, 0, &file_info))
{
return tr_found_file_t{ file_info, filename, base };
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
}
tr_buildBuf(filename, base, "/"sv, subpath, ".part"sv);
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
filename.assign(filename, base, "/"sv, subpath, PartialFileSuffix);
if (tr_sys_path_get_info(filename, 0, &file_info))
{
return tr_found_file_t{ file_info, filename, base };
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
}
}
@ -2699,63 +2684,33 @@ std::optional<tr_torrent::tr_found_file_t> tr_torrent::findFile(std::string& fil
{
auto const base = this->incompleteDir();
tr_buildBuf(filename, base, "/"sv, subpath);
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
filename.assign(base, "/"sv, subpath);
if (tr_sys_path_get_info(filename, 0, &file_info))
{
return tr_found_file_t{ file_info, filename, base };
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
}
tr_buildBuf(filename, base, "/"sv, subpath, ".part"sv);
if (tr_sys_path_get_info(filename.c_str(), 0, &file_info))
filename.assign(base, "/"sv, subpath, PartialFileSuffix);
if (tr_sys_path_get_info(filename, 0, &file_info))
{
return tr_found_file_t{ file_info, filename, base };
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
}
}
return {};
}
// TODO: clients that call this should call tr_torrent::findFile() instead
bool tr_torrentFindFile2(tr_torrent const* tor, tr_file_index_t fileNum, char const** base, char** subpath, time_t* mtime)
{
auto filename = std::string{};
auto const found = tor->findFile(filename, fileNum);
if (!found)
{
return false;
}
if (base != nullptr)
{
*base = std::data(found->base);
}
if (subpath != nullptr)
{
*subpath = tr_strvDup(found->subpath);
}
if (mtime != nullptr)
{
*mtime = found->last_modified_at;
}
return true;
}
// TODO: clients that call this should call tr_torrent::findFile() instead
char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum)
{
auto filename = std::string{};
auto const found = tor->findFile(filename, fileNum);
return found ? tr_strdup(filename.c_str()) : nullptr;
auto const found = tor->findFile(fileNum);
return found ? tr_strdup(found->filename.c_str()) : nullptr;
}
/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
static void refreshCurrentDir(tr_torrent* tor)
{
tr_interned_string dir;
auto dir = tr_interned_string{};
if (std::empty(tor->incompleteDir()))
{
@ -2767,8 +2722,7 @@ static void refreshCurrentDir(tr_torrent* tor)
}
else
{
auto filename = std::string{};
auto const found = tor->findFile(filename, 0);
auto const found = tor->findFile(0);
dir = found ? tr_interned_string{ found->base } : tor->incompleteDir();
}
@ -2778,11 +2732,6 @@ static void refreshCurrentDir(tr_torrent* tor)
tor->current_dir = dir;
}
char* tr_torrentBuildPartial(tr_torrent const* tor, tr_file_index_t i)
{
return tr_strvDup(tr_strvJoin(tor->fileSubpath(i), ".part"sv));
}
/***
****
***/
@ -2972,14 +2921,15 @@ static int renamePath(tr_torrent* tor, char const* oldpath, char const* newname)
if (!tr_sys_path_exists(src.c_str())) /* check for it as a partial */
{
src += ".part"sv;
src += tr_torrent::PartialFileSuffix;
}
if (tr_sys_path_exists(src.c_str()))
{
auto const parent = tr_sys_path_dirname(src);
auto const tgt = tr_strvEndsWith(src, ".part"sv) ? tr_strvJoin(parent, TR_PATH_DELIMITER_STR, newname, ".part"sv) :
tr_strvPath(parent, newname);
auto const tgt = tr_strvEndsWith(src, tr_torrent::PartialFileSuffix) ?
tr_strvJoin(parent, TR_PATH_DELIMITER_STR, newname, tr_torrent::PartialFileSuffix) :
tr_strvPath(parent, newname);
auto tmp = errno;
bool const tgt_exists = tr_sys_path_exists(tgt.c_str());
@ -3210,10 +3160,9 @@ void tr_torrent::initCheckedPieces(tr_bitfield const& checked, time_t const* mti
auto const n = this->fileCount();
this->file_mtimes_.resize(n);
auto filename = std::string{};
for (size_t i = 0; i < n; ++i)
{
auto const found = this->findFile(filename, i);
auto const found = this->findFile(i);
auto const mtime = found ? found->last_modified_at : 0;
this->file_mtimes_[i] = mtime;

View File

@ -372,20 +372,20 @@ public:
struct tr_found_file_t : public tr_sys_path_info
{
std::string& filename; // /home/foo/Downloads/torrent/01-file-one.txt
tr_pathbuf filename; // /home/foo/Downloads/torrent/01-file-one.txt
std::string_view base; // /home/foo/Downloads
std::string_view subpath; // /torrent/01-file-one.txt
tr_found_file_t(tr_sys_path_info info, std::string& f, std::string_view b)
tr_found_file_t(tr_sys_path_info info, tr_pathbuf&& filename_in, size_t base_len)
: tr_sys_path_info{ info }
, filename{ f }
, base{ b }
, subpath{ f.c_str() + std::size(b) + 1 }
, filename{ std::move(filename_in) }
, base{ filename.sv().substr(0, base_len) }
, subpath{ filename.sv().substr(base_len + 1) }
{
}
};
std::optional<tr_found_file_t> findFile(std::string& filename, tr_file_index_t i) const;
std::optional<tr_found_file_t> findFile(tr_file_index_t i) const;
/// METAINFO - TRACKERS
@ -582,6 +582,8 @@ public:
torrent's content than any other mime-type. */
std::string_view primaryMimeType() const;
static constexpr std::string_view PartialFileSuffix = std::string_view{ ".part" };
tr_torrent_metainfo metainfo_;
// TODO(ckerr): make private once some of torrent.cc's `tr_torrentFoo()` methods are member functions
@ -765,27 +767,6 @@ constexpr bool tr_isTorrent(tr_torrent const* tor)
*/
void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t blockIndex);
/**
* @brief Like tr_torrentFindFile(), but splits the filename into base and subpath.
*
* If the file is found, "tr_strvPath(base, subpath, nullptr)"
* will generate the complete filename.
*
* @return true if the file is found, false otherwise.
*
* @param base if the torrent is found, this will be either
* tor->downloadDir or tor->incompleteDir
* @param subpath on success, this pointer is assigned a newly-allocated
* string holding the second half of the filename.
*/
bool tr_torrentFindFile2(tr_torrent const*, tr_file_index_t fileNo, char const** base, char** subpath, time_t* mtime);
/* Returns a newly-allocated version of the tr_file.name string
* that's been modified to denote that it's not a complete file yet.
* In the current implementation this is done by appending ".part"
* a la Firefox. */
char* tr_torrentBuildPartial(tr_torrent const*, tr_file_index_t fileNo);
tr_peer_id_t const& tr_torrentGetPeerId(tr_torrent* tor);
tr_torrent_metainfo&& tr_ctorStealMetainfo(tr_ctor* ctor);

View File

@ -34,13 +34,13 @@ static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
auto const begin = tr_time();
tr_sys_file_t fd = TR_BAD_SYS_FILE;
uint64_t filePos = 0;
uint64_t file_pos = 0;
bool changed = false;
bool hadPiece = false;
time_t lastSleptAt = 0;
uint32_t piecePos = 0;
tr_file_index_t fileIndex = 0;
tr_file_index_t prevFileIndex = ~fileIndex;
bool had_piece = false;
time_t last_slept_at = 0;
uint32_t piece_pos = 0;
tr_file_index_t file_index = 0;
tr_file_index_t prev_file_index = ~file_index;
tr_piece_index_t piece = 0;
auto buffer = std::vector<std::byte>(1024 * 256);
auto sha = tr_sha1_init();
@ -50,58 +50,56 @@ static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
while (!*stopFlag && piece < tor->pieceCount())
{
auto const file_length = tor->fileSize(fileIndex);
auto const file_length = tor->fileSize(file_index);
/* if we're starting a new piece... */
if (piecePos == 0)
if (piece_pos == 0)
{
hadPiece = tor->hasPiece(piece);
had_piece = tor->hasPiece(piece);
}
/* if we're starting a new file... */
if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex)
if (file_pos == 0 && fd == TR_BAD_SYS_FILE && file_index != prev_file_index)
{
char* const filename = tr_torrentFindFile(tor, fileIndex);
fd = filename == nullptr ? TR_BAD_SYS_FILE :
tr_sys_file_open(filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0);
tr_free(filename);
prevFileIndex = fileIndex;
auto const found = tor->findFile(file_index);
fd = !found ? TR_BAD_SYS_FILE : tr_sys_file_open(found->filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0);
prev_file_index = file_index;
}
/* figure out how much we can read this pass */
uint64_t leftInPiece = tor->pieceSize(piece) - piecePos;
uint64_t leftInFile = file_length - filePos;
uint64_t bytesThisPass = std::min(leftInFile, leftInPiece);
bytesThisPass = std::min(bytesThisPass, uint64_t(std::size(buffer)));
uint64_t left_in_piece = tor->pieceSize(piece) - piece_pos;
uint64_t left_in_file = file_length - file_pos;
uint64_t bytes_this_pass = std::min(left_in_file, left_in_piece);
bytes_this_pass = std::min(bytes_this_pass, uint64_t(std::size(buffer)));
/* read a bit */
if (fd != TR_BAD_SYS_FILE)
{
auto numRead = uint64_t{};
if (tr_sys_file_read_at(fd, std::data(buffer), bytesThisPass, filePos, &numRead) && numRead > 0)
auto num_read = uint64_t{};
if (tr_sys_file_read_at(fd, std::data(buffer), bytes_this_pass, file_pos, &num_read) && num_read > 0)
{
bytesThisPass = numRead;
tr_sha1_update(sha, std::data(buffer), bytesThisPass);
tr_sys_file_advise(fd, filePos, bytesThisPass, TR_SYS_FILE_ADVICE_DONT_NEED);
bytes_this_pass = num_read;
tr_sha1_update(sha, std::data(buffer), bytes_this_pass);
tr_sys_file_advise(fd, file_pos, bytes_this_pass, TR_SYS_FILE_ADVICE_DONT_NEED);
}
}
/* move our offsets */
leftInPiece -= bytesThisPass;
leftInFile -= bytesThisPass;
piecePos += bytesThisPass;
filePos += bytesThisPass;
left_in_piece -= bytes_this_pass;
left_in_file -= bytes_this_pass;
piece_pos += bytes_this_pass;
file_pos += bytes_this_pass;
/* if we're finishing a piece... */
if (leftInPiece == 0)
if (left_in_piece == 0)
{
auto hash = tr_sha1_final(sha);
auto const hasPiece = hash && *hash == tor->pieceHash(piece);
auto const hash = tr_sha1_final(sha);
auto const has_piece = hash && *hash == tor->pieceHash(piece);
if (hasPiece || hadPiece)
if (has_piece || had_piece)
{
tor->setHasPiece(piece, hasPiece);
changed |= hasPiece != hadPiece;
tor->setHasPiece(piece, has_piece);
changed |= has_piece != had_piece;
}
tor->checked_pieces_.set(piece, true);
@ -109,20 +107,20 @@ static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
/* sleeping even just a few msec per second goes a long
* way towards reducing IO load... */
if (auto const now = tr_time(); lastSleptAt != now)
if (auto const now = tr_time(); last_slept_at != now)
{
lastSleptAt = now;
last_slept_at = now;
tr_wait_msec(MsecToSleepPerSecondDuringVerify);
}
sha = tr_sha1_init();
++piece;
tor->verify_progress = piece / double(tor->pieceCount());
piecePos = 0;
piece_pos = 0;
}
/* if we're finishing a file... */
if (leftInFile == 0)
if (left_in_file == 0)
{
if (fd != TR_BAD_SYS_FILE)
{
@ -130,8 +128,8 @@ static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
fd = TR_BAD_SYS_FILE;
}
fileIndex++;
filePos = 0;
++file_index;
file_pos = 0;
}
}