/* * Author: Sven Gothel * Copyright (c) 2020 Gothel Software e.K. * Copyright (c) 2020 ZAFENA AB * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef JAU_BYTE_UTIL_HPP_ #define JAU_BYTE_UTIL_HPP_ #include #include #include #include #include #include #include #include #include #include namespace jau { /** @defgroup ByteUtils Byte Utilities * Byte utility functions and types for endian- and bit conversions, * inclusive alignment handling and general get & put functionality. * * This category is also supporting \ref Integer. * * All \ref endian API entries are of `constexpr` and hence evaluated at compile time.
* Therefore, if-branches and expressions are also of `constexpr` and optimized 'away' at compile time.
* This includes the `cpu_to_(..)` and `_to_cpu(..)` etc utility functions. * * See \ref endian enum class regarding endian `constexpr` compile time determination. * * Aligned memory transfer from and to potentially unaligned memory * is performed via put_uint16(), get_uint16() with all its explicit stdint types, * as well as the generic template functions put_value() and get_value(). * The implementation uses \ref packed_t to resolve a potential memory alignment issue *free of costs*, * see \ref packed_t_alignment_cast. * * @{ */ #if defined __has_builtin #if __has_builtin(__builtin_bswap16) #define __has_builtin_bswap16 1 #endif #elif defined(__GNUC__) && __GNUC_PREREQ (4, 8) #define __has_builtin_bswap16 1 #endif #if defined __has_builtin #if __has_builtin(__builtin_bswap32) #define __has_builtin_bswap32 1 #endif #elif defined(__GNUC__) && __GNUC_PREREQ (4, 8) #define __has_builtin_bswap32 1 #endif #if defined __has_builtin #if __has_builtin(__builtin_bswap64) #define __has_builtin_bswap64 1 #endif #elif defined(__GNUC__) && __GNUC_PREREQ (4, 8) #define __has_builtin_bswap64 1 #endif // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline. constexpr uint16_t bswap(uint16_t const source) noexcept { #if defined __has_builtin_bswap16 return __builtin_bswap16(source); #else return (uint16_t) ( ( ( (source) >> 8 ) & 0xff ) | ( ( (source) & 0xff) << 8 ) ); #endif } constexpr uint32_t bswap(uint32_t const source) noexcept { #if defined __has_builtin_bswap32 return __builtin_bswap32(source); #else return ( ( source & 0xff000000U ) >> 24 ) | ( ( source & 0x00ff0000U ) >> 8 ) | ( ( source & 0x0000ff00U ) << 8 ) | ( ( source & 0x000000ffU ) << 24 ); #endif } constexpr uint64_t bswap(uint64_t const & source) noexcept { #if defined __has_builtin_bswap64 return __builtin_bswap64(source); #else return ( ( source & 0xff00000000000000ULL ) >> 56 ) | ( ( source & 0x00ff000000000000ULL ) >> 40 ) | ( ( source & 0x0000ff0000000000ULL ) >> 24 ) | ( ( source & 0x000000ff00000000ULL ) >> 8 ) | ( ( source & 0x00000000ff000000ULL ) << 8 ) | ( ( source & 0x0000000000ff0000ULL ) << 24 ) | ( ( source & 0x000000000000ff00ULL ) << 40 ) | ( ( source & 0x00000000000000ffULL ) << 56 ); #endif } constexpr void bswap(uint8_t * sink, uint8_t const * source, nsize_t len) { source += len - 1; for (; len > 0; len--) { *sink++ = *source--; } } constexpr uint128_t bswap(uint128_t const & source) noexcept { uint128_t dest; bswap(dest.data, source.data, 16); return dest; } constexpr uint192_t bswap(uint192_t const & source) noexcept { uint192_t dest; bswap(dest.data, source.data, 24); return dest; } constexpr uint256_t bswap(uint256_t const & source) noexcept { uint256_t dest; bswap(dest.data, source.data, 32); return dest; } /** // ************************************************* // ************************************************* // ************************************************* */ PRAGMA_DISABLE_WARNING_PUSH PRAGMA_DISABLE_WARNING_MULTICHAR namespace impl { constexpr uint32_t get_host_order() noexcept { if constexpr ( jau::is_builtin_bit_cast_available() ) { constexpr uint8_t b[4] { 0x44, 0x43, 0x42, 0x41 }; // h->l: 41 42 43 44 = 'ABCD' hex ASCII code return jau::bit_cast( b ); } else { // The pragma to stop multichar warning == error `-Werror=multichar` doesn't seem to work with GCC <= 10.2 // Hence we have to disable this specific warning via: `-Wno-multichar` // until all our compiler support `__builtin_bit_cast(T, a)` return 'ABCD'; // h->l: 41 42 43 44 = 'ABCD' hex ASCII code } } } /** * Endian identifier, indicating endianess of all scalar types. * * Inspired by C++20 std::endian * * Corner case platforms currently not supported, * i.e. unified endianess and mixed endianess. * * All endian API entries are of `constexpr` and hence evaluated at compile time.
* Therefore, if-branches and expressions are also of `constexpr` and optimized 'away' at compile time.
* This includes the `cpu_to_(..)` and `_to_cpu(..)` etc utility functions. * * On i386 platforms the host byte order is Least Significant Byte first (LSB) or Little-Endian, * whereas the network byte order, as used on the Internet, is Most Significant Byte first (MSB) or Big-Endian. * See #include * * Bluetooth is LSB or Little-Endian! */ enum class endian : uint32_t { /** Identifier for little endian. */ little = 0x41424344U, // h->l: 41 42 43 44 = 'ABCD' hex ASCII code /** Identifier for big endian. */ big = 0x44434241U, // h->l: 44 43 42 41 = 'DCBA' hex ASCII code /** Identifier for DEC PDP-11, aka `ENDIAN_LITTLE_WORD`. */ pdp = 0x43444142U, // h->l: 43 44 41 42 = 'CDAB' hex ASCII code /** Identifier for Honeywell 316, aka `ENDIAN_BIG_WORD`. */ honeywell = 0x42414443U, // h->l: 42 41 44 43 = 'BADC' hex ASCII code /** Undetermined endian */ undefined = 0x00000000U, /** Identifier for native platform type, one of the above. */ native = impl::get_host_order() }; PRAGMA_DISABLE_WARNING_POP /** * Return std::string representation of the given \ref endian. * @param v the \ref endian value * @return the std::string representation */ std::string to_string(const endian& v) noexcept; /** * Evaluates `true` if the given \ref endian is defined, * i.e. `little`, `big`, `pdp` or `honeywell`. */ constexpr bool isDefinedEndian(const endian &v) noexcept { switch(v) { case endian::little: [[fallthrough]]; case endian::big: [[fallthrough]]; case endian::pdp: [[fallthrough]]; case endian::honeywell: return true; default: return false; } } /** * Evaluates `true` if platform is running in little \ref endian mode, * i.e. `jau::endian::little == jau::endian::native`. */ constexpr bool isLittleEndian() noexcept { return endian::little == endian::native; } /** * Evaluates `true` if platform is running in big \ref endian mode, * i.e. `jau::endian::big == jau::endian::native`. */ constexpr bool isBigEndian() noexcept { return endian::big == endian::native; } /** * Evaluates `true` if platform is running in little or big \ref endian mode, * i.e. `jau::endian::little == jau::endian::native || jau::endian::big == jau::endian::native`. */ constexpr bool isLittleOrBigEndian() noexcept { return jau::endian::little == jau::endian::native || jau::endian::big == jau::endian::native; } /** * A little-endian type trait for convenience .. *

