summaryrefslogtreecommitdiffstats
path: root/include/jau
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-05-02 06:00:22 +0200
committerSven Gothel <[email protected]>2022-05-02 06:00:22 +0200
commitbc4647de599c1fea30229495c96805796097b34c (patch)
tree8d08734c02cead644817f9701e806317012af0f4 /include/jau
parent76c695c4b8e2fd034d31cdeb54883f759b4df353 (diff)
Complete ByteUtils API doc of packed_t and get_<stdint>() get_value() and its put variants, detail packed_t's alignment.
Diffstat (limited to 'include/jau')
-rw-r--r--include/jau/byte_util.hpp234
-rw-r--r--include/jau/packed_attribute.hpp43
2 files changed, 222 insertions, 55 deletions
diff --git a/include/jau/byte_util.hpp b/include/jau/byte_util.hpp
index 9359100..582e60f 100644
--- a/include/jau/byte_util.hpp
+++ b/include/jau/byte_util.hpp
@@ -52,7 +52,14 @@ namespace jau {
* This includes the `cpu_to_<endian>(..)` and `<endian>_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
@@ -145,10 +152,6 @@ namespace jau {
// *************************************************
*/
- // 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)`
-
PRAGMA_DISABLE_WARNING_PUSH
PRAGMA_DISABLE_WARNING_MULTICHAR
@@ -158,6 +161,9 @@ namespace jau {
constexpr uint8_t b[4] { 0x44, 0x43, 0x42, 0x41 }; // h->l: 41 42 43 44 = 'ABCD' hex ASCII code
return jau::bit_cast<uint32_t, uint8_t[4]>( 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
}
}
@@ -543,146 +549,226 @@ namespace jau {
return *pointer_cast<int8_t const *>( 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<typename T>
- constexpr T get_value(const packed_t<T>* source, const bool littleEndian) noexcept { return littleEndian ? le_to_cpu(source->store) : be_to_cpu(source->store); }
+ constexpr T get_packed_value(const packed_t<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
{
- /**
- * Handle potentially misaligned address of buffer + byte_offset, can't just
- * uint16_t * p = (uint16_t *) ( buffer + byte_offset );
- * *p = v;
- * Universal alternative using memcpy is costly:
- * memcpy(buffer + byte_offset, &v, sizeof(v));
- * Use compiler magic 'struct __attribute__((__packed__))' access:
- */
pointer_cast<packed_t<uint16_t>*>( 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
{
- /**
- * Handle potentially misaligned address of buffer + byte_offset, can't just
- * uint16_t * p = (uint16_t *) ( buffer + byte_offset );
- * *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v);
- * Universal alternative using memcpy is costly:
- * const uint16_t v2 = littleEndian ? cpu_to_le(v) : cpu_to_be(v);
- * memcpy(buffer + byte_offset, &v2, sizeof(v2));
- * Use compiler magic 'struct __attribute__((__packed__))' access:
- */
pointer_cast<packed_t<uint16_t>*>( 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
{
- /**
- * Handle potentially misaligned address of buffer + byte_offset, can't just
- * uint16_t const * p = (uint16_t const *) ( buffer + byte_offset );
- * return *p;
- * Universal alternative using memcpy is costly:
- * uint16_t v;
- * memcpy(&v, buffer + byte_offset, sizeof(v));
- * return v;
- * Use compiler magic 'struct __attribute__((__packed__))' access:
- */
return pointer_cast<const packed_t<uint16_t>*>( 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
{
- /**
- * Handle potentially misaligned address of buffer + byte_offset, can't just
- * uint16_t const * p = (uint16_t const *) ( buffer + byte_offset );
- * return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p);
- * Universal alternative using memcpy is costly:
- * uint16_t v;
- * memcpy(&v, buffer + byte_offset, sizeof(v));
- * return littleEndian ? le_to_cpu(v) : be_to_cpu(v);
- * Use compiler magic 'struct __attribute__((__packed__))' access:
- */
- return get_value(pointer_cast<const packed_t<uint16_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint16_t>*>( 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<packed_t<uint32_t>*>( 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<packed_t<uint32_t>*>( 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<const packed_t<uint32_t>*>( 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_value(pointer_cast<const packed_t<uint32_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint32_t>*>( 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<packed_t<uint64_t>*>( 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<packed_t<uint64_t>*>( 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<const packed_t<uint64_t>*>( 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_value(pointer_cast<const packed_t<uint64_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint64_t>*>( 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<packed_t<uint128_t>*>( 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<packed_t<uint128_t>*>( 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<const packed_t<uint128_t>*>( 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_value(pointer_cast<const packed_t<uint128_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint128_t>*>( 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<packed_t<uint192_t>*>( 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<packed_t<uint192_t>*>( 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<const packed_t<uint192_t>*>( 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_value(pointer_cast<const packed_t<uint192_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint192_t>*>( 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<packed_t<uint256_t>*>( 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<packed_t<uint256_t>*>( 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<const packed_t<uint256_t>*>( 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_value(pointer_cast<const packed_t<uint256_t>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<uint256_t>*>( buffer + byte_offset ), littleEndian);
}
/**
@@ -691,6 +777,16 @@ namespace jau {
// *************************************************
*/
+ /**
+ * 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<typename T>
constexpr
typename std::enable_if_t<
@@ -702,6 +798,20 @@ namespace jau {
pointer_cast<packed_t<T>*>( 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<typename T>
constexpr
typename std::enable_if_t<
@@ -712,6 +822,16 @@ namespace jau {
pointer_cast<packed_t<T>*>( 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<typename T>
constexpr
typename std::enable_if_t<
@@ -722,6 +842,20 @@ namespace jau {
return pointer_cast<const packed_t<T>*>( 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<typename T>
constexpr
typename std::enable_if_t<
@@ -729,7 +863,7 @@ namespace jau {
T>
get_value(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept
{
- return get_value(pointer_cast<const packed_t<T>*>( buffer + byte_offset ), littleEndian);
+ return get_packed_value(pointer_cast<const packed_t<T>*>( buffer + byte_offset ), littleEndian);
}
/**@}*/
diff --git a/include/jau/packed_attribute.hpp b/include/jau/packed_attribute.hpp
index 98412cd..428c8d0 100644
--- a/include/jau/packed_attribute.hpp
+++ b/include/jau/packed_attribute.hpp
@@ -68,11 +68,44 @@ namespace jau {
*/
/**
- * Safe access to a pointer cast from unaligned memory via __packed__ attribute,
- * i.e. utilizing compiler generated safe load and store operations.
- * <p>
- * This template shall cause no costs, the cast data pointer is identical to 'T * p = &store'.
- * </p>
+ * Support aligned memory transfer from and to potentially unaligned memory.
+ *
+ * This template causes no costs, the cast data pointer is identical to 'T * p = &store'.
+ *
+ * packed_t is used in \ref ByteUtils.
+ *
+ * @anchor packed_t_alignment_cast
+ * Background for using packed_t:
+ *
+ * Due to the potentially unaligned memory address of `buffer`,
+ * we can't just directly use pointer arithmetic like:
+ * <pre>
+ * // return uint16_t from memory
+ * return *( (uint16_t *) ( buffer ) );
+ *
+ * // store uint16_t to memory
+ * *( (uint16_t *) ( buffer ) ) = v;
+ * </pre>
+ *
+ * The universal alternative using `memcpy()` is costly:
+ * <pre>
+ * // return uint16_t from memory
+ * memcpy(&v, buffer, sizeof(v));
+ * return v;
+ *
+ * // store uint16_t to memory
+ * memcpy(buffer, &v, sizeof(v));
+ * </pre>
+ *
+ * Solution is to use the *free of costs* high performance *compiler magic* 'struct __attribute__((__packed__))'.<br />
+ * The offset memory is pointer_cast() into the desired packed_t type and its packed_t::store accessed thereafter:
+ * <pre>
+ * // return uint16_t from memory
+ * return pointer_cast<const packed_t<uint16_t>*>( buffer )->store;
+ *
+ * // store uint16_t to memory
+ * pointer_cast<packed_t<uint16_t>*>( buffer )->store = v;
+ * </pre>
*/
template<typename T> __pack ( struct packed_t {
T store;