premiere-libtorrent/src/storage.cpp

1105 lines
26 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>
2004-01-16 03:57:45 +01:00
//#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
2004-01-16 03:57:45 +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>
#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"
2003-11-09 19:17:09 +01:00
#if defined(_MSC_VER)
#define for if (false) {} else for
#endif
2003-11-26 15:11:25 +01:00
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_)
{}
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;
}
};
void print_bitmask(const std::vector<bool>& x)
{
for (std::size_t i = 0; i < x.size(); ++i)
{
std::cout << x[i];
}
}
} // namespace unnamed
2003-12-07 02:26:57 +01:00
namespace fs = boost::filesystem;
2003-12-07 15:12:14 +01:00
namespace {
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
}
}
2003-12-07 02:26:57 +01:00
namespace libtorrent {
2003-12-07 17:26:16 +01:00
struct thread_safe_storage
{
thread_safe_storage(std::size_t n)
: slots(n, false)
{}
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_)
{
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;
};
2003-12-09 09:49:49 +01:00
struct storage::impl : thread_safe_storage
2003-12-07 17:26:16 +01:00
{
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
}
2003-12-07 02:26:57 +01:00
storage::size_type storage::read(
char* buf
, int slot
, size_type offset
, size_type size)
{
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
/*
2003-12-07 02:26:57 +01:00
fs::ifstream in(
2003-12-07 17:26:16 +01:00
m_pimpl->save_path / file_iter->path / file_iter->filename
2003-12-07 06:53:04 +01:00
, std::ios_base::binary
2003-12-07 02:26:57 +01:00
);
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.seekg(file_offset);
in.seek(file_offset);
2003-12-07 15:12:14 +01:00
2004-01-16 03:57:45 +01:00
// assert(size_type(in.tellg()) == file_offset);
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
size_type left_to_read = size;
2003-12-07 17:26:16 +01:00
size_type 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);
int result = left_to_read;
int buf_pos = 0;
while (left_to_read > 0)
{
int read_bytes = left_to_read;
if (file_offset + read_bytes > file_iter->size)
2003-12-08 09:55:24 +01:00
read_bytes = 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();
int actual_read = in.read(buf + buf_pos, read_bytes);
2003-12-07 02:26:57 +01:00
2003-12-08 02:37:30 +01:00
assert(read_bytes == actual_read);
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.close();
// in.clear();
// in.open(path, std::ios_base::binary);
in.open(path, file::in);
}
2003-12-07 02:26:57 +01:00
}
return result;
}
void storage::write(const char* buf, int slot, size_type offset, size_type size)
{
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
/*
2003-12-07 15:12:14 +01:00
fs::ofstream out;
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
*/
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.seekp(file_offset);
out.seek(file_offset);
2003-12-07 15:12:14 +01:00
2004-01-16 03:57:45 +01:00
// assert(file_offset == out.tellp());
2004-01-16 16:18:02 +01:00
#ifndef NDEBUG
size_type out_tell = out.tell();
assert(file_offset == out_tell);
#endif
2003-12-07 02:26:57 +01:00
size_type left_to_write = size;
2003-12-07 17:26:16 +01:00
size_type 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;
// TODO
// handle case when we can't write size bytes.
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 = file_iter->size - file_offset;
}
assert(buf_pos >= 0);
assert(write_bytes > 0);
out.write(buf + buf_pos, write_bytes);
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
{
public:
2004-01-16 04:58:24 +01:00
typedef boost::int64_t size_type;
2003-12-09 09:49:49 +01:00
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);
2003-12-09 09:49:49 +01:00
size_type read(char* buf, int piece_index, size_type offset, size_type size);
void write(const char* buf, int piece_index, size_type offset, size_type size);
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
int slot_for_piece(int piece_index);
void check_invariant() const;
void debug_log() const;
storage m_storage;
// total number of bytes left to be downloaded
size_type m_bytes_left;
// a bitmask representing the pieces we have
std::vector<bool> m_have_piece;
const torrent_info& m_info;
// maps piece index to slot index. -1 means the piece
// doesn't exist
2004-01-16 17:19:27 +01:00
enum { has_no_slot=-3 };
2003-12-09 09:49:49 +01:00
std::vector<int> m_piece_to_slot;
// 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;
// index here is a slot number in the file
2004-01-16 17:19:27 +01:00
// if index>=0, the slot is assigned to this piece
// otherwise it can have one of these values:
enum {
unallocated=-1, // the slot is unallocated
unassigned=-2 // the slot is allocated but not assigned to a piece
};
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)
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-13 04:08:59 +01:00
check_invariant();
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
}
2004-01-13 04:08:59 +01:00
check_invariant();
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-13 04:08:59 +01:00
#ifndef NDEBUG
check_invariant();
#endif
assert(piece_index >= 0 && piece_index < m_piece_to_slot.size());
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
2004-01-13 04:08:59 +01:00
#ifndef NDEBUG
check_invariant();
#endif
2004-01-12 04:05:10 +01:00
}
void piece_manager::mark_failed(int index)
{
m_pimpl->mark_failed(index);
}
2003-12-09 09:49:49 +01:00
piece_manager::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
, piece_manager::size_type offset
, piece_manager::size_type size)
2003-12-07 02:26:57 +01:00
{
assert(m_piece_to_slot[piece_index] >= 0);
int slot = m_piece_to_slot[piece_index];
return m_storage.read(buf, slot, offset, size);
}
2003-12-09 09:49:49 +01:00
piece_manager::size_type piece_manager::read(
char* buf
, int piece_index
, piece_manager::size_type offset
, piece_manager::size_type size)
{
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
, piece_manager::size_type offset
, piece_manager::size_type size)
2003-12-07 02:26:57 +01:00
{
int slot = slot_for_piece(piece_index);
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
, piece_manager::size_type offset
, piece_manager::size_type size)
{
m_pimpl->write(buf, piece_index, offset, size);
}
2003-12-21 18:28:27 +01:00
// TODO: must handle the case where some hashes are identical
// correctly
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);
// ----------------------------------------------------------------------
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);
2003-12-07 02:26:57 +01:00
m_bytes_left = m_info.total_size();
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-03 03:10:11 +01:00
// if we have fast-resume info
// use it instead of doingthe actual checking
if (!data.piece_map.empty()
&& data.piece_map.size() <= m_slot_to_piece.size())
{
for (int i = 0; i < 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())
{
m_bytes_left -= m_info.piece_size(found_piece);
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);
}
}
for (int i = data.piece_map.size(); i < pieces.size(); ++i)
{
m_unallocated_slots.push_back(i);
}
return;
}
2003-12-07 02:26:57 +01:00
bool changed_file = true;
2004-01-16 03:57:45 +01:00
// fs::ifstream in;
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;
2003-12-07 02:26:57 +01:00
std::size_t bytes_to_read = piece_size;
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;)
{
{
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;
}
2003-12-11 04:32:20 +01:00
assert(current_slot <= m_info.num_pieces());
2003-12-07 02:26:57 +01:00
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
/*
2003-12-07 02:26:57 +01:00
in.close();
in.clear();
in.open(path, std::ios_base::binary);
changed_file = false;
bytes_current_read = seek_into_next;
if (!in)
{
filesize = 0;
}
else
{
in.seekg(0, std::ios_base::end);
filesize = in.tellg();
in.seekg(seek_into_next, std::ios_base::beg);
}
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
// in.read(&piece_data[piece_offset], bytes_to_read);
// bytes_read = in.gcount();
bytes_read = in.read(&piece_data[piece_offset], bytes_to_read);
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;
assert(current_slot <= m_info.num_pieces());
2003-12-07 02:26:57 +01:00
}
seek_into_next = pos - file_end;
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;
}
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-21 18:28:27 +01:00
// TODO: there's still potential problems if some
// pieces have the same hash
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[
i == m_info.num_pieces() - 1]->get();
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-16 17:19:27 +01:00
assert(m_piece_to_slot[found_piece] >= 0);
assert(m_piece_to_slot[found_piece] < m_slot_to_piece.size());
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]);
}
else
{
2003-12-07 16:03:06 +01:00
m_bytes_left -= m_info.piece_size(found_piece);
2003-12-14 06:56:12 +01:00
}
2003-12-07 16:03:06 +01:00
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-16 17:19:27 +01:00
m_slot_to_piece[current_slot] = unassigned;
2003-12-07 02:26:57 +01:00
2004-01-16 04:58:24 +01:00
size_type last_pos =
2003-12-07 02:26:57 +01:00
m_info.total_size() -
m_info.piece_size(
m_info.num_pieces() - 1);
2003-12-11 04:32:20 +01:00
m_free_slots.push_back(current_slot);
}
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;
2003-12-07 02:26:57 +01:00
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
}
std::cout << " m_free_slots: " << m_free_slots.size() << "\n";
std::cout << " m_unallocated_slots: " << m_unallocated_slots.size() << "\n";
std::cout << " num pieces: " << m_info.num_pieces() << "\n";
std::cout << " have_pieces: ";
2003-12-07 06:53:04 +01:00
print_bitmask(pieces);
2003-12-07 02:26:57 +01:00
std::cout << "\n";
2003-12-07 06:53:04 +01:00
std::cout << std::count(pieces.begin(), pieces.end(), true) << "\n";
2003-12-07 02:26:57 +01:00
check_invariant();
}
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);
}
int piece_manager::impl::slot_for_piece(int piece_index)
2003-12-07 02:26:57 +01:00
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2003-12-10 01:24:16 +01:00
check_invariant();
2003-12-07 02:26:57 +01:00
assert(piece_index >= 0 && piece_index < m_piece_to_slot.size());
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
{
assert(slot_index >= 0);
assert(slot_index < m_slot_to_piece.size());
2004-01-13 04:08:59 +01:00
check_invariant();
2003-12-07 02:26:57 +01:00
return slot_index;
}
if (m_free_slots.empty())
{
allocate_slots(5);
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)
allocate_slots(5);
assert(m_free_slots.size() > 1);
// TODO: assumes that all allocated slots
// 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
{
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:19:04 +01:00
int piece_at_our_slot = m_slot_to_piece[piece_index];
2004-01-16 16:07:01 +01:00
assert(m_piece_to_slot[piece_at_our_slot] == piece_index);
2003-12-07 15:19:04 +01:00
2003-12-07 15:12:14 +01:00
print_to_log(s.str());
debug_log();
2003-12-07 02:26:57 +01:00
std::vector<char> buf(m_info.piece_length());
2003-12-07 15:12:14 +01:00
m_storage.read(&buf[0], piece_index, 0, m_info.piece_length());
m_storage.write(&buf[0], slot_index, 0, m_info.piece_length());
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-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;
2003-12-07 15:12:14 +01:00
debug_log();
}
2003-12-07 02:26:57 +01:00
2003-12-07 15:12:14 +01:00
check_invariant();
2003-12-07 02:26:57 +01:00
return slot_index;
}
2003-12-09 09:55:03 +01:00
2003-12-09 09:49:49 +01:00
void piece_manager::impl::allocate_slots(int num_slots)
{
2003-12-08 09:55:24 +01:00
{
boost::mutex::scoped_lock lock(m_allocating_monitor);
while (m_allocating)
m_allocating_condition.wait(lock);
m_allocating = true;
}
2003-12-07 02:26:57 +01:00
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
// ----------------------------------------------------------------------
2003-12-10 01:24:16 +01:00
check_invariant();
2003-12-07 02:26:57 +01:00
namespace fs = boost::filesystem;
std::vector<int>::iterator iter
= m_unallocated_slots.begin();
std::vector<int>::iterator end_iter
= m_unallocated_slots.end();
const size_type piece_size = m_info.piece_length();
std::vector<char> zeros(piece_size, 0);
2003-12-08 09:55:24 +01:00
2003-12-07 06:53:04 +01:00
for (int i = 0; i < num_slots; ++i, ++iter)
{
2003-12-07 02:26:57 +01:00
if (iter == end_iter)
break;
int pos = *iter;
int piece_pos = pos;
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-16 17:19:27 +01:00
m_slot_to_piece[new_free_slot] = unassigned;
2003-12-07 02:26:57 +01:00
m_free_slots.push_back(new_free_slot);
m_storage.write(&zeros[0], pos, 0, m_info.piece_size(pos));
}
2003-12-07 02:26:57 +01:00
m_unallocated_slots.erase(m_unallocated_slots.begin(), iter);
2003-12-08 09:55:24 +01:00
m_allocating = false;
2003-12-07 15:12:14 +01:00
check_invariant();
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();
}
2003-12-07 02:26:57 +01:00
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);
// ----------------------------------------------------------------------
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
{
2003-12-07 15:12:14 +01:00
if (m_piece_to_slot[i] != i && m_piece_to_slot[i] >= 0)
2004-01-16 17:19:27 +01:00
assert(m_slot_to_piece[i] == unallocated);
2003-12-10 01:24:16 +01:00
2004-01-16 17:19:27 +01:00
if (m_slot_to_piece[i]>=0)
{
assert(m_slot_to_piece[i]<m_piece_to_slot.size());
assert(m_piece_to_slot[m_slot_to_piece[i]]==i);
assert(
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()
);
}
else if (m_slot_to_piece[i] == unallocated)
{
assert(
std::find(
m_unallocated_slots.begin()
, m_unallocated_slots.end()
, i) != m_unallocated_slots.end()
);
}
else if (m_slot_to_piece[i] == unassigned)
2003-12-10 01:24:16 +01:00
{
assert(
std::find(
m_free_slots.begin()
, m_free_slots.end()
, i) != m_free_slots.end()
);
}
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
}
}
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());
}
2003-12-10 01:39:47 +01:00
2003-12-07 02:26:57 +01:00
} // namespace libtorrent
2003-12-01 23:09:58 +01:00