added support for flz (find last zero) and copied bitfield functions from PR #565
This commit is contained in:
parent
e9a7271aca
commit
af565e2b86
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
80
src/ffs.cpp
80
src/ffs.cpp
|
@ -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
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue