summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/bit_field.h14
-rw-r--r--src/common/swap.h174
2 files changed, 150 insertions, 38 deletions
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7433c39ba..8e35c463f 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -34,6 +34,7 @@
#include <limits>
#include <type_traits>
#include "common/common_funcs.h"
+#include "common/swap.h"
/*
* Abstract bitfield class
@@ -108,7 +109,7 @@
* symptoms.
*/
#pragma pack(1)
-template <std::size_t Position, std::size_t Bits, typename T>
+template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
struct BitField {
private:
// UnderlyingType is T for non-enum types and the underlying type of T if
@@ -121,7 +122,11 @@ private:
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
using StorageType = std::make_unsigned_t<UnderlyingType>;
+ using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::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;
@@ -170,7 +175,7 @@ public:
}
constexpr FORCE_INLINE void Assign(const T& value) {
- storage = (storage & ~mask) | FormatValue(value);
+ storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
}
constexpr T Value() const {
@@ -182,7 +187,7 @@ public:
}
private:
- StorageType storage;
+ StorageTypeWithEndian storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
@@ -193,3 +198,6 @@ private:
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
};
#pragma pack()
+
+template <std::size_t Position, std::size_t Bits, typename T>
+using BitFieldBE = BitField<Position, Bits, T, BETag>;
diff --git a/src/common/swap.h b/src/common/swap.h
index 0e219747f..b3eab1324 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,6 +17,8 @@
#pragma once
+#include <type_traits>
+
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
@@ -170,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);
@@ -605,52 +607,154 @@ struct swap_double_t {
}
};
-#if COMMON_LITTLE_ENDIAN
-using u16_le = u16;
-using u32_le = u32;
-using u64_le = u64;
+template <typename T>
+struct swap_enum_t {
+ static_assert(std::is_enum_v<T>);
+ using base = std::underlying_type_t<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<base>(swap(value));
+ }
-using s16_le = s16;
-using s32_le = s32;
-using s64_le = s64;
+protected:
+ T value{};
+ // clang-format off
+ using swap_t = std::conditional_t<
+ std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
+ std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
+ std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
+ std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
+ std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
+ std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
+ // clang-format on
+ static T swap(T x) {
+ return static_cast<T>(swap_t::swap(static_cast<base>(x)));
+ }
+};
-using float_le = float;
-using double_le = double;
+struct SwapTag {}; // Use the different endianness from the system
+struct KeepTag {}; // Use the same endianness as the system
-using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
-using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
+template <typename T, typename Tag>
+struct AddEndian;
-using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
-using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
+// KeepTag specializations
-using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
-using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
+template <typename T>
+struct AddEndian<T, KeepTag> {
+ using type = T;
+};
-using float_be = swap_struct_t<float, swap_float_t<float>>;
-using double_be = swap_struct_t<double, swap_double_t<double>>;
-#else
+// SwapTag specializations
+
+template <>
+struct AddEndian<u8, SwapTag> {
+ using type = u8;
+};
+
+template <>
+struct AddEndian<u16, SwapTag> {
+ using type = swap_struct_t<u16, swap_16_t<u16>>;
+};
+
+template <>
+struct AddEndian<u32, SwapTag> {
+ using type = swap_struct_t<u32, swap_32_t<u32>>;
+};
-using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
-using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
+template <>
+struct AddEndian<u64, SwapTag> {
+ using type = swap_struct_t<u64, swap_64_t<u64>>;
+};
+
+template <>
+struct AddEndian<s8, SwapTag> {
+ using type = s8;
+};
-using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
-using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
+template <>
+struct AddEndian<s16, SwapTag> {
+ using type = swap_struct_t<s16, swap_16_t<s16>>;
+};
-using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
-using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
+template <>
+struct AddEndian<s32, SwapTag> {
+ using type = swap_struct_t<s32, swap_32_t<s32>>;
+};
+
+template <>
+struct AddEndian<s64, SwapTag> {
+ using type = swap_struct_t<s64, swap_64_t<s64>>;
+};
+
+template <>
+struct AddEndian<float, SwapTag> {
+ using type = swap_struct_t<float, swap_float_t<float>>;
+};
+
+template <>
+struct AddEndian<double, SwapTag> {
+ using type = swap_struct_t<double, swap_double_t<double>>;
+};
+
+template <typename T>
+struct AddEndian<T, SwapTag> {
+ static_assert(std::is_enum_v<T>);
+ using type = swap_enum_t<T>;
+};
-using float_le = swap_struct_t<float, swap_float_t<float>>;
-using double_le = swap_struct_t<double, swap_double_t<double>>;
+// Alias LETag/BETag as KeepTag/SwapTag depending on the system
+#if COMMON_LITTLE_ENDIAN
-using u16_be = u16;
-using u32_be = u32;
-using u64_be = u64;
+using LETag = KeepTag;
+using BETag = SwapTag;
-using s16_be = s16;
-using s32_be = s32;
-using s64_be = s64;
+#else
-using float_be = float;
-using double_be = double;
+using BETag = KeepTag;
+using LETag = SwapTag;
#endif
+
+// Aliases for LE types
+using u16_le = AddEndian<u16, LETag>::type;
+using u32_le = AddEndian<u32, LETag>::type;
+using u64_le = AddEndian<u64, LETag>::type;
+
+using s16_le = AddEndian<s16, LETag>::type;
+using s32_le = AddEndian<s32, LETag>::type;
+using s64_le = AddEndian<s64, LETag>::type;
+
+template <typename T>
+using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
+
+using float_le = AddEndian<float, LETag>::type;
+using double_le = AddEndian<double, LETag>::type;
+
+// Aliases for BE types
+using u16_be = AddEndian<u16, BETag>::type;
+using u32_be = AddEndian<u32, BETag>::type;
+using u64_be = AddEndian<u64, BETag>::type;
+
+using s16_be = AddEndian<s16, BETag>::type;
+using s32_be = AddEndian<s32, BETag>::type;
+using s64_be = AddEndian<s64, BETag>::type;
+
+template <typename T>
+using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
+
+using float_be = AddEndian<float, BETag>::type;
+using double_be = AddEndian<double, BETag>::type;