/* * Copyright (c) 2017, Matias Fontanini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef TINS_ENDIANNESS_H #define TINS_ENDIANNESS_H #include #include #if defined(__APPLE__) #include #define TINS_IS_LITTLE_ENDIAN (BYTE_ORDER == LITTLE_ENDIAN) #define TINS_IS_BIG_ENDIAN (BYTE_ORDER == BIG_ENDIAN) #elif defined(BSD) #include #define TINS_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) #define TINS_IS_BIG_ENDIAN (_BYTE_ORDER == _BIG_ENDIAN) #elif defined(_WIN32) #include // Assume windows == little endian. fixme later #define TINS_IS_LITTLE_ENDIAN 1 #define TINS_IS_BIG_ENDIAN 0 #else #include #define TINS_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) #define TINS_IS_BIG_ENDIAN (__BYTE_ORDER == __BIG_ENDIAN) #endif // Define macros to swap bytes using compiler intrinsics when possible #if defined(_MSC_VER) #define TINS_BYTE_SWAP_16(data) _byteswap_ushort(data) #define TINS_BYTE_SWAP_32(data) _byteswap_ulong(data) #define TINS_BYTE_SWAP_64(data) _byteswap_uint64(data) #elif defined(TINS_HAVE_GCC_BUILTIN_SWAP) #define TINS_BYTE_SWAP_16(data) __builtin_bswap16(data) #define TINS_BYTE_SWAP_32(data) __builtin_bswap32(data) #define TINS_BYTE_SWAP_64(data) __builtin_bswap64(data) #else #define TINS_NO_BYTE_SWAP_INTRINSICS #endif namespace Tins { namespace Endian { /** * \brief "Changes" a 8-bit integral value's endianess. This is an * identity function. * * \param data The data to convert. */ inline uint8_t do_change_endian(uint8_t data) { return data; } /** * \brief Changes a 16-bit integral value's endianess. * * \param data The data to convert. */ inline uint16_t do_change_endian(uint16_t data) { #ifdef TINS_NO_BYTE_SWAP_INTRINSICS return ((data & 0xff00) >> 8) | ((data & 0x00ff) << 8); #else return TINS_BYTE_SWAP_16(data); #endif } /** * \brief Changes a 32-bit integral value's endianess. * * \param data The data to convert. */ inline uint32_t do_change_endian(uint32_t data) { #ifdef TINS_NO_BYTE_SWAP_INTRINSICS return (((data & 0xff000000) >> 24) | ((data & 0x00ff0000) >> 8) | ((data & 0x0000ff00) << 8) | ((data & 0x000000ff) << 24)); #else return TINS_BYTE_SWAP_32(data); #endif } /** * \brief Changes a 64-bit integral value's endianess. * * \param data The data to convert. */ inline uint64_t do_change_endian(uint64_t data) { #ifdef TINS_NO_BYTE_SWAP_INTRINSICS return (((uint64_t)(do_change_endian((uint32_t)(data & 0xffffffff))) << 32) | (do_change_endian(((uint32_t)(data >> 32))))); #else return TINS_BYTE_SWAP_64(data); #endif } /** * \cond */ // Helpers to convert template struct conversion_dispatch_helper { static T dispatch(T data) { return do_change_endian(data); } }; template struct conversion_dispatcher; template<> struct conversion_dispatcher : public conversion_dispatch_helper { }; template<> struct conversion_dispatcher : public conversion_dispatch_helper { }; template<> struct conversion_dispatcher : public conversion_dispatch_helper { }; template<> struct conversion_dispatcher : public conversion_dispatch_helper { }; /** * \endcond */ /** * \brief Changes an integral value's endianess. * * This dispatchs to the corresponding function. * * \param data The data to convert. */ template inline T change_endian(T data) { return conversion_dispatcher::dispatch(data); } #if TINS_IS_LITTLE_ENDIAN /** * \brief Convert any integral type to big endian. * * \param data The data to convert. */ template inline T host_to_be(T data) { return change_endian(data); } /** * \brief Convert any integral type to little endian. * * On little endian platforms, the parameter is simply returned. * * \param data The data to convert. */ template inline T host_to_le(T data) { return data; } /** * \brief Convert any big endian value to the host's endianess. * * \param data The data to convert. */ template inline T be_to_host(T data) { return change_endian(data); } /** * \brief Convert any little endian value to the host's endianess. * * \param data The data to convert. */ template inline T le_to_host(T data) { return data; } #elif TINS_IS_BIG_ENDIAN /** * \brief Convert any integral type to big endian. * * \param data The data to convert. */ template inline T host_to_be(T data) { return data; } /** * \brief Convert any integral type to little endian. * * On little endian platforms, the parameter is simply returned. * * \param data The data to convert. */ template inline T host_to_le(T data) { return change_endian(data); } /** * \brief Convert any big endian value to the host's endianess. * * \param data The data to convert. */ template inline T be_to_host(T data) { return data; } /** * \brief Convert any little endian value to the host's endianess. * * \param data The data to convert. */ template inline T le_to_host(T data) { return change_endian(data); } #endif } // Endian } // Tins #endif // TINS_ENDIANNESS_H