diff --git a/include/boost/endian/buffers.hpp b/include/boost/endian/buffers.hpp index 890919b..d8be041 100644 --- a/include/boost/endian/buffers.hpp +++ b/include/boost/endian/buffers.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -283,36 +284,14 @@ namespace endian inline T load_big_endian(const void* bytes) BOOST_NOEXCEPT { -# if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) - // All major x86 compilers elide this test and optimize out memcpy - // (the x86 architecture allows unaligned loads, but -fsanitize=undefined does not) - if (sizeof(T) == n_bytes) - { - T t; - std::memcpy( &t, bytes, sizeof(T) ); - return endian::big_to_native(t); - } -# endif - return unrolled_byte_loops::load_big - (static_cast(bytes) + n_bytes); + return endian::endian_load( static_cast( bytes ) ); } template inline T load_little_endian(const void* bytes) BOOST_NOEXCEPT { -# if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) - // All major x86 compilers elide this test and optimize out memcpy - // (the x86 architecture allows unaligned loads, but -fsanitize=undefined does not) - if (sizeof(T) == n_bytes) - { - T t; - std::memcpy( &t, bytes, sizeof(T) ); - return t; // or endian::little_to_native(t) if we ever extend the #ifdef to non-x86 - } -# endif - return unrolled_byte_loops::load_little - (static_cast(bytes)); + return endian::endian_load( static_cast( bytes ) ); } template diff --git a/include/boost/endian/detail/endian_load.hpp b/include/boost/endian/detail/endian_load.hpp new file mode 100644 index 0000000..323ce5e --- /dev/null +++ b/include/boost/endian/detail/endian_load.hpp @@ -0,0 +1,265 @@ +#ifndef BOOST_ENDIAN_DETAIL_ENDIAN_LOAD_HPP_INCLUDED +#define BOOST_ENDIAN_DETAIL_ENDIAN_LOAD_HPP_INCLUDED + +// Copyright 2019 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +namespace boost +{ +namespace endian +{ + +namespace detail +{ + +template struct endian_load_impl +{ +}; + +} // namespace detail + +template +inline T endian_load( unsigned char const * p ) BOOST_NOEXCEPT +{ + return detail::endian_load_impl()( p ); +} + +namespace detail +{ + +template struct integral_by_size +{ +}; + +template<> struct integral_by_size<1> +{ + typedef uint8_t type; +}; + +template<> struct integral_by_size<2> +{ + typedef uint16_t type; +}; + +template<> struct integral_by_size<4> +{ + typedef uint32_t type; +}; + +template<> struct integral_by_size<8> +{ + typedef uint64_t type; +}; + +// same endianness, same size + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + T t; + std::memcpy( &t, p, N ); + return t; + } +}; + +// same size, reverse endianness + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + typename integral_by_size::type tmp; + std::memcpy( &tmp, p, N ); + + endian_reverse_inplace( tmp ); + + T t; + std::memcpy( &t, &tmp, N ); + return t; + } +}; + +// expanding load 3 -> 4 + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 4 ]; + + tmp[0] = p[0]; + tmp[1] = p[1]; + tmp[2] = p[2]; + tmp[3] = boost::is_signed::value && ( p[2] & 0x80 )? 0xFF: 0x00; + + return endian::endian_load( tmp ); + } +}; + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 4 ]; + + tmp[0] = boost::is_signed::value && ( p[0] & 0x80 )? 0xFF: 0x00; + tmp[1] = p[0]; + tmp[2] = p[1]; + tmp[3] = p[2]; + + return endian::endian_load( tmp ); + } +}; + +// expanding load 5 -> 8 + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[4] & 0x80 )? 0xFF: 0x00; + + tmp[0] = p[0]; + tmp[1] = p[1]; + tmp[2] = p[2]; + tmp[3] = p[3]; + tmp[4] = p[4]; + + tmp[5] = fill; + tmp[6] = fill; + tmp[7] = fill; + + return endian::endian_load( tmp ); + } +}; + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[0] & 0x80 )? 0xFF: 0x00; + + tmp[0] = fill; + tmp[1] = fill; + tmp[2] = fill; + + tmp[3] = p[0]; + tmp[4] = p[1]; + tmp[5] = p[2]; + tmp[6] = p[3]; + tmp[7] = p[4]; + + return endian::endian_load( tmp ); + } +}; + +// expanding load 6 -> 8 + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[5] & 0x80 )? 0xFF: 0x00; + + tmp[0] = p[0]; + tmp[1] = p[1]; + tmp[2] = p[2]; + tmp[3] = p[3]; + tmp[4] = p[4]; + tmp[5] = p[5]; + + tmp[6] = fill; + tmp[7] = fill; + + return endian::endian_load( tmp ); + } +}; + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[0] & 0x80 )? 0xFF: 0x00; + + tmp[0] = fill; + tmp[1] = fill; + + tmp[2] = p[0]; + tmp[3] = p[1]; + tmp[4] = p[2]; + tmp[5] = p[3]; + tmp[6] = p[4]; + tmp[7] = p[5]; + + return endian::endian_load( tmp ); + } +}; + +// expanding load 7 -> 8 + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[6] & 0x80 )? 0xFF: 0x00; + + tmp[0] = p[0]; + tmp[1] = p[1]; + tmp[2] = p[2]; + tmp[3] = p[3]; + tmp[4] = p[4]; + tmp[5] = p[5]; + tmp[6] = p[6]; + + tmp[7] = fill; + + return endian::endian_load( tmp ); + } +}; + +template struct endian_load_impl +{ + inline T operator()( unsigned char const * p ) const BOOST_NOEXCEPT + { + unsigned char tmp[ 8 ]; + + unsigned char fill = boost::is_signed::value && ( p[0] & 0x80 )? 0xFF: 0x00; + + tmp[0] = fill; + + tmp[1] = p[0]; + tmp[2] = p[1]; + tmp[3] = p[2]; + tmp[4] = p[3]; + tmp[5] = p[4]; + tmp[6] = p[5]; + tmp[7] = p[6]; + + return endian::endian_load( tmp ); + } +}; + +} // namespace detail + +} // namespace endian +} // namespace boost + +#endif // BOOST_ENDIAN_DETAIL_ENDIAN_LOAD_HPP_INCLUDED