diff --git a/CMakeLists.txt b/CMakeLists.txt index 62a660384..922951bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ set(sources proxy_settings session_stats settings_pack + sha1_hash socket_io socket_type socks5_stream diff --git a/Jamfile b/Jamfile index 2193df710..d72ed522f 100644 --- a/Jamfile +++ b/Jamfile @@ -601,6 +601,7 @@ SOURCES = session_impl session_call settings_pack + sha1_hash socket_io socket_type socks5_stream diff --git a/include/libtorrent/sha1_hash.hpp b/include/libtorrent/sha1_hash.hpp index d17fd920c..457b228db 100644 --- a/include/libtorrent/sha1_hash.hpp +++ b/include/libtorrent/sha1_hash.hpp @@ -133,81 +133,10 @@ namespace libtorrent } // shift left ``n`` bits. - sha1_hash& operator<<=(int n) - { - TORRENT_ASSERT(n >= 0); - const int num_words = n / 32; - if (num_words >= number_size) - { - std::memset(m_number, 0, size); - return *this; - } - - if (num_words > 0) - { - std::memmove(m_number, m_number + num_words - , (number_size - num_words) * sizeof(boost::uint32_t)); - std::memset(m_number + (number_size - num_words) - , 0, num_words * sizeof(boost::uint32_t)); - n -= num_words * 32; - } - if (n > 0) - { - // keep in mind that the uint32_t are stored in network - // byte order, so they have to be byteswapped before - // applying the shift operations, and then byteswapped - // back again. - m_number[0] = aux::network_to_host(m_number[0]); - for (int i = 0; i < number_size - 1; ++i) - { - m_number[i] <<= n; - m_number[i+1] = aux::network_to_host(m_number[i+1]); - m_number[i] |= m_number[i+1] >> (32 - n); - m_number[i] = aux::host_to_network(m_number[i]); - } - m_number[number_size-1] <<= n; - m_number[number_size-1] = aux::host_to_network(m_number[number_size-1]); - } - return *this; - } + sha1_hash& operator<<=(int n); // shift r ``n`` bits. - sha1_hash& operator>>=(int n) - { - TORRENT_ASSERT(n >= 0); - const int num_words = n / 32; - if (num_words >= number_size) - { - std::memset(m_number, 0, size_t(size)); - return *this; - } - if (num_words > 0) - { - std::memmove(m_number + num_words - , m_number, (number_size - num_words) * sizeof(boost::uint32_t)); - std::memset(m_number, 0, num_words * sizeof(boost::uint32_t)); - n -= num_words * 32; - } - if (n > 0) - { - // keep in mind that the uint32_t are stored in network - // byte order, so they have to be byteswapped before - // applying the shift operations, and then byteswapped - // back again. - m_number[number_size-1] = aux::network_to_host(m_number[number_size-1]); - - for (int i = number_size - 1; i > 0; --i) - { - m_number[i] >>= n; - m_number[i-1] = aux::network_to_host(m_number[i-1]); - m_number[i] |= (m_number[i-1] << (32 - n)) & 0xffffffff; - m_number[i] = aux::host_to_network(m_number[i]); - } - m_number[0] >>= n; - m_number[0] = aux::host_to_network(m_number[0]); - } - return *this; - } + sha1_hash& operator>>=(int n); // standard comparison operators bool operator==(sha1_hash const& n) const @@ -230,6 +159,8 @@ namespace libtorrent return false; } + int count_leading_zeroes() const; + // returns a bit-wise negated copy of the sha1-hash sha1_hash operator~() const { diff --git a/src/kademlia/node_id.cpp b/src/kademlia/node_id.cpp index e74d60c19..e8f42520a 100644 --- a/src/kademlia/node_id.cpp +++ b/src/kademlia/node_id.cpp @@ -49,55 +49,25 @@ namespace libtorrent { namespace dht // using the kademlia XOR-metric node_id distance(node_id const& n1, node_id const& n2) { - node_id ret; - node_id::iterator k = ret.begin(); - // TODO: 3 the XORing should be done at full words instead of bytes - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , end(n1.end()); i != end; ++i, ++j, ++k) - { - *k = *i ^ *j; - } - return ret; + return n1 ^ n2; } // returns true if: distance(n1, ref) < distance(n2, ref) bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref) { - // TODO: 3 the XORing should be done at full words instead of bytes - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k) - { - boost::uint8_t lhs = (*i ^ *k); - boost::uint8_t rhs = (*j ^ *k); - if (lhs < rhs) return true; - if (lhs > rhs) return false; - } - return false; + node_id const lhs = n1 ^ ref; + node_id const rhs = n2 ^ ref; + return lhs < rhs; } // returns n in: 2^n <= distance(n1, n2) < 2^(n+1) // useful for finding out which bucket a node belongs to int distance_exp(node_id const& n1, node_id const& n2) { - // TODO: 3 the xoring should be done at full words and _builtin_clz() could - // be used as the last step - int byte = node_id::size - 1; - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , end(n1.end()); i != end; ++i, ++j, --byte) - { - TORRENT_ASSERT(byte >= 0); - boost::uint8_t t = *i ^ *j; - if (t == 0) continue; - // we have found the first non-zero byte - // return the bit-number of the first bit - // that differs - int const bit = byte * 8; - for (int b = 7; b >= 0; --b) - if (t >= (1 << b)) return bit + b; - return bit; - } - - return 0; + // TODO: it's a little bit weird to return 159 - leading zeroes. It should + // probably be 160 - leading zeroes, but all other code in here is tuned to + // this expectation now, and it doesn't really matter (other than complexity) + return (std::max)(159 - distance(n1, n2).count_leading_zeroes(), 0); } node_id generate_id_impl(address const& ip_, boost::uint32_t r) diff --git a/src/sha1_hash.cpp b/src/sha1_hash.cpp new file mode 100644 index 000000000..3f59eebb0 --- /dev/null +++ b/src/sha1_hash.cpp @@ -0,0 +1,158 @@ +/* + +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 "libtorrent/sha1_hash.hpp" +#include "libtorrent/aux_/cpuid.hpp" + +namespace libtorrent +{ + int sha1_hash::count_leading_zeroes() const + { + int ret = 0; + for (int i = 0; i < number_size; ++i) + { + boost::uint32_t v = aux::network_to_host(m_number[i]); + if (v == 0) + { + ret += 32; + continue; + } + +#if TORRENT_HAS_SSE + if (aux::mmx_support) + { +#ifdef __GNUC__ + return ret + __builtin_clz(v); +#else + DWORD pos; + _BitScanReverse(&pos, v); + return ret + 31 - pos; +#endif + } +#else + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious + static const int MultiplyDeBruijnBitPosition[32] = + { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + v |= v >> 1; // first round down to one less than a power of 2 + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + + return ret + MultiplyDeBruijnBitPosition[ + static_cast(v * 0x07C4ACDDU) >> 27]; +#endif + } + return ret; + } + + sha1_hash& sha1_hash::operator<<=(int n) + { + TORRENT_ASSERT(n >= 0); + const int num_words = n / 32; + if (num_words >= number_size) + { + std::memset(m_number, 0, size); + return *this; + } + + if (num_words > 0) + { + std::memmove(m_number, m_number + num_words + , (number_size - num_words) * sizeof(boost::uint32_t)); + std::memset(m_number + (number_size - num_words) + , 0, num_words * sizeof(boost::uint32_t)); + n -= num_words * 32; + } + if (n > 0) + { + // keep in mind that the uint32_t are stored in network + // byte order, so they have to be byteswapped before + // applying the shift operations, and then byteswapped + // back again. + m_number[0] = aux::network_to_host(m_number[0]); + for (int i = 0; i < number_size - 1; ++i) + { + m_number[i] <<= n; + m_number[i+1] = aux::network_to_host(m_number[i+1]); + m_number[i] |= m_number[i+1] >> (32 - n); + m_number[i] = aux::host_to_network(m_number[i]); + } + m_number[number_size-1] <<= n; + m_number[number_size-1] = aux::host_to_network(m_number[number_size-1]); + } + return *this; + } + + sha1_hash& sha1_hash::operator>>=(int n) + { + TORRENT_ASSERT(n >= 0); + const int num_words = n / 32; + if (num_words >= number_size) + { + std::memset(m_number, 0, size_t(size)); + return *this; + } + if (num_words > 0) + { + std::memmove(m_number + num_words + , m_number, (number_size - num_words) * sizeof(boost::uint32_t)); + std::memset(m_number, 0, num_words * sizeof(boost::uint32_t)); + n -= num_words * 32; + } + if (n > 0) + { + // keep in mind that the uint32_t are stored in network + // byte order, so they have to be byteswapped before + // applying the shift operations, and then byteswapped + // back again. + m_number[number_size-1] = aux::network_to_host(m_number[number_size-1]); + + for (int i = number_size - 1; i > 0; --i) + { + m_number[i] >>= n; + m_number[i-1] = aux::network_to_host(m_number[i-1]); + m_number[i] |= (m_number[i-1] << (32 - n)) & 0xffffffff; + m_number[i] = aux::host_to_network(m_number[i]); + } + m_number[0] >>= n; + m_number[0] = aux::host_to_network(m_number[0]); + } + return *this; + } + +} + diff --git a/test/test_dht.cpp b/test/test_dht.cpp index 34dd8c429..075ac8021 100644 --- a/test/test_dht.cpp +++ b/test/test_dht.cpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/ed25519.hpp" #include #include +#include #include "test.hpp" #include "setup_transfer.hpp" @@ -142,12 +143,11 @@ void lazy_from_entry(entry const& e, bdecode_node& l) void write_peers(entry::dictionary_type& r, std::set const& peers) { entry::list_type& pe = r["values"].list(); - for (std::set::const_iterator it = peers.begin() - ; it != peers.end(); ++it) + for (auto const& p : peers) { std::string endpoint(18, '\0'); std::string::iterator out = endpoint.begin(); - libtorrent::detail::write_endpoint(*it, out); + libtorrent::detail::write_endpoint(p, out); endpoint.resize(out - endpoint.begin()); pe.push_back(entry(endpoint)); } @@ -1194,70 +1194,6 @@ void do_test_dht(address(&rand_addr)()) } - // test node-id functions - using namespace libtorrent::dht; - - TEST_EQUAL(generate_prefix_mask(0), to_hash("0000000000000000000000000000000000000000")); - TEST_EQUAL(generate_prefix_mask(1), to_hash("8000000000000000000000000000000000000000")); - TEST_EQUAL(generate_prefix_mask(2), to_hash("c000000000000000000000000000000000000000")); - TEST_EQUAL(generate_prefix_mask(11), to_hash("ffe0000000000000000000000000000000000000")); - TEST_EQUAL(generate_prefix_mask(17), to_hash("ffff800000000000000000000000000000000000")); - TEST_EQUAL(generate_prefix_mask(160), to_hash("ffffffffffffffffffffffffffffffffffffffff")); - - // test kademlia functions - - // distance_exp - - TEST_EQUAL(distance_exp( - to_hash("ffffffffffffffffffffffffffffffffffffffff"), - to_hash("0000000000000000000000000000000000000000")) - , 159); - - TEST_EQUAL(distance_exp( - to_hash("ffffffffffffffffffffffffffffffffffffffff"), - to_hash("7fffffffffffffffffffffffffffffffffffffff")) - , 159); - - TEST_EQUAL(distance_exp( - to_hash("ffffffffffffffffffffffffffffffffffffffff"), - to_hash("ffffffffffffffffffffffffffffffffffffffff")) - , 0); - - TEST_EQUAL(distance_exp( - to_hash("ffffffffffffffffffffffffffffffffffffffff"), - to_hash("fffffffffffffffffffffffffffffffffffffffe")) - , 0); - - TEST_EQUAL(distance_exp( - to_hash("8000000000000000000000000000000000000000"), - to_hash("fffffffffffffffffffffffffffffffffffffffe")) - , 158); - - TEST_EQUAL(distance_exp( - to_hash("c000000000000000000000000000000000000000"), - to_hash("fffffffffffffffffffffffffffffffffffffffe")) - , 157); - - TEST_EQUAL(distance_exp( - to_hash("e000000000000000000000000000000000000000"), - to_hash("fffffffffffffffffffffffffffffffffffffffe")) - , 156); - - TEST_EQUAL(distance_exp( - to_hash("f000000000000000000000000000000000000000"), - to_hash("fffffffffffffffffffffffffffffffffffffffe")) - , 155); - - TEST_EQUAL(distance_exp( - to_hash("f8f2340985723049587230495872304958703294"), - to_hash("f743589043r890f023980f90e203980d090c3840")) - , 155); - - TEST_EQUAL(distance_exp( - to_hash("ffff740985723049587230495872304958703294"), - to_hash("ffff889043r890f023980f90e203980d090c3840")) - , 159 - 16); - { // test kademlia routing table dht_settings s; @@ -2211,7 +2147,7 @@ TORRENT_TEST(dht_dual_stack) bdecode_node nodes46_keys[5]; ret = verify_message(response, nodes46_desc, nodes46_keys, error_string - , sizeof(error_string)); + , sizeof(error_string)); if (ret) { @@ -2817,5 +2753,77 @@ TORRENT_TEST(dht_verify_node_address) TEST_EQUAL(table.size().get<0>(), 1); TEST_EQUAL(nodes.size(), 1); } + +TORRENT_TEST(generate_prefix_mask) +{ + // test node-id functions + using namespace libtorrent::dht; + + std::vector> const test = { + { 0, "0000000000000000000000000000000000000000" }, + { 1, "8000000000000000000000000000000000000000" }, + { 2, "c000000000000000000000000000000000000000" }, + { 11, "ffe0000000000000000000000000000000000000" }, + { 17, "ffff800000000000000000000000000000000000" }, + { 37, "fffffffff8000000000000000000000000000000" }, + { 160, "ffffffffffffffffffffffffffffffffffffffff" }, + }; + + for (auto const& i : test) + { + TEST_EQUAL(generate_prefix_mask(i.first), to_hash(i.second)); + } +} + +TORRENT_TEST(distance_exp) +{ + // distance_exp + + + using tst = std::tuple; + + std::vector> const distance_tests = { + tst{ "ffffffffffffffffffffffffffffffffffffffff" + , "0000000000000000000000000000000000000000", 159 }, + + tst{ "ffffffffffffffffffffffffffffffffffffffff" + , "7fffffffffffffffffffffffffffffffffffffff", 159 }, + + tst{ "ffffffffffffffffffffffffffffffffffffffff" + , "ffffffffffffffffffffffffffffffffffffffff", 0 }, + + tst{ "ffffffffffffffffffffffffffffffffffffffff" + , "fffffffffffffffffffffffffffffffffffffffe", 0 }, + + tst{ "8000000000000000000000000000000000000000" + , "fffffffffffffffffffffffffffffffffffffffe", 158 }, + + tst{ "c000000000000000000000000000000000000000" + , "fffffffffffffffffffffffffffffffffffffffe", 157 }, + + tst{ "e000000000000000000000000000000000000000" + , "fffffffffffffffffffffffffffffffffffffffe", 156 }, + + tst{ "f000000000000000000000000000000000000000" + , "fffffffffffffffffffffffffffffffffffffffe", 155 }, + + tst{ "f8f2340985723049587230495872304958703294" + , "f743589043r890f023980f90e203980d090c3840", 155 }, + + tst{ "ffff740985723049587230495872304958703294" + , "ffff889043r890f023980f90e203980d090c3840", 159 - 16 }, + }; + + for (auto const& t : distance_tests) + { + fprintf(stderr, "%s %s: %d\n" + , std::get<0>(t), std::get<1>(t), std::get<2>(t)); + + TEST_EQUAL(distance_exp( + to_hash(std::get<0>(t)) + , to_hash(std::get<1>(t)) + ), std::get<2>(t)); + } +} #endif diff --git a/test/test_sha1_hash.cpp b/test/test_sha1_hash.cpp index 61228130f..d6f9edc27 100644 --- a/test/test_sha1_hash.cpp +++ b/test/test_sha1_hash.cpp @@ -110,3 +110,37 @@ TORRENT_TEST(sha1_hash) TEST_CHECK(h1 == to_hash("0000000070000000000000000000000000000000")); } +TORRENT_TEST(count_leading_zeroes) +{ + std::vector> const tests = { + { "ffffffffffffffffffffffffffffffffffffffff", 0 }, + { "0000000000000000000000000000000000000000", 160 }, + { "fff0000000000000000000000000000000000000", 0 }, + { "7ff0000000000000000000000000000000000000", 1 }, + { "3ff0000000000000000000000000000000000000", 2 }, + { "1ff0000000000000000000000000000000000000", 3 }, + { "0ff0000000000000000000000000000000000000", 4 }, + { "07f0000000000000000000000000000000000000", 5 }, + { "03f0000000000000000000000000000000000000", 6 }, + { "01f0000000000000000000000000000000000000", 7 }, + { "00f0000000000000000000000000000000000000", 8 }, + { "0070000000000000000000000000000000000000", 9 }, + { "0030000000000000000000000000000000000000", 10 }, + { "0010000000000000000000000000000000000000", 11 }, + { "0000000ffff00000000000000000000000000000", 28 }, + { "00000007fff00000000000000000000000000000", 29 }, + { "00000003fff00000000000000000000000000000", 30 }, + { "00000001fff00000000000000000000000000000", 31 }, + { "00000000fff00000000000000000000000000000", 32 }, + { "000000007ff00000000000000000000000000000", 33 }, + { "000000003ff00000000000000000000000000000", 34 }, + { "000000001ff00000000000000000000000000000", 35 }, + }; + + for (auto const& t : tests) + { + fprintf(stderr, "%s\n", t.first); + TEST_EQUAL(to_hash(t.first).count_leading_zeroes(), t.second); + } +} +