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
This commit is contained in:
Alden Torres 2016-08-07 16:21:08 -04:00 committed by Arvid Norberg
parent e9a7271aca
commit af565e2b86
8 changed files with 262 additions and 19 deletions

View File

@ -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<std::uint32_t const> buf);
TORRENT_EXTRA_EXPORT int count_leading_zeros_sw(span<std::uint32_t const> buf);
// if this function is called in an unsupported platform, returns -1
// consider call always clz(buf)
TORRENT_EXTRA_EXPORT int clz_hw(span<std::uint32_t const> buf);
// consider call always count_leading_zeros(buf)
TORRENT_EXTRA_EXPORT int count_leading_zeros_hw(span<std::uint32_t const> 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<std::uint32_t const> buf);
TORRENT_EXTRA_EXPORT int count_leading_zeros(span<std::uint32_t const> buf);
// these functions expect the range to be in big-endian byte order
TORRENT_EXTRA_EXPORT int count_trailing_ones_sw(span<std::uint32_t const> 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<std::uint32_t const> 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<std::uint32_t const> buf);
}}
#endif // TORRENT_FFS_HPP_INCLUDE

View File

@ -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 <cstring> // for memset and memcpy
#include <cstdint> // 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
{

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -44,11 +44,14 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent { namespace aux
{
int clz_sw(span<std::uint32_t const> buf)
int count_leading_zeros_sw(span<std::uint32_t const> 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<std::uint32_t const> buf)
int count_leading_zeros_hw(span<std::uint32_t const> 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<std::uint32_t const> buf)
int count_leading_zeros(span<std::uint32_t const> 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<std::uint32_t const> 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<std::uint32_t const> 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<std::uint32_t const> 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
}
}}

View File

@ -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);
}

View File

@ -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<char*>(&buf[0]));
}
TORRENT_TEST(clz)
TORRENT_TEST(count_leading_zeros)
{
std::vector<std::pair<char const*, int>> 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);
}