premiere-libtorrent/src/storage_utils.cpp

188 lines
6.1 KiB
C++
Raw Normal View History

/*
Copyright (c) 2003-2016, 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.
*/
#include "libtorrent/aux_/storage_utils.hpp"
#include "libtorrent/file_storage.hpp"
#include "libtorrent/alloca.hpp"
#include "libtorrent/file.hpp" // for count_bufs
namespace libtorrent
{
int copy_bufs(span<iovec_t const> bufs, int bytes, span<iovec_t> target)
{
int size = 0;
for (int i = 0;; i++)
{
target[i] = bufs[i];
size += int(bufs[i].iov_len);
if (size >= bytes)
{
target[i].iov_len -= size - bytes;
return i + 1;
}
}
}
span<iovec_t> advance_bufs(span<iovec_t> bufs, int bytes)
{
int size = 0;
for (;;)
{
size += int(bufs.front().iov_len);
if (size >= bytes)
{
bufs.front().iov_base = reinterpret_cast<char*>(bufs.front().iov_base)
+ bufs.front().iov_len - (size - bytes);
bufs.front().iov_len = size - bytes;
return bufs;
}
bufs = bufs.subspan(1);
}
}
#if TORRENT_USE_ASSERTS
namespace {
int count_bufs(span<iovec_t const> bufs, int bytes)
{
int size = 0;
int count = 1;
if (bytes == 0) return 0;
for (auto i = bufs.begin();; ++i, ++count)
{
size += int(i->iov_len);
if (size >= bytes) return count;
}
}
}
#endif
// 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, span<iovec_t const> const bufs
, piece_index_t const piece, const int offset, fileop& op
, storage_error& ec)
{
TORRENT_ASSERT(piece >= piece_index_t(0));
TORRENT_ASSERT(piece < files.end_piece());
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(bufs.size() > 0);
const int size = bufs_size(bufs);
TORRENT_ASSERT(size > 0);
// find the file iterator and file offset
std::int64_t const torrent_offset = static_cast<int>(piece) * std::int64_t(files.piece_length()) + offset;
file_index_t 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));
std::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.
int bytes_left = size;
TORRENT_ASSERT(bytes_left >= 0);
// 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())
TORRENT_ALLOCA(current_buf, iovec_t, bufs.size());
copy_bufs(bufs, size, current_buf);
TORRENT_ASSERT(count_bufs(current_buf, size) == int(bufs.size()));
TORRENT_ALLOCA(tmp_buf, iovec_t, bufs.size());
// 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;
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);
// there are no bytes left in this file, move to the next one
// this loop skips over empty files
while (file_bytes_left == 0)
{
++file_index;
file_offset = 0;
TORRENT_ASSERT(file_index < files.end_file());
// 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.end_file()) 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);
}
// make a copy of the iovec array that _just_ covers the next
// file_bytes_left bytes, i.e. just this one operation
int tmp_bufs_used = copy_bufs(current_buf, file_bytes_left, tmp_buf);
int bytes_transferred = op.file_op(file_index, file_offset
, tmp_buf.first(tmp_bufs_used), ec);
if (ec) return -1;
// advance our position in the iovec array and the file offset.
current_buf = advance_bufs(current_buf, bytes_transferred);
bytes_left -= bytes_transferred;
file_offset += bytes_transferred;
TORRENT_ASSERT(count_bufs(current_buf, bytes_left) <= int(bufs.size()));
// 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
// a short-read as an error
ec.file(file_index);
}
return size - bytes_left;
}
}
return size;
}
}