Core/Bitset: Add Read method and FromPointer static method
This commit is contained in:
parent
43a3f15794
commit
d9774f30a3
|
|
@ -24,6 +24,7 @@ namespace Nz
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Bit;
|
class Bit;
|
||||||
|
using PointerSequence = std::pair<const void*, std::size_t>; //< Start pointer, bit offset
|
||||||
|
|
||||||
Bitset();
|
Bitset();
|
||||||
explicit Bitset(std::size_t bitCount, bool val);
|
explicit Bitset(std::size_t bitCount, bool val);
|
||||||
|
|
@ -49,6 +50,9 @@ namespace Nz
|
||||||
std::size_t GetCapacity() const;
|
std::size_t GetCapacity() const;
|
||||||
std::size_t GetSize() const;
|
std::size_t GetSize() const;
|
||||||
|
|
||||||
|
PointerSequence Read(const void* ptr, std::size_t bitCount);
|
||||||
|
PointerSequence Read(const PointerSequence& sequence, std::size_t bitCount);
|
||||||
|
|
||||||
void PerformsAND(const Bitset& a, const Bitset& b);
|
void PerformsAND(const Bitset& a, const Bitset& b);
|
||||||
void PerformsNOT(const Bitset& a);
|
void PerformsNOT(const Bitset& a);
|
||||||
void PerformsOR(const Bitset& a, const Bitset& b);
|
void PerformsOR(const Bitset& a, const Bitset& b);
|
||||||
|
|
@ -107,6 +111,8 @@ namespace Nz
|
||||||
static constexpr std::size_t bitsPerBlock = std::numeric_limits<Block>::digits;
|
static constexpr std::size_t bitsPerBlock = std::numeric_limits<Block>::digits;
|
||||||
static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();
|
static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();
|
||||||
|
|
||||||
|
static Bitset FromPointer(const void* ptr, std::size_t bitCount, PointerSequence* sequence = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t FindFirstFrom(std::size_t blockIndex) const;
|
std::size_t FindFirstFrom(std::size_t blockIndex) const;
|
||||||
Block GetLastBlockMask() const;
|
Block GetLastBlockMask() const;
|
||||||
|
|
|
||||||
|
|
@ -323,6 +323,76 @@ namespace Nz
|
||||||
return m_bitCount;
|
return m_bitCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Read a byte sequence into a bitset
|
||||||
|
*
|
||||||
|
* This function expand the bitset with bits read from a byte sequence
|
||||||
|
*
|
||||||
|
* \param ptr A pointer to the start of the byte sequence
|
||||||
|
* \param bitCount Number of bits to read from the byte sequence
|
||||||
|
*
|
||||||
|
* \returns A pointer to the next byte to read along with the next bit index (useful when reading multiple times)
|
||||||
|
*
|
||||||
|
* \remark For technical reasons, ceil(bitCount / 8) bytes from the sequence will always be read (even with non-multiple-of-8 bitCount)
|
||||||
|
*
|
||||||
|
* \see AppendBits
|
||||||
|
* \see Read
|
||||||
|
*/
|
||||||
|
template<typename Block, class Allocator>
|
||||||
|
typename Bitset<Block, Allocator>::PointerSequence Bitset<Block, Allocator>::Read(const void* ptr, std::size_t bitCount)
|
||||||
|
{
|
||||||
|
return Read(PointerSequence(ptr, 0U), bitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Read a byte sequence into a bitset
|
||||||
|
*
|
||||||
|
* This function expand the bitset with bits read from a pointer sequence (made of a pointer and a bit index)
|
||||||
|
*
|
||||||
|
* \param sequence A pointer sequence to the start of the byte sequence
|
||||||
|
* \param bitCount Number of bits to read from the byte sequence
|
||||||
|
*
|
||||||
|
* \returns A pointer to the next byte to read along with the next bit index (useful when reading multiple times)
|
||||||
|
*
|
||||||
|
* \remark For technical reasons, ceil(bitCount / 8) bytes from the sequence will always be read (even with non-multiple-of-8 bitCount)
|
||||||
|
*
|
||||||
|
* \see AppendBits
|
||||||
|
* \see Read
|
||||||
|
*/
|
||||||
|
template<typename Block, class Allocator>
|
||||||
|
typename Bitset<Block, Allocator>::PointerSequence Bitset<Block, Allocator>::Read(const PointerSequence& sequence, std::size_t bitCount)
|
||||||
|
{
|
||||||
|
NazaraAssert(sequence.first, "Invalid pointer sequence");
|
||||||
|
NazaraAssert(sequence.second < 8, "Invalid next bit index (must be < 8)");
|
||||||
|
|
||||||
|
std::size_t totalBitCount = sequence.second + bitCount;
|
||||||
|
|
||||||
|
const UInt8* u8Ptr = static_cast<const UInt8*>(sequence.first);
|
||||||
|
const UInt8* endPtr = u8Ptr + ((totalBitCount != 0) ? (totalBitCount - 1) / 8 : 0);
|
||||||
|
const UInt8* nextPtr = endPtr + ((totalBitCount % 8 != 0) ? 0 : 1);
|
||||||
|
|
||||||
|
// Read the first block apart to apply a mask on the first byte if necessary
|
||||||
|
if (sequence.second != 0)
|
||||||
|
{
|
||||||
|
UInt8 mask = ~((1U << sequence.second) - 1U);
|
||||||
|
|
||||||
|
std::size_t readCount = std::min(bitCount, 8 - sequence.second);
|
||||||
|
AppendBits(Block(*u8Ptr++ & mask) >> sequence.second, readCount);
|
||||||
|
bitCount -= readCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And then read the remaining bytes
|
||||||
|
while (u8Ptr <= endPtr)
|
||||||
|
{
|
||||||
|
std::size_t bitToRead = std::min<std::size_t>(bitCount, 8);
|
||||||
|
AppendBits(*u8Ptr++, bitToRead);
|
||||||
|
bitCount -= bitToRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns informations to continue reading
|
||||||
|
return PointerSequence(nextPtr, totalBitCount % 8);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Performs the "AND" operator between two bitsets
|
* \brief Performs the "AND" operator between two bitsets
|
||||||
*
|
*
|
||||||
|
|
@ -1039,6 +1109,35 @@ namespace Nz
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Builds a bitset from a byte sequence
|
||||||
|
*
|
||||||
|
* This function builds a bitset using a byte sequence by reading bitCount bits from it
|
||||||
|
*
|
||||||
|
* \param ptr A pointer to the start of the byte sequence
|
||||||
|
* \param bitCount Number of bits to read from the byte sequence
|
||||||
|
* \param sequence Optional data to pass to a next call to Read
|
||||||
|
*
|
||||||
|
* \return The constructed bitset
|
||||||
|
*
|
||||||
|
* \remark For technical reasons, ceil(bitCount / 8) bytes from the sequence will always be read (even with non-multiple-of-8 bitCount)
|
||||||
|
*
|
||||||
|
* \see AppendBits
|
||||||
|
* \see Read
|
||||||
|
*/
|
||||||
|
template<typename Block, class Allocator>
|
||||||
|
Bitset<Block, Allocator> Bitset<Block, Allocator>::FromPointer(const void* ptr, std::size_t bitCount, PointerSequence* sequence)
|
||||||
|
{
|
||||||
|
Bitset bitset;
|
||||||
|
|
||||||
|
if (sequence)
|
||||||
|
*sequence = bitset.Read(ptr, bitCount);
|
||||||
|
else
|
||||||
|
bitset.Read(ptr, bitCount);
|
||||||
|
|
||||||
|
return bitset;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Finds the position of the first bit set to true after the blockIndex
|
* \brief Finds the position of the first bit set to true after the blockIndex
|
||||||
* \return The position of the bit
|
* \return The position of the bit
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ template<typename Block> void CheckAppend(const char* title);
|
||||||
template<typename Block> void CheckBitOps(const char* title);
|
template<typename Block> void CheckBitOps(const char* title);
|
||||||
template<typename Block> void CheckConstructor(const char* title);
|
template<typename Block> void CheckConstructor(const char* title);
|
||||||
template<typename Block> void CheckCopyMoveSwap(const char* title);
|
template<typename Block> void CheckCopyMoveSwap(const char* title);
|
||||||
|
template<typename Block> void CheckRead(const char* title);
|
||||||
|
|
||||||
SCENARIO("Bitset", "[CORE][BITSET]")
|
SCENARIO("Bitset", "[CORE][BITSET]")
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +29,7 @@ void Check(const char* title)
|
||||||
CheckBitOps<Block>(title);
|
CheckBitOps<Block>(title);
|
||||||
|
|
||||||
CheckAppend<Block>(title);
|
CheckAppend<Block>(title);
|
||||||
|
CheckRead<Block>(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Block>
|
template<typename Block>
|
||||||
|
|
@ -217,3 +219,55 @@ void CheckCopyMoveSwap(const char* title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Block>
|
||||||
|
void CheckRead(const char* title)
|
||||||
|
{
|
||||||
|
SECTION(title)
|
||||||
|
{
|
||||||
|
GIVEN("An empty bitset filled by reading")
|
||||||
|
{
|
||||||
|
#define BitVal1 10010101
|
||||||
|
#define BitVal2 11010010
|
||||||
|
#define BitVal3 01101010
|
||||||
|
std::array<Nz::UInt8, 3> data = {NazaraPrefixMacro(BitVal1, 0b), NazaraPrefixMacro(BitVal2, 0b), NazaraPrefixMacro(BitVal3, 0b)};
|
||||||
|
const char result[] = NazaraStringifyMacro(BitVal3) NazaraStringifyMacro(BitVal2) NazaraStringifyMacro(BitVal1);
|
||||||
|
std::size_t resultLength = Nz::CountOf(result) - 1;
|
||||||
|
std::size_t bitCount = data.size() * 8;
|
||||||
|
#undef BitVal1
|
||||||
|
#undef BitVal2
|
||||||
|
#undef BitVal3
|
||||||
|
|
||||||
|
std::array<std::pair<const char*, std::size_t>, 8> tests = {
|
||||||
|
{
|
||||||
|
{"We read bits one by one", 1},
|
||||||
|
{"We read bits two by two", 2},
|
||||||
|
{"We read bits three by three", 3},
|
||||||
|
{"We read bits four by four", 4},
|
||||||
|
{"We read bits six by six", 6},
|
||||||
|
{"We read bits byte by byte", 8},
|
||||||
|
{"We read bits twelve by twelve", 12},
|
||||||
|
{"We read bits all at once", 24}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& pair : tests)
|
||||||
|
{
|
||||||
|
WHEN(pair.first)
|
||||||
|
{
|
||||||
|
Nz::Bitset<Block> bitset;
|
||||||
|
|
||||||
|
auto seq = bitset.Read(data.data(), pair.second);
|
||||||
|
for (std::size_t i = pair.second; i < bitCount; i += pair.second)
|
||||||
|
seq = bitset.Read(seq, pair.second);
|
||||||
|
|
||||||
|
REQUIRE(bitset.GetSize() == bitCount);
|
||||||
|
|
||||||
|
Nz::Bitset<Block> expectedBitset(result);
|
||||||
|
|
||||||
|
CHECK(bitset == expectedBitset);
|
||||||
|
CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min<std::size_t>(1, bitCount % bitset.bitsPerBlock)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue