diff --git a/CMakeLists.txt b/CMakeLists.txt index dc79c6c5b..d5123c64a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ set(sources ConvertUTF xml_parse version + ffs # -- extensions -- ut_pex diff --git a/Jamfile b/Jamfile index d9a6d4a29..046256adb 100644 --- a/Jamfile +++ b/Jamfile @@ -679,6 +679,7 @@ SOURCES = session_settings proxy_settings file_progress + ffs # -- extensions -- ut_pex diff --git a/include/libtorrent/Makefile.am b/include/libtorrent/Makefile.am index d734ea1a1..e1cfc71bc 100644 --- a/include/libtorrent/Makefile.am +++ b/include/libtorrent/Makefile.am @@ -171,6 +171,7 @@ nobase_include_HEADERS = \ aux_/openssl.hpp \ aux_/byteswap.hpp \ aux_/cppint_import_export.hpp \ + aux_/ffs.hpp \ \ extensions/smart_ban.hpp \ extensions/ut_metadata.hpp \ diff --git a/include/libtorrent/aux_/cpuid.hpp b/include/libtorrent/aux_/cpuid.hpp index fda93d61d..6a5f22954 100644 --- a/include/libtorrent/aux_/cpuid.hpp +++ b/include/libtorrent/aux_/cpuid.hpp @@ -45,4 +45,3 @@ namespace libtorrent { namespace aux } } #endif // TORRENT_CPUID_HPP_INCLUDED - diff --git a/include/libtorrent/aux_/ffs.hpp b/include/libtorrent/aux_/ffs.hpp new file mode 100644 index 000000000..c7fa98bcd --- /dev/null +++ b/include/libtorrent/aux_/ffs.hpp @@ -0,0 +1,53 @@ +/* + +Copyright (c) 2014-2016, Arvid Norberg, Alden Torres +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. + +*/ + +#ifndef TORRENT_FFS_HPP_INCLUDE +#define TORRENT_FFS_HPP_INCLUDE + +#include +#include "libtorrent/export.hpp" +#include "libtorrent/span.hpp" + +namespace libtorrent { namespace aux +{ + // these functions expect the range to be in big-endian byte order + TORRENT_EXTRA_EXPORT int clz_sw(span buf); + // if this function is called in an unsupported platform, returns -1 + // consider call always clz(buf) + TORRENT_EXTRA_EXPORT int clz_hw(span buf); + + // this function statically determines if hardware or software is used + // and expect the range to be in big-endian byte order + TORRENT_EXTRA_EXPORT int clz(span buf); +}} + +#endif // TORRENT_FFS_HPP_INCLUDE diff --git a/include/libtorrent/sha1_hash.hpp b/include/libtorrent/sha1_hash.hpp index 5ded405df..0c43bf3aa 100644 --- a/include/libtorrent/sha1_hash.hpp +++ b/include/libtorrent/sha1_hash.hpp @@ -41,19 +41,12 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/aux_/byteswap.hpp" +#include "libtorrent/aux_/ffs.hpp" #if TORRENT_USE_IOSTREAM #include #endif // TORRENT_USE_IOSTREAM -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - namespace libtorrent { @@ -80,7 +73,7 @@ namespace libtorrent static sha1_hash max() { sha1_hash ret; - memset(ret.m_number, 0xff, size()); + std::memset(ret.m_number, 0xff, size()); return ret; } @@ -90,7 +83,7 @@ namespace libtorrent static sha1_hash min() { sha1_hash ret; - memset(ret.m_number, 0, size()); + std::memset(ret.m_number, 0, size()); return ret; } @@ -137,11 +130,11 @@ namespace libtorrent // standard comparison operators bool operator==(sha1_hash const& n) const { - return std::equal(n.m_number, n.m_number+number_size, m_number); + return std::equal(n.m_number, n.m_number + number_size, m_number); } bool operator!=(sha1_hash const& n) const { - return !std::equal(n.m_number, n.m_number+number_size, m_number); + return !std::equal(n.m_number, n.m_number + number_size, m_number); } bool operator<(sha1_hash const& n) const { @@ -155,7 +148,10 @@ namespace libtorrent return false; } - int count_leading_zeroes() const; + int count_leading_zeroes() const + { + return aux::clz({m_number, number_size}); + } // returns a bit-wise negated copy of the sha1-hash sha1_hash operator~() const @@ -281,4 +277,3 @@ namespace std { } #endif // TORRENT_SHA1_HASH_HPP_INCLUDED - diff --git a/src/Makefile.am b/src/Makefile.am index 4ca6a9f6e..d34a498b7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -145,6 +145,7 @@ libtorrent_rasterbar_la_SOURCES = \ xml_parse.cpp \ version.cpp \ file_progress.cpp \ + ffs.cpp \ \ $(KADEMLIA_SOURCES) diff --git a/src/ffs.cpp b/src/ffs.cpp new file mode 100644 index 000000000..724e8f21c --- /dev/null +++ b/src/ffs.cpp @@ -0,0 +1,110 @@ +/* + +Copyright (c) 2014-2016, Arvid Norberg, Alden Torres +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/config.hpp" +#include "libtorrent/aux_/ffs.hpp" +#include "libtorrent/aux_/byteswap.hpp" + +#include "libtorrent/aux_/disable_warnings_push.hpp" + +#if (defined _MSC_VER && _MSC_VER >= 1600) +#include +#endif + +#include "libtorrent/aux_/disable_warnings_pop.hpp" + +namespace libtorrent { namespace aux +{ + int clz_sw(span buf) + { + int num = int(buf.size()); + std::uint32_t const* ptr = buf.data(); + + for (int i = 0; i < num; i++) + { + if (ptr[i] == 0) continue; + std::uint32_t v = aux::network_to_host(ptr[i]); + + // 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 i * 32 + 31 - MultiplyDeBruijnBitPosition[ + static_cast(v * 0x07C4ACDDU) >> 27]; + } + + return num * 32; + } + + int clz_hw(span buf) + { + int num = int(buf.size()); + std::uint32_t const* ptr = buf.data(); + + for (int i = 0; i < num; i++) + { + if (ptr[i] == 0) continue; + std::uint32_t v = aux::network_to_host(ptr[i]); + +#if TORRENT_HAS_BUILTIN_CLZ + return i * 32 + __builtin_clz(v); +#elif defined _MSC_VER + DWORD pos; + _BitScanReverse(&pos, v); + return i * 32 + 31 - pos; +#else + return -1; +#endif + } + + return num * 32; + } + + int clz(span buf) + { +#if TORRENT_HAS_BUILTIN_CLZ || defined _MSC_VER + return aux::clz_hw(buf); +#else + return aux::clz_sw(buf); +#endif + } + +}} diff --git a/src/sha1_hash.cpp b/src/sha1_hash.cpp index 5b7e7c927..b5fb48b2b 100644 --- a/src/sha1_hash.cpp +++ b/src/sha1_hash.cpp @@ -31,7 +31,6 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "libtorrent/sha1_hash.hpp" -#include "libtorrent/aux_/cpuid.hpp" #include "libtorrent/hex.hpp" // to_hex, from_hex #if TORRENT_USE_IOSTREAM @@ -63,45 +62,6 @@ namespace libtorrent #endif // TORRENT_USE_IOSTREAM - int sha1_hash::count_leading_zeroes() const - { - int ret = 0; - for (int i = 0; i < number_size; ++i) - { - std::uint32_t v = aux::network_to_host(m_number[i]); - if (v == 0) - { - ret += 32; - continue; - } - -#if TORRENT_HAS_BUILTIN_CLZ - return ret + __builtin_clz(v); -#elif TORRENT_HAS_SSE && defined _MSC_VER - DWORD pos; - _BitScanReverse(&pos, v); - return ret + 31 - pos; -#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); @@ -130,12 +90,12 @@ namespace libtorrent 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 + 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]); + m_number[number_size - 1] <<= n; + m_number[number_size - 1] = aux::host_to_network(m_number[number_size - 1]); } return *this; } @@ -162,13 +122,13 @@ namespace libtorrent // 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]); + 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 - 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; @@ -178,4 +138,3 @@ namespace libtorrent } } - diff --git a/test/Jamfile b/test/Jamfile index 184fccd7b..abc3bce8b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -153,6 +153,7 @@ test-suite libtorrent : [ run test_sha1_hash.cpp ] [ run test_bitfield.cpp ] [ run test_crc32.cpp ] + [ run test_ffs.cpp ] [ run test_receive_buffer.cpp ] [ run test_alert_manager.cpp ] [ run test_magnet.cpp ] @@ -230,6 +231,7 @@ alias arm-tests : test_sha1_hash test_bitfield test_crc32 + test_ffs ; explicit arm-tests ; diff --git a/test/Makefile.am b/test/Makefile.am index f6d88bd6b..380f1acad 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -43,7 +43,8 @@ test_programs = \ test_enum_net \ test_file_progress \ test_linked_list \ - test_direct_dht + test_direct_dht \ + test_ffs if ENABLE_TESTS check_PROGRAMS = $(test_programs) @@ -224,6 +225,7 @@ test_remap_files_SOURCES = test_remap_files.cpp test_file_progress_SOURCES = test_file_progress.cpp test_linked_list_SOURCES = test_linked_list.cpp test_direct_dht_SOURCES = test_direct_dht.cpp +test_ffs_SOURCES = test_ffs.cpp LDADD = libtest.la $(top_builddir)/src/libtorrent-rasterbar.la diff --git a/test/test_ffs.cpp b/test/test_ffs.cpp new file mode 100644 index 000000000..9303c47b0 --- /dev/null +++ b/test/test_ffs.cpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2015, Arvid Norberg, Alden Torres +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/span.hpp" +#include "libtorrent/hex.hpp" // from_hex +#include "libtorrent/aux_/ffs.hpp" + +using namespace libtorrent; + +static void to_binary(char const* s, std::uint32_t* buf) +{ + aux::from_hex({s, 40}, reinterpret_cast(&buf[0])); +} + +TORRENT_TEST(clz) +{ + 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) + { + std::fprintf(stderr, "%s\n", t.first); + std::uint32_t buf[5]; + to_binary(t.first, buf); + TEST_EQUAL(aux::clz_sw({buf, 5}), t.second); + TEST_EQUAL(aux::clz_hw({buf, 5}), t.second); + TEST_EQUAL(aux::clz({buf, 5}), t.second); + } +} diff --git a/test/test_sha1_hash.cpp b/test/test_sha1_hash.cpp index 774cf5285..6eb686a75 100644 --- a/test/test_sha1_hash.cpp +++ b/test/test_sha1_hash.cpp @@ -143,8 +143,4 @@ TORRENT_TEST(count_leading_zeroes) std::fprintf(stderr, "%s\n", t.first); TEST_EQUAL(to_hash(t.first).count_leading_zeroes(), t.second); } - -#if TORRENT_HAS_ARM && !TORRENT_HAS_BUILTIN_CLZ -#error "expected built-in clz for arm architecture" -#endif }