mirror of
https://github.com/transmission/transmission
synced 2025-01-03 21:45:49 +00:00
953f07375a
* 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
268 lines
6.9 KiB
C++
268 lines
6.9 KiB
C++
/*
|
|
* This file Copyright (C) 2008-2014 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
|
|
|
|
#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
|
|
***/
|
|
explicit Bitfield(size_t bit_count);
|
|
|
|
Bitfield()
|
|
: Bitfield(0)
|
|
{
|
|
}
|
|
|
|
/// @brief Builds bits from array of boolean flags
|
|
Bitfield(bool const* bytes, size_t n);
|
|
|
|
~Bitfield()
|
|
{
|
|
setMode(OperationMode::None);
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
/// @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);
|
|
}
|
|
|
|
/// @brief Change the state (mode of operation)
|
|
void setMode(OperationMode new_mode);
|
|
|
|
/// @brief Sets one bit
|
|
void setBit(size_t bit_index);
|
|
|
|
/// @brief Sets bit range [begin, end) to 1
|
|
void setBitRange(size_t begin, size_t end);
|
|
|
|
/// @brief Clears one bit
|
|
void clearBit(size_t bit);
|
|
|
|
/// @brief Clears bit range [begin, end) to 0
|
|
void clearBitRange(size_t begin, size_t end);
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
[[nodiscard]] size_t countRange(size_t begin, size_t end) const
|
|
{
|
|
if (hasAll())
|
|
{
|
|
return end - begin;
|
|
}
|
|
|
|
if (hasNone())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
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 ((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 ((bit_count_ != 0) && (true_count_ == 0)) || (mode_ == OperationMode::None);
|
|
}
|
|
|
|
[[nodiscard]] bool readBit(size_t n) const;
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
[[nodiscard]] std::vector<uint8_t> getRaw() const;
|
|
|
|
[[nodiscard]] size_t getBitCount() const
|
|
{
|
|
return bit_count_;
|
|
}
|
|
|
|
private:
|
|
/// @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 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());
|
|
}
|
|
|
|
#ifdef TR_ENABLE_ASSERTS
|
|
[[nodiscard]] bool isValid() const;
|
|
#endif
|
|
|
|
/// @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;
|
|
OperationMode mode_ = OperationMode::Start;
|
|
};
|