improve node-id operations (#693)
improve node-id operations, to operate on 32 bits at a time instead of 8. move out some code into sha1_hash.cpp, from the header file
This commit is contained in:
parent
f56c1f8b2f
commit
077e9bb10c
|
@ -75,6 +75,7 @@ set(sources
|
|||
proxy_settings
|
||||
session_stats
|
||||
settings_pack
|
||||
sha1_hash
|
||||
socket_io
|
||||
socket_type
|
||||
socks5_stream
|
||||
|
|
1
Jamfile
1
Jamfile
|
@ -601,6 +601,7 @@ SOURCES =
|
|||
session_impl
|
||||
session_call
|
||||
settings_pack
|
||||
sha1_hash
|
||||
socket_io
|
||||
socket_type
|
||||
socks5_stream
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<boost::uint32_t>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/ed25519.hpp"
|
||||
#include <numeric>
|
||||
#include <cstdarg>
|
||||
#include <tuple>
|
||||
|
||||
#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<tcp::endpoint> const& peers)
|
||||
{
|
||||
entry::list_type& pe = r["values"].list();
|
||||
for (std::set<tcp::endpoint>::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<std::pair<int, char const*>> 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<char const*, char const*, int>;
|
||||
|
||||
std::vector<std::tuple<char const*, char const*, int>> 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
|
||||
|
||||
|
|
|
@ -110,3 +110,37 @@ TORRENT_TEST(sha1_hash)
|
|||
TEST_CHECK(h1 == to_hash("0000000070000000000000000000000000000000"));
|
||||
}
|
||||
|
||||
TORRENT_TEST(count_leading_zeroes)
|
||||
{
|
||||
std::vector<std::pair<char const*, int>> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue