premiere-libtorrent/src/storage.cpp

1842 lines
46 KiB
C++
Raw Normal View History

/*
2003-11-29 02:54:41 +01:00
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
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.
*/
2003-12-14 23:55:32 +01:00
#include <ios>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
2004-01-16 17:36:09 +01:00
#include <boost/filesystem/fstream.hpp>
2003-10-30 00:28:09 +01:00
#include <boost/thread/mutex.hpp>
2003-11-26 15:11:25 +01:00
#include <boost/ref.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/storage.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/hasher.hpp"
2003-10-30 00:28:09 +01:00
#include "libtorrent/session.hpp"
2003-12-07 06:53:04 +01:00
#include "libtorrent/peer_id.hpp"
#include "libtorrent/file.hpp"
#include "libtorrent/invariant_check.hpp"
2003-11-09 19:17:09 +01:00
#if defined(_MSC_VER)
#define for if (false) {} else for
#endif
/*
namespace
{
2003-11-26 15:11:25 +01:00
struct lazy_hash
{
2003-12-07 06:53:04 +01:00
mutable libtorrent::sha1_hash digest;
mutable libtorrent::hasher h;
2003-11-26 15:11:25 +01:00
mutable const char* data;
std::size_t size;
lazy_hash(const char* data_, std::size_t size_)
: data(data_)
, size(size_)
2004-01-25 05:18:08 +01:00
{
2004-01-25 13:37:15 +01:00
assert(data_ != 0);
assert(size_ > 0);
2004-01-25 05:18:08 +01:00
}
2003-11-26 15:11:25 +01:00
2003-12-07 06:53:04 +01:00
const libtorrent::sha1_hash& get() const
2003-11-26 15:11:25 +01:00
{
if (data)
{
h.update(data, size);
digest = h.final();
data = 0;
}
return digest;
}
};
} // namespace unnamed
*/
2003-12-07 02:26:57 +01:00
namespace fs = boost::filesystem;
namespace
{
2003-12-07 15:12:14 +01:00
void print_to_log(const std::string& s)
{
static std::ofstream log("log.txt");
log << s;
2003-12-10 01:24:16 +01:00
log.flush();
2003-12-07 15:12:14 +01:00
}
}
2004-01-17 21:04:19 +01:00
namespace libtorrent
{
std::vector<size_type> get_filesizes(
const torrent_info& t
, const boost::filesystem::path& p)
{
std::vector<size_type> sizes;
for (torrent_info::file_iterator i = t.begin_files();
i != t.end_files();
++i)
{
2004-01-18 11:22:18 +01:00
size_type file_size;
try
{
file f(p / i->path / i->filename, file::in);
f.seek(0, file::end);
file_size = f.tell();
}
catch (file_error&)
{
file_size = 0;
}
sizes.push_back(file_size);
2004-01-17 21:04:19 +01:00
}
return sizes;
}
bool match_filesizes(
const torrent_info& t
, const boost::filesystem::path& p
, const std::vector<size_type>& sizes)
{
2004-01-26 11:29:00 +01:00
if ((int)sizes.size() != t.num_files()) return false;
2004-01-17 21:04:19 +01:00
std::vector<size_type>::const_iterator s = sizes.begin();
for (torrent_info::file_iterator i = t.begin_files();
i != t.end_files();
++i, ++s)
{
2004-01-18 11:22:18 +01:00
size_type file_size;
try
{
file f(p / i->path / i->filename, file::in);
f.seek(0, file::end);
file_size = f.tell();
}
catch (file_error&)
{
file_size = 0;
}
if (file_size != *s) return false;
2004-01-17 21:04:19 +01:00
}
return true;
}
2003-12-07 02:26:57 +01:00
2003-12-07 17:26:16 +01:00
struct thread_safe_storage
{
thread_safe_storage(std::size_t n)
: slots(n, false)
2004-01-25 13:37:15 +01:00
{}
2003-12-07 17:26:16 +01:00
boost::mutex mutex;
boost::condition condition;
std::vector<bool> slots;
};
struct slot_lock
{
slot_lock(thread_safe_storage& s, int slot_)
: storage_(s)
, slot(slot_)
{
2004-01-25 13:37:15 +01:00
assert(slot_>=0 && slot_ < (int)s.slots.size());
2003-12-07 17:26:16 +01:00
boost::mutex::scoped_lock lock(storage_.mutex);
while (storage_.slots[slot])
storage_.condition.wait(lock);
storage_.slots[slot] = true;
}
~slot_lock()
{
storage_.slots[slot] = false;
storage_.condition.notify_all();
}
thread_safe_storage& storage_;
int slot;
};
class storage::impl : public thread_safe_storage
2003-12-07 17:26:16 +01:00
{
public:
2003-12-09 09:49:49 +01:00
impl(const torrent_info& info, const fs::path& path)
2003-12-07 17:26:16 +01:00
: thread_safe_storage(info.num_pieces())
, info(info)
, save_path(path)
{}
2003-12-09 09:49:49 +01:00
impl(const impl& x)
2003-12-07 17:26:16 +01:00
: thread_safe_storage(x.info.num_pieces())
, info(x.info)
, save_path(x.save_path)
{}
const torrent_info& info;
const boost::filesystem::path save_path;
};
2003-12-07 02:26:57 +01:00
storage::storage(const torrent_info& info, const fs::path& path)
2003-12-09 09:49:49 +01:00
: m_pimpl(new impl(info, path))
2003-12-07 06:53:04 +01:00
{
assert(info.begin_files() != info.end_files());
}
2003-12-07 02:26:57 +01:00
2003-12-07 17:26:16 +01:00
void storage::swap(storage& other)
{
2003-12-09 09:49:49 +01:00
m_pimpl.swap(other.m_pimpl);
2003-12-07 17:26:16 +01:00
}
2004-01-27 00:38:08 +01:00
#ifndef NDEBUG
void storage::shuffle()
{
int num_pieces = m_pimpl->info.num_pieces();
std::vector<int> pieces(num_pieces);
for (std::vector<int>::iterator i = pieces.begin();
i != pieces.end();
++i)
{
2004-01-28 12:37:46 +01:00
*i = static_cast<int>(i - pieces.begin());
2004-01-27 00:38:08 +01:00
}
std::srand((unsigned int)std::time(0));
2004-01-27 00:38:08 +01:00
std::vector<int> targets(pieces);
std::random_shuffle(pieces.begin(), pieces.end());
std::random_shuffle(targets.begin(), targets.end());
2004-02-01 18:26:09 +01:00
for (int i = 0; i < std::max(num_pieces / 50, 1); ++i)
2004-01-27 00:38:08 +01:00
{
const int slot_index = targets[i];
const int piece_index = pieces[i];
const int slot_size = m_pimpl->info.piece_size(slot_index);
std::vector<char> buf(slot_size);
read(&buf[0], piece_index, 0, slot_size);
write(&buf[0], slot_index, 0, slot_size);
}
}
#endif
size_type storage::read(
2003-12-07 02:26:57 +01:00
char* buf
, int slot
, int offset
, int size)
2003-12-07 02:26:57 +01:00
{
assert(buf != 0);
2004-01-25 05:18:08 +01:00
assert(slot >= 0 && slot < m_pimpl->info.num_pieces());
2003-12-17 17:37:20 +01:00
assert(offset >= 0);
assert(offset < m_pimpl->info.piece_size(slot));
2003-12-07 15:12:14 +01:00
assert(size > 0);
2003-12-07 17:26:16 +01:00
slot_lock lock(*m_pimpl, slot);
2003-12-09 09:49:49 +01:00
2003-12-07 17:26:16 +01:00
size_type start = slot * m_pimpl->info.piece_length() + offset;
2003-12-07 02:26:57 +01:00
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
2003-12-07 02:26:57 +01:00
2003-12-07 17:26:16 +01:00
for (file_iter = m_pimpl->info.begin_files();;)
2003-12-07 02:26:57 +01:00
{
if (file_offset < file_iter->size)
break;
file_offset -= file_iter->size;
++file_iter;
}
2004-01-16 03:57:45 +01:00
file in(
m_pimpl->save_path / file_iter->path / file_iter->filename
, file::in);
2003-12-07 02:26:57 +01:00
assert(file_offset < file_iter->size);
2004-01-16 03:57:45 +01:00
in.seek(file_offset);
if (in.tell() != file_offset)
{
// the file was not big enough
throw file_error("slot has no storage");
}
2003-12-07 15:12:14 +01:00
2004-01-16 14:07:47 +01:00
#ifndef NDEBUG
2004-01-16 16:18:02 +01:00
size_type in_tell = in.tell();
assert(in_tell == file_offset);
2004-01-16 14:07:47 +01:00
#endif
2003-12-07 02:26:57 +01:00
int left_to_read = size;
int slot_size = m_pimpl->info.piece_size(slot);
2003-12-07 02:26:57 +01:00
if (offset + left_to_read > slot_size)
left_to_read = slot_size - offset;
assert(left_to_read >= 0);
size_type result = left_to_read;
2003-12-07 02:26:57 +01:00
int buf_pos = 0;
while (left_to_read > 0)
{
int read_bytes = left_to_read;
if (file_offset + read_bytes > file_iter->size)
read_bytes = static_cast<int>(file_iter->size - file_offset);
2003-12-07 02:26:57 +01:00
assert(read_bytes > 0);
2004-01-16 03:57:45 +01:00
// in.read(buf + buf_pos, read_bytes);
// int actual_read = in.gcount();
size_type actual_read = in.read(buf + buf_pos, read_bytes);
2003-12-07 02:26:57 +01:00
if (read_bytes != actual_read)
{
// the file was not big enough
throw file_error("slot has no storage");
}
2003-12-07 02:26:57 +01:00
left_to_read -= read_bytes;
buf_pos += read_bytes;
assert(buf_pos >= 0);
file_offset += read_bytes;
if (left_to_read > 0)
{
2003-12-07 02:26:57 +01:00
++file_iter;
2003-12-07 17:26:16 +01:00
fs::path path = m_pimpl->save_path / file_iter->path / file_iter->filename;
2003-12-07 02:26:57 +01:00
file_offset = 0;
2004-01-16 03:57:45 +01:00
in.open(path, file::in);
}
2003-12-07 02:26:57 +01:00
}
return result;
}
2004-01-26 11:29:00 +01:00
// throws file_error if it fails to write
void storage::write(
const char* buf
, int slot
, int offset
, int size)
2003-12-07 02:26:57 +01:00
{
2004-01-25 13:37:15 +01:00
assert(buf != 0);
assert(slot >= 0);
assert(slot < m_pimpl->info.num_pieces());
2004-01-25 05:18:08 +01:00
assert(offset >= 0);
2003-12-07 15:12:14 +01:00
assert(size > 0);
2003-12-07 17:26:16 +01:00
slot_lock lock(*m_pimpl, slot);
size_type start = slot * m_pimpl->info.piece_length() + offset;
2003-12-07 02:26:57 +01:00
// find the file iterator and file offset
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
2003-12-07 02:26:57 +01:00
2003-12-07 17:26:16 +01:00
for (file_iter = m_pimpl->info.begin_files();;)
2003-12-07 02:26:57 +01:00
{
if (file_offset < file_iter->size)
break;
file_offset -= file_iter->size;
++file_iter;
}
2003-12-07 17:26:16 +01:00
fs::path path(m_pimpl->save_path / file_iter->path / file_iter->filename);
2004-01-16 03:57:45 +01:00
file out(path, file::out);
2003-12-07 02:26:57 +01:00
assert(file_offset < file_iter->size);
2004-01-16 03:57:45 +01:00
out.seek(file_offset);
2003-12-07 15:12:14 +01:00
2004-01-26 11:29:00 +01:00
if (out.tell() != file_offset)
{
std::stringstream s;
s << "no storage for slot " << slot;
throw file_error(s.str());
}
2003-12-07 02:26:57 +01:00
int left_to_write = size;
int slot_size = m_pimpl->info.piece_size(slot);
2003-12-07 02:26:57 +01:00
if (offset + left_to_write > slot_size)
left_to_write = slot_size - offset;
assert(left_to_write >= 0);
int buf_pos = 0;
while (left_to_write > 0)
{
int write_bytes = left_to_write;
if (file_offset + write_bytes > file_iter->size)
{
2003-12-07 02:26:57 +01:00
assert(file_iter->size > file_offset);
write_bytes = static_cast<int>(file_iter->size - file_offset);
2003-12-07 02:26:57 +01:00
}
assert(buf_pos >= 0);
assert(write_bytes > 0);
2004-01-26 11:29:00 +01:00
size_type written = out.write(buf + buf_pos, write_bytes);
if (written != write_bytes)
{
std::stringstream s;
s << "no storage for slot " << slot;
throw file_error(s.str());
}
2003-12-07 02:26:57 +01:00
left_to_write -= write_bytes;
buf_pos += write_bytes;
assert(buf_pos >= 0);
file_offset += write_bytes;
assert(file_offset <= file_iter->size);
if (left_to_write > 0)
{
++file_iter;
2003-12-07 17:26:16 +01:00
assert(file_iter != m_pimpl->info.end_files());
2003-12-07 02:26:57 +01:00
2003-12-07 17:26:16 +01:00
fs::path path = m_pimpl->save_path / file_iter->path / file_iter->filename;
2003-12-07 02:26:57 +01:00
file_offset = 0;
2004-01-16 03:57:45 +01:00
/*
2003-12-07 02:26:57 +01:00
out.close();
out.clear();
2003-12-07 15:12:14 +01:00
if (fs::exists(path))
out.open(path, std::ios_base::binary | std::ios_base::in);
else
out.open(path, std::ios_base::binary);
2004-01-16 03:57:45 +01:00
*/
out.open(path, file::out);
}
}
2003-12-09 09:49:49 +01:00
}
2003-12-07 02:26:57 +01:00
2003-12-09 09:49:49 +01:00
// -- piece_manager -----------------------------------------------------
class piece_manager::impl
{
friend class invariant_access;
2003-12-09 09:49:49 +01:00
public:
impl(
const torrent_info& info
, const boost::filesystem::path& path);
void check_pieces(
boost::mutex& mutex
, detail::piece_checker_data& data
, std::vector<bool>& pieces);
void allocate_slots(int num_slots);
2004-01-12 04:05:10 +01:00
void mark_failed(int index);
2004-01-24 18:14:03 +01:00
unsigned long piece_crc(
int slot_index
, int block_size
, const std::bitset<256>& bitmask);
int slot_for_piece(int piece_index) const;
size_type read(
char* buf
, int piece_index
, int offset
, int size);
void write(
const char* buf
, int piece_index
, int offset
, int size);
2003-12-09 09:49:49 +01:00
const boost::filesystem::path& save_path() const
{ return m_save_path; }
2003-12-27 02:34:50 +01:00
void export_piece_map(std::vector<int>& p) const;
2003-12-25 13:11:31 +01:00
2003-12-09 09:49:49 +01:00
private:
// returns the slot currently associated with the given
// piece or assigns the given piece_index to a free slot
2004-01-24 20:19:17 +01:00
int identify_data(
const std::vector<char>& piece_data
, int current_slot
, std::vector<bool>& have_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece);
2004-01-24 18:14:03 +01:00
int allocate_slot_for_piece(int piece_index);
2004-01-17 21:04:19 +01:00
#ifndef NDEBUG
2003-12-09 09:49:49 +01:00
void check_invariant() const;
2004-01-24 18:14:03 +01:00
#ifdef TORRENT_STORAGE_DEBUG
2003-12-09 09:49:49 +01:00
void debug_log() const;
2004-01-24 18:14:03 +01:00
#endif
2004-01-17 21:04:19 +01:00
#endif
2003-12-09 09:49:49 +01:00
storage m_storage;
// a bitmask representing the pieces we have
std::vector<bool> m_have_piece;
const torrent_info& m_info;
// slots that hasn't had any file storage allocated
std::vector<int> m_unallocated_slots;
// slots that has file storage, but isn't assigned to a piece
std::vector<int> m_free_slots;
enum
{
has_no_slot = -3 // the piece has no storage
};
// maps piece indices to slots. If a piece doesn't
// have any storage, it is set to 'has_no_slot'
std::vector<int> m_piece_to_slot;
2004-01-16 17:36:09 +01:00
enum
{
unallocated = -1, // the slot is unallocated
unassigned = -2 // the slot is allocated but not assigned to a piece
2004-01-16 17:19:27 +01:00
};
2004-01-16 17:36:09 +01:00
// maps slots to piece indices, if a slot doesn't have a piece
// it can either be 'unassigned' or 'unallocated'
2003-12-09 09:49:49 +01:00
std::vector<int> m_slot_to_piece;
boost::filesystem::path m_save_path;
mutable boost::recursive_mutex m_mutex;
bool m_allocating;
boost::mutex m_allocating_monitor;
boost::condition m_allocating_condition;
};
piece_manager::impl::impl(
2003-12-07 02:26:57 +01:00
const torrent_info& info
2003-12-09 09:49:49 +01:00
, const fs::path& save_path)
2003-12-07 02:26:57 +01:00
: m_storage(info, save_path)
, m_info(info)
2003-12-07 06:53:04 +01:00
, m_save_path(save_path)
, m_allocating(false)
2003-12-07 02:26:57 +01:00
{
}
2003-12-09 09:49:49 +01:00
piece_manager::piece_manager(
const torrent_info& info
, const fs::path& save_path)
: m_pimpl(new impl(info, save_path))
{
}
2004-01-04 13:54:38 +01:00
piece_manager::~piece_manager()
{
}
2003-12-25 13:11:31 +01:00
void piece_manager::impl::export_piece_map(
2003-12-27 02:34:50 +01:00
std::vector<int>& p) const
2003-12-25 13:11:31 +01:00
{
2004-01-12 04:05:10 +01:00
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2004-01-25 22:37:19 +01:00
INVARIANT_CHECK;
2003-12-25 13:11:31 +01:00
p.clear();
2003-12-27 02:34:50 +01:00
std::vector<int>::const_reverse_iterator last;
for (last = m_slot_to_piece.rbegin();
last != m_slot_to_piece.rend();
++last)
{
2004-01-16 17:19:27 +01:00
if (*last != unallocated) break;
2003-12-27 02:34:50 +01:00
}
for (std::vector<int>::const_iterator i =
m_slot_to_piece.begin();
i != last.base();
2003-12-25 13:11:31 +01:00
++i)
{
2003-12-27 02:34:50 +01:00
p.push_back(*i);
2003-12-25 13:11:31 +01:00
}
}
void piece_manager::export_piece_map(
2003-12-27 02:34:50 +01:00
std::vector<int>& p) const
2003-12-25 13:11:31 +01:00
{
m_pimpl->export_piece_map(p);
}
2004-01-12 04:05:10 +01:00
2004-01-13 04:08:59 +01:00
void piece_manager::impl::mark_failed(int piece_index)
2004-01-12 04:05:10 +01:00
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2004-01-25 22:37:19 +01:00
INVARIANT_CHECK;
2004-01-25 13:37:15 +01:00
assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
2004-01-13 04:08:59 +01:00
assert(m_piece_to_slot[piece_index] >= 0);
int slot_index = m_piece_to_slot[piece_index];
assert(slot_index >= 0);
2004-01-12 04:05:10 +01:00
2004-01-16 17:19:27 +01:00
m_slot_to_piece[slot_index] = unassigned;
m_piece_to_slot[piece_index] = has_no_slot;
2004-01-13 04:08:59 +01:00
m_free_slots.push_back(slot_index);
2004-01-12 04:05:10 +01:00
}
void piece_manager::mark_failed(int index)
{
m_pimpl->mark_failed(index);
}
2004-01-24 18:14:03 +01:00
int piece_manager::slot_for_piece(int piece_index) const
{
return m_pimpl->slot_for_piece(piece_index);
}
int piece_manager::impl::slot_for_piece(int piece_index) const
{
assert(piece_index >= 0 && piece_index < m_info.num_pieces());
return m_piece_to_slot[piece_index];
}
unsigned long piece_manager::piece_crc(
int index
, int block_size
, const std::bitset<256>& bitmask)
{
return m_pimpl->piece_crc(index, block_size, bitmask);
}
unsigned long piece_manager::impl::piece_crc(
int slot_index
, int block_size
, const std::bitset<256>& bitmask)
{
2004-01-25 13:37:15 +01:00
assert(slot_index >= 0);
assert(slot_index < m_info.num_pieces());
assert(block_size > 0);
2004-01-25 05:18:08 +01:00
2004-01-24 18:14:03 +01:00
adler32_crc crc;
std::vector<char> buf(block_size);
int num_blocks = m_info.piece_size(slot_index) / block_size;
int last_block_size = m_info.piece_size(slot_index) % block_size;
if (last_block_size == 0) last_block_size = block_size;
for (int i = 0; i < num_blocks-1; ++i)
{
if (!bitmask[i]) continue;
m_storage.read(
&buf[0]
, slot_index
, i * block_size
, block_size);
crc.update(&buf[0], block_size);
}
if (bitmask[num_blocks - 1])
{
m_storage.read(
&buf[0]
, slot_index
, block_size * (num_blocks - 1)
, last_block_size);
crc.update(&buf[0], last_block_size);
}
return crc.final();
}
2004-01-12 04:05:10 +01:00
size_type piece_manager::impl::read(
2003-12-07 06:53:04 +01:00
char* buf
2003-12-09 09:49:49 +01:00
, int piece_index
, int offset
, int size)
2003-12-07 02:26:57 +01:00
{
2004-01-24 20:19:17 +01:00
assert(buf);
assert(offset >= 0);
assert(size > 0);
2004-01-25 13:37:15 +01:00
assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
assert(m_piece_to_slot[piece_index] >= 0 && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size());
2003-12-07 02:26:57 +01:00
int slot = m_piece_to_slot[piece_index];
2004-01-25 13:37:15 +01:00
assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
2003-12-07 02:26:57 +01:00
return m_storage.read(buf, slot, offset, size);
}
size_type piece_manager::read(
2003-12-09 09:49:49 +01:00
char* buf
, int piece_index
, int offset
, int size)
2003-12-09 09:49:49 +01:00
{
return m_pimpl->read(buf, piece_index, offset, size);
}
void piece_manager::impl::write(
2003-12-07 06:53:04 +01:00
const char* buf
2003-12-09 09:49:49 +01:00
, int piece_index
, int offset
, int size)
2003-12-07 02:26:57 +01:00
{
2004-01-24 20:19:17 +01:00
assert(buf);
assert(offset >= 0);
assert(size > 0);
2004-01-25 13:37:15 +01:00
assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
2004-01-24 18:14:03 +01:00
int slot = allocate_slot_for_piece(piece_index);
2004-01-25 13:37:15 +01:00
assert(slot >= 0 && slot < (int)m_slot_to_piece.size());
2003-12-07 02:26:57 +01:00
m_storage.write(buf, slot, offset, size);
}
2003-12-09 09:49:49 +01:00
void piece_manager::write(
const char* buf
, int piece_index
, int offset
, int size)
2003-12-09 09:49:49 +01:00
{
m_pimpl->write(buf, piece_index, offset, size);
}
/*
2003-12-09 09:49:49 +01:00
void piece_manager::impl::check_pieces(
2003-12-07 06:53:04 +01:00
boost::mutex& mutex
2003-12-09 09:49:49 +01:00
, detail::piece_checker_data& data
, std::vector<bool>& pieces)
2003-12-07 02:26:57 +01:00
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2004-01-24 20:19:17 +01:00
#ifndef NDEBUG
check_invariant();
#endif
2003-12-08 09:55:24 +01:00
m_allocating = false;
2004-01-16 17:19:27 +01:00
m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot);
m_slot_to_piece.resize(m_info.num_pieces(), unallocated);
2004-01-25 13:37:15 +01:00
m_free_slots.clear();
m_unallocated_slots.clear();
2003-12-07 02:26:57 +01:00
const std::size_t piece_size = m_info.piece_length();
const std::size_t last_piece_size = m_info.piece_size(
m_info.num_pieces() - 1);
2004-01-17 21:04:19 +01:00
2004-01-03 03:10:11 +01:00
// if we have fast-resume info
2004-01-17 21:04:19 +01:00
// use it instead of doing the actual checking
2004-01-03 03:10:11 +01:00
if (!data.piece_map.empty()
&& data.piece_map.size() <= m_slot_to_piece.size())
{
2004-01-25 13:37:15 +01:00
for (int i = 0; i < (int)data.piece_map.size(); ++i)
2004-01-03 03:10:11 +01:00
{
m_slot_to_piece[i] = data.piece_map[i];
if (data.piece_map[i] >= 0)
{
m_piece_to_slot[data.piece_map[i]] = i;
int found_piece = data.piece_map[i];
// if the piece is not in the unfinished list
// we have all of it
if (std::find_if(
data.unfinished_pieces.begin()
, data.unfinished_pieces.end()
, piece_picker::has_index(found_piece))
== data.unfinished_pieces.end())
{
pieces[found_piece] = true;
}
}
2004-01-16 17:19:27 +01:00
else if (data.piece_map[i] == unassigned)
2004-01-03 03:10:11 +01:00
{
m_free_slots.push_back(i);
}
else
{
2004-01-16 17:19:27 +01:00
assert(data.piece_map[i] == unallocated);
2004-01-03 03:10:11 +01:00
m_unallocated_slots.push_back(i);
}
}
2004-01-25 13:37:15 +01:00
for (int i = data.piece_map.size(); i < (int)pieces.size(); ++i)
2004-01-03 03:10:11 +01:00
{
m_unallocated_slots.push_back(i);
}
2004-01-24 20:19:17 +01:00
#ifndef NDEBUG
check_invariant();
#endif
2004-01-03 03:10:11 +01:00
return;
}
2004-01-17 21:04:19 +01:00
// do the real check (we didn't have valid resume data)
2003-12-07 02:26:57 +01:00
bool changed_file = true;
2004-01-16 03:57:45 +01:00
file in;
2003-12-07 02:26:57 +01:00
std::vector<char> piece_data(m_info.piece_length());
std::size_t piece_offset = 0;
2003-12-11 04:32:20 +01:00
int current_slot = 0;
2004-01-24 20:19:17 +01:00
std::size_t bytes_to_read = m_info.piece_size(0);
2004-01-16 17:19:27 +01:00
size_type bytes_current_read = 0;
size_type seek_into_next = 0;
2004-01-16 04:58:24 +01:00
size_type filesize = 0;
size_type start_of_read = 0;
size_type start_of_file = 0;
{
2003-12-07 02:26:57 +01:00
boost::mutex::scoped_lock lock(mutex);
2003-12-07 06:53:04 +01:00
data.progress = 0.f;
2003-12-07 02:26:57 +01:00
}
for (torrent_info::file_iterator file_iter = m_info.begin_files(),
end_iter = m_info.end_files();
file_iter != end_iter;)
{
assert(current_slot >= 0 && current_slot < m_info.num_pieces());
2004-01-24 20:19:17 +01:00
// Update progress meter and check if we've been requested to abort
2003-12-07 02:26:57 +01:00
{
boost::mutex::scoped_lock lock(mutex);
2003-12-11 04:32:20 +01:00
data.progress = (float)current_slot / m_info.num_pieces();
2003-12-07 06:53:04 +01:00
if (data.abort)
2003-12-07 02:26:57 +01:00
return;
}
fs::path path(m_save_path / file_iter->path);
// if the path doesn't exist, create the
// entire directory tree
if (!fs::exists(path))
fs::create_directories(path);
path /= file_iter->filename;
if (changed_file)
{
2004-01-16 03:57:45 +01:00
try
{
changed_file = false;
bytes_current_read = seek_into_next;
in.open(path, file::in);
in.seek(0, file::end);
filesize = in.tell();
in.seek(seek_into_next);
}
catch (file_error&)
{
filesize = 0;
}
2003-12-07 02:26:57 +01:00
}
// we are at the start of a new piece
// so we store the start of the piece
2003-12-11 04:32:20 +01:00
if (bytes_to_read == m_info.piece_size(current_slot))
2004-01-16 17:19:27 +01:00
start_of_read = current_slot * (size_type)piece_size;
2003-12-07 02:26:57 +01:00
std::size_t bytes_read = 0;
if (filesize > 0)
{
2004-01-16 03:57:45 +01:00
bytes_read = in.read(&piece_data[piece_offset], bytes_to_read);
2004-01-24 20:19:17 +01:00
assert(bytes_read>0);
2003-12-07 02:26:57 +01:00
}
bytes_current_read += bytes_read;
bytes_to_read -= bytes_read;
assert(bytes_to_read >= 0);
// bytes left to read, go on with next file
if (bytes_to_read > 0)
{
2003-12-07 02:26:57 +01:00
if (bytes_current_read != file_iter->size)
{
2004-01-16 04:58:24 +01:00
size_type pos;
size_type file_end = start_of_file + file_iter->size;
2003-12-07 02:26:57 +01:00
for (pos = start_of_read; pos < file_end;
pos += piece_size)
{
2003-12-11 04:32:20 +01:00
m_unallocated_slots.push_back(current_slot);
++current_slot;
2003-12-07 02:26:57 +01:00
}
seek_into_next = pos - file_end;
if (current_slot >= m_info.num_pieces())
{
break;
}
2003-12-11 04:32:20 +01:00
bytes_to_read = m_info.piece_size(current_slot);
2003-12-07 02:26:57 +01:00
piece_offset = 0;
}
else
{
seek_into_next = 0;
piece_offset += bytes_read;
}
changed_file = true;
start_of_file += file_iter->size;
++file_iter;
continue;
}
2004-01-24 20:19:17 +01:00
assert(current_slot < m_info.num_pieces());
2004-01-25 13:37:15 +01:00
assert(m_slot_to_piece[current_slot] == unallocated);
assert(m_piece_to_slot[current_slot] == has_no_slot ||
(m_piece_to_slot[current_slot] >= 0 &&
m_piece_to_slot[current_slot] < current_slot &&
m_slot_to_piece[m_piece_to_slot[current_slot]] == current_slot));
2004-01-24 20:19:17 +01:00
2003-12-07 02:26:57 +01:00
// we need to take special actions if this is
// the last piece, since that piece might actually
// be smaller than piece_size.
lazy_hash large_digest(&piece_data[0], piece_size);
lazy_hash small_digest(&piece_data[0], last_piece_size);
const lazy_hash* digest[2] = {
&large_digest, &small_digest
};
2003-12-07 16:03:06 +01:00
int found_piece = -1;
2003-12-07 02:26:57 +01:00
2003-12-11 04:32:20 +01:00
for (int i = current_slot; i < m_info.num_pieces(); ++i)
{
2003-12-14 06:56:12 +01:00
if (pieces[i] && i != current_slot) continue;
2003-12-07 02:26:57 +01:00
const sha1_hash& hash = digest[
2004-01-24 20:19:17 +01:00
(i == m_info.num_pieces() - 1) ? 1 : 0]->get();
2003-12-07 02:26:57 +01:00
2003-12-07 16:03:06 +01:00
if (hash == m_info.hash_for_piece(i))
2003-12-21 18:28:27 +01:00
{
2003-12-07 16:03:06 +01:00
found_piece = i;
2003-12-21 18:28:27 +01:00
if (i == current_slot) break;
}
2003-12-07 02:26:57 +01:00
}
2003-12-07 16:03:06 +01:00
if (found_piece != -1)
{
2004-01-03 03:10:11 +01:00
// if we have found this piece hash once already
// move it to the free pieces and don't decrease
// bytes_left
2003-12-14 06:56:12 +01:00
if (pieces[found_piece])
{
2004-01-24 20:19:17 +01:00
assert(m_piece_to_slot[found_piece] == current_slot);
2004-01-16 17:19:27 +01:00
m_slot_to_piece[m_piece_to_slot[found_piece]] = unassigned;
2003-12-14 06:56:12 +01:00
m_free_slots.push_back(m_piece_to_slot[found_piece]);
2004-01-25 13:37:15 +01:00
m_piece_to_slot[found_piece] = has_no_slot;
2003-12-14 06:56:12 +01:00
}
2003-12-07 16:03:06 +01:00
2004-01-25 13:37:15 +01:00
assert(m_piece_to_slot[found_piece] == has_no_slot);
2003-12-14 06:56:12 +01:00
m_piece_to_slot[found_piece] = current_slot;
m_slot_to_piece[current_slot] = found_piece;
pieces[found_piece] = true;
2003-12-07 16:03:06 +01:00
}
else
2003-12-07 02:26:57 +01:00
{
2004-01-25 13:37:15 +01:00
assert(found_piece == -1);
2004-01-16 17:19:27 +01:00
m_slot_to_piece[current_slot] = unassigned;
2003-12-07 02:26:57 +01:00
2003-12-11 04:32:20 +01:00
m_free_slots.push_back(current_slot);
}
2003-12-07 02:26:57 +01:00
2004-01-25 13:37:15 +01:00
assert(m_slot_to_piece[current_slot] != unallocated);
2004-01-24 20:19:17 +01:00
2003-12-07 02:26:57 +01:00
// done with piece, move on to next
piece_offset = 0;
2003-12-11 04:32:20 +01:00
++current_slot;
2004-01-25 13:37:15 +01:00
if (current_slot == m_info.num_pieces())
2004-01-24 20:19:17 +01:00
{
assert(file_iter == end_iter-1);
break;
}
2003-12-11 04:32:20 +01:00
bytes_to_read = m_info.piece_size(current_slot);
2003-12-07 02:26:57 +01:00
}
2004-01-25 03:59:27 +01:00
// dirty "fix" for a bug when file is corrupt
for (int i = 0; i < (int)m_info.num_pieces(); ++i)
2004-01-25 03:59:27 +01:00
{
2004-01-25 13:37:15 +01:00
if(m_piece_to_slot[i] != has_no_slot
&& m_piece_to_slot[i] != i
&& m_slot_to_piece[i] != unallocated)
2004-01-25 03:59:27 +01:00
{
2004-01-25 13:37:15 +01:00
assert(m_piece_to_slot[i] >= 0);
assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
assert(m_slot_to_piece[m_piece_to_slot[i]] == i);
2004-01-25 03:59:27 +01:00
if(m_slot_to_piece[i]!=unassigned)
{
2004-01-25 13:37:15 +01:00
assert(m_slot_to_piece[i] >= 0);
assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
assert(m_piece_to_slot[m_slot_to_piece[i]] == i);
m_piece_to_slot[m_slot_to_piece[i]] = has_no_slot;
m_slot_to_piece[i] = unassigned;
2004-01-25 03:59:27 +01:00
m_free_slots.push_back(i);
}
2004-01-25 13:37:15 +01:00
m_slot_to_piece[m_piece_to_slot[i]] = unassigned;
2004-01-25 03:59:27 +01:00
m_free_slots.push_back(m_piece_to_slot[i]);
2004-01-25 13:37:15 +01:00
m_piece_to_slot[i] = has_no_slot;
2004-01-25 03:59:27 +01:00
}
}
2004-01-17 21:04:19 +01:00
#ifndef NDEBUG
std::stringstream s;
2003-12-07 02:26:57 +01:00
2004-01-17 21:04:19 +01:00
s << " m_free_slots: " << m_free_slots.size() << "\n"
" m_unallocated_slots: " << m_unallocated_slots.size() << "\n"
" num pieces: " << m_info.num_pieces() << "\n"
" have_pieces:\n";
for (std::vector<bool>::iterator i = pieces.begin();
i != pieces.end();
++i)
{
if (((i - pieces.begin()) % 60) == 0) s << "\n";
if (*i) s << "1"; else s << "0";
}
s << "\n";
s << std::count(pieces.begin(), pieces.end(), true) << "\n";
data.torrent_ptr->debug_log(s.str());
#endif
2003-12-07 02:26:57 +01:00
2004-01-17 21:04:19 +01:00
#ifndef NDEBUG
2003-12-07 02:26:57 +01:00
check_invariant();
2004-01-17 21:04:19 +01:00
#endif
2003-12-07 02:26:57 +01:00
}
*/
int piece_manager::impl::identify_data(
const std::vector<char>& piece_data
, int current_slot
, std::vector<bool>& have_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece)
{
2004-01-27 00:38:08 +01:00
INVARIANT_CHECK;
2004-01-26 11:29:00 +01:00
assert((int)have_pieces.size() == m_info.num_pieces());
const int piece_size = m_info.piece_length();
const int last_piece_size = m_info.piece_size(
m_info.num_pieces() - 1);
assert((int)piece_data.size() >= last_piece_size);
// calculate a small digest, with the same
// size as the last piece. And a large digest
// which has the same size as a normal piece
hasher small_digest;
small_digest.update(&piece_data[0], last_piece_size);
hasher large_digest(small_digest);
large_digest.update(
&piece_data[last_piece_size]
, piece_size - last_piece_size);
sha1_hash large_hash = large_digest.final();
sha1_hash small_hash = small_digest.final();
typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
map_iter begin1;
map_iter end1;
map_iter begin2;
map_iter end2;
// makes the lookups for the small digest and the large digest
boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash);
boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash);
// copy all potential piece indices into this vector
std::vector<int> matching_pieces;
for (map_iter i = begin1; i != end1; ++i)
matching_pieces.push_back(i->second);
for (map_iter i = begin2; i != end2; ++i)
matching_pieces.push_back(i->second);
// no piece matched the data in the slot
if (matching_pieces.empty())
return unassigned;
// ------------------------------------------
// CHECK IF THE PIECE IS IN ITS CORRECT PLACE
// ------------------------------------------
if (std::find(
matching_pieces.begin()
, matching_pieces.end()
, current_slot) != matching_pieces.end())
{
const int piece_index = current_slot;
if (have_pieces[piece_index])
{
// we have already found a piece with
// this index.
int other_slot = m_piece_to_slot[piece_index];
assert(other_slot >= 0);
// take one of the other matching pieces
// that hasn't already been assigned
int other_piece = -1;
for (std::vector<int>::iterator i = matching_pieces.begin();
i != matching_pieces.end();
++i)
{
if (have_pieces[*i] || *i == piece_index) continue;
other_piece = *i;
break;
}
if (other_piece >= 0)
{
2004-01-27 00:38:08 +01:00
// replace the old slot with 'other_piece'
assert(have_pieces[other_piece] == false);
have_pieces[other_piece] = true;
m_slot_to_piece[other_slot] = other_piece;
m_piece_to_slot[other_piece] = other_slot;
}
else
{
2004-01-27 00:38:08 +01:00
// this index is the only piece with this
// hash. The previous slot we found with
// this hash must be tha same piece. Mark
// that piece as unassigned, since this slot
// is the correct place for the piece.
m_slot_to_piece[other_slot] = unassigned;
m_free_slots.push_back(other_slot);
}
2004-01-26 02:08:59 +01:00
assert(m_piece_to_slot[piece_index] != current_slot);
assert(m_piece_to_slot[piece_index] >= 0);
m_piece_to_slot[piece_index] = has_no_slot;
2004-01-26 02:45:30 +01:00
have_pieces[piece_index] = false;
}
2004-01-26 02:45:30 +01:00
assert(have_pieces[piece_index] == false);
assert(m_piece_to_slot[piece_index] == has_no_slot);
have_pieces[piece_index] = true;
return piece_index;
}
2004-01-26 02:08:59 +01:00
// find a matching piece that hasn't
// already been assigned
int free_piece = unassigned;
2004-01-26 02:08:59 +01:00
for (std::vector<int>::iterator i = matching_pieces.begin();
i != matching_pieces.end();
++i)
{
if (have_pieces[*i]) continue;
free_piece = *i;
break;
}
2004-01-26 02:45:30 +01:00
if (free_piece >= 0)
{
assert(have_pieces[free_piece] == false);
assert(m_piece_to_slot[free_piece] == has_no_slot);
have_pieces[free_piece] = true;
2004-01-27 00:38:08 +01:00
2004-01-26 02:45:30 +01:00
return free_piece;
}
else
{
assert(free_piece == unassigned);
return unassigned;
2004-01-26 02:45:30 +01:00
}
}
void piece_manager::impl::check_pieces(
boost::mutex& mutex
, detail::piece_checker_data& data
, std::vector<bool>& pieces)
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
INVARIANT_CHECK;
2004-01-27 00:39:24 +01:00
// This will corrupt the storage
// use while debugging to find
// states that cannot be scanned
// by check_pieces.
2004-01-27 00:38:08 +01:00
// m_storage.shuffle();
m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot);
m_slot_to_piece.resize(m_info.num_pieces(), unallocated);
m_free_slots.clear();
m_unallocated_slots.clear();
pieces.clear();
pieces.resize(m_info.num_pieces(), false);
2004-01-27 00:39:24 +01:00
// if we have fast-resume info
// use it instead of doing the actual checking
if (!data.piece_map.empty()
&& data.piece_map.size() <= m_slot_to_piece.size())
{
for (int i = 0; i < (int)data.piece_map.size(); ++i)
{
m_slot_to_piece[i] = data.piece_map[i];
if (data.piece_map[i] >= 0)
{
m_piece_to_slot[data.piece_map[i]] = i;
int found_piece = data.piece_map[i];
// if the piece is not in the unfinished list
// we have all of it
if (std::find_if(
data.unfinished_pieces.begin()
, data.unfinished_pieces.end()
, piece_picker::has_index(found_piece))
== data.unfinished_pieces.end())
{
pieces[found_piece] = true;
}
}
else if (data.piece_map[i] == unassigned)
{
m_free_slots.push_back(i);
}
else
{
assert(data.piece_map[i] == unallocated);
m_unallocated_slots.push_back(i);
}
}
for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i)
{
m_unallocated_slots.push_back(i);
}
return;
}
2004-01-27 00:39:24 +01:00
2004-01-25 22:51:30 +01:00
// ------------------------
// DO THE FULL CHECK
// ------------------------
// first, create all missing directories
for (torrent_info::file_iterator file_iter = m_info.begin_files(),
end_iter = m_info.end_files();
file_iter != end_iter;
++file_iter)
{
fs::path dir = m_save_path / file_iter->path;
fs::create_directories(dir);
}
std::vector<char> piece_data(m_info.piece_length());
std::multimap<sha1_hash, int> hash_to_piece;
// build the hash-map, that maps hashes to pieces
for (int i = 0; i < m_info.num_pieces(); ++i)
{
hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i));
}
for (int current_slot = 0; current_slot < m_info.num_pieces(); ++current_slot)
{
try
{
2004-01-27 00:38:08 +01:00
m_storage.read(
&piece_data[0]
, current_slot
, 0
, m_info.piece_size(current_slot));
int piece_index = identify_data(
piece_data
, current_slot
, pieces
, hash_to_piece);
assert(piece_index == unassigned || piece_index >= 0);
2004-01-26 02:45:30 +01:00
const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
const bool other_should_move = m_piece_to_slot[current_slot] != has_no_slot;
// check if this piece should be swapped with any other slot
// this section will ensure that the storage is correctly sorted
// libtorrent will never leave the storage in a state that
// requires this sorting, but other clients may.
// example of worst case:
// | current_slot = 5
// V
// +---+- - - +---+- - - +---+- -
// | x | | 5 | | 3 | <- piece data in slots
// +---+- - - +---+- - - +---+- -
// 3 y 5 <- slot index
// in this example, the data in the current_slot (5)
// is piece 3. It has to be moved into slot 3. The data
// in slot y (piece 5) should be moved into the current_slot.
// and the data in slot 3 (piece x) should be moved to slot y.
// there are three possible cases.
// 1. There's another piece that should be placed into this slot
// 2. This piece should be placed into another slot.
// 3. There's another piece that should be placed into this slot
// and this piece should be placed into another slot
// swap piece_index with this slot
// case 1
if (this_should_move && !other_should_move)
{
assert(piece_index != current_slot);
2004-01-27 00:38:08 +01:00
const int other_slot = piece_index;
assert(other_slot >= 0);
int other_piece = m_slot_to_piece[other_slot];
2004-01-27 00:38:08 +01:00
m_slot_to_piece[other_slot] = piece_index;
m_slot_to_piece[current_slot] = other_piece;
m_piece_to_slot[piece_index] = piece_index;
if (other_piece >= 0) m_piece_to_slot[other_piece] = current_slot;
2004-01-27 00:38:08 +01:00
if (other_piece == unassigned)
{
std::vector<int>::iterator i =
std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
assert(i != m_free_slots.end());
m_free_slots.erase(i);
m_free_slots.push_back(current_slot);
}
2004-01-27 00:38:08 +01:00
const int slot1_size = m_info.piece_size(piece_index);
const int slot2_size = other_piece >= 0 ? m_info.piece_size(other_piece) : 0;
std::vector<char> buf1(slot1_size);
m_storage.read(&buf1[0], current_slot, 0, slot1_size);
if (slot2_size > 0)
{
2004-01-27 00:38:08 +01:00
std::vector<char> buf2(slot2_size);
m_storage.read(&buf2[0], piece_index, 0, slot2_size);
m_storage.write(&buf2[0], current_slot, 0, slot2_size);
}
m_storage.write(&buf1[0], piece_index, 0, slot1_size);
assert(m_slot_to_piece[current_slot] == unassigned
|| m_piece_to_slot[m_slot_to_piece[current_slot]] == current_slot);
}
// case 2
else if (!this_should_move && other_should_move)
{
assert(piece_index != current_slot);
const int other_piece = current_slot;
const int other_slot = m_piece_to_slot[other_piece];
assert(other_slot >= 0);
m_slot_to_piece[current_slot] = other_piece;
m_slot_to_piece[other_slot] = piece_index;
m_piece_to_slot[other_piece] = current_slot;
if (piece_index >= 0) m_piece_to_slot[piece_index] = other_slot;
if (piece_index == unassigned)
2004-01-27 00:38:08 +01:00
{
m_free_slots.push_back(other_slot);
}
2004-01-27 00:38:08 +01:00
const int slot1_size = m_info.piece_size(other_piece);
const int slot2_size = piece_index >= 0 ? m_info.piece_size(piece_index) : 0;
std::vector<char> buf1(slot1_size);
m_storage.read(&buf1[0], other_slot, 0, slot1_size);
if (slot2_size > 0)
{
std::vector<char> buf2(slot2_size);
m_storage.read(&buf2[0], current_slot, 0, slot2_size);
m_storage.write(&buf2[0], other_slot, 0, slot2_size);
}
m_storage.write(&buf1[0], current_slot, 0, slot1_size);
assert(m_slot_to_piece[current_slot] == unassigned
|| m_piece_to_slot[m_slot_to_piece[current_slot]] == current_slot);
}
else if (this_should_move && other_should_move)
{
assert(piece_index != current_slot);
assert(piece_index >= 0);
const int piece1 = m_slot_to_piece[piece_index];
const int piece2 = current_slot;
const int slot1 = piece_index;
const int slot2 = m_piece_to_slot[piece2];
assert(slot1 >= 0);
assert(slot2 >= 0);
assert(piece2 >= 0);
2004-01-26 02:45:30 +01:00
// movement diagram:
// +---------------------------------------+
// | |
// +--> slot1 --> slot2 --> current_slot --+
m_slot_to_piece[slot1] = piece_index;
m_slot_to_piece[slot2] = piece1;
m_slot_to_piece[current_slot] = piece2;
m_piece_to_slot[piece_index] = slot1;
m_piece_to_slot[current_slot] = piece2;
if (piece1 >= 0) m_piece_to_slot[piece1] = slot2;
if (piece1 == unassigned)
{
std::vector<int>::iterator i =
std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
assert(i != m_free_slots.end());
m_free_slots.erase(i);
m_free_slots.push_back(slot2);
2004-01-27 00:38:08 +01:00
}
const int slot1_size = piece1 >= 0 ? m_info.piece_size(piece1) : 0;
const int slot2_size = m_info.piece_size(piece2);
const int slot3_size = m_info.piece_size(piece_index);
std::vector<char> buf1(m_info.piece_length());
std::vector<char> buf2(m_info.piece_length());
m_storage.read(&buf2[0], current_slot, 0, slot3_size);
m_storage.read(&buf1[0], slot2, 0, slot2_size);
m_storage.write(&buf1[0], current_slot, 0, slot2_size);
if (slot1_size > 0)
{
m_storage.read(&buf1[0], slot1, 0, slot1_size);
m_storage.write(&buf1[0], slot2, 0, slot1_size);
}
m_storage.write(&buf2[0], slot1, 0, slot3_size);
assert(m_slot_to_piece[current_slot] == unassigned
|| m_piece_to_slot[m_slot_to_piece[current_slot]] == current_slot);
}
else
{
assert(m_piece_to_slot[current_slot] == has_no_slot || piece_index != current_slot);
2004-01-27 00:38:08 +01:00
assert(m_slot_to_piece[current_slot] == unallocated);
assert(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
// the slot was identified as piece 'piece_index'
if (piece_index != unassigned)
m_piece_to_slot[piece_index] = current_slot;
else
m_free_slots.push_back(current_slot);
m_slot_to_piece[current_slot] = piece_index;
assert(m_slot_to_piece[current_slot] == unassigned
|| m_piece_to_slot[m_slot_to_piece[current_slot]] == current_slot);
}
}
catch (file_error&)
{
// this means the slot wasn't allocated
2004-01-25 22:37:19 +01:00
assert(m_slot_to_piece[current_slot] == unallocated);
m_unallocated_slots.push_back(current_slot);
}
// Update progress meter and check if we've been requested to abort
{
boost::mutex::scoped_lock lock(mutex);
data.progress = (float)current_slot / m_info.num_pieces();
if (data.abort)
return;
}
}
// TODO: sort m_free_slots and m_unallocated_slots?
}
2003-12-07 02:26:57 +01:00
2003-12-09 09:49:49 +01:00
void piece_manager::check_pieces(
boost::mutex& mutex
, detail::piece_checker_data& data
, std::vector<bool>& pieces)
{
m_pimpl->check_pieces(mutex, data, pieces);
}
2004-01-24 18:14:03 +01:00
int piece_manager::impl::allocate_slot_for_piece(int piece_index)
2003-12-07 02:26:57 +01:00
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
INVARIANT_CHECK;
2003-12-10 01:24:16 +01:00
2004-01-25 13:37:15 +01:00
assert(piece_index >= 0);
assert(piece_index < (int)m_piece_to_slot.size());
2003-12-07 02:26:57 +01:00
assert(m_piece_to_slot.size() == m_slot_to_piece.size());
int slot_index = m_piece_to_slot[piece_index];
2004-01-16 17:19:27 +01:00
if (slot_index != has_no_slot)
2003-12-07 02:26:57 +01:00
{
2004-01-25 13:37:15 +01:00
assert(slot_index >= 0);
assert(slot_index < (int)m_slot_to_piece.size());
2003-12-07 02:26:57 +01:00
return slot_index;
}
if (m_free_slots.empty())
{
2004-01-26 11:29:00 +01:00
allocate_slots(1);
2003-12-07 02:26:57 +01:00
assert(!m_free_slots.empty());
}
2003-12-07 02:26:57 +01:00
2003-12-07 06:53:04 +01:00
std::vector<int>::iterator iter(
2003-12-07 02:26:57 +01:00
std::find(
m_free_slots.begin()
2003-12-07 06:53:04 +01:00
, m_free_slots.end()
, piece_index));
2003-12-07 02:26:57 +01:00
if (iter == m_free_slots.end())
{
2004-01-16 17:19:27 +01:00
assert(m_slot_to_piece[piece_index] != unassigned);
2003-12-07 02:26:57 +01:00
iter = m_free_slots.end() - 1;
// special case to make sure we don't use the last slot
// when we shouldn't, since it's smaller than ordinary slots
if (*iter == m_info.num_pieces() - 1 && piece_index != *iter)
{
2003-12-07 02:26:57 +01:00
if (m_free_slots.size() == 1)
2004-01-26 11:29:00 +01:00
allocate_slots(1);
2003-12-07 02:26:57 +01:00
assert(m_free_slots.size() > 1);
2004-01-26 11:29:00 +01:00
// assumes that all allocated slots
2003-12-07 02:26:57 +01:00
// are put at the end of the free_slots vector
iter = m_free_slots.end() - 1;
}
2003-12-07 02:26:57 +01:00
}
slot_index = *iter;
2003-12-07 06:53:04 +01:00
m_free_slots.erase(iter);
2003-12-07 02:26:57 +01:00
2004-01-16 17:19:27 +01:00
assert(m_slot_to_piece[slot_index] == unassigned);
2003-12-07 02:26:57 +01:00
m_slot_to_piece[slot_index] = piece_index;
m_piece_to_slot[piece_index] = slot_index;
// there is another piece already assigned to
// the slot we are interested in, swap positions
2003-12-07 15:12:14 +01:00
if (slot_index != piece_index
&& m_slot_to_piece[piece_index] >= 0)
2003-12-07 02:26:57 +01:00
{
#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2003-12-07 15:12:14 +01:00
std::stringstream s;
s << "there is another piece at our slot, swapping..";
s << "\n piece_index: ";
s << piece_index;
s << "\n slot_index: ";
s << slot_index;
s << "\n piece at our slot: ";
s << m_slot_to_piece[piece_index];
s << "\n";
2003-12-07 15:12:14 +01:00
print_to_log(s.str());
debug_log();
2004-01-24 18:14:03 +01:00
#endif
int piece_at_our_slot = m_slot_to_piece[piece_index];
assert(m_piece_to_slot[piece_at_our_slot] == piece_index);
2003-12-07 02:26:57 +01:00
std::swap(
m_slot_to_piece[piece_index]
, m_slot_to_piece[slot_index]);
2003-10-30 00:28:09 +01:00
2003-12-07 02:26:57 +01:00
std::swap(
m_piece_to_slot[piece_index]
2003-12-07 15:19:04 +01:00
, m_piece_to_slot[piece_at_our_slot]);
2003-12-07 02:26:57 +01:00
2004-01-27 00:38:08 +01:00
const int slot_size = m_info.piece_size(slot_index);
std::vector<char> buf(slot_size);
m_storage.read(&buf[0], piece_index, 0, slot_size);
m_storage.write(&buf[0], slot_index, 0, slot_size);
2004-01-17 21:04:19 +01:00
2004-01-16 16:07:01 +01:00
assert(m_slot_to_piece[piece_index] == piece_index);
assert(m_piece_to_slot[piece_index] == piece_index);
2003-12-07 02:26:57 +01:00
slot_index = piece_index;
2004-01-24 18:14:03 +01:00
#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2003-12-07 15:12:14 +01:00
debug_log();
2004-01-17 21:04:19 +01:00
#endif
}
2003-12-07 02:26:57 +01:00
2004-01-25 13:37:15 +01:00
assert(slot_index >= 0);
assert(slot_index < (int)m_slot_to_piece.size());
2003-12-07 02:26:57 +01:00
return slot_index;
}
2003-12-09 09:55:03 +01:00
2004-01-25 22:37:19 +01:00
namespace
{
// this is used to notify potential other
// threads that the allocation-function has exited
2004-01-26 11:29:00 +01:00
struct allocation_syncronization
2004-01-25 22:37:19 +01:00
{
2004-01-26 11:29:00 +01:00
allocation_syncronization(
bool& flag
, boost::condition& cond
, boost::mutex& monitor)
2004-01-25 22:37:19 +01:00
: m_flag(flag)
, m_cond(cond)
2004-01-26 11:29:00 +01:00
, m_monitor(monitor)
{
boost::mutex::scoped_lock lock(m_monitor);
while (m_flag)
m_cond.wait(lock);
m_flag = true;
}
2004-01-25 22:37:19 +01:00
2004-01-26 11:29:00 +01:00
~allocation_syncronization()
2004-01-25 22:37:19 +01:00
{
2004-01-26 11:29:00 +01:00
boost::mutex::scoped_lock lock(m_monitor);
2004-01-25 22:37:19 +01:00
m_flag = false;
m_cond.notify_one();
}
bool& m_flag;
boost::condition& m_cond;
2004-01-26 11:29:00 +01:00
boost::mutex& m_monitor;
2004-01-25 22:37:19 +01:00
};
}
2003-12-09 09:49:49 +01:00
void piece_manager::impl::allocate_slots(int num_slots)
{
assert(num_slots > 0);
2004-01-24 20:19:17 +01:00
2004-01-26 11:29:00 +01:00
// this object will syncronize the allocation with
// potential other threads
allocation_syncronization sync_obj(
m_allocating
, m_allocating_condition
, m_allocating_monitor);
2004-01-25 22:37:19 +01:00
2003-12-07 02:26:57 +01:00
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
INVARIANT_CHECK;
2003-12-10 01:24:16 +01:00
2003-12-07 02:26:57 +01:00
namespace fs = boost::filesystem;
const int piece_size = m_info.piece_length();
2003-12-07 02:26:57 +01:00
std::vector<char> zeros(piece_size, 0);
2003-12-08 09:55:24 +01:00
2004-01-25 22:37:19 +01:00
for (int i = 0;
i < num_slots && !m_unallocated_slots.empty();
++i)
{
2004-01-25 22:37:19 +01:00
int pos = m_unallocated_slots.front();
2004-01-26 11:29:00 +01:00
// int piece_pos = pos;
2003-12-07 02:26:57 +01:00
int new_free_slot = pos;
2004-01-16 17:19:27 +01:00
if (m_piece_to_slot[pos] != has_no_slot)
2003-12-07 02:26:57 +01:00
{
assert(m_piece_to_slot[pos] >= 0);
m_storage.read(&zeros[0], m_piece_to_slot[pos], 0, m_info.piece_size(pos));
new_free_slot = m_piece_to_slot[pos];
m_slot_to_piece[pos] = pos;
m_piece_to_slot[pos] = pos;
}
2004-01-25 22:37:19 +01:00
m_storage.write(&zeros[0], pos, 0, m_info.piece_size(pos));
2003-12-07 02:26:57 +01:00
2004-01-25 22:37:19 +01:00
m_free_slots.push_back(new_free_slot);
m_slot_to_piece[new_free_slot] = unassigned;
m_unallocated_slots.erase(m_unallocated_slots.begin());
}
2004-01-25 22:37:19 +01:00
assert(m_free_slots.size() > 0);
2003-12-07 02:26:57 +01:00
}
2003-12-09 09:49:49 +01:00
void piece_manager::allocate_slots(int num_slots)
{
m_pimpl->allocate_slots(num_slots);
}
const boost::filesystem::path& piece_manager::save_path() const
{
return m_pimpl->save_path();
}
2004-01-17 21:04:19 +01:00
#ifndef NDEBUG
2003-12-09 09:49:49 +01:00
void piece_manager::impl::check_invariant() const
2003-12-07 02:26:57 +01:00
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2004-01-24 20:19:17 +01:00
if (m_piece_to_slot.empty()) return;
2004-01-26 11:29:00 +01:00
assert((int)m_piece_to_slot.size() == m_info.num_pieces());
assert((int)m_slot_to_piece.size() == m_info.num_pieces());
2004-01-24 20:19:17 +01:00
2004-01-25 22:37:19 +01:00
for (std::vector<int>::const_iterator i = m_free_slots.begin();
i != m_free_slots.end();
++i)
2004-01-24 20:19:17 +01:00
{
2004-01-25 22:37:19 +01:00
assert(*i < (int)m_slot_to_piece.size());
assert(*i >= 0);
assert(m_slot_to_piece[*i] == unassigned);
2004-01-24 20:19:17 +01:00
}
2004-01-25 22:37:19 +01:00
for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
i != m_unallocated_slots.end();
++i)
2004-01-24 20:19:17 +01:00
{
2004-01-25 22:37:19 +01:00
assert(*i < (int)m_slot_to_piece.size());
assert(*i >= 0);
assert(m_slot_to_piece[*i] == unallocated);
2004-01-24 20:19:17 +01:00
}
2003-12-01 23:09:58 +01:00
2003-12-07 02:26:57 +01:00
for (int i = 0; i < m_info.num_pieces(); ++i)
2003-12-07 06:53:04 +01:00
{
2004-01-25 03:59:27 +01:00
// Check domain of piece_to_slot's elements
2004-01-25 13:37:15 +01:00
if (m_piece_to_slot[i] != has_no_slot)
{
assert(m_piece_to_slot[i] >= 0);
assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
}
2004-01-24 20:19:17 +01:00
2004-01-25 03:59:27 +01:00
// Check domain of slot_to_piece's elements
2004-01-25 13:37:15 +01:00
if (m_slot_to_piece[i] != unallocated
&& m_slot_to_piece[i] != unassigned)
{
assert(m_slot_to_piece[i] >= 0);
assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
}
2004-01-24 20:19:17 +01:00
// do more detailed checks on piece_to_slot
2004-01-25 13:37:15 +01:00
if (m_piece_to_slot[i] >= 0)
2004-01-24 20:19:17 +01:00
{
2004-01-27 00:38:08 +01:00
assert(m_slot_to_piece[m_piece_to_slot[i]] == i);
2004-01-24 20:19:17 +01:00
if (m_piece_to_slot[i] != i)
{
assert(m_slot_to_piece[i] == unallocated);
}
}
else
{
2004-01-27 00:38:08 +01:00
assert(m_piece_to_slot[i] == has_no_slot);
2004-01-24 20:19:17 +01:00
}
// do more detailed checks on slot_to_piece
2003-12-10 01:24:16 +01:00
2004-01-25 22:37:19 +01:00
if (m_slot_to_piece[i] >= 0)
2004-01-16 17:19:27 +01:00
{
2004-01-25 13:37:15 +01:00
assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
assert(m_piece_to_slot[m_slot_to_piece[i]] == i);
2004-01-24 18:14:03 +01:00
#ifdef TORRENT_STORAGE_DEBUG
assert(
2004-01-16 17:19:27 +01:00
std::find(
m_unallocated_slots.begin()
, m_unallocated_slots.end()
, i) == m_unallocated_slots.end()
);
assert(
std::find(
m_free_slots.begin()
, m_free_slots.end()
, i) == m_free_slots.end()
);
2004-01-24 18:14:03 +01:00
#endif
}
2004-01-16 17:19:27 +01:00
else if (m_slot_to_piece[i] == unallocated)
{
2004-01-24 18:14:03 +01:00
#ifdef TORRENT_STORAGE_DEBUG
assert(
2004-01-16 17:19:27 +01:00
std::find(
m_unallocated_slots.begin()
, m_unallocated_slots.end()
, i) != m_unallocated_slots.end()
);
2004-01-24 18:14:03 +01:00
#endif
}
2004-01-16 17:19:27 +01:00
else if (m_slot_to_piece[i] == unassigned)
2003-12-10 01:24:16 +01:00
{
2004-01-24 18:14:03 +01:00
#ifdef TORRENT_STORAGE_DEBUG
assert(
2003-12-10 01:24:16 +01:00
std::find(
m_free_slots.begin()
, m_free_slots.end()
, i) != m_free_slots.end()
);
2004-01-24 18:14:03 +01:00
#endif
}
2004-01-16 17:19:27 +01:00
else
{
assert(false && "m_slot_to_piece[i] is invalid");
}
2003-12-07 02:26:57 +01:00
}
}
2004-01-24 18:14:03 +01:00
#ifdef TORRENT_STORAGE_DEBUG
2003-12-09 09:49:49 +01:00
void piece_manager::impl::debug_log() const
2003-12-07 15:12:14 +01:00
{
std::stringstream s;
s << "index\tslot\tpiece\n";
for (int i = 0; i < m_info.num_pieces(); ++i)
{
s << i << "\t" << m_slot_to_piece[i] << "\t";
s << m_piece_to_slot[i] << "\n";
}
s << "---------------------------------\n";
print_to_log(s.str());
}
2004-01-17 21:04:19 +01:00
#endif
2004-01-24 18:14:03 +01:00
#endif
2003-12-07 02:26:57 +01:00
} // namespace libtorrent
2003-12-01 23:09:58 +01:00