From 43a3f15794d401d4cbea68e9c1bceeae7b915e79 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 17 Nov 2016 22:56:48 +0100 Subject: [PATCH] Core/Bitset: Add AppendBits function --- include/Nazara/Core/Bitset.hpp | 2 ++ include/Nazara/Core/Bitset.inl | 48 +++++++++++++++++++++++++++ tests/Engine/Core/Bitset.cpp | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/include/Nazara/Core/Bitset.hpp b/include/Nazara/Core/Bitset.hpp index df3fff34d..5aca6ef39 100644 --- a/include/Nazara/Core/Bitset.hpp +++ b/include/Nazara/Core/Bitset.hpp @@ -35,6 +35,8 @@ namespace Nz Bitset(Bitset&& bitset) noexcept = default; ~Bitset() noexcept = default; + template void AppendBits(T bits, std::size_t bitCount); + void Clear() noexcept; std::size_t Count() const; void Flip(); diff --git a/include/Nazara/Core/Bitset.inl b/include/Nazara/Core/Bitset.inl index c060da60e..7a7015059 100644 --- a/include/Nazara/Core/Bitset.inl +++ b/include/Nazara/Core/Bitset.inl @@ -136,6 +136,54 @@ namespace Nz } } + /*! + * \brief Appends bits to the bitset + * + * This function expand the bitset with bits extracted from a number value + * + * \param bits A number value from where bits will be extracted + * \param bitCount Number of bits to extract from the value + * + * \remark This function does not require bitCount to be lower or equal to the number of bits of T, thus + * reading 32 bits from a UInt8 will work (by extracting the first 8 bits values and appending 24 zeros afterneath). + * + * \see AppendBits + * \see Read + */ + template + template + void Bitset::AppendBits(T bits, std::size_t bitCount) + { + std::size_t bitShift = m_bitCount % bitsPerBlock; + m_bitCount += bitCount; + + if (bitShift != 0) + { + std::size_t remainingBits = bitsPerBlock - bitShift; + m_blocks.back() |= Block(bits) << bitShift; + bits >>= bitsPerBlock - bitShift; + + bitCount -= std::min(remainingBits, bitCount); + } + + if (bitCount > 0) + { + std::size_t blockCount = ComputeBlockCount(bitCount); + for (std::size_t block = 0; block < blockCount - 1; ++block) + { + m_blocks.push_back(static_cast(bits)); + bits >>= std::numeric_limits::digits; + bitCount -= std::numeric_limits::digits; + } + + // For the last iteration, mask out the bits we don't want + std::size_t remainingBits = bitCount; + + bits &= ((Block(1U) << remainingBits) - 1U); + m_blocks.push_back(static_cast(bits)); + } + } + /*! * \brief Clears the content of the bitset, GetSize() is now equals to 0 * diff --git a/tests/Engine/Core/Bitset.cpp b/tests/Engine/Core/Bitset.cpp index 27458a063..c8b98288c 100644 --- a/tests/Engine/Core/Bitset.cpp +++ b/tests/Engine/Core/Bitset.cpp @@ -6,6 +6,7 @@ #include template void Check(const char* title); +template void CheckAppend(const char* title); template void CheckBitOps(const char* title); template void CheckConstructor(const char* title); template void CheckCopyMoveSwap(const char* title); @@ -25,6 +26,65 @@ void Check(const char* title) CheckCopyMoveSwap(title); CheckBitOps(title); + + CheckAppend(title); +} + +template +void CheckAppend(const char* title) +{ + SECTION(title) + { + GIVEN("An empty bitset filled by bytes") + { + #define BitVal1 00110111 + #define BitVal2 11011110 + #define BitVal3 01000010 + std::array 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, 7> tests = { + { + {"We append bits one by one", 1}, + {"We append bits two by two", 2}, + {"We append bits three by three", 3}, + {"We append bits four by four", 4}, + {"We append bits six by six", 6}, + {"We append bits byte by byte", 8}, + {"We append bits twelve by twelve", 12} + } + }; + + for (auto& pair : tests) + { + WHEN(pair.first) + { + Nz::Bitset bitset; + + for (std::size_t i = 0; i < bitCount; i += pair.second) + { + Nz::UInt16 value = data[i / 8] >> (i % 8); + if ((i % 8) + pair.second > 8 && i/8 != data.size()-1) + value |= static_cast(data[i / 8 + 1]) << (8 - (i % 8)); + + bitset.AppendBits(value, pair.second); + } + + REQUIRE(bitset.GetSize() == bitCount); + + Nz::Bitset expectedBitset(result); + + CHECK(bitset == expectedBitset); + CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min(1, bitCount % bitset.bitsPerBlock))); + } + } + } + } } template