parselink-old/source/include/parselink/msgpack/util/endianness.h

156 lines
4.3 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
// Endianness helper file. Might be replaced with <bit>'s endian in the near
// future.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef msgpack_endianness_d8ae54f45851ed13
#define msgpack_endianness_d8ae54f45851ed13
#include <array>
#include <bit>
#include <cstdint>
#include <type_traits>
enum class endianness {
big,
little,
other
};
namespace detail {
/**
* Structure which determines the target's endianness at compile time.
*/
struct host_endianness_check {
constexpr static inline std::uint32_t impl = 0x01020304;
constexpr static inline auto test = static_cast<const unsigned char&>(impl);
constexpr static inline auto value = []{
switch (test) {
case 4: return endianness::little;
case 1: return endianness::big;
default: return endianness::other;
}
}();
};
/**
* Helper function that swaps bytes of arbitrary lengths by copying input to
* output in reverse order.
*/
template <typename Iter>
constexpr void byte_swap(Iter begin, Iter end, Iter dest) {
while (begin != end) {
--end;
*dest = *end;
++dest;
}
}
/**
* Optionally swap the byte order of an array based on endianness. If the
* endianness is identical, this should optimize away. Otherwise, a call to
* byte_swap above should optimize to bswap or some equivalent assembly.
*/
template <endianness From, endianness To, std::size_t N>
requires (From != endianness::other && To != endianness::other)
constexpr auto maybe_swap(std::array<std::byte, N> val) noexcept {
if constexpr (From == To) {
return val;
} else {
decltype(val) dest;
byte_swap(val.begin(), val.end(), dest.begin());
return dest;
}
}
/**
* A helper function for converting a data type into an std::array for use
* with byte swap operations. May also come in use for reinterpreting data
* at a byte level.
*/
template <typename T>
constexpr auto raw_cast(T val) noexcept {
union trick_to_array {
T val;
std::array<std::byte, sizeof(T)> data;
};
trick_to_array u{val};
return u.data;
}
/**
* A helper function for converting a std::array into some arbitrary type.
* Beware, this must be used on trivial data types.
*/
template <typename T, typename Array>
constexpr auto value_cast(Array&& data) noexcept {
union trick_to_value {
Array data;
T val;
};
trick_to_value u{std::forward<Array>(data)};
return u.val;
}
/**
* Byte swap implementation for arbitrary endiannesses.
*/
template <endianness From, endianness To, typename T>
requires std::is_trivial_v<std::remove_reference_t<T>>
constexpr T swap(T val) noexcept {
return value_cast<T>(
maybe_swap<From, To>(raw_cast(val)));
}
} // namespace detail;
/**
* Exposes endianness information about a target.
*/
struct endian {
constexpr static inline auto big = endianness::big;
constexpr static inline auto little = endianness::little;
constexpr static inline auto network = endianness::big;
constexpr static inline auto host = detail::host_endianness_check::value;
};
/*----------------------------------------------------------------------------
* The functions below implement byte-swapping on a type.
*---------------------------------------------------------------------------*/
template <typename T>
constexpr T be_to_host(T val) noexcept {
return detail::swap<endian::big, endian::host>(val);
}
template <typename T>
constexpr T host_to_be(T val) noexcept {
return detail::swap<endian::host, endian::big>(val);
}
template <typename T>
constexpr T le_to_host(T val) noexcept {
return detail::swap<endian::little, endian::host>(val);
}
template <typename T>
constexpr T host_to_le(T val) noexcept {
return detail::swap<endian::host, endian::little>(val);
}
#endif // msgpack_endianness_d8ae54f45851ed13