diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 286f3f42c..cec81683b 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -151,6 +151,9 @@ int main(int argc, char* argv[]) { using namespace libtorrent; + // TEMPORARY + boost::filesystem::path::default_name_check(boost::filesystem::no_check); + if (argc < 2) { std::cerr << "usage: ./client_test torrent-files ...\n" diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 91db3256a..5efc94144 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -90,7 +90,7 @@ namespace libtorrent enum open_mode { in, out }; // opens a piece with the given index from storage s - void open(storage* s, int index, open_mode m, int seek_offset = 0); + void open(storage* s, int index, open_mode m, int seek_offset = 0, bool lock_ = true); void close() { //std::cout << std::clock() << "close " << m_piece_index << "\n"; @@ -99,18 +99,21 @@ namespace libtorrent m_storage = 0; } - void write(const char* buf, int size); - int read(char* buf, int size); - void seek_forward(int step); + void write(const char* buf, int size, bool lock_ = true); + int read(char* buf, int size, bool lock_ = true); + void seek_forward(int step, bool lock_ = true); // tells the position in the file int tell() const { return m_piece_offset; } int left() const { return m_piece_size - m_piece_offset; } int index() const { return m_piece_index; } - + void lock(bool lock_ = true); + private: + void reopen(); + // the file itself std::fstream m_file; @@ -123,6 +126,9 @@ namespace libtorrent // file we're currently reading from/writing to std::vector::const_iterator m_file_iter; + // the global position + entry::integer_type m_position; + // the position we're at in the current file std::size_t m_file_offset; @@ -142,6 +148,7 @@ namespace libtorrent class storage { friend class piece_file; + friend class piece_sorter; public: void initialize_pieces(torrent* t, @@ -158,6 +165,9 @@ namespace libtorrent const std::vector& pieces() const { return m_have_piece; } + entry::integer_type piece_storage(int piece); + void allocate_pieces(int num); + private: // total number of bytes left to be downloaded @@ -171,8 +181,44 @@ namespace libtorrent const torrent_info* m_torrent_file; + // allocated pieces in file + std::vector m_allocated_pieces; + // unallocated blocks at the end of files + std::vector m_free_blocks; + // allocated blocks whose checksum doesn't match + std::vector m_free_pieces; + + // index here is a slot number in the file + // -1 : the slot is unallocated + // -2 : the slot is allocated but not assigned to a piece + // * : the slot is assigned to this piece + std::vector m_slot_to_piece; + + // synchronization + boost::mutex m_locked_pieces_monitor; + boost::condition m_unlocked_pieces; + std::vector m_locked_pieces; + + boost::recursive_mutex m_mutex; + + torrent* m_torrent; + }; + + class piece_sorter + { + public: + void operator()(); + + private: + typedef std::vector pieces_type; + + storage* m_storage; + boost::mutex m_monitor; + boost::condition m_more_pieces; + pieces_type m_pieces; }; } #endif // TORRENT_STORAGE_HPP_INCLUDED + diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 99898bbe3..9bae38470 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -91,8 +91,9 @@ namespace libtorrent // tracker request bool should_request() const throw() { - boost::posix_time::time_duration d = m_next_request - boost::posix_time::second_clock::local_time(); - return d.is_negative(); +// boost::posix_time::time_duration d = m_next_request - boost::posix_time::second_clock::local_time(); +// return d.is_negative(); + return m_next_request < boost::posix_time::second_clock::local_time(); } bool failed() const throw() { return !m_failed.empty(); } @@ -221,11 +222,12 @@ namespace libtorrent logger* spawn_logger(const char* title); #endif - private: #ifndef NDEBUG virtual void debug_log(const std::string& line); #endif + private: + void try_next_tracker(); enum event_id diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index e02aa09e3..46a94fede 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -81,8 +81,8 @@ libtorrent::peer_connection::peer_connection( , m_timeout(120) , m_packet_size(1) , m_recv_pos(0) - , m_last_receive(std::time(0)) - , m_last_sent(std::time(0)) + , m_last_receive(boost::gregorian::date(std::time(0))) + , m_last_sent(boost::gregorian::date(std::time(0))) , m_selector(sel) , m_socket(s) , m_torrent(t) @@ -125,8 +125,8 @@ libtorrent::peer_connection::peer_connection( , m_timeout(120) , m_packet_size(1) , m_recv_pos(0) - , m_last_receive(std::time(0)) - , m_last_sent(std::time(0)) + , m_last_receive(boost::gregorian::date(std::time(0))) + , m_last_sent(boost::gregorian::date(std::time(0))) , m_selector(sel) , m_socket(s) , m_torrent(0) diff --git a/src/storage.cpp b/src/storage.cpp index b790a2623..bebf131b4 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -41,7 +41,10 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include +#include +#include #include "libtorrent/storage.hpp" #include "libtorrent/torrent.hpp" @@ -54,13 +57,198 @@ POSSIBILITY OF SUCH DAMAGE. using namespace libtorrent; +//#define SUPER_VERBOSE_LOGGING -// TODO: when a piece_file is opened, a seek offset should be -// accepted as an argument, this way we may avoid opening a -// file in vain if we're about to seek forward anyway +namespace { -void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_offset) + struct defered_action_base {}; + + template + struct defered_value_impl : defered_action_base + { + defered_value_impl(T ref_, U enter, U exit_) + : exit(exit_) + , ref(ref_) + { + ref = enter; + } + + ~defered_value_impl() + { + ref = exit; + } + + mutable U exit; + T ref; + }; + + template + defered_value_impl defered_value(T& x, const U& enter, const U& exit) + { + return defered_value_impl(x, enter, exit); + } + + template + struct defered_action_impl : defered_action_base + { + defered_action_impl(const Fn& fn_) + : fn(fn_) + {} + + ~defered_action_impl() + { + fn(); + } + + Fn fn; + }; + + template + defered_action_impl defered_action(const Fn& fn) + { + return defered_action_impl(fn); + } + + typedef const defered_action_base& defered; + + struct defered_bitvector_value : defered_action_base + { + defered_bitvector_value(std::vector& v, std::size_t n_, bool entry_, bool exit_) + : vec(v) + , n(n_) + , onexit(exit_) + { + vec[n] = entry_; + } + + ~defered_bitvector_value() + { + vec[n] = onexit; + } + + std::vector& vec; + std::size_t n; + bool onexit; + }; + +} // namespace unnamed + + +void libtorrent::piece_file::reopen() { + m_position = m_storage->piece_storage(m_piece_index); + + entry::integer_type global_pos = m_position; + + assert(global_pos >= 0 + && global_pos < m_storage->m_torrent_file->total_size()); + + for (m_file_iter = m_storage->m_torrent_file->begin_files(); + global_pos > m_file_iter->size; global_pos -= m_file_iter->size); + + m_file_offset = global_pos + m_piece_offset; + + namespace fs = boost::filesystem; + + fs::path path = m_storage->m_save_path / + m_file_iter->path / m_file_iter->filename; + + m_file.close(); + m_file.clear(); + + m_file.open(path.native_file_string().c_str(), m_file_mode); + + if (m_mode == in) m_file.seekg(m_file_offset, std::ios_base::beg); + else m_file.seekp(m_file_offset, std::ios_base::beg); +} + +void libtorrent::piece_file::lock(bool lock_) +{ + boost::mutex::scoped_lock lock(m_storage->m_locked_pieces_monitor); + + if (lock_) + { + while (m_storage->m_locked_pieces[m_piece_index]) + m_storage->m_unlocked_pieces.wait(lock); + m_storage->m_locked_pieces[m_piece_index] = true; + } + else + { + m_storage->m_locked_pieces[m_piece_index] = false; + m_storage->m_unlocked_pieces.notify_all(); + } +} + +void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_offset, bool lock_) +{ + // do this here so that blocks can be allocated before we lock the piece + m_position = s->piece_storage(index); + + // synchronization ------------------------------------------------------ + + m_storage = s; + m_piece_index = index; + + lock(); + defered unlock = defered_action( + boost::bind( + &piece_file::lock + , this + , false)); + +/* std::vector& locked_pieces = s->m_locked_pieces; + boost::mutex::scoped_lock piece_lock(s->m_locked_pieces_monitor); + + while (locked_pieces[index]) + s->m_unlocked_pieces.wait(piece_lock); + + defered notifier = defered_action( + boost::bind( + &boost::condition::notify_all + , boost::ref(s->m_unlocked_pieces) + )); + + defered piece_locker = defered_bitvector_value( + locked_pieces, index, true, false);*/ + // ---------------------------------------------------------------------- + + assert(index >= 0 && index < s->m_torrent_file->num_pieces() && "internal error"); + + m_mode = o; + m_piece_size = m_storage->m_torrent_file->piece_size(m_piece_index); + m_position = s->piece_storage(index); + + m_piece_offset = seek_offset; + + entry::integer_type global_pos = m_position; + + assert(global_pos >= 0 && global_pos < s->m_torrent_file->total_size()); + + for (m_file_iter = m_storage->m_torrent_file->begin_files(); + global_pos > m_file_iter->size; global_pos -= m_file_iter->size); + + m_file_offset = global_pos + m_piece_offset; + + std::ios_base::openmode m; + if (m_mode == out) m = std::ios_base::out | std::ios_base::in | std::ios_base::binary; + else m = std::ios_base::in | std::ios_base::binary; + + namespace fs = boost::filesystem; + + fs::path path = m_storage->m_save_path / + m_file_iter->path / m_file_iter->filename; + + m_file.close(); + m_file.clear(); + + m_file_mode = m; + m_file.open(path.native_file_string().c_str(), m_file_mode); + + if (m_mode == in) m_file.seekg(m_file_offset, std::ios_base::beg); + else m_file.seekp(m_file_offset, std::ios_base::beg); + +#if 0 // old implementation + open_mode old_mode = m_mode; storage* old_storage = m_storage; std::vector::const_iterator old_file_iter = m_file_iter; @@ -126,10 +314,85 @@ void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_o assert(m_mode == out || m_file_offset == gpos && "internal error"); assert(m_mode == in || m_file_offset == ppos && "internal error"); #endif + +#endif } -int libtorrent::piece_file::read(char* buf, int size) +int libtorrent::piece_file::read(char* buf, int size, bool lock_) { + assert(m_mode == in); + assert(!m_file.fail()); + assert(m_file.is_open()); + + // synchronization ------------------------------------------------------ +/* std::vector& locked_pieces = m_storage->m_locked_pieces; + boost::mutex::scoped_lock piece_lock(m_storage->m_locked_pieces_monitor); + + while (locked_pieces[m_piece_index]) + m_storage->m_unlocked_pieces.wait(piece_lock); + + defered notifier = defered_action( + boost::bind( + &boost::condition::notify_all + , boost::ref(m_storage->m_unlocked_pieces) + )); + + defered piece_locker = defered_bitvector_value( + locked_pieces, m_piece_index, true, false);*/ + + lock(); + defered unlock = defered_action( + boost::bind( + &piece_file::lock + , this + , false)); + // ---------------------------------------------------------------------- + + if (m_storage->piece_storage(m_piece_index) != m_position) + { + reopen(); + } + + int left_to_read = size; + + if (m_piece_offset + left_to_read > m_piece_size) + left_to_read = m_piece_size - m_piece_offset; + + int result = left_to_read; + int buf_pos = 0; + + while (left_to_read > 0) + { + int read_bytes = left_to_read; + if (m_file_offset + read_bytes > m_file_iter->size) + read_bytes = m_file_iter->size - m_file_offset; + + m_file.read(buf + buf_pos, read_bytes); + + assert(read_bytes == m_file.gcount()); + + left_to_read -= read_bytes; + buf_pos += read_bytes; + m_file_offset += read_bytes; + m_piece_offset += read_bytes; + + if (left_to_read > 0) + { + ++m_file_iter; + boost::filesystem::path path = m_storage->m_save_path / + m_file_iter->path / m_file_iter->filename; + + m_file_offset = 0; + m_file.close(); + m_file.clear(); + m_file.open(path.native_file_string().c_str(), m_file_mode); + } + } + + return result; + +#if 0 // old implementation + assert(m_file_offset == (long)m_file.tellg() && "internal error"); // std::cout << std::clock() << "read " << m_piece_index << "\n"; @@ -188,10 +451,83 @@ int libtorrent::piece_file::read(char* buf, int size) assert(m_file_offset == gpos && "internal error"); #endif return read_total; + +#endif } -void libtorrent::piece_file::write(const char* buf, int size) +void libtorrent::piece_file::write(const char* buf, int size, bool lock_) { + assert(m_mode == out); + assert(!m_file.fail()); + assert(m_file.is_open()); + + // synchronization ------------------------------------------------------ +/* std::vector& locked_pieces = m_storage->m_locked_pieces; + boost::mutex::scoped_lock piece_lock(m_storage->m_locked_pieces_monitor); + + while (locked_pieces[m_piece_index]) + m_storage->m_unlocked_pieces.wait(piece_lock); + + defered notifier = defered_action( + boost::bind( + &boost::condition::notify_all + , boost::ref(m_storage->m_unlocked_pieces) + )); + + defered piece_locker = defered_bitvector_value( + locked_pieces, m_piece_index, true, false);*/ + lock(); + defered unlock = defered_action( + boost::bind( + &piece_file::lock + , this + , false)); + + // ---------------------------------------------------------------------- + + if (m_storage->piece_storage(m_piece_index) != m_position) + { + reopen(); + } + + int left_to_write = size; + + if (m_piece_offset + left_to_write > m_piece_size) + left_to_write = m_piece_size - m_piece_offset; + + int buf_pos = 0; + + while (left_to_write > 0) + { + int write_bytes = left_to_write; + if (m_file_offset + write_bytes > m_file_iter->size) + write_bytes = m_file_iter->size - m_file_offset; + + m_file.write(buf + buf_pos, write_bytes); + + left_to_write -= write_bytes; + buf_pos += write_bytes; + m_file_offset += write_bytes; + m_piece_offset += write_bytes; + + if (left_to_write > 0) + { + ++m_file_iter; + + assert(m_file_iter != m_storage->m_torrent_file->end_files()); + + boost::filesystem::path path = m_storage->m_save_path / + m_file_iter->path / m_file_iter->filename; + + m_file_offset = 0; + m_file.close(); + m_file.clear(); + m_file.open(path.native_file_string().c_str(), m_file_mode); + } + } + +#if 0 // old implementation + assert(m_mode == out); int left_to_write = size; @@ -231,10 +567,19 @@ void libtorrent::piece_file::write(const char* buf, int size) } } } while (left_to_write > 0); + +#endif } -void libtorrent::piece_file::seek_forward(int step) +void libtorrent::piece_file::seek_forward(int step, bool lock_) { + lock(); + defered unlock = defered_action( + boost::bind( + &piece_file::lock + , this + , false)); + assert(step >= 0 && "you cannot seek backwards in piece files"); assert(m_file_offset == (long)m_file.tellg() && "internal error"); if (step == 0) return; @@ -324,6 +669,650 @@ bool libtorrent::storage::verify_piece(piece_file& file) return false; } +namespace { + + struct equal_hash + { + bool operator()(const sha1_hash& lhs, const sha1_hash& rhs) const + { + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + }; + + struct lazy_hash + { + mutable sha1_hash digest; + mutable hasher h; + mutable const char* data; + std::size_t size; + + lazy_hash(const char* data_, std::size_t size_) + : data(data_) + , size(size_) + {} + + const sha1_hash& get() const + { + if (data) + { + h.update(data, size); + digest = h.final(); + data = 0; + } + return digest; + } + }; + + void print_bitmask(const std::vector& x) + { + for (std::size_t i = 0; i < x.size(); ++i) + { + std::cout << x[i]; + } + } + +} // namespace unnamed + +void libtorrent::storage::allocate_pieces(int num) +{ + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + namespace fs = boost::filesystem; + + std::cout << "allocating pieces...\n"; + + std::vector::iterator iter + = m_free_blocks.begin(); + std::vector::iterator end_iter + = m_free_blocks.end(); + + const entry::integer_type piece_size = m_torrent_file->piece_length(); + + std::vector zeros(piece_size, 0); + + int last_piece_index = -1; + + for (int i = 0; i < num; ++i, ++iter) + { + if (iter == end_iter) + break; + + entry::integer_type pos = *iter; + entry::integer_type piece_pos = pos; + + const bool last_piece = + pos == m_torrent_file->total_size() + - m_torrent_file->piece_size( + m_torrent_file->num_pieces() - 1); + + if (last_piece) + last_piece_index = i; + + torrent_info::file_iterator file_iter; + + for (file_iter = m_torrent_file->begin_files(); + pos > file_iter->size; ++file_iter) + { + pos -= file_iter->size; + } + + entry::integer_type bytes_left = last_piece + ? m_torrent_file->piece_size(m_torrent_file->num_pieces() - 1) + : piece_size; + + while (bytes_left > 0) + { +// fs::ofstream out(fs::path("foo.bar"), std::ios_base::binary | std::ios_base::in); + + fs::path path(m_save_path / file_iter->path / file_iter->filename); + + 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); + +// std::ofstream out((m_save_path / file_iter->path / file_iter->filename).native_file_string().c_str() +// , std::ios_base::binary | std::ios_base::in); + + out.seekp(pos, std::ios_base::beg); + + assert((entry::integer_type)out.tellp() == pos); + + entry::integer_type bytes_to_write = bytes_left; + + if (pos + bytes_to_write >= file_iter->size) + { + bytes_to_write = file_iter->size - pos; + } + + out.write(&zeros[0], bytes_to_write); + + bytes_left -= bytes_to_write; + ++file_iter; + pos = 0; + } + + m_free_pieces.push_back(piece_pos); + m_slot_to_piece[piece_pos / piece_size] = -2; + } + + m_free_blocks.erase(m_free_blocks.begin(), iter); + + // move last slot to the end + if (last_piece_index != -1) + std::swap(m_free_pieces[last_piece_index], m_free_pieces.front()); + + std::cout << "\tfree pieces: " << m_free_pieces.size() << "\n"; + std::cout << "\tfree blocks: " << m_free_blocks.size() << "\n"; +} + +#include + +entry::integer_type libtorrent::storage::piece_storage(int piece) +{ + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + assert(piece >= 0 && piece < m_allocated_pieces.size()); + + entry::integer_type result; + + result = m_allocated_pieces[piece]; + + if (result != -1) + { + return result; + } + + if (m_free_pieces.empty()) + { + allocate_pieces(5); + assert(!m_free_pieces.empty()); + } + + entry::integer_type wanted_pos = piece * m_torrent_file->piece_length(); + + std::vector::iterator iter( + std::find( + m_free_pieces.begin() + , m_free_pieces.end() + , wanted_pos)); + + if (iter == m_free_pieces.end()) + iter = m_free_pieces.end() - 1; + + result = *iter; + m_free_pieces.erase(iter); + +// assert(m_slot_to_piece[result / m_torrent_file->piece_length()] < 0); + + m_slot_to_piece[result / m_torrent_file->piece_length()] = piece; + m_allocated_pieces[piece] = result; + + // the last slot can only be given to the last piece! + // if this is the last slot, swap position with the last piece + const int last_piece = m_torrent_file->num_pieces() - 1; + const bool last_slot = result == + m_torrent_file->total_size() - m_torrent_file->piece_size(last_piece); + + if (last_slot && piece != last_piece) + { + assert(m_allocated_pieces[last_piece] != -1); + + piece_file f; + + f.open(this, last_piece, piece_file::in); + std::vector buf(m_torrent_file->piece_size(last_piece)); + f.read(&buf[0], m_torrent_file->piece_size(last_piece)); + f.close(); + + f.open(this, piece, piece_file::out); + f.write(&buf[0], m_torrent_file->piece_size(last_piece)); + f.close(); + + std::swap( + m_slot_to_piece[m_allocated_pieces[piece] / m_torrent_file->piece_length()] + , m_slot_to_piece[m_allocated_pieces[last_piece] / m_torrent_file->piece_length()] + ); + + std::swap(m_allocated_pieces[piece], m_allocated_pieces[last_piece]); + result = m_allocated_pieces[piece]; + } + else + { + int my_slot = result / m_torrent_file->piece_length(); + int other_piece = m_slot_to_piece[piece]; + + if (piece == my_slot) + return result; + + // the slot that we want is available and + // some other piece want slot + if (m_allocated_pieces[my_slot] >= 0 // another piece wants this position + && other_piece >= 0 + && m_slot_to_piece[piece] != my_slot) // .. and we want another piece position) + { + piece_file f; + + std::vector buf1(m_torrent_file->piece_length()); + std::vector buf2(m_torrent_file->piece_length()); + +#ifdef SUPER_VERBOSE_LOGGING + std::stringstream s; + + s << "double move!\n"; + + s << "allocating for piece #" << piece << " at slot " << my_slot << "\n"; + s << " piece at my wanted storage: " << other_piece << "\n"; + s << " slot that wants my storage: " << + m_allocated_pieces[my_slot] / m_torrent_file->piece_length() << "\n"; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_slot_to_piece[i] == i) + s << i << " is in correct position\n"; + else if (m_slot_to_piece[i] < 0) + s << i << " is not used\n"; + else + s << i << " is in wrong position (" << m_slot_to_piece[i] << ")\n"; + } +#endif + + // m_allocated_pieces[my_slot] -> piece + // other_piece -> m_allocated_pieces[my_slot] + // piece -> other_piece + + // read piece that wants my storage + f.open(this, my_slot, piece_file::in); + f.read(&buf1[0], m_torrent_file->piece_length()); + f.close(); + + // read piece that should be moved away + f.open(this, other_piece, piece_file::in); + f.read(&buf2[0], m_torrent_file->piece_length()); + f.close(); + + // write piece that wants my storage + f.open(this, piece, piece_file::out); + f.write(&buf1[0], m_torrent_file->piece_length()); + f.close(); + + // write piece that should be moved away + f.open(this, my_slot, piece_file::out); + f.write(&buf2[0], m_torrent_file->piece_length()); + f.close(); + + entry::integer_type pos[3] = { + m_allocated_pieces[piece] // me + , m_allocated_pieces[my_slot] // piece that wants my storage + , m_allocated_pieces[other_piece] // piece that is moved away + }; + + int slots[3] = { + my_slot //me + , m_allocated_pieces[my_slot] / m_torrent_file->piece_length() + // piece that wants my storage + , piece // piece that is moved away + }; + + m_slot_to_piece[my_slot] = my_slot; + m_slot_to_piece[piece] = piece; + m_slot_to_piece[ + m_allocated_pieces[my_slot] / m_torrent_file->piece_length() + ] = other_piece; + + m_allocated_pieces[piece] = pos[2]; + m_allocated_pieces[my_slot] = pos[0]; + m_allocated_pieces[other_piece] = pos[1]; + +#ifdef SUPER_VERBOSE_LOGGING + s << "i want slot : #" << piece << "\n"; + s << "occupied by piece : #" << other_piece << "\n"; + s << "wants my slot : #" << my_slot << "\n"; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_slot_to_piece[i] == i) + s << i << " is in correct position\n"; + else if (m_slot_to_piece[i] < 0) + s << i << " is not used\n"; + else + s << i << " is in wrong position (" << m_slot_to_piece[i] << ")\n"; + } + + m_torrent->debug_log(s.str()); +#endif + + return m_allocated_pieces[piece]; + } + + // there's another piece that wans my slot, swap positions + if (m_allocated_pieces[my_slot] > 0 && m_slot_to_piece[piece] != my_slot) + { + piece_file f; +#ifdef SUPER_VERBOSE_LOGGING + std::stringstream s; + + s << "single move!\n"; + + s << "allocating for piece #" << piece << " at slot " << my_slot << "\n"; + s << " slot that wants my storage: " << + m_allocated_pieces[my_slot] / m_torrent_file->piece_length() << "\n"; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_slot_to_piece[i] == i) + s << i << " is in correct position\n"; + else if (m_slot_to_piece[i] < 0) + s << i << " is not used\n"; + else + s << i << " is in wrong position (" << m_slot_to_piece[i] << ")\n"; + } +#endif + f.open(this, my_slot, piece_file::in); + std::vector buf(m_torrent_file->piece_length()); + f.read(&buf[0], m_torrent_file->piece_length()); + f.close(); + + f.open(this, piece, piece_file::out); + f.write(&buf[0], m_torrent_file->piece_length()); + f.close(); + + std::swap( + m_slot_to_piece[my_slot] + , m_slot_to_piece[m_allocated_pieces[my_slot] / m_torrent_file->piece_length()] + ); + + std::swap(m_allocated_pieces[piece], m_allocated_pieces[my_slot]); +#ifdef SUPER_VERBOSE_LOGGING + s << "-------------\n"; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_slot_to_piece[i] == i) + s << i << " is in correct position\n"; + else if (m_slot_to_piece[i] < 0) + s << i << " is not used\n"; + else + s << i << " is in wrong position (" << m_slot_to_piece[i] << ")\n"; + } + + m_torrent->debug_log(s.str()); +#endif + return m_allocated_pieces[piece]; + } + + if (other_piece >= 0 && other_piece != piece) + { + piece_file f; + + f.open(this, other_piece, piece_file::in); + std::vector buf(m_torrent_file->piece_length()); + f.read(&buf[0], m_torrent_file->piece_length()); + f.close(); + + f.open(this, piece, piece_file::out); + f.write(&buf[0], m_torrent_file->piece_length()); + f.close(); + + std::swap( + m_slot_to_piece[piece] + , m_slot_to_piece[result / m_torrent_file->piece_length()] + ); + + std::swap(m_allocated_pieces[piece], m_allocated_pieces[other_piece]); + +#ifdef SUPER_VERBOSE_LOGGING + std::stringstream s; + + s << "\nswapping #" << piece << " into place\n"; + s << "moved #" << other_piece << " doing it..\n"; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_slot_to_piece[i] == i) + s << i << " is in correct position\n"; + else if (m_slot_to_piece[i] < 0) + s << i << " is not used\n"; + else + s << i << " is in wrong position (" << m_slot_to_piece[i] << ")\n"; + } + + m_torrent->debug_log(s.str()); +#endif + return m_allocated_pieces[piece]; + } + + assert(false); + } + + return result; +} + +void libtorrent::storage::initialize_pieces(torrent* t, + const boost::filesystem::path& path, + detail::piece_checker_data* data, + boost::mutex& mutex) +{ + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + namespace fs = boost::filesystem; + + m_torrent = t; + + m_save_path = path; + m_torrent_file = &t->torrent_file(); + + // free up some memory + std::vector( + m_torrent_file->num_pieces(), false + ).swap(m_have_piece); + + std::vector( + m_torrent_file->num_pieces(), -1 + ).swap(m_allocated_pieces); + + std::vector( + m_torrent_file->num_pieces(), false + ).swap(m_locked_pieces); + + std::vector( + m_torrent_file->num_pieces(), -1 + ).swap(m_slot_to_piece); + + std::vector().swap(m_free_blocks); + std::vector().swap(m_free_pieces); + + m_bytes_left = m_torrent_file->total_size(); + + const std::size_t piece_size = m_torrent_file->piece_length(); + const std::size_t last_piece_size = m_torrent_file->piece_size( + m_torrent_file->num_pieces() - 1); + + bool changed_file = true; + fs::ifstream in; + + std::vector piece_data(m_torrent_file->piece_length()); + std::size_t piece_offset = 0; + + std::size_t current_piece = 0; + std::size_t bytes_to_read = piece_size; + std::size_t bytes_current_read = 0; + std::size_t seek_into_next = 0; + entry::integer_type filesize = 0; + entry::integer_type start_of_read = 0; + entry::integer_type start_of_file = 0; + + for (torrent_info::file_iterator file_iter = m_torrent_file->begin_files(), + end_iter = m_torrent_file->end_files(); + file_iter != end_iter;) + { + { + boost::mutex::scoped_lock lock(mutex); + + if (data->abort) + ; + } + + 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) + { + 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); + } + } + + // we are at the start of a new piece + // so we store the start of the piece + if (bytes_to_read == m_torrent_file->piece_size(current_piece)) + start_of_read = current_piece * piece_size; + + std::size_t bytes_read = 0; + + if (filesize > 0) + { + in.read(&piece_data[piece_offset], bytes_to_read); + bytes_read = in.gcount(); + } + + 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) + { + if (bytes_current_read != file_iter->size) + { + entry::integer_type pos; + entry::integer_type file_end = start_of_file + file_iter->size; + + for (pos = start_of_read; pos < file_end; + pos += piece_size) + { + m_free_blocks.push_back(pos); + ++current_piece; + } + + seek_into_next = pos - file_end; + bytes_to_read = piece_size; + piece_offset = 0; + } + else + { + seek_into_next = 0; + piece_offset += bytes_read; + } + + changed_file = true; + start_of_file += file_iter->size; + ++file_iter; + continue; + } + + // done with piece, move on to next + piece_offset = 0; + ++current_piece; + + // 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 + }; + + bool found = false; + + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (m_have_piece[i]) + continue; + + const sha1_hash& hash = digest[ + i == m_torrent_file->num_pieces() - 1]->get(); + + if (equal_hash()(hash, m_torrent_file->hash_for_piece(i))) + { + m_bytes_left -= m_torrent_file->piece_size(i); + + m_allocated_pieces[i] = start_of_read; + m_slot_to_piece[start_of_read / piece_size] = i; + m_have_piece[i] = true; + found = true; + break; + } + } + + if (!found) + { + m_slot_to_piece[start_of_read / piece_size] = -2; + + entry::integer_type last_pos = + m_torrent_file->total_size() - + m_torrent_file->piece_size( + m_torrent_file->num_pieces() - 1); + + // treat the last slot as unallocated space + // this means that when we get to the last + // slot we are either allocating space for + // the last piece, or the last piece has already + // been allocated + if (start_of_read == last_pos) + m_free_blocks.push_back(start_of_read); + else + m_free_pieces.push_back(start_of_read); + } + + bytes_to_read = m_torrent_file->piece_size(current_piece); + } + + std::cout << " free pieces: " << m_free_pieces.size() << "\n"; + std::cout << " free blocks: " << m_free_blocks.size() << "\n"; + std::cout << " num pieces: " << m_torrent_file->num_pieces() << "\n"; + + std::cout << " have_pieces: "; + print_bitmask(m_have_piece); + std::cout << "\n"; + std::cout << std::count(m_have_piece.begin(), m_have_piece.end(), true) << "\n"; +} + +#if 0 // OLD STORAGE + // allocate files will create all files that are missing // if there are some files that already exists, it checks // that they have the correct filesize @@ -488,79 +1477,6 @@ void libtorrent::storage::initialize_pieces(torrent* t, } } -/* -// reads the piece with the given index from disk -// and writes it into the buffer. The buffer must have -// room for at least m_piece_length bytes. -// the return value is the size of the piece that was read -// and the number of bytes written to the buffer -int libtorrent::torrent::read_piece(unsigned int index, char* buffer) const -{ - assert(index < m_torrent_file.num_pieces() && "internal error"); - int piece_byte_offset = index * m_torrent_file.piece_length(); - entry::integer_type file_byte_offset = 0; - std::vector::const_iterator i; - for (i = m_torrent_file.begin(); i != m_torrent_file.end(); ++i) - { - if (file_byte_offset + i->size > piece_byte_offset) break; - file_byte_offset += i->size; - } - - // i now refers to the first file this piece is stored in - int left_to_read = m_torrent_file.piece_size(index); - const int piece_size = m_torrent_file.piece_size(index); - while (left_to_read > 0) - { - assert(i != m_torrent_file.end() && "internal error, please report!"); - boost::filesystem::path path = m_save_path; - path /= i->path; - path /= i->filename; - std::ifstream f(path.native_file_string().c_str(), std::ios_base::binary); - f.seekg(piece_byte_offset - file_byte_offset, std::ios_base::beg); - f.read(buffer + piece_size - left_to_read, left_to_read); - int read = f.gcount(); - left_to_read -= read; - piece_byte_offset += read; - file_byte_offset += i->size; - ++i; - } - return m_torrent_file.piece_size(index); -} -void libtorrent::torrent::write_piece(unsigned int index, const char* buffer) const -{ - const int piece_byte_offset = index * m_torrent_file.piece_length(); - entry::integer_type file_byte_offset = 0; - std::vector::const_iterator i; - for (i = m_torrent_file.begin(); i != m_torrent_file.end(); ++i) - { - if (file_byte_offset + i->size > piece_byte_offset) break; - file_byte_offset += i->size; - } - assert(i != m_torrent_file.end() && "internal error, please report!"); +#endif - // i now refers to the first file this piece is stored in - - int piece_size = m_torrent_file.piece_size(index); - - int written = 0; - while (written < piece_size) - { - boost::filesystem::path path = m_save_path; - path /= i->path; - path /= i->filename; - std::fstream f(path.native_file_string().c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::out); - f.seekp(piece_byte_offset + written - file_byte_offset, std::ios_base::beg); - - int this_write = piece_size - written; - // check if this file is big enough to store the entire piece - if ( this_write > i->size - piece_byte_offset - written + file_byte_offset) - this_write = i->size - piece_byte_offset - written + file_byte_offset; - - f.write(buffer + written, this_write); - written += this_write; - file_byte_offset += i->size; - ++i; - } -} -*/