diff --git a/ChangeLog b/ChangeLog index e449062a3..2c52e2bff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added new alert when individual files complete * added support for storing symbolic links in .torrent files * added support for uTorrent interpretation of multi-tracker torrents * handle torrents with duplicate filenames diff --git a/docs/manual.rst b/docs/manual.rst index d49040ea0..15c1c4581 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1817,7 +1817,7 @@ Its declaration looks like this:: torrent_handle(); torrent_status status(); - void file_progress(std::vector& fp); + void file_progress(std::vector& fp, int flags = 0); void get_download_queue(std::vector& queue) const; void get_peer_info(std::vector& v) const; torrent_info const& get_torrent_info() const; @@ -2029,7 +2029,7 @@ file_progress() :: - void file_progress(std::vector& fp); + void file_progress(std::vector& fp, int flags = 0); This function fills in the supplied vector with the the number of bytes downloaded of each file in this torrent. The progress values are ordered the same as the files @@ -2037,6 +2037,14 @@ in the `torrent_info`_. This operation is not very cheap. Its complexity is *O(n Where *n* is the number of files, *m* is the number of downloading pieces and *j* is the number of blocks in a piece. +The ``flags`` parameter can be used to specify the granularity of the file progress. If +left at the default value of 0, the progress will be as accurate as possible, but also +more expensive to calculate. If ``torrent_handle::piece_granularity`` is specified, +the progress will be specified in piece granularity. i.e. only pieces that have been +fully downloaded and passed the hash check count. When specifying piece granularity, +the operation is a lot cheaper, since libtorrent already keeps track of this internally +and no calculation is required. + save_path() ----------- @@ -4779,7 +4787,7 @@ generated and the torrent is paused. }; file_renamed_alert ------------------------- +------------------ This is posted as a response to a ``torrent_handle::rename_file`` call, if the rename operation succeeds. @@ -5152,6 +5160,23 @@ This alert is generated when a block request receives a response. }; +file_completed_alert +-------------------- + +This is posted whenever an individual file completes its download. i.e. +All pieces overlapping this file have passed their hash check. + +:: + + struct file_completed_alert: torrent_alert + { + // ... + int index; + }; + +The ``index`` member refers to the index of the file that completed. + + block_downloading_alert ----------------------- diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 8fbdf6fee..c1e947757 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -125,6 +125,30 @@ namespace libtorrent int size; }; + struct TORRENT_EXPORT file_completed_alert: torrent_alert + { + file_completed_alert(torrent_handle const& h + , int index_) + : torrent_alert(h) + , index(index_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new file_completed_alert(*this)); } + const static int static_category = alert::progress_notification; + virtual int category() const { return static_category; } + virtual char const* what() const { return "file completed"; } + virtual std::string message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), "%s: file %d finished downloading" + , torrent_alert::message().c_str(), index); + return msg; + } + + int index; + }; + struct TORRENT_EXPORT file_renamed_alert: torrent_alert { file_renamed_alert(torrent_handle const& h diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 242ab3f25..154333f6f 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -270,7 +270,7 @@ namespace libtorrent torrent_status status() const; - void file_progress(std::vector& fp) const; + void file_progress(std::vector& fp, int flags = 0) const; void use_interface(const char* net_interface); tcp::endpoint const& get_interface() const { return m_net_interface; } @@ -427,6 +427,10 @@ namespace libtorrent return has_picker()?m_picker->have_piece(index):true; } + // called when we learn that we have a piece + // only once per piece + void we_have(int index); + int num_have() const { return has_picker() @@ -838,6 +842,11 @@ namespace libtorrent std::vector m_file_priority; + // this vector contains the number of bytes completely + // downloaded (as in passed-hash-check) in each file. + // this lets us trigger on individual files completing + std::vector m_file_progress; + boost::scoped_ptr m_picker; std::vector m_trackers; diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 3d4cd1182..66f696160 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -371,7 +371,12 @@ namespace libtorrent // the torrent_info. void file_progress(std::vector& progress) const TORRENT_DEPRECATED; #endif - void file_progress(std::vector& progress) const; + enum file_progress_flags_t + { + piece_granularity = 1 + }; + + void file_progress(std::vector& progress, int flags = 0) const; void clear_error() const; diff --git a/src/file_storage.cpp b/src/file_storage.cpp index ac62978fb..2ee287c2f 100644 --- a/src/file_storage.cpp +++ b/src/file_storage.cpp @@ -91,18 +91,6 @@ namespace libtorrent m_files[index].path = new_filename; } - file_storage::iterator file_storage::file_at_offset(size_type offset) const - { - // TODO: do a binary search - std::vector::const_iterator i; - for (i = begin(); i != end(); ++i) - { - if (i->offset <= offset && i->offset + i->size > offset) - return i; - } - return i; - } - namespace { bool compare_file_offset(file_entry const& lhs, file_entry const& rhs) @@ -111,6 +99,21 @@ namespace libtorrent } } + file_storage::iterator file_storage::file_at_offset(size_type offset) const + { + // find the file iterator and file offset + file_entry target; + target.offset = offset; + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + begin(), end(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != begin()); + --file_iter; + return file_iter; + } + std::vector file_storage::map_block(int piece, size_type offset , int size) const { diff --git a/src/torrent.cpp b/src/torrent.cpp index 9a80a9336..2fd889c81 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -238,6 +238,7 @@ namespace libtorrent if (!m_seed_mode) { m_picker.reset(new piece_picker()); + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); if (!m_resume_data.empty()) { @@ -599,6 +600,7 @@ namespace libtorrent TORRENT_ASSERT(m_torrent_file->total_size() >= 0); m_file_priority.resize(m_torrent_file->num_files(), 1); + m_file_progress.resize(m_torrent_file->num_files(), 0); m_block_size = (std::min)(m_block_size, m_torrent_file->piece_length()); @@ -830,7 +832,7 @@ namespace libtorrent char const* pieces_str = pieces->string_ptr(); for (int i = 0, end(pieces->string_length()); i < end; ++i) { - if (pieces_str[i] & 1) m_picker->we_have(i); + if (pieces_str[i] & 1) we_have(i); if (m_seed_mode && (pieces_str[i] & 2)) m_verified.set_bit(i); } } @@ -842,7 +844,7 @@ namespace libtorrent for (int i = 0; i < slots->list_size(); ++i) { int piece = slots->list_int_value_at(i, -1); - if (piece >= 0) m_picker->we_have(piece); + if (piece >= 0) we_have(piece); } } } @@ -931,6 +933,7 @@ namespace libtorrent m_owning_storage->async_release_files(); if (!m_picker) m_picker.reset(new piece_picker()); + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); m_picker->init(m_torrent_file->piece_length() / m_block_size , int((m_torrent_file->total_size()+m_block_size-1)/m_block_size)); // assume that we don't have anything @@ -1014,7 +1017,7 @@ namespace libtorrent TORRENT_ASSERT(m_picker); if (j.offset >= 0 && !m_picker->have_piece(j.offset)) - m_picker->we_have(j.offset); + we_have(j.offset); // we're not done checking yet // this handler will be called repeatedly until @@ -1789,6 +1792,48 @@ namespace libtorrent m_picker->set_piece_priority(i, 6); } + void torrent::we_have(int index) + { + // update m_file_progress + TORRENT_ASSERT(m_picker); + TORRENT_ASSERT(!have_piece(index)); + + std::cerr << "[" << this << "] GOT PIECE " << index << std::endl; + + const int piece_size = m_torrent_file->piece_length(); + size_type off = size_type(index) * piece_size; + file_storage::iterator f = m_torrent_file->files().file_at_offset(off); + int size = m_torrent_file->piece_size(index); + int file_index = f - m_torrent_file->files().begin(); + for (; size > 0; ++f, ++file_index) + { + size_type file_offset = off - f->offset; + TORRENT_ASSERT(f != m_torrent_file->files().end()); + TORRENT_ASSERT(file_offset < f->size); + int add = (std::min)(f->size - file_offset, (size_type)size); + m_file_progress[file_index] += add; + std::cerr << " adding " << add << " to " << file_index << std::endl; + + TORRENT_ASSERT(m_file_progress[file_index] + <= m_torrent_file->files().at(file_index).size); + + if (m_file_progress[file_index] >= m_torrent_file->files().at(file_index).size) + { + if (m_ses.m_alerts.should_post()) + { + // this file just completed, post alert + m_ses.m_alerts.post_alert(file_completed_alert(get_handle() + , file_index)); + } + } + size -= add; + off += add; + TORRENT_ASSERT(size >= 0); + } + + m_picker->we_have(index); + } + void torrent::piece_passed(int index) { // INVARIANT_CHECK; @@ -1815,7 +1860,8 @@ namespace libtorrent std::set peers; std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); - m_picker->we_have(index); + we_have(index); + for (peer_iterator i = m_connections.begin(); i != m_connections.end();) { peer_connection* p = *i; @@ -4454,6 +4500,14 @@ namespace libtorrent TORRENT_ASSERT((m_torrent_file->piece_length() & (m_block_size-1)) == 0); } // if (is_seed()) TORRENT_ASSERT(m_picker.get() == 0); + + + for (std::vector::const_iterator i = m_file_progress.begin() + , end(m_file_progress.end()); i != end; ++i) + { + int index = i - m_file_progress.begin(); + TORRENT_ASSERT(*i <= m_torrent_file->files().at(index).size); + } } #endif @@ -5333,12 +5387,18 @@ namespace libtorrent } } - void torrent::file_progress(std::vector& fp) const + void torrent::file_progress(std::vector& fp, int flags) const { TORRENT_ASSERT(valid_metadata()); fp.resize(m_torrent_file->num_files(), 0); + if (flags & torrent_handle::piece_granularity) + { + std::copy(m_file_progress.begin(), m_file_progress.end(), fp.begin()); + return; + } + if (is_seed()) { for (int i = 0; i < m_torrent_file->num_files(); ++i) diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index d6e07ae7a..82f923da3 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -378,10 +378,10 @@ namespace libtorrent } #endif - void torrent_handle::file_progress(std::vector& progress) const + void torrent_handle::file_progress(std::vector& progress, int flags) const { INVARIANT_CHECK; - TORRENT_FORWARD(file_progress(progress)); + TORRENT_FORWARD(file_progress(progress, flags)); } torrent_status torrent_handle::status() const diff --git a/test/test_transfer.cpp b/test/test_transfer.cpp index 3a9ea9a20..2b4eb65dd 100644 --- a/test/test_transfer.cpp +++ b/test/test_transfer.cpp @@ -71,8 +71,8 @@ void test_rate() boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 , true, false, true, "_transfer", 0, &t); - ses1.set_alert_mask(alert::all_categories & ~(alert::progress_notification | alert::performance_warning)); - ses2.set_alert_mask(alert::all_categories & ~(alert::progress_notification | alert::performance_warning)); + ses1.set_alert_mask(alert::all_categories & ~(alert::performance_warning)); + ses2.set_alert_mask(alert::all_categories & ~(alert::performance_warning)); ptime start = time_now();