/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2010-2014 Danny Robson */ #ifndef __UTIL_ENDIAN_HPP #define __UTIL_ENDIAN_HPP #include "std.hpp" #include "types/bits.hpp" #include #include #include #include namespace cruft { //------------------------------------------------------------------------- // CXX: Update using "if constexpr" when available. It doesn't seem to be // worth the dicking about to use enable_if_t to differentiate the // signed/unsigned cases. #define SIGNED_BSWAP(S) \ constexpr \ int##S##_t \ bswap (int##S##_t v) noexcept { \ const union { \ int##S##_t s; \ uint##S##_t u; \ } vu = { v }; \ \ return __builtin_bswap##S (vu.u); \ } SIGNED_BSWAP(16) SIGNED_BSWAP(32) SIGNED_BSWAP(64) constexpr int8_t bswap ( int8_t v) noexcept { return v; } constexpr uint8_t bswap (uint8_t v) noexcept { return v; } constexpr uint16_t bswap (uint16_t v) noexcept { return __builtin_bswap16 (v); } constexpr uint32_t bswap (uint32_t v) noexcept { return __builtin_bswap32 (v); } constexpr uint64_t bswap (uint64_t v) noexcept { return __builtin_bswap64 (v); } // use a type-punning union to punt the byte swapping operation to the // unsigned integer code. // // this is debatably safe under production compilers like gcc + clang // despite what the standard may say about unions. constexpr float bswap (float v) noexcept { union { float f; uint32_t u; } temp { .f = v }; temp.u = bswap (temp.u); return temp.f; } //------------------------------------------------------------------------- template constexpr T identity (T &&v) { return v; } template < typename T, typename = std::enable_if_t< std::is_integral_v > > T readle (const void *_data) { auto data = reinterpret_cast (_data); T value = 0; for (size_t i = 0; i < sizeof (value); ++i) value |= T{data[i]} << (i * 8); return value; } /// read bytes from a supplied data pointer and return an integer using /// host endian layout. template < typename T, typename = std::enable_if_t< std::is_integral_v > > T readhe (const std::uint8_t *data) { std::aligned_union_t buffer; memcpy (reinterpret_cast (&buffer), data, sizeof (T)); return *reinterpret_cast (&buffer); } //------------------------------------------------------------------------- #if defined(WORDS_BIGENDIAN) template constexpr T hton (T v) { return v; } template constexpr T ntoh (T v) { return v; } template constexpr T htob (T v) { return v; } template constexpr T htol (T v) { return bswap (v); } template constexpr T btoh (T v) { return v; } template constexpr T ltoh (T v) { return bswap (v); } #else template constexpr T hton (T v) { return bswap (v); } template constexpr T ntoh (T v) { return bswap (v); } template constexpr T htob (T v) { return bswap (v); } template constexpr T htol (T v) { return v; } template constexpr T btoh (T v) { return bswap (v); } template constexpr T ltoh (T v) { return v; } #endif template T btol (T t) { return bswap (t); } template T ltob (T t) { return bswap (t); } namespace endian { //--------------------------------------------------------------------- // Uses the TIFF header values. Just because. Don't rely on this. enum class scheme : uint16_t { BIG = 0x4D4D, LITTLE = 0x4949, big = BIG, little = LITTLE, #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ native = little, #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ native = big #else #error Unhandled endianness #endif }; template struct converter; template <> struct converter { template static T convert (T t) { return btol (t); } }; template <> struct converter { template static T convert (T t) { return ltob (t); } }; template T convert (T t) { return converter::convert (t); } } //------------------------------------------------------------------------- struct from_endian { explicit from_endian (endian::scheme _endian): src (_endian) { ; } template T operator() (const T v) const { static_assert (std::is_integral::value || std::is_enum::value, "endian conversion is only defined for integrals currently"); union { typename sized_type::sint sint; typename sized_type::uint uint; }; if (std::is_signed::value) sint = v; else uint = v; uint = (src == endian::scheme::LITTLE) ? ltoh (uint) : btoh (uint); if (std::is_signed::value) return T (sint); else return T (uint); } endian::scheme src; }; namespace endian { template struct value { operator ValueT () const { return convert (raw); } decltype(auto) operator+ () const { return +this->operator ValueT (); } ValueT operator<< (size_t s) const { return convert (raw) << s; } auto& operator= (ValueT const &val) { raw = convert (val); return *this; } ValueT raw; }; template using little = value; template using big = value; template std::ostream& operator<< (std::ostream &os, value const &val) { return os << +ValueT(val); } } using bu16 = endian::big; using bu32 = endian::big; } #endif