From af565e2b8661d9454d3f1d39164e465f192af2f8 Mon Sep 17 00:00:00 2001 From: Alden Torres Date: Sun, 7 Aug 2016 16:21:08 -0400 Subject: [PATCH] added support for flz (find last zero) and copied bitfield functions from PR #565 (#986) added support for flz (find last zero) and copied bitfield functions from PR #565 --- include/libtorrent/aux_/ffs.hpp | 21 ++++++-- include/libtorrent/bitfield.hpp | 20 +++++++ include/libtorrent/config.hpp | 10 ++++ include/libtorrent/sha1_hash.hpp | 2 +- src/bitfield.cpp | 6 +-- src/ffs.cpp | 80 ++++++++++++++++++++++++--- test/test_bitfield.cpp | 93 ++++++++++++++++++++++++++++++++ test/test_ffs.cpp | 49 +++++++++++++++-- 8 files changed, 262 insertions(+), 19 deletions(-) diff --git a/include/libtorrent/aux_/ffs.hpp b/include/libtorrent/aux_/ffs.hpp index c7fa98bcd..1358025cf 100644 --- a/include/libtorrent/aux_/ffs.hpp +++ b/include/libtorrent/aux_/ffs.hpp @@ -39,15 +39,28 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { namespace aux { + // For a general reference of the problems these routines are about + // see http://en.wikipedia.org/wiki/Find_first_set + // these functions expect the range to be in big-endian byte order - TORRENT_EXTRA_EXPORT int clz_sw(span buf); + TORRENT_EXTRA_EXPORT int count_leading_zeros_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); + // consider call always count_leading_zeros(buf) + TORRENT_EXTRA_EXPORT int count_leading_zeros_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); + TORRENT_EXTRA_EXPORT int count_leading_zeros(span buf); + + // these functions expect the range to be in big-endian byte order + TORRENT_EXTRA_EXPORT int count_trailing_ones_sw(span buf); + // if this function is called in an unsupported platform, returns -1 + // consider call always count_trailing_ones(buf) + TORRENT_EXTRA_EXPORT int count_trailing_ones_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 count_trailing_ones(span buf); }} #endif // TORRENT_FFS_HPP_INCLUDE diff --git a/include/libtorrent/bitfield.hpp b/include/libtorrent/bitfield.hpp index 53153fedf..2e343c4a3 100644 --- a/include/libtorrent/bitfield.hpp +++ b/include/libtorrent/bitfield.hpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" #include "libtorrent/aux_/byteswap.hpp" +#include "libtorrent/aux_/ffs.hpp" #include // for memset and memcpy #include // uint32_t @@ -153,6 +154,25 @@ namespace libtorrent // count the number of bits in the bitfield that are set to 1. int count() const; + int find_first_set() const + { + size_t const num = num_words(); + if (num == 0) return -1; + int const count = aux::count_leading_zeros({&m_buf[1], num}); + return count != int(num) * 32 ? count : -1; + } + int find_last_clear() const + { + size_t const num = num_words(); + if (num == 0) return - 1; + int const size = this->size(); + std::uint32_t const mask = 0xffffffff << (32 - (size & 31)); + std::uint32_t const last = m_buf[num] ^ aux::host_to_network(mask); + int const ext = aux::count_trailing_ones(~last) - (31 - (size % 32)); + return last != 0 + ? (int(num) - 1) * 32 + ext + : size - (aux::count_trailing_ones({&m_buf[1], num - 1}) + ext); + } struct const_iterator { diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp index f30afcd98..40aa3c529 100644 --- a/include/libtorrent/config.hpp +++ b/include/libtorrent/config.hpp @@ -540,6 +540,16 @@ POSSIBILITY OF SUCH DAMAGE. # define TORRENT_HAS_BUILTIN_CLZ 0 #endif // TORRENT_HAS_BUILTIN_CLZ +#if (TORRENT_HAS_SSE && __GNUC__) +# define TORRENT_HAS_BUILTIN_CTZ 1 +#elif (TORRENT_HAS_ARM && defined __GNUC__ && !defined __clang__) +# define TORRENT_HAS_BUILTIN_CTZ 1 +#elif (defined __clang__ && __has_builtin(__builtin_ctz)) +# define TORRENT_HAS_BUILTIN_CTZ 1 +#else +# define TORRENT_HAS_BUILTIN_CTZ 0 +#endif // TORRENT_HAS_BUILTIN_CTZ + #if TORRENT_HAS_ARM && defined __ARM_NEON # define TORRENT_HAS_ARM_NEON 1 #else diff --git a/include/libtorrent/sha1_hash.hpp b/include/libtorrent/sha1_hash.hpp index 0c43bf3aa..c186b61e2 100644 --- a/include/libtorrent/sha1_hash.hpp +++ b/include/libtorrent/sha1_hash.hpp @@ -150,7 +150,7 @@ namespace libtorrent int count_leading_zeroes() const { - return aux::clz({m_number, number_size}); + return aux::count_leading_zeros({m_number, number_size}); } // returns a bit-wise negated copy of the sha1-hash diff --git a/src/bitfield.cpp b/src/bitfield.cpp index 1b8703ea2..8ee350f5f 100644 --- a/src/bitfield.cpp +++ b/src/bitfield.cpp @@ -66,7 +66,7 @@ namespace libtorrent #if TORRENT_HAS_SSE if (aux::mmx_support) { - for (int i = 1; i < words+1; ++i) + for (int i = 1; i < words + 1; ++i) { #ifdef __GNUC__ std::uint32_t cnt = 0; @@ -174,10 +174,10 @@ namespace libtorrent if (b == nullptr) std::terminate(); #endif b[0] = bits; - if (m_buf) memcpy(&b[1], buf(), (std::min)(new_size_words, cur_size_words) * 4); + if (m_buf) std::memcpy(&b[1], buf(), (std::min)(new_size_words, cur_size_words) * 4); if (new_size_words > cur_size_words) { - memset(&b[1 + cur_size_words], 0 + std::memset(&b[1 + cur_size_words], 0 , (new_size_words - cur_size_words) * 4); } m_buf = std::move(b); diff --git a/src/ffs.cpp b/src/ffs.cpp index 724e8f21c..c6bf3f9af 100644 --- a/src/ffs.cpp +++ b/src/ffs.cpp @@ -44,11 +44,14 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { namespace aux { - int clz_sw(span buf) + int count_leading_zeros_sw(span buf) { - int num = int(buf.size()); + int const num = int(buf.size()); std::uint32_t const* ptr = buf.data(); + TORRENT_ASSERT(num >= 0); + TORRENT_ASSERT(ptr != nullptr); + for (int i = 0; i < num; i++) { if (ptr[i] == 0) continue; @@ -74,11 +77,14 @@ namespace libtorrent { namespace aux return num * 32; } - int clz_hw(span buf) + int count_leading_zeros_hw(span buf) { - int num = int(buf.size()); + int const num = int(buf.size()); std::uint32_t const* ptr = buf.data(); + TORRENT_ASSERT(num >= 0); + TORRENT_ASSERT(ptr != nullptr); + for (int i = 0; i < num; i++) { if (ptr[i] == 0) continue; @@ -91,6 +97,7 @@ namespace libtorrent { namespace aux _BitScanReverse(&pos, v); return i * 32 + 31 - pos; #else + TORRENT_ASSERT_FAIL(); return -1; #endif } @@ -98,13 +105,72 @@ namespace libtorrent { namespace aux return num * 32; } - int clz(span buf) + int count_leading_zeros(span buf) { #if TORRENT_HAS_BUILTIN_CLZ || defined _MSC_VER - return aux::clz_hw(buf); + return aux::count_leading_zeros_hw(buf); #else - return aux::clz_sw(buf); + return aux::count_leading_zeros_sw(buf); #endif } + int count_trailing_ones_sw(span buf) + { + int const num = int(buf.size()); + std::uint32_t const* ptr = buf.data(); + + TORRENT_ASSERT(num >= 0); + TORRENT_ASSERT(ptr != nullptr); + + for (int i = num - 1; i >= 0; i--) + { + if (ptr[i] == 0xffffffff) continue; + std::uint32_t v = ~aux::network_to_host(ptr[i]); + + for (int k = 0; k < 32; ++k, v >>= 1) + { + if ((v & 1) == 0) continue; + return (num - i - 1) * 32 + k; + } + } + + return num * 32; + } + + int count_trailing_ones_hw(span buf) + { + int const num = int(buf.size()); + std::uint32_t const* ptr = buf.data(); + + TORRENT_ASSERT(num >= 0); + TORRENT_ASSERT(ptr != nullptr); + + for (int i = num - 1; i >= 0; i--) + { + if (ptr[i] == 0xffffffff) continue; + std::uint32_t v = ~aux::network_to_host(ptr[i]); + +#if TORRENT_HAS_BUILTIN_CTZ + return (num - i - 1) * 32 + __builtin_ctz(v); +#elif defined _MSC_VER + DWORD pos; + _BitScanForward(&pos, v); + return (num - i - 1) * 32 + pos; +#else + TORRENT_ASSERT_FAIL(); + return -1; +#endif + } + + return num * 32; + } + + int count_trailing_ones(span buf) + { +#if TORRENT_HAS_BUILTIN_CTZ || defined _MSC_VER + return aux::count_trailing_ones_hw(buf); +#else + return aux::count_trailing_ones_sw(buf); +#endif + } }} diff --git a/test/test_bitfield.cpp b/test/test_bitfield.cpp index 28ef8c48e..fc8763a41 100644 --- a/test/test_bitfield.cpp +++ b/test/test_bitfield.cpp @@ -262,3 +262,96 @@ TORRENT_TEST(test_resize_down) TEST_EQUAL(test1.count(), i / 2); } } + +TORRENT_TEST(find_first_set_empty) +{ + bitfield test1(0); + TEST_EQUAL(test1.find_first_set(), -1); +} + +TORRENT_TEST(find_first_set_small) +{ + bitfield test1(10, false); + TEST_EQUAL(test1.find_first_set(), -1); +} + +TORRENT_TEST(find_first_set_large) +{ + bitfield test1(100, false); + TEST_EQUAL(test1.find_first_set(), -1); +} + +TORRENT_TEST(find_first_set_early) +{ + bitfield test1(100, false); + test1.set_bit(4); + TEST_EQUAL(test1.find_first_set(), 4); +} + +TORRENT_TEST(find_first_set_late) +{ + bitfield test1(100, false); + test1.set_bit(98); + TEST_EQUAL(test1.find_first_set(), 98); +} + +TORRENT_TEST(find_last_clear_empty) +{ + bitfield test1(0); + TEST_EQUAL(test1.find_last_clear(), -1); +} + +TORRENT_TEST(find_last_clear_small) +{ + bitfield test1(10, true); + TEST_EQUAL(test1.find_last_clear(), -1); +} + +TORRENT_TEST(find_last_clear_large) +{ + bitfield test1(100, true); + TEST_EQUAL(test1.find_last_clear(), -1); +} + +TORRENT_TEST(find_last_clear_early) +{ + bitfield test1(100, true); + test1.clear_bit(4); + TEST_EQUAL(test1.find_last_clear(), 4); +} + +TORRENT_TEST(find_last_clear_late) +{ + bitfield test1(100, true); + test1.clear_bit(98); + TEST_EQUAL(test1.find_last_clear(), 98); +} + +TORRENT_TEST(find_last_clear_misc) +{ + bitfield test1(100, true); + test1.clear_bit(11); + test1.clear_bit(91); + TEST_EQUAL(test1.find_last_clear(), 91); + + bitfield test2(78, true); + test2.clear_bit(12); + test2.clear_bit(43); + test2.clear_bit(34); + TEST_EQUAL(test2.find_last_clear(), 43); + + bitfield test3(123, true); + test3.clear_bit(49); + test3.clear_bit(33); + test3.clear_bit(32); + test3.clear_bit(50); + TEST_EQUAL(test3.find_last_clear(), 50); + + bitfield test4(1000, true); + test4.clear_bit(11); + test4.clear_bit(91); + test4.clear_bit(14); + test4.clear_bit(15); + test4.clear_bit(89); + TEST_EQUAL(test4.find_last_clear(), 91); +} diff --git a/test/test_ffs.cpp b/test/test_ffs.cpp index 9303c47b0..f1566d5c3 100644 --- a/test/test_ffs.cpp +++ b/test/test_ffs.cpp @@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/span.hpp" #include "libtorrent/hex.hpp" // from_hex #include "libtorrent/aux_/ffs.hpp" +#include "libtorrent/aux_/byteswap.hpp" using namespace libtorrent; @@ -42,7 +43,7 @@ static void to_binary(char const* s, std::uint32_t* buf) aux::from_hex({s, 40}, reinterpret_cast(&buf[0])); } -TORRENT_TEST(clz) +TORRENT_TEST(count_leading_zeros) { std::vector> const tests = { { "ffffffffffffffffffffffffffffffffffffffff", 0 }, @@ -74,8 +75,48 @@ TORRENT_TEST(clz) 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); + TEST_EQUAL(aux::count_leading_zeros_sw({buf, 5}), t.second); + TEST_EQUAL(aux::count_leading_zeros_hw({buf, 5}), t.second); + TEST_EQUAL(aux::count_leading_zeros({buf, 5}), t.second); } } + +TORRENT_TEST(count_trailing_ones_u32) +{ + std::uint32_t v = 0; + TEST_EQUAL(aux::count_trailing_ones_sw(v), 0); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 0); + TEST_EQUAL(aux::count_trailing_ones(v), 0); + + v = 0xffffffff; + TEST_EQUAL(aux::count_trailing_ones_sw(v), 32); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 32); + TEST_EQUAL(aux::count_trailing_ones(v), 32); + + v = aux::host_to_network(0xff00ff00); + TEST_EQUAL(aux::count_trailing_ones_sw(v), 0); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 0); + TEST_EQUAL(aux::count_trailing_ones(v), 0); + + v = aux::host_to_network(0xff0fff00); + TEST_EQUAL(aux::count_trailing_ones_sw(v), 0); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 0); + TEST_EQUAL(aux::count_trailing_ones(v), 0); + + v = aux::host_to_network(0xf0ff00ff); + TEST_EQUAL(aux::count_trailing_ones_sw(v), 8); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 8); + TEST_EQUAL(aux::count_trailing_ones(v), 8); + + v = aux::host_to_network(0xf0ff0fff); + TEST_EQUAL(aux::count_trailing_ones_sw(v), 12); + TEST_EQUAL(aux::count_trailing_ones_hw(v), 12); + TEST_EQUAL(aux::count_trailing_ones(v), 12); + + std::uint32_t const arr[2] = { + aux::host_to_network(0xf0ff0fff) + , 0xffffffff}; + TEST_EQUAL(aux::count_trailing_ones_sw(arr), 44); + TEST_EQUAL(aux::count_trailing_ones_hw(arr), 44); + TEST_EQUAL(aux::count_trailing_ones(arr), 44); +}