forked from premiere/premiere-libtorrent
clean up memory allocation in bitfield (#910)
clean up memory allocation in bitfield
This commit is contained in:
parent
e72f95e0d9
commit
31fbbab89c
|
@ -35,12 +35,12 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/assert.hpp"
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/buffer.hpp"
|
||||
#include "libtorrent/aux_/byteswap.hpp"
|
||||
|
||||
#include <cstring> // for memset and memcpy
|
||||
#include <cstdlib> // for malloc, free and realloc
|
||||
#include <cstdint> // uint32_t
|
||||
#include <algorithm> // for min()
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -56,22 +56,12 @@ namespace libtorrent
|
|||
// The constructor taking a pointer ``b`` and ``bits`` copies a bitfield
|
||||
// from the specified buffer, and ``bits`` number of bits (rounded up to
|
||||
// the nearest byte boundary).
|
||||
bitfield(): m_buf(nullptr) {}
|
||||
bitfield(int bits): m_buf(nullptr)
|
||||
{ resize(bits); }
|
||||
bitfield(int bits, bool val): m_buf(nullptr)
|
||||
{ resize(bits, val); }
|
||||
bitfield(char const* b, int bits): m_buf(nullptr)
|
||||
{ assign(b, bits); }
|
||||
bitfield(bitfield const& rhs): m_buf(nullptr)
|
||||
{ assign(rhs.data(), rhs.size()); }
|
||||
#if __cplusplus > 199711L
|
||||
bitfield(bitfield&& rhs): m_buf(rhs.m_buf)
|
||||
{ rhs.m_buf = nullptr; }
|
||||
#endif
|
||||
|
||||
// hidden
|
||||
~bitfield() { dealloc(); }
|
||||
bitfield() = default;
|
||||
bitfield(int bits) { resize(bits); }
|
||||
bitfield(int bits, bool val) { resize(bits, val); }
|
||||
bitfield(char const* b, int bits) { assign(b, bits); }
|
||||
bitfield(bitfield const& rhs) { assign(rhs.data(), rhs.size()); }
|
||||
bitfield(bitfield&& rhs) = default;
|
||||
|
||||
// copy bitfield from buffer ``b`` of ``bits`` number of bits, rounded up to
|
||||
// the nearest byte boundary.
|
||||
|
@ -80,7 +70,7 @@ namespace libtorrent
|
|||
resize(bits);
|
||||
if (bits > 0)
|
||||
{
|
||||
std::memcpy(m_buf, b, size_t((bits + 7) / 8));
|
||||
std::memcpy(buf(), b, size_t((bits + 7) / 8));
|
||||
clear_trailing_bits();
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +83,7 @@ namespace libtorrent
|
|||
{
|
||||
TORRENT_ASSERT(index >= 0);
|
||||
TORRENT_ASSERT(index < size());
|
||||
return (m_buf[index / 32] & aux::host_to_network((0x80000000 >> (index & 31)))) != 0;
|
||||
return (buf()[index / 32] & aux::host_to_network((0x80000000 >> (index & 31)))) != 0;
|
||||
}
|
||||
|
||||
// set bit at ``index`` to 0 (clear_bit) or 1 (set_bit).
|
||||
|
@ -101,13 +91,13 @@ namespace libtorrent
|
|||
{
|
||||
TORRENT_ASSERT(index >= 0);
|
||||
TORRENT_ASSERT(index < size());
|
||||
m_buf[index / 32] &= aux::host_to_network(~(0x80000000 >> (index & 31)));
|
||||
buf()[index / 32] &= aux::host_to_network(~(0x80000000 >> (index & 31)));
|
||||
}
|
||||
void set_bit(int index)
|
||||
{
|
||||
TORRENT_ASSERT(index >= 0);
|
||||
TORRENT_ASSERT(index < size());
|
||||
m_buf[index / 32] |= aux::host_to_network((0x80000000 >> (index & 31)));
|
||||
buf()[index / 32] |= aux::host_to_network((0x80000000 >> (index & 31)));
|
||||
}
|
||||
|
||||
// returns true if all bits in the bitfield are set
|
||||
|
@ -116,9 +106,10 @@ namespace libtorrent
|
|||
bool none_set() const
|
||||
{
|
||||
const int words = num_words();
|
||||
std::uint32_t const* b = buf();
|
||||
for (int i = 0; i < words; ++i)
|
||||
{
|
||||
if (m_buf[i] != 0) return false;
|
||||
if (b[i] != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -126,7 +117,7 @@ namespace libtorrent
|
|||
// returns the size of the bitfield in bits.
|
||||
int size() const
|
||||
{
|
||||
return m_buf == nullptr ? 0 : int(m_buf[-1]);
|
||||
return m_buf == nullptr ? 0 : int(m_buf[0]);
|
||||
}
|
||||
|
||||
int num_words() const
|
||||
|
@ -135,28 +126,29 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
// returns true if the bitfield has zero size.
|
||||
bool empty() const { return m_buf == nullptr ? true : m_buf[-1] == 0; }
|
||||
bool empty() const { return m_buf == nullptr ? true : m_buf[0] == 0; }
|
||||
|
||||
// returns a pointer to the internal buffer of the bitfield.
|
||||
char const* data() const { return reinterpret_cast<char const*>(m_buf); }
|
||||
char const* data() const { return m_buf ? reinterpret_cast<char const*>(&m_buf[1]) : nullptr; }
|
||||
char* data() { return m_buf ? reinterpret_cast<char*>(&m_buf[1]) : nullptr; }
|
||||
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
TORRENT_DEPRECATED
|
||||
char const* bytes() const { return data(); }
|
||||
#endif
|
||||
|
||||
// copy operator
|
||||
// assignment operator
|
||||
bitfield& operator=(bitfield const& rhs)
|
||||
{
|
||||
assign(rhs.data(), rhs.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bitfield& operator=(bitfield&& rhs) = default;
|
||||
|
||||
void swap(bitfield& rhs)
|
||||
{
|
||||
std::uint32_t* tmp = m_buf;
|
||||
m_buf = rhs.m_buf;
|
||||
rhs.m_buf = tmp;
|
||||
std::swap(m_buf, rhs.m_buf);
|
||||
}
|
||||
|
||||
// count the number of bits in the bitfield that are set to 1.
|
||||
|
@ -220,49 +212,44 @@ namespace libtorrent
|
|||
std::uint32_t bit;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(m_buf, 0); }
|
||||
const_iterator begin() const { return const_iterator(m_buf ? buf() : nullptr, 0); }
|
||||
const_iterator end() const { return const_iterator(
|
||||
m_buf + num_words() - (((size() & 31) == 0) ? 0 : 1), size() & 31); }
|
||||
m_buf ? buf() + num_words() - (((size() & 31) == 0) ? 0 : 1) : nullptr, size() & 31); }
|
||||
|
||||
// set the size of the bitfield to ``bits`` length. If the bitfield is extended,
|
||||
// the new bits are initialized to ``val``.
|
||||
void resize(int bits, bool val);
|
||||
|
||||
void resize(int bits);
|
||||
|
||||
// set all bits in the bitfield to 1 (set_all) or 0 (clear_all).
|
||||
void set_all()
|
||||
{
|
||||
std::memset(m_buf, 0xff, size_t(num_words() * 4));
|
||||
if (m_buf == nullptr) return;
|
||||
std::memset(buf(), 0xff, size_t(num_words() * 4));
|
||||
clear_trailing_bits();
|
||||
}
|
||||
void clear_all()
|
||||
{
|
||||
std::memset(m_buf, 0x00, size_t(num_words() * 4));
|
||||
if (m_buf == nullptr) return;
|
||||
std::memset(buf(), 0x00, size_t(num_words() * 4));
|
||||
}
|
||||
|
||||
// make the bitfield empty, of zero size.
|
||||
void clear() { dealloc(); }
|
||||
void clear() { m_buf.reset(); }
|
||||
|
||||
private:
|
||||
|
||||
std::uint32_t const* buf() const { TORRENT_ASSERT(m_buf); return &m_buf[1]; }
|
||||
std::uint32_t* buf() { TORRENT_ASSERT(m_buf); return &m_buf[1]; }
|
||||
void clear_trailing_bits()
|
||||
{
|
||||
// clear the tail bits in the last byte
|
||||
if (size() & 31) m_buf[num_words() - 1] &= aux::host_to_network(0xffffffff << (32 - (size() & 31)));
|
||||
}
|
||||
|
||||
void dealloc()
|
||||
{
|
||||
if (m_buf) std::free(m_buf-1);
|
||||
m_buf = nullptr;
|
||||
if (size() & 31) buf()[num_words() - 1] &= aux::host_to_network(0xffffffff << (32 - (size() & 31)));
|
||||
}
|
||||
|
||||
// the first element is not part of the bitfield, it's the
|
||||
// number of bits. For this purpose, the m_buf actually points
|
||||
// the element 1, not 0. To access the size (in bits), access
|
||||
// m_buf[-1]
|
||||
std::uint32_t* m_buf;
|
||||
// number of bits.
|
||||
std::unique_ptr<std::uint32_t[]> m_buf;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ public:
|
|||
// allocate an uninitialized buffer of the specified size
|
||||
buffer(std::size_t size = 0)
|
||||
{
|
||||
TORRENT_ASSERT(size < std::size_t(std::numeric_limits<std::int32_t>::max()));
|
||||
TORRENT_ASSERT(size < std::size_t((std::numeric_limits<std::int32_t>::max)()));
|
||||
|
||||
if (size == 0) return;
|
||||
|
||||
|
@ -172,7 +172,7 @@ public:
|
|||
TORRENT_ASSERT(initialize.size() <= size);
|
||||
if (initialize.size() > 0)
|
||||
{
|
||||
memcpy(m_begin, initialize.data(), std::min(initialize.size(), size));
|
||||
memcpy(m_begin, initialize.data(), (std::min)(initialize.size(), size));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,16 +45,16 @@ namespace libtorrent
|
|||
{
|
||||
bool bitfield::all_set() const
|
||||
{
|
||||
const int words = size() / 32;
|
||||
for (int i = 0; i < words; ++i)
|
||||
int const words = size() / 32;
|
||||
for (int i = 1; i < words + 1; ++i)
|
||||
{
|
||||
if (m_buf[i] != 0xffffffff) return false;
|
||||
}
|
||||
int rest = size() & 31;
|
||||
int const rest = size() & 31;
|
||||
if (rest > 0)
|
||||
{
|
||||
std::uint32_t mask = aux::host_to_network(0xffffffff << (32-rest));
|
||||
if ((m_buf[words] & mask) != mask) return false;
|
||||
std::uint32_t const mask = aux::host_to_network(0xffffffff << (32-rest));
|
||||
if ((m_buf[words+1] & mask) != mask) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -62,11 +62,11 @@ namespace libtorrent
|
|||
int bitfield::count() const
|
||||
{
|
||||
int ret = 0;
|
||||
const int words = num_words();
|
||||
int const words = num_words();
|
||||
#if TORRENT_HAS_SSE
|
||||
if (aux::mmx_support)
|
||||
{
|
||||
for (int i = 0; i < words; ++i)
|
||||
for (int i = 1; i < words+1; ++i)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
std::uint32_t cnt = 0;
|
||||
|
@ -79,6 +79,8 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(ret <= size());
|
||||
TORRENT_ASSERT(ret >= 0);
|
||||
return ret;
|
||||
}
|
||||
#endif // TORRENT_HAS_SSE
|
||||
|
@ -86,7 +88,7 @@ namespace libtorrent
|
|||
#if TORRENT_HAS_ARM_NEON
|
||||
if (aux::arm_neon_support)
|
||||
{
|
||||
for (int i = 0; i < words; ++i)
|
||||
for (int i = 1; i < words + 1; ++i)
|
||||
{
|
||||
uint8x8_t const in_val = vld1_u8((unsigned char *) &m_buf[i]);
|
||||
uint8x8_t const cnt8x8_val = vcnt_u8(in_val);
|
||||
|
@ -98,13 +100,15 @@ namespace libtorrent
|
|||
ret += cnt;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(ret <= size());
|
||||
TORRENT_ASSERT(ret >= 0);
|
||||
return ret;
|
||||
}
|
||||
#endif // TORRENT_HAS_ARM_NEON
|
||||
|
||||
for (int i = 0; i < words; ++i)
|
||||
for (int i = 1; i < words + 1; ++i)
|
||||
{
|
||||
std::uint32_t v = m_buf[i];
|
||||
std::uint32_t const v = m_buf[i];
|
||||
// from:
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
|
||||
static const int S[] = {1, 2, 4, 8, 16}; // Magic Binary Numbers
|
||||
|
@ -116,6 +120,7 @@ namespace libtorrent
|
|||
c = ((c >> S[3]) + c) & B[3];
|
||||
c = ((c >> S[4]) + c) & B[4];
|
||||
ret += c;
|
||||
TORRENT_ASSERT(ret <= size());
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(ret <= size());
|
||||
|
@ -127,24 +132,24 @@ namespace libtorrent
|
|||
{
|
||||
if (bits == size()) return;
|
||||
|
||||
int s = size();
|
||||
int b = size() & 31;
|
||||
int const s = size();
|
||||
int const b = size() & 31;
|
||||
resize(bits);
|
||||
if (s >= size()) return;
|
||||
int old_size_words = (s + 31) / 32;
|
||||
int new_size_words = num_words();
|
||||
int const old_size_words = (s + 31) / 32;
|
||||
int const new_size_words = num_words();
|
||||
if (val)
|
||||
{
|
||||
if (old_size_words && b) m_buf[old_size_words - 1] |= aux::host_to_network((0xffffffff >> b));
|
||||
if (old_size_words && b) buf()[old_size_words - 1] |= aux::host_to_network((0xffffffff >> b));
|
||||
if (old_size_words < new_size_words)
|
||||
std::memset(m_buf + old_size_words, 0xff
|
||||
std::memset(buf() + old_size_words, 0xff
|
||||
, size_t((new_size_words - old_size_words) * 4));
|
||||
clear_trailing_bits();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (old_size_words < new_size_words)
|
||||
std::memset(m_buf + old_size_words, 0x00
|
||||
std::memset(buf() + old_size_words, 0x00
|
||||
, size_t((new_size_words - old_size_words) * 4));
|
||||
}
|
||||
TORRENT_ASSERT(size() == bits);
|
||||
|
@ -155,37 +160,35 @@ namespace libtorrent
|
|||
if (bits == size()) return;
|
||||
|
||||
TORRENT_ASSERT(bits >= 0);
|
||||
const int b = (bits + 31) / 32;
|
||||
if (bits == 0)
|
||||
{
|
||||
if (m_buf != nullptr)
|
||||
{
|
||||
std::free(m_buf-1);
|
||||
m_buf = nullptr;
|
||||
}
|
||||
m_buf.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_buf)
|
||||
int const new_size_words = (bits + 31) / 32;
|
||||
int const cur_size_words = num_words();
|
||||
if (cur_size_words != new_size_words)
|
||||
{
|
||||
std::uint32_t* tmp = static_cast<std::uint32_t*>(std::realloc(m_buf-1, (b+1) * 4));
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (tmp == nullptr) throw std::bad_alloc();
|
||||
std::unique_ptr<std::uint32_t[]> b(new std::uint32_t[new_size_words + 1]);
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
if (b == nullptr) std::terminate();
|
||||
#endif
|
||||
m_buf = tmp + 1;
|
||||
m_buf[-1] = bits;
|
||||
b[0] = bits;
|
||||
if (m_buf) 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
|
||||
, (new_size_words - cur_size_words) * 4);
|
||||
}
|
||||
m_buf = std::move(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
// +1 because the first word is the size (in bits)
|
||||
std::uint32_t* tmp = static_cast<std::uint32_t*>(std::malloc((b+1) * 4));
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (tmp == nullptr) throw std::bad_alloc();
|
||||
#endif
|
||||
m_buf = tmp + 1;
|
||||
m_buf[-1] = bits;
|
||||
m_buf[0] = bits;
|
||||
}
|
||||
|
||||
clear_trailing_bits();
|
||||
TORRENT_ASSERT(size() == bits);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,10 +40,9 @@ using namespace libtorrent;
|
|||
void print_bitfield(bitfield const& b)
|
||||
{
|
||||
std::string out;
|
||||
for (int i = 0; i < b.size(); ++i)
|
||||
{
|
||||
out += b.get_bit(i) ? "1" : "0";
|
||||
}
|
||||
out.reserve(b.size());
|
||||
for (bool bit : b)
|
||||
out += bit ? '1' : '0';
|
||||
std::printf("%s\n", out.c_str());
|
||||
}
|
||||
|
||||
|
@ -116,16 +115,16 @@ TORRENT_TEST(bitfield)
|
|||
|
||||
test1.resize(100, true);
|
||||
TEST_CHECK(test1.all_set() == true);
|
||||
TEST_CHECK(test1.count() == 100);
|
||||
TEST_EQUAL(test1.count(), 100);
|
||||
test1.resize(200, false);
|
||||
TEST_CHECK(test1.all_set() == false);
|
||||
TEST_CHECK(test1.count() == 100);
|
||||
TEST_EQUAL(test1.count(), 100);
|
||||
test1.resize(50, false);
|
||||
TEST_CHECK(test1.all_set() == true);
|
||||
TEST_CHECK(test1.count() == 50);
|
||||
TEST_EQUAL(test1.count(), 50);
|
||||
test1.resize(101, true);
|
||||
TEST_CHECK(test1.all_set() == true);
|
||||
TEST_CHECK(test1.count() == 101);
|
||||
TEST_EQUAL(test1.count(), 101);
|
||||
|
||||
std::uint8_t b1[] = { 0x08, 0x10 };
|
||||
test1.assign((char*)b1, 14);
|
||||
|
@ -149,7 +148,11 @@ TORRENT_TEST(bitfield)
|
|||
TEST_EQUAL(test1.get_bit(0), true);
|
||||
TEST_EQUAL(test1.get_bit(1), false);
|
||||
TEST_EQUAL(test1.get_bit(2), true);
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_assign3)
|
||||
{
|
||||
bitfield test1;
|
||||
std::uint8_t b2[] = { 0x08, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf, 0xc, 0x7f };
|
||||
test1.assign((char*)b2, 72);
|
||||
print_bitfield(test1);
|
||||
|
@ -159,31 +162,42 @@ TORRENT_TEST(bitfield)
|
|||
test1.assign((char*)b3, 64);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.count(), 40);
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_iterators)
|
||||
{
|
||||
bitfield test1;
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
test1.resize(i, false);
|
||||
test_iterators(test1);
|
||||
}
|
||||
}
|
||||
|
||||
// test alignment
|
||||
std::uint32_t buffer[4];
|
||||
char* b = (char*)buffer;
|
||||
TORRENT_TEST(test_assign)
|
||||
{
|
||||
std::array<char, 16> b;
|
||||
bitfield test1;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
b[i] = char(0xc0);
|
||||
test1.assign(b + i, 2);
|
||||
test1.assign(&b[i], 2);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.count(), 2);
|
||||
TEST_EQUAL(test1.all_set(), true);
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_assign2)
|
||||
{
|
||||
std::array<char, 16> b;
|
||||
bitfield test1;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
memset(b + i, 0xff, 5);
|
||||
memset(&b[i], 0xff, 5);
|
||||
b[i + 5] = char(0xc0);
|
||||
test1.assign(b + i, 32 + 8 + 2);
|
||||
test1.assign(&b[i], 32 + 8 + 2);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.count(), 32 + 8 + 2);
|
||||
TEST_EQUAL(test1.all_set(), true);
|
||||
|
@ -195,3 +209,56 @@ TORRENT_TEST(bitfield)
|
|||
TORRENT_ASSERT(!aux::arm_neon_support);
|
||||
#endif
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_resize_val)
|
||||
{
|
||||
std::array<char, 8> b;
|
||||
b.fill(0xcc);
|
||||
|
||||
bitfield test1(b.data(), 8 * 8);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.size(), 8 * 8);
|
||||
TEST_EQUAL(test1.count(), 4 * 8);
|
||||
|
||||
for (int i = 1; i < 4 * 8; ++i)
|
||||
{
|
||||
test1.resize(8 * 8 + i, true);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.count(), 4 * 8 + i);
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_resize_up)
|
||||
{
|
||||
std::array<char, 8> b;
|
||||
b.fill(0xcc);
|
||||
|
||||
bitfield test1(b.data(), 8 * 8);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.size(), 8 * 8);
|
||||
TEST_EQUAL(test1.count(), 4 * 8);
|
||||
|
||||
for (int i = 1; i < 5 * 8; ++i)
|
||||
{
|
||||
test1.resize(8 * 8 + i);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.size(), 8 * 8 + i);
|
||||
TEST_EQUAL(test1.count(), 4 * 8);
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(test_resize_down)
|
||||
{
|
||||
std::array<char, 8> b;
|
||||
b.fill(0x55);
|
||||
|
||||
bitfield test1(b.data(), 8 * 8);
|
||||
|
||||
for (int i = 8 * 8; i > -1; --i)
|
||||
{
|
||||
test1.resize(i);
|
||||
print_bitfield(test1);
|
||||
TEST_EQUAL(test1.size(), i);
|
||||
TEST_EQUAL(test1.count(), i / 2);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue