Tidy endianness support (se_t) implementation

Move se_t and se_storage to util/endian.hpp
Use single template instead of two specializations.
Add minor optimization for MSVC.
Remove v128 dependency.
Try to enable intrinsics for unaligned data.
Fix minor bug in u16/u32/u64 specializations.
This commit is contained in:
Nekotekina 2019-09-26 21:57:03 +03:00
parent c7c12941bc
commit bd1a24b894
8 changed files with 308 additions and 344 deletions

View file

@ -1,6 +1,7 @@
#pragma once
#include "types.h"
#include "util/endian.hpp"
#include <cstring>
// 128-bit vector type and also se_storage<> storage type
@ -332,339 +333,13 @@ inline v128 operator~(const v128& other)
return v128::from64(~other._u64[0], ~other._u64[1]);
}
template <typename T, std::size_t Align, std::size_t Size>
struct se_storage
{
struct type
{
alignas(Align) std::byte data[Size];
};
// Unoptimized generic byteswap for unaligned data
static void reverse(u8* dst, const u8* src)
{
for (std::size_t i = 0; i < Size; i++)
{
dst[i] = src[Size - 1 - i];
}
}
static type to(const T& src)
{
type result;
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
return result;
}
static T from(const type& src)
{
T result;
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
return result;
}
};
template <typename T>
struct se_storage<T, 2, 2>
{
using type = u16;
static constexpr u16 swap(u16 src)
{
#if defined(__GNUG__)
return __builtin_bswap16(src);
#else
return _byteswap_ushort(src);
#endif
}
static inline u16 to(const T& src)
{
return swap(std::bit_cast<u16>(src));
}
static inline T from(u16 src)
{
return std::bit_cast<T, u16>(swap(src));
}
};
template <typename T>
struct se_storage<T, 4, 4>
{
using type = u32;
static constexpr u32 swap(u32 src)
{
#if defined(__GNUG__)
return __builtin_bswap32(src);
#else
return _byteswap_ulong(src);
#endif
}
static inline u32 to(const T& src)
{
return swap(std::bit_cast<u32>(src));
}
static inline T from(u32 src)
{
return std::bit_cast<T, u32>(swap(src));
}
};
template <typename T>
struct se_storage<T, 8, 8>
{
using type = u64;
static constexpr u64 swap(u64 src)
{
#if defined(__GNUG__)
return __builtin_bswap64(src);
#else
return _byteswap_uint64(src);
#endif
}
static inline u64 to(const T& src)
{
return swap(std::bit_cast<u64>(src));
}
static inline T from(u64 src)
{
return std::bit_cast<T, u64>(swap(src));
}
};
template <typename T>
struct se_storage<T, 16, 16>
{
using type = v128;
static inline v128 swap(const v128& src)
{
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
}
static inline v128 to(const T& src)
{
return swap(std::bit_cast<v128>(src));
}
static inline T from(const v128& src)
{
return std::bit_cast<T, v128>(swap(src));
}
};
// Switched endianness
template <typename T, std::size_t Align>
class se_t<T, true, Align>
{
using type = typename std::remove_cv<T>::type;
using stype = typename se_storage<type, Align>::type;
using storage = se_storage<type, Align>;
stype m_data;
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
public:
se_t() = default;
se_t(const se_t& right) = default;
se_t(type value)
: m_data(storage::to(value))
{
}
type value() const
{
return storage::from(m_data);
}
stype& raw()
{
return m_data;
}
se_t& operator=(const se_t&) = default;
se_t& operator=(type value)
{
return m_data = storage::to(value), *this;
}
using simple_type = simple_t<T>;
operator type() const
{
return storage::from(m_data);
}
};
// Native endianness
template <typename T, std::size_t Align>
class se_t<T, false, Align>
{
using type = typename std::remove_cv<T>::type;
using stype = typename se_storage<type, Align>::type;
using storage = se_storage<type, Align>;
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
stype m_data;
public:
se_t() = default;
se_t(type value)
: m_data(std::bit_cast<stype>(value))
{
}
type value() const
{
return std::bit_cast<type>(m_data);
}
stype& raw()
{
return m_data;
}
se_t& operator=(const se_t& value) = default;
se_t& operator=(type value)
{
m_data = std::bit_cast<stype>(value);
return *this;
}
using simple_type = simple_t<T>;
operator type() const
{
return value();
}
};
using stx::se_t;
using stx::se_storage;
// se_t<> with native endianness
template <typename T, std::size_t Align = alignof(T)>
using nse_t = se_t<T, false, Align>;
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator+=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value += right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator-=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value -= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator*=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value *= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator/=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value /= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator%=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value %= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator&=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value &= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator|=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value |= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator^=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value ^= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator<<=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value <<= right);
}
template <typename T, bool Se, std::size_t Align, typename T1>
inline se_t<T, Se, Align>& operator>>=(se_t<T, Se, Align>& left, const T1& right)
{
auto value = left.value();
return left = (value >>= right);
}
template <typename T, bool Se, std::size_t Align>
inline se_t<T, Se, Align> operator++(se_t<T, Se, Align>& left, int)
{
auto value = left.value();
auto result = value++;
left = value;
return result;
}
template <typename T, bool Se, std::size_t Align>
inline se_t<T, Se, Align> operator--(se_t<T, Se, Align>& left, int)
{
auto value = left.value();
auto result = value--;
left = value;
return result;
}
template <typename T, bool Se, std::size_t Align>
inline se_t<T, Se, Align>& operator++(se_t<T, Se, Align>& right)
{
auto value = right.value();
return right = ++value;
}
template <typename T, bool Se, std::size_t Align>
inline se_t<T, Se, Align>& operator--(se_t<T, Se, Align>& right)
{
auto value = right.value();
return right = --value;
}
#if IS_LE_MACHINE == 1
template <typename T, std::size_t Align = alignof(T)>
using be_t = se_t<T, true, Align>;