summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/bit_field.h12
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/bit_field.cpp90
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)
111template <std::size_t Position, std::size_t Bits, typename T> 112template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
112struct BitField { 113struct BitField {
113private: 114private:
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
130public: 133public:
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
186private: 189private:
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
202template <std::size_t Position, std::size_t Bits, typename T>
203using 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 @@
1add_executable(tests 1add_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
11TEST_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}