From cef9773c700f24cb1845f450a483b630e4ba5b19 Mon Sep 17 00:00:00 2001 From: arvidn Date: Fri, 14 Apr 2017 17:25:24 -0700 Subject: [PATCH] generalize part_file::export_file to pass back the buffers to a callback function --- include/libtorrent/part_file.hpp | 11 +-- src/part_file.cpp | 117 ++++++++++++++----------------- src/storage.cpp | 8 ++- test/test_part_file.cpp | 26 +++---- 4 files changed, 74 insertions(+), 88 deletions(-) diff --git a/include/libtorrent/part_file.hpp b/include/libtorrent/part_file.hpp index d3fec3ce3..4a7b0fd6d 100644 --- a/include/libtorrent/part_file.hpp +++ b/include/libtorrent/part_file.hpp @@ -65,8 +65,11 @@ namespace libtorrent { void move_partfile(std::string const& path, error_code& ec); - void import_file(file& f, std::int64_t offset, std::int64_t size, error_code& ec); - void export_file(file& f, std::int64_t offset, std::int64_t size, error_code& ec); + // the function is called for every block of data belonging to the + // specified range that's in the part_file. The first parameter is the + // offset within the range + void export_file(std::function)> f + , std::int64_t offset, std::int64_t size, error_code& ec); // flush the metadata void flush_metadata(error_code& ec); @@ -92,7 +95,7 @@ namespace libtorrent { std::vector m_free_slots; // this is the number of slots allocated - slot_index_t m_num_allocated; + slot_index_t m_num_allocated{0}; // the max number of pieces in the torrent this part file is // backing @@ -109,7 +112,7 @@ namespace libtorrent { // if this is true, the metadata in memory has changed since // we last saved or read it from disk. It means that we // need to flush the metadata before closing the file - bool m_dirty_metadata; + bool m_dirty_metadata = false; // maps a piece index to the part-file slot it is stored in std::unordered_map m_piece_map; diff --git a/src/part_file.cpp b/src/part_file.cpp index 39d7e4ee3..c70630b0a 100644 --- a/src/part_file.cpp +++ b/src/part_file.cpp @@ -67,6 +67,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/vector.hpp" #include "libtorrent/aux_/path.hpp" +#include // for std::function +#include + namespace { // round up to even kilobyte @@ -77,14 +80,12 @@ namespace { namespace libtorrent { part_file::part_file(std::string const& path, std::string const& name - , int num_pieces, int piece_size) + , int const num_pieces, int const piece_size) : m_path(path) , m_name(name) - , m_num_allocated(0) , m_max_pieces(num_pieces) , m_piece_size(piece_size) , m_header_size(round_up((2 + num_pieces) * 4)) - , m_dirty_metadata(false) { TORRENT_ASSERT(num_pieces > 0); TORRENT_ASSERT(m_piece_size > 0); @@ -92,56 +93,55 @@ namespace libtorrent { error_code ec; std::string fn = combine_path(m_path, m_name); m_file.open(fn, file::read_only, ec); - if (!ec) + if (ec) return; + + // parse header + std::unique_ptr header(new std::uint32_t[m_header_size]); + iovec_t b = {header.get(), std::size_t(m_header_size)}; + int n = int(m_file.readv(0, b, ec)); + if (ec) return; + + // we don't have a full header. consider the file empty + if (n < m_header_size) return; + using namespace libtorrent::detail; + + char* ptr = reinterpret_cast(header.get()); + // we have a header. Parse it + int const num_pieces_ = int(read_uint32(ptr)); + int const piece_size_ = int(read_uint32(ptr)); + + // if there is a mismatch in number of pieces or piece size + // consider the file empty and overwrite anything in there + if (num_pieces != num_pieces_ || m_piece_size != piece_size_) return; + + // this is used to determine which slots are free, and how many + // slots are allocated + aux::vector free_slots; + free_slots.resize(num_pieces, true); + + for (piece_index_t i = piece_index_t(0); i < piece_index_t(num_pieces); ++i) { - // parse header - std::unique_ptr header(new std::uint32_t[m_header_size]); - iovec_t b = {header.get(), std::size_t(m_header_size)}; - int n = int(m_file.readv(0, b, ec)); - if (ec) return; + slot_index_t const slot(read_int32(ptr)); + if (static_cast(slot) < 0) continue; - // we don't have a full header. consider the file empty - if (n < m_header_size) return; - using namespace libtorrent::detail; + // invalid part-file + TORRENT_ASSERT(slot < slot_index_t(num_pieces)); + if (slot >= slot_index_t(num_pieces)) continue; - char* ptr = reinterpret_cast(header.get()); - // we have a header. Parse it - int const num_pieces_ = int(read_uint32(ptr)); - int const piece_size_ = int(read_uint32(ptr)); + if (slot >= m_num_allocated) + m_num_allocated = next(slot); - // if there is a mismatch in number of pieces or piece size - // consider the file empty and overwrite anything in there - if (num_pieces != num_pieces_ || m_piece_size != piece_size_) return; - - // this is used to determine which slots are free, and how many - // slots are allocated - aux::vector free_slots; - free_slots.resize(num_pieces, true); - - for (piece_index_t i = piece_index_t(0); i < piece_index_t(num_pieces); ++i) - { - slot_index_t const slot(read_int32(ptr)); - if (static_cast(slot) < 0) continue; - - // invalid part-file - TORRENT_ASSERT(slot < slot_index_t(num_pieces)); - if (slot >= slot_index_t(num_pieces)) continue; - - if (slot >= m_num_allocated) - m_num_allocated = next(slot); - - free_slots[slot] = false; - m_piece_map[i] = slot; - } - - // now, populate the free_list with the "holes" - for (slot_index_t i(0); i < m_num_allocated; ++i) - { - if (free_slots[i]) m_free_slots.push_back(i); - } - - m_file.close(); + free_slots[slot] = false; + m_piece_map[i] = slot; } + + // now, populate the free_list with the "holes" + for (slot_index_t i(0); i < m_num_allocated; ++i) + { + if (free_slots[i]) m_free_slots.push_back(i); + } + + m_file.close(); } part_file::~part_file() @@ -281,21 +281,8 @@ namespace libtorrent { m_path = path; } - void part_file::import_file(file& f, std::int64_t offset - , std::int64_t size, error_code& ec) - { - TORRENT_UNUSED(f); - TORRENT_UNUSED(offset); - TORRENT_UNUSED(size); - TORRENT_UNUSED(ec); - - // not implemented - TORRENT_ASSERT_FAIL(); - ec.assign(boost::system::errc::operation_not_supported - , boost::system::generic_category()); - } - - void part_file::export_file(file& f, std::int64_t const offset, std::int64_t size, error_code& ec) + void part_file::export_file(std::function)> f + , std::int64_t const offset, std::int64_t size, error_code& ec) { std::unique_lock l(m_mutex); @@ -330,9 +317,7 @@ namespace libtorrent { TORRENT_ASSERT(!ec); if (ec || v.iov_len == 0) return; - std::int64_t const ret = f.writev(file_offset, v, ec); - TORRENT_ASSERT(ec || ret == std::int64_t(v.iov_len)); - if (ec || ret != std::int64_t(v.iov_len)) return; + f(file_offset, {buf.get(), std::size_t(block_to_copy)}); // we're done with the disk I/O, grab the lock again to update // the slot map diff --git a/src/storage.cpp b/src/storage.cpp index 950276af0..cb706160f 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -298,7 +298,13 @@ namespace libtorrent { need_partfile(); - m_part_file->export_file(*f, fs.file_offset(i), fs.file_size(i), ec.ec); + m_part_file->export_file([&f, &ec](std::int64_t file_offset, span buf) + { + iovec_t const v = {buf.data(), buf.size()}; + std::int64_t const ret = f->writev(file_offset, v, ec.ec); + TORRENT_ASSERT(ec || ret == std::int64_t(v.iov_len)); + }, fs.file_offset(i), fs.file_size(i), ec.ec); + if (ec) { ec.file(i); diff --git a/test/test_part_file.cpp b/test/test_part_file.cpp index 90a7dbc1c..2f06f67c3 100644 --- a/test/test_part_file.cpp +++ b/test/test_part_file.cpp @@ -112,10 +112,16 @@ TORRENT_TEST(part_file) std::string output_filename = combine_path(combine_path(cwd, "partfile_test_dir") , "part_file_test_export"); - file output(output_filename, file::read_write, ec); - if (ec) std::printf("export open file: %s\n", ec.message().c_str()); - pf.export_file(output, 10 * piece_size, 1024, ec); + pf.export_file([](std::int64_t file_offset, span buf) + { + for (char i : buf) + { + // make sure we got the bytes we expected + TEST_CHECK(i == static_cast(file_offset)); + ++file_offset; + } + }, 10 * piece_size, 1024, ec); if (ec) std::printf("export_file: %s\n", ec.message().c_str()); pf.free_piece(piece_index_t(10)); @@ -128,20 +134,6 @@ TORRENT_TEST(part_file) TEST_CHECK(!exists(combine_path(combine_path(cwd, "partfile_test_dir2"), "partfile.parts"), ec)); TEST_CHECK(!ec); if (ec) std::printf("exists: %s\n", ec.message().c_str()); - - output.close(); - - // verify that the exported file is what we expect it to be - output.open(output_filename, file::read_only, ec); - if (ec) std::printf("exported file open: %s\n", ec.message().c_str()); - - memset(buf, 0, sizeof(buf)); - - output.readv(0, v, ec); - if (ec) std::printf("exported file read: %s\n", ec.message().c_str()); - - for (int i = 0; i < 1024; ++i) - TEST_CHECK(buf[i] == char(i)); } }