generalize part_file::export_file to pass back the buffers to a callback function
This commit is contained in:
parent
645d658214
commit
cef9773c70
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue