Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkcgen <kcgen@users.noreply.github.com>2022-06-04 04:30:35 +0300
committerkcgen <kcgen@users.noreply.github.com>2022-06-06 09:42:28 +0300
commit47422de44b118f178e38e9cbf8d51d7a26803e4a (patch)
treecb30d1ed302916f74f5754e8589a78f0243fee42
parent5ca09cb7a01e09b9e84bd25be5f126e2cb4bd2ba (diff)
Refactor BitField into "bit_view" and add unit-testskc/bit_view-1
"bit_view" is a better description for how the class operates given it's a "view" of the bits held in the the underlying storage. This is meant to be similar to "string_view", which is also a similar "functional wrapper" around the underlying char* data. --- The author of the BitField class also describes it like this: Q. How do you ensure all the bitfields are usable? Because inherently, union will only use the max size member. You will not be able to see unique data for other members, if one is assigned a value, it will be the value for all other members. A. Remember that this isn't actually using bit-fields, but is emulating the effect of them. The "bits" are just a "view" into the underlying storage. Changing one is intended to be reflected in both. --- Other changes: - Use lower-case types and name similar to 'string_view', given there's nothing DOSBox-specific about it. This makes it feel more like "standard plumbing", and indeed, anyone can take this class into other projects - Add compile-time asserts to catch index and width issues - Make all functionality constexpr (compile-time evaluation) - Add unit tests for all functions - Fix decrement bug caught in unit tests - Fix comparison bugs caught in unit tests - Drop single-bit template specialization. A bit_view with a size of 1 is already known and optimized at compile-time now using "constexpr" - Remove the bool cast operators in favour of exact value operator, which are clearer to understand - Add member functions from bitops: - clear(): sets bits to zero - flip(): flip bits - none(): check if all bits are unset - any(): check if at least one bit is set - all(): check if all bits are set - Add comments - License as GPL v2+ (from public domain), given all lines have changed. Credit and thanks given to Evan Teran (thanks!)
-rw-r--r--include/BitField.h84
-rw-r--r--include/bit_view.h245
-rw-r--r--tests/bit_view_tests.cpp417
-rw-r--r--tests/meson.build1
-rw-r--r--tests/vs/tests.vcxproj1
5 files changed, 664 insertions, 84 deletions
diff --git a/include/BitField.h b/include/BitField.h
deleted file mode 100644
index 5d7e5ad3c..000000000
--- a/include/BitField.h
+++ /dev/null
@@ -1,84 +0,0 @@
-#ifndef BITFIELD_H_
-#define BITFIELD_H_
-
-#include <cstddef>
-#include <cstdint>
-
-template <class T, size_t Index, size_t Bits = 1>
-class BitField {
-private:
- enum {
- Mask = (1u << Bits) - 1u
- };
-
-public:
- BitField &operator=(const BitField &rhs) {
- value_ = (value_ & ~Mask) | (rhs.value_ & Mask);
- return *this;
- }
-
- template <class T2>
- BitField &operator=(T2 value) {
- value_ = (value_ & ~(Mask << Index)) | ((value & Mask) << Index);
- return *this;
- }
-
- operator T() const {
- return (value_ >> Index) & Mask;
- }
-
- explicit operator bool() const {
- return (value_ & (Mask << Index)) != 0;
- }
-
- BitField &operator++() {
- return *this = *this + 1;
- }
-
- T operator++(int) {
- T r = *this;
- ++*this;
- return r;
- }
-
- BitField &operator--() {
- return *this = *this - 1;
- }
-
- T operator--(int) {
- T r = *this;
- ++*this;
- return r;
- }
-
-private:
- T value_;
-};
-
-template <class T, size_t Index>
-class BitField<T, Index, 1> {
-private:
- enum {
- Bits = 1,
- Mask = 0x01
- };
-
-public:
- BitField &operator=(bool value) {
- value_ = (value_ & ~(Mask << Index)) | (value << Index);
- return *this;
- }
-
- operator bool() const {
- return (value_ & (Mask << Index)) != 0;
- }
-
- bool operator!() const {
- return (value_ & (Mask << Index)) == 0;
- }
-
-private:
- T value_;
-};
-
-#endif
diff --git a/include/bit_view.h b/include/bit_view.h
new file mode 100644
index 000000000..dd5cccc39
--- /dev/null
+++ b/include/bit_view.h
@@ -0,0 +1,245 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2022-2022 The DOSBox Staging Team
+ *
+ * Thanks to Evan Teran for his public domain BitField class, on which
+ * this is based.
+ *
+ * This program 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 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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
+ * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ * Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+bit_view
+~~~~~~~~
+
+The bit_view class is a wrapper around a data value, typically a union member:
+
+union Register {
+ uint8_t data = 0;
+ bit_view<1, 1> first_bit;
+};
+
+It provides a view into a subset of its bits allowing them to be
+read, written, assigned, flipped, cleared, and tested, without the need to for
+the usual twiddling operations.
+
+Constructing a bit_view is similar to C bitfields, however unlike C bitfields,
+bit_views are free from undefined behavior and have been proven using GCC's
+and Clang's undefined behavior sanitizers.
+
+This gives us the benefits of bitfields without their specification downsides:
+- they're succinct and clear to use
+- they're just as fast as bitwise operators (maybe more so, being constexpr)
+- they're self-documenting using their bit positions, sizes, and field names
+
+Example usage
+~~~~~~~~~~~~~
+
+Assume we have a 16-bit audio control register that holds the card's:
+- left volume at bit 1 using 6 bits
+- right volume at bit 7, also 6 bits
+- speaker on/off state at bit 13 with just one bit
+
+The bit_view makes the register's logical elements self-documenting:
+
+union AudioReg {
+ uint16_t data = 0;
+ bit_view<1, 6> left_volume;
+ bit_view<7, 6> right_volume;
+ bit_view<13, 1> speaker_on;
+};
+
+AudioReg reg = {data};
+
+if (reg.speaker_on)
+ enable_speaker();
+else
+ disable_speaker();
+
+const auto left_percent = reg.left_volume / 63.0;
+const auto right_percent = reg.right_volume / 63.0;
+set_volume(left_percent, right_percent);
+*/
+
+#ifndef BIT_VIEW_H_
+#define BIT_VIEW_H_
+
+#include <cassert>
+#include <type_traits>
+
+#include "bitops.h"
+#include "support.h"
+
+template <int view_index, int view_width>
+class bit_view {
+private:
+ // ensure the data type is large enough to hold the view
+ using data_type = nearest_uint_t<view_index + view_width>;
+
+ // compile-time assert that the view is valid
+ static_assert(std::is_unsigned<data_type>::value,
+ "the bit_view's data type needs to be unsigned");
+
+ static_assert(view_index >= 0,
+ "the bit_view's index needs to be zero or greater");
+
+ static_assert(view_width > 0,
+ "the bit_view's width needs to span at least one bit");
+
+ static_assert(view_index + view_width <= std::numeric_limits<data_type>::digits,
+ "the bit_view's extents need to fit in the data type");
+
+ // ensure the right-hand-side is an integer and fits in the data type
+ template <typename rhs_type>
+ constexpr void check_rhs([[maybe_unused]] const rhs_type rhs_value) noexcept
+ {
+ // detect attempts to assign from non-integral types
+ static_assert(std::is_integral<rhs_type>::value,
+ "the bit_view's value can only accept integral types");
+
+ // detect assignments of negative values
+ if (std::is_signed<rhs_type>::value)
+ assert(rhs_value >= 0);
+
+ // detect assignment of values that are too large
+ constexpr uint64_t max_data_value = (1u << view_width);
+ assert(static_cast<uint64_t>(rhs_value) < max_data_value);
+ }
+
+ // hold the view's masks using an enum to avoid using class storage
+ enum mask : data_type {
+ unshifted = (1u << view_width) - 1u,
+ shifted = unshifted << view_index,
+ };
+
+ // leave the member uninitialised; let union peer(s) initialize the data
+ data_type data;
+
+public:
+ constexpr bit_view() = default;
+
+ // construct the view from a right-hand-side value
+ template <class rhs_type>
+ constexpr bit_view(const rhs_type rhs_value) noexcept
+ {
+ // leverage the implicit conversion operator to assign the value
+ *this = rhs_value;
+ }
+
+ // assign from right-hand-side value
+ template <class rhs_type>
+ bit_view &operator=(const rhs_type rhs_value) noexcept
+ {
+ check_rhs(rhs_value);
+ const auto outer = data & ~mask::shifted;
+ const auto inner = (rhs_value & mask::unshifted) << view_index;
+
+ data = static_cast<data_type>(outer | inner);
+ return *this;
+ }
+
+ // assign the view from another bit_view if the same type
+ constexpr bit_view &operator=(const bit_view &other) noexcept
+ {
+ const data_type d = other;
+ return *this = d;
+ }
+
+ // read the view's value
+ constexpr operator data_type() const noexcept
+ {
+ return (data & mask::shifted) >> view_index;
+ }
+
+ // pre-increment the view's value
+ constexpr bit_view &operator++() noexcept
+ {
+ return *this = *this + 1;
+ }
+
+ // post-increment the view's value
+ constexpr data_type operator++(int) noexcept
+ {
+ const data_type d = *this;
+ ++*this;
+ return d;
+ }
+
+ // increment the view's value by the right-hand side
+ template <class rhs_type>
+ constexpr bit_view &operator+=(const rhs_type rhs_value) noexcept
+ {
+ check_rhs(rhs_value);
+ return *this = *this + rhs_value;
+ }
+
+ // pre-decrement the view's value
+ constexpr bit_view &operator--() noexcept
+ {
+ return *this = *this - 1;
+ }
+
+ // post-decrement the view's value
+ constexpr data_type operator--(int) noexcept
+ {
+ const data_type d = *this;
+ --*this;
+ return d;
+ }
+
+ // decrement the view's value by the right-hand side
+ template <class rhs_type>
+ constexpr bit_view &operator-=(const rhs_type rhs_value) noexcept
+ {
+ check_rhs(rhs_value);
+ return *this = *this - rhs_value;
+ }
+
+ // check if all the view's bits are set
+ constexpr bool all() const noexcept
+ {
+ return bit::is(data, mask::shifted);
+ }
+
+ // check if any of the view's bits are set
+ constexpr bool any() const noexcept
+ {
+ return bit::any(data, mask::shifted);
+ }
+
+ // check if none of the view's bits are set
+ constexpr bool none() const noexcept
+ {
+ return bit::cleared(data, mask::shifted);
+ }
+
+ // expose the view's underlying data
+ constexpr data_type get_data() const noexcept
+ {
+ return data & mask::shifted;
+ }
+
+ // flip the view's bits
+ constexpr void flip() noexcept
+ {
+ bit::flip(data, mask::shifted);
+ }
+
+ // clear the view's bits
+ constexpr void clear() noexcept
+ {
+ bit::clear(data, mask::shifted);
+ }
+};
+
+#endif
diff --git a/tests/bit_view_tests.cpp b/tests/bit_view_tests.cpp
new file mode 100644
index 000000000..db9db5a00
--- /dev/null
+++ b/tests/bit_view_tests.cpp
@@ -0,0 +1,417 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2022-2022 The DOSBox Staging Team
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "bit_view.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+namespace {
+
+union Register {
+ uint8_t data = 0;
+ bit_view<0, 2> first_2; // is bits 0 and 1
+ bit_view<2, 3> middle_3; // is bits 2, 3, and 4
+ bit_view<5, 3> last_3; // is bits 5, 6, and 7
+};
+
+// function to take in a reference of a Register and set its bits
+void set_bits(Register &reg, uint8_t first, uint8_t middle, uint8_t last)
+{
+ reg.first_2 = first;
+ reg.middle_3 = middle;
+ reg.last_3 = last;
+}
+
+// function to take in a reference of a Register and get its bits
+void get_bits(const Register &reg, uint8_t &first, uint8_t &middle, uint8_t &last)
+{
+ first = reg.first_2;
+ middle = reg.middle_3;
+ last = reg.last_3;
+}
+
+// function to take in a pointer to a Register and set its bits
+void set_bits(Register *reg, uint8_t first, uint8_t middle, uint8_t last)
+{
+ reg->first_2 = first;
+ reg->middle_3 = middle;
+ reg->last_3 = last;
+}
+
+// function to take in a pointer to a Register and get its bits
+void get_bits(const Register *reg, uint8_t &first, uint8_t &middle, uint8_t &last)
+{
+ first = reg->first_2;
+ middle = reg->middle_3;
+ last = reg->last_3;
+}
+
+TEST(bit_view, assign_from_literal)
+{
+ constexpr Register r = {0b111'000'11};
+
+ EXPECT_EQ(r.first_2, 0b11);
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+}
+
+TEST(bit_view, assign_to_data)
+{
+ Register r1 = {0b111'000'11};
+ Register r2 = {0b000'111'00};
+
+ // storage-to-storage assignment
+ r1.data = r2.data;
+
+ EXPECT_EQ(r1.first_2, 0b00);
+ EXPECT_EQ(r1.middle_3, 0b111);
+ EXPECT_EQ(r1.last_3, 0b000);
+}
+
+TEST(bit_view, assign_from_parts)
+{
+ Register r1 = {0b111'000'11};
+ Register r2 = {0b000'111'00};
+
+ r1.middle_3 = r2.middle_3;
+
+ EXPECT_EQ(r1.first_2, 0b11);
+ EXPECT_EQ(r1.middle_3, 0b111);
+ EXPECT_EQ(r1.last_3, 0b111);
+}
+
+TEST(bit_view, flip)
+{
+ Register r = {0b111'000'11};
+
+ r.middle_3.flip();
+
+ EXPECT_EQ(r.first_2, 0b11);
+ EXPECT_EQ(r.middle_3, 0b111);
+ EXPECT_EQ(r.last_3, 0b111);
+}
+
+TEST(bit_view, increment)
+{
+ Register r = {0b111'000'00};
+
+ // post-increment
+ EXPECT_EQ(r.first_2++, 0b00);
+ EXPECT_EQ(r.first_2, 0b01);
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // pre-increment
+ EXPECT_EQ(++r.first_2, 0b10);
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // RHS value increment
+ r.first_2 += 1;
+ EXPECT_EQ(r.first_2, 0b11);
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // overflow is caught
+ EXPECT_DEBUG_DEATH({ r.first_2++; }, "");
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+}
+
+TEST(bit_view, decrement)
+{
+ Register r = {0b111'000'11};
+
+ // post-decrement
+ EXPECT_EQ(r.first_2--, 0b11);
+ EXPECT_EQ(r.first_2, 0b10);
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // pre-decrement
+ EXPECT_EQ(--r.first_2, 0b01);
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // RHS value decrement
+ r.first_2 -= 1;
+ EXPECT_EQ(r.first_2, 0b00);
+ // next decrement will underflow
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+
+ // underflow is caught
+ EXPECT_DEBUG_DEATH({ r.first_2--; }, "");
+
+ // make sure adjacent bits are not affected
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+}
+
+TEST(bit_view, clear)
+{
+ Register r = {0b111'111'11};
+
+ r.middle_3.clear();
+
+ EXPECT_EQ(r.first_2, 0b11);
+ EXPECT_EQ(r.middle_3, 0b000);
+ EXPECT_EQ(r.last_3, 0b111);
+}
+
+TEST(bit_view, boolean_checks)
+{
+ constexpr Register r = {0b111'010'00};
+
+ EXPECT_TRUE(r.first_2.none());
+ EXPECT_FALSE(r.first_2.any());
+ EXPECT_FALSE(r.first_2.all());
+
+ EXPECT_FALSE(r.middle_3.none());
+ EXPECT_TRUE(r.middle_3.any());
+ EXPECT_FALSE(r.middle_3.all());
+
+ EXPECT_FALSE(r.last_3.none());
+ EXPECT_TRUE(r.last_3.any());
+ EXPECT_TRUE(r.last_3.all());
+}
+
+TEST(bit_view, equality)
+{
+ constexpr Register r1 = {0b111'010'00};
+ constexpr Register r2 = {0b111'010'11};
+
+ // equality tests
+ EXPECT_FALSE(r1.first_2 == r2.first_2);
+ EXPECT_TRUE(r1.middle_3 == r2.middle_3);
+ EXPECT_TRUE(r1.last_3 == r2.last_3);
+
+ // in-equality tests
+ EXPECT_TRUE(r1.first_2 != r2.first_2);
+ EXPECT_FALSE(r1.middle_3 != r2.middle_3);
+ EXPECT_FALSE(r1.last_3 != r2.last_3);
+}
+
+TEST(bit_view, size_safety)
+{
+ // unions are sized to the largest member, so in this case,
+ // it's the second view which is requires to be 16-bit to accomodate the
+ // view's bit position and width.
+ union RegisterSmallData {
+ uint8_t data = 0;
+ bit_view<0, 8> first_8;
+ bit_view<8, 8> last_8;
+ };
+
+ RegisterSmallData r8 = {0b0000'1111};
+ EXPECT_TRUE(r8.first_8 == 0b000'1111);
+
+ // write into bits 8 through 15 are legal & safe
+ r8.last_8 = 0b1111'0000;
+ EXPECT_TRUE(r8.last_8 == 0b1111'0000);
+
+ // make sure adjacent bits are not affected
+ EXPECT_TRUE(r8.first_8 == 0b000'1111);
+}
+
+TEST(bit_view, illegal_view)
+{
+ union Register {
+ uint32_t data = 0;
+ // the following fails to compile because the view is out of
+ // range:
+
+ // bit_view<48, 128> too_large;
+
+ // error: static_assert failed due to requirement 48 + 128 <=
+ // std::numeric_limits<unsigned long>::digits' "bit_view cannot
+ // exceed the number of bits in the data_type"
+ };
+}
+
+TEST(bit_view, writable_via_reference)
+{
+ // create a register and set it bits using the "set_bits" function
+ Register r = {0};
+
+ constexpr auto first_val = 0b11;
+ constexpr auto middle_val = 0b010;
+ constexpr auto last_val = 0b111;
+
+ set_bits(r, first_val, middle_val, last_val);
+
+ EXPECT_EQ(r.first_2, first_val);
+ EXPECT_EQ(r.middle_3, middle_val);
+ EXPECT_EQ(r.last_3, last_val);
+}
+
+TEST(bit_view, readable_via_reference)
+{
+ // create a register and set it bits using the "set_bits" function
+ constexpr Register r = {0b111'010'11};
+
+ uint8_t first_val = 0;
+ uint8_t middle_val = 0;
+ uint8_t last_val = 0;
+
+ get_bits(r, first_val, middle_val, last_val);
+
+ EXPECT_EQ(first_val, 0b11);
+ EXPECT_EQ(middle_val, 0b010);
+ EXPECT_EQ(last_val, 0b111);
+}
+
+TEST(bit_view, writable_via_pointer)
+{
+ // create a register and set it bits using the "set_bits" function
+ Register r = {0};
+
+ constexpr auto first_val = 0b11;
+ constexpr auto middle_val = 0b010;
+ constexpr auto last_val = 0b111;
+
+ set_bits(&r, first_val, middle_val, last_val);
+
+ EXPECT_EQ(r.first_2, first_val);
+ EXPECT_EQ(r.middle_3, middle_val);
+ EXPECT_EQ(r.last_3, last_val);
+}
+
+TEST(bit_view, readable_via_pointer)
+{
+ // create a register and set it bits using the "set_bits" function
+ constexpr Register r = {0b111'010'11};
+
+ uint8_t first_val = 0;
+ uint8_t middle_val = 0;
+ uint8_t last_val = 0;
+
+ get_bits(&r, first_val, middle_val, last_val);
+
+ EXPECT_EQ(first_val, 0b11);
+ EXPECT_EQ(middle_val, 0b010);
+ EXPECT_EQ(last_val, 0b111);
+}
+
+TEST(bit_view, create_with_new)
+{
+ // create a register and set it bits using the "set_bits" function
+ auto r = new Register{0b111'010'11};
+
+ constexpr auto first_val = 0b11;
+ constexpr auto middle_val = 0b010;
+ constexpr auto last_val = 0b111;
+
+ set_bits(r, first_val, middle_val, last_val);
+
+ EXPECT_EQ(r->first_2, first_val);
+ EXPECT_EQ(r->middle_3, middle_val);
+ EXPECT_EQ(r->last_3, last_val);
+
+ delete r;
+ r = nullptr;
+}
+
+TEST(bit_view, create_with_make_unique)
+{
+ // create a register and set it bits using the "set_bits" function
+ auto r = std::make_unique<Register>();
+
+ r->data = {0b111'010'11};
+
+ constexpr auto first_val = 0b11;
+ constexpr auto middle_val = 0b010;
+ constexpr auto last_val = 0b111;
+
+ set_bits(r.get(), first_val, middle_val, last_val);
+
+ EXPECT_EQ(r->first_2, first_val);
+ EXPECT_EQ(r->middle_3, middle_val);
+ EXPECT_EQ(r->last_3, last_val);
+
+ r.reset();
+}
+
+TEST(bit_view, use_in_array)
+{
+ Register regs[2] = {{0b111'010'11}, {0b000'101'00}};
+
+ // test a couple
+ EXPECT_EQ(regs[0].first_2, 0b11);
+ EXPECT_EQ(regs[1].middle_3, 0b101);
+
+ // flip a couple
+ regs[0].first_2.flip();
+ regs[1].middle_3.flip();
+
+ EXPECT_EQ(regs[0].first_2, regs[1].first_2);
+ EXPECT_EQ(regs[0].middle_3, regs[1].middle_3);
+}
+
+TEST(bit_view, bare_initialization)
+{
+ const bit_view<0, 2> two_bits = {0b10};
+ EXPECT_EQ(two_bits, 0b10);
+ EXPECT_EQ(two_bits.get_data(), 0b000000'10);
+
+ const bit_view<2, 3> three_bits = {0b101};
+ EXPECT_EQ(three_bits, 0b101);
+ EXPECT_EQ(three_bits.get_data(), 0b000'101'00);
+
+ const bit_view<4, 4> four_bits = {0b1011};
+ EXPECT_EQ(four_bits, 0b1011);
+ EXPECT_EQ(four_bits.get_data(), 0b1011'0000);
+}
+
+TEST(bit_view, bare_constructor)
+{
+ const bit_view<0, 2> one_bit = {0b1};
+ EXPECT_EQ(one_bit, 0b1);
+ EXPECT_EQ(one_bit.get_data(), 0b000000'1);
+
+ const bit_view<0, 2> two_bits(0b11);
+ EXPECT_EQ(two_bits, 0b11);
+ EXPECT_EQ(two_bits.get_data(), 0b000000'11);
+
+ const bit_view<2, 3> three_bits(0b111);
+ EXPECT_EQ(three_bits, 0b111);
+ EXPECT_EQ(three_bits.get_data(), 0b000'111'00);
+
+ const bit_view<4, 4> four_bits(0b1111);
+ EXPECT_EQ(four_bits, 0b1111);
+ EXPECT_EQ(four_bits.get_data(), 0b1111'0000);
+}
+
+} // namespace
diff --git a/tests/meson.build b/tests/meson.build
index cfb425a25..2ed998bee 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -57,6 +57,7 @@ test('gtest fs_utils', fs_utils,
unit_tests = [
{'name' : 'bitops', 'deps' : []},
+ {'name' : 'bit_view', 'deps' : []},
{'name' : 'iohandler_containers', 'deps' : [libmisc_dep]},
{'name' : 'rwqueue', 'deps' : [libmisc_dep]},
{'name' : 'soft_limiter', 'deps' : [atomic_dep, libiir1_dep, libmisc_dep]},
diff --git a/tests/vs/tests.vcxproj b/tests/vs/tests.vcxproj
index 7c1d5eae2..ec3c8ced2 100644
--- a/tests/vs/tests.vcxproj
+++ b/tests/vs/tests.vcxproj
@@ -241,6 +241,7 @@ $(TargetPath)</Command>
<ClCompile Include="..\..\src\libs\iir1\iir\RBJ.cpp" />
<ClCompile Include="..\ansi_code_markup_tests.cpp" />
<ClCompile Include="..\bitops_tests.cpp" />
+ <ClCompile Include="..\bit_view_tests.cpp" />
<ClCompile Include="..\fs_utils_tests.cpp" />
<ClCompile Include="..\iohandler_containers_tests.cpp" />
<ClCompile Include="..\rwqueue_tests.cpp" />