From 5096bbae9b58626b23118a7cee3e04af894aa165 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 6 Dec 2021 22:18:17 -0600 Subject: [PATCH] refactor: add tr_torrent::file() (#2274) * refactor: add tr_torrent::fileCount() --- libtransmission/inout.cc | 13 +++-- libtransmission/resume.cc | 14 +++--- libtransmission/torrent.cc | 89 ++++++++++++++++------------------ libtransmission/torrent.h | 64 ++++++++++++++---------- libtransmission/transmission.h | 43 ++++++---------- libtransmission/verify.cc | 2 +- libtransmission/webseed.cc | 2 +- 7 files changed, 110 insertions(+), 117 deletions(-) diff --git a/libtransmission/inout.cc b/libtransmission/inout.cc index b1459d43a..3dc6bac8a 100644 --- a/libtransmission/inout.cc +++ b/libtransmission/inout.cc @@ -52,8 +52,7 @@ static int readOrWriteBytes( int err = 0; bool const doWrite = ioMode >= TR_IO_WRITE; - TR_ASSERT(fileIndex < tor->fileCount()); - auto const& file = tor->info.files[fileIndex]; + auto const& file = tor->file(fileIndex); TR_ASSERT(file.length == 0 || fileOffset < file.length); TR_ASSERT(fileOffset + buflen <= file.length); @@ -168,6 +167,7 @@ static int compareOffsetToFile(void const* a, void const* b) return 0; } +// TODO(ckerr) migrate to fpm void tr_ioFindFileLocation( tr_torrent const* tor, tr_piece_index_t pieceIndex, @@ -191,7 +191,7 @@ void tr_ioFindFileLocation( *fileOffset = offset - file->priv.offset; TR_ASSERT(*fileIndex < n_files); TR_ASSERT(*fileOffset < file->length); - TR_ASSERT(tor->info.files[*fileIndex].priv.offset + *fileOffset == offset); + TR_ASSERT(tor->file(*fileIndex).priv.offset + *fileOffset == offset); } } @@ -205,7 +205,6 @@ static int readOrWritePiece( size_t buflen) { int err = 0; - tr_info const* info = &tor->info; if (pieceIndex >= tor->info.pieceCount) { @@ -218,8 +217,8 @@ static int readOrWritePiece( while (buflen != 0 && err == 0) { - tr_file const* file = &info->files[fileIndex]; - uint64_t const bytesThisPass = std::min(uint64_t{ buflen }, uint64_t{ file->length - fileOffset }); + auto const& file = tor->file(fileIndex); + uint64_t const bytesThisPass = std::min(uint64_t{ buflen }, uint64_t{ file.length - fileOffset }); err = readOrWriteBytes(tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass); buf += bytesThisPass; @@ -229,7 +228,7 @@ static int readOrWritePiece( if (err != 0 && ioMode == TR_IO_WRITE && tor->error != TR_STAT_LOCAL_ERROR) { - auto const path = tr_strvPath(tor->downloadDir, file->name); + auto const path = tr_strvPath(tor->downloadDir, file.name); tr_torrentSetLocalError(tor, "%s (%s)", tr_strerror(err), path.c_str()); } } diff --git a/libtransmission/resume.cc b/libtransmission/resume.cc index c9a51a3b3..e431d9708 100644 --- a/libtransmission/resume.cc +++ b/libtransmission/resume.cc @@ -391,13 +391,12 @@ static uint64_t loadName(tr_variant* dict, tr_torrent* tor) static void saveFilenames(tr_variant* dict, tr_torrent const* tor) { auto const n = tor->fileCount(); - tr_file const* files = tor->info.files; bool any_renamed = false; for (tr_file_index_t i = 0; !any_renamed && i < n; ++i) { - any_renamed = files[i].priv.is_renamed; + any_renamed = tor->file(i).priv.is_renamed; } if (any_renamed) @@ -406,7 +405,8 @@ static void saveFilenames(tr_variant* dict, tr_torrent const* tor) for (tr_file_index_t i = 0; i < n; ++i) { - tr_variantListAddStrView(list, files[i].priv.is_renamed ? files[i].name : ""); + auto const& file = tor->file(i); + tr_variantListAddStrView(list, file.priv.is_renamed ? file.name : ""); } } } @@ -426,7 +426,7 @@ static uint64_t loadFilenames(tr_variant* dict, tr_torrent* tor) auto sv = std::string_view{}; if (tr_variantGetStrView(tr_variantListChild(list, i), &sv) && !std::empty(sv)) { - auto& file = tor->info.files[i]; + auto& file = tor->file(i); tr_free(file.name); file.name = tr_strvDup(sv); file.priv.is_renamed = true; @@ -475,16 +475,14 @@ static void rawToBitfield(tr_bitfield& bitfield, uint8_t const* raw, size_t rawl static void saveProgress(tr_variant* dict, tr_torrent* tor) { - tr_info const* inf = tr_torrentInfo(tor); - tr_variant* const prog = tr_variantDictAddDict(dict, TR_KEY_progress, 4); // add the mtimes auto const n = tor->fileCount(); tr_variant* const l = tr_variantDictAddList(prog, TR_KEY_mtimes, n); - for (auto const *file = inf->files, *end = file + n; file != end; ++file) + for (tr_file_index_t i = 0; i < n; ++i) { - tr_variantListAddInt(l, file->priv.mtime); + tr_variantListAddInt(l, tor->file(i).priv.mtime); } // add the 'checked pieces' bitfield diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index 63b7e505a..281676c53 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -583,10 +583,11 @@ static void tr_torrentInitFileOffsets(tr_torrent* tor) { auto offset = uint64_t{ 0 }; - for (auto *it = tor->info.files, *end = it + tor->fileCount(); it != end; ++it) + for (tr_file_index_t i = 0, n = tor->fileCount(); i < n; ++i) { - it->priv.offset = offset; - offset += it->length; + auto& file = tor->file(i); + file.priv.offset = offset; + offset += file.length; } } @@ -1170,7 +1171,7 @@ tr_stat const* tr_torrentStat(tr_torrent* tor) static uint64_t countFileBytesCompleted(tr_torrent const* tor, tr_file_index_t index) { - tr_file const& f = tor->info.files[index]; + tr_file const& f = tor->file(index); if (f.length == 0) { return 0; @@ -1217,9 +1218,8 @@ static uint64_t countFileBytesCompleted(tr_torrent const* tor, tr_file_index_t i tr_file_view tr_torrentFile(tr_torrent const* torrent, tr_file_index_t i) { TR_ASSERT(tr_isTorrent(torrent)); - TR_ASSERT(i < torrent->fileCount()); - auto const& file = torrent->info.files[i]; + auto const& file = torrent->file(i); auto const* const name = file.name; auto const priority = torrent->file_priorities_.filePriority(i); auto const wanted = torrent->files_wanted_.fileWanted(i); @@ -2096,6 +2096,7 @@ bool tr_torrentReqIsValid(tr_torrent const* tor, tr_piece_index_t index, uint32_ return err == 0; } +// TODO(ckerr) migrate to block_info uint64_t tr_pieceOffset(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset, uint32_t length) { TR_ASSERT(tr_isTorrent(tor)); @@ -2108,18 +2109,19 @@ uint64_t tr_pieceOffset(tr_torrent const* tor, tr_piece_index_t index, uint32_t return ret; } -tr_block_span_t tr_torGetFileBlockSpan(tr_torrent const* tor, tr_file_index_t const file) +// TODO(ckerr) migrate to fpm +tr_block_span_t tr_torGetFileBlockSpan(tr_torrent const* tor, tr_file_index_t const i) { - tr_file const* f = &tor->info.files[file]; + tr_file const& file = tor->file(i); - uint64_t offset = f->priv.offset; + uint64_t offset = file.priv.offset; tr_block_index_t const begin = offset / tor->block_size; - if (f->length == 0) + if (file.length == 0) { return { begin, begin }; } - offset += f->length - 1; + offset += file.length - 1; tr_block_index_t const end = 1 + offset / tor->block_size; return { begin, end }; } @@ -2423,7 +2425,7 @@ static void deleteLocalData(tr_torrent* tor, tr_fileFunc func) for (tr_file_index_t f = 0, n = tor->fileCount(); f < n; ++f) { /* try to find the file, looking in the partial and download dirs */ - auto filename = tr_strvPath(top, tor->info.files[f].name); + auto filename = tr_strvPath(top, tor->file(f).name); if (!tr_sys_path_exists(filename.c_str(), nullptr)) { @@ -2438,7 +2440,7 @@ static void deleteLocalData(tr_torrent* tor, tr_fileFunc func) /* if we found the file, move it */ if (!std::empty(filename)) { - auto target = tr_strvPath(tmpdir, tor->info.files[f].name); + auto target = tr_strvPath(tmpdir, tor->file(f).name); tr_moveFile(filename.c_str(), target.c_str(), nullptr); files.emplace_back(target); } @@ -2496,7 +2498,7 @@ static void deleteLocalData(tr_torrent* tor, tr_fileFunc func) for (tr_file_index_t f = 0, n = tor->fileCount(); f < n; ++f) { /* get the directory that this file goes in... */ - auto const filename = tr_strvPath(top, tor->info.files[f].name); + auto const filename = tr_strvPath(top, tor->file(f).name); char* dir = tr_sys_path_dirname(filename.c_str(), nullptr); if (dir == nullptr) { @@ -2596,7 +2598,7 @@ static void setLocationImpl(void* vdata) * if the target directory runs out of space halfway through... */ for (tr_file_index_t i = 0, n = tor->fileCount(); !err && i < n; ++i) { - tr_file const* const f = &tor->info.files[i]; + auto const file_length = tor->file(i).length; char const* oldbase = nullptr; char* sub = nullptr; @@ -2631,7 +2633,7 @@ static void setLocationImpl(void* vdata) if (data->setme_progress != nullptr) { - bytesHandled += f->length; + bytesHandled += file_length; *data->setme_progress = bytesHandled / tor->info.totalSize; } } @@ -2706,16 +2708,15 @@ void tr_torrentSetLocation( std::string_view tr_torrentPrimaryMimeType(tr_torrent const* tor) { - tr_info const* inf = &tor->info; - // count up how many bytes there are for each mime-type in the torrent // NB: get_mime_type_for_filename() always returns the same ptr for a // mime_type, so its raw pointer can be used as a key. auto size_per_mime_type = std::unordered_map{}; - for (tr_file const *it = inf->files, *end = it + tor->fileCount(); it != end; ++it) + for (tr_file_index_t i = 0, n = tor->fileCount(); i < n; ++i) { - auto const mime_type = tr_get_mime_type_for_filename(it->name); - size_per_mime_type[mime_type] += it->length; + auto const& file = tor->file(i); + auto const mime_type = tr_get_mime_type_for_filename(file.name); + size_per_mime_type[mime_type] += file.length; } if (std::empty(size_per_mime_type)) @@ -2738,31 +2739,28 @@ std::string_view tr_torrentPrimaryMimeType(tr_torrent const* tor) **** ***/ -static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t fileIndex) +static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t i) { - tr_info const* const inf = &tor->info; - tr_file* const f = &inf->files[fileIndex]; - time_t const now = tr_time(); - /* close the file so that we can reopen in read-only mode as needed */ - tr_cacheFlushFile(tor->session->cache, tor, fileIndex); - tr_fdFileClose(tor->session, tor, fileIndex); + tr_cacheFlushFile(tor->session->cache, tor, i); + tr_fdFileClose(tor->session, tor, i); /* now that the file is complete and closed, we can start watching its * mtime timestamp for changes to know if we need to reverify pieces */ - f->priv.mtime = now; + tr_file& file = tor->file(i); + file.priv.mtime = tr_time(); /* 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, fileIndex, &base, &sub, nullptr)) + if (tr_torrentFindFile2(tor, i, &base, &sub, nullptr)) { - if (strcmp(sub, f->name) != 0) + if (strcmp(sub, file.name) != 0) { auto const oldpath = tr_strvPath(base, sub); - auto const newpath = tr_strvPath(base, f->name); + auto const newpath = tr_strvPath(base, file.name); tr_error* error = nullptr; if (!tr_sys_path_rename(oldpath.c_str(), newpath.c_str(), &error)) @@ -2835,8 +2833,7 @@ void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block) std::optional tr_torrent::findFile(std::string& filename, tr_file_index_t i) const { - TR_ASSERT(i < this->fileCount()); - tr_file const& file = info.files[i]; + tr_file const& file = this->file(i); auto file_info = tr_sys_path_info{}; if (this->downloadDir != nullptr) @@ -2937,9 +2934,9 @@ static void refreshCurrentDir(tr_torrent* tor) tor->currentDir = dir; } -char* tr_torrentBuildPartial(tr_torrent const* tor, tr_file_index_t fileNum) +char* tr_torrentBuildPartial(tr_torrent const* tor, tr_file_index_t i) { - return tr_strdup_printf("%s.part", tor->info.files[fileNum].name); + return tr_strdup_printf("%s.part", tor->file(i).name); } /*** @@ -3114,7 +3111,7 @@ static tr_file_index_t* renameFindAffectedFiles(tr_torrent* tor, char const* old for (tr_file_index_t i = 0; i < n_files; ++i) { - char const* name = tor->info.files[i].name; + char const* name = tor->file(i).name; size_t const len = strlen(name); if ((len == oldpath_len || (len > oldpath_len && name[oldpath_len] == '/')) && memcmp(oldpath, name, oldpath_len) == 0) @@ -3174,18 +3171,18 @@ static int renamePath(tr_torrent* tor, char const* oldpath, char const* newname) static void renameTorrentFileString(tr_torrent* tor, char const* oldpath, char const* newname, tr_file_index_t fileIndex) { char* name = nullptr; - tr_file* file = &tor->info.files[fileIndex]; + tr_file& file = tor->file(fileIndex); size_t const oldpath_len = strlen(oldpath); if (strchr(oldpath, TR_PATH_DELIMITER) == nullptr) { - if (oldpath_len >= strlen(file->name)) + if (oldpath_len >= strlen(file.name)) { name = tr_buildPath(newname, nullptr); } else { - name = tr_buildPath(newname, file->name + oldpath_len + 1, nullptr); + name = tr_buildPath(newname, file.name + oldpath_len + 1, nullptr); } } else @@ -3197,27 +3194,27 @@ static void renameTorrentFileString(tr_torrent* tor, char const* oldpath, char c return; } - if (oldpath_len >= strlen(file->name)) + if (oldpath_len >= strlen(file.name)) { name = tr_buildPath(tmp, newname, nullptr); } else { - name = tr_buildPath(tmp, newname, file->name + oldpath_len + 1, nullptr); + name = tr_buildPath(tmp, newname, file.name + oldpath_len + 1, nullptr); } tr_free(tmp); } - if (strcmp(file->name, name) == 0) + if (strcmp(file.name, name) == 0) { tr_free(name); } else { - tr_free(file->name); - file->name = name; - file->priv.is_renamed = true; + tr_free(file.name); + file.name = name; + file.priv.is_renamed = true; } } diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index cb62fb713..7a556a98e 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -289,6 +289,44 @@ public: setDirty(); } + /// FILES + + tr_file_index_t fileCount() const + { + return info.fileCount; + } + + auto& file(tr_file_index_t i) + { + TR_ASSERT(i < this->fileCount()); + + return info.files[i]; + } + + auto const& file(tr_file_index_t i) const + { + TR_ASSERT(i < this->fileCount()); + + return info.files[i]; + } + + struct tr_found_file_t : public tr_sys_path_info + { + std::string& 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_sys_path_info{ info } + , filename{ f } + , base{ b } + , subpath{ f.c_str() + std::size(b) + 1 } + { + } + }; + + std::optional findFile(std::string& filename, tr_file_index_t i) const; + /// CHECKSUMS bool ensurePieceIsChecked(tr_piece_index_t piece) @@ -319,7 +357,7 @@ public: auto const found = this->findFile(filename, i); auto const mtime = found ? found->last_modified_at : 0; - info.files[i].priv.mtime = mtime; + this->file(i).priv.mtime = mtime; // if a file has changed, mark its pieces as unchecked if (mtime == 0 || mtime != mtimes[i]) @@ -330,30 +368,6 @@ public: } } - /// FILES - - tr_file_index_t fileCount() const - { - return info.fileCount; - } - - struct tr_found_file_t : public tr_sys_path_info - { - std::string& 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_sys_path_info{ info } - , filename{ f } - , base{ b } - , subpath{ f.c_str() + std::size(b) + 1 } - { - } - }; - - std::optional findFile(std::string& filename, tr_file_index_t i) const; - tr_info info = {}; tr_bitfield dnd_pieces_ = tr_bitfield{ 0 }; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 956f1178c..42e3c76a8 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -999,12 +999,12 @@ using tr_torrent_rename_done_func = void (*)( // * EXAMPLES * * Consider a tr_torrent where its - * info.files[0].name is "frobnitz-linux/checksum" and - * info.files[1].name is "frobnitz-linux/frobnitz.iso". + * tr_torrentFile(tor, 0).name is "frobnitz-linux/checksum" and + * tr_torrentFile(tor, 1).name is "frobnitz-linux/frobnitz.iso". * * 1. tr_torrentRenamePath(tor, "frobnitz-linux", "foo") will rename - * the "frotbnitz-linux" folder as "foo", and update both info.name - * and info.files[*].name. + * the "frotbnitz-linux" folder as "foo", and update both + * tr_torrentName(tor) and tr_torrentFile(tor, *).name. * * 2. tr_torrentRenamePath(tor, "frobnitz-linux/checksum", "foo") will * rename the "frobnitz-linux/checksum" file as "foo" and update @@ -1513,34 +1513,19 @@ void tr_torrentTrackersFree(tr_tracker_stat* trackerStats, int trackerCount); */ double* tr_torrentWebSpeeds_KBps(tr_torrent const* torrent); +/* + * This view structure is intended for short-term use. Its pointers are owned + * by the torrent and may be invalidated if the torrent is edited or removed. + */ struct tr_file_view { - // This file's name. Includes the full subpath in the torrent. - char const* name; - - // the current size of the file, i.e. how much we've downloaded - uint64_t have; - - // the total size of the file - uint64_t length; - - // have / length - double progress; - - // the file's priority - tr_priority_t priority; - - // do we want to download this file? - bool wanted; + char const* name; // This file's name. Includes the full subpath in the torrent. + uint64_t have; // the current size of the file, i.e. how much we've downloaded + uint64_t length; // the total size of the file + double progress; // have / length + tr_priority_t priority; // the file's priority + bool wanted; // do we want to download this file? }; - -/** - * Returns a tr_file_view containing information about a file. - * - * This view structure is intended for short-term use. Pointers in it are - * owned and managed by the torrent. Callers who want to keep this info - * must make their own copy. - */ tr_file_view tr_torrentFile(tr_torrent const* torrent, tr_file_index_t file); size_t tr_torrentFileCount(tr_torrent const* torrent); diff --git a/libtransmission/verify.cc b/libtransmission/verify.cc index e1239c4cb..34f2112eb 100644 --- a/libtransmission/verify.cc +++ b/libtransmission/verify.cc @@ -51,7 +51,7 @@ static bool verifyTorrent(tr_torrent* tor, bool* stopFlag) while (!*stopFlag && piece < tor->info.pieceCount) { - auto const file_length = tor->info.files[fileIndex].length; + auto const file_length = tor->file(fileIndex).length; /* if we're starting a new piece... */ if (piecePos == 0) diff --git a/libtransmission/webseed.cc b/libtransmission/webseed.cc index fb109ffed..9dfd829ea 100644 --- a/libtransmission/webseed.cc +++ b/libtransmission/webseed.cc @@ -518,7 +518,7 @@ static void task_request_next_chunk(struct tr_webseed_task* t) auto file_offset = uint64_t{}; tr_ioFindFileLocation(tor, step_piece, step_piece_offset, &file_index, &file_offset); - auto const& file = inf->files[file_index]; + auto const& file = tor->file(file_index); uint64_t this_pass = std::min(remain, file.length - file_offset); if (std::empty(urls[file_index]))