From f1d229aae61faac0d6a4b4c1d58e8b06137e3ab4 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 3 Jan 2009 08:11:31 +0000 Subject: [PATCH] first pass of replacing coalesce writes and reads by writev and readv (no windows support yet) --- include/Makefile.am | 1 + include/libtorrent/alloca.hpp | 51 +++++++ include/libtorrent/storage.hpp | 12 +- src/Makefile.am | 1 + src/disk_io_thread.cpp | 88 ++++++++--- src/file.cpp | 6 +- src/storage.cpp | 263 ++++++++++++++++++++++++--------- 7 files changed, 322 insertions(+), 100 deletions(-) create mode 100644 include/libtorrent/alloca.hpp diff --git a/include/Makefile.am b/include/Makefile.am index 1cdea6211..332c20f9e 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,6 +1,7 @@ nobase_include_HEADERS = libtorrent/alert.hpp \ libtorrent/alert_types.hpp \ libtorrent/assert.hpp \ +libtorrent/alloca.hpp \ libtorrent/bandwidth_manager.hpp \ libtorrent/bandwidth_limit.hpp \ libtorrent/bandwidth_queue_entry.hpp \ diff --git a/include/libtorrent/alloca.hpp b/include/libtorrent/alloca.hpp new file mode 100644 index 000000000..2d2601e7b --- /dev/null +++ b/include/libtorrent/alloca.hpp @@ -0,0 +1,51 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCA + +#include "libtorrent/config.hpp" + +#ifdef TORRENT_WINDOWS + +#include +#define TORRENT_ALLOCA(t, n) static_cast(_alloca(sizeof(t) * n)); + +#else + +#include +#define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * n)); + +#endif + +#endif + + diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 6bd1d4755..1cf3e822d 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -58,6 +58,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/hasher.hpp" #include "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" +#include "libtorrent/file.hpp" namespace libtorrent { @@ -123,6 +124,9 @@ namespace libtorrent // false return value indicates an error virtual bool initialize(bool allocate_files) = 0; + virtual int readv(file::iovec_t* bufs, int slot, int offset, int num_bufs); + virtual int writev(file::iovec_t* buf, int slot, int offset, int num_bufs); + // negative return value indicates an error virtual int read(char* buf, int slot, int offset, int size) = 0; @@ -305,16 +309,16 @@ namespace libtorrent bool allocate_slots(int num_slots, bool abort_on_disk = false); int read_impl( - char* buf + file::iovec_t* bufs , int piece_index , int offset - , int size); + , int num_bufs); int write_impl( - const char* buf + file::iovec_t* bufs , int piece_index , int offset - , int size); + , int num_bufs); // -1=error 0=ok 1=skip int check_one_piece(int& have_piece); diff --git a/src/Makefile.am b/src/Makefile.am index 367dbff29..c68a02f35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,7 @@ noinst_HEADERS = \ $(top_srcdir)/include/libtorrent/alert.hpp \ $(top_srcdir)/include/libtorrent/alert_types.hpp \ $(top_srcdir)/include/libtorrent/assert.hpp \ +$(top_srcdir)/include/libtorrent/alloca.hpp \ $(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ $(top_srcdir)/include/libtorrent/bandwidth_manager.hpp \ $(top_srcdir)/include/libtorrent/bandwidth_limit.hpp \ diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 1da0a530c..f4a26c0f5 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -55,8 +55,17 @@ namespace libtorrent , m_queue_buffer_size(0) , m_cache_size(512) // 512 * 16kB = 8MB , m_cache_expiry(60) // 1 minute +// the file class doesn't support proper writev +// and readv on windows, so it's more efficient +// to coalesce reads and writes into a bigger +// buffer first +#ifdef TORRENT_WINDOWS , m_coalesce_writes(true) , m_coalesce_reads(true) +#else + , m_coalesce_writes(false) + , m_coalesce_reads(false) +#endif , m_use_read_cache(true) #ifndef TORRENT_DISABLE_POOL_ALLOCATOR , m_pool(block_size) @@ -322,29 +331,47 @@ namespace libtorrent , mutex_t::scoped_lock& l) { INVARIANT_CHECK; + // TODO: copy *e and unlink it before unlocking cached_piece_entry& p = *e; int piece_size = p.storage->info()->piece_size(p.piece); #ifdef TORRENT_DISK_STATS m_log << log_time() << " flushing " << piece_size << std::endl; #endif TORRENT_ASSERT(piece_size > 0); - boost::scoped_array buf; - if (m_coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]); int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; int buffer_size = 0; int offset = 0; + + boost::scoped_array buf; + boost::scoped_array iov; + int iov_counter = 0; + if (m_coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]); + // TOOD: replace with alloca + else iov.reset(new file::iovec_t[blocks_in_piece]); + for (int i = 0; i <= blocks_in_piece; ++i) { if (i == blocks_in_piece || p.blocks[i] == 0) { if (buffer_size == 0) continue; - TORRENT_ASSERT(buf); TORRENT_ASSERT(buffer_size <= i * m_block_size); l.unlock(); - p.storage->write_impl(buf.get(), p.piece, (std::min)( - i * m_block_size, piece_size) - buffer_size, buffer_size); + if (iov) + { + TORRENT_ASSERT(iov); + p.storage->write_impl(iov.get(), p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, iov_counter); + iov_counter = 0; + } + else + { + TORRENT_ASSERT(buf); + file::iovec_t b = { buf.get(), buffer_size }; + p.storage->write_impl(&b, p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, 1); + } l.lock(); ++m_cache_stats.writes; // std::cerr << " flushing p: " << p.piece << " bytes: " << buffer_size << std::endl; @@ -357,24 +384,29 @@ namespace libtorrent TORRENT_ASSERT(offset + block_size > 0); if (!buf) { - l.unlock(); - p.storage->write_impl(p.blocks[i], p.piece, i * m_block_size, block_size); - l.lock(); - ++m_cache_stats.writes; + iov[iov_counter].iov_base = p.blocks[i]; + iov[iov_counter].iov_len = block_size; + ++iov_counter; } else { std::memcpy(buf.get() + offset, p.blocks[i], block_size); offset += m_block_size; - buffer_size += block_size; } - free_buffer(p.blocks[i]); - p.blocks[i] = 0; + buffer_size += block_size; TORRENT_ASSERT(p.num_blocks > 0); --p.num_blocks; ++m_cache_stats.blocks_written; --m_cache_stats.cache_size; } + + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i] == 0) continue; + free_buffer(p.blocks[i]); + p.blocks[i] = 0; + } + TORRENT_ASSERT(buffer_size == 0); // std::cerr << " flushing p: " << p.piece << " cached_blocks: " << m_cache_stats.cache_size << std::endl; #ifdef TORRENT_DEBUG @@ -439,12 +471,17 @@ namespace libtorrent TORRENT_ASSERT(buffer_size <= piece_size); TORRENT_ASSERT(buffer_size + start_block * m_block_size <= piece_size); boost::scoped_array buf; + boost::scoped_array iov; + int iov_counter = 0; if (m_coalesce_reads) buf.reset(new (std::nothrow) char[buffer_size]); + else iov.reset(new file::iovec_t[end_block - start_block]); + int ret = 0; if (buf) { l.unlock(); - ret += p.storage->read_impl(buf.get(), p.piece, start_block * m_block_size, buffer_size); + file::iovec_t b = { buf.get(), buffer_size }; + ret += p.storage->read_impl(&b, p.piece, start_block * m_block_size, 1); l.lock(); if (p.storage->error()) { return -1; } ++m_cache_stats.reads; @@ -465,15 +502,23 @@ namespace libtorrent } else { - l.unlock(); - ret += p.storage->read_impl(p.blocks[i], p.piece, piece_offset, block_size); - if (p.storage->error()) { return -1; } - l.lock(); - ++m_cache_stats.reads; + iov[iov_counter].iov_base = p.blocks[i]; + iov[iov_counter].iov_len = block_size; + ++iov_counter; } offset += m_block_size; piece_offset += m_block_size; } + + if (iov) + { + l.unlock(); + ret += p.storage->read_impl(iov.get(), p.piece, start_block * m_block_size, iov_counter); + l.lock(); + if (p.storage->error()) { return -1; } + ++m_cache_stats.reads; + } + TORRENT_ASSERT(ret <= buffer_size); return (ret != buffer_size) ? -1 : ret; } @@ -913,8 +958,8 @@ namespace libtorrent } else if (ret == -2) { - ret = j.storage->read_impl(j.buffer, j.piece, j.offset - , j.buffer_size); + file::iovec_t b = { j.buffer, j.buffer_size }; + ret = j.storage->read_impl(&b, j.piece, j.offset, 1); if (ret < 0) { test_error(j); @@ -1182,7 +1227,8 @@ namespace libtorrent } } #ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception& e) + } + catch (std::exception& e) { ret = -1; try diff --git a/src/file.cpp b/src/file.cpp index c4212d637..69fba4206 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -244,8 +244,9 @@ namespace libtorrent TORRENT_ASSERT(is_open()); #ifdef TORRENT_WINDOWS + // TODO: Replace with ReadFileScatter if possible size_type ret = 0; - for (iovec_t* i = bufs, end(bufs + num_bufs); i < end; ++i) + for (iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { if (i->iov_len <= 0) continue; DWORD intermediate = 0; @@ -271,8 +272,9 @@ namespace libtorrent TORRENT_ASSERT(is_open()); #ifdef TORRENT_WINDOWS + // Replace by WriteFileGather if possible size_type ret = 0; - for (iovec_* i = bufs, end(bufs + num_bufs); i < end; ++i) + for (iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { if (i->iov_len <= 0) continue; DWORD intermediate = 0; diff --git a/src/storage.cpp b/src/storage.cpp index 7b77aafce..e1fd991f0 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -50,9 +50,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include -#include -#include +#include #if BOOST_VERSION >= 103500 #include #endif @@ -72,6 +70,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/file_pool.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/alloca.hpp" //#define TORRENT_PARTIAL_HASH_LOG @@ -315,6 +314,86 @@ namespace libtorrent return true; } + // for backwards compatibility, let the default readv and + // writev implementations be implemented in terms of the + // old read and write + int storage_interface::readv(file::iovec_t* bufs + , int slot, int offset, int num_bufs) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = write((char const*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int storage_interface::writev(file::iovec_t* bufs, int slot + , int offset, int num_bufs) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = read((char*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target) + { + int size = 0; + int ret = 1; + for (;;) + { + *target = *bufs; + size += bufs->iov_len; + if (size >= bytes) + { + target->iov_len -= size - bytes; + return ret; + } + ++bufs; + ++target; + ++ret; + } + } + + void advance_bufs(file::iovec_t*& bufs, int bytes) + { + int size = 0; + for (;;) + { + size += bufs->iov_len; + if (size >= bytes) + { + ((char*&)bufs->iov_base) += bufs->iov_len - (size - bytes); + bufs->iov_len = size - bytes; + return; + } + ++bufs; + } + } + + int bufs_size(file::iovec_t *bufs, int num_bufs) + { + int size = 0; + for (file::iovec_t* i = bufs, *end(bufs + num_bufs); i < end; ++i) + size += i->iov_len; + return size; + } + + void clear_bufs(file::iovec_t *bufs, int num_bufs) + { + for (file::iovec_t* i = bufs, *end(bufs + num_bufs); i < end; ++i) + std::memset(i->iov_base, 0, i->iov_len); + } + class storage : public storage_interface, boost::noncopyable { public: @@ -333,7 +412,9 @@ namespace libtorrent bool initialize(bool allocate_files); bool move_storage(fs::path save_path); int read(char* buf, int slot, int offset, int size); - int write(const char* buf, int slot, int offset, int size); + int write(char const* buf, int slot, int offset, int size); + int readv(file::iovec_t* bufs, int slot, int offset, int num_bufs); + int writev(file::iovec_t* buf, int slot, int offset, int num_bufs); bool move_slot(int src_slot, int dst_slot); bool swap_slots(int slot1, int slot2); bool swap_slots3(int slot1, int slot2, int slot3); @@ -341,8 +422,6 @@ namespace libtorrent bool write_resume_data(entry& rd) const; sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size); - int read_impl(char* buf, int slot, int offset, int size, bool fill_zero); - ~storage() { m_pool.release(this); } @@ -370,7 +449,7 @@ namespace libtorrent hasher whole; int slot_size1 = piece_size; m_scratch_buffer.resize(slot_size1); - read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, false); + read(&m_scratch_buffer[0], slot, 0, slot_size1); if (error()) return sha1_hash(0); if (ph.offset > 0) partial.update(&m_scratch_buffer[0], ph.offset); @@ -382,7 +461,7 @@ namespace libtorrent if (slot_size > 0) { m_scratch_buffer.resize(slot_size); - read_impl(&m_scratch_buffer[0], slot, ph.offset, slot_size, false); + read(&m_scratch_buffer[0], slot, ph.offset, slot_size); if (error()) return sha1_hash(0); ph.h.update(&m_scratch_buffer[0], slot_size); } @@ -836,9 +915,11 @@ namespace libtorrent { int piece_size = m_files.piece_size(dst_slot); m_scratch_buffer.resize(piece_size); - int ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true); - int ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size); - return ret1 != piece_size || ret2 != piece_size; + int ret = read(&m_scratch_buffer[0], src_slot, 0, piece_size); + if (ret != piece_size) return true; + ret = write(&m_scratch_buffer[0], dst_slot, 0, piece_size); + if (ret != piece_size) return true; + return false; } bool storage::swap_slots(int slot1, int slot2) @@ -848,12 +929,15 @@ namespace libtorrent int piece1_size = m_files.piece_size(slot2); int piece2_size = m_files.piece_size(slot1); m_scratch_buffer.resize(piece_size * 2); - int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); - int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); - int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size); - int ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size); - return ret1 != piece1_size || ret2 != piece2_size - || ret3 != piece1_size || ret4 != piece2_size; + int ret = read(&m_scratch_buffer[0], slot1, 0, piece1_size); + if (ret != piece1_size) return true; + ret = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size); + if (ret != piece2_size) return true; + ret = write(&m_scratch_buffer[0], slot2, 0, piece1_size); + if (ret != piece1_size) return true; + ret = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size); + if (ret != piece2_size) return true; + return false; } bool storage::swap_slots3(int slot1, int slot2, int slot3) @@ -864,37 +948,36 @@ namespace libtorrent int piece2_size = m_files.piece_size(slot3); int piece3_size = m_files.piece_size(slot1); m_scratch_buffer.resize(piece_size * 2); - int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); - int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); - int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size); - int ret4 = read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true); - int ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size); - int ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size); - return ret1 != piece1_size || ret2 != piece2_size - || ret3 != piece1_size || ret4 != piece3_size - || ret5 != piece2_size || ret6 != piece3_size; + int ret = 0; + ret = read(&m_scratch_buffer[0], slot1, 0, piece1_size); + if (ret != piece1_size) return true; + ret = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size); + if (ret != piece2_size) return true; + ret = write(&m_scratch_buffer[0], slot2, 0, piece1_size); + if (ret != piece1_size) return true; + ret = read(&m_scratch_buffer[0], slot3, 0, piece3_size); + if (ret != piece3_size) return true; + ret = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size); + if (ret != piece2_size) return true; + ret = write(&m_scratch_buffer[0], slot1, 0, piece3_size); + if (ret != piece3_size) return true; + return false; } - int storage::read( - char* buf + int storage::readv( + file::iovec_t* bufs , int slot , int offset - , int size) + , int num_bufs) { - return read_impl(buf, slot, offset, size, false); - } - - int storage::read_impl( - char* buf - , int slot - , int offset - , int size - , bool fill_zero) - { - TORRENT_ASSERT(buf != 0); + TORRENT_ASSERT(bufs != 0); TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces()); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(offset < m_files.piece_size(slot)); + TORRENT_ASSERT(num_bufs > 0); + + int size = bufs_size(bufs, num_bufs); + TORRENT_ASSERT(size > 0); #ifdef TORRENT_DEBUG @@ -933,11 +1016,13 @@ namespace libtorrent TORRENT_ASSERT(left_to_read >= 0); size_type result = left_to_read; + TORRENT_ASSERT(left_to_read == size); #ifdef TORRENT_DEBUG int counter = 0; #endif + file::iovec_t* current_buf = bufs; int read_bytes; for (;left_to_read > 0; ++file_iter, left_to_read -= read_bytes , buf_pos += read_bytes) @@ -962,7 +1047,10 @@ namespace libtorrent if (file_iter->pad_file) { - std::memset(buf + buf_pos, 0, read_bytes); + file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); + int num_tmp_bufs = copy_bufs(current_buf, file_iter->size, tmp_bufs); + clear_bufs(tmp_bufs, num_tmp_bufs); + advance_bufs(current_buf, file_iter->size); continue; } @@ -978,30 +1066,23 @@ namespace libtorrent size_type pos = in->seek(file_iter->file_base + file_offset, file::begin, ec); if (pos != file_iter->file_base + file_offset || ec) { - if (!fill_zero) - { - set_error(m_save_path / file_iter->path, ec); - return -1; - } - std::memset(buf + buf_pos, 0, size - buf_pos); - return size; + set_error(m_save_path / file_iter->path, ec); + return -1; } file_offset = 0; - int actual_read = int(in->read(buf + buf_pos, read_bytes, ec)); + file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); + int num_tmp_bufs = copy_bufs(current_buf, read_bytes, tmp_bufs); + int actual_read = int(in->readv(tmp_bufs, num_tmp_bufs, ec)); if (read_bytes != actual_read || ec) { // the file was not big enough if (actual_read > 0) buf_pos += actual_read; - if (!fill_zero) - { - set_error(m_save_path / file_iter->path, ec); - return -1; - } - std::memset(buf + buf_pos, 0, size - buf_pos); - return size; + set_error(m_save_path / file_iter->path, ec); + return -1; } + advance_bufs(current_buf, actual_read); } return result; @@ -1013,10 +1094,33 @@ namespace libtorrent , int offset , int size) { - TORRENT_ASSERT(buf != 0); + file::iovec_t b = { (void*)buf, size }; + return writev(&b, slot, offset, 1); + } + + int storage::read( + char* buf + , int slot + , int offset + , int size) + { + file::iovec_t b = { (void*)buf, size }; + return readv(&b, slot, offset, 1); + } + + int storage::writev( + file::iovec_t* bufs + , int slot + , int offset + , int num_bufs) + { + TORRENT_ASSERT(bufs != 0); TORRENT_ASSERT(slot >= 0); TORRENT_ASSERT(slot < m_files.num_pieces()); TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(num_bufs > 0); + + int size = bufs_size(bufs, num_bufs); TORRENT_ASSERT(size > 0); #ifdef TORRENT_DEBUG @@ -1058,6 +1162,7 @@ namespace libtorrent int counter = 0; #endif + file::iovec_t* current_buf = bufs; int write_bytes; for (;left_to_write > 0; ++file_iter, left_to_write -= write_bytes , buf_pos += write_bytes) @@ -1100,8 +1205,10 @@ namespace libtorrent } file_offset = 0; - int actual_written = int(out->write(buf + buf_pos, write_bytes, ec)); - + file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); + int num_tmp_bufs = copy_bufs(current_buf, write_bytes, tmp_bufs); + int actual_written = int(out->writev(tmp_bufs, num_tmp_bufs, ec)); + if (write_bytes != actual_written || ec) { // the file was not big enough @@ -1109,7 +1216,7 @@ namespace libtorrent set_error(m_save_path / file_iter->path, ec); return -1; } - + advance_bufs(current_buf, actual_written); } return size; } @@ -1366,31 +1473,35 @@ namespace libtorrent } int piece_manager::read_impl( - char* buf + file::iovec_t* bufs , int piece_index , int offset - , int size) + , int num_bufs) { - TORRENT_ASSERT(buf); + TORRENT_ASSERT(bufs); TORRENT_ASSERT(offset >= 0); - TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(num_bufs > 0); int slot = slot_for(piece_index); - return m_storage->read(buf, slot, offset, size); + return m_storage->readv(bufs, slot, offset, num_bufs); } int piece_manager::write_impl( - const char* buf + file::iovec_t* bufs , int piece_index , int offset - , int size) + , int num_bufs) { - TORRENT_ASSERT(buf); + TORRENT_ASSERT(bufs); TORRENT_ASSERT(offset >= 0); - TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(num_bufs > 0); TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); + int size = bufs_size(bufs, num_bufs); + + file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs); + std::copy(bufs, bufs + num_bufs, iov); int slot = allocate_slot_for_piece(piece_index); - int ret = m_storage->write(buf, slot, offset, size); + int ret = m_storage->writev(bufs, slot, offset, num_bufs); // only save the partial hash if the write succeeds if (ret != size) return ret; @@ -1403,7 +1514,10 @@ namespace libtorrent partial_hash& ph = m_piece_hasher[piece_index]; TORRENT_ASSERT(ph.offset == 0); ph.offset = size; - ph.h.update(buf, size); + + for (file::iovec_t* i = iov, *end(iov + num_bufs); i < end; ++i) + ph.h.update((char const*)i->iov_base, i->iov_len); + #ifdef TORRENT_PARTIAL_HASH_LOG out << time_now_string() << " NEW [" " s: " << this @@ -1435,8 +1549,11 @@ namespace libtorrent << " entries: " << m_piece_hasher.size() << " ]" << std::endl; #endif - i->second.offset += size; - i->second.h.update(buf, size); + for (file::iovec_t* b = iov, *end(iov + num_bufs); b < end; ++b) + { + i->second.h.update((char const*)b->iov_base, b->iov_len); + i->second.offset += b->iov_len; + } } #ifdef TORRENT_PARTIAL_HASH_LOG else