From 94bc48dd783989a178ae8cb5956f50d990878a92 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Fri, 21 Sep 2018 19:53:14 -0400 Subject: common/swap: add swap template for enum --- src/common/swap.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'src/common') diff --git a/src/common/swap.h b/src/common/swap.h index 32af0b6ac..466096f58 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -17,6 +17,8 @@ #pragma once +#include + #if defined(_MSC_VER) #include #elif defined(__linux__) @@ -605,6 +607,44 @@ struct swap_double_t { } }; +template +struct swap_enum_t { + static_assert(std::is_enum_v); + using base = std::underlying_type_t; + +public: + swap_enum_t() = default; + swap_enum_t(const T& v) : value(swap(v)) {} + + swap_enum_t& operator=(const T& v) { + value = swap(v); + return *this; + } + + operator T() const { + return swap(value); + } + + explicit operator base() const { + return static_cast(swap(value)); + } + +protected: + T value{}; + // clang-format off + using swap_t = std::conditional_t< + std::is_same_v, swap_16_t, std::conditional_t< + std::is_same_v, swap_16_t, std::conditional_t< + std::is_same_v, swap_32_t, std::conditional_t< + std::is_same_v, swap_32_t, std::conditional_t< + std::is_same_v, swap_64_t, std::conditional_t< + std::is_same_v, swap_64_t, void>>>>>>; + // clang-format on + static T swap(T x) { + return static_cast(swap_t::swap(static_cast(x))); + } +}; + #if COMMON_LITTLE_ENDIAN using u16_le = u16; using u32_le = u32; @@ -614,6 +654,9 @@ using s16_le = s16; using s32_le = s32; using s64_le = s64; +template +using enum_le = std::enable_if_t, T>; + using float_le = float; using double_le = double; @@ -626,6 +669,9 @@ using s32_be = swap_struct_t>; using u16_be = swap_struct_t>; using s16_be = swap_struct_t>; +template +using enum_be = swap_enum_t; + using float_be = swap_struct_t>; using double_be = swap_struct_t>; #else @@ -639,6 +685,9 @@ using s32_le = swap_struct_t>; using u16_le = swap_struct_t>; using s16_le = swap_struct_t>; +template +using enum_le = swap_enum_t; + using float_le = swap_struct_t>; using double_le = swap_struct_t>; @@ -650,6 +699,9 @@ using s16_be = s16; using s32_be = s32; using s64_be = s64; +template +using enum_be = std::enable_if_t, T>; + using float_be = float; using double_be = double; -- cgit v1.2.3 From 6734c6497653b93d8313ecc30e4eae5348dca2c0 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Fri, 25 Jan 2019 12:09:12 -0500 Subject: common/swap: use template and tag for LE/BE specification The tag can be useful for other type-generic templates like BitFields to forward the endianness specification --- src/common/swap.h | 130 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 39 deletions(-) (limited to 'src/common') diff --git a/src/common/swap.h b/src/common/swap.h index 466096f58..97aacb4dc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -645,64 +645,116 @@ protected: } }; -#if COMMON_LITTLE_ENDIAN -using u16_le = u16; -using u32_le = u32; -using u64_le = u64; +struct SwapTag {}; // Use the different endianness from the system +struct KeepTag {}; // Use the same endianness as the system + +template +struct AddEndian; -using s16_le = s16; -using s32_le = s32; -using s64_le = s64; +// KeepTag specializations template -using enum_le = std::enable_if_t, T>; +struct AddEndian { + using type = T; +}; + +// SwapTag specializations -using float_le = float; -using double_le = double; +template <> +struct AddEndian { + using type = u8; +}; -using u64_be = swap_struct_t>; -using s64_be = swap_struct_t>; +template <> +struct AddEndian { + using type = swap_struct_t>; +}; -using u32_be = swap_struct_t>; -using s32_be = swap_struct_t>; +template <> +struct AddEndian { + using type = swap_struct_t>; +}; -using u16_be = swap_struct_t>; -using s16_be = swap_struct_t>; +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = s8; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; template -using enum_be = swap_enum_t; +struct AddEndian { + static_assert(std::is_enum_v); + using type = swap_enum_t; +}; + +// Alias LETag/BETag as KeepTag/SwapTag depending on the system +#if COMMON_LITTLE_ENDIAN + +using LETag = KeepTag; +using BETag = SwapTag; -using float_be = swap_struct_t>; -using double_be = swap_struct_t>; #else -using u64_le = swap_struct_t>; -using s64_le = swap_struct_t>; +using BETag = KeepTag; +using LETag = SwapTag; -using u32_le = swap_struct_t>; -using s32_le = swap_struct_t>; +#endif -using u16_le = swap_struct_t>; -using s16_le = swap_struct_t>; +// Aliases for LE types +using u16_le = AddEndian::type; +using u32_le = AddEndian::type; +using u64_le = AddEndian::type; + +using s16_le = AddEndian::type; +using s32_le = AddEndian::type; +using s64_le = AddEndian::type; template -using enum_le = swap_enum_t; +using enum_le = std::enable_if_t, typename AddEndian::type>; -using float_le = swap_struct_t>; -using double_le = swap_struct_t>; +using float_le = AddEndian::type; +using double_le = AddEndian::type; -using u16_be = u16; -using u32_be = u32; -using u64_be = u64; +// Aliases for BE types +using u16_be = AddEndian::type; +using u32_be = AddEndian::type; +using u64_be = AddEndian::type; -using s16_be = s16; -using s32_be = s32; -using s64_be = s64; +using s16_be = AddEndian::type; +using s32_be = AddEndian::type; +using s64_be = AddEndian::type; template -using enum_be = std::enable_if_t, T>; - -using float_be = float; -using double_be = double; +using enum_be = std::enable_if_t, typename AddEndian::type>; -#endif +using float_be = AddEndian::type; +using double_be = AddEndian::type; -- cgit v1.2.3 From 71530781f33423fc48c9bf43702f1291a38a259d Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Fri, 25 Jan 2019 12:16:00 -0500 Subject: common/swap: remove default value for swap type internal storage This is compromise for swap type being used in union. A union has deleted default constructor if it has at least one variant member with non-trivial default constructor, and no variant member of T has a default member initializer. In the use case of Bitfield, all variant members will be the swap type on endianness mismatch, which would all have non-trivial default constructor if default value is specified, and non of them can have member initializer --- src/common/swap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/swap.h b/src/common/swap.h index 97aacb4dc..4b82865fe 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -172,7 +172,7 @@ struct swap_struct_t { using swapped_t = swap_struct_t; protected: - T value = T(); + T value; static T swap(T v) { return F::swap(v); -- cgit v1.2.3 From 6b81ceb060a0e985380bc33d2f51dcc76aad3eb3 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Fri, 25 Jan 2019 12:16:23 -0500 Subject: common/bitfield: make it endianness-aware --- src/common/bit_field.h | 12 ++++-- src/tests/CMakeLists.txt | 1 + src/tests/common/bit_field.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 src/tests/common/bit_field.cpp (limited to 'src/common') 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 @@ #include #include #include "common/common_funcs.h" +#include "common/swap.h" /* * Abstract bitfield class @@ -108,7 +109,7 @@ * symptoms. */ #pragma pack(1) -template +template struct BitField { private: // We hide the copy assigment operator here, because the default copy @@ -127,6 +128,8 @@ private: // We store the value as the unsigned type to avoid undefined behaviour on value shifting using StorageType = std::make_unsigned_t; + using StorageTypeWithEndian = typename AddEndian::type; + public: /// Constants to allow limited introspection of fields if needed static constexpr std::size_t position = Position; @@ -172,7 +175,7 @@ public: } constexpr FORCE_INLINE void Assign(const T& value) { - storage = (storage & ~mask) | FormatValue(value); + storage = (static_cast(storage) & ~mask) | FormatValue(value); } constexpr T Value() const { @@ -184,7 +187,7 @@ public: } private: - StorageType storage; + StorageTypeWithEndian storage; static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); @@ -195,3 +198,6 @@ private: static_assert(std::is_trivially_copyable_v, "T must be trivially copyable in a BitField"); }; #pragma pack() + +template +using BitFieldBE = BitField; 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 @@ add_executable(tests + common/bit_field.cpp common/param_package.cpp common/ring_buffer.cpp 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 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/bit_field.h" + +TEST_CASE("BitField", "[common]") { + enum class TestEnum : u32 { + A = 0b10111101, + B = 0b10101110, + C = 0b00001111, + }; + + union LEBitField { + u32_le raw; + BitField<0, 6, u32> a; + BitField<6, 4, s32> b; + BitField<10, 8, TestEnum> c; + BitField<18, 14, u32> d; + } le_bitfield; + + union BEBitField { + u32_be raw; + BitFieldBE<0, 6, u32> a; + BitFieldBE<6, 4, s32> b; + BitFieldBE<10, 8, TestEnum> c; + BitFieldBE<18, 14, u32> d; + } be_bitfield; + + static_assert(sizeof(LEBitField) == sizeof(u32)); + static_assert(sizeof(BEBitField) == sizeof(u32)); + static_assert(std::is_trivially_copyable_v); + static_assert(std::is_trivially_copyable_v); + + std::array raw{{ + 0b01101100, + 0b11110110, + 0b10111010, + 0b11101100, + }}; + + std::memcpy(&le_bitfield, &raw, sizeof(raw)); + std::memcpy(&be_bitfield, &raw, sizeof(raw)); + + // bit fields: 11101100101110'10111101'1001'101100 + REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100); + REQUIRE(le_bitfield.a == 0b101100); + REQUIRE(le_bitfield.b == -7); // 1001 as two's complement + REQUIRE(le_bitfield.c == TestEnum::A); + REQUIRE(le_bitfield.d == 0b11101100101110); + + le_bitfield.a.Assign(0b000111); + le_bitfield.b.Assign(-1); + le_bitfield.c.Assign(TestEnum::C); + le_bitfield.d.Assign(0b01010101010101); + std::memcpy(&raw, &le_bitfield, sizeof(raw)); + // bit fields: 01010101010101'00001111'1111'000111 + REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111); + REQUIRE(raw == std::array{{ + 0b11000111, + 0b00111111, + 0b01010100, + 0b01010101, + }}); + + // bit fields: 01101100111101'10101110'1011'101100 + REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100); + REQUIRE(be_bitfield.a == 0b101100); + REQUIRE(be_bitfield.b == -5); // 1011 as two's complement + REQUIRE(be_bitfield.c == TestEnum::B); + REQUIRE(be_bitfield.d == 0b01101100111101); + + be_bitfield.a.Assign(0b000111); + be_bitfield.b.Assign(-1); + be_bitfield.c.Assign(TestEnum::C); + be_bitfield.d.Assign(0b01010101010101); + std::memcpy(&raw, &be_bitfield, sizeof(raw)); + // bit fields: 01010101010101'00001111'1111'000111 + REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111); + REQUIRE(raw == std::array{{ + 0b01010101, + 0b01010100, + 0b00111111, + 0b11000111, + }}); +} -- cgit v1.2.3 From efd83570bdb70597b3e06eeb3bced5486ac85eab Mon Sep 17 00:00:00 2001 From: fearlessTobi Date: Wed, 5 Sep 2018 02:24:44 +0200 Subject: Make bitfield assignment operator public This change needs to be made to get the code compiling again. It was suggested after a conversation with Lioncash. The conversation can be seen here: https://user-images.githubusercontent.com/20753089/45064197-b6107800-b0b2-11e8-9db8-f696299fb86a.PNG --- src/common/bit_field.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/common') diff --git a/src/common/bit_field.h b/src/common/bit_field.h index bd9e21e1e..639efe22d 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -112,12 +112,6 @@ template struct BitField { private: - // We hide the copy assigment operator here, because the default copy - // assignment would copy the full storage value, rather than just the bits - // relevant to this particular bit field. - // We don't delete it because we want BitField to be trivially copyable. - constexpr BitField& operator=(const BitField&) = default; - // UnderlyingType is T for non-enum types and the underlying type of T if // T is an enumeration. Note that T is wrapped within an enable_if in the // former case to workaround compile errors which arise when using @@ -131,6 +125,8 @@ private: using StorageTypeWithEndian = typename AddEndian::type; public: + BitField& operator=(const BitField&) = default; + /// Constants to allow limited introspection of fields if needed static constexpr std::size_t position = Position; static constexpr std::size_t bits = Bits; -- cgit v1.2.3