* Since all \ref endian definitions are of `constexpr`, code can simply use expressions of these * for compile-time evaluation and optimization. A template `SFINAE` is not required. *

* @tparam Dummy_type just to make template `SFINAE` happy */ template struct has_endian_little : std::integral_constant {}; /** * Value access of little-endian type trait for convenience .. *

* Since all \ref endian definitions are of `constexpr`, code can simply use expressions of these * for compile-time evaluation and optimization. A template `SFINAE` is not required. *

* @tparam Dummy_type just to make template `SFINAE` happy */ template constexpr bool has_endian_little_v = has_endian_little::value; /** * A big-endian type trait for convenience .. *

* Since all \ref endian definitions are of `constexpr`, code can simply use expressions of these * for compile-time evaluation and optimization. A template `SFINAE` is not required. *

* @tparam Dummy_type just to make template `SFINAE` happy */ template struct has_endian_big : std::integral_constant {}; /** * Value access of big-endian type trait for convenience .. *

* Since all \ref endian definitions are of `constexpr`, code can simply use expressions of these * for compile-time evaluation and optimization. A template `SFINAE` is not required. *

* @tparam Dummy_type just to make template `SFINAE` happy */ template constexpr bool has_endian_big_v = has_endian_big::value; /** // ************************************************* // ************************************************* // ************************************************* */ constexpr uint16_t be_to_cpu(uint16_t const n) noexcept { static_assert(isLittleOrBigEndian()); // one static_assert is sufficient for whole compilation unit if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint16_t cpu_to_be(uint16_t const h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint16_t le_to_cpu(uint16_t const l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint16_t cpu_to_le(uint16_t const h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint32_t be_to_cpu(uint32_t const n) noexcept { if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint32_t cpu_to_be(uint32_t const h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint32_t le_to_cpu(uint32_t const l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint32_t cpu_to_le(uint32_t const h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint64_t be_to_cpu(uint64_t const & n) noexcept { if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint64_t cpu_to_be(uint64_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint64_t le_to_cpu(uint64_t const & l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint64_t cpu_to_le(uint64_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return 0; // unreachable -> static_assert(..) above } } constexpr uint128_t be_to_cpu(uint128_t const & n) noexcept { if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint128_t cpu_to_be(uint128_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint128_t le_to_cpu(uint128_t const & l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint128_t cpu_to_le(uint128_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint192_t be_to_cpu(uint192_t const & n) noexcept { if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint192_t cpu_to_be(uint192_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint192_t le_to_cpu(uint192_t const & l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint192_t cpu_to_le(uint192_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint256_t be_to_cpu(uint256_t const & n) noexcept { if constexpr ( isLittleEndian() ) { return bswap(n); } else if constexpr ( isBigEndian() ) { return n; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint256_t cpu_to_be(uint256_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return bswap(h); } else if constexpr ( isBigEndian() ) { return h; } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint256_t le_to_cpu(uint256_t const & l) noexcept { if constexpr ( isLittleEndian() ) { return l; } else if constexpr ( isBigEndian() ) { return bswap(l); } else { return {}; // unreachable -> static_assert(..) above } } constexpr uint256_t cpu_to_le(uint256_t const & h) noexcept { if constexpr ( isLittleEndian() ) { return h; } else if constexpr ( isBigEndian() ) { return bswap(h); } else { return {}; // unreachable -> static_assert(..) above } } /** // ************************************************* // ************************************************* // ************************************************* */ constexpr void put_uint8(uint8_t * buffer, nsize_t const byte_offset, const uint8_t v) noexcept { *pointer_cast( buffer + byte_offset ) = v; } constexpr uint8_t get_uint8(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return *pointer_cast( buffer + byte_offset ); } constexpr int8_t get_int8(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return *pointer_cast( buffer + byte_offset ); } /** * Return packed_t::store after converting it to from either endian::little ot endian::big depending on given `littleEndian` * to endian::native. * @tparam T * @param source * @param littleEndian */ template constexpr T get_packed_value(const packed_t* source, const bool littleEndian) noexcept { return littleEndian ? le_to_cpu(source->store) : be_to_cpu(source->store); } /** * Put the given uint16_t value into the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * @see \ref packed_t_alignment_cast */ constexpr void put_uint16(uint8_t * buffer, nsize_t const byte_offset, const uint16_t v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * Put the given uint16_t value into the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * The value is converted from endian::native to either endian::little ot endian::big depending on given `littleEndian` * before it is stored in memory. * * @see \ref packed_t_alignment_cast */ constexpr void put_uint16(uint8_t * buffer, nsize_t const byte_offset, const uint16_t v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * Returns a uint16_t value from the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * @see \ref packed_t_alignment_cast */ constexpr uint16_t get_uint16(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * Returns a uint16_t value from the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * The value is converted from either endian::little ot endian::big depending on given `littleEndian` * to endian::native before it is returned to the caller. * * @see \ref packed_t_alignment_cast */ constexpr uint16_t get_uint16(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint32(uint8_t * buffer, nsize_t const byte_offset, const uint32_t v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint32(uint8_t * buffer, nsize_t const byte_offset, const uint32_t v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint32_t get_uint32(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint32_t get_uint32(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint64(uint8_t * buffer, nsize_t const byte_offset, const uint64_t & v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint64(uint8_t * buffer, nsize_t const byte_offset, const uint64_t & v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint64_t get_uint64(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint64_t get_uint64(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint128(uint8_t * buffer, nsize_t const byte_offset, const uint128_t & v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint128(uint8_t * buffer, nsize_t const byte_offset, const uint128_t & v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint128_t get_uint128(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint128_t get_uint128(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint192(uint8_t * buffer, nsize_t const byte_offset, const uint192_t & v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint192(uint8_t * buffer, nsize_t const byte_offset, const uint192_t & v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint192_t get_uint192(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint192_t get_uint192(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint256(uint8_t * buffer, nsize_t const byte_offset, const uint256_t & v) noexcept { pointer_cast*>( buffer + byte_offset )->store = v; } /** * See put_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr void put_uint256(uint8_t * buffer, nsize_t const byte_offset, const uint256_t & v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint256_t get_uint256(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * See get_uint16() for reference. * @see \ref packed_t_alignment_cast */ constexpr uint256_t get_uint256(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /** // ************************************************* // ************************************************* // ************************************************* */ /** * Put the given T value into the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * @tparam T * @param buffer * @param byte_offset * @param v * @see \ref packed_t_alignment_cast */ template constexpr typename std::enable_if_t< std::is_standard_layout_v, void> put_value(uint8_t * buffer, nsize_t const byte_offset, const T& v) noexcept { // reinterpret_cast*>( buffer + byte_offset )->store = v; pointer_cast*>( buffer + byte_offset )->store = v; } /** * Put the given T value into the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * The value is converted from endian::native to either endian::little ot endian::big depending on given `littleEndian` * before it is stored in memory. * * @tparam T * @param buffer * @param byte_offset * @param v * @param littleEndian * @see \ref packed_t_alignment_cast */ template constexpr typename std::enable_if_t< std::is_standard_layout_v, void> put_value(uint8_t * buffer, nsize_t const byte_offset, const T& v, const bool littleEndian) noexcept { pointer_cast*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } /** * Returns a T value from the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * @tparam T * @param buffer * @param byte_offset * @return * @see \ref packed_t_alignment_cast */ template constexpr typename std::enable_if_t< std::is_standard_layout_v, T> get_value(uint8_t const * buffer, nsize_t const byte_offset) noexcept { return pointer_cast*>( buffer + byte_offset )->store; } /** * Returns a T value from the given byte address * using \ref packed_t to resolve a potential memory alignment issue *free of costs*. * * The value is converted from either endian::little ot endian::big depending on given `littleEndian` * to endian::native before it is returned to the caller. * * @tparam T * @param buffer * @param byte_offset * @param littleEndian * @return * @see \ref packed_t_alignment_cast */ template constexpr typename std::enable_if_t< std::is_standard_layout_v, T> get_value(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept { return get_packed_value(pointer_cast*>( buffer + byte_offset ), littleEndian); } /**@}*/ } // namespace jau /** \example test_basictypeconv.cpp * This C++ unit test validates the jau::bswap and get/set value implementation */ #endif /* JAU_BYTE_UTIL_HPP_ */