switched resume data parsing over to use lazy_bdecode. Improves memory allocation performance, especially noticable when heap allocations are expensive. Makes it more practical to run with malloc debug. Changed resume data interface to take a vector as opposed to a parsed structure

This commit is contained in:
Arvid Norberg 2008-06-30 23:14:31 +00:00
parent 6a15c5567b
commit 9c84908bb9
15 changed files with 259 additions and 248 deletions

View File

@ -253,7 +253,7 @@ add_torrent()
sha1_hash info_hash;
char const* name;
fs::path save_path;
entry const* resume_data;
std::vector<char>* resume_data;
storage_mode_t storage_mode;
bool paused;
bool auto_managed;
@ -289,7 +289,8 @@ torrent.
The optional parameter, ``resume_data`` can be given if up to date fast-resume data
is available. The fast-resume data can be acquired from a running torrent by calling
`save_resume_data()`_ on `torrent_handle`_. See `fast resume`_.
`save_resume_data()`_ on `torrent_handle`_. See `fast resume`_. The ``vector`` that is
passed in will be swapped into the running torrent instance with ``std::vector::swap()``.
The ``storage_mode`` parameter refers to the layout of the storage for this torrent.
There are 3 different modes:
@ -4416,7 +4417,7 @@ this::
virtual size_type read(char* buf, int slot, int offset, int size) = 0;
virtual void write(const char* buf, int slot, int offset, int size) = 0;
virtual bool move_storage(fs::path save_path) = 0;
virtual bool verify_resume_data(entry& rd, std::string& error) = 0;
virtual bool verify_resume_data(lazy_entry& rd, std::string& error) = 0;
virtual void write_resume_data(entry& rd) const = 0;
virtual void move_slot(int src_slot, int dst_slot) = 0;
virtual void swap_slots(int slot1, int slot2) = 0;

View File

