From a7d0cefbb96e637d6f456d0589f34fcc01b6c62b Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Tue, 20 Nov 2018 08:56:43 +0000 Subject: [PATCH] Add BitfieldPack implementation. --- include/duckhandy/bitfield_pack.hpp | 153 ++++++++++++++++++++++++++++ test/unit/CMakeLists.txt | 1 + test/unit/bitfield_pack_test.cpp | 35 +++++++ 3 files changed, 189 insertions(+) create mode 100644 include/duckhandy/bitfield_pack.hpp create mode 100644 test/unit/bitfield_pack_test.cpp diff --git a/include/duckhandy/bitfield_pack.hpp b/include/duckhandy/bitfield_pack.hpp new file mode 100644 index 0000000..93a2bfc --- /dev/null +++ b/include/duckhandy/bitfield_pack.hpp @@ -0,0 +1,153 @@ +/* Copyright 2016-2018 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#ifndef id895441FC389F481EB824A809A0BF5004 +#define id895441FC389F481EB824A809A0BF5004 + +#include +#include +#include + +namespace dhandy { + namespace implem { + template + class BitfieldData; + + template + class BitfieldData : public BitfieldData { + protected: + BitfieldData (T init) : BitfieldData(init), m_value(init) {} + public: + T& value() { return m_value; } + T value() const { return m_value; } + private: + T m_value; + }; + + template + class BitfieldData { + protected: + BitfieldData (T init) : m_value(init) {} + public: + T& value() { return m_value; } + T value() const { return m_value; } + private: + T m_value; + }; + + template + class BitfieldData; + + struct SizeAndOffs { + std::size_t size, offset; + }; + + template + [[gnu::pure]] + constexpr inline SizeAndOffs size_and_offset_impl (std::size_t) { + throw std::out_of_range("Requested pack index out of range"); + } + + template + [[gnu::pure]] + constexpr inline SizeAndOffs size_and_offset_impl (std::size_t index) { + return (index ? size_and_offset_impl(index - 1) : SizeAndOffs{Sz, Sum}); + } + + template + [[gnu::pure]] + constexpr inline SizeAndOffs size_and_offset (std::size_t index) { + return size_and_offset_impl<0, Sizes...>(index); + } + + template + struct BitfieldFacts { + static const constexpr std::size_t Sum = size_and_offset(sizeof...(Sizes) - 1).size + size_and_offset(sizeof...(Sizes) - 1).offset; + static const constexpr std::size_t ElementCount = (Sum + CHAR_BIT * sizeof(T) - 1) / (CHAR_BIT *sizeof(T)); + }; + + template + constexpr inline T make_mask (std::size_t len) { + return (1 << len) - 1; + } + } //namespace implem + + template + class BitfieldPack; + + template + T get (const BitfieldPack&); + template + void set (BitfieldPack&, V); + + template + class BitfieldPack : private implem::BitfieldData::ElementCount> { + template friend U get (const BitfieldPack&); + template friend void set (BitfieldPack&, V); + typedef implem::BitfieldData::ElementCount> base_type; + public: + BitfieldPack (T init=T{}) : base_type(init) {} + }; + + template + T get (const BitfieldPack& pack) { + using implem::size_and_offset; + using implem::BitfieldData; + using implem::make_mask; + + const constexpr auto so = size_and_offset(Idx); + const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT); + const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT); + const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT; + + const T bottom = static_cast&>(pack).value() >> reloffs; + if (bleeds_into_next) { + const std::size_t safe_index = index + 2 - not bleeds_into_next; + const T val = static_cast&>(pack).value(); + const T top = val << (sizeof(T) * CHAR_BIT - reloffs); + return static_cast(top | bottom) & make_mask(so.size); + } + + return bottom & make_mask(so.size); + } + + template + void set (BitfieldPack& pack, V val) { + using implem::size_and_offset; + using implem::BitfieldData; + using implem::make_mask; + + const constexpr auto so = size_and_offset(Idx); + const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT); + const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT); + const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT; + + T& bottom = static_cast&>(pack).value(); + bottom &= ~(make_mask(so.size) << reloffs); + bottom |= static_cast((val << reloffs) & make_mask(so.size)); + + if (bleeds_into_next) { + const std::size_t safe_index = index + 2 - not bleeds_into_next; + T& top = static_cast&>(pack).value(); + const constexpr std::size_t shift = sizeof(T) * CHAR_BIT - reloffs; + top &= ~(make_mask(so.size) >> shift); + top |= val >> shift; + } + } +} //namespace dhandy + +#endif diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f2db58b..99f2a2f 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(${PROJECT_NAME} endianness_test.cpp int_conv_test.cpp reversed_sized_array_test.cpp + bitfield_pack_test.cpp ) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/test/unit/bitfield_pack_test.cpp b/test/unit/bitfield_pack_test.cpp new file mode 100644 index 0000000..cad75bb --- /dev/null +++ b/test/unit/bitfield_pack_test.cpp @@ -0,0 +1,35 @@ +/* Copyright 2016-2018 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "duckhandy" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "duckhandy". If not, see . + */ + +#include "catch2/catch.hpp" +#include "duckhandy/bitfield_pack.hpp" +#include + +TEST_CASE ("Check correctness in BitfieldPack", "[BitfieldPack][containers]") { + dhandy::BitfieldPack pack(0xAA); + CHECK(sizeof(pack) == sizeof(uint8_t) * 2); + + CHECK(dhandy::get<0>(pack) == 0b1010U); + CHECK(dhandy::get<1>(pack) == 0b0101010U); + CHECK(dhandy::get<2>(pack) == 0b1U); + + dhandy::set<0>(pack, 2); + CHECK(dhandy::get<0>(pack) == 2); + dhandy::set<1>(pack, 50); + CHECK(dhandy::get<1>(pack) == 50); + CHECK(dhandy::get<2>(pack) == 0b1U); +}