diff options
author | Sven Gothel <[email protected]> | 2020-10-17 05:33:16 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-10-17 05:33:16 +0200 |
commit | 9662620e9a19685f8dde2cac5bec74ba39cb71c7 (patch) | |
tree | 26106f716e0247168b5391fbdf3d2f02b019aeec | |
parent | f4681f5ff116ce9e320f8804aa4d09480f3c97e7 (diff) |
basic_types: Handle unaligned access via compiler magic 'struct __attribute__((__packed__))' access - much cheaper!
Utilizing a no-cost template struct having the __packed__ attribute
utilizes the compiler to generate safe load and store operations.
This is validated via '-Wcast-align=strict', which verifies safe pointer cast for all platforms
on the current compilation host.
This also lead to fixing merge_uint128(*) unsafe cast + assignment.
-rw-r--r-- | include/jau/basic_types.hpp | 176 | ||||
-rw-r--r-- | src/basic_types.cpp | 10 |
2 files changed, 85 insertions, 101 deletions
diff --git a/include/jau/basic_types.hpp b/include/jau/basic_types.hpp index aeb63ed..75344d9 100644 --- a/include/jau/basic_types.hpp +++ b/include/jau/basic_types.hpp @@ -275,140 +275,122 @@ namespace jau { int8_t const * p = (int8_t const *) ( buffer + byte_offset ); return *p; } + + /** + * 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> + */ + template<typename T> struct __attribute__((__packed__)) packed_t { + T store; + constexpr T get(const bool littleEndian) const noexcept { return littleEndian ? le_to_cpu(store) : be_to_cpu(store); } + }; + inline void put_uint16(uint8_t * buffer, int 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; - memcpy(buffer + byte_offset, &v, sizeof(v)); - } - inline void put_uint16(uint8_t * buffer, int const byte_offset, const uint16_t v, bool littleEndian) 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: + */ + reinterpret_cast<packed_t<uint16_t>*>( buffer + byte_offset )->store = v; + } + inline void put_uint16(uint8_t * buffer, int 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); - const uint16_t v2 = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - memcpy(buffer + byte_offset, &v2, sizeof(v2)); + /** + * 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: + */ + reinterpret_cast<packed_t<uint16_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } inline uint16_t get_uint16(uint8_t const * buffer, int 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; - uint16_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return v; - } - inline uint16_t get_uint16(uint8_t const * buffer, int const byte_offset, 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 *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 reinterpret_cast<const packed_t<uint16_t>*>( buffer + byte_offset )->store; + } + inline uint16_t get_uint16(uint8_t const * buffer, int 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); - uint16_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return littleEndian ? le_to_cpu(v) : be_to_cpu(v); + /** + * 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 reinterpret_cast<const packed_t<uint16_t>*>( buffer + byte_offset )->get(littleEndian); } inline void put_uint32(uint8_t * buffer, int const byte_offset, const uint32_t v) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint32_t * p = (uint32_t *) ( buffer + byte_offset ); - // *p = v; - memcpy(buffer + byte_offset, &v, sizeof(v)); + reinterpret_cast<packed_t<uint32_t>*>( buffer + byte_offset )->store = v; } - inline void put_uint32(uint8_t * buffer, int const byte_offset, const uint32_t v, bool littleEndian) noexcept + inline void put_uint32(uint8_t * buffer, int const byte_offset, const uint32_t v, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint32_t * p = (uint32_t *) ( buffer + byte_offset ); - // *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - const uint32_t v2 = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - memcpy(buffer + byte_offset, &v2, sizeof(v2)); + reinterpret_cast<packed_t<uint32_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } inline uint32_t get_uint32(uint8_t const * buffer, int const byte_offset) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint32_t const * p = (uint32_t const *) ( buffer + byte_offset ); - // return *p; - uint32_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return v; - } - inline uint32_t get_uint32(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept + return reinterpret_cast<const packed_t<uint32_t>*>( buffer + byte_offset )->store; + } + inline uint32_t get_uint32(uint8_t const * buffer, int const byte_offset, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint32_t const * p = (uint32_t const *) ( buffer + byte_offset ); - // return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p); - uint32_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return littleEndian ? le_to_cpu(v) : be_to_cpu(v); + return reinterpret_cast<const packed_t<uint32_t>*>( buffer + byte_offset )->get(littleEndian); } inline void put_uint64(uint8_t * buffer, int const byte_offset, const uint64_t v) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint64_t * p = (uint64_t *) ( buffer + byte_offset ); - // *p = v; - memcpy(buffer + byte_offset, &v, sizeof(v)); + reinterpret_cast<packed_t<uint64_t>*>( buffer + byte_offset )->store = v; } - inline void put_uint64(uint8_t * buffer, int const byte_offset, const uint64_t v, bool littleEndian) noexcept + inline void put_uint64(uint8_t * buffer, int const byte_offset, const uint64_t v, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint64_t * p = (uint64_t *) ( buffer + byte_offset ); - // *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - const uint64_t v2 = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - memcpy(buffer + byte_offset, &v2, sizeof(v2)); + reinterpret_cast<packed_t<uint64_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } inline uint64_t get_uint64(uint8_t const * buffer, int const byte_offset) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint64_t const * p = (uint64_t const *) ( buffer + byte_offset ); - // return *p; - uint64_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return v; - } - inline uint64_t get_uint64(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept + return reinterpret_cast<const packed_t<uint64_t>*>( buffer + byte_offset )->store; + } + inline uint64_t get_uint64(uint8_t const * buffer, int const byte_offset, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint64_t const * p = (uint64_t const *) ( buffer + byte_offset ); - // return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p); - uint64_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return littleEndian ? le_to_cpu(v) : be_to_cpu(v); + return reinterpret_cast<const packed_t<uint64_t>*>( buffer + byte_offset )->get(littleEndian); } inline void put_uint128(uint8_t * buffer, int const byte_offset, const uint128_t v) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint128_t * p = (uint128_t *) ( buffer + byte_offset ); - // *p = v; - memcpy(buffer + byte_offset, &v, sizeof(v)); + reinterpret_cast<packed_t<uint128_t>*>( buffer + byte_offset )->store = v; } - inline void put_uint128(uint8_t * buffer, int const byte_offset, const uint128_t v, bool littleEndian) noexcept + inline void put_uint128(uint8_t * buffer, int const byte_offset, const uint128_t v, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint128_t * p = (uint128_t *) ( buffer + byte_offset ); - // *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - const uint128_t v2 = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - memcpy(buffer + byte_offset, &v2, sizeof(v2)); + reinterpret_cast<packed_t<uint128_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); } inline uint128_t get_uint128(uint8_t const * buffer, int const byte_offset) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint128_t const * p = (uint128_t const *) ( buffer + byte_offset ); - // return *p; - uint128_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return v; - } - inline uint128_t get_uint128(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept + return reinterpret_cast<const packed_t<uint128_t>*>( buffer + byte_offset )->store; + } + inline uint128_t get_uint128(uint8_t const * buffer, int const byte_offset, const bool littleEndian) noexcept { - // Handle potentially misaligned address of buffer + byte_offset, can't just - // uint128_t const * p = (uint128_t const *) ( buffer + byte_offset ); - // return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p); - uint128_t v; - memcpy(&v, buffer + byte_offset, sizeof(v)); - return littleEndian ? le_to_cpu(v) : be_to_cpu(v); + return reinterpret_cast<const packed_t<uint128_t>*>( buffer + byte_offset )->get(littleEndian); } inline void set_bit_uint32(const uint8_t nr, uint32_t &mask) diff --git a/src/basic_types.cpp b/src/basic_types.cpp index f9a9cc3..67421d9 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -102,8 +102,9 @@ uint128_t jau::merge_uint128(uint16_t const uuid16, uint128_t const & base_uuid, #else #error "Unexpected __BYTE_ORDER" #endif - uint16_t * destu16 = (uint16_t*)(dest.data + offset); - *destu16 += uuid16; + // uint16_t * destu16 = (uint16_t*)(dest.data + offset); + // *destu16 += uuid16; + reinterpret_cast<packed_t<uint16_t>*>( dest.data + offset )->store += uuid16; return dest; } @@ -137,8 +138,9 @@ uint128_t jau::merge_uint128(uint32_t const uuid32, uint128_t const & base_uuid, #else #error "Unexpected __BYTE_ORDER" #endif - uint32_t * destu32 = (uint32_t*)(dest.data + offset); - *destu32 += uuid32; + // uint32_t * destu32 = (uint32_t*)(dest.data + offset); + // *destu32 += uuid32; + reinterpret_cast<packed_t<uint32_t>*>( dest.data + offset )->store += uuid32; return dest; } |