diff options
| -rw-r--r-- | src/common/bit_field.h | 12 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/tests/common/bit_field.cpp | 90 |
3 files changed, 100 insertions, 3 deletions
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 21e07925d..bd9e21e1e 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <limits> | 34 | #include <limits> |
| 35 | #include <type_traits> | 35 | #include <type_traits> |
| 36 | #include "common/common_funcs.h" | 36 | #include "common/common_funcs.h" |
| 37 | #include "common/swap.h" | ||
| 37 | 38 | ||
| 38 | /* | 39 | /* |
| 39 | * Abstract bitfield class | 40 | * Abstract bitfield class |
| @@ -108,7 +109,7 @@ | |||
| 108 | * symptoms. | 109 | * symptoms. |
| 109 | */ | 110 | */ |
| 110 | #pragma pack(1) | 111 | #pragma pack(1) |
| 111 | template <std::size_t Position, std::size_t Bits, typename T> | 112 | template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag> |
| 112 | struct BitField { | 113 | struct BitField { |
| 113 | private: | 114 | private: |
| 114 | // We hide the copy assigment operator here, because the default copy | 115 | // We hide the copy assigment operator here, because the default copy |
| @@ -127,6 +128,8 @@ private: | |||
| 127 | // We store the value as the unsigned type to avoid undefined behaviour on value shifting | 128 | // We store the value as the unsigned type to avoid undefined behaviour on value shifting |
| 128 | using StorageType = std::make_unsigned_t<UnderlyingType>; | 129 | using StorageType = std::make_unsigned_t<UnderlyingType>; |
| 129 | 130 | ||
| 131 | using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type; | ||
| 132 | |||
| 130 | public: | 133 | public: |
| 131 | /// Constants to allow limited introspection of fields if needed | 134 | /// Constants to allow limited introspection of fields if needed |
| 132 | static constexpr std::size_t position = Position; | 135 | static constexpr std::size_t position = Position; |
| @@ -172,7 +175,7 @@ public: | |||
| 172 | } | 175 | } |
| 173 | 176 | ||
| 174 | constexpr FORCE_INLINE void Assign(const T& value) { | 177 | constexpr FORCE_INLINE void Assign(const T& value) { |
| 175 | storage = (storage & ~mask) | FormatValue(value); | 178 | storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value); |
| 176 | } | 179 | } |
| 177 | 180 | ||
| 178 | constexpr T Value() const { | 181 | constexpr T Value() const { |
| @@ -184,7 +187,7 @@ public: | |||
| 184 | } | 187 | } |
| 185 | 188 | ||
| 186 | private: | 189 | private: |
| 187 | StorageType storage; | 190 | StorageTypeWithEndian storage; |
| 188 | 191 | ||
| 189 | static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); | 192 | static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); |
| 190 | 193 | ||
| @@ -195,3 +198,6 @@ private: | |||
| 195 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); | 198 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); |
| 196 | }; | 199 | }; |
| 197 | #pragma pack() | 200 | #pragma pack() |
| 201 | |||
| 202 | template <std::size_t Position, std::size_t Bits, typename T> | ||
| 203 | using BitFieldBE = BitField<Position, Bits, T, BETag>; | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 37f09ce5f..d0284bdf4 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | ||
| 2 | common/param_package.cpp | 3 | common/param_package.cpp |
| 3 | common/ring_buffer.cpp | 4 | common/ring_buffer.cpp |
| 4 | core/arm/arm_test_common.cpp | 5 | core/arm/arm_test_common.cpp |
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp new file mode 100644 index 000000000..8ca1889f9 --- /dev/null +++ b/src/tests/common/bit_field.cpp | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2019 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include <type_traits> | ||
| 8 | #include <catch2/catch.hpp> | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | |||
| 11 | TEST_CASE("BitField", "[common]") { | ||
| 12 | enum class TestEnum : u32 { | ||
| 13 | A = 0b10111101, | ||
| 14 | B = 0b10101110, | ||
| 15 | C = 0b00001111, | ||
| 16 | }; | ||
| 17 | |||
| 18 | union LEBitField { | ||
| 19 | u32_le raw; | ||
| 20 | BitField<0, 6, u32> a; | ||
| 21 | BitField<6, 4, s32> b; | ||
| 22 | BitField<10, 8, TestEnum> c; | ||
| 23 | BitField<18, 14, u32> d; | ||
| 24 | } le_bitfield; | ||
| 25 | |||
| 26 | union BEBitField { | ||
| 27 | u32_be raw; | ||
| 28 | BitFieldBE<0, 6, u32> a; | ||
| 29 | BitFieldBE<6, 4, s32> b; | ||
| 30 | BitFieldBE<10, 8, TestEnum> c; | ||
| 31 | BitFieldBE<18, 14, u32> d; | ||
| 32 | } be_bitfield; | ||
| 33 | |||
| 34 | static_assert(sizeof(LEBitField) == sizeof(u32)); | ||
| 35 | static_assert(sizeof(BEBitField) == sizeof(u32)); | ||
| 36 | static_assert(std::is_trivially_copyable_v<LEBitField>); | ||
| 37 | static_assert(std::is_trivially_copyable_v<BEBitField>); | ||
| 38 | |||
| 39 | std::array<u8, 4> raw{{ | ||
| 40 | 0b01101100, | ||
| 41 | 0b11110110, | ||
| 42 | 0b10111010, | ||
| 43 | 0b11101100, | ||
| 44 | }}; | ||
| 45 | |||
| 46 | std::memcpy(&le_bitfield, &raw, sizeof(raw)); | ||
| 47 | std::memcpy(&be_bitfield, &raw, sizeof(raw)); | ||
| 48 | |||
| 49 | // bit fields: 11101100101110'10111101'1001'101100 | ||
| 50 | REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100); | ||
| 51 | REQUIRE(le_bitfield.a == 0b101100); | ||
| 52 | REQUIRE(le_bitfield.b == -7); // 1001 as two's complement | ||
| 53 | REQUIRE(le_bitfield.c == TestEnum::A); | ||
| 54 | REQUIRE(le_bitfield.d == 0b11101100101110); | ||
| 55 | |||
| 56 | le_bitfield.a.Assign(0b000111); | ||
| 57 | le_bitfield.b.Assign(-1); | ||
| 58 | le_bitfield.c.Assign(TestEnum::C); | ||
| 59 | le_bitfield.d.Assign(0b01010101010101); | ||
| 60 | std::memcpy(&raw, &le_bitfield, sizeof(raw)); | ||
| 61 | // bit fields: 01010101010101'00001111'1111'000111 | ||
| 62 | REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111); | ||
| 63 | REQUIRE(raw == std::array<u8, 4>{{ | ||
| 64 | 0b11000111, | ||
| 65 | 0b00111111, | ||
| 66 | 0b01010100, | ||
| 67 | 0b01010101, | ||
| 68 | }}); | ||
| 69 | |||
| 70 | // bit fields: 01101100111101'10101110'1011'101100 | ||
| 71 | REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100); | ||
| 72 | REQUIRE(be_bitfield.a == 0b101100); | ||
| 73 | REQUIRE(be_bitfield.b == -5); // 1011 as two's complement | ||
| 74 | REQUIRE(be_bitfield.c == TestEnum::B); | ||
| 75 | REQUIRE(be_bitfield.d == 0b01101100111101); | ||
| 76 | |||
| 77 | be_bitfield.a.Assign(0b000111); | ||
| 78 | be_bitfield.b.Assign(-1); | ||
| 79 | be_bitfield.c.Assign(TestEnum::C); | ||
| 80 | be_bitfield.d.Assign(0b01010101010101); | ||
| 81 | std::memcpy(&raw, &be_bitfield, sizeof(raw)); | ||
| 82 | // bit fields: 01010101010101'00001111'1111'000111 | ||
| 83 | REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111); | ||
| 84 | REQUIRE(raw == std::array<u8, 4>{{ | ||
| 85 | 0b01010101, | ||
| 86 | 0b01010100, | ||
| 87 | 0b00111111, | ||
| 88 | 0b11000111, | ||
| 89 | }}); | ||
| 90 | } | ||