@ -66,6 +66,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/magnet_uri.hpp"
#include "libtorrent/bitfield.hpp"
#include "libtorrent/file.hpp"
using boost::bind;
@ -485,19 +486,17 @@ void add_torrent(libtorrent::session& ses
std::cout << t->name() << "\n";
entry resume_data;
std::stringstream s;
s << t->name() << ".fastresume";
boost::filesystem::ifstream resume_file(save_path / s.str(), std::ios_base::binary);
resume_file.unsetf(std::ios_base::skipws);
resume_data = bdecode(
std::istream_iterator<char>(resume_file)
, std::istream_iterator<char>());
add_torrent_params p;
lazy_entry resume_data;
std::string filename = (save_path / (t->name() + ".fastresume")).string();
std::vector<char> buf;
if (load_file(filename.c_str(), buf) == 0)
p.resume_data = &buf;
p.ti = t;
p.save_path = save_path;
p.resume_data = &resume_data;
p.storage_mode = compact_mode ? storage_mode_compact : storage_mode_sparse;
p.paused = true;
p.duplicate_is_error = false;

View File

@ -34,7 +34,8 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_FILE_HPP_INCLUDED
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#ifdef _MSC_VER
#pragma warning(push, 1)
@ -54,10 +55,7 @@ namespace libtorrent
{
namespace fs = boost::filesystem;
struct TORRENT_EXPORT file_error: std::runtime_error
{
file_error(std::string const& msg): std::runtime_error(msg) {}
};
int load_file(fs::path const& filename, std::vector<char>& v);
class TORRENT_EXPORT file: public boost::noncopyable
{

View File

@ -170,7 +170,7 @@ namespace libtorrent
return m_size;
}
// end points one byte passed end
// end points one byte passed last byte
void set_end(char const* end)
{
TORRENT_ASSERT(end > m_begin);

View File

@ -140,7 +140,7 @@ namespace libtorrent
sha1_hash info_hash;
char const* name;
fs::path save_path;
entry const* resume_data;
std::vector<char>* resume_data;
storage_mode_t storage_mode;
bool paused;
bool auto_managed;

View File

@ -133,7 +133,7 @@ namespace libtorrent
virtual bool move_storage(fs::path save_path) = 0;
// verify storage dependent fast resume entries
virtual bool verify_resume_data(entry const& rd, std::string& error) = 0;
virtual bool verify_resume_data(lazy_entry const& rd, std::string& error) = 0;
// write storage dependent fast resume entries
virtual bool write_resume_data(entry& rd) const = 0;
@ -212,7 +212,7 @@ namespace libtorrent
boost::intrusive_ptr<torrent_info const> info() const { return m_info; }
void write_resume_data(entry& rd) const;
void async_check_fastresume(entry const* resume_data
void async_check_fastresume(lazy_entry const* resume_data
, boost::function<void(int, disk_io_job const&)> const& handler);
void async_check_files(boost::function<void(int, disk_io_job const&)> const& handler);
@ -259,7 +259,7 @@ namespace libtorrent
fs::path save_path() const;
bool verify_resume_data(entry const& rd, std::string& error)
bool verify_resume_data(lazy_entry const& rd, std::string& error)
{ return m_storage->verify_resume_data(rd, error); }
bool is_allocating() const
@ -282,7 +282,7 @@ namespace libtorrent
// the error message indicates that the fast resume data was rejected
// if 'fatal_disk_error' is returned, the error message indicates what
// when wrong in the disk access
int check_fastresume(entry const& rd, std::string& error);
int check_fastresume(lazy_entry const& rd, std::string& error);
// this function returns true if the checking is complete
int check_files(int& current_slot, int& have_piece, std::string& error);

View File

@ -107,7 +107,7 @@ namespace libtorrent
, int block_size
, storage_constructor_type sc
, bool paused
, entry const* resume_data
, std::vector<char>* resume_data
, int seq
, bool auto_managed);
@ -124,12 +124,14 @@ namespace libtorrent
, int block_size
, storage_constructor_type sc
, bool paused
, entry const* resume_data
, std::vector<char>* resume_data
, int seq
, bool auto_managed);
~torrent();
void parse_resume_data(std::vector<char>* resume_data);
// starts the announce timer
void start();
@ -564,7 +566,7 @@ namespace libtorrent
torrent_handle get_handle();
void write_resume_data(entry& rd) const;
void read_resume_data(entry const& rd);
void read_resume_data(lazy_entry const& rd);
// LOGGING
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
@ -787,7 +789,9 @@ namespace libtorrent
// error message
std::string m_error;
entry m_resume_data;
// used if there is any resume data
std::vector<char> m_resume_data;
lazy_entry m_resume_entry;
// if the torrent is started without metadata, it may
// still be given a name until the metadata is received

View File

@ -1048,7 +1048,7 @@ namespace libtorrent
#ifdef TORRENT_DISK_STATS
m_log << log_time() << " check fastresume" << std::endl;
#endif
entry const* rd = (entry const*)j.buffer;
lazy_entry const* rd = (lazy_entry const*)j.buffer;
TORRENT_ASSERT(rd != 0);
ret = j.storage->check_fastresume(*rd, j.str);
break;

View File

@ -70,6 +70,7 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
#include "libtorrent/file.hpp"
#include <sstream>
#include <cstring>
#include <vector>
#ifndef O_BINARY
#define O_BINARY 0
@ -130,9 +131,22 @@ namespace
namespace libtorrent
{
namespace fs = boost::filesystem;
int load_file(fs::path const& filename, std::vector<char>& v)
{
file f;
if (!f.open(filename, file::in)) return -1;
f.seek(0, file::end);
size_type s = f.tell();
if (s > 5000000) return -2;
v.resize(s);
f.seek(0);
size_type read = f.read(&v[0], s);
if (read != s) return -3;
return 0;
}
const file::open_mode file::in(mode_in);
const file::open_mode file::out(mode_out);
@ -160,7 +174,6 @@ namespace libtorrent
bool open(fs::path const& path, int mode)
{
TORRENT_ASSERT(path.is_complete());
close();
#if defined(_WIN32) && defined(UNICODE)
std::wstring wpath(safe_convert(path.native_file_string()));

View File

@ -560,34 +560,32 @@ namespace libtorrent
return false;
}
bool verify_resume_data(entry const& rd, std::string& error)
bool verify_resume_data(lazy_entry const& rd, std::string& error)
{
if (rd.type() != entry::dictionary_t)
if (rd.type() != lazy_entry::dict_t)
{
error = "invalid fastresume file";
error = "invalid fastresume file (not a dictionary)";
return true;
}
std::vector<std::pair<size_type, std::time_t> > file_sizes;
entry const* file_sizes_ent = rd.find_key("file sizes");
if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t)
lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
if (file_sizes_ent == 0)
{
error = "missing or invalid 'file sizes' entry in resume data";
return false;
}
entry::list_type const& l = file_sizes_ent->list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
for (int i = 0; i < file_sizes_ent->list_size(); ++i)
{
if (i->type() != entry::list_t) break;
entry::list_type const& pair = i->list();
if (pair.size() != 2 || pair.front().type() != entry::int_t
|| pair.back().type() != entry::int_t)
break;
lazy_entry const* e = file_sizes_ent->list_at(i);
if (e->type() != lazy_entry::list_t
|| e->list_size() != 2
|| e->list_at(0)->type() != lazy_entry::int_t
|| e->list_at(1)->type() != lazy_entry::int_t)
continue;
file_sizes.push_back(std::pair<size_type, std::time_t>(
pair.front().integer(), pair.back().integer()));
e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
}
if (file_sizes.empty())
@ -596,24 +594,30 @@ namespace libtorrent
return false;
}
entry const* slots_ent = rd.find_key("slots");
if (slots_ent == 0 || slots_ent->type() != entry::list_t)
lazy_entry const* slots = rd.dict_find_list("slots");
if (slots == 0)
{
error = "missing or invalid 'slots' entry in resume data";
return false;
}
entry::list_type const& slots = slots_ent->list();
bool seed = int(slots.size()) == files().num_pieces()
&& std::find_if(slots.begin(), slots.end()
, boost::bind<bool>(std::less<int>()
, boost::bind((size_type const& (entry::*)() const)
&entry::integer, _1), 0)) == slots.end();
bool seed = false;
if (int(slots->list_size()) == m_files.num_pieces())
{
bool seed = true;
for (int i = 0; i < slots->list_size(); ++i)
{
lazy_entry const* e = slots->list_at(i);
if (e->list_int_value_at(i, -1) >= 0) continue;
seed = false;
break;
}
}
bool full_allocation_mode = false;
entry const* allocation_mode = rd.find_key("allocation");
if (allocation_mode && allocation_mode->type() == entry::string_t)
full_allocation_mode = allocation_mode->string() == "full";
if (rd.dict_find_string_value("allocation") == "full")
full_allocation_mode = true;
if (seed)
{
@ -640,7 +644,6 @@ namespace libtorrent
return false;
}
}
return true;
}
return match_filesizes(files(), m_save_path, file_sizes

View File

@ -257,7 +257,12 @@ namespace libtorrent
add_torrent_params p(sc);
p.ti = tip;
p.save_path = save_path;
p.resume_data = &resume_data;
std::vector<char> buf;
if (resume_data.type() != entry::undefined_t)
{
bencode(std::back_inserter(buf), resume_data);
p.resume_data = &buf;
}
p.storage_mode = storage_mode;
p.paused = paused;
return m_impl->add_torrent(p);
@ -275,7 +280,12 @@ namespace libtorrent
add_torrent_params p(sc);
p.ti = ti;
p.save_path = save_path;
p.resume_data = &resume_data;
std::vector<char> buf;
if (resume_data.type() != entry::undefined_t)
{
bencode(std::back_inserter(buf), resume_data);
p.resume_data = &buf;
}
p.storage_mode = storage_mode;
p.paused = paused;
p.userdata = userdata;

View File

@ -422,7 +422,7 @@ namespace libtorrent
bool move_slot(int src_slot, int dst_slot);
bool swap_slots(int slot1, int slot2);
bool swap_slots3(int slot1, int slot2, int slot3);
bool verify_resume_data(entry const& rd, std::string& error);
bool verify_resume_data(lazy_entry const& rd, std::string& error);
bool write_resume_data(entry& rd) const;
sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size);
@ -685,34 +685,32 @@ namespace libtorrent
return false;
}
bool storage::verify_resume_data(entry const& rd, std::string& error)
bool storage::verify_resume_data(lazy_entry const& rd, std::string& error)
{
if (rd.type() != entry::dictionary_t)
if (rd.type() != lazy_entry::dict_t)
{
error = "invalid fastresume file (not a dictionary)";
return true;
}
std::vector<std::pair<size_type, std::time_t> > file_sizes;
entry const* file_sizes_ent = rd.find_key("file sizes");
if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t)
lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
if (file_sizes_ent == 0)
{
error = "missing or invalid 'file sizes' entry in resume data";
return false;
}
entry::list_type const& l = file_sizes_ent->list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
for (int i = 0; i < file_sizes_ent->list_size(); ++i)
{
if (i->type() != entry::list_t) break;
entry::list_type const& pair = i->list();
if (pair.size() != 2 || pair.front().type() != entry::int_t
|| pair.back().type() != entry::int_t)
break;
lazy_entry const* e = file_sizes_ent->list_at(i);
if (e->type() != lazy_entry::list_t
|| e->list_size() != 2
|| e->list_at(0)->type() != lazy_entry::int_t
|| e->list_at(1)->type() != lazy_entry::int_t)
continue;
file_sizes.push_back(std::pair<size_type, std::time_t>(
pair.front().integer(), std::time_t(pair.back().integer())));
e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
}
if (file_sizes.empty())
@ -721,32 +719,38 @@ namespace libtorrent
return false;
}
entry const* slots_ent = rd.find_key("slots");
if (slots_ent == 0 || slots_ent->type() != entry::list_t)
lazy_entry const* slots = rd.dict_find_list("slots");
if (slots == 0)
{
error = "missing or invalid 'slots' entry in resume data";
return false;
}
entry::list_type const& slots = slots_ent->list();
bool seed = int(slots.size()) == m_files.num_pieces()
&& std::find_if(slots.begin(), slots.end()
, boost::bind<bool>(std::less<int>()
, boost::bind((size_type const& (entry::*)() const)
&entry::integer, _1), 0)) == slots.end();
bool seed = false;
if (int(slots->list_size()) == m_files.num_pieces())
{
bool seed = true;
for (int i = 0; i < slots->list_size(); ++i)
{
lazy_entry const* e = slots->list_at(i);
if (e->list_int_value_at(i, -1) >= 0) continue;
seed = false;
break;
}
}
bool full_allocation_mode = false;
entry const* allocation_mode = rd.find_key("allocation");
if (allocation_mode && allocation_mode->type() == entry::string_t)
full_allocation_mode = allocation_mode->string() == "full";
if (rd.dict_find_string_value("allocation") == "full")
full_allocation_mode = true;
if (seed)
{
if (m_files.num_files() != (int)file_sizes.size())
if (files().num_files() != (int)file_sizes.size())
{
error = "the number of files does not match the torrent (num: "
+ boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
+ boost::lexical_cast<std::string>(m_files.num_files()) + ")";
+ boost::lexical_cast<std::string>(files().num_files()) + ")";
return false;
}
@ -1300,7 +1304,7 @@ namespace libtorrent
m_io_thread.add_job(j, handler);
}
void piece_manager::async_check_fastresume(entry const* resume_data
void piece_manager::async_check_fastresume(lazy_entry const* resume_data
, boost::function<void(int, disk_io_job const&)> const& handler)
{
TORRENT_ASSERT(resume_data != 0);
@ -1783,7 +1787,7 @@ namespace libtorrent
// isn't return false and the full check
// will be run
int piece_manager::check_fastresume(
entry const& rd, std::string& error)
lazy_entry const& rd, std::string& error)
{
boost::recursive_mutex::scoped_lock lock(m_mutex);
@ -1792,29 +1796,25 @@ namespace libtorrent
TORRENT_ASSERT(m_files.piece_length() > 0);
// if we don't have any resume data, return
if (rd.type() == entry::undefined_t) return check_no_fastresume(error);
if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error);
if (rd.type() != entry::dictionary_t)
if (rd.type() != lazy_entry::dict_t)
{
error = "invalid fastresume data (not a bencoded dictionary)";
error = "invalid fastresume data (not a dictionary)";
return check_no_fastresume(error);
}
int block_size = (std::min)(16 * 1024, m_files.piece_length());
entry const* blocks_per_piece_ent = rd.find_key("blocks per piece");
if (blocks_per_piece_ent != 0
&& blocks_per_piece_ent->type() == entry::int_t
&& blocks_per_piece_ent->integer() != m_files.piece_length() / block_size)
int blocks_per_piece = rd.dict_find_int_value("blocks per piece", -1);
if (blocks_per_piece != -1
&& blocks_per_piece != m_files.piece_length() / block_size)
{
error = "invalid 'blocks per piece' entry";
return check_no_fastresume(error);
}
storage_mode_t storage_mode = storage_mode_compact;
entry const* allocation = rd.find_key("allocation");
if (allocation != 0
&& allocation->type() == entry::string_t
&& allocation->string() != "compact")
if (rd.dict_find_string_value("allocation") != "compact")
storage_mode = storage_mode_sparse;
// assume no piece is out of place (i.e. in a slot
@ -1823,20 +1823,20 @@ namespace libtorrent
// if we don't have a piece map, we need the slots
// if we're in compact mode, we also need the slots map
if (storage_mode == storage_mode_compact || rd.find_key("pieces") == 0)
if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0)
{
// read slots map
entry const* slots = rd.find_key("slots");
if (slots == 0 || slots->type() != entry::list_t)
lazy_entry const* slots = rd.dict_find_list("slots");
if (slots == 0)
{
error = "missing slot list";
return check_no_fastresume(error);
}
if ((int)slots->list().size() > m_files.num_pieces())
if ((int)slots->list_size() > m_files.num_pieces())
{
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(slots->list().size()) + " size: "
+ boost::lexical_cast<std::string>(slots->list_size()) + " size: "
+ boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
return check_no_fastresume(error);
}
@ -1846,17 +1846,16 @@ namespace libtorrent
int num_pieces = int(m_files.num_pieces());
m_slot_to_piece.resize(num_pieces, unallocated);
m_piece_to_slot.resize(num_pieces, has_no_slot);
int slot = 0;
for (entry::list_type::const_iterator i = slots->list().begin();
i != slots->list().end(); ++i, ++slot)
for (int i = 0; i < slots->list_size(); ++i)
{
if (i->type() != entry::int_t)
lazy_entry const* e = slots->list_at(i);
if (e->type() != lazy_entry::int_t)
{
error = "invalid entry type in slot list";
return check_no_fastresume(error);
}
int index = int(i->integer());
int index = int(e->int_value());
if (index >= num_pieces || index < -2)
{
error = "too high index number in slot map (index: "
@ -1866,37 +1865,36 @@ namespace libtorrent
}
if (index >= 0)
{
m_slot_to_piece[slot] = index;
m_piece_to_slot[index] = slot;
if (slot != index) out_of_place = true;
m_slot_to_piece[i] = index;
m_piece_to_slot[index] = i;
if (i != index) out_of_place = true;
}
else if (index == unassigned)
{
if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(slot);
m_free_slots.push_back(i);
}
else
{
TORRENT_ASSERT(index == unallocated);
if (m_storage_mode == storage_mode_compact)
m_unallocated_slots.push_back(slot);
m_unallocated_slots.push_back(i);
}
}
}
else
{
int slot = 0;
for (entry::list_type::const_iterator i = slots->list().begin();
i != slots->list().end(); ++i, ++slot)
for (int i = 0; i < slots->list_size(); ++i)
{
if (i->type() != entry::int_t)
lazy_entry const* e = slots->list_at(i);
if (e->type() != lazy_entry::int_t)
{
error = "invalid entry type in slot list";
return check_no_fastresume(error);
}
int index = int(i->integer());
if (index != slot && index >= 0)
int index = int(e->int_value());
if (index != i && index >= 0)
{
error = "invalid slot index";
return check_no_fastresume(error);
@ -1937,17 +1935,17 @@ namespace libtorrent
else if (m_storage_mode == storage_mode_compact)
{
// read piece map
entry const* pieces = rd.find_key("pieces");
if (pieces == 0 || pieces->type() != entry::string_t)
lazy_entry const* pieces = rd.dict_find("pieces");
if (pieces == 0 || pieces->type() != lazy_entry::string_t)
{
error = "missing pieces entry";
return check_no_fastresume(error);
}
if ((int)pieces->string().size() != m_files.num_pieces())
if ((int)pieces->string_length() != m_files.num_pieces())
{
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(pieces->string().size()) + " size: "
+ boost::lexical_cast<std::string>(pieces->string_length()) + " size: "
+ boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
return check_no_fastresume(error);
}
@ -1955,7 +1953,7 @@ namespace libtorrent
int num_pieces = int(m_files.num_pieces());
m_slot_to_piece.resize(num_pieces, unallocated);
m_piece_to_slot.resize(num_pieces, has_no_slot);
std::string const& have_pieces = pieces->string();
char const* have_pieces = pieces->string_ptr();
for (int i = 0; i < num_pieces; ++i)
{
if (have_pieces[i] & 1)

View File

@ -144,7 +144,7 @@ namespace libtorrent
, int block_size
, storage_constructor_type sc
, bool paused
, entry const* resume_data
, std::vector<char>* resume_data
, int seq
, bool auto_managed)
: m_policy(this)
@ -203,7 +203,7 @@ namespace libtorrent
, m_has_incoming(false)
, m_files_checked(false)
{
if (resume_data) m_resume_data = *resume_data;
parse_resume_data(resume_data);
}
torrent::torrent(
@ -217,7 +217,7 @@ namespace libtorrent
, int block_size
, storage_constructor_type sc
, bool paused
, entry const* resume_data
, std::vector<char>* resume_data
, int seq
, bool auto_managed)
: m_policy(this)
@ -274,7 +274,7 @@ namespace libtorrent
, m_connections_initialized(false)
, m_has_incoming(false)
{
if (resume_data) m_resume_data = *resume_data;
parse_resume_data(resume_data);
#ifndef NDEBUG
m_files_checked = false;
#endif
@ -289,6 +289,25 @@ namespace libtorrent
}
}
void torrent::parse_resume_data(std::vector<char>* resume_data)
{
if (!resume_data) return;
m_resume_data.swap(*resume_data);
if (lazy_bdecode(&m_resume_data[0], &m_resume_data[0]
+ m_resume_data.size(), m_resume_entry) != 0)
{
std::vector<char>().swap(m_resume_data);
if (m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), "parse failed"));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << "fastresume data for "
<< torrent_file().name() << " rejected: parse failed\n";
#endif
}
}
}
void torrent::start()
{
boost::weak_ptr<torrent> self(shared_from_this());
@ -429,18 +448,17 @@ namespace libtorrent
m_state = torrent_status::queued_for_checking;
if (m_resume_data.type() == entry::dictionary_t)
if (m_resume_entry.type() == lazy_entry::dict_t)
{
char const* error = 0;
entry const* file_format = m_resume_data.find_key("file-format");
if (file_format->string() != "libtorrent resume file")
if (m_resume_entry.dict_find_string_value("file-format") != "libtorrent resume file")
error = "invalid file format tag";
entry const* info_hash = m_resume_data.find_key("info-hash");
if (!error && (info_hash == 0 || info_hash->type() != entry::string_t))
std::string info_hash = m_resume_entry.dict_find_string_value("info-hash");
if (!error && info_hash.empty())
error = "missing info-hash";
if (!error && sha1_hash(info_hash->string()) != m_torrent_file->info_hash())
if (!error && sha1_hash(info_hash) != m_torrent_file->info_hash())
error = "mismatching info-hash";
if (error && m_ses.m_alerts.should_post(alert::warning))
@ -453,11 +471,18 @@ namespace libtorrent
#endif
}
if (error) m_resume_data = entry();
else read_resume_data(m_resume_data);
if (error)
{
std::vector<char>().swap(m_resume_data);
m_resume_entry = lazy_entry();
}
else
{
read_resume_data(m_resume_entry);
}
}
m_storage->async_check_fastresume(&m_resume_data
m_storage->async_check_fastresume(&m_resume_entry
, bind(&torrent::on_resume_data_checked
, shared_from_this(), _1, _2));
}
@ -483,54 +508,41 @@ namespace libtorrent
return;
}
// parse out "peers" from the resume data and add them to the peer list
entry const* peers_entry = m_resume_data.find_key("peers");
if (peers_entry && peers_entry->type() == entry::list_t)
if (m_resume_entry.type() == lazy_entry::dict_t)
{
peer_id id;
std::fill(id.begin(), id.end(), 0);
entry::list_type const& peer_list = peers_entry->list();
for (entry::list_type::const_iterator i = peer_list.begin();
i != peer_list.end(); ++i)
// parse out "peers" from the resume data and add them to the peer list
if (lazy_entry const* peers_entry = m_resume_entry.dict_find_list("peers"))
{
if (i->type() != entry::dictionary_t) continue;
entry const* ip = i->find_key("ip");
entry const* port = i->find_key("port");
if (ip == 0 || port == 0
|| ip->type() != entry::string_t
|| port->type() != entry::int_t)
continue;
tcp::endpoint a(
address::from_string(ip->string())
, (unsigned short)port->integer());
m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
peer_id id(0);
for (int i = 0; i < peers_entry->list_size(); ++i)
{
lazy_entry const* e = peers_entry->list_at(i);
if (e->type() != lazy_entry::dict_t) continue;
std::string ip = e->dict_find_string_value("ip");
int port = e->dict_find_int_value("port");
if (ip.empty() || port == 0) continue;
tcp::endpoint a(address::from_string(ip), (unsigned short)port);
m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
}
}
}
// parse out "banned_peers" and add them as banned
entry const* banned_peers_entry = m_resume_data.find_key("banned_peers");
if (banned_peers_entry != 0 && banned_peers_entry->type() == entry::list_t)
{
peer_id id;
std::fill(id.begin(), id.end(), 0);
entry::list_type const& peer_list = banned_peers_entry->list();
for (entry::list_type::const_iterator i = peer_list.begin();
i != peer_list.end(); ++i)
// parse out "banned_peers" and add them as banned
if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_list("banned_peers"))
{
if (i->type() != entry::dictionary_t) continue;
entry const* ip = i->find_key("ip");
entry const* port = i->find_key("port");
if (ip == 0 || port == 0
|| ip->type() != entry::string_t
|| port->type() != entry::int_t)
continue;
tcp::endpoint a(
address::from_string(ip->string())
, (unsigned short)port->integer());
policy::peer* p = m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
if (p) p->banned = true;
peer_id id(0);
for (int i = 0; i < banned_peers_entry->list_size(); ++i)
{
lazy_entry const* e = banned_peers_entry->list_at(i);
if (e->type() != lazy_entry::dict_t) continue;
std::string ip = e->dict_find_string_value("ip");
int port = e->dict_find_int_value("port");
if (ip.empty() || port == 0) continue;
tcp::endpoint a(address::from_string(ip), (unsigned short)port);
policy::peer* p = m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
if (p) p->banned = true;
}
}
}
@ -551,17 +563,15 @@ namespace libtorrent
// there are either no files for this torrent
// or the resume_data was accepted
if (!fastresume_rejected)
if (!fastresume_rejected && m_resume_entry.type() == lazy_entry::dict_t)
{
TORRENT_ASSERT(m_resume_data.type() == entry::dictionary_t);
// parse have bitmask
entry const* pieces = m_resume_data.find_key("pieces");
if (pieces && pieces->type() == entry::string_t
&& int(pieces->string().length()) == m_torrent_file->num_pieces())
lazy_entry const* pieces = m_resume_entry.dict_find("pieces");
if (pieces && pieces->type() == lazy_entry::string_t
&& int(pieces->string_length()) == m_torrent_file->num_pieces())
{
std::string const& pieces_str = pieces->string();
for (int i = 0, end(pieces_str.size()); i < end; ++i)
char const* pieces_str = pieces->string_ptr();
for (int i = 0, end(pieces->string_length()); i < end; ++i)
{
if ((pieces_str[i] & 1) == 0) continue;
m_picker->we_have(i);
@ -572,27 +582,20 @@ namespace libtorrent
int num_blocks_per_piece =
static_cast<int>(torrent_file().piece_length()) / block_size();
entry const* unfinished_ent = m_resume_data.find_key("unfinished");
if (unfinished_ent != 0 && unfinished_ent->type() == entry::list_t)
if (lazy_entry const* unfinished_ent = m_resume_entry.dict_find_list("unfinished"))
{
entry::list_type const& unfinished = unfinished_ent->list();
int index = 0;
for (entry::list_type::const_iterator i = unfinished.begin();
i != unfinished.end(); ++i, ++index)
for (int i = 0; i < unfinished_ent->list_size(); ++i)
{
if (i->type() != entry::dictionary_t) continue;
entry const* piece = i->find_key("piece");
if (piece == 0 || piece->type() != entry::int_t) continue;
int piece_index = int(piece->integer());
if (piece_index < 0 || piece_index >= torrent_file().num_pieces())
continue;
lazy_entry const* e = unfinished_ent->list_at(i);
if (e->type() != lazy_entry::dict_t) continue;
int piece = e->dict_find_int_value("piece", -1);
if (piece < 0 || piece > torrent_file().num_pieces()) continue;
if (m_picker->have_piece(piece_index))
m_picker->we_dont_have(piece_index);
if (m_picker->have_piece(piece))
m_picker->we_dont_have(piece);
entry const* bitmask_ent = i->find_key("bitmask");
if (bitmask_ent == 0 || bitmask_ent->type() != entry::string_t) break;
std::string const& bitmask = bitmask_ent->string();
std::string bitmask = e->dict_find_string_value("bitmask");
if (bitmask.empty()) continue;
const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1);
if ((int)bitmask.size() != num_bitmask_bytes) continue;
@ -605,10 +608,10 @@ namespace libtorrent
const int bit = j * 8 + k;
if (bits & (1 << k))
{
m_picker->mark_as_finished(piece_block(piece_index, bit), 0);
if (m_picker->is_piece_finished(piece_index))
async_verify_piece(piece_index, bind(&torrent::piece_finished
, shared_from_this(), piece_index, _1));
m_picker->mark_as_finished(piece_block(piece, bit), 0);
if (m_picker->is_piece_finished(piece))
async_verify_piece(piece, bind(&torrent::piece_finished
, shared_from_this(), piece, _1));
}
}
}
@ -649,8 +652,9 @@ namespace libtorrent
if (m_auto_managed)
set_queue_position((std::numeric_limits<int>::max)());
m_resume_data = entry();
m_storage->async_check_fastresume(&m_resume_data
std::vector<char>().swap(m_resume_data);
m_resume_entry = lazy_entry();
m_storage->async_check_fastresume(&m_resume_entry
, bind(&torrent::on_force_recheck
, shared_from_this(), _1, _2));
}
@ -2413,31 +2417,14 @@ namespace libtorrent
}
#endif
void torrent::read_resume_data(entry const& rd)
void torrent::read_resume_data(lazy_entry const& rd)
{
entry const* e = 0;
e = rd.find_key("total_uploaded");
m_total_uploaded = (e != 0 && e->type() == entry::int_t)?e->integer():0;
e = rd.find_key("total_downloaded");
m_total_downloaded = (e != 0 && e->type() == entry::int_t)?e->integer():0;
e = rd.find_key("active_time");
m_active_time = seconds((e != 0 && e->type() == entry::int_t)?e->integer():0);
e = rd.find_key("seeding_time");
m_seeding_time = seconds((e != 0 && e->type() == entry::int_t)?e->integer():0);
e = rd.find_key("num_seeds");
m_complete = (e != 0 && e->type() == entry::int_t)?e->integer():-1;
e = rd.find_key("num_downloaders");
m_incomplete = (e != 0 && e->type() == entry::int_t)?e->integer():-1;
/*
m_total_uploaded = rd.find_int_value("total_uploaded");
m_total_downloaded = rd.find_inte_value("total_downloaded");
m_active_time = rd.find_int_value("active_time");
m_seeding_time = rd.find_int_value("seeding_time");
m_complete = rd.find_int_value("num_seeds", -1);
m_incomplete = rd.find_int_value("num_downloaders", -1);
*/
m_total_uploaded = rd.dict_find_int_value("total_uploaded");
m_total_downloaded = rd.dict_find_int_value("total_downloaded");
m_active_time = seconds(rd.dict_find_int_value("active_time"));
m_seeding_time = seconds(rd.dict_find_int_value("seeding_time"));
m_complete = rd.dict_find_int_value("num_seeds", -1);
m_incomplete = rd.dict_find_int_value("num_downloaders", -1);
}
void torrent::write_resume_data(entry& ret) const
@ -3332,8 +3319,8 @@ namespace libtorrent
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
TORRENT_ASSERT(m_resume_data.type() == entry::dictionary_t
|| m_resume_data.type() == entry::undefined_t);
TORRENT_ASSERT(m_resume_entry.type() == lazy_entry::dict_t
|| m_resume_entry.type() == lazy_entry::none_t);
TORRENT_ASSERT(m_bandwidth_queue[0].size() <= m_connections.size());
TORRENT_ASSERT(m_bandwidth_queue[1].size() <= m_connections.size());

View File

@ -58,6 +58,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/file.hpp"
namespace gr = boost::gregorian;
@ -282,16 +283,13 @@ namespace libtorrent
, m_multifile(false)
, m_private(false)
{
size_type s = fs::file_size(fs::path(filename));
// don't load torrent files larger than 2 MB
if (s > 2000000) return;
std::vector<char> buf(s);
std::ifstream f(filename, std::ios_base::binary);
f.read(&buf[0], s);
std::vector<char> buf;
int ret = load_file(filename, buf);
if (ret < 0) return;
std::string error;
lazy_entry e;
lazy_bdecode(&buf[0], &buf[0] + buf.size(), e);
std::string error;
#ifndef BOOST_NO_EXCEPTIONS
if (!parse_torrent_file(e, error))
throw invalid_torrent_file();

View File

@ -107,7 +107,7 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
, test_path, fp, io, default_storage_constructor, storage_mode);
boost::mutex lock;
entry frd;
lazy_entry frd;
pm->async_check_fastresume(&frd, &on_check_resume_data);
ios.reset();
ios.run();