diff options
author | Sven Gothel <[email protected]> | 2021-02-08 15:02:43 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-02-08 15:02:43 +0100 |
commit | b218b12a155a2f07eabbd02e3fbada3feed3aab7 (patch) | |
tree | b432c68247f08810927520b99c3b03af45c05492 /include | |
parent | 112528c73f09b262bd53f817f9d9ff9343af0ee9 (diff) |
basic_types.hpp: Cleanup; Add constexpr 'enum class endian', 'pointer_cast()' and 'bit_cast()' and have all byte-order conversion and get/set functions be of constexpr; Add generalized template [get|put]_value(..) and to_hex_string(..)
Cleanup
- Split basic_types.hpp -> basic_types.hpp + byte_util.hpp + int_types.hpp + string_util.hpp
- Moved nsize_t, snsize_t to int_types.hpp and
using 'uint_fast32_t' and 'int_fast32_t' as natural types.
- Renamed cpp_lang_macros.hpp -> cpp_lang_util.hpp
+++
Add constexpr 'enum class endian', 'pointer_cast()' and 'bit_cast()'
and have all byte-order conversion and get/set functions be of constexpr.
- Exposing '__builtin_bit_cast(Dest_type, arg)' via
'constexpr bool is_builtin_bit_cast_available()' and type traits.
- Adding constexpr bit_cast<>() template for C++17 (a C++20 std),
allowing constexpr type conversion using '__builtin_bit_cast(Dest_type, arg)',
if the latter is available.
- Adding constexpr pointer_cast<>() template,
allowing constexpr pointer type conversion.
Either using '__builtin_bit_cast(Dest_type, arg)' or reinterpret_cast<>().
- Add constexpr 'enum class endian' API,
providing compile-time C++ endian evaluation w/o predefined macros.
Inspired by C++20.
- Replace linux bswap_[16,32,64] with either __builtin_bswap[16,32,64] or own const definition,
both allowing constexpr
- Add unified overloaded 'constexpr bswap(uint[16,32,64,128,192,256]_t const &)'
using constexpr endian API.
- Have all [get|put]_<type>(..) operations be of constexpr,
using pointer_cast<>() instead of plain reinterpret_cast<>()
and the new 'constexpr bswap(..)' methods.
+++
Add generalized template [get|put]_value(..) and to_hex_string(..)
- Add generalized template 'constexpr T [get|put]_value(..) {}' for std::is_standard_layout_v<T>
- Add generalized template 'inline to_hex_string(T const &) {}' for std::is_standard_layout_v<T>
+++
All of the above is covered by unit test 'test_basictypecon.cpp' '-std=c++17':
- gcc 8.3.0 on arm32, arm64: __builtin_bit_cast() not available
- gcc 10.2.1 on amd64: __builtin_bit_cast() not available
- clang 9.0.1 on amd64, arm64: __builtin_bit_cast() is available
- clang 11.0.1 on amd64: __builtin_bit_cast() is available
Full build time (user) incl unit tests (C++ and Java) on GCC
- amd64 gcc 11.0.1, 32 cores: 1m38s
- arm64 gcc 8.3.0, 4 cores: 13m30s
- arm32 gcc 8.3.0, 4 cores: 17m58s
Diffstat (limited to 'include')
-rw-r--r-- | include/jau/basic_types.hpp | 726 | ||||
-rw-r--r-- | include/jau/byte_util.hpp | 753 | ||||
-rw-r--r-- | include/jau/cow_darray.hpp | 2 | ||||
-rw-r--r-- | include/jau/cow_iterator.hpp | 2 | ||||
-rw-r--r-- | include/jau/cow_vector.hpp | 2 | ||||
-rw-r--r-- | include/jau/cpp_lang_macros.hpp | 94 | ||||
-rw-r--r-- | include/jau/cpp_lang_util.hpp | 260 | ||||
-rw-r--r-- | include/jau/cpp_pragma.hpp | 63 | ||||
-rw-r--r-- | include/jau/int_math.hpp | 32 | ||||
-rw-r--r-- | include/jau/int_types.hpp | 133 | ||||
-rw-r--r-- | include/jau/string_util.hpp | 329 | ||||
-rw-r--r-- | include/jau/type_traits_queries.hpp | 11 |
12 files changed, 1550 insertions, 857 deletions
diff --git a/include/jau/basic_types.hpp b/include/jau/basic_types.hpp index 27a28f3..2b54aee 100644 --- a/include/jau/basic_types.hpp +++ b/include/jau/basic_types.hpp @@ -33,16 +33,15 @@ #include <vector> #include <type_traits> -extern "C" { - #include <endian.h> - #include <byteswap.h> -} - -#include <jau/int_math.hpp> -#include <jau/cpp_lang_macros.hpp> +#include <jau/cpp_lang_util.hpp> #include <jau/packed_attribute.hpp> #include <jau/type_traits_queries.hpp> +#include <jau/int_types.hpp> +#include <jau/int_math.hpp> +#include <jau/byte_util.hpp> +#include <jau/string_util.hpp> + namespace jau { /** @@ -139,443 +138,6 @@ namespace jau { // ************************************************* */ - __pack( struct uint128_t { - uint8_t data[16]; - - constexpr uint128_t() noexcept : data{0} {} - constexpr uint128_t(const uint128_t &o) noexcept = default; - uint128_t(uint128_t &&o) noexcept = default; - constexpr uint128_t& operator=(const uint128_t &o) noexcept = default; - uint128_t& operator=(uint128_t &&o) noexcept = default; - - void clear() noexcept { bzero(data, sizeof(data)); } - - constexpr bool operator==(uint128_t const &o) const noexcept { - if( this == &o ) { - return true; - } - return !std::memcmp(data, o.data, sizeof(data)); - } - constexpr bool operator!=(uint128_t const &o) const noexcept - { return !(*this == o); } - } ) ; - - constexpr uint128_t bswap(uint128_t const & source) noexcept { - uint128_t dest; - uint8_t const * const s = source.data; - uint8_t * const d = dest.data; - for(nsize_t i=0; i<16; i++) { - d[i] = s[15-i]; - } - return dest; - } - - __pack( struct uint192_t { - uint8_t data[24]; - - constexpr uint192_t() noexcept : data{0} {} - constexpr uint192_t(const uint192_t &o) noexcept = default; - uint192_t(uint192_t &&o) noexcept = default; - constexpr uint192_t& operator=(const uint192_t &o) noexcept = default; - uint192_t& operator=(uint192_t &&o) noexcept = default; - - void clear() noexcept { bzero(data, sizeof(data)); } - - constexpr bool operator==(uint192_t const &o) const noexcept { - if( this == &o ) { - return true; - } - return !std::memcmp(data, o.data, sizeof(data)); - } - constexpr bool operator!=(uint192_t const &o) const noexcept - { return !(*this == o); } - } ); - - constexpr uint192_t bswap(uint192_t const & source) noexcept { - uint192_t dest; - uint8_t const * const s = source.data; - uint8_t * const d = dest.data; - for(nsize_t i=0; i<24; i++) { - d[i] = s[23-i]; - } - return dest; - } - - __pack( struct uint256_t { - uint8_t data[32]; - - constexpr uint256_t() noexcept : data{0} {} - constexpr uint256_t(const uint256_t &o) noexcept = default; - uint256_t(uint256_t &&o) noexcept = default; - constexpr uint256_t& operator=(const uint256_t &o) noexcept = default; - uint256_t& operator=(uint256_t &&o) noexcept = default; - - void clear() noexcept { bzero(data, sizeof(data)); } - - constexpr bool operator==(uint256_t const &o) const noexcept { - if( this == &o ) { - return true; - } - return !std::memcmp(data, o.data, sizeof(data)); - } - constexpr bool operator!=(uint256_t const &o) const noexcept - { return !(*this == o); } - } ); - - constexpr uint256_t bswap(uint256_t const & source) noexcept { - uint256_t dest; - uint8_t const * const s = source.data; - uint8_t * const d = dest.data; - for(nsize_t i=0; i<32; i++) { - d[i] = s[31-i]; - } - return dest; - } - - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - /** - * On the i386 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 <arpa/inet.h> - * - * Bluetooth is LSB or Little-Endian! - */ - -#if __BYTE_ORDER == __BIG_ENDIAN - inline uint16_t be_to_cpu(uint16_t const n) noexcept { - return n; - } - inline uint16_t cpu_to_be(uint16_t const h) noexcept { - return h; - } - inline uint16_t le_to_cpu(uint16_t const l) noexcept { - return bswap_16(l); - } - inline uint16_t cpu_to_le(uint16_t const h) noexcept { - return bswap_16(h); - } - - inline uint32_t be_to_cpu(uint32_t const n) noexcept { - return n; - } - inline uint32_t cpu_to_be(uint32_t const h) noexcept { - return h; - } - inline uint32_t le_to_cpu(uint32_t const l) noexcept { - return bswap_32(l); - } - inline uint32_t cpu_to_le(uint32_t const h) noexcept { - return bswap_32(h); - } - - inline uint64_t be_to_cpu(uint64_t const & n) noexcept { - return n; - } - inline uint64_t cpu_to_be(uint64_t const & h) noexcept { - return h; - } - inline uint64_t le_to_cpu(uint64_t const & l) noexcept { - return bswap_64(l); - } - inline uint64_t cpu_to_le(uint64_t const & h) noexcept { - return bswap_64(h); - } - - constexpr uint128_t be_to_cpu(uint128_t const & n) noexcept { - return n; - } - constexpr uint128_t cpu_to_be(uint128_t const & h) noexcept { - return n; - } - constexpr uint128_t le_to_cpu(uint128_t const & l) noexcept { - return bswap(l); - } - constexpr uint128_t cpu_to_le(uint128_t const & h) noexcept { - return bswap(h); - } - - constexpr uint192_t be_to_cpu(uint192_t const & n) noexcept { - return n; - } - constexpr uint192_t cpu_to_be(uint192_t const & h) noexcept { - return n; - } - constexpr uint192_t le_to_cpu(uint192_t const & l) noexcept { - return bswap(l); - } - constexpr uint192_t cpu_to_le(uint192_t const & h) noexcept { - return bswap(h); - } - - constexpr uint256_t be_to_cpu(uint256_t const & n) noexcept { - return n; - } - constexpr uint256_t cpu_to_be(uint256_t const & h) noexcept { - return n; - } - constexpr uint256_t le_to_cpu(uint256_t const & l) noexcept { - return bswap(l); - } - constexpr uint256_t cpu_to_le(uint256_t const & h) noexcept { - return bswap(h); - } -#elif __BYTE_ORDER == __LITTLE_ENDIAN - inline uint16_t be_to_cpu(uint16_t const n) noexcept { - return bswap_16(n); - } - inline uint16_t cpu_to_be(uint16_t const h) noexcept { - return bswap_16(h); - } - inline uint16_t le_to_cpu(uint16_t const l) noexcept { - return l; - } - inline uint16_t cpu_to_le(uint16_t const h) noexcept { - return h; - } - - inline uint32_t be_to_cpu(uint32_t const n) noexcept { - return bswap_32(n); - } - inline uint32_t cpu_to_be(uint32_t const h) noexcept { - return bswap_32(h); - } - inline uint32_t le_to_cpu(uint32_t const l) noexcept { - return l; - } - inline uint32_t cpu_to_le(uint32_t const h) noexcept { - return h; - } - - inline uint64_t be_to_cpu(uint64_t const & n) noexcept { - return bswap_64(n); - } - inline uint64_t cpu_to_be(uint64_t const & h) noexcept { - return bswap_64(h); - } - inline uint64_t le_to_cpu(uint64_t const & l) noexcept { - return l; - } - inline uint64_t cpu_to_le(uint64_t const & h) noexcept { - return h; - } - - constexpr uint128_t be_to_cpu(uint128_t const & n) noexcept { - return bswap(n); - } - constexpr uint128_t cpu_to_be(uint128_t const & h) noexcept { - return bswap(h); - } - constexpr uint128_t le_to_cpu(uint128_t const & l) noexcept { - return l; - } - constexpr uint128_t cpu_to_le(uint128_t const & h) noexcept { - return h; - } - - constexpr uint192_t be_to_cpu(uint192_t const & n) noexcept { - return bswap(n); - } - constexpr uint192_t cpu_to_be(uint192_t const & h) noexcept { - return bswap(h); - } - constexpr uint192_t le_to_cpu(uint192_t const & l) noexcept { - return l; - } - constexpr uint192_t cpu_to_le(uint192_t const & h) noexcept { - return h; - } - - constexpr uint256_t be_to_cpu(uint256_t const & n) noexcept { - return bswap(n); - } - constexpr uint256_t cpu_to_be(uint256_t const & h) noexcept { - return bswap(h); - } - constexpr uint256_t le_to_cpu(uint256_t const & l) noexcept { - return l; - } - constexpr uint256_t cpu_to_le(uint256_t const & h) noexcept { - return h; - } -#else - #error "Unexpected __BYTE_ORDER" -#endif - - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - inline void put_uint8(uint8_t * buffer, nsize_t const byte_offset, const uint8_t v) noexcept - { - *reinterpret_cast<uint8_t *>( buffer + byte_offset ) = v; - } - inline uint8_t get_uint8(uint8_t const * buffer, nsize_t const byte_offset) noexcept - { - return *reinterpret_cast<uint8_t const *>( buffer + byte_offset ); - } - inline int8_t get_int8(uint8_t const * buffer, nsize_t const byte_offset) noexcept - { - return *reinterpret_cast<int8_t const *>( buffer + byte_offset ); - } - - /** - * 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> __pack ( struct 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, 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: - */ - reinterpret_cast<packed_t<uint16_t>*>( buffer + byte_offset )->store = v; - } - inline 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: - */ - 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, 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 reinterpret_cast<const packed_t<uint16_t>*>( buffer + byte_offset )->store; - } - inline 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 reinterpret_cast<const packed_t<uint16_t>*>( buffer + byte_offset )->get(littleEndian); - } - - inline void put_uint32(uint8_t * buffer, nsize_t const byte_offset, const uint32_t v) noexcept - { - reinterpret_cast<packed_t<uint32_t>*>( buffer + byte_offset )->store = v; - } - inline void put_uint32(uint8_t * buffer, nsize_t const byte_offset, const uint32_t v, const bool littleEndian) noexcept - { - 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, nsize_t const byte_offset) noexcept - { - return reinterpret_cast<const packed_t<uint32_t>*>( buffer + byte_offset )->store; - } - inline uint32_t get_uint32(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept - { - return reinterpret_cast<const packed_t<uint32_t>*>( buffer + byte_offset )->get(littleEndian); - } - - inline void put_uint64(uint8_t * buffer, nsize_t const byte_offset, const uint64_t & v) noexcept - { - reinterpret_cast<packed_t<uint64_t>*>( buffer + byte_offset )->store = v; - } - inline void put_uint64(uint8_t * buffer, nsize_t const byte_offset, const uint64_t & v, const bool littleEndian) noexcept - { - 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, nsize_t const byte_offset) noexcept - { - return reinterpret_cast<const packed_t<uint64_t>*>( buffer + byte_offset )->store; - } - inline uint64_t get_uint64(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept - { - return reinterpret_cast<const packed_t<uint64_t>*>( buffer + byte_offset )->get(littleEndian); - } - - inline void put_uint128(uint8_t * buffer, nsize_t const byte_offset, const uint128_t & v) noexcept - { - reinterpret_cast<packed_t<uint128_t>*>( buffer + byte_offset )->store = v; - } - inline void put_uint128(uint8_t * buffer, nsize_t const byte_offset, const uint128_t & v, const bool littleEndian) noexcept - { - 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, nsize_t const byte_offset) noexcept - { - return reinterpret_cast<const packed_t<uint128_t>*>( buffer + byte_offset )->store; - } - inline uint128_t get_uint128(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept - { - return reinterpret_cast<const packed_t<uint128_t>*>( buffer + byte_offset )->get(littleEndian); - } - - inline void put_uint192(uint8_t * buffer, nsize_t const byte_offset, const uint192_t & v) noexcept - { - reinterpret_cast<packed_t<uint192_t>*>( buffer + byte_offset )->store = v; - } - inline void put_uint192(uint8_t * buffer, nsize_t const byte_offset, const uint192_t & v, const bool littleEndian) noexcept - { - reinterpret_cast<packed_t<uint192_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - } - inline uint192_t get_uint192(uint8_t const * buffer, nsize_t const byte_offset) noexcept - { - return reinterpret_cast<const packed_t<uint192_t>*>( buffer + byte_offset )->store; - } - inline uint192_t get_uint192(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept - { - return reinterpret_cast<const packed_t<uint192_t>*>( buffer + byte_offset )->get(littleEndian); - } - - inline void put_uint256(uint8_t * buffer, nsize_t const byte_offset, const uint256_t & v) noexcept - { - reinterpret_cast<packed_t<uint256_t>*>( buffer + byte_offset )->store = v; - } - inline void put_uint256(uint8_t * buffer, nsize_t const byte_offset, const uint256_t & v, const bool littleEndian) noexcept - { - reinterpret_cast<packed_t<uint256_t>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); - } - inline uint256_t get_uint256(uint8_t const * buffer, nsize_t const byte_offset) noexcept - { - return reinterpret_cast<const packed_t<uint256_t>*>( buffer + byte_offset )->store; - } - inline uint256_t get_uint256(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept - { - return reinterpret_cast<const packed_t<uint256_t>*>( buffer + byte_offset )->get(littleEndian); - } - inline void set_bit_uint32(const uint8_t nr, uint32_t &mask) { if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, E_FILE_LINE); } @@ -619,30 +181,6 @@ namespace jau { */ /** - * Returns a C++ String taken from buffer with maximum length of min(max_len, max_len). - * <p> - * The maximum length only delimits the string length and does not contain the EOS null byte. - * An EOS null byte will will be added. - * </p> - * <p> - * The source string within buffer is not required to contain an EOS null byte; - * </p> - */ - std::string get_string(const uint8_t *buffer, nsize_t const buffer_len, nsize_t const max_len) noexcept; - - /** trim in place */ - void trimInPlace(std::string &s) noexcept; - - /** trim copy */ - std::string trimCopy(const std::string &s) noexcept; - - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - /** * Merge the given 'uuid16' into a 'base_uuid' copy at the given little endian 'uuid16_le_octet_index' position. * <p> * The given 'uuid16' value will be added with the 'base_uuid' copy at the given position. @@ -686,258 +224,6 @@ namespace jau { */ uint128_t merge_uint128(uint32_t const uuid32, uint128_t const & base_uuid, nsize_t const uuid32_le_octet_index); - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - /** - * Produce a hexadecimal string representation of the given byte values. - * <p> - * If lsbFirst is true, orders LSB left -> MSB right, usual for byte streams. Result will not have a leading `0x`.<br> - * Otherwise orders MSB left -> LSB right, usual for readable integer values. Result will have a leading `0x`. - * </p> - * @param bytes pointer to the first byte to print, less offset - * @param offset offset to bytes pointer to the first byte to print - * @param length number of bytes to print - * @param lsbFirst true having the least significant byte printed first (lowest addressed byte to highest), - * otherwise have the most significant byte printed first (highest addressed byte to lowest). - * A leading `0x` will be prepended if `lsbFirst == false`. - * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. - * @return the hex-string representation of the data - */ - std::string bytesHexString(const uint8_t * bytes, const nsize_t offset, const nsize_t length, - const bool lsbFirst, const bool lowerCase=true) noexcept; - - /** - * Produce a hexadecimal string representation of the given byte value. - * @param dest the std::string reference destination to append - * @param value the byte value to represent - * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. - * @return the given std::string reference for chaining - */ - std::string& byteHexString(std::string& dest, const uint8_t value, const bool lowerCase) noexcept; - - /** - * Produce a lower-case hexadecimal string representation of the given uint8_t values. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint8HexString(const uint8_t v) noexcept { - return bytesHexString(reinterpret_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); - } - - /** - * Produce a lower-case hexadecimal string representation of the given uint16_t value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint16HexString(const uint16_t v) noexcept { - return bytesHexString(reinterpret_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); - } - - /** - * Produce a lower-case hexadecimal string representation of the given uint32_t value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint32HexString(const uint32_t v) noexcept { - return bytesHexString(reinterpret_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); - } - - /** - * Produce a lower-case hexadecimal string representation of the given uint64_t value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint64HexString(const uint64_t& v) noexcept { - return bytesHexString(reinterpret_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); - } - - /** - * Produce a lower-case hexadecimal string representation of the given 'void *' value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string aptrHexString(const void * v) noexcept { - return uint64HexString(reinterpret_cast<uint64_t>(v)); - } - - /** - * Produce a lower-case hexadecimal string representation of the given uint128_t value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint128HexString(const uint128_t& v) noexcept { - return bytesHexString(v.data, 0, sizeof(v.data), false /* lsbFirst */); - } - - /** - * Produce a lower-case hexadecimal string representation of the given uint256_t value. - * @param v the value - * @return the hex-string representation of the value - */ - inline std::string uint256HexString(const uint256_t& v) noexcept { - return bytesHexString(v.data, 0, sizeof(v.data), false /* lsbFirst */); - } - - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - /** - * Produce a decimal string representation of an integral integer value. - * @tparam T an integral integer type - * @param v the integral integer value - * @param separator if not 0, use as separation character, otherwise no separation characters are being used - * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. - * @return the string representation of the integral integer value - */ - template<class T> - std::string to_decimal_string(const T& v, const char separator=',', const nsize_t width=0) noexcept { - const snsize_t v_sign = jau::sign<T>(v); - const nsize_t digit10_count1 = jau::digits10<T>(v, v_sign, true /* sign_is_digit */); - const nsize_t digit10_count2 = v_sign < 0 ? digit10_count1 - 1 : digit10_count1; // less sign - - const nsize_t comma_count = 0 == separator ? 0 : ( digit10_count1 - 1 ) / 3; - const nsize_t net_chars = digit10_count1 + comma_count; - const nsize_t total_chars = std::max<nsize_t>(width, net_chars); - std::string res(total_chars, ' '); - - T n = v; - nsize_t char_iter = 0; - - for(nsize_t digit10_iter = 0; digit10_iter < digit10_count2 /* && char_iter < total_chars */; digit10_iter++ ) { - const int digit = v_sign < 0 ? invert_sign( n % 10 ) : n % 10; - n /= 10; - if( 0 < digit10_iter && 0 == digit10_iter % 3 ) { - res[total_chars-1-(char_iter++)] = separator; - } - res[total_chars-1-(char_iter++)] = '0' + digit; - } - if( v_sign < 0 /* && char_iter < total_chars */ ) { - res[total_chars-1-(char_iter++)] = '-'; - } - return res; - } - - /** - * Produce a decimal string representation of an int32_t value. - * @param v the value - * @param separator if not 0, use as separation character, otherwise no separation characters are being used - * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. - * @return the string representation of the value - */ - inline std::string int32DecString(const int32_t v, const char separator=',', const nsize_t width=0) noexcept { - return to_decimal_string<int32_t>(v, separator, width); - } - - /** - * Produce a decimal string representation of a uint32_t value. - * @param v the value - * @param separator if not 0, use as separation character, otherwise no separation characters are being used - * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. - * @return the string representation of the value - */ - inline std::string uint32DecString(const uint32_t v, const char separator=',', const nsize_t width=0) noexcept { - return to_decimal_string<uint32_t>(v, separator, width); - } - - /** - * Produce a decimal string representation of an int64_t value. - * @param v the value - * @param separator if not 0, use as separation character, otherwise no separation characters are being used - * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. - * @return the string representation of the value - */ - inline std::string int64DecString(const int64_t& v, const char separator=',', const nsize_t width=0) noexcept { - return to_decimal_string<int64_t>(v, separator, width); - } - - /** - * Produce a decimal string representation of a uint64_t value. - * @param v the value - * @param separator if not 0, use as separation character, otherwise no separation characters are being used - * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. - * @return the string representation of the value - */ - inline std::string uint64DecString(const uint64_t& v, const char separator=',', const nsize_t width=0) noexcept { - return to_decimal_string<uint64_t>(v, separator, width); - } - - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - template< class value_type, - std::enable_if_t< std::is_integral_v<value_type> || - std::is_floating_point_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) - { - return std::to_string(ref); - } - template< class value_type, - std::enable_if_t<!std::is_integral_v<value_type> && - !std::is_floating_point_v<value_type> && - std::is_pointer_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) - { - return aptrHexString((void*)ref); - } - - template< class value_type, - std::enable_if_t<!std::is_integral_v<value_type> && - !std::is_floating_point_v<value_type> && - !std::is_pointer_v<value_type> && - jau::has_toString_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) { - return ref.toString(); - } - - template< class value_type, - std::enable_if_t<!std::is_integral_v<value_type> && - !std::is_floating_point_v<value_type> && - !std::is_pointer_v<value_type> && - !jau::has_toString_v<value_type> && - jau::has_to_string_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) { - return ref.to_string(); - } - - template< class value_type, - std::enable_if_t<!std::is_integral_v<value_type> && - !std::is_floating_point_v<value_type> && - !std::is_pointer_v<value_type> && - !jau::has_toString_v<value_type> && - !jau::has_to_string_v<value_type> && - jau::has_member_of_pointer_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) { - return aptrHexString((void*)ref.operator->()); - } - - template< class value_type, - std::enable_if_t<!std::is_integral_v<value_type> && - !std::is_floating_point_v<value_type> && - !std::is_pointer_v<value_type> && - !jau::has_toString_v<value_type> && - !jau::has_to_string_v<value_type> && - !jau::has_member_of_pointer_v<value_type>, - bool> = true> - std::string to_string(const value_type & ref) { - (void)ref; - return "jau::to_string<T> not available for "+type_cue<value_type>::print("unknown", TypeTraitGroup::ALL); - } - } // namespace jau /** \example test_intdecstring01.cpp diff --git a/include/jau/byte_util.hpp b/include/jau/byte_util.hpp new file mode 100644 index 0000000..801805d --- /dev/null +++ b/include/jau/byte_util.hpp @@ -0,0 +1,753 @@ +/* + * Author: Sven Gothel <[email protected]> + * 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 <cstring> +#include <string> +#include <memory> +#include <cstdint> +#include <vector> +#include <type_traits> + +#include <jau/cpp_lang_util.hpp> +#include <jau/cpp_pragma.hpp> +#include <jau/packed_attribute.hpp> + +#include <jau/int_types.hpp> + +namespace jau { + + #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 + + 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 uint128_t bswap(uint128_t const & source) noexcept { + uint128_t dest; + uint8_t const * const s = source.data; + uint8_t * const d = dest.data; + for(nsize_t i=0; i<16; i++) { + d[i] = s[15-i]; + } + return dest; + } + + constexpr uint192_t bswap(uint192_t const & source) noexcept { + uint192_t dest; + uint8_t const * const s = source.data; + uint8_t * const d = dest.data; + for(nsize_t i=0; i<24; i++) { + d[i] = s[23-i]; + } + return dest; + } + + constexpr uint256_t bswap(uint256_t const & source) noexcept { + uint256_t dest; + uint8_t const * const s = source.data; + uint8_t * const d = dest.data; + for(nsize_t i=0; i<32; i++) { + d[i] = s[31-i]; + } + return dest; + } + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + // 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 + + namespace impl { + constexpr uint32_t get_host_order() noexcept { + if( 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<uint32_t, uint8_t[4]>( b ); + } else { + return 'ABCD'; // h->l: 41 42 43 44 = 'ABCD' hex ASCII code + } + } + } + + /** + * Endian identifier, indicating endianess of all scaler types. + * <p> + * Inspired by C++20 std::endian + * </p> + * <p> + * Corner case platforms currently not supported, + * i.e. unified endianess and mixed endianess. + * </p> + * <p> + * All endian API entries are of `constexpr` and hence evaluated at compile time.<br> + * Therefore, if-branches and expressions are also of `constexpr` and optimized 'away' at compile time.<br> + * This includes the `cpu_to_<endian>(..)` and `<endian>_to_cpu(..)` etc utility functions. + * </p> + */ + 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 jau::endian. + * @param v the jau::endian value + * @return the std::string representation + */ + constexpr_func_cxx20 std::string to_string(const endian& v) noexcept { + switch(v) { + case endian::little: return "little"; + case endian::big: return "big"; + case endian::pdp: return "pdb"; + case endian::honeywell: return "honeywell"; + case endian::undefined: return "undefined"; + } + return "unlisted"; + } + + /** + * Evaluates `true` if the given 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 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 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 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 .. + * <p> + * Since all endian definitions are of `constexpr`, code can simply use expressions of these + * for compile-time evaluation and optimization. A template `SFINAE` is not required. + * </p> + * @tparam Dummy_type just to make template `SFINAE` happy + */ + template <typename Dummy_type> struct has_endian_little : std::integral_constant<bool, endian::little == endian::native> {}; + + /** + * Value access of little-endian type trait for convenience .. + * <p> + * Since all endian definitions are of `constexpr`, code can simply use expressions of these + * for compile-time evaluation and optimization. A template `SFINAE` is not required. + * </p> + * @tparam Dummy_type just to make template `SFINAE` happy + */ + template <typename Dummy_type> inline constexpr bool has_endian_little_v = has_endian_little<Dummy_type>::value; + + /** + * A big-endian type trait for convenience .. + * <p> + * Since all endian definitions are of `constexpr`, code can simply use expressions of these + * for compile-time evaluation and optimization. A template `SFINAE` is not required. + * </p> + * @tparam Dummy_type just to make template `SFINAE` happy + */ + struct has_endian_big : std::integral_constant<bool, endian::big == endian::native> {}; + + /** + * Value access of big-endian type trait for convenience .. + * <p> + * Since all endian definitions are of `constexpr`, code can simply use expressions of these + * for compile-time evaluation and optimization. A template `SFINAE` is not required. + * </p> + * @tparam Dummy_type just to make template `SFINAE` happy + */ + inline constexpr bool has_endian_big_v = has_endian_big::value; + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + /** + * On the i386 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 <arpa/inet.h> + * + * Bluetooth is LSB or Little-Endian! + */ + + constexpr uint16_t be_to_cpu(uint16_t const n) noexcept { + static_assert(isLittleOrBigEndian()); // one static_assert is sufficient for whole compilation unit + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return bswap(n); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint16_t cpu_to_be(uint16_t const h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint16_t le_to_cpu(uint16_t const l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint16_t cpu_to_le(uint16_t const h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + + constexpr uint32_t be_to_cpu(uint32_t const n) noexcept { + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return n; + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint32_t cpu_to_be(uint32_t const h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint32_t le_to_cpu(uint32_t const l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint32_t cpu_to_le(uint32_t const h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + + constexpr uint64_t be_to_cpu(uint64_t const & n) noexcept { + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return n; + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint64_t cpu_to_be(uint64_t const & h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint64_t le_to_cpu(uint64_t const & l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + constexpr uint64_t cpu_to_le(uint64_t const & h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return 0; // unreachable -> static_assert(..) above + } + } + + constexpr uint128_t be_to_cpu(uint128_t const & n) noexcept { + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return n; + } else { + return uint128_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint128_t cpu_to_be(uint128_t const & h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return uint128_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint128_t le_to_cpu(uint128_t const & l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return uint128_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint128_t cpu_to_le(uint128_t const & h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return uint128_t(); // unreachable -> static_assert(..) above + } + } + + constexpr uint192_t be_to_cpu(uint192_t const & n) noexcept { + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return n; + } else { + return uint192_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint192_t cpu_to_be(uint192_t const & h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return uint192_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint192_t le_to_cpu(uint192_t const & l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return uint192_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint192_t cpu_to_le(uint192_t const & h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return uint192_t(); // unreachable -> static_assert(..) above + } + } + + constexpr uint256_t be_to_cpu(uint256_t const & n) noexcept { + if( isLittleEndian() ) { + return bswap(n); + } else if( isBigEndian() ) { + return n; + } else { + return uint256_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint256_t cpu_to_be(uint256_t const & h) noexcept { + if( isLittleEndian() ) { + return bswap(h); + } else if( isBigEndian() ) { + return h; + } else { + return uint256_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint256_t le_to_cpu(uint256_t const & l) noexcept { + if( isLittleEndian() ) { + return l; + } else if( isBigEndian() ) { + return bswap(l); + } else { + return uint256_t(); // unreachable -> static_assert(..) above + } + } + constexpr uint256_t cpu_to_le(uint256_t const & h) noexcept { + if( isLittleEndian() ) { + return h; + } else if( isBigEndian() ) { + return bswap(h); + } else { + return uint256_t(); // unreachable -> static_assert(..) above + } + + } + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + constexpr void put_uint8(uint8_t * buffer, nsize_t const byte_offset, const uint8_t v) noexcept + { + *pointer_cast<uint8_t *>( buffer + byte_offset ) = v; + } + constexpr uint8_t get_uint8(uint8_t const * buffer, nsize_t const byte_offset) noexcept + { + return *pointer_cast<uint8_t const *>( buffer + byte_offset ); + } + constexpr int8_t get_int8(uint8_t const * buffer, nsize_t const byte_offset) noexcept + { + return *pointer_cast<int8_t const *>( buffer + byte_offset ); + } + + /** + * 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> __pack ( struct packed_t { + T store; + constexpr T get(const bool littleEndian) const noexcept { return littleEndian ? le_to_cpu(store) : be_to_cpu(store); } + } ) ; + + 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; + } + 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); + } + 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; + } + 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 pointer_cast<const packed_t<uint16_t>*>( buffer + byte_offset )->get(littleEndian); + } + + 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; + } + 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); + } + 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; + } + constexpr uint32_t get_uint32(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<uint32_t>*>( buffer + byte_offset )->get(littleEndian); + } + + 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; + } + 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); + } + 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; + } + constexpr uint64_t get_uint64(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<uint64_t>*>( buffer + byte_offset )->get(littleEndian); + } + + 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; + } + 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); + } + 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; + } + constexpr uint128_t get_uint128(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<uint128_t>*>( buffer + byte_offset )->get(littleEndian); + } + + 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; + } + 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); + } + 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; + } + constexpr uint192_t get_uint192(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<uint192_t>*>( buffer + byte_offset )->get(littleEndian); + } + + 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; + } + 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); + } + 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; + } + constexpr uint256_t get_uint256(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<uint256_t>*>( buffer + byte_offset )->get(littleEndian); + } + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + template<typename T> + constexpr + typename std::enable_if_t< + std::is_standard_layout_v<T>, + void> + put_value(uint8_t * buffer, nsize_t const byte_offset, const T& v) noexcept + { + // reinterpret_cast<packed_t<T>*>( buffer + byte_offset )->store = v; + pointer_cast<packed_t<T>*>( buffer + byte_offset )->store = v; + } + + template<typename T> + constexpr + typename std::enable_if_t< + std::is_standard_layout_v<T>, + void> + put_value(uint8_t * buffer, nsize_t const byte_offset, const T& v, const bool littleEndian) noexcept + { + pointer_cast<packed_t<T>*>( buffer + byte_offset )->store = littleEndian ? cpu_to_le(v) : cpu_to_be(v); + } + + template<typename T> + constexpr + typename std::enable_if_t< + std::is_standard_layout_v<T>, + T> + get_value(uint8_t const * buffer, nsize_t const byte_offset) noexcept + { + return pointer_cast<const packed_t<T>*>( buffer + byte_offset )->store; + } + + template<typename T> + constexpr + typename std::enable_if_t< + std::is_standard_layout_v<T>, + T> + get_value(uint8_t const * buffer, nsize_t const byte_offset, const bool littleEndian) noexcept + { + return pointer_cast<const packed_t<T>*>( buffer + byte_offset )->get(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_ */ diff --git a/include/jau/cow_darray.hpp b/include/jau/cow_darray.hpp index b59c5d5..d7939d7 100644 --- a/include/jau/cow_darray.hpp +++ b/include/jau/cow_darray.hpp @@ -35,7 +35,7 @@ #include <condition_variable> #include <algorithm> -#include <jau/cpp_lang_macros.hpp> +#include <jau/cpp_lang_util.hpp> #include <jau/debug.hpp> #include <jau/darray.hpp> #include <jau/basic_types.hpp> diff --git a/include/jau/cow_iterator.hpp b/include/jau/cow_iterator.hpp index 4133c1d..f0dce50 100644 --- a/include/jau/cow_iterator.hpp +++ b/include/jau/cow_iterator.hpp @@ -33,7 +33,7 @@ #include <type_traits> #include <iostream> -#include <jau/cpp_lang_macros.hpp> +#include <jau/cpp_lang_util.hpp> #include <jau/debug.hpp> #include <jau/basic_types.hpp> diff --git a/include/jau/cow_vector.hpp b/include/jau/cow_vector.hpp index 6539e91..e59b349 100644 --- a/include/jau/cow_vector.hpp +++ b/include/jau/cow_vector.hpp @@ -37,7 +37,7 @@ #include <vector> #include <algorithm> -#include <jau/cpp_lang_macros.hpp> +#include <jau/cpp_lang_util.hpp> #include <jau/debug.hpp> #include <jau/basic_types.hpp> #include <jau/ordered_atomic.hpp> diff --git a/include/jau/cpp_lang_macros.hpp b/include/jau/cpp_lang_macros.hpp deleted file mode 100644 index 7d45922..0000000 --- a/include/jau/cpp_lang_macros.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020 Gothel Software e.K. - * - * 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 CPP_LANG_MACROS_HPP_ -#define CPP_LANG_MACROS_HPP_ - - /** - * <code>constexpr</code> enabled for C++20. - * <p> - * The alternative qualifier used is `inline`, - * as it is implied for for `constexpr` used for functions. - * </p> - */ -#if __cplusplus > 201703L - #define constexpr_func_cxx20 constexpr -#else - #define constexpr_func_cxx20 inline -#endif - - /** - * Used when designed to declare a function <code>constexpr</code>, - * but prohibited by its specific implementation. - * <p> - * The alternative qualifier used is `inline`, - * as it is implied for for `constexpr` used for functions. - * </p> - * <p> - * Here it but uses non-literal variables, such as std::lock_guard etc. - * As these can't be evaluated at compile time, the standard does - * not allow using <code>constexpr</code> here. - * </p> - * <p> - * Empty until standard defines otherwise. - * </p> - */ - #define constexpr_fun_non_literal_var inline - - /** - * Used when designed to declare a function <code>constexpr</code>, - * but prohibited by its specific implementation. - * <p> - * The alternative qualifier used is `inline`, - * as it is implied for for `constexpr` used for functions. - * </p> - * <p> - * Here it uses thread-safety related measures like atomic storage - * or mutex locks, which are non-literal variables and hence - * prohibit the use of <code>constexpr</code>. - * </p> - * @see constexpr_non_literal_var - */ - #define constexpr_func_atomic inline - - /** - * Set define if RTTI is enabled during compilation, - * implying its runtime availability. - * <pre> - * - clang ('__clang__') may have '__has_feature(cxx_rtti)' - * - g++ ('__GNUC__') may have '__GXX_RTTI' - * - msvc (_MSC_VER) may have: '_CPPRTTI' - * </pre> - */ - #if defined(__clang__) - #if __has_feature(cxx_rtti) - #define __cxx_rtti_available__ 1 - #endif - #else - #if defined(__GXX_RTTI) || defined(_CPPRTTI) - #define __cxx_rtti_available__ 1 - #endif - #endif - -#endif /* CPP_LANG_MACROS_HPP_ */ diff --git a/include/jau/cpp_lang_util.hpp b/include/jau/cpp_lang_util.hpp new file mode 100644 index 0000000..3a30f05 --- /dev/null +++ b/include/jau/cpp_lang_util.hpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020 Gothel Software e.K. + * + * 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 CPP_LANG_EXT_HPP_ +#define CPP_LANG_EXT_HPP_ + +#include <type_traits> + +namespace jau { + + /** + * <code>constexpr</code> enabled for C++20. + * <p> + * The alternative qualifier used is `inline`, + * as it is implied for for `constexpr` used for functions. + * </p> + */ +#if __cplusplus > 201703L + #define constexpr_func_cxx20 constexpr +#else + #define constexpr_func_cxx20 inline +#endif + + /** + * Used when designed to declare a function <code>constexpr</code>, + * but prohibited by its specific implementation. + * <p> + * The alternative qualifier used is `inline`, + * as it is implied for for `constexpr` used for functions. + * </p> + * <p> + * Here it but uses non-literal variables, such as std::lock_guard etc. + * As these can't be evaluated at compile time, the standard does + * not allow using <code>constexpr</code> here. + * </p> + * <p> + * Empty until standard defines otherwise. + * </p> + */ + #define constexpr_fun_non_literal_var inline + + /** + * Used when designed to declare a function <code>constexpr</code>, + * but prohibited by its specific implementation. + * <p> + * The alternative qualifier used is `inline`, + * as it is implied for for `constexpr` used for functions. + * </p> + * <p> + * Here it uses thread-safety related measures like atomic storage + * or mutex locks, which are non-literal variables and hence + * prohibit the use of <code>constexpr</code>. + * </p> + * @see constexpr_non_literal_var + */ + #define constexpr_func_atomic inline + + #if defined(__clang__) + #if __has_feature(cxx_rtti) + /** + * Set define if RTTI is enabled during compilation, + * implying its runtime availability. + * <pre> + * - clang ('__clang__') may have '__has_feature(cxx_rtti)' + * - g++ ('__GNUC__') may have '__GXX_RTTI' + * - msvc (_MSC_VER) may have: '_CPPRTTI' + * </pre> + */ + #define __cxx_rtti_available__ 1 + #endif + #else + #if defined(__GXX_RTTI) || defined(_CPPRTTI) + /** + * Set define if RTTI is enabled during compilation, + * implying its runtime availability. + * <pre> + * - clang ('__clang__') may have '__has_feature(cxx_rtti)' + * - g++ ('__GNUC__') may have '__GXX_RTTI' + * - msvc (_MSC_VER) may have: '_CPPRTTI' + * </pre> + */ + #define __cxx_rtti_available__ 1 + #endif + #endif + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + #if defined __has_builtin + #if __has_builtin(__builtin_bit_cast) + #define __has_builtin_bit_cast 1 + #endif + #endif + + /** + * Convenience type trait for `__has_builtin(__builtin_bit_cast)`. + * @tparam Dummy_type just to make template `SFINAE` happy + * @see jau::is_builtin_bit_cast_available() + * @see jau::bit_cast() + * @see jau::pointer_cast() + */ + template <typename Dummy_type> + struct has_builtin_bit_cast + #if defined __has_builtin_bit_cast + : std::true_type + #else + : std::false_type + #endif + {}; + /** + * Value access of has_builtin_bit_cast type trait for convenience .. + * @tparam Dummy_type just to make template `SFINAE` happy + * @see has_builtin_bit_cast + */ + template <typename Dummy_type> inline constexpr bool has_builtin_bit_cast_v = has_builtin_bit_cast<Dummy_type>::value; + + #if !defined __has_builtin_bit_cast + /** + * Dummy definition in the absence of this builtin function + * as required to have this compilation unit compile clean. + * @param Dest_type the target type + * @param Value_arg the source value argument + */ + #define __builtin_bit_cast(Dest_type,Value_arg) 0 + #endif + + namespace impl { + template<class Dummy_type> + constexpr bool has_builtin_bit_cast_impl( + std::enable_if_t< has_builtin_bit_cast_v<Dummy_type>, bool> = true ) noexcept + { + return true; + } + + template<class Dummy_type> + constexpr bool has_builtin_bit_cast_impl( + std::enable_if_t< !has_builtin_bit_cast_v<Dummy_type>, bool> = true ) noexcept + { + return false; + } + } + + /** + * Query whether `__builtin_bit_cast(Dest_type, arg)` is available, using jau::has_builtin_bit_cast. + * + * - - - - - - - - - - - - - - - + * + * Availability of `__builtin_bit_cast(Dest_type, arg)` + * + * Reflecting my manual platform tests using `test_basictypeconv.cpp` + * + * Compiler | Version | Architecture | Available | + * :--------- | -------: | :------------------ | :-------- | + * GCC | 8.3.0 | amd64, arm64, arm32 | no | + * GCC | 10.2.1 | amd64 | no | + * clang | 9.0.1 | amd64, arm64 | yes | + * clang | 11.0.1 | amd64 | yes | + * + * @return `true` if query subject is available, otherwise not. + * @see has_builtin_bit_cast + * @see bit_cast() + * @see pointer_cast() + */ + constexpr bool is_builtin_bit_cast_available() noexcept { + return impl::has_builtin_bit_cast_impl<bool>(); + } + + /** + * C++20 `bit_cast<>(arg)` implementation for C++17. + * <p> + * Functional if is_builtin_bit_cast_available() evaluates `true`. + * </p> + * @tparam Dest the target type + * @tparam Source the source argument type + * @param src the value to convert to Dest type + * @return the converted Dest type value + * @see jau::has_builtin_bit_cast + * @see is_builtin_bit_cast_available() + * @see pointer_cast() + */ + template <class Dest, class Source> + constexpr + typename std::enable_if_t< + sizeof(Dest) == sizeof(Source) && + std::is_trivially_copyable_v<Dest> && + std::is_trivially_copyable_v<Source>, + Dest> + bit_cast(const Source& src) noexcept + { + if( is_builtin_bit_cast_available() ) { + return __builtin_bit_cast(Dest, src); + } else { + (void)src; + return 0; + } + } + + /** + * A `constexpr` pointer cast implementation for C++17, + * inspired by C++20 `bit_cast<>(arg)`. + * <p> + * If is_builtin_bit_cast_available() evaluates `true`, + * implementation uses `__builtin_bit_cast(Dest, src)`.<br> + * + * Otherwise a simple `reinterpret_cast<Dest>(src)` is utilized, + * which officially is questionable to deliver a `constexpr`. + * </p> + * @tparam Dest the target pointer type + * @tparam Source the source pointer argument type + * @param src the pointer to convert to Dest pointer type + * @return the converted Dest pointer type value + * @see jau::has_builtin_bit_cast + * @see is_builtin_bit_cast_available() + * @see bit_cast() + */ + template <class Dest, class Source> + constexpr + typename std::enable_if_t< + sizeof(Dest) == sizeof(Source) && + std::is_pointer_v<Source> && + std::is_pointer_v<Dest>, + Dest> + pointer_cast(const Source& src) noexcept + { + if( is_builtin_bit_cast_available() ) { + return __builtin_bit_cast(Dest, src); + } else { + // not 'really' constexpr .. oops, working though + return reinterpret_cast<Dest>(src); + } + } + + +} // namespace jau + +#endif /* CPP_LANG_EXT_HPP_ */ diff --git a/include/jau/cpp_pragma.hpp b/include/jau/cpp_pragma.hpp new file mode 100644 index 0000000..771d261 --- /dev/null +++ b/include/jau/cpp_pragma.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 Gothel Software e.K. + * + * 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 CPP_PRAGMA_HPP_ +#define CPP_PRAGMA_HPP_ + +namespace jau { + +#if defined(_MSC_VER) + #define PRAGMA_DISABLE_WARNING_PUSH __pragma(warning( push )) + #define PRAGMA_DISABLE_WARNING_POP __pragma(warning( pop )) + #define PRAGMA_DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber )) + + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER PRAGMA_DISABLE_WARNING(4100) + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FUNCTION PRAGMA_DISABLE_WARNING(4505) + #define PRAGMA_DISABLE_WARNING_CPP + #define PRAGMA_DISABLE_WARNING_MULTICHAR + +#elif defined(__GNUC__) || defined(__clang__) + #define DO_PRAGMA(X) _Pragma(#X) + #define PRAGMA_DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) + #define PRAGMA_DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) + #define PRAGMA_DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName) + #define PRAGMA_WARNING_ONLY(warningName) DO_PRAGMA(GCC diagnostic warning #warningName) + + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER PRAGMA_DISABLE_WARNING(-Wunused-parameter) + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FUNCTION PRAGMA_DISABLE_WARNING(-Wunused-function) + #define PRAGMA_DISABLE_WARNING_CPP PRAGMA_DISABLE_WARNING(-Wcpp) + #define PRAGMA_DISABLE_WARNING_MULTICHAR PRAGMA_DISABLE_WARNING(-Wmultichar) + +#else + #define PRAGMA_DISABLE_WARNING_PUSH + #define PRAGMA_DISABLE_WARNING_POP + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER + #define PRAGMA_DISABLE_WARNING_UNREFERENCED_FUNCTION + #define PRAGMA_DISABLE_WARNING_CPP + #define PRAGMA_DISABLE_WARNING_MULTICHAR +#endif + +} // namespace jau + +#endif /* CPP_PRAGMA_HPP_ */ diff --git a/include/jau/int_math.hpp b/include/jau/int_math.hpp index cd37795..1d0e926 100644 --- a/include/jau/int_math.hpp +++ b/include/jau/int_math.hpp @@ -26,38 +26,12 @@ #ifndef JAU_BASIC_INT_MATH_HPP_ #define JAU_BASIC_INT_MATH_HPP_ +#include <cstdint> #include <cmath> -namespace jau { - /** - // ************************************************* - // ************************************************* - // ************************************************* - */ - - /** - * Natural 'size_t' alternative using 'unsigned int' as its natural sized type. - * <p> - * The leading 'n' stands for natural. - * </p> - * <p> - * This is a compromise to indicate intend, - * but to avoid handling a multiple sized 'size_t' footprint where not desired. - * </p> - */ - typedef unsigned int nsize_t; +#include <jau/int_types.hpp> - /** - * Natural 'ssize_t' alternative using 'signed int' as its natural sized type. - * <p> - * The leading 'n' stands for natural. - * </p> - * <p> - * This is a compromise to indicate intend, - * but to avoid handling a multiple sized 'ssize_t' footprint where not desired. - * </p> - */ - typedef signed int snsize_t; +namespace jau { /** // ************************************************* diff --git a/include/jau/int_types.hpp b/include/jau/int_types.hpp new file mode 100644 index 0000000..edebcb6 --- /dev/null +++ b/include/jau/int_types.hpp @@ -0,0 +1,133 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2021 Gothel Software e.K. + * + * 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_INT_TYPES_HPP_ +#define JAU_INT_TYPES_HPP_ + +#include <cstdint> +#include <cstring> + +#include <jau/packed_attribute.hpp> + +namespace jau { + /** + * Natural 'size_t' alternative using `uint_fast32_t` as its natural sized type. + * <p> + * The leading 'n' stands for natural. + * </p> + * <p> + * This is a compromise to indicate intend, + * but to avoid handling a multiple sized `size_t` footprint where not desired. + * </p> + */ + typedef uint_fast32_t nsize_t; + + /** + * Natural 'ssize_t' alternative using `int_fast32_t` as its natural sized type. + * <p> + * The leading 'n' stands for natural. + * </p> + * <p> + * This is a compromise to indicate intend, + * but to avoid handling a multiple sized `ssize_t` footprint where not desired. + * </p> + */ + typedef int_fast32_t snsize_t; + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + __pack( struct uint128_t { + uint8_t data[16]; + + constexpr uint128_t() noexcept : data{0} {} + constexpr uint128_t(const uint128_t &o) noexcept = default; + uint128_t(uint128_t &&o) noexcept = default; + constexpr uint128_t& operator=(const uint128_t &o) noexcept = default; + uint128_t& operator=(uint128_t &&o) noexcept = default; + + void clear() noexcept { bzero(data, sizeof(data)); } + + constexpr bool operator==(uint128_t const &o) const noexcept { + if( this == &o ) { + return true; + } + return !std::memcmp(data, o.data, sizeof(data)); + } + constexpr bool operator!=(uint128_t const &o) const noexcept + { return !(*this == o); } + } ) ; + + __pack( struct uint192_t { + uint8_t data[24]; + + constexpr uint192_t() noexcept : data{0} {} + constexpr uint192_t(const uint192_t &o) noexcept = default; + uint192_t(uint192_t &&o) noexcept = default; + constexpr uint192_t& operator=(const uint192_t &o) noexcept = default; + uint192_t& operator=(uint192_t &&o) noexcept = default; + + void clear() noexcept { bzero(data, sizeof(data)); } + + constexpr bool operator==(uint192_t const &o) const noexcept { + if( this == &o ) { + return true; + } + return !std::memcmp(data, o.data, sizeof(data)); + } + constexpr bool operator!=(uint192_t const &o) const noexcept + { return !(*this == o); } + } ); + + __pack( struct uint256_t { + uint8_t data[32]; + + constexpr uint256_t() noexcept : data{0} {} + constexpr uint256_t(const uint256_t &o) noexcept = default; + uint256_t(uint256_t &&o) noexcept = default; + constexpr uint256_t& operator=(const uint256_t &o) noexcept = default; + uint256_t& operator=(uint256_t &&o) noexcept = default; + + void clear() noexcept { bzero(data, sizeof(data)); } + + constexpr bool operator==(uint256_t const &o) const noexcept { + if( this == &o ) { + return true; + } + return !std::memcmp(data, o.data, sizeof(data)); + } + constexpr bool operator!=(uint256_t const &o) const noexcept + { return !(*this == o); } + } ); + +} // namespace jau + +/** \example test_basictypeconv.cpp + * This C++ unit test validates the jau::bswap and get/set value implementation + */ + +#endif /* JAU_INT_TYPES_HPP_ */ diff --git a/include/jau/string_util.hpp b/include/jau/string_util.hpp new file mode 100644 index 0000000..54578a8 --- /dev/null +++ b/include/jau/string_util.hpp @@ -0,0 +1,329 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2021 Gothel Software e.K. + * + * 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_STRING_UTIL_HPP_ +#define JAU_STRING_UTIL_HPP_ + +#include <cstdint> +#include <cstring> +#include <string> +#include <memory> +#include <type_traits> + +#include <jau/cpp_lang_util.hpp> +#include <jau/packed_attribute.hpp> +#include <jau/type_traits_queries.hpp> + +#include <jau/int_types.hpp> +#include <jau/int_math.hpp> + +namespace jau { + + /** + * Returns a C++ String taken from buffer with maximum length of min(max_len, max_len). + * <p> + * The maximum length only delimits the string length and does not contain the EOS null byte. + * An EOS null byte will will be added. + * </p> + * <p> + * The source string within buffer is not required to contain an EOS null byte; + * </p> + */ + std::string get_string(const uint8_t *buffer, nsize_t const buffer_len, nsize_t const max_len) noexcept; + + /** trim in place */ + void trimInPlace(std::string &s) noexcept; + + /** trim copy */ + std::string trimCopy(const std::string &s) noexcept; + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + /** + * Produce a hexadecimal string representation of the given byte values. + * <p> + * If lsbFirst is true, orders LSB left -> MSB right, usual for byte streams. Result will not have a leading `0x`.<br> + * Otherwise orders MSB left -> LSB right, usual for readable integer values. Result will have a leading `0x`. + * </p> + * @param bytes pointer to the first byte to print, less offset + * @param offset offset to bytes pointer to the first byte to print + * @param length number of bytes to print + * @param lsbFirst true having the least significant byte printed first (lowest addressed byte to highest), + * otherwise have the most significant byte printed first (highest addressed byte to lowest). + * A leading `0x` will be prepended if `lsbFirst == false`. + * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. + * @return the hex-string representation of the data + */ + std::string bytesHexString(const uint8_t * bytes, const nsize_t offset, const nsize_t length, + const bool lsbFirst, const bool lowerCase=true) noexcept; + + /** + * Produce a hexadecimal string representation of the given byte value. + * @param dest the std::string reference destination to append + * @param value the byte value to represent + * @param lowerCase true to use lower case hex-chars, otherwise capital letters are being used. + * @return the given std::string reference for chaining + */ + std::string& byteHexString(std::string& dest, const uint8_t value, const bool lowerCase) noexcept; + + /** + * Produce a lower-case hexadecimal string representation of the given uint8_t values. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint8HexString(const uint8_t v) noexcept { + return bytesHexString(pointer_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); + } + + /** + * Produce a lower-case hexadecimal string representation of the given uint16_t value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint16HexString(const uint16_t v) noexcept { + return bytesHexString(pointer_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); + } + + /** + * Produce a lower-case hexadecimal string representation of the given uint32_t value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint32HexString(const uint32_t v) noexcept { + return bytesHexString(pointer_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); + } + + /** + * Produce a lower-case hexadecimal string representation of the given uint64_t value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint64HexString(const uint64_t& v) noexcept { + return bytesHexString(pointer_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); + } + + /** + * Produce a lower-case hexadecimal string representation of the given 'void *' value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string aptrHexString(const void * v) noexcept { + return uint64HexString(reinterpret_cast<uint64_t>(v)); + } + + /** + * Produce a lower-case hexadecimal string representation of the given uint128_t value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint128HexString(const uint128_t& v) noexcept { + return bytesHexString(v.data, 0, sizeof(v.data), false /* lsbFirst */); + } + + /** + * Produce a lower-case hexadecimal string representation of the given uint256_t value. + * @param v the value + * @return the hex-string representation of the value + */ + inline std::string uint256HexString(const uint256_t& v) noexcept { + return bytesHexString(v.data, 0, sizeof(v.data), false /* lsbFirst */); + } + + template<typename T> + inline + typename std::enable_if_t< + std::is_standard_layout_v<T>, + std::string> + to_hex_string(T const & v) noexcept + { + return bytesHexString(pointer_cast<const uint8_t*>(&v), 0, sizeof(v), false /* lsbFirst */); + } + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + /** + * Produce a decimal string representation of an integral integer value. + * @tparam T an integral integer type + * @param v the integral integer value + * @param separator if not 0, use as separation character, otherwise no separation characters are being used + * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. + * @return the string representation of the integral integer value + */ + template<class T> + std::string to_decimal_string(const T& v, const char separator=',', const nsize_t width=0) noexcept { + const snsize_t v_sign = jau::sign<T>(v); + const nsize_t digit10_count1 = jau::digits10<T>(v, v_sign, true /* sign_is_digit */); + const nsize_t digit10_count2 = v_sign < 0 ? digit10_count1 - 1 : digit10_count1; // less sign + + const nsize_t comma_count = 0 == separator ? 0 : ( digit10_count1 - 1 ) / 3; + const nsize_t net_chars = digit10_count1 + comma_count; + const nsize_t total_chars = std::max<nsize_t>(width, net_chars); + std::string res(total_chars, ' '); + + T n = v; + nsize_t char_iter = 0; + + for(nsize_t digit10_iter = 0; digit10_iter < digit10_count2 /* && char_iter < total_chars */; digit10_iter++ ) { + const int digit = v_sign < 0 ? invert_sign( n % 10 ) : n % 10; + n /= 10; + if( 0 < digit10_iter && 0 == digit10_iter % 3 ) { + res[total_chars-1-(char_iter++)] = separator; + } + res[total_chars-1-(char_iter++)] = '0' + digit; + } + if( v_sign < 0 /* && char_iter < total_chars */ ) { + res[total_chars-1-(char_iter++)] = '-'; + } + return res; + } + + /** + * Produce a decimal string representation of an int32_t value. + * @param v the value + * @param separator if not 0, use as separation character, otherwise no separation characters are being used + * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. + * @return the string representation of the value + */ + inline std::string int32DecString(const int32_t v, const char separator=',', const nsize_t width=0) noexcept { + return to_decimal_string<int32_t>(v, separator, width); + } + + /** + * Produce a decimal string representation of a uint32_t value. + * @param v the value + * @param separator if not 0, use as separation character, otherwise no separation characters are being used + * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. + * @return the string representation of the value + */ + inline std::string uint32DecString(const uint32_t v, const char separator=',', const nsize_t width=0) noexcept { + return to_decimal_string<uint32_t>(v, separator, width); + } + + /** + * Produce a decimal string representation of an int64_t value. + * @param v the value + * @param separator if not 0, use as separation character, otherwise no separation characters are being used + * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. + * @return the string representation of the value + */ + inline std::string int64DecString(const int64_t& v, const char separator=',', const nsize_t width=0) noexcept { + return to_decimal_string<int64_t>(v, separator, width); + } + + /** + * Produce a decimal string representation of a uint64_t value. + * @param v the value + * @param separator if not 0, use as separation character, otherwise no separation characters are being used + * @param width the minimum number of characters to be printed. Add padding with blank space if result is shorter. + * @return the string representation of the value + */ + inline std::string uint64DecString(const uint64_t& v, const char separator=',', const nsize_t width=0) noexcept { + return to_decimal_string<uint64_t>(v, separator, width); + } + + /** + // ************************************************* + // ************************************************* + // ************************************************* + */ + + template< class value_type, + std::enable_if_t< std::is_integral_v<value_type> || + std::is_floating_point_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) + { + return std::to_string(ref); + } + template< class value_type, + std::enable_if_t<!std::is_integral_v<value_type> && + !std::is_floating_point_v<value_type> && + std::is_pointer_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) + { + return aptrHexString((void*)ref); + } + + template< class value_type, + std::enable_if_t<!std::is_integral_v<value_type> && + !std::is_floating_point_v<value_type> && + !std::is_pointer_v<value_type> && + jau::has_toString_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) { + return ref.toString(); + } + + template< class value_type, + std::enable_if_t<!std::is_integral_v<value_type> && + !std::is_floating_point_v<value_type> && + !std::is_pointer_v<value_type> && + !jau::has_toString_v<value_type> && + jau::has_to_string_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) { + return ref.to_string(); + } + + template< class value_type, + std::enable_if_t<!std::is_integral_v<value_type> && + !std::is_floating_point_v<value_type> && + !std::is_pointer_v<value_type> && + !jau::has_toString_v<value_type> && + !jau::has_to_string_v<value_type> && + jau::has_member_of_pointer_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) { + return aptrHexString((void*)ref.operator->()); + } + + template< class value_type, + std::enable_if_t<!std::is_integral_v<value_type> && + !std::is_floating_point_v<value_type> && + !std::is_pointer_v<value_type> && + !jau::has_toString_v<value_type> && + !jau::has_to_string_v<value_type> && + !jau::has_member_of_pointer_v<value_type>, + bool> = true> + std::string to_string(const value_type & ref) { + (void)ref; + return "jau::to_string<T> not available for "+type_cue<value_type>::print("unknown", TypeTraitGroup::ALL); + } + +} // namespace jau + +/** \example test_intdecstring01.cpp + * This C++ unit test validates the jau::to_decimal_string implementation + */ + +#endif /* JAU_STRING_UTIL_HPP_ */ diff --git a/include/jau/type_traits_queries.hpp b/include/jau/type_traits_queries.hpp index 42fdec8..d2db3db 100644 --- a/include/jau/type_traits_queries.hpp +++ b/include/jau/type_traits_queries.hpp @@ -32,20 +32,9 @@ #include <cstring> #include <string> -#include <memory> #include <cstdint> -#include <vector> #include <type_traits> -extern "C" { - #include <endian.h> - #include <byteswap.h> -} - -#include <jau/int_math.hpp> -#include <jau/cpp_lang_macros.hpp> -#include <jau/packed_attribute.hpp> - namespace jau { // Author: Sven Gothel |