refactor: extract some file management into tr_files class (#2906)
This commit is contained in:
parent
8c51c48472
commit
4a65956cc9
|
@ -14,6 +14,8 @@
|
|||
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 */; };
|
||||
2856E0656A49F2665D69E760 /* benc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2856E0656A49F2665D69E761 /* benc.h */; };
|
||||
A47A7C87B8B57BE50DF0D410 /* files.cc in Sources */ = {isa = PBXBuildFile; fileRef = A47A7C87B8B57BE50DF0D411 /* files.cc */; };
|
||||
A47A7C87B8B57BE50DF0D412 /* files.h in Headers */ = {isa = PBXBuildFile; fileRef = A47A7C87B8B57BE50DF0D413 /* files.h */; };
|
||||
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB1 /* torrents.cc */; };
|
||||
2B9BA6C508B488FE586A0AB2 /* torrents.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB3 /* torrents.h */; };
|
||||
35F373030C2DA89000DAA8F2 /* FilePriorityCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */; };
|
||||
|
@ -569,6 +571,8 @@
|
|||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
2B9BA6C508B488FE586A0AB1 /* torrents.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = torrents.cc; sourceTree = "<group>"; };
|
||||
2B9BA6C508B488FE586A0AB3 /* torrents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = torrents.h; sourceTree = "<group>"; };
|
||||
A47A7C87B8B57BE50DF0D411 /* files.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = files.cc; sourceTree = "<group>"; };
|
||||
A47A7C87B8B57BE50DF0D413 /* files.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = files.h; sourceTree = "<group>"; };
|
||||
32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Transmission_Prefix.pch; sourceTree = "<group>"; };
|
||||
35F373000C2DA88F00DAA8F2 /* FilePriorityCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilePriorityCell.h; sourceTree = "<group>"; };
|
||||
35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FilePriorityCell.mm; sourceTree = "<group>"; };
|
||||
|
@ -1560,6 +1564,7 @@
|
|||
A29DF8B70DB2544C00D04E5A /* resume.h */,
|
||||
A29DF8B80DB2544C00D04E5A /* torrent.h */,
|
||||
2B9BA6C508B488FE586A0AB3 /* torrents.h */,
|
||||
A47A7C87B8B57BE50DF0D413 /* files.h */,
|
||||
C1033E031A3279B800EF44D8 /* crypto-utils-fallback.cc */,
|
||||
C1033E041A3279B800EF44D8 /* crypto-utils-ccrypto.cc */,
|
||||
C1033E051A3279B800EF44D8 /* crypto-utils.cc */,
|
||||
|
@ -1607,6 +1612,7 @@
|
|||
A2AA9BE0132CAC8D00FA131E /* announcer-udp.cc */,
|
||||
BEFC1DF90C07861A00B0BB3C /* torrent.cc */,
|
||||
2B9BA6C508B488FE586A0AB1 /* torrents.cc */,
|
||||
A47A7C87B8B57BE50DF0D411 /* files.cc */,
|
||||
BEFC1DFC0C07861A00B0BB3C /* port-forwarding.h */,
|
||||
BEFC1DFD0C07861A00B0BB3C /* port-forwarding.cc */,
|
||||
A21FBBA90EDA78C300BC3C51 /* bandwidth.h */,
|
||||
|
@ -2096,6 +2102,7 @@
|
|||
A29DF8BA0DB2544C00D04E5A /* resume.h in Headers */,
|
||||
A29DF8BB0DB2544C00D04E5A /* torrent.h in Headers */,
|
||||
2B9BA6C508B488FE586A0AB2 /* torrents.h in Headers */,
|
||||
A47A7C87B8B57BE50DF0D412 /* files.h in Headers */,
|
||||
A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */,
|
||||
C1FEE57B1C3223CC00D62832 /* watchdir.h in Headers */,
|
||||
A2AAB6650DE0D08B00E04DDA /* blocklist.h in Headers */,
|
||||
|
@ -2772,6 +2779,7 @@
|
|||
BEFC1E2F0C07861A00B0BB3C /* session.cc in Sources */,
|
||||
BEFC1E320C07861A00B0BB3C /* torrent.cc in Sources */,
|
||||
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */,
|
||||
A47A7C87B8B57BE50DF0D410 /* files.cc */,
|
||||
BEFC1E360C07861A00B0BB3C /* port-forwarding.cc in Sources */,
|
||||
BEFC1E3C0C07861A00B0BB3C /* platform.cc in Sources */,
|
||||
BEFC1E460C07861A00B0BB3C /* net.cc in Sources */,
|
||||
|
|
|
@ -31,6 +31,7 @@ set(PROJECT_FILES
|
|||
file-posix.cc
|
||||
file-win32.cc
|
||||
file.cc
|
||||
files.cc
|
||||
handshake.cc
|
||||
inout.cc
|
||||
log.cc
|
||||
|
@ -170,6 +171,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
|
|||
fdlimit.h
|
||||
file-info.h
|
||||
file-piece-map.h
|
||||
files.h
|
||||
handshake.h
|
||||
history.h
|
||||
inout.h
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
// This file Copyright © 2022 Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "files.h"
|
||||
|
||||
bool tr_files::empty() const noexcept
|
||||
{
|
||||
return std::empty(files_);
|
||||
}
|
||||
|
||||
size_t tr_files::size() const noexcept
|
||||
{
|
||||
return std::size(files_);
|
||||
}
|
||||
|
||||
uint64_t tr_files::size(tr_file_index_t file_index) const
|
||||
{
|
||||
return files_.at(file_index).size_;
|
||||
}
|
||||
|
||||
std::string const& tr_files::path(tr_file_index_t file_index) const
|
||||
{
|
||||
return files_.at(file_index).path_;
|
||||
}
|
||||
|
||||
void tr_files::setPath(tr_file_index_t file_index, std::string_view path)
|
||||
{
|
||||
files_.at(file_index).setPath(path);
|
||||
}
|
||||
|
||||
void tr_files::reserve(size_t n_files)
|
||||
{
|
||||
files_.reserve(n_files);
|
||||
}
|
||||
|
||||
void tr_files::shrinkToFit()
|
||||
{
|
||||
files_.shrink_to_fit();
|
||||
}
|
||||
|
||||
tr_file_index_t tr_files::add(std::string_view path, uint64_t size)
|
||||
{
|
||||
auto const file_index = static_cast<tr_file_index_t>(std::size(files_));
|
||||
files_.emplace_back(path, size);
|
||||
return file_index;
|
||||
}
|
||||
|
||||
void tr_files::clear() noexcept
|
||||
{
|
||||
files_.clear();
|
||||
}
|
||||
|
||||
std::optional<tr_files::FoundFile> tr_files::find(
|
||||
tr_file_index_t file_index,
|
||||
std::string_view const* search_paths,
|
||||
size_t n_paths) const
|
||||
{
|
||||
auto filename = tr_pathbuf{};
|
||||
auto file_info = tr_sys_path_info{};
|
||||
auto const& subpath = path(file_index);
|
||||
|
||||
for (size_t path_idx = 0; path_idx < n_paths; ++path_idx)
|
||||
{
|
||||
auto const base = search_paths[path_idx];
|
||||
|
||||
filename.assign(base, '/', subpath);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
return FoundFile{ file_info, std::move(filename), std::size(base) };
|
||||
}
|
||||
|
||||
filename.assign(filename, base, '/', subpath, PartialFileSuffix);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
return FoundFile{ file_info, std::move(filename), std::size(base) };
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// This file Copyright © 2022 Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "file.h"
|
||||
#include "tr-strbuf.h"
|
||||
|
||||
/**
|
||||
* A simple ordered collection of files.
|
||||
*/
|
||||
struct tr_files
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] bool empty() const noexcept;
|
||||
[[nodiscard]] size_t size() const noexcept;
|
||||
[[nodiscard]] uint64_t size(tr_file_index_t) const;
|
||||
[[nodiscard]] std::string const& path(tr_file_index_t) const;
|
||||
|
||||
void setPath(tr_file_index_t, std::string_view path);
|
||||
|
||||
void reserve(size_t);
|
||||
tr_file_index_t add(std::string_view path, uint64_t size);
|
||||
void shrinkToFit();
|
||||
void clear() noexcept;
|
||||
|
||||
struct FoundFile : public tr_sys_path_info
|
||||
{
|
||||
public:
|
||||
FoundFile(tr_sys_path_info info, tr_pathbuf&& filename_in, size_t base_len_in)
|
||||
: tr_sys_path_info{ info }
|
||||
, filename_{ std::move(filename_in) }
|
||||
, base_len_{ base_len_in }
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& filename() const noexcept
|
||||
{
|
||||
// /home/foo/Downloads/torrent/01-file-one.txt
|
||||
return filename_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto base() const noexcept
|
||||
{
|
||||
// /home/foo/Downloads
|
||||
return filename_.sv().substr(0, base_len_);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto subpath() const noexcept
|
||||
{
|
||||
// torrent/01-file-one.txt
|
||||
return filename_.sv().substr(base_len_ + 1);
|
||||
}
|
||||
|
||||
private:
|
||||
tr_pathbuf filename_;
|
||||
size_t base_len_;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::optional<FoundFile> find(
|
||||
tr_file_index_t file_index,
|
||||
std::string_view const* search_paths,
|
||||
size_t n_paths) const;
|
||||
|
||||
static constexpr std::string_view PartialFileSuffix = ".part";
|
||||
|
||||
private:
|
||||
struct file_t
|
||||
{
|
||||
public:
|
||||
void setPath(std::string_view subpath)
|
||||
{
|
||||
path_ = subpath;
|
||||
}
|
||||
|
||||
file_t(std::string_view path, uint64_t size)
|
||||
: path_{ path }
|
||||
, size_{ size }
|
||||
{
|
||||
}
|
||||
|
||||
std::string path_;
|
||||
uint64_t size_ = 0;
|
||||
};
|
||||
|
||||
std::vector<file_t> files_;
|
||||
};
|
|
@ -126,7 +126,7 @@ int readOrWriteBytes(
|
|||
auto const prealloc = (!do_write || !tor->fileIsWanted(file_index)) ? TR_PREALLOCATE_NONE :
|
||||
tor->session->preallocationMode;
|
||||
|
||||
fd = tr_fdFileCheckout(session, tor->uniqueId, file_index, found->filename, do_write, 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;
|
||||
|
@ -134,7 +134,7 @@ int readOrWriteBytes(
|
|||
tor,
|
||||
fmt::format(
|
||||
_("Couldn't get '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", found->filename),
|
||||
fmt::arg("path", found->filename()),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ std::string_view tr_torrent_metainfo::parseFiles(tr_torrent_metainfo& setme, tr_
|
|||
if (tr_variantDictFindInt(info_dict, TR_KEY_length, &len))
|
||||
{
|
||||
total_size = len;
|
||||
setme.files_.emplace_back(root_name, len);
|
||||
setme.files_.add(root_name, len);
|
||||
}
|
||||
|
||||
// "For the purposes of the other keys, the multi-file case is treated as
|
||||
|
@ -285,7 +285,7 @@ std::string_view tr_torrent_metainfo::parseFiles(tr_torrent_metainfo& setme, tr_
|
|||
return "path";
|
||||
}
|
||||
|
||||
setme.files_.emplace_back(buf, len);
|
||||
setme.files_.add(buf, len);
|
||||
total_size += len;
|
||||
}
|
||||
}
|
||||
|
@ -559,24 +559,3 @@ void tr_torrent_metainfo::removeFile(
|
|||
tr_sys_path_remove(makeFilename(dirname, name, info_hash_string, BasenameFormat::NameAndPartialHash, suffix));
|
||||
tr_sys_path_remove(makeFilename(dirname, name, info_hash_string, BasenameFormat::Hash, suffix));
|
||||
}
|
||||
|
||||
std::string const& tr_torrent_metainfo::fileSubpath(tr_file_index_t i) const
|
||||
{
|
||||
TR_ASSERT(i < fileCount());
|
||||
|
||||
return files_.at(i).path();
|
||||
}
|
||||
|
||||
void tr_torrent_metainfo::setFileSubpath(tr_file_index_t i, std::string_view subpath)
|
||||
{
|
||||
TR_ASSERT(i < fileCount());
|
||||
|
||||
files_.at(i).setSubpath(subpath);
|
||||
}
|
||||
|
||||
uint64_t tr_torrent_metainfo::fileSize(tr_file_index_t i) const
|
||||
{
|
||||
TR_ASSERT(i < fileCount());
|
||||
|
||||
return files_.at(i).size();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#include "block-info.h"
|
||||
#include "files.h"
|
||||
#include "magnet-metainfo.h"
|
||||
#include "tr-strbuf.h"
|
||||
|
||||
|
@ -35,6 +36,29 @@ public:
|
|||
// load multiple files.
|
||||
bool parseTorrentFile(std::string_view benc_filename, std::vector<char>* buffer = nullptr, tr_error** error = nullptr);
|
||||
|
||||
// FILES
|
||||
|
||||
[[nodiscard]] constexpr auto const& files() const noexcept
|
||||
{
|
||||
return files_;
|
||||
}
|
||||
[[nodiscard]] auto fileCount() const noexcept
|
||||
{
|
||||
return std::size(files());
|
||||
}
|
||||
[[nodiscard]] auto fileSize(tr_file_index_t i) const
|
||||
{
|
||||
return files().size(i);
|
||||
}
|
||||
[[nodiscard]] auto const& fileSubpath(tr_file_index_t i) const
|
||||
{
|
||||
return files().path(i);
|
||||
}
|
||||
void setFileSubpath(tr_file_index_t i, std::string_view subpath)
|
||||
{
|
||||
files_.setPath(i, subpath);
|
||||
}
|
||||
|
||||
/// BLOCK INFO
|
||||
|
||||
[[nodiscard]] constexpr auto const& blockInfo() const noexcept
|
||||
|
@ -83,6 +107,8 @@ public:
|
|||
return blockInfo().totalSize();
|
||||
}
|
||||
|
||||
// OTHER PROPERTIES
|
||||
|
||||
[[nodiscard]] constexpr auto const& comment() const noexcept
|
||||
{
|
||||
return comment_;
|
||||
|
@ -96,17 +122,6 @@ public:
|
|||
return source_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto fileCount() const noexcept
|
||||
{
|
||||
return std::size(files_);
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t fileSize(tr_file_index_t i) const;
|
||||
|
||||
[[nodiscard]] std::string const& fileSubpath(tr_file_index_t i) const;
|
||||
|
||||
void setFileSubpath(tr_file_index_t i, std::string_view subpath);
|
||||
|
||||
[[nodiscard]] constexpr auto const& isPrivate() const noexcept
|
||||
{
|
||||
return is_private_;
|
||||
|
@ -136,6 +151,8 @@ public:
|
|||
return pieces_offset_;
|
||||
}
|
||||
|
||||
// UTILS
|
||||
|
||||
[[nodiscard]] auto torrentFile(std::string_view torrent_dir) const
|
||||
{
|
||||
return makeFilename(torrent_dir, name(), infoHashString(), BasenameFormat::Hash, ".torrent");
|
||||
|
@ -189,39 +206,11 @@ private:
|
|||
return makeFilename(dirname, name(), infoHashString(), format, suffix);
|
||||
}
|
||||
|
||||
struct file_t
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::string const& path() const noexcept
|
||||
{
|
||||
return path_;
|
||||
}
|
||||
|
||||
void setSubpath(std::string_view subpath)
|
||||
{
|
||||
path_ = subpath;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t size() const noexcept
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
file_t(std::string_view path, uint64_t size)
|
||||
: path_{ path }
|
||||
, size_{ size }
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
uint64_t size_ = 0;
|
||||
};
|
||||
|
||||
tr_block_info block_info_ = tr_block_info{ 0, 0 };
|
||||
|
||||
tr_files files_;
|
||||
|
||||
std::vector<tr_sha1_digest_t> pieces_;
|
||||
std::vector<file_t> files_;
|
||||
|
||||
std::string comment_;
|
||||
std::string creator_;
|
||||
|
|
|
@ -645,7 +645,7 @@ static bool isNewTorrentASeed(tr_torrent* tor)
|
|||
}
|
||||
|
||||
// it's not a new seed if a file is partial
|
||||
if (tr_strvEndsWith(found->filename, tr_torrent::PartialFileSuffix))
|
||||
if (tr_strvEndsWith(found->filename(), tr_torrent::PartialFileSuffix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2415,7 +2415,7 @@ static void setLocationImpl(struct LocationData* const data)
|
|||
|
||||
if (auto found = tor->findFile(i); found)
|
||||
{
|
||||
auto const& oldpath = found->filename;
|
||||
auto const& oldpath = found->filename();
|
||||
auto const newpath = tr_pathbuf{ location, '/', found->subpath() };
|
||||
|
||||
tr_logAddTraceTor(tor, fmt::format("Found file #{}: '{}'", i, oldpath));
|
||||
|
@ -2565,7 +2565,7 @@ static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t i)
|
|||
{
|
||||
if (auto const& file_subpath = tor->fileSubpath(i); file_subpath != found->subpath())
|
||||
{
|
||||
auto const& oldpath = found->filename;
|
||||
auto const& oldpath = found->filename();
|
||||
auto const newpath = tr_pathbuf{ found->base(), '/', file_subpath };
|
||||
tr_error* error = nullptr;
|
||||
|
||||
|
@ -2642,54 +2642,11 @@ void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block)
|
|||
****
|
||||
***/
|
||||
|
||||
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{};
|
||||
|
||||
if (!std::empty(this->downloadDir()))
|
||||
{
|
||||
auto const base = this->downloadDir();
|
||||
|
||||
filename.assign(base, "/"sv, subpath);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
|
||||
}
|
||||
|
||||
filename.assign(filename, base, "/"sv, subpath, PartialFileSuffix);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::empty(this->incompleteDir()))
|
||||
{
|
||||
auto const base = this->incompleteDir();
|
||||
|
||||
filename.assign(base, "/"sv, subpath);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
return tr_found_file_t{ file_info, std::move(filename), std::size(base) };
|
||||
}
|
||||
|
||||
filename.assign(base, "/"sv, subpath, PartialFileSuffix);
|
||||
if (tr_sys_path_get_info(filename, 0, &file_info))
|
||||
{
|
||||
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
|
||||
char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum)
|
||||
{
|
||||
auto const found = tor->findFile(fileNum);
|
||||
return found ? tr_strdup(found->filename.c_str()) : nullptr;
|
||||
return found ? tr_strdup(found->filename()) : nullptr;
|
||||
}
|
||||
|
||||
/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <cstddef> // size_t
|
||||
#include <ctime>
|
||||
#include <optional>
|
||||
|
@ -368,33 +369,23 @@ public:
|
|||
metainfo_.setFileSubpath(i, subpath);
|
||||
}
|
||||
|
||||
struct tr_found_file_t : public tr_sys_path_info
|
||||
[[nodiscard]] auto findFile(tr_file_index_t file_index) const
|
||||
{
|
||||
// /home/foo/Downloads/torrent/01-file-one.txt
|
||||
tr_pathbuf filename;
|
||||
size_t base_len;
|
||||
auto n_paths = size_t{ 0U };
|
||||
auto paths = std::array<std::string_view, 2>{};
|
||||
|
||||
tr_found_file_t(tr_sys_path_info info, tr_pathbuf&& filename_in, size_t base_len_in)
|
||||
: tr_sys_path_info{ info }
|
||||
, filename{ std::move(filename_in) }
|
||||
, base_len{ base_len_in }
|
||||
if (auto const path = downloadDir(); !std::empty(path))
|
||||
{
|
||||
paths[n_paths++] = path.sv();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto base() const
|
||||
if (auto const path = incompleteDir(); !std::empty(path))
|
||||
{
|
||||
// /home/foo/Downloads
|
||||
return filename.sv().substr(0, base_len);
|
||||
paths[n_paths++] = path.sv();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto subpath() const
|
||||
{
|
||||
// torrent/01-file-one.txt
|
||||
return filename.sv().substr(base_len + 1);
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<tr_found_file_t> findFile(tr_file_index_t i) const;
|
||||
return metainfo_.files().find(file_index, std::data(paths), n_paths);
|
||||
}
|
||||
|
||||
/// METAINFO - TRACKERS
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
|
|||
if (file_pos == 0 && fd == TR_BAD_SYS_FILE && file_index != prev_file_index)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ add_executable(libtransmission-test
|
|||
error-test.cc
|
||||
file-piece-map-test.cc
|
||||
file-test.cc
|
||||
files-test.cc
|
||||
getopt-test.cc
|
||||
history-test.cc
|
||||
json-test.cc
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// This file Copyright (C) 2022 Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "files.h"
|
||||
|
||||
#include "test-fixtures.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
class FilesTest : public ::libtransmission::test::SandboxedTest
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(FilesTest, add)
|
||||
{
|
||||
auto constexpr Path = "/hello/world"sv;
|
||||
auto constexpr Size = size_t{ 1024 };
|
||||
|
||||
auto files = tr_files{};
|
||||
EXPECT_EQ(size_t{ 0U }, std::size(files));
|
||||
EXPECT_TRUE(std::empty(files));
|
||||
|
||||
auto const file_index = files.add(Path, Size);
|
||||
EXPECT_EQ(tr_file_index_t{ 0U }, file_index);
|
||||
EXPECT_EQ(size_t{ 1U }, std::size(files));
|
||||
EXPECT_EQ(Size, files.size(file_index));
|
||||
EXPECT_EQ(Path, files.path(file_index));
|
||||
EXPECT_FALSE(std::empty(files));
|
||||
}
|
||||
|
||||
TEST_F(FilesTest, setPath)
|
||||
{
|
||||
auto constexpr Path1 = "/hello/world"sv;
|
||||
auto constexpr Path2 = "/hello/there"sv;
|
||||
auto constexpr Size = size_t{ 2048 };
|
||||
|
||||
auto files = tr_files{};
|
||||
auto const file_index = files.add(Path1, Size);
|
||||
EXPECT_EQ(Path1, files.path(file_index));
|
||||
EXPECT_EQ(Size, files.size(file_index));
|
||||
|
||||
files.setPath(file_index, Path2);
|
||||
EXPECT_EQ(Path2, files.path(file_index));
|
||||
EXPECT_EQ(Size, files.size(file_index));
|
||||
}
|
||||
|
||||
TEST_F(FilesTest, clear)
|
||||
{
|
||||
auto constexpr Path1 = "/hello/world"sv;
|
||||
auto constexpr Path2 = "/hello/there"sv;
|
||||
auto constexpr Size = size_t{ 2048 };
|
||||
|
||||
auto files = tr_files{};
|
||||
files.add(Path1, Size);
|
||||
EXPECT_EQ(size_t{ 1U }, std::size(files));
|
||||
files.add(Path2, Size);
|
||||
EXPECT_EQ(size_t{ 2U }, std::size(files));
|
||||
|
||||
files.clear();
|
||||
EXPECT_TRUE(std::empty(files));
|
||||
EXPECT_EQ(size_t{ 0U }, std::size(files));
|
||||
}
|
||||
|
||||
TEST_F(FilesTest, find)
|
||||
{
|
||||
static auto constexpr Contents = "hello"sv;
|
||||
auto const filename = tr_pathbuf{ sandboxDir(), "/first_dir/hello.txt"sv };
|
||||
createFileWithContents(std::string{ filename }, std::data(Contents), std::size(Contents));
|
||||
|
||||
auto files = tr_files{};
|
||||
auto const file_index = files.add("first_dir/hello.txt", 1024);
|
||||
|
||||
auto const search_path_1 = tr_pathbuf{ sandboxDir() };
|
||||
auto const search_path_2 = tr_pathbuf{ "/tmp"sv };
|
||||
|
||||
auto search_path = std::vector<std::string_view>{ search_path_1.sv(), search_path_2.sv() };
|
||||
auto found = files.find(file_index, std::data(search_path), std::size(search_path));
|
||||
EXPECT_TRUE(found);
|
||||
EXPECT_EQ(filename, found->filename());
|
||||
|
||||
// same search, but with the search paths reversed
|
||||
search_path = std::vector<std::string_view>{ search_path_2.sv(), search_path_1.sv() };
|
||||
found = files.find(file_index, std::data(search_path), std::size(search_path));
|
||||
EXPECT_TRUE(found);
|
||||
EXPECT_EQ(filename, found->filename());
|
||||
|
||||
// now make it an incomplete file
|
||||
auto const partial_filename = tr_pathbuf{ filename, tr_files::PartialFileSuffix };
|
||||
EXPECT_TRUE(tr_sys_path_rename(filename, partial_filename));
|
||||
search_path = std::vector<std::string_view>{ search_path_1.sv(), search_path_2.sv() };
|
||||
found = files.find(file_index, std::data(search_path), std::size(search_path));
|
||||
EXPECT_TRUE(found);
|
||||
EXPECT_EQ(partial_filename, found->filename());
|
||||
|
||||
// same search, but with the search paths reversed
|
||||
search_path = std::vector<std::string_view>{ search_path_2.sv(), search_path_1.sv() };
|
||||
found = files.find(file_index, std::data(search_path), std::size(search_path));
|
||||
EXPECT_TRUE(found);
|
||||
EXPECT_EQ(partial_filename, found->filename());
|
||||
|
||||
// what about if we look for a file that does not exist
|
||||
EXPECT_TRUE(tr_sys_path_remove(partial_filename));
|
||||
EXPECT_FALSE(files.find(file_index, std::data(search_path), std::size(search_path)));
|
||||
}
|
Loading…
Reference in New Issue