generalize part_file::export_file to pass back the buffers to a callback function

This commit is contained in:
arvidn 2017-04-14 17:25:24 -07:00 committed by Arvid Norberg
parent 645d658214
commit cef9773c70
4 changed files with 74 additions and 88 deletions

View File

@ -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<void(std::int64_t, span<char>)> 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<slot_index_t> 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<piece_index_t, slot_index_t> m_piece_map;

View File

@ -67,6 +67,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/vector.hpp"
#include "libtorrent/aux_/path.hpp"
#include <functional> // for std::function
#include <cstdint>
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<std::uint32_t[]> 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<char*>(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<bool, slot_index_t> 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<std::uint32_t[]> 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<int>(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<char*>(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<bool, slot_index_t> 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<int>(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<void(std::int64_t, span<char>)> f
, std::int64_t const offset, std::int64_t size, error_code& ec)
{
std::unique_lock<std::mutex> 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

View File

@ -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<char> 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);

View File

@ -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<char> buf)
{
for (char i : buf)
{
// make sure we got the bytes we expected
TEST_CHECK(i == static_cast<char>(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));
}
}