Modernize bitfield.cc: Storage changes and refactor (#1927)
* Modernize bitfield.cc: Storage changed to vector of bytes, return vector from getRaw, new Span readonly memory view Modernize bitfield.cc: Code style/review notes Modernize bitfield.cc: Code format * Modernize bitfield.cc: Swap end and begin in bit counting code assertion * Modernize bitfield.cc: Rewrite states and simplify code * Modernize bitfield.cc: Fixing the code and tests * Modernize bitfield.cc: Fixing tests * Modernize bitfield.cc: Formatting; +std::size, +const
This commit is contained in:
parent
43ad1346eb
commit
953f07375a
|
@ -7,18 +7,19 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring> /* memset */
|
||||
#include <iterator> // std::back_inserter
|
||||
#include <numeric> // std::accumulate
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bitfield.h"
|
||||
#include "tr-assert.h"
|
||||
#include "utils.h" /* tr_new0() */
|
||||
#include "span.h"
|
||||
|
||||
/****
|
||||
*****
|
||||
****/
|
||||
|
||||
static constexpr int8_t const trueBitCount[256] = {
|
||||
std::array<int8_t const, 256> Bitfield::true_bits_lookup_ = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, //
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //
|
||||
|
@ -37,17 +38,13 @@ static constexpr int8_t const trueBitCount[256] = {
|
|||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, //
|
||||
};
|
||||
|
||||
constexpr size_t Bitfield::countArray() const
|
||||
size_t Bitfield::countArray() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
size_t i = this->alloc_count_;
|
||||
|
||||
while (i > 0)
|
||||
{
|
||||
ret += trueBitCount[this->bits_[--i]];
|
||||
}
|
||||
|
||||
return ret;
|
||||
return std::accumulate(
|
||||
std::begin(bits_),
|
||||
std::end(bits_),
|
||||
0,
|
||||
[](auto acc, auto item) { return acc + true_bits_lookup_[item]; });
|
||||
}
|
||||
|
||||
size_t Bitfield::countRangeImpl(size_t begin, size_t end) const
|
||||
|
@ -56,22 +53,21 @@ size_t Bitfield::countRangeImpl(size_t begin, size_t end) const
|
|||
size_t const first_byte = begin >> 3U;
|
||||
size_t const last_byte = (end - 1) >> 3U;
|
||||
|
||||
if (this->bit_count_ == 0)
|
||||
if (bit_count_ == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (first_byte >= this->alloc_count_)
|
||||
if (first_byte >= std::size(bits_))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TR_ASSERT(begin < end);
|
||||
TR_ASSERT(this->bits_ != nullptr);
|
||||
|
||||
if (first_byte == last_byte)
|
||||
{
|
||||
uint8_t val = this->bits_[first_byte];
|
||||
uint8_t val = bits_[first_byte];
|
||||
|
||||
int i = begin - (first_byte * 8);
|
||||
val <<= i;
|
||||
|
@ -80,58 +76,63 @@ size_t Bitfield::countRangeImpl(size_t begin, size_t end) const
|
|||
val >>= i;
|
||||
val <<= i;
|
||||
|
||||
ret += trueBitCount[val];
|
||||
ret += true_bits_lookup_[val];
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t const walk_end = std::min(this->alloc_count_, last_byte);
|
||||
size_t const walk_end = std::min(std::size(bits_), last_byte);
|
||||
|
||||
/* first byte */
|
||||
size_t const first_shift = begin - (first_byte * 8);
|
||||
uint8_t val = this->bits_[first_byte];
|
||||
uint8_t val = bits_[first_byte];
|
||||
val <<= first_shift;
|
||||
val >>= first_shift;
|
||||
ret += trueBitCount[val];
|
||||
ret += true_bits_lookup_[val];
|
||||
|
||||
/* middle bytes */
|
||||
for (size_t i = first_byte + 1; i < walk_end; ++i)
|
||||
{
|
||||
ret += trueBitCount[this->bits_[i]];
|
||||
ret += true_bits_lookup_[bits_[i]];
|
||||
}
|
||||
|
||||
/* last byte */
|
||||
if (last_byte < this->alloc_count_)
|
||||
if (last_byte < std::size(bits_))
|
||||
{
|
||||
size_t const last_shift = (last_byte + 1) * 8 - end;
|
||||
val = this->bits_[last_byte];
|
||||
val = bits_[last_byte];
|
||||
val >>= last_shift;
|
||||
val <<= last_shift;
|
||||
ret += trueBitCount[val];
|
||||
ret += true_bits_lookup_[val];
|
||||
}
|
||||
}
|
||||
|
||||
TR_ASSERT(ret <= (begin - end));
|
||||
TR_ASSERT(ret <= (end - begin));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Bitfield::readBit(size_t n) const
|
||||
{
|
||||
if (this->hasAll())
|
||||
switch (mode_)
|
||||
{
|
||||
case OperationMode::Normal:
|
||||
{
|
||||
auto const byte_offset = n >> 3;
|
||||
|
||||
if (byte_offset >= std::size(bits_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((bits_[byte_offset] << (n & 7)) & 0x80) != 0;
|
||||
}
|
||||
case OperationMode::All:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this->hasNone())
|
||||
{
|
||||
case OperationMode::None:
|
||||
return false;
|
||||
case OperationMode::Start:
|
||||
TR_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (n >> 3U >= this->alloc_count_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this->bits_[n >> 3U] << (n & 7U) & 0x80) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -142,8 +143,13 @@ bool Bitfield::readBit(size_t n) const
|
|||
|
||||
bool Bitfield::isValid() const
|
||||
{
|
||||
TR_ASSERT((this->alloc_count_ == 0) == (this->bits_ == nullptr));
|
||||
TR_ASSERT(this->bits_ == nullptr || this->true_count_ == this->countArray());
|
||||
TR_ASSERT_MSG(
|
||||
std::size(bits_) == 0 || true_count_ == countArray(),
|
||||
"Invalid Bitfield state: bits.size()=%zu bit_count=%zu true_count=%zu countArray()=%zu",
|
||||
std::size(bits_),
|
||||
bit_count_,
|
||||
true_count_,
|
||||
countArray());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -152,124 +158,54 @@ bool Bitfield::isValid() const
|
|||
|
||||
size_t Bitfield::countBits() const
|
||||
{
|
||||
TR_ASSERT(this->isValid());
|
||||
TR_ASSERT(isValid());
|
||||
|
||||
return this->true_count_;
|
||||
return true_count_;
|
||||
}
|
||||
|
||||
void Bitfield::setBitsInArray(uint8_t* array, size_t bit_count)
|
||||
void Bitfield::setBitsInArray(std::vector<uint8_t>& array, size_t bit_count)
|
||||
{
|
||||
uint8_t const val = 0xFF;
|
||||
size_t const n = getStorageSize(bit_count);
|
||||
TR_ASSERT(getStorageSize(bit_count) == std::size(array));
|
||||
|
||||
if (n > 0)
|
||||
if (!std::empty(array) && bit_count > 0)
|
||||
{
|
||||
memset(array, val, n - 1);
|
||||
auto const last_byte_index = getStorageSize(bit_count) - 1;
|
||||
|
||||
array[n - 1] = val << (n * 8 - bit_count);
|
||||
std::fill_n(std::begin(array), last_byte_index, 0xFF);
|
||||
array[last_byte_index] = 0xFF << (last_byte_index * 8 - bit_count);
|
||||
}
|
||||
}
|
||||
|
||||
void* Bitfield::getRaw(size_t* byte_count) const
|
||||
std::vector<uint8_t> Bitfield::getRaw() const
|
||||
{
|
||||
TR_ASSERT(this->bit_count_ > 0);
|
||||
TR_ASSERT(bit_count_ > 0);
|
||||
|
||||
size_t const n = getStorageSize(this->bit_count_);
|
||||
uint8_t* newBits = tr_new0(uint8_t, n);
|
||||
size_t const n = getStorageSize(bit_count_);
|
||||
auto new_bits = std::vector<uint8_t>(n);
|
||||
|
||||
if (this->alloc_count_ != 0)
|
||||
if (!std::empty(bits_))
|
||||
{
|
||||
TR_ASSERT(this->alloc_count_ <= n);
|
||||
std::memcpy(newBits, this->bits_, this->alloc_count_);
|
||||
TR_ASSERT(std::size(bits_) <= n);
|
||||
std::copy(std::cbegin(bits_), std::cend(bits_), std::back_inserter(new_bits));
|
||||
}
|
||||
else if (this->hasAll())
|
||||
else if (hasAll())
|
||||
{
|
||||
setBitsInArray(newBits, this->bit_count_);
|
||||
setBitsInArray(new_bits, bit_count_);
|
||||
}
|
||||
|
||||
*byte_count = n;
|
||||
return newBits;
|
||||
return new_bits;
|
||||
}
|
||||
|
||||
void Bitfield::ensureBitsAlloced(size_t n)
|
||||
void Bitfield::ensureNthBitFits(size_t n)
|
||||
{
|
||||
size_t bytes_needed;
|
||||
bool const has_all = this->hasAll();
|
||||
TR_ASSERT_MSG(mode_ == OperationMode::Normal, "Can only reallocate storage in Normal mode");
|
||||
|
||||
if (has_all)
|
||||
size_t bytes_needed = getStorageSize(std::max(n, true_count_));
|
||||
|
||||
if (std::size(bits_) < bytes_needed)
|
||||
{
|
||||
bytes_needed = getStorageSize(std::max(n, this->true_count_));
|
||||
bits_.resize(bytes_needed);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes_needed = getStorageSize(n);
|
||||
}
|
||||
|
||||
if (this->alloc_count_ < bytes_needed)
|
||||
{
|
||||
this->bits_ = tr_renew(uint8_t, this->bits_, bytes_needed);
|
||||
std::memset(this->bits_ + this->alloc_count_, 0, bytes_needed - this->alloc_count_);
|
||||
this->alloc_count_ = bytes_needed;
|
||||
|
||||
if (has_all)
|
||||
{
|
||||
setBitsInArray(this->bits_, this->true_count_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Bitfield::ensureNthBitAlloced(size_t nth)
|
||||
{
|
||||
// count is zero-based, so we need to allocate nth+1 bits before setting the nth
|
||||
if (nth == SIZE_MAX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->ensureBitsAlloced(nth + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bitfield::freeArray()
|
||||
{
|
||||
tr_free(this->bits_);
|
||||
this->bits_ = nullptr;
|
||||
this->alloc_count_ = 0;
|
||||
}
|
||||
|
||||
void Bitfield::setTrueCount(size_t n)
|
||||
{
|
||||
TR_ASSERT(this->bit_count_ == 0 || n <= this->bit_count_);
|
||||
|
||||
this->true_count_ = n;
|
||||
|
||||
if (this->hasAll() || this->hasNone())
|
||||
{
|
||||
this->freeArray();
|
||||
}
|
||||
|
||||
TR_ASSERT(this->isValid());
|
||||
}
|
||||
|
||||
void Bitfield::rebuildTrueCount()
|
||||
{
|
||||
this->setTrueCount(this->countArray());
|
||||
}
|
||||
|
||||
void Bitfield::incTrueCount(size_t i)
|
||||
{
|
||||
TR_ASSERT(this->bit_count_ == 0 || i <= this->bit_count_);
|
||||
TR_ASSERT(this->bit_count_ == 0 || this->true_count_ <= this->bit_count_ - i);
|
||||
|
||||
this->setTrueCount(this->true_count_ + i);
|
||||
}
|
||||
|
||||
void Bitfield::decTrueCount(size_t i)
|
||||
{
|
||||
TR_ASSERT(this->bit_count_ == 0 || i <= this->bit_count_);
|
||||
TR_ASSERT(this->bit_count_ == 0 || this->true_count_ >= i);
|
||||
|
||||
this->setTrueCount(this->true_count_ - i);
|
||||
}
|
||||
|
||||
/****
|
||||
|
@ -278,215 +214,212 @@ void Bitfield::decTrueCount(size_t i)
|
|||
|
||||
Bitfield::Bitfield(size_t bit_count)
|
||||
{
|
||||
this->bit_count_ = bit_count;
|
||||
this->true_count_ = 0;
|
||||
this->bits_ = nullptr;
|
||||
this->alloc_count_ = 0;
|
||||
this->hint_ = NORMAL;
|
||||
bit_count_ = bit_count;
|
||||
true_count_ = 0;
|
||||
setMode(OperationMode::Normal);
|
||||
|
||||
TR_ASSERT(this->isValid());
|
||||
TR_ASSERT(isValid());
|
||||
}
|
||||
|
||||
void Bitfield::setHasNone()
|
||||
void Bitfield::setMode(Bitfield::OperationMode new_mode)
|
||||
{
|
||||
this->freeArray();
|
||||
this->true_count_ = 0;
|
||||
this->hint_ = HAS_NONE;
|
||||
|
||||
TR_ASSERT(this->isValid());
|
||||
}
|
||||
|
||||
void Bitfield::setHasAll()
|
||||
{
|
||||
this->freeArray();
|
||||
this->true_count_ = this->bit_count_;
|
||||
this->hint_ = HAS_ALL;
|
||||
|
||||
TR_ASSERT(this->isValid());
|
||||
}
|
||||
|
||||
void Bitfield::setFromBitfield(Bitfield const& src)
|
||||
{
|
||||
if (src.hasAll())
|
||||
switch (new_mode)
|
||||
{
|
||||
this->setHasAll();
|
||||
}
|
||||
else if (src.hasNone())
|
||||
{
|
||||
this->setHasNone();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setRaw(src.bits_, src.alloc_count_, true);
|
||||
case OperationMode::Normal:
|
||||
switch (mode_)
|
||||
{
|
||||
case OperationMode::All:
|
||||
{
|
||||
// Switching from ALL mode to NORMAL, should set the bits
|
||||
mode_ = OperationMode::Normal;
|
||||
ensureNthBitFits(bit_count_);
|
||||
setBitRangeImpl(0, bit_count_ - 1);
|
||||
true_count_ = bit_count_; // switching from mode ALL, all bits are set
|
||||
break;
|
||||
}
|
||||
case OperationMode::None:
|
||||
{
|
||||
// Switching from ALL mode to NORMAL, should set the bits
|
||||
mode_ = OperationMode::Normal;
|
||||
ensureNthBitFits(bit_count_);
|
||||
clearBitRangeImpl(0, bit_count_ - 1);
|
||||
true_count_ = 0; // switching from mode NONE, all bits are not set
|
||||
break;
|
||||
}
|
||||
case OperationMode::Start:
|
||||
mode_ = OperationMode::Normal;
|
||||
// fall through
|
||||
case OperationMode::Normal:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case OperationMode::All:
|
||||
clearStorage();
|
||||
true_count_ = bit_count_;
|
||||
mode_ = OperationMode::All;
|
||||
break;
|
||||
case OperationMode::None:
|
||||
clearStorage();
|
||||
true_count_ = 0;
|
||||
mode_ = OperationMode::None;
|
||||
break;
|
||||
case OperationMode::Start:
|
||||
TR_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
TR_ASSERT(isValid());
|
||||
}
|
||||
|
||||
void Bitfield::setRaw(void const* newBits, size_t byte_count, bool bounded)
|
||||
Bitfield::Bitfield(Span<uint8_t> new_bits, size_t bit_count, bool bounded)
|
||||
: bit_count_(bit_count)
|
||||
{
|
||||
this->freeArray();
|
||||
this->true_count_ = 0;
|
||||
true_count_ = 0;
|
||||
setMode(OperationMode::Normal);
|
||||
|
||||
// Having bounded=true, limits the amount of moved data to available storage size
|
||||
size_t byte_count = bounded ? std::min(std::size(new_bits), getStorageSize(bit_count_)) : std::size(new_bits);
|
||||
|
||||
bits_.resize(byte_count);
|
||||
std::copy_n(std::begin(new_bits), byte_count, std::begin(bits_));
|
||||
|
||||
if (bounded)
|
||||
{
|
||||
byte_count = std::min(byte_count, getStorageSize(this->bit_count_));
|
||||
}
|
||||
|
||||
this->bits_ = static_cast<uint8_t*>(tr_memdup(newBits, byte_count));
|
||||
this->alloc_count_ = byte_count;
|
||||
|
||||
if (bounded)
|
||||
{
|
||||
/* ensure the excess newBits are set to '0' */
|
||||
int const excess_bit_count = byte_count * 8 - this->bit_count_;
|
||||
|
||||
TR_ASSERT(excess_bit_count >= 0);
|
||||
TR_ASSERT(excess_bit_count <= 7);
|
||||
/* ensure the excess new_bits are set to '0' */
|
||||
int const excess_bit_count = bit_count_ & 7;
|
||||
|
||||
if (excess_bit_count != 0)
|
||||
{
|
||||
this->bits_[this->alloc_count_ - 1] &= 0xff << excess_bit_count;
|
||||
bits_[byte_count - 1] &= 0xFF << excess_bit_count;
|
||||
}
|
||||
}
|
||||
|
||||
this->rebuildTrueCount();
|
||||
setTrueCount(countArray());
|
||||
}
|
||||
|
||||
void Bitfield::setFromFlags(bool const* flags, size_t n)
|
||||
Bitfield::Bitfield(bool const* flags, size_t n)
|
||||
: mode_(OperationMode::Normal)
|
||||
{
|
||||
size_t trueCount = 0;
|
||||
|
||||
this->freeArray();
|
||||
this->ensureBitsAlloced(n);
|
||||
clearStorage();
|
||||
ensureNthBitFits(n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
TR_ASSERT(std::size(bits_) >= getStorageSize(n));
|
||||
|
||||
for (size_t index = 0; index < n; ++index)
|
||||
{
|
||||
if (flags[i] && this->bits_ != nullptr)
|
||||
if (flags[index])
|
||||
{
|
||||
++trueCount;
|
||||
this->bits_[i >> 3U] |= (0x80 >> (i & 7U));
|
||||
bits_[index >> 3] |= (0x80 >> (index & 7));
|
||||
}
|
||||
}
|
||||
|
||||
this->setTrueCount(trueCount);
|
||||
setTrueCount(trueCount);
|
||||
}
|
||||
|
||||
void Bitfield::setBit(size_t bit)
|
||||
void Bitfield::setBit(size_t bit_index)
|
||||
{
|
||||
if (!this->readBit(bit) && this->ensureNthBitAlloced(bit))
|
||||
switch (mode_)
|
||||
{
|
||||
size_t const offset = bit >> 3U;
|
||||
|
||||
if ((this->bits_ != nullptr) && (offset < this->alloc_count_))
|
||||
case OperationMode::Normal:
|
||||
{
|
||||
this->bits_[offset] |= 0x80 >> (bit & 7U);
|
||||
this->incTrueCount(1);
|
||||
setBitImpl(bit_index);
|
||||
break;
|
||||
}
|
||||
case OperationMode::All:
|
||||
TR_ASSERT(bit_index <= bit_count_);
|
||||
break;
|
||||
case OperationMode::None:
|
||||
setMode(OperationMode::Normal);
|
||||
setBitImpl(bit_index);
|
||||
break;
|
||||
case OperationMode::Start:
|
||||
TR_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitfield::setBitRange(size_t begin, size_t end)
|
||||
{
|
||||
size_t sb;
|
||||
size_t eb;
|
||||
unsigned char sm;
|
||||
unsigned char em;
|
||||
size_t const diff = (end - begin) - this->countRange(begin, end);
|
||||
if (mode_ == OperationMode::All)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (mode_ == OperationMode::None)
|
||||
{
|
||||
setMode(OperationMode::Normal);
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
size_t const true_bits_difference = (end - begin) - countRange(begin, end);
|
||||
|
||||
if (true_bits_difference == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
end--;
|
||||
|
||||
if (end >= this->bit_count_ || begin > end)
|
||||
if (end >= bit_count_ || begin > end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sb = begin >> 3;
|
||||
sm = ~(0xff << (8 - (begin & 7)));
|
||||
eb = end >> 3;
|
||||
em = 0xff << (7 - (end & 7));
|
||||
setBitRangeImpl(begin, end);
|
||||
|
||||
if (!this->ensureNthBitAlloced(end))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sb == eb)
|
||||
{
|
||||
this->bits_[sb] |= sm & em;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bits_[sb] |= sm;
|
||||
this->bits_[eb] |= em;
|
||||
|
||||
if (++sb < eb)
|
||||
{
|
||||
std::memset(this->bits_ + sb, 0xff, eb - sb);
|
||||
}
|
||||
}
|
||||
|
||||
this->incTrueCount(diff);
|
||||
TR_ASSERT(true_count_ + true_bits_difference <= bit_count_);
|
||||
setTrueCount(true_count_ + true_bits_difference);
|
||||
}
|
||||
|
||||
void Bitfield::clearBit(size_t bit)
|
||||
{
|
||||
TR_ASSERT(this->isValid());
|
||||
TR_ASSERT(isValid());
|
||||
|
||||
if (this->readBit(bit) && this->ensureNthBitAlloced(bit))
|
||||
switch (mode_)
|
||||
{
|
||||
this->bits_[bit >> 3U] &= 0xff7f >> (bit & 7U);
|
||||
this->decTrueCount(1);
|
||||
case OperationMode::Normal:
|
||||
clearBitImpl(bit);
|
||||
break;
|
||||
case OperationMode::All:
|
||||
setMode(OperationMode::Normal);
|
||||
clearBitImpl(bit);
|
||||
break;
|
||||
case OperationMode::None:
|
||||
break;
|
||||
case OperationMode::Start:
|
||||
TR_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitfield::clearBitRange(size_t begin, size_t end)
|
||||
{
|
||||
size_t sb;
|
||||
size_t eb;
|
||||
unsigned char sm;
|
||||
unsigned char em;
|
||||
size_t const diff = this->countRange(begin, end);
|
||||
if (mode_ == OperationMode::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (mode_ == OperationMode::All)
|
||||
{
|
||||
setMode(OperationMode::Normal);
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
size_t const true_bits_difference = countRange(begin, end); // all true bits in range will be gone
|
||||
|
||||
if (true_bits_difference == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
end--;
|
||||
|
||||
if (end >= this->bit_count_ || begin > end)
|
||||
if (end >= bit_count_ || begin > end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sb = begin >> 3;
|
||||
sm = 0xff << (8 - (begin & 7));
|
||||
eb = end >> 3;
|
||||
em = ~(0xff << (7 - (end & 7)));
|
||||
clearBitRangeImpl(begin, end);
|
||||
|
||||
if (!this->ensureNthBitAlloced(end))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sb == eb)
|
||||
{
|
||||
this->bits_[sb] &= sm | em;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bits_[sb] &= sm;
|
||||
this->bits_[eb] &= em;
|
||||
|
||||
if (++sb < eb)
|
||||
{
|
||||
std::memset(this->bits_ + sb, 0, eb - sb);
|
||||
}
|
||||
}
|
||||
|
||||
this->decTrueCount(diff);
|
||||
TR_ASSERT(true_count_ >= true_bits_difference);
|
||||
setTrueCount(true_count_ - true_bits_difference);
|
||||
}
|
||||
|
|
|
@ -12,14 +12,33 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "tr-macros.h"
|
||||
#include "tr-assert.h"
|
||||
#include "span.h"
|
||||
|
||||
/// @brief Implementation of the BitTorrent spec's Bitfield array of bits
|
||||
struct Bitfield
|
||||
{
|
||||
public:
|
||||
/// @brief State diagram for modes of operation: None -> Normal <==> All
|
||||
/// ALL and NONE: Special cases for when full or empty but we don't know the bitCount.
|
||||
/// This occurs when a magnet link's peers send have all / have none
|
||||
enum struct OperationMode
|
||||
{
|
||||
/// @brief State at the creation
|
||||
Start,
|
||||
/// @brief Normal operation: storage of bytes contains bits to set or clear
|
||||
Normal,
|
||||
/// @brief If bit_count_==0, storage is inactive, consider all bits to be 1
|
||||
All,
|
||||
/// @brief If bit_count_==0, storage is inactive, consider all bits to be 0
|
||||
None,
|
||||
};
|
||||
|
||||
/***
|
||||
**** life cycle
|
||||
***/
|
||||
|
@ -30,21 +49,29 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
/// @brief Builds bits from array of boolean flags
|
||||
Bitfield(bool const* bytes, size_t n);
|
||||
|
||||
~Bitfield()
|
||||
{
|
||||
this->setHasNone();
|
||||
setMode(OperationMode::None);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void setHasAll();
|
||||
/// @brief Creates new Bitfield with same count of bits as *this and replaces data from new_bits
|
||||
void setFrom(Span<uint8_t> new_bits, bool bounded)
|
||||
{
|
||||
*this = Bitfield(new_bits, this->bit_count_, bounded);
|
||||
}
|
||||
|
||||
void setHasNone();
|
||||
/// @brief Change the state (mode of operation)
|
||||
void setMode(OperationMode new_mode);
|
||||
|
||||
/// @brief Sets one bit
|
||||
void setBit(size_t bit);
|
||||
void setBit(size_t bit_index);
|
||||
|
||||
/// @brief Sets bit range [begin, end) to 1
|
||||
void setBitRange(size_t begin, size_t end);
|
||||
|
@ -61,29 +88,31 @@ public:
|
|||
|
||||
[[nodiscard]] size_t countRange(size_t begin, size_t end) const
|
||||
{
|
||||
if (this->hasAll())
|
||||
if (hasAll())
|
||||
{
|
||||
return end - begin;
|
||||
}
|
||||
|
||||
if (this->hasNone())
|
||||
if (hasNone())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->countRangeImpl(begin, end);
|
||||
return countRangeImpl(begin, end);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t countBits() const;
|
||||
|
||||
/// @brief Returns whether all bits are set, or mode is ALL. Always false for zero sized bitfield.
|
||||
[[nodiscard]] constexpr bool hasAll() const
|
||||
{
|
||||
return this->bit_count_ != 0 ? (this->true_count_ == this->bit_count_) : this->hint_ == HAS_ALL;
|
||||
return ((bit_count_ != 0) && (true_count_ == bit_count_)) || (mode_ == OperationMode::All);
|
||||
}
|
||||
|
||||
/// @brief Returns whether all bits are clear, or mode is NONE. Always false for zero sized bitfield.
|
||||
[[nodiscard]] constexpr bool hasNone() const
|
||||
{
|
||||
return this->bit_count_ != 0 ? (this->true_count_ == 0) : this->hint_ == HAS_NONE;
|
||||
return ((bit_count_ != 0) && (true_count_ == 0)) || (mode_ == OperationMode::None);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool readBit(size_t n) const;
|
||||
|
@ -92,13 +121,7 @@ public:
|
|||
****
|
||||
***/
|
||||
|
||||
void setFromFlags(bool const* bytes, size_t n);
|
||||
|
||||
void setFromBitfield(Bitfield const& src);
|
||||
|
||||
void setRaw(void const* newBits, size_t byte_count, bool bounded);
|
||||
|
||||
void* getRaw(size_t* byte_count) const;
|
||||
[[nodiscard]] std::vector<uint8_t> getRaw() const;
|
||||
|
||||
[[nodiscard]] size_t getBitCount() const
|
||||
{
|
||||
|
@ -106,41 +129,140 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr size_t countArray() const;
|
||||
[[nodiscard]] size_t countRangeImpl(size_t begin, size_t end) const;
|
||||
static void setBitsInArray(uint8_t* array, size_t bit_count);
|
||||
/// @brief Copies bits from the readonly view new_bits. Use Bitfield::setFrom to access this constructor
|
||||
/// @param bounded Whether incoming data is constrained by our memory and bit size
|
||||
Bitfield(Span<uint8_t> new_bits, size_t bit_count, bool bounded);
|
||||
|
||||
/// @brief Contains lookup table for how many set bits are there in 0..255
|
||||
static std::array<int8_t const, 256> true_bits_lookup_;
|
||||
|
||||
static constexpr size_t getStorageSize(size_t bit_count)
|
||||
{
|
||||
return (bit_count >> 3) + ((bit_count & 7) != 0 ? 1 : 0);
|
||||
return 1 + ((bit_count + 7) >> 3);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t countArray() const;
|
||||
[[nodiscard]] size_t countRangeImpl(size_t begin, size_t end) const;
|
||||
|
||||
/// @brief Given bit count, sets that many bits in the array, assumes array size is big enough.
|
||||
static void setBitsInArray(std::vector<uint8_t>& array, size_t bit_count);
|
||||
|
||||
void ensureNthBitFits(size_t n);
|
||||
|
||||
inline void setTrueCount(size_t n)
|
||||
{
|
||||
TR_ASSERT(mode_ == OperationMode::Normal);
|
||||
TR_ASSERT(n <= bit_count_);
|
||||
|
||||
true_count_ = n;
|
||||
|
||||
TR_ASSERT(isValid());
|
||||
}
|
||||
void ensureBitsAlloced(size_t n);
|
||||
bool ensureNthBitAlloced(size_t nth);
|
||||
void freeArray();
|
||||
void setTrueCount(size_t n);
|
||||
void rebuildTrueCount();
|
||||
void incTrueCount(size_t i);
|
||||
void decTrueCount(size_t i);
|
||||
|
||||
#ifdef TR_ENABLE_ASSERTS
|
||||
[[nodiscard]] bool isValid() const;
|
||||
#endif
|
||||
|
||||
uint8_t* bits_ = nullptr;
|
||||
size_t alloc_count_ = 0;
|
||||
/// @brief Set the bit
|
||||
inline void setBitImpl(size_t bit)
|
||||
{
|
||||
TR_ASSERT_MSG(mode_ == OperationMode::Normal, "Can only set bits in Normal operation mode");
|
||||
TR_ASSERT(isValid());
|
||||
|
||||
if (!readBit(bit))
|
||||
{
|
||||
ensureNthBitFits(bit);
|
||||
|
||||
auto const byte_offset = bit >> 3;
|
||||
size_t bit_value = size_t{ 0x80U } >> (bit & 7);
|
||||
|
||||
bits_[byte_offset] |= bit_value;
|
||||
setTrueCount(true_count_ + 1);
|
||||
}
|
||||
|
||||
TR_ASSERT(isValid());
|
||||
}
|
||||
|
||||
/// @brief Clear the bit
|
||||
inline void clearBitImpl(size_t bit)
|
||||
{
|
||||
TR_ASSERT_MSG(mode_ == OperationMode::Normal, "Can only set bits in Normal operation mode");
|
||||
TR_ASSERT(isValid());
|
||||
|
||||
if (readBit(bit))
|
||||
{
|
||||
ensureNthBitFits(bit);
|
||||
|
||||
size_t const byte_mask = size_t{ 0xFF7FU } >> (bit & 7U);
|
||||
bits_[bit >> 3] &= byte_mask;
|
||||
|
||||
TR_ASSERT(true_count_ > 0);
|
||||
setTrueCount(true_count_ - 1);
|
||||
}
|
||||
|
||||
TR_ASSERT(isValid());
|
||||
}
|
||||
|
||||
/// @brief Ensure that the memory is properly deallocated and size becomes zero
|
||||
inline void clearStorage()
|
||||
{
|
||||
bits_ = std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
inline void setBitRangeImpl(size_t begin, size_t end)
|
||||
{
|
||||
size_t start_byte = begin >> 3;
|
||||
size_t start_mask = ~(size_t{ 0xFFU } << (8 - (begin & 7)));
|
||||
|
||||
size_t end_byte = end >> 3;
|
||||
size_t end_mask = size_t{ 0xFFU } << (7 - (end & 7));
|
||||
|
||||
ensureNthBitFits(end);
|
||||
|
||||
if (start_byte == end_byte)
|
||||
{
|
||||
bits_[start_byte] |= start_mask & end_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
bits_[start_byte] |= start_mask;
|
||||
bits_[end_byte] |= end_mask;
|
||||
|
||||
if (++start_byte < end_byte)
|
||||
{
|
||||
std::fill_n(std::begin(bits_) + start_byte, end_byte - start_byte, 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void clearBitRangeImpl(size_t begin, size_t end)
|
||||
{
|
||||
size_t start_byte = begin >> 3;
|
||||
size_t start_mask = size_t{ 0xFFU } << (8 - (begin & 7));
|
||||
|
||||
size_t end_byte = end >> 3;
|
||||
size_t end_mask = ~(size_t{ 0xFFU } << (7 - (end & 7)));
|
||||
|
||||
ensureNthBitFits(end);
|
||||
|
||||
if (start_byte == end_byte)
|
||||
{
|
||||
bits_[start_byte] &= start_mask | end_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
bits_[start_byte] &= start_mask;
|
||||
bits_[end_byte] &= end_mask;
|
||||
|
||||
if (++start_byte < end_byte)
|
||||
{
|
||||
std::fill_n(std::begin(bits_) + start_byte, end_byte - start_byte, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bits_;
|
||||
size_t bit_count_ = 0;
|
||||
size_t true_count_ = 0;
|
||||
|
||||
enum OperationMode
|
||||
{
|
||||
/// @brief Normal operation: storage of bytes contains bits to set or clear
|
||||
NORMAL,
|
||||
/// @brief If bit_count_==0, storage is inactive, consider all bits to be 1
|
||||
HAS_ALL,
|
||||
/// @brief If bit_count_==0, storage is inactive, consider all bits to be 0
|
||||
HAS_NONE,
|
||||
};
|
||||
|
||||
// Special cases for when full or empty but we don't know the bitCount.
|
||||
// This occurs when a magnet link's peers send have all / have none
|
||||
OperationMode hint_ = NORMAL;
|
||||
OperationMode mode_ = OperationMode::Start;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ static void tr_cpReset(tr_completion* cp)
|
|||
cp->sizeNow = 0;
|
||||
cp->sizeWhenDoneIsDirty = true;
|
||||
cp->haveValidIsDirty = true;
|
||||
cp->blockBitfield->setHasNone();
|
||||
cp->blockBitfield->setMode(Bitfield::OperationMode::None);
|
||||
}
|
||||
|
||||
void tr_cpConstruct(tr_completion* cp, tr_torrent* tor)
|
||||
|
@ -36,7 +36,7 @@ void tr_cpBlockInit(tr_completion* cp, Bitfield const& b)
|
|||
tr_cpReset(cp);
|
||||
|
||||
// set blockBitfield
|
||||
cp->blockBitfield->setFromBitfield(b);
|
||||
*(cp->blockBitfield) = b;
|
||||
|
||||
// set sizeNow
|
||||
cp->sizeNow = cp->blockBitfield->countBits();
|
||||
|
@ -294,11 +294,10 @@ bool tr_cpFileIsComplete(tr_completion const* cp, tr_file_index_t i)
|
|||
return cp->blockBitfield->countRange(f, l + 1) == (l + 1 - f);
|
||||
}
|
||||
|
||||
void* tr_cpCreatePieceBitfield(tr_completion const* cp, size_t* byte_count)
|
||||
std::vector<uint8_t> tr_cpCreatePieceBitfield(tr_completion const* cp)
|
||||
{
|
||||
TR_ASSERT(tr_torrentHasMetadata(cp->tor));
|
||||
|
||||
void* ret;
|
||||
tr_piece_index_t n;
|
||||
|
||||
n = cp->tor->info.pieceCount;
|
||||
|
@ -307,7 +306,7 @@ void* tr_cpCreatePieceBitfield(tr_completion const* cp, size_t* byte_count)
|
|||
|
||||
if (tr_cpHasAll(cp))
|
||||
{
|
||||
pieces.setHasAll();
|
||||
pieces.setMode(Bitfield::OperationMode::All);
|
||||
}
|
||||
else if (!tr_cpHasNone(cp))
|
||||
{
|
||||
|
@ -318,12 +317,11 @@ void* tr_cpCreatePieceBitfield(tr_completion const* cp, size_t* byte_count)
|
|||
flags[i] = tr_cpPieceIsComplete(cp, i);
|
||||
}
|
||||
|
||||
pieces.setFromFlags(flags, n);
|
||||
pieces = Bitfield(flags, n);
|
||||
tr_free(flags);
|
||||
}
|
||||
|
||||
ret = pieces.getRaw(byte_count);
|
||||
return ret;
|
||||
return pieces.getRaw();
|
||||
}
|
||||
|
||||
double tr_cpPercentComplete(tr_completion const* cp)
|
||||
|
|
|
@ -124,7 +124,7 @@ static inline bool tr_cpBlockIsComplete(tr_completion const* cp, tr_block_index_
|
|||
|
||||
bool tr_cpFileIsComplete(tr_completion const* cp, tr_file_index_t);
|
||||
|
||||
void* tr_cpCreatePieceBitfield(tr_completion const* cp, size_t* byte_count);
|
||||
std::vector<uint8_t> tr_cpCreatePieceBitfield(tr_completion const* cp);
|
||||
|
||||
constexpr void tr_cpInvalidateDND(tr_completion* cp)
|
||||
{
|
||||
|
|
|
@ -1681,7 +1681,7 @@ static ReadState readBtMessage(tr_peerMsgsImpl* msgs, struct evbuffer* inbuf, si
|
|||
uint8_t* tmp = tr_new(uint8_t, msglen);
|
||||
dbgmsg(msgs, "got a bitfield");
|
||||
tr_peerIoReadBytes(msgs->io, inbuf, tmp, msglen);
|
||||
msgs->have.setRaw(tmp, msglen, tr_torrentHasMetadata(msgs->torrent));
|
||||
msgs->have.setFrom(Span{ tmp, msglen }, tr_torrentHasMetadata(msgs->torrent));
|
||||
msgs->publishClientGotBitfield(&msgs->have);
|
||||
updatePeerProgress(msgs);
|
||||
tr_free(tmp);
|
||||
|
@ -1775,7 +1775,7 @@ static ReadState readBtMessage(tr_peerMsgsImpl* msgs, struct evbuffer* inbuf, si
|
|||
|
||||
if (fext)
|
||||
{
|
||||
msgs->have.setHasAll();
|
||||
msgs->have.setMode(Bitfield::OperationMode::All);
|
||||
TR_ASSERT(msgs->have.hasAll());
|
||||
msgs->publishClientGotHaveAll();
|
||||
updatePeerProgress(msgs);
|
||||
|
@ -1793,7 +1793,7 @@ static ReadState readBtMessage(tr_peerMsgsImpl* msgs, struct evbuffer* inbuf, si
|
|||
|
||||
if (fext)
|
||||
{
|
||||
msgs->have.setHasNone();
|
||||
msgs->have.setMode(Bitfield::OperationMode::None);
|
||||
msgs->publishClientGotHaveNone();
|
||||
updatePeerProgress(msgs);
|
||||
}
|
||||
|
@ -2299,18 +2299,14 @@ static void sendBitfield(tr_peerMsgsImpl* msgs)
|
|||
{
|
||||
TR_ASSERT(tr_torrentHasMetadata(msgs->torrent));
|
||||
|
||||
void* bytes;
|
||||
size_t byte_count = 0;
|
||||
struct evbuffer* out = msgs->outMessages;
|
||||
|
||||
bytes = tr_torrentCreatePieceBitfield(msgs->torrent, &byte_count);
|
||||
evbuffer_add_uint32(out, sizeof(uint8_t) + byte_count);
|
||||
auto bytes = tr_torrentCreatePieceBitfield(msgs->torrent);
|
||||
evbuffer_add_uint32(out, sizeof(uint8_t) + bytes.size());
|
||||
evbuffer_add_uint8(out, BT_BITFIELD);
|
||||
evbuffer_add(out, bytes, byte_count);
|
||||
evbuffer_add(out, bytes.data(), std::size(bytes));
|
||||
dbgmsg(msgs, "sending bitfield... outMessage size is now %zu", evbuffer_get_length(out));
|
||||
pokeBatchPeriod(msgs, IMMEDIATE_PRIORITY_INTERVAL_SECS);
|
||||
|
||||
tr_free(bytes);
|
||||
}
|
||||
|
||||
static void tellPeerWhatWeHave(tr_peerMsgsImpl* msgs)
|
||||
|
|
|
@ -484,10 +484,8 @@ static void bitfieldToBenc(Bitfield const* b, tr_variant* benc)
|
|||
}
|
||||
else
|
||||
{
|
||||
size_t byte_count = 0;
|
||||
auto* raw = static_cast<uint8_t*>(b->getRaw(&byte_count));
|
||||
tr_variantInitRaw(benc, raw, byte_count);
|
||||
tr_free(raw);
|
||||
auto const raw = b->getRaw();
|
||||
tr_variantInitRaw(benc, raw.data(), std::size(raw));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,22 +674,22 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
}
|
||||
else if (buflen == 3 && memcmp(buf, "all", 3) == 0)
|
||||
{
|
||||
blocks.setHasAll();
|
||||
blocks.setMode(Bitfield::OperationMode::All);
|
||||
}
|
||||
else if (buflen == 4 && memcmp(buf, "none", 4) == 0)
|
||||
{
|
||||
blocks.setHasNone();
|
||||
blocks.setMode(Bitfield::OperationMode::None);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks.setRaw(buf, buflen, true);
|
||||
blocks.setFrom(Span{ buf, buflen }, true);
|
||||
}
|
||||
}
|
||||
else if (tr_variantDictFindStr(prog, TR_KEY_have, &str, nullptr))
|
||||
{
|
||||
if (strcmp(str, "all") == 0)
|
||||
{
|
||||
blocks.setHasAll();
|
||||
blocks.setMode(Bitfield::OperationMode::All);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -700,7 +698,7 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
}
|
||||
else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen))
|
||||
{
|
||||
blocks.setRaw(raw, rawlen, true);
|
||||
blocks.setFrom(Span{ raw, rawlen }, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -759,12 +759,10 @@ static void initField(
|
|||
case TR_KEY_pieces:
|
||||
if (tr_torrentHasMetadata(tor))
|
||||
{
|
||||
size_t byte_count = 0;
|
||||
void* bytes = tr_torrentCreatePieceBitfield(tor, &byte_count);
|
||||
auto* enc = static_cast<char*>(tr_base64_encode(bytes, byte_count, nullptr));
|
||||
auto const bytes = tr_torrentCreatePieceBitfield(tor);
|
||||
auto* enc = static_cast<char*>(tr_base64_encode(bytes.data(), std::size(bytes), nullptr));
|
||||
tr_variantInitStr(initme, enc != nullptr ? enc : "", TR_BAD_SIZE);
|
||||
tr_free(enc);
|
||||
tr_free(bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1514,7 +1514,7 @@ enum
|
|||
|
||||
static void turtleUpdateTable(struct tr_turtle_info* t)
|
||||
{
|
||||
t->minutes->setHasNone();
|
||||
t->minutes->setMode(Bitfield::OperationMode::None);
|
||||
|
||||
for (int day = 0; day < 7; ++day)
|
||||
{
|
||||
|
@ -2981,7 +2981,7 @@ std::vector<tr_torrent*> tr_sessionGetNextQueuedTorrents(tr_session* session, tr
|
|||
}
|
||||
|
||||
// find the best n candidates
|
||||
num_wanted = std::min(num_wanted, candidates.size());
|
||||
num_wanted = std::min(num_wanted, std::size(candidates));
|
||||
if (num_wanted < candidates.size())
|
||||
{
|
||||
std::partial_sort(
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file Copyright (C) Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __TRANSMISSION__
|
||||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
/// @brief Readonly non-owning view into a provided memory block with start pointer and size.
|
||||
/// In C++20 this appears in standard library as std::span and remotely similar usage.
|
||||
template<typename T>
|
||||
class Span
|
||||
{
|
||||
public:
|
||||
Span(T const* ptr, size_t size)
|
||||
: ptr_{ ptr }
|
||||
, size_{ size }
|
||||
{
|
||||
}
|
||||
|
||||
T const* begin() const
|
||||
{
|
||||
return this->ptr_;
|
||||
}
|
||||
|
||||
T const* end() const
|
||||
{
|
||||
return this->ptr_ + this->size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
private:
|
||||
T const* ptr_;
|
||||
size_t size_;
|
||||
};
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "bandwidth.h" /* tr_bandwidth */
|
||||
#include "completion.h" /* tr_completion */
|
||||
|
@ -460,9 +461,9 @@ static inline size_t tr_torrentMissingBytesInPiece(tr_torrent const* tor, tr_pie
|
|||
return tr_cpMissingBytesInPiece(&tor->completion, i);
|
||||
}
|
||||
|
||||
static inline void* tr_torrentCreatePieceBitfield(tr_torrent const* tor, size_t* byte_count)
|
||||
static inline std::vector<uint8_t> tr_torrentCreatePieceBitfield(tr_torrent const* tor)
|
||||
{
|
||||
return tr_cpCreatePieceBitfield(&tor->completion, byte_count);
|
||||
return tr_cpCreatePieceBitfield(&tor->completion);
|
||||
}
|
||||
|
||||
constexpr uint64_t tr_torrentHaveTotal(tr_torrent const* tor)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#define TR_ASSERT(x) ((void)(TR_LIKELY(x) || tr_assert_report(__FILE__, __LINE__, "%s", #x)))
|
||||
#define TR_ASSERT_MSG(x, ...) ((void)(TR_LIKELY(x) || tr_assert_report(__FILE__, __LINE__, __VA_ARGS__)))
|
||||
#define TR_UNREACHABLE() tr_assert_report(__FILE__, __LINE__, "Unreachable code")
|
||||
|
||||
#define TR_ENABLE_ASSERTS
|
||||
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
#define TR_ASSERT(x) ((void)0)
|
||||
#define TR_ASSERT_MSG(x, ...) ((void)0)
|
||||
#define TR_UNREACHABLE() ((void)0)
|
||||
|
||||
#undef TR_ENABLE_ASSERTS
|
||||
|
||||
|
|
|
@ -1179,7 +1179,7 @@ uint16_t tr_torrentGetPeerLimit(tr_torrent const* tor);
|
|||
enum
|
||||
{
|
||||
TR_PRI_LOW = -1,
|
||||
TR_PRI_NORMAL = 0, /* since NORMAL is 0, memset initializes nicely */
|
||||
TR_PRI_NORMAL = 0, /* since Normal is 0, memset initializes nicely */
|
||||
TR_PRI_HIGH = 1
|
||||
};
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ char const* tr_strip_positional_args(char const* str)
|
|||
char const* in = str;
|
||||
size_t pos = 0;
|
||||
|
||||
for (; str && *str && pos + 1 < buf.size(); ++str)
|
||||
for (; str && *str && pos + 1 < std::size(buf); ++str)
|
||||
{
|
||||
buf[pos++] = *str;
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
, bandwidth(tor->bandwidth)
|
||||
{
|
||||
// init parent bits
|
||||
have.setHasAll();
|
||||
have.setMode(Bitfield::OperationMode::All);
|
||||
tr_peerUpdateProgress(tor, this);
|
||||
|
||||
file_urls.resize(tr_torrentInfo(tor)->fileCount);
|
||||
|
|
|
@ -182,11 +182,11 @@ TEST(Bitfields, hasAllNone)
|
|||
EXPECT_TRUE(field.hasAll());
|
||||
EXPECT_TRUE(!field.hasNone());
|
||||
|
||||
field.setHasNone();
|
||||
field.setMode(Bitfield::OperationMode::None);
|
||||
EXPECT_TRUE(!field.hasAll());
|
||||
EXPECT_TRUE(field.hasNone());
|
||||
|
||||
field.setHasAll();
|
||||
field.setMode(Bitfield::OperationMode::All);
|
||||
EXPECT_TRUE(field.hasAll());
|
||||
EXPECT_TRUE(!field.hasNone());
|
||||
}
|
||||
|
@ -197,11 +197,11 @@ TEST(Bitfields, hasAllNone)
|
|||
EXPECT_TRUE(!field.hasAll());
|
||||
EXPECT_TRUE(!field.hasNone());
|
||||
|
||||
field.setHasNone();
|
||||
field.setMode(Bitfield::OperationMode::None);
|
||||
EXPECT_TRUE(!field.hasAll());
|
||||
EXPECT_TRUE(field.hasNone());
|
||||
|
||||
field.setHasAll();
|
||||
field.setMode(Bitfield::OperationMode::All);
|
||||
EXPECT_TRUE(field.hasAll());
|
||||
EXPECT_TRUE(!field.hasNone());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue