156 lines
4.3 KiB
C++
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
|