diff --git a/ChangeLog b/ChangeLog index 6f1f19a8765..3e74209ddfb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * the entry class is now a standard variant type * use std::string_view instead of boost counterpart * libtorrent now requires C++17 to build * added support for WebTorrent diff --git a/bindings/python/src/entry.cpp b/bindings/python/src/entry.cpp index 9ffdfab9763..f7de5a69912 100644 --- a/bindings/python/src/entry.cpp +++ b/bindings/python/src/entry.cpp @@ -110,7 +110,7 @@ struct entry_from_python { result.dict().insert( std::make_pair( - extract(items[i][0])(), + extract(items[i][0])(), construct0(items[i][1]) ) ); diff --git a/include/libtorrent/bencode.hpp b/include/libtorrent/bencode.hpp index 30d8bb8b9e8..cfaab42c972 100644 --- a/include/libtorrent/bencode.hpp +++ b/include/libtorrent/bencode.hpp @@ -114,61 +114,70 @@ namespace aux { } } - template - int bencode_recursive(OutIt& out, const entry& e) + template + struct bencode_visitor { - int ret = 0; - switch(e.type()) + OutIt& out; + + int operator()(entry::integer_type i) { - case entry::int_t: write_char(out, 'i'); - ret += write_integer(out, e.integer()); + int const ret = write_integer(out, i); write_char(out, 'e'); - ret += 2; - break; - case entry::string_t: - ret += write_integer(out, e.string().length()); + return ret + 2; + } + + int operator()(entry::string_type const& str) + { + int ret = write_integer(out, str.length()); write_char(out, ':'); - ret += write_string(e.string(), out); - ret += 1; - break; - case entry::list_t: + ret += write_string(str, out); + return ret + 1; + } + + int operator()(entry::list_type const& l) + { write_char(out, 'l'); - for (auto const& i : e.list()) - ret += bencode_recursive(out, i); + int ret = 2; + for (auto const& i : l) + ret += std::visit(*this, static_cast(i)); write_char(out, 'e'); - ret += 2; - break; - case entry::dictionary_t: + return ret; + } + + int operator()(entry::dictionary_type const& d) + { write_char(out, 'd'); - for (auto const& i : e.dict()) + int ret = 2; + for (auto const& i : d) { // write key ret += write_integer(out, i.first.length()); write_char(out, ':'); ret += write_string(i.first, out); // write value - ret += bencode_recursive(out, i.second); + ret += std::visit(*this, static_cast(i.second)); ret += 1; } write_char(out, 'e'); - ret += 2; - break; - case entry::preformatted_t: - std::copy(e.preformatted().begin(), e.preformatted().end(), out); - ret += static_cast(e.preformatted().size()); - break; - case entry::undefined_t: + return ret; + } + + int operator()(entry::preformatted_type const& pre) + { + std::copy(pre.begin(), pre.end(), out); + return static_cast(pre.size()); + } + int operator()(entry::uninitialized_type const&) + { // empty string write_char(out, '0'); write_char(out, ':'); - - ret += 2; - break; + return 2; } - return ret; - } + }; + #if TORRENT_ABI_VERSION == 1 template void bdecode_recursive(InIt& in, InIt end, entry& ret, bool& err, int depth) @@ -182,9 +191,6 @@ namespace aux { if (in == end) { err = true; -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif return; } switch (*in) @@ -202,9 +208,6 @@ namespace aux { ret = entry(entry::int_t); char* end_pointer; ret.integer() = std::strtoll(val.c_str(), &end_pointer, 10); -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif if (end_pointer == val.c_str()) { err = true; @@ -225,23 +228,14 @@ namespace aux { bdecode_recursive(in, end, e, err, depth + 1); if (err) { -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif return; } if (in == end) { err = true; -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif return; } } -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif TORRENT_ASSERT(*in == 'e'); ++in; // 'e' break; @@ -255,34 +249,16 @@ namespace aux { { entry key; bdecode_recursive(in, end, key, err, depth + 1); - if (err || key.type() != entry::string_t) - { -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif - return; - } + if (err || key.type() != entry::string_t) return; entry& e = ret[key.string()]; bdecode_recursive(in, end, e, err, depth + 1); - if (err) - { -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif - return; - } + if (err) return; if (in == end) { err = true; -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif return; } } -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif TORRENT_ASSERT(*in == 'e'); ++in; // 'e' break; @@ -294,37 +270,19 @@ namespace aux { if (is_digit(char(*in))) { std::string len_s = read_until(in, end, ':', err); - if (err) - { -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif - return; - } + if (err) return; TORRENT_ASSERT(*in == ':'); ++in; // ':' int len = atoi(len_s.c_str()); ret = entry(entry::string_t); read_string(in, end, len, ret.string(), err); - if (err) - { -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif - return; - } + if (err) return; } else { err = true; -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif return; } -#if TORRENT_USE_ASSERTS - ret.m_type_queried = false; -#endif } } #endif // TORRENT_ABI_VERSION @@ -350,7 +308,7 @@ namespace aux { // .. _back_insert_iterator: https://en.cppreference.com/w/cpp/iterator/back_insert_iterator template int bencode(OutIt out, const entry& e) { - return aux::bencode_recursive(out, e); + return std::visit(aux::bencode_visitor{out}, static_cast(e)); } #if TORRENT_ABI_VERSION == 1 @@ -361,7 +319,6 @@ namespace aux { entry e; bool err = false; aux::bdecode_recursive(start, end, e, err, 0); - TORRENT_ASSERT(e.m_type_queried == false); if (err) return entry(); return e; } diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index da034f336ed..6d789519466 100644 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -44,6 +44,7 @@ see LICENSE file. #include #include #include +#include #if TORRENT_USE_IOSTREAM #include #endif @@ -52,9 +53,13 @@ see LICENSE file. #include "libtorrent/error_code.hpp" #include "libtorrent/span.hpp" #include "libtorrent/string_view.hpp" -#include "libtorrent/aux_/aligned_union.hpp" +#include "libtorrent/aux_/noexcept_movable.hpp" #include "libtorrent/aux_/strview_less.hpp" +#include "libtorrent/aux_/disable_warnings_push.hpp" +#include +#include "libtorrent/aux_/disable_warnings_pop.hpp" + namespace libtorrent { #if TORRENT_ABI_VERSION == 1 @@ -62,22 +67,47 @@ namespace libtorrent { using type_error = system_error; #endif struct bdecode_node; + struct entry; + + namespace entry_types { + + using dictionary_type = boost::container::map; + using string_type = std::string; + using list_type = std::vector; + using integer_type = std::int64_t; + using preformatted_type = std::vector; + struct uninitialized_type { + bool operator==(uninitialized_type const&) const { return true; } + }; + + // internal + using variant_type = std::variant; + + static_assert(std::is_nothrow_move_constructible::value + , "expected variant to be nothrow move constructible"); + } // The ``entry`` class represents one node in a bencoded hierarchy. It works as a // variant type, it can be either a list, a dictionary (``std::map``), an integer // or a string. - class TORRENT_EXPORT entry + struct TORRENT_EXPORT entry : entry_types::variant_type { - public: - // the key is always a string. If a generic entry would be allowed // as a key, sorting would become a problem (e.g. to compare a string // to a list). The definition doesn't mention such a limit though. - using dictionary_type = std::map; - using string_type = std::string; - using list_type = std::vector; - using integer_type = std::int64_t; - using preformatted_type = std::vector; + using dictionary_type = entry_types::dictionary_type; + using string_type = entry_types::string_type; + using list_type = entry_types::list_type; + using integer_type = entry_types::integer_type; + using preformatted_type = entry_types::preformatted_type; + using uninitialized_type = entry_types::uninitialized_type; + + using variant_type = entry_types::variant_type; // the types an entry can have enum data_type @@ -86,8 +116,8 @@ namespace libtorrent { string_t, list_t, dictionary_t, + preformatted_t, undefined_t, - preformatted_t }; // returns the concrete type of the entry @@ -98,24 +128,18 @@ namespace libtorrent { // newly constructed entry entry(dictionary_type); // NOLINT entry(span); // NOLINT + entry(string_view); // NOLINT + entry(string_type); // NOLINT entry(list_type); // NOLINT entry(integer_type); // NOLINT entry(preformatted_type); // NOLINT // hidden + // this is here to disambiguate between std::string and string_view. It + // needs to be a template to prevent implicit conversions from literal 0 template ::value - || std::is_same::value - || std::is_same::value>::type> - entry(U v) // NOLINT - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) string_type(std::move(v)); - m_type = string_t; - } + std::is_same::value>::type> + entry(U v); // construct an empty entry of the specified type. // see data_type enum. @@ -130,36 +154,27 @@ namespace libtorrent { // hidden entry(); - - // hidden ~entry(); // copies the structure of the right hand side into this // entry. entry& operator=(bdecode_node const&) &; entry& operator=(entry const&) &; - entry& operator=(entry&&) & noexcept; + entry& operator=(entry&&) & ; entry& operator=(dictionary_type) &; entry& operator=(span) &; + entry& operator=(string_view) &; + entry& operator=(string_type) &; entry& operator=(list_type) &; entry& operator=(integer_type) &; entry& operator=(preformatted_type) &; // hidden + // this is here to disambiguate between std::string and string_view. It + // needs to be a template to prevent implicit conversions from literal 0 template ::value - || std::is_same::value - || std::is_same::value>::type> - entry& operator=(U v) & - { - destruct(); - new(&data) string_type(std::move(v)); - m_type = string_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } + std::is_same::value>::type> + entry& operator=(U v) &; // The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions // are accessors that return the respective type. If the ``entry`` object @@ -220,7 +235,7 @@ namespace libtorrent { preformatted_type const& preformatted() const; // swaps the content of *this* with ``e``. - void swap(entry& e); + using variant_type::swap; // All of these functions requires the entry to be a dictionary, if it // isn't they will throw ``system_error``. @@ -249,41 +264,26 @@ namespace libtorrent { // of the bencoded structure, with JSON-style syntax std::string to_string(bool single_line = false) const; - protected: - - void construct(data_type t); - void copy(const entry& e); - void destruct(); - private: - aux::aligned_union<1 - , list_type - , dictionary_type - , preformatted_type - , string_type - , integer_type - >::type data; - - // the bitfield is used so that the m_type_queried field still fits, so - // that the ABI is the same for debug builds and release builds. It - // appears to be very hard to match debug builds with debug versions of - // libtorrent - std::uint8_t m_type:7; - - public: - // hidden - // in debug mode this is set to false by bdecode to indicate that the - // program has not yet queried the type of this entry, and should not - // assume that it has a certain type. This is asserted in the accessor - // functions. This does not apply if exceptions are used. - mutable std::uint8_t m_type_queried:1; + template T& get(); + template T const& get() const; }; // hidden TORRENT_EXPORT bool operator==(entry const& lhs, entry const& rhs); inline bool operator!=(entry const& lhs, entry const& rhs) { return !(lhs == rhs); } + // hidden + // explicit template declaration + extern template + entry::entry(char const*); + + // hidden + // explicit template declaration + extern template + entry& entry::operator=(char const*) &; + namespace aux { // internal diff --git a/include/libtorrent/fwd.hpp b/include/libtorrent/fwd.hpp index fec5e99b8fd..a6087b15059 100644 --- a/include/libtorrent/fwd.hpp +++ b/include/libtorrent/fwd.hpp @@ -152,7 +152,7 @@ struct storage_holder; struct disk_observer; // include/libtorrent/entry.hpp -class entry; +struct entry; // include/libtorrent/error_code.hpp struct storage_error; diff --git a/include/libtorrent/kademlia/dht_observer.hpp b/include/libtorrent/kademlia/dht_observer.hpp index e3359fd68f2..61266aee624 100644 --- a/include/libtorrent/kademlia/dht_observer.hpp +++ b/include/libtorrent/kademlia/dht_observer.hpp @@ -20,7 +20,7 @@ see LICENSE file. namespace libtorrent { -class entry; +struct entry; namespace aux { struct listen_socket_handle; diff --git a/include/libtorrent/kademlia/dht_storage.hpp b/include/libtorrent/kademlia/dht_storage.hpp index 635a31cbc22..5c85ea0f14a 100644 --- a/include/libtorrent/kademlia/dht_storage.hpp +++ b/include/libtorrent/kademlia/dht_storage.hpp @@ -24,7 +24,7 @@ see LICENSE file. #include namespace libtorrent { - class entry; + struct entry; struct settings_interface; } diff --git a/include/libtorrent/kademlia/rpc_manager.hpp b/include/libtorrent/kademlia/rpc_manager.hpp index 49023dd7826..a9961035525 100644 --- a/include/libtorrent/kademlia/rpc_manager.hpp +++ b/include/libtorrent/kademlia/rpc_manager.hpp @@ -27,7 +27,7 @@ see LICENSE file. #include namespace libtorrent { -class entry; +struct entry; namespace aux { struct session_settings; } diff --git a/src/entry.cpp b/src/entry.cpp index 00d4f7383ca..aaeb7734259 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -86,16 +86,8 @@ namespace aux { } // aux namespace { - - [[noreturn]] inline void throw_error() - { aux::throw_ex(errors::invalid_entry_type); } - - template - void call_destructor(T* o) - { - TORRENT_ASSERT(o); - o->~T(); - } +[[noreturn]] inline void throw_error() +{ aux::throw_ex(errors::invalid_entry_type); } } // anonymous entry& entry::operator[](string_view key) @@ -152,251 +144,104 @@ namespace { entry::data_type entry::type() const { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return entry::data_type(m_type); - } - - entry::~entry() { destruct(); } - - entry& entry::operator=(const entry& e) & - { - if (&e == this) return *this; - destruct(); - copy(e); - return *this; - } - - entry& entry::operator=(entry&& e) & noexcept - { - if (&e == this) return *this; - destruct(); - const auto t = e.type(); - switch (t) - { - case int_t: - new (&data) integer_type(std::move(e.integer())); - break; - case string_t: - new (&data) string_type(std::move(e.string())); - break; - case list_t: - new (&data) list_type(std::move(e.list())); - break; - case dictionary_t: - new (&data) dictionary_type(std::move(e.dict())); - break; - case undefined_t: - break; - case preformatted_t: - new (&data) preformatted_type(std::move(e.preformatted())); - break; - } - m_type = t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - - entry::integer_type& entry::integer() - { - if (m_type == undefined_t) construct(int_t); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - if (m_type != int_t) throw_error(); - TORRENT_ASSERT(m_type == int_t); - return *reinterpret_cast(&data); - } - - entry::integer_type const& entry::integer() const - { - if (m_type != int_t) throw_error(); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - TORRENT_ASSERT(m_type == int_t); - return *reinterpret_cast(&data); - } - - entry::string_type& entry::string() - { - if (m_type == undefined_t) construct(string_t); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - if (m_type != string_t) throw_error(); - TORRENT_ASSERT(m_type == string_t); - return *reinterpret_cast(&data); - } - - entry::string_type const& entry::string() const - { - if (m_type != string_t) throw_error(); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - TORRENT_ASSERT(m_type == string_t); - return *reinterpret_cast(&data); + return static_cast(index()); } - entry::list_type& entry::list() - { - if (m_type == undefined_t) construct(list_t); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - if (m_type != list_t) throw_error(); - TORRENT_ASSERT(m_type == list_t); - return *reinterpret_cast(&data); - } + entry::~entry() = default; - entry::list_type const& entry::list() const - { - if (m_type != list_t) throw_error(); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - TORRENT_ASSERT(m_type == list_t); - return *reinterpret_cast(&data); - } + entry& entry::operator=(entry const& e) & = default; + entry& entry::operator=(entry&& e) & = default; - entry::dictionary_type& entry::dict() - { - if (m_type == undefined_t) construct(dictionary_t); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - if (m_type != dictionary_t) throw_error(); - TORRENT_ASSERT(m_type == dictionary_t); - return *reinterpret_cast(&data); - } + entry& entry::operator=(dictionary_type d) & { emplace(std::move(d)); return *this; } + entry& entry::operator=(span str) & { emplace(str.data(), str.size()); return *this; } + entry& entry::operator=(string_view str) & { emplace(str.data(), str.size()); return *this; } + entry& entry::operator=(string_type str) & { emplace(std::move(str)); return *this; } + entry& entry::operator=(list_type i) & { emplace(std::move(i)); return *this; } + entry& entry::operator=(integer_type i) & { emplace(i); return *this; } + entry& entry::operator=(preformatted_type d) & { emplace(std::move(d)); return *this; } - entry::dictionary_type const& entry::dict() const + template + entry& entry::operator=(U v) & { - if (m_type != dictionary_t) throw_error(); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - TORRENT_ASSERT(m_type == dictionary_t); - return *reinterpret_cast(&data); + emplace(v); + return *this; } - entry::preformatted_type& entry::preformatted() - { - if (m_type == undefined_t) construct(preformatted_t); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - if (m_type != preformatted_t) throw_error(); - TORRENT_ASSERT(m_type == preformatted_t); - return *reinterpret_cast(&data); - } + // explicit template instantiation + template TORRENT_EXPORT + entry& entry::operator=(char const*) &; - entry::preformatted_type const& entry::preformatted() const + template + T& entry::get() { - if (m_type != preformatted_t) throw_error(); -#ifdef BOOST_NO_EXCEPTIONS - TORRENT_ASSERT(m_type_queried); -#endif - TORRENT_ASSERT(m_type == preformatted_t); - return *reinterpret_cast(&data); + if (std::holds_alternative(*this)) emplace(); + else if (!std::holds_alternative(*this)) throw_error(); + return std::get(*this); } - entry::entry() - : m_type(undefined_t) + template + T const& entry::get() const { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif + if (!std::holds_alternative(*this)) throw_error(); + return std::get(*this); } - entry::entry(data_type t) - : m_type(undefined_t) - { - construct(t); -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - } + entry::integer_type& entry::integer() { return get(); } + entry::integer_type const& entry::integer() const { return get(); } + entry::string_type& entry::string() { return get(); } + entry::string_type const& entry::string() const { return get(); } + entry::list_type& entry::list() { return get(); } + entry::list_type const& entry::list() const { return get(); } + entry::dictionary_type& entry::dict() { return get(); } + entry::dictionary_type const& entry::dict() const { return get(); } + entry::preformatted_type& entry::preformatted() { return get(); } + entry::preformatted_type const& entry::preformatted() const { return get(); } - entry::entry(const entry& e) - : m_type(undefined_t) + entry::entry() : variant_type(std::in_place_type) {} + entry::entry(data_type t) : variant_type(std::in_place_type) { - copy(e); -#if TORRENT_USE_ASSERTS - m_type_queried = e.m_type_queried; -#endif + switch (t) + { + case int_t: emplace(); break; + case string_t: emplace(); break; + case list_t: emplace(); break; + case dictionary_t: emplace(); break; + case undefined_t: emplace(); break; + case preformatted_t: emplace(); break; + } } - entry::entry(entry&& e) noexcept - : m_type(undefined_t) - { - this->operator=(std::move(e)); - } + entry::entry(const entry& e) = default; + entry::entry(entry&& e) noexcept = default; entry::entry(bdecode_node const& n) - : m_type(undefined_t) + : variant_type(std::in_place_type) { this->operator=(n); } - entry::entry(dictionary_type v) - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) dictionary_type(std::move(v)); - m_type = dictionary_t; - } + entry::entry(dictionary_type v) : variant_type(std::move(v)) {} + entry::entry(list_type v) : variant_type(std::move(v)) {} + entry::entry(span v) : variant_type(std::in_place_type, v.data(), v.size()) {} + entry::entry(string_view v) : variant_type(std::in_place_type, v.data(), v.size()) {} + entry::entry(string_type s) : variant_type(std::move(s)) {} - entry::entry(span v) - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) string_type(v.data(), std::size_t(v.size())); - m_type = string_t; - } + template + entry::entry(U v) // NOLINT + : variant_type(std::in_place_type, std::move(v)) + {} - entry::entry(list_type v) - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) list_type(std::move(v)); - m_type = list_t; - } + // explicit template instantiation + template TORRENT_EXPORT + entry::entry(char const*); - entry::entry(integer_type v) - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) integer_type(std::move(v)); - m_type = int_t; - } - - entry::entry(preformatted_type v) - : m_type(undefined_t) - { -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - new(&data) preformatted_type(std::move(v)); - m_type = preformatted_t; - } + entry::entry(integer_type v) : variant_type(std::move(v)) {} + entry::entry(preformatted_type v) : variant_type(std::move(v)) {} // convert a bdecode_node into an old school entry entry& entry::operator=(bdecode_node const& e) & { - destruct(); + emplace(); switch (e.type()) { case bdecode_node::string_t: @@ -431,229 +276,10 @@ namespace { return *this; } - entry& entry::operator=(preformatted_type v) & - { - destruct(); - new(&data) preformatted_type(std::move(v)); - m_type = preformatted_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - - entry& entry::operator=(dictionary_type v) & - { - destruct(); - new(&data) dictionary_type(std::move(v)); - m_type = dictionary_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - - entry& entry::operator=(span v) & - { - destruct(); - new(&data) string_type(v.data(), std::size_t(v.size())); - m_type = string_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - - entry& entry::operator=(list_type v) & - { - destruct(); - new(&data) list_type(std::move(v)); - m_type = list_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - - entry& entry::operator=(integer_type v) & - { - destruct(); - new(&data) integer_type(std::move(v)); - m_type = int_t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - return *this; - } - bool operator==(entry const& lhs, entry const& rhs) { - if (lhs.type() != rhs.type()) return false; - - switch (lhs.type()) - { - case entry::int_t: - return lhs.integer() == rhs.integer(); - case entry::string_t: - return lhs.string() == rhs.string(); - case entry::list_t: - return lhs.list() == rhs.list(); - case entry::dictionary_t: - return lhs.dict() == rhs.dict(); - case entry::preformatted_t: - return lhs.preformatted() == rhs.preformatted(); - case entry::undefined_t: - return true; - } - return false; - } - - void entry::construct(data_type t) - { - switch (t) - { - case int_t: - new (&data) integer_type(0); - break; - case string_t: - new (&data) string_type; - break; - case list_t: - new (&data) list_type; - break; - case dictionary_t: - new (&data) dictionary_type; - break; - case undefined_t: - break; - case preformatted_t: - new (&data) preformatted_type; - break; - } - m_type = t; -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - } - - void entry::copy(entry const& e) - { - switch (e.type()) - { - case int_t: - new (&data) integer_type(e.integer()); - break; - case string_t: - new (&data) string_type(e.string()); - break; - case list_t: - new (&data) list_type(e.list()); - break; - case dictionary_t: - new (&data) dictionary_type(e.dict()); - 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(); -#if TORRENT_USE_ASSERTS - m_type_queried = true; -#endif - } - - void entry::destruct() - { - switch(m_type) - { - case int_t: - call_destructor(reinterpret_cast(&data)); - break; - case string_t: - call_destructor(reinterpret_cast(&data)); - break; - case list_t: - call_destructor(reinterpret_cast(&data)); - break; - case dictionary_t: - call_destructor(reinterpret_cast(&data)); - break; - case preformatted_t: - call_destructor(reinterpret_cast(&data)); - break; - default: - TORRENT_ASSERT(m_type == undefined_t); - break; - } - m_type = undefined_t; -#if TORRENT_USE_ASSERTS - m_type_queried = false; -#endif - } - - void entry::swap(entry& e) - { - bool clear_this = false; - bool clear_that = false; - - if (m_type == undefined_t && e.m_type == undefined_t) - return; - - if (m_type == undefined_t) - { - construct(data_type(e.m_type)); - clear_that = true; - } - - if (e.m_type == undefined_t) - { - e.construct(data_type(m_type)); - clear_this = true; - } - - if (m_type == e.m_type) - { - switch (m_type) - { - case int_t: - std::swap(*reinterpret_cast(&data) - , *reinterpret_cast(&e.data)); - break; - case string_t: - std::swap(*reinterpret_cast(&data) - , *reinterpret_cast(&e.data)); - break; - case list_t: - std::swap(*reinterpret_cast(&data) - , *reinterpret_cast(&e.data)); - break; - case dictionary_t: - std::swap(*reinterpret_cast(&data) - , *reinterpret_cast(&e.data)); - break; - case preformatted_t: - std::swap(*reinterpret_cast(&data) - , *reinterpret_cast(&e.data)); - break; - default: - break; - } - - if (clear_this) - destruct(); - - if (clear_that) - e.destruct(); - } - else - { - // currently, only swapping entries of the same type or where one - // of the entries is uninitialized is supported. - TORRENT_ASSERT_FAIL(); - } + return static_cast(lhs) + == static_cast(rhs); } namespace { @@ -674,77 +300,77 @@ namespace { out.resize(out.size() + size_t(indent), ' '); } - void print_list(std::string&, entry const&, int, bool); - void print_dict(std::string&, entry const&, int, bool); - - void to_string_impl(std::string& out, entry const& e, int const indent - , bool const single_line) + struct to_string_visitor { - TORRENT_ASSERT(indent >= 0); - switch (e.type()) + std::string& out; + int indent; + bool single_line; + + void operator()(entry::integer_type i) const + { + out += libtorrent::to_string(i).data(); + } + + void operator()(entry::string_type const& s) const { - case entry::int_t: - out += libtorrent::to_string(e.integer()).data(); - break; - case entry::string_t: out += "'"; - out += print_string(e.string()); + out += print_string(s); out += "'"; - break; - case entry::list_t: - print_list(out, e, indent + 1, single_line); - break; - case entry::dictionary_t: - print_dict(out, e, indent + 1, single_line); - break; - case entry::preformatted_t: - out += ""; - break; - case entry::undefined_t: - out += ""; - break; } - } - void print_list(std::string& out, entry const& e - , int const indent, bool const single_line) - { - out += single_line ? "[ " : "[\n"; - bool first = true; - for (auto const& item : e.list()) + void operator()(entry::dictionary_type const& d) { - if (!first) out += single_line ? ", " : ",\n"; - first = false; - if (!single_line) add_indent(out, indent); - to_string_impl(out, item, indent, single_line); + out += single_line ? "{ " : "{\n"; + bool first = true; + ++indent; + for (auto const& item : d) + { + if (!first) out += single_line ? ", " : ",\n"; + first = false; + if (!single_line) add_indent(out, indent); + out += "'"; + out += print_string(item.first); + out += "': "; + + std::visit(*this, static_cast(item.second)); + } + --indent; + out += " }"; } - out += " ]"; - } - void print_dict(std::string& out, entry const& e - , int const indent, bool const single_line) - { - out += single_line ? "{ " : "{\n"; - bool first = true; - for (auto const& item : e.dict()) + void operator()(entry::list_type const& l) { - if (!first) out += single_line ? ", " : ",\n"; - first = false; - if (!single_line) add_indent(out, indent); - out += "'"; - out += print_string(item.first); - out += "': "; + out += single_line ? "[ " : "[\n"; + bool first = true; + ++indent; + for (auto const& item : l) + { + if (!first) out += single_line ? ", " : ",\n"; + first = false; + if (!single_line) add_indent(out, indent); - to_string_impl(out, item.second, indent+1, single_line); + std::visit(*this, static_cast(item)); + } + out += " ]"; + --indent; } - out += " }"; - } + + void operator()(entry::preformatted_type const&) const + { + out += ""; + } + + void operator()(entry::uninitialized_type const&) const + { + out += ""; + } + }; } std::string entry::to_string(bool const single_line) const { std::string ret; - to_string_impl(ret, *this, 0, single_line); + std::visit(to_string_visitor{ret, 0, single_line}, static_cast(*this)); return ret; } diff --git a/test/test_bencoding.cpp b/test/test_bencoding.cpp index a45ff9335c0..5134b5825e9 100644 --- a/test/test_bencoding.cpp +++ b/test/test_bencoding.cpp @@ -195,7 +195,15 @@ TORRENT_TEST(print_deep_dict) e["ints"].list().push_back(entry(2)); e["ints"].list().push_back(entry(3)); e["a"] = "foobar"; - TEST_EQUAL(e.to_string(), "{\n 'a': 'foobar',\n 'ints': [\n 1,\n 2,\n 3 ],\n 'strings': [\n 'foo',\n 'bar' ] }"); + TEST_EQUAL(e.to_string(), R"({ + 'a': 'foobar', + 'ints': [ + 1, + 2, + 3 ], + 'strings': [ + 'foo', + 'bar' ] })"); } TORRENT_TEST(dict_constructor) @@ -203,7 +211,9 @@ TORRENT_TEST(dict_constructor) entry::dictionary_type e{{std::string("foo"), std::string("bar")}, {std::string("bar"), 1234}}; - TEST_EQUAL(entry(e).to_string(), "{\n 'bar': 1234,\n 'foo': 'bar' }"); + TEST_EQUAL(entry(e).to_string(), R"({ + 'bar': 1234, + 'foo': 'bar' })"); } TORRENT_TEST(integer_to_str) diff --git a/test/test_dht.cpp b/test/test_dht.cpp index a775ce9e679..6992d0abc95 100644 --- a/test/test_dht.cpp +++ b/test/test_dht.cpp @@ -261,7 +261,8 @@ void send_dht_request(node& node, char const* msg, udp::endpoint const& ep e["t"] = t; e["y"] = "q"; e["a"] = args.a; - e["a"].dict().insert(std::make_pair("id", generate_next().to_string())); + // this will only insert the "id" if it wasn't already added by args.a + e["a"].dict().insert(std::pair("id", generate_next().to_string())); char msg_buf[1500]; int size = bencode(msg_buf, e); @@ -306,7 +307,8 @@ void send_dht_response(node& node, bdecode_node const& request, udp::endpoint co e["t"] = std::string(request.dict_find_string_value("t")); // e["ip"] = endpoint_to_bytes(ep); e["r"] = args.a; - e["r"].dict().insert(std::make_pair("id", generate_next().to_string())); + // this will only insert the "id" if it wasn't already added by args.a + e["r"].dict().insert(std::pair("id", generate_next().to_string())); char msg_buf[1500]; int const size = bencode(msg_buf, e); @@ -2764,7 +2766,7 @@ TORRENT_TEST(multi_home) e["q"] = "ping"; e["t"] = "10"; e["y"] = "q"; - e["a"].dict().insert(std::make_pair("id", generate_next().to_string())); + e["a"]["id"] = generate_next().to_string(); char msg_buf[1500]; int size = bencode(msg_buf, e); diff --git a/tools/gen_fwd.py b/tools/gen_fwd.py index c24be432afb..103a2df4d2a 100644 --- a/tools/gen_fwd.py +++ b/tools/gen_fwd.py @@ -6,7 +6,8 @@ file_header = '''/* Copyright (c) 2017-2018, Steven Siloti -Copyright (c) 2017-2019, Arvid Norberg +Copyright (c) 2017-2020, Arvid Norberg +Copyright (c) 2020, Alden Torres All rights reserved. Redistribution and use in source and binary forms, with or without