Merge pull request #270 from arvidn/storage-refactor
factor out readwritev to a free function, to allow unit testing of it
This commit is contained in:
commit
6fc0b3609f
|
@ -398,6 +398,8 @@ namespace libtorrent
|
|||
// override some of its behavior, when implementing a custom storage.
|
||||
class TORRENT_EXPORT default_storage : public storage_interface, boost::noncopyable
|
||||
{
|
||||
friend struct write_fileop;
|
||||
friend struct read_fileop;
|
||||
public:
|
||||
// constructs the default_storage based on the give file_storage (fs).
|
||||
// ``mapped`` is an optional argument (it may be NULL). If non-NULL it
|
||||
|
@ -452,23 +454,7 @@ namespace libtorrent
|
|||
|
||||
int sparse_end(int start) const;
|
||||
|
||||
// this identifies a read or write operation
|
||||
// so that default_storage::readwritev() knows what to
|
||||
// do when it's actually touching the file
|
||||
struct fileop
|
||||
{
|
||||
// file operation
|
||||
boost::int64_t (file::*op)(boost::int64_t file_offset
|
||||
, file::iovec_t const* bufs, int num_bufs, error_code& ec, int flags);
|
||||
// file open mode (file::read_only, file::write_only etc.)
|
||||
// this is used to open the file, but also passed along as the
|
||||
// flags argument to the file operation (readv or writev)
|
||||
int mode;
|
||||
};
|
||||
|
||||
void delete_one_file(std::string const& p, error_code& ec);
|
||||
int readwritev(file::iovec_t const* bufs, int piece, int offset
|
||||
, int num_bufs, fileop const& op, storage_error& ec);
|
||||
|
||||
void need_partfile();
|
||||
|
||||
|
@ -501,7 +487,7 @@ namespace libtorrent
|
|||
// whose bit is 0, we set the file size, to make the file allocated
|
||||
// on disk (in full allocation mode) and just sparsely allocated in
|
||||
// case of sparse allocation mode
|
||||
bitfield m_file_created;
|
||||
mutable bitfield m_file_created;
|
||||
|
||||
bool m_allocate_files;
|
||||
};
|
||||
|
@ -716,6 +702,21 @@ namespace libtorrent
|
|||
boost::shared_ptr<void> m_torrent;
|
||||
};
|
||||
|
||||
// this identifies a read or write operation so that readwritev() knows
|
||||
// what to do when it's actually touching the file
|
||||
struct fileop
|
||||
{
|
||||
virtual int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec) = 0;
|
||||
};
|
||||
|
||||
// this function is responsible for turning read and write operations in the
|
||||
// torrent space (pieces) into read and write operations in the filesystem
|
||||
// space (files on disk).
|
||||
TORRENT_EXTRA_EXPORT int readwritev(file_storage const& files
|
||||
, file::iovec_t const* bufs, int piece, int offset, int num_bufs
|
||||
, fileop& op, storage_error& ec);
|
||||
|
||||
}
|
||||
|
||||
#endif // TORRENT_STORAGE_HPP_INCLUDED
|
||||
|
|
|
@ -263,8 +263,7 @@ TORRENT_EXPORT void assert_fail(char const* expr, int line
|
|||
char const* message = "assertion failed. Please file a bugreport at "
|
||||
"https://github.com/arvidn/libtorrent/issues\n"
|
||||
"Please include the following information:\n\n"
|
||||
"version: " LIBTORRENT_VERSION "\n"
|
||||
LIBTORRENT_REVISION "\n";
|
||||
"version: " LIBTORRENT_VERSION "-" LIBTORRENT_REVISION "\n";
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
|
|
410
src/storage.cpp
410
src/storage.cpp
|
@ -166,7 +166,6 @@ namespace libtorrent
|
|||
|
||||
namespace {
|
||||
|
||||
#if TORRENT_USE_ASSERTS
|
||||
int count_bufs(file::iovec_t const* bufs, int bytes)
|
||||
{
|
||||
int size = 0;
|
||||
|
@ -175,11 +174,9 @@ namespace libtorrent
|
|||
for (file::iovec_t const* i = bufs;; ++i, ++count)
|
||||
{
|
||||
size += i->iov_len;
|
||||
TORRENT_ASSERT(size <= bytes);
|
||||
if (size >= bytes) return count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
static boost::atomic<int> event_id;
|
||||
|
@ -223,8 +220,185 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
struct write_fileop : fileop
|
||||
{
|
||||
write_fileop(default_storage& st, int flags)
|
||||
: m_storage(st)
|
||||
, m_flags(flags)
|
||||
{}
|
||||
|
||||
int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec)
|
||||
TORRENT_OVERRIDE TORRENT_FINAL
|
||||
{
|
||||
if (m_storage.files().pad_file_at(file_index))
|
||||
{
|
||||
// writing to a pad-file is a no-op
|
||||
return size;
|
||||
}
|
||||
|
||||
int num_bufs = count_bufs(bufs, size);
|
||||
|
||||
if (file_index < int(m_storage.m_file_priority.size())
|
||||
&& m_storage.m_file_priority[file_index] == 0)
|
||||
{
|
||||
m_storage.need_partfile();
|
||||
|
||||
error_code e;
|
||||
peer_request map = m_storage.files().map_file(file_index
|
||||
, file_offset, 0);
|
||||
int ret = m_storage.m_part_file->writev(bufs, num_bufs
|
||||
, map.piece, map.start, e);
|
||||
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
ec.operation = storage_error::partfile_write;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// invalidate our stat cache for this file, since
|
||||
// we're writing to it
|
||||
m_storage.m_stat_cache.set_dirty(file_index);
|
||||
|
||||
file_handle handle = m_storage.open_file(file_index
|
||||
, file::read_write, ec);
|
||||
if (ec) return -1;
|
||||
|
||||
// please ignore the adjusted_offset. It's just file_offset.
|
||||
boost::int64_t adjusted_offset =
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
m_storage.files().file_base_deprecated(file_index) +
|
||||
#endif
|
||||
file_offset;
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
write_access_log(adjusted_offset, handle->file_id(), op_start | op_write, clock_type::now());
|
||||
#endif
|
||||
|
||||
error_code e;
|
||||
int ret = handle->writev(adjusted_offset
|
||||
, bufs, num_bufs, e, m_flags);
|
||||
|
||||
// set this unconditionally in case the upper layer would like to treat
|
||||
// short reads as errors
|
||||
ec.operation = storage_error::write;
|
||||
|
||||
// we either get an error or 0 or more bytes read
|
||||
TORRENT_ASSERT(e || ret >= 0);
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_write, clock_type::now());
|
||||
#endif
|
||||
TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs));
|
||||
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
default_storage& m_storage;
|
||||
int m_flags;
|
||||
};
|
||||
|
||||
struct read_fileop : fileop
|
||||
{
|
||||
read_fileop(default_storage& st, int flags)
|
||||
: m_storage(st)
|
||||
, m_flags(flags)
|
||||
{}
|
||||
|
||||
int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec)
|
||||
TORRENT_OVERRIDE TORRENT_FINAL
|
||||
{
|
||||
int num_bufs = count_bufs(bufs, size);
|
||||
|
||||
if (m_storage.files().pad_file_at(file_index))
|
||||
{
|
||||
// reading from a pad file yields zeroes
|
||||
clear_bufs(bufs, num_bufs);
|
||||
return size;
|
||||
}
|
||||
|
||||
if (file_index < int(m_storage.m_file_priority.size())
|
||||
&& m_storage.m_file_priority[file_index] == 0)
|
||||
{
|
||||
m_storage.need_partfile();
|
||||
|
||||
error_code e;
|
||||
peer_request map = m_storage.files().map_file(file_index
|
||||
, file_offset, 0);
|
||||
int ret = m_storage.m_part_file->readv(bufs, num_bufs
|
||||
, map.piece, map.start, e);
|
||||
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
ec.operation = storage_error::partfile_read;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
file_handle handle = m_storage.open_file(file_index
|
||||
, file::read_only | m_flags, ec);
|
||||
if (ec) return -1;
|
||||
|
||||
// please ignore the adjusted_offset. It's just file_offset.
|
||||
boost::int64_t adjusted_offset =
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
m_storage.files().file_base_deprecated(file_index) +
|
||||
#endif
|
||||
file_offset;
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
write_access_log(adjusted_offset, handle->file_id(), op_start | op_read, clock_type::now());
|
||||
#endif
|
||||
|
||||
error_code e;
|
||||
int ret = handle->readv(adjusted_offset
|
||||
, bufs, num_bufs, e, m_flags);
|
||||
|
||||
// set this unconditionally in case the upper layer would like to treat
|
||||
// short reads as errors
|
||||
ec.operation = storage_error::read;
|
||||
|
||||
// we either get an error or 0 or more bytes read
|
||||
TORRENT_ASSERT(e || ret >= 0);
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
write_access_log(adjusted_offset + ret , handle->file_id(), op_end | op_read, clock_type::now());
|
||||
#endif
|
||||
TORRENT_ASSERT(ret <= bufs_size(bufs, num_bufs));
|
||||
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
default_storage& m_storage;
|
||||
int m_flags;
|
||||
};
|
||||
|
||||
default_storage::default_storage(storage_params const& params)
|
||||
: m_files(*params.files)
|
||||
, m_pool(*params.pool)
|
||||
|
@ -1098,50 +1272,46 @@ namespace libtorrent
|
|||
int default_storage::readv(file::iovec_t const* bufs, int num_bufs
|
||||
, int piece, int offset, int flags, storage_error& ec)
|
||||
{
|
||||
fileop op = { &file::readv
|
||||
, file::read_only | flags };
|
||||
read_fileop op(*this, flags);
|
||||
|
||||
#ifdef TORRENT_SIMULATE_SLOW_READ
|
||||
boost::thread::sleep(boost::get_system_time()
|
||||
+ boost::posix_time::milliseconds(1000));
|
||||
#endif
|
||||
return readwritev(bufs, piece, offset, num_bufs, op, ec);
|
||||
return readwritev(files(), bufs, piece, offset, num_bufs, op, ec);
|
||||
}
|
||||
|
||||
int default_storage::writev(file::iovec_t const* bufs, int num_bufs
|
||||
, int piece, int offset, int flags, storage_error& ec)
|
||||
{
|
||||
fileop op = { &file::writev
|
||||
, file::read_write | flags };
|
||||
return readwritev(bufs, piece, offset, num_bufs, op, ec);
|
||||
write_fileop op(*this, flags);
|
||||
return readwritev(files(), bufs, piece, offset, num_bufs, op, ec);
|
||||
}
|
||||
|
||||
// much of what needs to be done when reading and writing
|
||||
// is buffer management and piece to file mapping. Most
|
||||
// of that is the same for reading and writing. This function
|
||||
// is a template, and the fileop decides what to do with the
|
||||
// file and the buffers.
|
||||
int default_storage::readwritev(file::iovec_t const* const bufs
|
||||
, const int piece, const int offset
|
||||
, const int num_bufs, fileop const& op, storage_error& ec)
|
||||
// much of what needs to be done when reading and writing is buffer
|
||||
// management and piece to file mapping. Most of that is the same for reading
|
||||
// and writing. This function is a template, and the fileop decides what to
|
||||
// do with the file and the buffers.
|
||||
int readwritev(file_storage const& files, file::iovec_t const* const bufs
|
||||
, const int piece, const int offset, const int num_bufs, fileop& op
|
||||
, storage_error& ec)
|
||||
{
|
||||
TORRENT_ASSERT(bufs != 0);
|
||||
TORRENT_ASSERT(piece >= 0);
|
||||
TORRENT_ASSERT(piece < m_files.num_pieces());
|
||||
TORRENT_ASSERT(piece < files.num_pieces());
|
||||
TORRENT_ASSERT(offset >= 0);
|
||||
TORRENT_ASSERT(num_bufs > 0);
|
||||
|
||||
const int size = bufs_size(bufs, num_bufs);
|
||||
TORRENT_ASSERT(size > 0);
|
||||
TORRENT_ASSERT(files().is_loaded());
|
||||
TORRENT_ASSERT(files.is_loaded());
|
||||
|
||||
// find the file iterator and file offset
|
||||
boost::uint64_t torrent_offset = piece * boost::uint64_t(m_files.piece_length()) + offset;
|
||||
int file_index = files().file_index_at_offset(torrent_offset);
|
||||
TORRENT_ASSERT(torrent_offset >= files().file_offset(file_index));
|
||||
TORRENT_ASSERT(torrent_offset < files().file_offset(file_index) + files().file_size(file_index));
|
||||
boost::int64_t file_offset = torrent_offset - files().file_offset(file_index);
|
||||
|
||||
file_handle handle;
|
||||
boost::uint64_t torrent_offset = piece * boost::uint64_t(files.piece_length()) + offset;
|
||||
int file_index = files.file_index_at_offset(torrent_offset);
|
||||
TORRENT_ASSERT(torrent_offset >= files.file_offset(file_index));
|
||||
TORRENT_ASSERT(torrent_offset < files.file_offset(file_index) + files.file_size(file_index));
|
||||
boost::int64_t file_offset = torrent_offset - files.file_offset(file_index);
|
||||
|
||||
// the number of bytes left before this read or write operation is
|
||||
// completely satisfied.
|
||||
|
@ -1149,188 +1319,69 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(bytes_left >= 0);
|
||||
|
||||
file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
||||
// copy the iovec array so we can use it to keep track of our current
|
||||
// location by updating the head base pointer and size. (see
|
||||
// advance_bufs())
|
||||
file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
||||
copy_bufs(bufs, size, current_buf);
|
||||
TORRENT_ASSERT(count_bufs(current_buf, size) == num_bufs);
|
||||
|
||||
file::iovec_t* tmp_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
||||
|
||||
// the number of bytes left to read in the current file (specified by
|
||||
// file_index). This is the minimum of (file_size - file_offset) and
|
||||
// bytes_left.
|
||||
int file_bytes_left;
|
||||
|
||||
// this is set to true if we advance the file_index. If so, we need to
|
||||
// re-open the file handle below, before we start the read or write to
|
||||
// it.
|
||||
bool need_reopen_file = true;
|
||||
|
||||
while (bytes_left > 0)
|
||||
{
|
||||
file_bytes_left = bytes_left;
|
||||
if (file_offset + file_bytes_left > files().file_size(file_index))
|
||||
file_bytes_left = (std::max)(static_cast<int>(files().file_size(file_index) - file_offset), 0);
|
||||
if (file_offset + file_bytes_left > files.file_size(file_index))
|
||||
file_bytes_left = (std::max)(static_cast<int>(files.file_size(file_index) - file_offset), 0);
|
||||
|
||||
// there are no bytes left in this file, move to the next one
|
||||
// this loop skips over empty files
|
||||
while (file_bytes_left == 0)
|
||||
{
|
||||
need_reopen_file = true;
|
||||
++file_index;
|
||||
file_offset = 0;
|
||||
TORRENT_ASSERT(file_index < files().num_files());
|
||||
TORRENT_ASSERT(file_index < files.num_files());
|
||||
|
||||
// this should not happen. bytes_left should be clamped by the total
|
||||
// size of the torrent, so we should never run off the end of it
|
||||
if (file_index >= files().num_files()) return size;
|
||||
if (file_index >= files.num_files()) return size;
|
||||
|
||||
file_bytes_left = bytes_left;
|
||||
if (file_offset + file_bytes_left > files().file_size(file_index))
|
||||
file_bytes_left = (std::max)(static_cast<int>(files().file_size(file_index) - file_offset), 0);
|
||||
if (file_offset + file_bytes_left > files.file_size(file_index))
|
||||
file_bytes_left = (std::max)(static_cast<int>(files.file_size(file_index) - file_offset), 0);
|
||||
}
|
||||
|
||||
if (files().pad_file_at(file_index))
|
||||
{
|
||||
if ((op.mode & file::rw_mask) == file::read_only)
|
||||
{
|
||||
int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs);
|
||||
TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs);
|
||||
TORRENT_ASSERT(num_tmp_bufs <= num_bufs);
|
||||
clear_bufs(tmp_bufs, num_tmp_bufs);
|
||||
}
|
||||
advance_bufs(current_buf, file_bytes_left);
|
||||
bytes_left -= file_bytes_left;
|
||||
file_offset += file_bytes_left;
|
||||
TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= num_bufs);
|
||||
continue;
|
||||
}
|
||||
// make a copy of the iovec array that _just_ covers the next
|
||||
// file_bytes_left bytes, i.e. just this one operation
|
||||
copy_bufs(current_buf, file_bytes_left, tmp_buf);
|
||||
|
||||
int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs);
|
||||
TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs);
|
||||
TORRENT_ASSERT(num_tmp_bufs <= num_bufs);
|
||||
int bytes_transferred = 0;
|
||||
error_code e;
|
||||
|
||||
if ((op.mode & file::rw_mask) == file::read_write)
|
||||
{
|
||||
// invalidate our stat cache for this file, since
|
||||
// we're writing to it
|
||||
m_stat_cache.set_dirty(file_index);
|
||||
}
|
||||
|
||||
if ((file_index < int(m_file_priority.size())
|
||||
&& m_file_priority[file_index] == 0)
|
||||
|| files().pad_file_at(file_index))
|
||||
{
|
||||
need_partfile();
|
||||
|
||||
if ((op.mode & file::rw_mask) == file::read_write)
|
||||
{
|
||||
// TODO: 3 which one of these are called should be determined by
|
||||
// the fileop object.
|
||||
// write
|
||||
bytes_transferred = m_part_file->writev(tmp_bufs, num_tmp_bufs
|
||||
, piece, offset, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
// read
|
||||
bytes_transferred = m_part_file->readv(tmp_bufs, num_tmp_bufs
|
||||
, piece, offset, e);
|
||||
}
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
ec.operation = (op.mode & file::rw_mask) == file::read_only
|
||||
? storage_error::partfile_read : storage_error::partfile_write;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (need_reopen_file)
|
||||
{
|
||||
handle = open_file(file_index, op.mode, ec);
|
||||
if (ec) return -1;
|
||||
need_reopen_file = false;
|
||||
|
||||
if (m_allocate_files && (op.mode & file::rw_mask) != file::read_only)
|
||||
{
|
||||
if (m_file_created.size() != files().num_files())
|
||||
m_file_created.resize(files().num_files(), false);
|
||||
|
||||
TORRENT_ASSERT(int(m_file_created.size()) == files().num_files());
|
||||
TORRENT_ASSERT(file_index < m_file_created.size());
|
||||
// if this is the first time we open this file for writing,
|
||||
// and we have m_allocate_files enabled, set the final size of
|
||||
// the file right away, to allocate it on the filesystem.
|
||||
if (m_file_created[file_index] == false)
|
||||
{
|
||||
handle->set_size(files().file_size(file_index), e);
|
||||
m_file_created.set_bit(file_index);
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
ec.operation = storage_error::fallocate;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// please ignore the adjusted_offset. It's just file_offset.
|
||||
boost::int64_t adjusted_offset =
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
files().file_base_deprecated(file_index) +
|
||||
#endif
|
||||
file_offset;
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
int flags = ((op.mode & file::rw_mask) == file::read_only) ? op_read : op_write;
|
||||
write_access_log(adjusted_offset, handle->file_id(), op_start | flags, clock_type::now());
|
||||
#endif
|
||||
|
||||
bytes_transferred = int(((*handle).*op.op)(adjusted_offset
|
||||
, tmp_bufs, num_tmp_bufs, e, op.mode));
|
||||
|
||||
// we either get an error or 0 or more bytes read
|
||||
TORRENT_ASSERT(e || bytes_transferred >= 0);
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
write_access_log(adjusted_offset + bytes_transferred, handle->file_id(), op_end | flags, clock_type::now());
|
||||
#endif
|
||||
TORRENT_ASSERT(bytes_transferred <= bufs_size(tmp_bufs, num_tmp_bufs));
|
||||
}
|
||||
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file_index;
|
||||
ec.operation = (op.mode & file::rw_mask) == file::read_only
|
||||
? storage_error::read : storage_error::write;
|
||||
return -1;
|
||||
}
|
||||
int bytes_transferred = op.file_op(file_index, file_offset,
|
||||
file_bytes_left, tmp_buf, ec);
|
||||
if (ec) return -1;
|
||||
|
||||
// advance our position in the iovec array and the file offset.
|
||||
advance_bufs(current_buf, bytes_transferred);
|
||||
bytes_left -= bytes_transferred;
|
||||
file_offset += bytes_transferred;
|
||||
|
||||
TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= num_bufs);
|
||||
|
||||
// if the file operation returned 0, we've hit end-of-file. We're done
|
||||
if (bytes_transferred == 0)
|
||||
{
|
||||
if (file_bytes_left > 0 )
|
||||
{
|
||||
// fill in this information in case the caller wants to treat
|
||||
// this as an error
|
||||
// a short-read as an error
|
||||
ec.file = file_index;
|
||||
ec.operation = (op.mode & file::rw_mask) == file::read_only
|
||||
? storage_error::read : storage_error::write;
|
||||
}
|
||||
return size - bytes_left;
|
||||
}
|
||||
|
||||
advance_bufs(current_buf, bytes_transferred);
|
||||
TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= num_bufs);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
@ -1366,6 +1417,31 @@ namespace libtorrent
|
|||
return file_handle();
|
||||
}
|
||||
TORRENT_ASSERT(h);
|
||||
|
||||
if (m_allocate_files && (mode & file::rw_mask) != file::read_only)
|
||||
{
|
||||
if (m_file_created.size() != files().num_files())
|
||||
m_file_created.resize(files().num_files(), false);
|
||||
|
||||
TORRENT_ASSERT(int(m_file_created.size()) == files().num_files());
|
||||
TORRENT_ASSERT(file < m_file_created.size());
|
||||
// if this is the first time we open this file for writing,
|
||||
// and we have m_allocate_files enabled, set the final size of
|
||||
// the file right away, to allocate it on the filesystem.
|
||||
if (m_file_created[file] == false)
|
||||
{
|
||||
error_code e;
|
||||
h->set_size(files().file_size(file), e);
|
||||
m_file_created.set_bit(file);
|
||||
if (e)
|
||||
{
|
||||
ec.ec = e;
|
||||
ec.file = file;
|
||||
ec.operation = storage_error::fallocate;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,7 +134,11 @@ void sig_handler(int sig)
|
|||
|
||||
output_test_log_to_terminal();
|
||||
|
||||
#ifdef WIN32
|
||||
exit(sig);
|
||||
#else
|
||||
exit(128 + sig);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_usage(char const* executable)
|
||||
|
@ -154,6 +158,17 @@ void print_usage(char const* executable)
|
|||
"by -l. If no test is specified, all tests are run\n", executable);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
LONG WINAPI seh_exception_handler(LPEXCEPTION_POINTERS p)
|
||||
{
|
||||
int sig = p->ExceptionRecord->ExceptionCode;
|
||||
fprintf(stderr, "SEH exception: %u\n"
|
||||
, p->ExceptionRecord->ExceptionCode);
|
||||
sig_handler(sig);
|
||||
exit(sig);
|
||||
}
|
||||
#endif
|
||||
|
||||
EXPORT int main(int argc, char const* argv[])
|
||||
{
|
||||
char const* executable = argv[0];
|
||||
|
@ -207,6 +222,8 @@ EXPORT int main(int argc, char const* argv[])
|
|||
// modal dialogs.
|
||||
SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT
|
||||
| SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
SetUnhandledExceptionFilter(&seh_exception_handler);
|
||||
#endif
|
||||
|
||||
#ifdef O_NONBLOCK
|
||||
|
|
|
@ -54,14 +54,8 @@ using namespace libtorrent;
|
|||
namespace lt = libtorrent;
|
||||
|
||||
const int piece_size = 16 * 1024 * 16;
|
||||
|
||||
const int half = piece_size / 2;
|
||||
|
||||
char* piece0 = page_aligned_allocator::malloc(piece_size);
|
||||
char* piece1 = page_aligned_allocator::malloc(piece_size);
|
||||
char* piece2 = page_aligned_allocator::malloc(piece_size);
|
||||
char* piece3 = page_aligned_allocator::malloc(piece_size);
|
||||
|
||||
void signal_bool(bool* b, char const* string)
|
||||
{
|
||||
*b = true;
|
||||
|
@ -171,6 +165,15 @@ boost::shared_ptr<default_storage> setup_torrent(file_storage& fs
|
|||
return s;
|
||||
}
|
||||
|
||||
typedef boost::shared_array<char> buf_ptr;
|
||||
|
||||
buf_ptr new_piece(int size)
|
||||
{
|
||||
buf_ptr ret(page_aligned_allocator::malloc(size), &page_aligned_allocator::free);
|
||||
std::generate(ret.get(), ret.get() + size, random_byte);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void run_storage_tests(boost::shared_ptr<torrent_info> info
|
||||
, file_storage& fs
|
||||
, std::string const& test_path
|
||||
|
@ -194,6 +197,10 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
int num_pieces = fs.num_pieces();
|
||||
TEST_CHECK(info->num_pieces() == num_pieces);
|
||||
|
||||
buf_ptr piece0 = new_piece(piece_size);
|
||||
buf_ptr piece1 = new_piece(piece_size);
|
||||
buf_ptr piece2 = new_piece(piece_size);
|
||||
|
||||
aux::session_settings set;
|
||||
set.set_int(settings_pack::disk_io_write_mode
|
||||
, unbuffered ? settings_pack::disable_os_cache
|
||||
|
@ -224,11 +231,11 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
int ret = 0;
|
||||
|
||||
// write piece 1 (in slot 0)
|
||||
file::iovec_t iov = { piece1, half};
|
||||
file::iovec_t iov = { piece1.get(), half};
|
||||
ret = s->writev(&iov, 1, 0, 0, 0, ec);
|
||||
if (ret != half) print_error("writev", ret, ec);
|
||||
|
||||
iov.iov_base = piece1 + half;
|
||||
iov.iov_base = piece1.get() + half;
|
||||
iov.iov_len = half;
|
||||
ret = s->writev(&iov, 1, 0, half, 0, ec);
|
||||
if (ret != half) print_error("writev", ret, ec);
|
||||
|
@ -238,7 +245,7 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
iov.iov_len = piece_size - 9;
|
||||
ret = s->readv(&iov, 1, 0, 3, 0, ec);
|
||||
if (ret != piece_size - 9) print_error("readv",ret, ec);
|
||||
TEST_CHECK(std::equal(piece+3, piece + piece_size-9, piece1+3));
|
||||
TEST_CHECK(std::equal(piece+3, piece + piece_size-9, piece1.get()+3));
|
||||
|
||||
// test unaligned read (where the bytes are not aligned)
|
||||
iov.iov_base = piece;
|
||||
|
@ -246,7 +253,7 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
ret = s->readv(&iov, 1, 0, 3, 0, ec);
|
||||
TEST_CHECK(ret == piece_size - 9);
|
||||
if (ret != piece_size - 9) print_error("readv", ret, ec);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1+3));
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1.get()+3));
|
||||
|
||||
// verify piece 1
|
||||
iov.iov_base = piece;
|
||||
|
@ -254,15 +261,15 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
ret = s->readv(&iov, 1, 0, 0, 0, ec);
|
||||
TEST_CHECK(ret == piece_size);
|
||||
if (ret != piece_size) print_error("readv", ret, ec);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece1));
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece1.get()));
|
||||
|
||||
// do the same with piece 0 and 2 (in slot 1 and 2)
|
||||
iov.iov_base = piece0;
|
||||
iov.iov_base = piece0.get();
|
||||
iov.iov_len = piece_size;
|
||||
ret = s->writev(&iov, 1, 1, 0, 0, ec);
|
||||
if (ret != piece_size) print_error("writev", ret, ec);
|
||||
|
||||
iov.iov_base = piece2;
|
||||
iov.iov_base = piece2.get();
|
||||
iov.iov_len = piece_size;
|
||||
ret = s->writev(&iov, 1, 2, 0, 0, ec);
|
||||
if (ret != piece_size) print_error("writev", ret, ec);
|
||||
|
@ -272,13 +279,13 @@ void run_storage_tests(boost::shared_ptr<torrent_info> info
|
|||
iov.iov_len = piece_size;
|
||||
ret = s->readv(&iov, 1, 1, 0, 0, ec);
|
||||
if (ret != piece_size) print_error("readv", ret, ec);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece0));
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece0.get()));
|
||||
|
||||
iov.iov_base = piece;
|
||||
iov.iov_len = piece_size;
|
||||
ret = s->readv(&iov, 1, 2, 0, 0, ec);
|
||||
if (ret != piece_size) print_error("readv", ret, ec);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece2));
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece2.get()));
|
||||
|
||||
s->release_files(ec);
|
||||
}
|
||||
|
@ -416,17 +423,14 @@ void test_check_files(std::string const& test_path
|
|||
fs.add_file("temp_storage/test2.tmp", piece_size * 2);
|
||||
fs.add_file("temp_storage/test3.tmp", piece_size);
|
||||
|
||||
char piece0[piece_size];
|
||||
char piece2[piece_size];
|
||||
|
||||
std::generate(piece0, piece0 + piece_size, random_byte);
|
||||
std::generate(piece2, piece2 + piece_size, random_byte);
|
||||
buf_ptr piece0 = new_piece(piece_size);
|
||||
buf_ptr piece2 = new_piece(piece_size);
|
||||
|
||||
libtorrent::create_torrent t(fs, piece_size, -1, 0);
|
||||
t.set_hash(0, hasher(piece0, piece_size).final());
|
||||
t.set_hash(0, hasher(piece0.get(), piece_size).final());
|
||||
t.set_hash(1, sha1_hash(0));
|
||||
t.set_hash(2, sha1_hash(0));
|
||||
t.set_hash(3, hasher(piece2, piece_size).final());
|
||||
t.set_hash(3, hasher(piece2.get(), piece_size).final());
|
||||
|
||||
create_directory(combine_path(test_path, "temp_storage"), ec);
|
||||
if (ec) std::cerr << "create_directory: " << ec.message() << std::endl;
|
||||
|
@ -434,11 +438,11 @@ void test_check_files(std::string const& test_path
|
|||
std::ofstream f;
|
||||
f.open(combine_path(test_path, combine_path("temp_storage", "test1.tmp")).c_str()
|
||||
, std::ios::trunc | std::ios::binary);
|
||||
f.write(piece0, sizeof(piece0));
|
||||
f.write(piece0.get(), piece_size);
|
||||
f.close();
|
||||
f.open(combine_path(test_path, combine_path("temp_storage", "test3.tmp")).c_str()
|
||||
, std::ios::trunc | std::ios::binary);
|
||||
f.write(piece2, sizeof(piece2));
|
||||
f.write(piece2.get(), piece_size);
|
||||
f.close();
|
||||
|
||||
std::vector<char> buf;
|
||||
|
@ -478,6 +482,7 @@ void test_check_files(std::string const& test_path
|
|||
#define storage_mode_compact storage_mode_sparse
|
||||
#endif
|
||||
|
||||
// TODO: 2 split this test up into smaller parts
|
||||
void run_test(bool unbuffered)
|
||||
{
|
||||
std::string test_path = current_working_directory();
|
||||
|
@ -485,6 +490,11 @@ void run_test(bool unbuffered)
|
|||
|
||||
boost::shared_ptr<torrent_info> info;
|
||||
|
||||
buf_ptr piece0 = new_piece(piece_size);
|
||||
buf_ptr piece1 = new_piece(piece_size);
|
||||
buf_ptr piece2 = new_piece(piece_size);
|
||||
buf_ptr piece3 = new_piece(piece_size);
|
||||
|
||||
{
|
||||
error_code ec;
|
||||
remove_all(combine_path(test_path, "temp_storage"), ec);
|
||||
|
@ -510,10 +520,10 @@ void run_test(bool unbuffered)
|
|||
|
||||
libtorrent::create_torrent t(fs, piece_size, -1, 0);
|
||||
TEST_CHECK(t.num_pieces() == 4);
|
||||
t.set_hash(0, hasher(piece0, piece_size).final());
|
||||
t.set_hash(1, hasher(piece1, piece_size).final());
|
||||
t.set_hash(2, hasher(piece2, piece_size).final());
|
||||
t.set_hash(3, hasher(piece3, piece_size).final());
|
||||
t.set_hash(0, hasher(piece0.get(), piece_size).final());
|
||||
t.set_hash(1, hasher(piece1.get(), piece_size).final());
|
||||
t.set_hash(2, hasher(piece2.get(), piece_size).final());
|
||||
t.set_hash(3, hasher(piece3.get(), piece_size).final());
|
||||
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), t.generate());
|
||||
|
@ -555,9 +565,9 @@ void run_test(bool unbuffered)
|
|||
fs.add_file(combine_path("temp_storage", "test1.tmp"), 3 * piece_size);
|
||||
libtorrent::create_torrent t(fs, piece_size, -1, 0);
|
||||
TEST_CHECK(fs.file_path(0) == combine_path("temp_storage", "test1.tmp"));
|
||||
t.set_hash(0, hasher(piece0, piece_size).final());
|
||||
t.set_hash(1, hasher(piece1, piece_size).final());
|
||||
t.set_hash(2, hasher(piece2, piece_size).final());
|
||||
t.set_hash(0, hasher(piece0.get(), piece_size).final());
|
||||
t.set_hash(1, hasher(piece1.get(), piece_size).final());
|
||||
t.set_hash(2, hasher(piece2.get(), piece_size).final());
|
||||
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), t.generate());
|
||||
|
@ -862,6 +872,17 @@ void fill_pattern(file::iovec_t* iov, int num_bufs)
|
|||
}
|
||||
}
|
||||
|
||||
bool check_pattern(std::vector<char> const& buf, int counter)
|
||||
{
|
||||
unsigned char* p = (unsigned char*)&buf[0];
|
||||
for (int k = 0; k < int(buf.size()); ++k)
|
||||
{
|
||||
if (p[k] != (counter & 0xff)) return false;
|
||||
++counter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void fill_pattern2(file::iovec_t* iov, int num_bufs)
|
||||
{
|
||||
for (int i = 0; i < num_bufs; ++i)
|
||||
|
@ -977,35 +998,261 @@ TORRENT_TEST(iovec_advance_bufs)
|
|||
free_iov(iov1, 10);
|
||||
}
|
||||
|
||||
TORRENT_TEST(storage)
|
||||
{
|
||||
// initialize test pieces
|
||||
for (char* p = piece0, *end(piece0 + piece_size); p < end; ++p)
|
||||
*p = random_byte();
|
||||
for (char* p = piece1, *end(piece1 + piece_size); p < end; ++p)
|
||||
*p = random_byte();
|
||||
for (char* p = piece2, *end(piece2 + piece_size); p < end; ++p)
|
||||
*p = random_byte();
|
||||
for (char* p = piece3, *end(piece3 + piece_size); p < end; ++p)
|
||||
*p = random_byte();
|
||||
|
||||
std::vector<std::string> test_paths;
|
||||
char* env = std::getenv("TORRENT_TEST_PATHS");
|
||||
if (env == 0)
|
||||
{
|
||||
test_paths.push_back(current_working_directory());
|
||||
}
|
||||
else
|
||||
{
|
||||
char* p = std::strtok(env, ";");
|
||||
while (p != 0)
|
||||
{
|
||||
test_paths.push_back(complete(p));
|
||||
p = std::strtok(0, ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(unbuffered) { run_test(true); }
|
||||
TORRENT_TEST(buffered) { run_test(false); }
|
||||
|
||||
file_storage make_fs()
|
||||
{
|
||||
file_storage fs;
|
||||
fs.add_file(combine_path("readwritev", "1"), 3);
|
||||
fs.add_file(combine_path("readwritev", "2"), 9);
|
||||
fs.add_file(combine_path("readwritev", "3"), 81);
|
||||
fs.add_file(combine_path("readwritev", "4"), 6561);
|
||||
fs.set_piece_length(0x1000);
|
||||
fs.set_num_pieces((fs.total_size() + 0xfff) / 0x1000);
|
||||
return fs;
|
||||
}
|
||||
|
||||
struct test_fileop : fileop
|
||||
{
|
||||
test_fileop(int stripe_size) : m_stripe_size(stripe_size) {}
|
||||
|
||||
int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec)
|
||||
{
|
||||
if (file_index >= int(m_file_data.size()))
|
||||
{
|
||||
m_file_data.resize(file_index + 1);
|
||||
}
|
||||
|
||||
const int write_size = (std::min)(m_stripe_size, size);
|
||||
|
||||
std::vector<char>& file = m_file_data[file_index];
|
||||
|
||||
if (file_offset + write_size > int(file.size()))
|
||||
{
|
||||
file.resize(file_offset + write_size);
|
||||
}
|
||||
|
||||
int left = write_size;
|
||||
while (left > 0)
|
||||
{
|
||||
const int copy_size = (std::min)(left, int(bufs->iov_len));
|
||||
memcpy(&file[file_offset], bufs->iov_base, copy_size);
|
||||
++bufs;
|
||||
file_offset += copy_size;
|
||||
left -= copy_size;
|
||||
}
|
||||
return write_size;
|
||||
}
|
||||
|
||||
int m_stripe_size;
|
||||
std::vector<std::vector<char> > m_file_data;
|
||||
};
|
||||
|
||||
struct test_read_fileop : fileop
|
||||
{
|
||||
// EOF after size bytes read
|
||||
test_read_fileop(int size) : m_size(size), m_counter(0) {}
|
||||
|
||||
int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec)
|
||||
{
|
||||
size = (std::min)(m_size, size);
|
||||
const int read = size;
|
||||
while (size > 0)
|
||||
{
|
||||
unsigned char* p = (unsigned char*)bufs->iov_base;
|
||||
const int len = (std::min)(int(bufs->iov_len), size);
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
p[i] = m_counter & 0xff;
|
||||
++m_counter;
|
||||
}
|
||||
size -= len;
|
||||
m_size -= len;
|
||||
++bufs;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int m_size;
|
||||
int m_counter;
|
||||
};
|
||||
|
||||
struct test_error_fileop : fileop
|
||||
{
|
||||
// EOF after size bytes read
|
||||
test_error_fileop(int error_file)
|
||||
: m_error_file(error_file) {}
|
||||
|
||||
int file_op(int file_index, boost::int64_t file_offset, int size
|
||||
, file::iovec_t const* bufs, storage_error& ec)
|
||||
{
|
||||
if (m_error_file == file_index)
|
||||
{
|
||||
ec.file = file_index;
|
||||
ec.ec.assign(boost::system::errc::permission_denied
|
||||
, boost::system::generic_category());
|
||||
ec.operation = storage_error::read;
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int m_error_file;
|
||||
};
|
||||
|
||||
int count_bufs(file::iovec_t const* bufs, int bytes)
|
||||
{
|
||||
int size = 0;
|
||||
int count = 1;
|
||||
if (bytes == 0) return 0;
|
||||
for (file::iovec_t const* i = bufs;; ++i, ++count)
|
||||
{
|
||||
size += i->iov_len;
|
||||
if (size >= bytes) return count;
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_stripe_1)
|
||||
{
|
||||
const int num_bufs = 30;
|
||||
file::iovec_t iov[num_bufs];
|
||||
|
||||
alloc_iov(iov, num_bufs);
|
||||
fill_pattern(iov, num_bufs);
|
||||
|
||||
file_storage fs = make_fs();
|
||||
test_fileop fop(1);
|
||||
storage_error ec;
|
||||
|
||||
TEST_CHECK(bufs_size(iov, num_bufs) >= fs.total_size());
|
||||
|
||||
file::iovec_t iov2[num_bufs];
|
||||
copy_bufs(iov, fs.total_size(), iov2);
|
||||
int num_bufs2 = count_bufs(iov2, fs.total_size());
|
||||
TEST_CHECK(num_bufs2 <= num_bufs);
|
||||
|
||||
int ret = readwritev(fs, iov2, 0, 0, num_bufs2, fop, ec);
|
||||
|
||||
TEST_EQUAL(ret, fs.total_size());
|
||||
TEST_EQUAL(fop.m_file_data.size(), 4);
|
||||
TEST_EQUAL(fop.m_file_data[0].size(), 3);
|
||||
TEST_EQUAL(fop.m_file_data[1].size(), 9);
|
||||
TEST_EQUAL(fop.m_file_data[2].size(), 81);
|
||||
TEST_EQUAL(fop.m_file_data[3].size(), 6561);
|
||||
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[0], 0));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[1], 3));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[2], 3 + 9));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[3], 3 + 9 + 81));
|
||||
|
||||
free_iov(iov, num_bufs);
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_single_buffer)
|
||||
{
|
||||
file_storage fs = make_fs();
|
||||
test_fileop fop(10000000);
|
||||
storage_error ec;
|
||||
|
||||
std::vector<char> buf(fs.total_size());
|
||||
file::iovec_t iov = { &buf[0], buf.size() };
|
||||
fill_pattern(&iov, 1);
|
||||
|
||||
int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec);
|
||||
|
||||
TEST_EQUAL(ret, fs.total_size());
|
||||
TEST_EQUAL(fop.m_file_data.size(), 4);
|
||||
TEST_EQUAL(fop.m_file_data[0].size(), 3);
|
||||
TEST_EQUAL(fop.m_file_data[1].size(), 9);
|
||||
TEST_EQUAL(fop.m_file_data[2].size(), 81);
|
||||
TEST_EQUAL(fop.m_file_data[3].size(), 6561);
|
||||
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[0], 0));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[1], 3));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[2], 3 + 9));
|
||||
TEST_CHECK(check_pattern(fop.m_file_data[3], 3 + 9 + 81));
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_read)
|
||||
{
|
||||
file_storage fs = make_fs();
|
||||
test_read_fileop fop(10000000);
|
||||
storage_error ec;
|
||||
|
||||
std::vector<char> buf(fs.total_size());
|
||||
file::iovec_t iov = { &buf[0], buf.size() };
|
||||
|
||||
// read everything
|
||||
int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec);
|
||||
|
||||
TEST_EQUAL(ret, fs.total_size());
|
||||
TEST_CHECK(check_pattern(buf, 0));
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_read_short)
|
||||
{
|
||||
file_storage fs = make_fs();
|
||||
test_read_fileop fop(100);
|
||||
storage_error ec;
|
||||
|
||||
std::vector<char> buf(fs.total_size());
|
||||
file::iovec_t iov = { &buf[0]
|
||||
, static_cast<size_t>(fs.total_size()) };
|
||||
|
||||
// read everything
|
||||
int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec);
|
||||
|
||||
TEST_EQUAL(ec.file, 3);
|
||||
|
||||
TEST_EQUAL(ret, 100);
|
||||
buf.resize(100);
|
||||
TEST_CHECK(check_pattern(buf, 0));
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_error)
|
||||
{
|
||||
file_storage fs = make_fs();
|
||||
test_error_fileop fop(2);
|
||||
storage_error ec;
|
||||
|
||||
std::vector<char> buf(fs.total_size());
|
||||
file::iovec_t iov = { &buf[0]
|
||||
, static_cast<size_t>(fs.total_size()) };
|
||||
|
||||
// read everything
|
||||
int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec);
|
||||
|
||||
TEST_EQUAL(ret, -1);
|
||||
TEST_EQUAL(ec.file, 2);
|
||||
TEST_EQUAL(ec.operation, storage_error::read);
|
||||
TEST_EQUAL(ec.ec, boost::system::errc::permission_denied);
|
||||
printf("error: %s\n", ec.ec.message().c_str());
|
||||
}
|
||||
|
||||
TORRENT_TEST(readwritev_zero_size_files)
|
||||
{
|
||||
file_storage fs;
|
||||
fs.add_file(combine_path("readwritev", "1"), 3);
|
||||
fs.add_file(combine_path("readwritev", "2"), 0);
|
||||
fs.add_file(combine_path("readwritev", "3"), 81);
|
||||
fs.add_file(combine_path("readwritev", "4"), 0);
|
||||
fs.add_file(combine_path("readwritev", "5"), 6561);
|
||||
fs.set_piece_length(0x1000);
|
||||
fs.set_num_pieces((fs.total_size() + 0xfff) / 0x1000);
|
||||
test_read_fileop fop(10000000);
|
||||
storage_error ec;
|
||||
|
||||
std::vector<char> buf(fs.total_size());
|
||||
file::iovec_t iov = { &buf[0]
|
||||
, static_cast<size_t>(fs.total_size()) };
|
||||
|
||||
// read everything
|
||||
int ret = readwritev(fs, &iov, 0, 0, 1, fop, ec);
|
||||
|
||||
TEST_EQUAL(ret, fs.total_size());
|
||||
TEST_CHECK(check_pattern(buf, 0));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue