//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // Endianness helper file. Might be replaced with 's endian in the near // future. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef msgpack_endianness_d8ae54f45851ed13 #define msgpack_endianness_d8ae54f45851ed13 #include #include #include #include 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(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 constexpr void byte_swap(Iter begin, Iter end, OutIter 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 requires(From != endianness::other && To != endianness::other) constexpr auto maybe_swap(std::array val) noexcept { if constexpr (From == To) { return val; } else { decltype(val) dest; byte_swap(val.begin(), val.end(), dest.begin()); return dest; } } template requires(From != endianness::other && To != endianness::other) constexpr void maybe_swap_iter(Iter begin, Iter end, OutIter dest) noexcept { if constexpr (From == To) { std::copy(begin, end, dest); } else { byte_swap(begin, end, 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 constexpr auto raw_cast(T val) noexcept { union trick_to_array { T val; std::array 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 constexpr auto value_cast(Array&& data) noexcept { union trick_to_value { Array data; T val; }; trick_to_value u{std::forward(data)}; return u.val; } /** * Byte swap implementation for arbitrary endiannesses. */ template requires std::is_trivial_v> constexpr T byte_swap(T val) noexcept { using array_type = std::array; return std::bit_cast( maybe_swap(std::bit_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 constexpr T be_to_host(T val) noexcept { return detail::byte_swap(val); } template constexpr void be_to_host(Iter begin, Iter end, OutIter dest) noexcept { return detail::maybe_swap_iter(begin, end, dest); } template constexpr T host_to_be(T val) noexcept { return detail::byte_swap(val); } template constexpr T le_to_host(T val) noexcept { return detail::byte_swap(val); } template constexpr T host_to_le(T val) noexcept { return detail::byte_swap(val); } #endif // msgpack_endianness_d8ae54f45851ed13