/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2010-2014 Danny Robson */ #ifndef __UTIL_ENDIAN_HPP #define __UTIL_ENDIAN_HPP #include "types/bits.hpp" #include #include #include namespace util { //------------------------------------------------------------------------- // Uses the TIFF header values. Just because. Don't rely on this. enum class endian : uint16_t { BIG = 0x4D4D, LITTLE = 0x4949, }; //------------------------------------------------------------------------- template constexpr T bswap (T) noexcept; // 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) \ template <> \ 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) template <> constexpr int8_t bswap ( int8_t v) noexcept { return v; } template <> constexpr uint8_t bswap (uint8_t v) noexcept { return v; } template <> constexpr uint16_t bswap (uint16_t v) noexcept { return __builtin_bswap16 (v); } template <> constexpr uint32_t bswap (uint32_t v) noexcept { return __builtin_bswap32 (v); } template <> 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. template <> 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 //------------------------------------------------------------------------- struct from_endian { explicit from_endian (endian _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::LITTLE) ? ltoh (uint) : btoh (uint); if (std::is_signed::value) return T (sint); else return T (uint); } endian src; }; } #endif