added new preformatted type to bencode entry (#698)

added new preformatted type to bencode entry to support carrying a verbatim copy of an already bencoded subtree. This is to support saving torrents in resume data and create_torrent based on existing torrents, in order to preserve key order
This commit is contained in:
Arvid Norberg 2016-05-05 21:38:57 -04:00
parent f9bc6dbc54
commit 985436636e
10 changed files with 220 additions and 52 deletions

View File

@ -1,5 +1,6 @@
1.1.1 release
* added a new "preformatted" type to bencode entry variant type
* improved Socks5 support and test coverage
* fix set_settings in python binding
* Added missing alert categories in python binding

View File

@ -210,6 +210,10 @@ namespace libtorrent
write_char(out, 'e');
ret += 2;
break;
case entry::preformatted_t:
std::copy(e.preformatted().begin(), e.preformatted().end(), out);
ret += e.preformatted().size();
break;
case entry::undefined_t:
// trying to encode a structure with uninitialized values!
// TORRENT_ASSERT_VAL(false, e.type());

View File

@ -108,6 +108,7 @@ namespace libtorrent
typedef std::string string_type;
typedef std::list<entry> list_type;
typedef boost::int64_t integer_type;
typedef std::vector<char> preformatted_type;
// the types an entry can have
enum data_type
@ -116,7 +117,8 @@ namespace libtorrent
string_t,
list_t,
dictionary_t,
undefined_t
undefined_t,
preformatted_t
};
// returns the concrete type of the entry
@ -129,6 +131,7 @@ namespace libtorrent
entry(string_type const&);
entry(list_type const&);
entry(integer_type const&);
entry(preformatted_type const&);
// construct an empty entry of the specified type.
// see data_type enum.
@ -158,6 +161,7 @@ namespace libtorrent
void operator=(string_type const&);
void operator=(list_type const&);
void operator=(integer_type const&);
void operator=(preformatted_type const&);
// The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions
// are accessors that return the respective type. If the ``entry`` object
@ -214,6 +218,8 @@ namespace libtorrent
const list_type& list() const;
dictionary_type& dict();
const dictionary_type& dict() const;
preformatted_type& preformatted();
const preformatted_type& preformatted() const;
// swaps the content of *this* with ``e``.
void swap(entry& e);
@ -266,17 +272,19 @@ namespace libtorrent
// assumes sizeof(map<string, char>) == sizeof(map<string, entry>)
// and sizeof(list<char>) == sizeof(list<entry>)
enum { union_size
= max4<sizeof(std::list<char>)
= max5<sizeof(std::list<char>)
, sizeof(std::map<std::string, char>)
, sizeof(string_type)
, sizeof(integer_type)>::value
, sizeof(integer_type)
, sizeof(preformatted_type)>::value
};
#else
enum { union_size
= max4<sizeof(list_type)
= max5<sizeof(list_type)
, sizeof(dictionary_type)
, sizeof(string_type)
, sizeof(integer_type)>::value
, sizeof(integer_type)
, sizeof(preformatted_type)>::value
};
#endif
integer_type data[(union_size + sizeof(integer_type) - 1)

View File

@ -416,7 +416,9 @@ namespace libtorrent
m_piece_hash.resize(m_files.num_pieces());
for (int i = 0; i < num_pieces(); ++i) set_hash(i, ti.hash_for_piece(i));
m_info_dict = bdecode(&ti.metadata()[0], &ti.metadata()[0] + ti.metadata_size());
boost::shared_array<char> const info = ti.metadata();
int const size = ti.metadata_size();
m_info_dict.preformatted().assign(&info[0], &info[0] + size);
m_info_hash = ti.info_hash();
}
@ -508,7 +510,8 @@ namespace libtorrent
}
entry& info = dict["info"];
if (m_info_dict.type() == entry::dictionary_t)
if (m_info_dict.type() == entry::dictionary_t
|| m_info_dict.type() == entry::preformatted_t)
{
info = m_info_dict;
return dict;

View File

@ -158,6 +158,7 @@ namespace libtorrent
void entry::operator=(const entry& e)
{
if (&e == this) return;
destruct();
copy(e);
}
@ -254,6 +255,29 @@ namespace libtorrent
return *reinterpret_cast<const dictionary_type*>(data);
}
entry::preformatted_type& entry::preformatted()
{
if (m_type == undefined_t) construct(preformatted_t);
#ifndef BOOST_NO_EXCEPTIONS
if (m_type != preformatted_t) throw_type_error();
#elif defined TORRENT_DEBUG
TORRENT_ASSERT(m_type_queried);
#endif
TORRENT_ASSERT(m_type == preformatted_t);
return *reinterpret_cast<preformatted_type*>(data);
}
entry::preformatted_type const& entry::preformatted() const
{
#ifndef BOOST_NO_EXCEPTIONS
if (m_type != preformatted_t) throw_type_error();
#elif defined TORRENT_DEBUG
TORRENT_ASSERT(m_type_queried);
#endif
TORRENT_ASSERT(m_type == preformatted_t);
return *reinterpret_cast<const preformatted_type*>(data);
}
entry::entry()
: m_type(undefined_t)
{
@ -320,6 +344,16 @@ namespace libtorrent
m_type = int_t;
}
entry::entry(preformatted_type const& v)
: m_type(undefined_t)
{
#ifdef TORRENT_DEBUG
m_type_queried = true;
#endif
new(data) preformatted_type(v);
m_type = preformatted_t;
}
// convert a bdecode_node into an old skool entry
void entry::operator=(bdecode_node const& e)
{
@ -396,6 +430,16 @@ namespace libtorrent
}
#endif
void entry::operator=(preformatted_type const& v)
{
destruct();
new(data) preformatted_type(v);
m_type = preformatted_t;
#ifdef TORRENT_DEBUG
m_type_queried = true;
#endif
}
void entry::operator=(dictionary_type const& v)
{
destruct();
@ -450,6 +494,8 @@ namespace libtorrent
return list() == e.list();
case dictionary_t:
return dict() == e.dict();
case preformatted_t:
return preformatted() == e.preformatted();
default:
TORRENT_ASSERT(m_type == undefined_t);
return true;
@ -474,6 +520,9 @@ namespace libtorrent
break;
case undefined_t:
break;
case preformatted_t:
new (data) preformatted_type;
break;
}
m_type = t;
#ifdef TORRENT_DEBUG
@ -499,6 +548,10 @@ namespace libtorrent
break;
case undefined_t:
TORRENT_ASSERT(e.type() == undefined_t);
break;
case preformatted_t:
new (data) preformatted_type(e.preformatted());
break;
}
m_type = e.type();
#ifdef TORRENT_DEBUG
@ -522,6 +575,9 @@ namespace libtorrent
case dictionary_t:
call_destructor(reinterpret_cast<dictionary_type*>(data));
break;
case preformatted_t:
call_destructor(reinterpret_cast<preformatted_type*>(data));
break;
default:
TORRENT_ASSERT(m_type == undefined_t);
break;
@ -572,6 +628,10 @@ namespace libtorrent
std::swap(*reinterpret_cast<dictionary_type*>(data)
, *reinterpret_cast<dictionary_type*>(e.data));
break;
case preformatted_t:
std::swap(*reinterpret_cast<preformatted_type*>(data)
, *reinterpret_cast<preformatted_type*>(e.data));
break;
default:
break;
}
@ -664,6 +724,9 @@ namespace libtorrent
i->second.to_string_impl(out, indent+2);
}
} break;
case preformatted_t:
out += "<preformatted>\n";
break;
case undefined_t:
default:
out += "<uninitialized>\n";

View File

@ -7188,8 +7188,11 @@ namespace libtorrent
if (valid_metadata())
{
if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict))
ret["info"] = bdecode(&torrent_file().metadata()[0]
, &torrent_file().metadata()[0] + torrent_file().metadata_size());
{
boost::shared_array<char> const info = torrent_file().metadata();
int const size = torrent_file().metadata_size();
ret["info"].preformatted().assign(&info[0], &info[0] + size);
}
}
// blocks per piece

View File

@ -108,6 +108,7 @@ feature.compose <launcher>valgrind : <testing.launcher>"valgrind --tool=memcheck
test-suite libtorrent :
[ run
test_primitives.cpp
test_create_torrent.cpp
test_packet_buffer.cpp
test_timestamp_history.cpp
test_sha1_hash.cpp

View File

@ -26,6 +26,7 @@ test_programs = \
test_torrent \
test_tracker \
test_transfer \
test_create_torrent \
enum_if \
test_utp \
test_session \
@ -206,6 +207,7 @@ test_ssl_SOURCES = test_ssl.cpp
test_torrent_SOURCES = test_torrent.cpp
test_tracker_SOURCES = test_tracker.cpp
test_transfer_SOURCES = test_transfer.cpp
test_create_torrent_SOURCES = test_create_torrent.cpp
enum_if_SOURCES = enum_if.cpp
test_utp_SOURCES = test_utp.cpp
test_session_SOURCES = test_session.cpp

View File

@ -58,54 +58,72 @@ entry decode(std::string const& str)
return bdecode(str.begin(), str.end());
}
TORRENT_TEST(bencoding)
TORRENT_TEST(strings)
{
// ** strings **
{
entry e("spam");
TEST_CHECK(encode(e) == "4:spam");
TEST_CHECK(decode(encode(e)) == e);
}
entry e("spam");
TEST_CHECK(encode(e) == "4:spam");
TEST_CHECK(decode(encode(e)) == e);
}
// ** integers **
{
entry e(3);
TEST_CHECK(encode(e) == "i3e");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(integers)
{
entry e(3);
TEST_CHECK(encode(e) == "i3e");
TEST_CHECK(decode(encode(e)) == e);
}
{
entry e(-3);
TEST_CHECK(encode(e) == "i-3e");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(integers2)
{
entry e(-3);
TEST_CHECK(encode(e) == "i-3e");
TEST_CHECK(decode(encode(e)) == e);
}
{
entry e(int(0));
TEST_CHECK(encode(e) == "i0e");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(integers3)
{
entry e(int(0));
TEST_CHECK(encode(e) == "i0e");
TEST_CHECK(decode(encode(e)) == e);
}
// ** lists **
{
entry::list_type l;
l.push_back(entry("spam"));
l.push_back(entry("eggs"));
entry e(l);
TEST_CHECK(encode(e) == "l4:spam4:eggse");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(lists)
{
entry::list_type l;
l.push_back(entry("spam"));
l.push_back(entry("eggs"));
entry e(l);
TEST_CHECK(encode(e) == "l4:spam4:eggse");
TEST_CHECK(decode(encode(e)) == e);
}
// ** dictionaries **
{
entry e(entry::dictionary_t);
e["spam"] = entry("eggs");
e["cow"] = entry("moo");
TEST_CHECK(encode(e) == "d3:cow3:moo4:spam4:eggse");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(dictionaries)
{
entry e(entry::dictionary_t);
e["spam"] = entry("eggs");
e["cow"] = entry("moo");
TEST_CHECK(encode(e) == "d3:cow3:moo4:spam4:eggse");
TEST_CHECK(decode(encode(e)) == e);
}
TORRENT_TEST(preformatted)
{
entry e(entry::preformatted_t);
char const str[] = "foobar";
e.preformatted().assign(str, str + sizeof(str)-1);
TEST_EQUAL(encode(e), "foobar");
}
TORRENT_TEST(preformatted_node)
{
entry e(entry::dictionary_t);
char const str[] = "foobar";
e["info"] = entry::preformatted_type(str, str + sizeof(str)-1);
TEST_EQUAL(encode(e), "d4:infofoobare");
}
#ifndef TORRENT_NO_DEPRECATE
TORRENT_TEST(lazy_entry)
{
{
char b[] = "i12453e";
lazy_entry e;
@ -609,8 +627,6 @@ TORRENT_TEST(bencoding)
printf("%s\n", print_entry(e).c_str());
}
}
#endif // TORRENT_NO_DEPRECATE
}
#endif // TORRENT_NO_DEPRECATE

View File

@ -0,0 +1,67 @@
/*
Copyright (c) 2016, Arvid Norberg
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.
*/
#include "test.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/create_torrent.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/aux_/escape_string.hpp" // for convert_path_to_posix
#include <boost/make_shared.hpp>
#include <cstring>
namespace lt = libtorrent;
// make sure creating a torrent from an existing handle preserves the
// info-dictionary verbatim, so as to not alter the info-hash
TORRENT_TEST(create_verbatim_torrent)
{
char const test_torrent[] = "d4:infod4:name6:foobar6:lengthi12345e12:piece lengthi65536e6:pieces20:ababababababababababee";
lt::torrent_info info(test_torrent, sizeof(test_torrent) - 1);
lt::create_torrent t(info);
std::vector<char> buffer;
lt::bencode(std::back_inserter(buffer), t.generate());
// now, make sure the info dictionary was unchanged
buffer.push_back('\0');
char const* dest_info = std::strstr(&buffer[0], "4:info");
TEST_CHECK(dest_info != NULL);
// +1 and -2 here is to strip the outermost dictionary from the source
// torrent, since create_torrent may have added items next to the info dict
TEST_CHECK(memcmp(dest_info, test_torrent + 1, sizeof(test_torrent)-3) == 0);
}