diff --git a/docs/types/unaligned-type.md b/docs/types/unaligned-type.md index 28c9f85f..8ae1378f 100644 --- a/docs/types/unaligned-type.md +++ b/docs/types/unaligned-type.md @@ -459,3 +459,317 @@ ETL_CONSTEXPR14 bool operator !=(T lhs, const unaligned_type& rhs) **Description** Inequality operator. Removed: `20.39.3` + +--- + +# Arbitrary width integers + +{{< callout type="info">}} + From: `20.50.0` +{{< /callout >}} + +`etl::unaligned_type` is templated on a fundamental type, so it can only store +integers whose width is that of a standard integer type. For protocols that use +odd widths, such as 24 or 48 bit integers, the following types are templated on +the **number of bytes of storage** instead. + +```cpp +template +class unaligned_uint_type + +template +class unaligned_int_type +``` + +Where `Size_` is the number of bytes of storage and `Endian_` is +`etl::endian::big` or `etl::endian::little`. + +`unaligned_uint_type` stores an unsigned integer. +`unaligned_int_type` stores a signed, two's complement integer, which is sign +extended when read. + +The value is interfaced to via `value_type`: the smallest standard integer type +that can hold `Size_` bytes. For example, `unaligned_uint_type<3U, ...>` has a +`value_type` of `uint32_t`. + +Values that do not fit in `Size_` bytes are truncated, in the same way as a +narrowing `static_cast`. + +Marked as `packed` for MSVC, GCC and Clang. + +## Constants +`int Endian` + The endianness of the type. +`size_t Size` + The size, in bytes, of the type. +`value_type Min_Value` + The lowest value that may be stored. +`value_type Max_Value` + The highest value that may be stored. + +## Predefined types + +The 40, 48 and 56 bit types are only defined if `ETL_USING_64BIT_TYPES`. +The host order types are only defined if `ETL_HAS_CONSTEXPR_ENDIANNESS`. + +### Little Endian +```cpp +le_uint24_t +le_int24_t +le_uint40_t ETL_USING_64BIT_TYPES +le_int40_t ETL_USING_64BIT_TYPES +le_uint48_t ETL_USING_64BIT_TYPES +le_int48_t ETL_USING_64BIT_TYPES +le_uint56_t ETL_USING_64BIT_TYPES +le_int56_t ETL_USING_64BIT_TYPES +``` + +### Big Endian +```cpp +be_uint24_t +be_int24_t +be_uint40_t ETL_USING_64BIT_TYPES +be_int40_t ETL_USING_64BIT_TYPES +be_uint48_t ETL_USING_64BIT_TYPES +be_int48_t ETL_USING_64BIT_TYPES +be_uint56_t ETL_USING_64BIT_TYPES +be_int56_t ETL_USING_64BIT_TYPES +``` + +### Network Order +Synonyms for Big Endian. +```cpp +using net_uint24_t = be_uint24_t +using net_int24_t = be_int24_t +using net_uint40_t = be_uint40_t +using net_int40_t = be_int40_t +using net_uint48_t = be_uint48_t +using net_int48_t = be_int48_t +using net_uint56_t = be_uint56_t +using net_int56_t = be_int56_t +``` + +### Host order +```cpp +host_uint24_t +host_int24_t +host_uint40_t +host_int40_t +host_uint48_t +host_int48_t +host_uint56_t +host_int56_t +``` + +## Constructors + +```cpp +unaligned_uint_type() +unaligned_int_type() +``` +**Description** +Constructs an uninitialised object. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(value_type value) +ETL_CONSTEXPR14 unaligned_int_type(value_type value) +``` +**Description** +Constructs with the supplied value. +Values that do not fit in `Size` bytes are truncated. + +--- + +```cpp +unaligned_uint_type(const void* address) +unaligned_int_type(const void* address) +``` +**Description** +Constructs from the `Size` bytes at the address. + +--- + +```cpp +unaligned_uint_type(const void* address, size_t buffer_size) +unaligned_int_type(const void* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size +results in a read beyond the end of the buffer. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address) +ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address) +``` +**Description** +Constructs from the `Size` bytes at the address. +Unlike the `const void*` overload, this requires no `reinterpret_cast` and so +may be used in a constexpr context. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address, size_t buffer_size) +ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. + +--- + +```cpp +template +unaligned_uint_type(const unaligned_uint_type& other) +``` +**Description** +Constructs from another type of the same size. +The endianness is converted, if necessary. + +## Member functions + +The storage interface (`data()`, `size()`, the iterators and `operator[]`) is +the same as that of `etl::unaligned_type`. + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type& operator =(value_type value) +``` +**Description** +Assignment operator. + +--- + +```cpp +ETL_CONSTEXPR14 operator value_type() const +``` +**Description** +Conversion operator. +For `unaligned_int_type` the stored value is sign extended. + +--- + +```cpp +ETL_CONSTEXPR14 value_type value() const +``` +**Description** +Gets the value. +For `unaligned_int_type` the stored value is sign extended. + +--- + +```cpp +static ETL_CONSTEXPR14 value_type value_from(const_pointer address) +``` +**Description** +Gets the value of the `Size` bytes at the address. +Unlike construction from an address, the bytes are not copied to storage first. + +--- + +```cpp +template +ETL_CONSTEXPR14 unaligned_uint_type& operator =(const unaligned_uint_type& other) +``` +**Description** +Assignment operator from other endianness. + +--- + +# Arbitrary width integers with an external buffer + +{{< callout type="info">}} + From: `20.50.0` +{{< /callout >}} + +```cpp +template +class unaligned_uint_type_ext + +template +class unaligned_int_type_ext +``` + +These are the equivalents of `unaligned_uint_type` and `unaligned_int_type` +that do not own their storage, but reference an externally supplied buffer. +They have the same `value_type`, constants and value interface. + +The default constructor is deleted: the storage must always be supplied. + +### Predefined types +As for the internal storage versions, but with an `_ext_t` suffix. +```cpp +le_uint24_ext_t be_uint24_ext_t net_uint24_ext_t host_uint24_ext_t +le_int24_ext_t be_int24_ext_t net_int24_ext_t host_int24_ext_t +le_uint40_ext_t be_uint40_ext_t net_uint40_ext_t host_uint40_ext_t +le_int40_ext_t be_int40_ext_t net_int40_ext_t host_int40_ext_t +le_uint48_ext_t be_uint48_ext_t net_uint48_ext_t host_uint48_ext_t +le_int48_ext_t be_int48_ext_t net_int48_ext_t host_int48_ext_t +le_uint56_ext_t be_uint56_ext_t net_uint56_ext_t host_uint56_ext_t +le_int56_ext_t be_int56_ext_t net_int56_ext_t host_int56_ext_t +``` + +## Constructors + +```cpp +unaligned_uint_type_ext() ETL_DELETE +``` +**Description** +Deleted. The storage must always be supplied. + +--- + +```cpp +unaligned_uint_type_ext(pointer storage) +``` +**Description** +Constructs, referencing the supplied storage. +The storage is not modified. + +--- + +```cpp +unaligned_uint_type_ext(value_type value, pointer storage) +``` +**Description** +Constructs, referencing the supplied storage, and writes the value to it. + +--- + +```cpp +template +unaligned_uint_type_ext(const unaligned_uint_type_ext& other, pointer storage) +``` +**Description** +Constructs, referencing the supplied storage, and copies the value of `other` +to it, converting the endianness if necessary. +**Note** +The storage of `other` must either be the same storage as that supplied, or +completely separate. Partially overlapping storage is undefined behaviour. + +## Member functions + +```cpp +void set_storage(pointer storage) +``` +**Description** +Sets the storage for the type. +The storage is not modified. + +--- + +```cpp +template +unaligned_uint_type_ext& operator =(const unaligned_uint_type_ext& other) +``` +**Description** +Assignment operator from other endianness. +**Note** +The storage of `other` must either be the same storage as this object's, or +completely separate. Partially overlapping storage is undefined behaviour. diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 6d8ef74c..34c945b0 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -43,6 +43,7 @@ SOFTWARE. #include "endianness.h" #include "exception.h" #include "file_error_numbers.h" +#include "integral_limits.h" #include "iterator.h" #include "type_traits.h" @@ -463,12 +464,40 @@ namespace etl value = value_from(store); } + //******************************* + /// \note 'src' and 'dst' must either be equal or refer to completely + /// separate storage. Partially overlapping storage is undefined + /// behaviour. //******************************* static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - for (size_t i = 0UL; i < Size_; ++i) + if (Endian_ == endian_src) { - dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; + if (src != dst) + { + for (size_t i = 0UL; i < Size_; ++i) + { + dst[i] = src[i]; + } + } + } + else + { + // Swap mirrored bytes in pairs, so that the reversal is correct even + // when 'src' and 'dst' are the same storage. + for (size_t i = 0UL; i < (Size_ / 2UL); ++i) + { + const storage_type low = src[i]; + const storage_type high = src[Size_ - 1UL - i]; + + dst[i] = high; + dst[Size_ - 1UL - i] = low; + } + + if ((Size_ % 2UL) != 0UL) + { + dst[Size_ / 2UL] = src[Size_ / 2UL]; + } } } }; @@ -602,12 +631,39 @@ namespace etl // This is pure byte manipulation (copy + optional reversal), with no // floating point arithmetic involved, so it is always constexpr-capable // (C++14 and later), regardless of bit_cast/builtin availability. + /// \note 'src' and 'dst' must either be equal or refer to completely + /// separate storage. Partially overlapping storage is undefined + /// behaviour. //******************************* static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - for (size_t i = 0UL; i < Size_; ++i) + if (Endian_ == endian_src) { - dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; + if (src != dst) + { + for (size_t i = 0UL; i < Size_; ++i) + { + dst[i] = src[i]; + } + } + } + else + { + // Swap mirrored bytes in pairs, so that the reversal is correct even + // when 'src' and 'dst' are the same storage. + for (size_t i = 0UL; i < (Size_ / 2UL); ++i) + { + const storage_type low = src[i]; + const storage_type high = src[Size_ - 1UL - i]; + + dst[i] = high; + dst[Size_ - 1UL - i] = low; + } + + if ((Size_ % 2UL) != 0UL) + { + dst[Size_ / 2UL] = src[Size_ / 2UL]; + } } } }; @@ -787,6 +843,475 @@ namespace etl template ETL_CONSTANT size_t unaligned_type::Size; + namespace private_unaligned_type + { + //************************************************************************* + /// The unsigned integer type used to interface to an unaligned unsigned + /// integer of 'Size' bytes. + /// The smallest standard unsigned integer type that can hold all of the + /// bytes is selected. + //************************************************************************* + template + struct interface_uint; + + template <> + struct interface_uint<1U> + { + typedef uint8_t type; + }; + + template <> + struct interface_uint<2U> + { + typedef uint16_t type; + }; + + template <> + struct interface_uint<3U> + { + typedef uint32_t type; + }; + + template <> + struct interface_uint<4U> + { + typedef uint32_t type; + }; + +#if ETL_USING_64BIT_TYPES + template <> + struct interface_uint<5U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<6U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<7U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<8U> + { + typedef uint64_t type; + }; +#endif + + //************************************************************************* + /// The signed integer type used to interface to an unaligned signed + /// integer of 'Size' bytes. + /// The smallest standard signed integer type that can hold all of the + /// bytes is selected. + //************************************************************************* + template + struct interface_int; + + template <> + struct interface_int<1U> + { + typedef int8_t type; + }; + + template <> + struct interface_int<2U> + { + typedef int16_t type; + }; + + template <> + struct interface_int<3U> + { + typedef int32_t type; + }; + + template <> + struct interface_int<4U> + { + typedef int32_t type; + }; + +#if ETL_USING_64BIT_TYPES + template <> + struct interface_int<5U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<6U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<7U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<8U> + { + typedef int64_t type; + }; +#endif + + //************************************************************************* + /// Sign extends the low 'Size_ * 8' bits of an unsigned value into the + /// full width of the signed interface type. + /// Uses only well defined unsigned arithmetic, so it is usable in a + /// constexpr context. + //************************************************************************* + template + ETL_CONSTEXPR TSigned sign_extend(TUnsigned uvalue) + { + return static_cast(static_cast(static_cast(uvalue ^ (static_cast(1) << ((Size_ * 8U) - 1U))) + - (static_cast(1) << ((Size_ * 8U) - 1U)))); + } + } // namespace private_unaligned_type + + //************************************************************************* + /// unaligned_uint_type + ///\brief Allows an unsigned integer of an arbitrary number of bytes to be + /// stored at an unaligned address. + /// Useful for types that have no native equivalent, such as a 24 bit + /// unsigned integer. + /// The value is interfaced to via the smallest standard unsigned + /// integer type that can hold it. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_uint_type) + : public private_unaligned_type::unaligned_type_storage + { + public: + + typedef typename private_unaligned_type::interface_uint::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Min_Value = static_cast(0); + static ETL_CONSTANT value_type Max_Value = static_cast(etl::integral_limits::max >> ((sizeof(value_type) - Size_) * 8U)); + + //************************************************************************* + /// Default constructor + //************************************************************************* + unaligned_uint_type() {} + + //************************************************************************* + /// Construct from a value. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Construct from an address. + //************************************************************************* + unaligned_uint_type(const void* address) + { + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + unaligned_uint_type(const void* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer. + /// Usable in a constexpr context (C++14 and later). + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address) + { + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Copy constructor + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + } + + //************************************************************************* + /// Copy constructor from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_uint_type(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + + //************************************************************************* + /// Assignment operator + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type& operator=(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_uint_type& operator=(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + + //************************************************************************* + /// Conversion operator + //************************************************************************* + ETL_CONSTEXPR14 operator value_type() const + { + value_type value = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, value); + + return value; + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + ETL_CONSTEXPR14 value_type value() const + { + value_type value = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, value); + + return value; + } + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_uint_type::Endian; + + template + ETL_CONSTANT size_t unaligned_uint_type::Size; + + template + ETL_CONSTANT typename unaligned_uint_type::value_type unaligned_uint_type::Min_Value; + + template + ETL_CONSTANT typename unaligned_uint_type::value_type unaligned_uint_type::Max_Value; + + //************************************************************************* + /// unaligned_int_type + ///\brief Allows a signed, two's complement, integer of an arbitrary number + /// of bytes to be stored at an unaligned address. + /// Useful for types that have no native equivalent, such as a 24 bit + /// signed integer. + /// The value is interfaced to via the smallest standard signed + /// integer type that can hold it, and is sign extended on reading. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_int_type) + : public private_unaligned_type::unaligned_type_storage + { + public: + + typedef typename private_unaligned_type::interface_int::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Max_Value = + static_cast(etl::integral_limits::type>::max >> (((sizeof(value_type) - Size_) * 8U) + 1U)); + static ETL_CONSTANT value_type Min_Value = static_cast(-Max_Value - 1); + + //************************************************************************* + /// Default constructor + //************************************************************************* + unaligned_int_type() {} + + //************************************************************************* + /// Construct from a value. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Construct from an address. + //************************************************************************* + unaligned_int_type(const void* address) + { + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + unaligned_int_type(const void* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer. + /// Usable in a constexpr context (C++14 and later). + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address) + { + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Copy constructor + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + } + + //************************************************************************* + /// Copy constructor from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_int_type(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + + //************************************************************************* + /// Assignment operator + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type& operator=(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_int_type& operator=(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + + //************************************************************************* + /// Conversion operator + //************************************************************************* + ETL_CONSTEXPR14 operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + ETL_CONSTEXPR14 value_type value() const + { + typedef typename etl::unsigned_type::type unsigned_t; + + unsigned_t uvalue = unsigned_t(0); + + unaligned_copy::copy_store_to_value(this->storage, uvalue); + + return private_unaligned_type::sign_extend(uvalue); + } + }; + ETL_END_PACKED + //************************************************************************* /// unaligned_type_ext ///\brief Allows an arithmetic type to be stored at an unaligned address. @@ -839,6 +1364,9 @@ namespace etl //************************************************************************* /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. //************************************************************************* template unaligned_type_ext(const unaligned_type_ext& other, pointer storage_) @@ -897,6 +1425,9 @@ namespace etl //************************************************************************* /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. //************************************************************************* template unaligned_type_ext& operator=(const unaligned_type_ext& other) @@ -984,6 +1515,528 @@ namespace etl template ETL_CONSTANT size_t unaligned_type_ext::Size; + //************************************************************************* + /// unaligned_uint_type_ext + ///\brief Allows an unsigned integer of an arbitrary number of bytes to be + /// stored at an unaligned address. Uses an external buffer. + /// The value is interfaced to via the smallest standard unsigned + /// integer type that can hold it. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_uint_type_ext) + : public private_unaligned_type::unaligned_type_storage_ext + { + public: + + template + friend class unaligned_uint_type_ext; + + typedef typename private_unaligned_type::interface_uint::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage_ext::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage_ext::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Min_Value = static_cast(0); + static ETL_CONSTANT value_type Max_Value = static_cast(etl::integral_limits::max >> ((sizeof(value_type) - Size_) * 8U)); + + //************************************************************************* + /// Construct from a storage pointer + //************************************************************************* + unaligned_uint_type_ext(pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + } + + //************************************************************************* + /// Construct from a value and storage pointer + //************************************************************************* + unaligned_uint_type_ext(value_type value, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_uint_type_ext(const unaligned_uint_type_ext& other, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move constructor + //************************************************************************* + unaligned_uint_type_ext(unaligned_uint_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + other.storage = ETL_NULLPTR; + } + + //************************************************************************* + /// Move constructor from other endianness + //************************************************************************* + template + unaligned_uint_type_ext(unaligned_uint_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + // If we're constructing from a different endianness then we need to + // reverse the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + } +#endif + + //************************************************************************* + /// Assignment operator + //************************************************************************* + unaligned_uint_type_ext& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator. + //************************************************************************* + unaligned_uint_type_ext& operator=(const unaligned_uint_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_uint_type_ext& operator=(const unaligned_uint_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move assignment operator. + //************************************************************************* + unaligned_uint_type_ext& operator=(unaligned_uint_type_ext&& other) + { + this->storage = other.storage; + other.storage = ETL_NULLPTR; + + return *this; + } + + //************************************************************************* + /// Move assignment operator from other endianness. + //************************************************************************* + template + unaligned_uint_type_ext& operator=(unaligned_uint_type_ext&& other) + { + this->storage = other.storage; + + // If we're assigning from a different endianness then we need to reverse + // the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + + return *this; + } +#endif + + //************************************************************************* + /// Conversion operator + //************************************************************************* + operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + value_type value() const + { + value_type v = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, v); + + return v; + } + + //************************************************************************* + /// Sets the storage for the type. + //************************************************************************* + void set_storage(pointer storage_) + { + this->storage = storage_; + } + + private: + + unaligned_uint_type_ext() ETL_DELETE; + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_uint_type_ext::Endian; + + template + ETL_CONSTANT size_t unaligned_uint_type_ext::Size; + + template + ETL_CONSTANT typename unaligned_uint_type_ext::value_type unaligned_uint_type_ext::Min_Value; + + template + ETL_CONSTANT typename unaligned_uint_type_ext::value_type unaligned_uint_type_ext::Max_Value; + + //************************************************************************* + /// unaligned_int_type_ext + ///\brief Allows a signed, two's complement, integer of an arbitrary number + /// of bytes to be stored at an unaligned address. Uses an external + /// buffer. + /// The value is interfaced to via the smallest standard signed + /// integer type that can hold it, and is sign extended on reading. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_int_type_ext) + : public private_unaligned_type::unaligned_type_storage_ext + { + public: + + template + friend class unaligned_int_type_ext; + + typedef typename private_unaligned_type::interface_int::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage_ext::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage_ext::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Max_Value = + static_cast(etl::integral_limits::type>::max >> (((sizeof(value_type) - Size_) * 8U) + 1U)); + static ETL_CONSTANT value_type Min_Value = static_cast(-Max_Value - 1); + + //************************************************************************* + /// Construct from a storage pointer + //************************************************************************* + unaligned_int_type_ext(pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + } + + //************************************************************************* + /// Construct from a value and storage pointer + //************************************************************************* + unaligned_int_type_ext(value_type value, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_int_type_ext(const unaligned_int_type_ext& other, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move constructor + //************************************************************************* + unaligned_int_type_ext(unaligned_int_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + other.storage = ETL_NULLPTR; + } + + //************************************************************************* + /// Move constructor from other endianness + //************************************************************************* + template + unaligned_int_type_ext(unaligned_int_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + // If we're constructing from a different endianness then we need to + // reverse the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + } +#endif + + //************************************************************************* + /// Assignment operator + //************************************************************************* + unaligned_int_type_ext& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator. + //************************************************************************* + unaligned_int_type_ext& operator=(const unaligned_int_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_int_type_ext& operator=(const unaligned_int_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move assignment operator. + //************************************************************************* + unaligned_int_type_ext& operator=(unaligned_int_type_ext&& other) + { + this->storage = other.storage; + other.storage = ETL_NULLPTR; + + return *this; + } + + //************************************************************************* + /// Move assignment operator from other endianness. + //************************************************************************* + template + unaligned_int_type_ext& operator=(unaligned_int_type_ext&& other) + { + this->storage = other.storage; + + // If we're assigning from a different endianness then we need to reverse + // the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + + return *this; + } +#endif + + //************************************************************************* + /// Conversion operator + //************************************************************************* + operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + value_type value() const + { + typedef typename etl::unsigned_type::type unsigned_t; + + unsigned_t uvalue = unsigned_t(0); + + unaligned_copy::copy_store_to_value(this->storage, uvalue); + + return private_unaligned_type::sign_extend(uvalue); + } + + //************************************************************************* + /// Sets the storage for the type. + //************************************************************************* + void set_storage(pointer storage_) + { + this->storage = storage_; + } + + private: + + unaligned_int_type_ext() ETL_DELETE; + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_int_type::Endian; + + template + ETL_CONSTANT size_t unaligned_int_type::Size; + + template + ETL_CONSTANT typename unaligned_int_type::value_type unaligned_int_type::Min_Value; + + template + ETL_CONSTANT typename unaligned_int_type::value_type unaligned_int_type::Max_Value; + + // 24 bit integers. + typedef unaligned_uint_type<3U, etl::endian::little> le_uint24_t; + typedef unaligned_uint_type<3U, etl::endian::big> be_uint24_t; + typedef be_uint24_t net_uint24_t; + typedef unaligned_int_type<3U, etl::endian::little> le_int24_t; + typedef unaligned_int_type<3U, etl::endian::big> be_int24_t; + typedef be_int24_t net_int24_t; +#if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type<3U, etl::endianness::value()> host_uint24_t; + typedef unaligned_int_type<3U, etl::endianness::value()> host_int24_t; +#endif + +#if ETL_USING_64BIT_TYPES + // 40, 48 and 56 bit integers. + typedef unaligned_uint_type<5U, etl::endian::little> le_uint40_t; + typedef unaligned_uint_type<5U, etl::endian::big> be_uint40_t; + typedef be_uint40_t net_uint40_t; + typedef unaligned_int_type<5U, etl::endian::little> le_int40_t; + typedef unaligned_int_type<5U, etl::endian::big> be_int40_t; + typedef be_int40_t net_int40_t; + + typedef unaligned_uint_type<6U, etl::endian::little> le_uint48_t; + typedef unaligned_uint_type<6U, etl::endian::big> be_uint48_t; + typedef be_uint48_t net_uint48_t; + typedef unaligned_int_type<6U, etl::endian::little> le_int48_t; + typedef unaligned_int_type<6U, etl::endian::big> be_int48_t; + typedef be_int48_t net_int48_t; + + typedef unaligned_uint_type<7U, etl::endian::little> le_uint56_t; + typedef unaligned_uint_type<7U, etl::endian::big> be_uint56_t; + typedef be_uint56_t net_uint56_t; + typedef unaligned_int_type<7U, etl::endian::little> le_int56_t; + typedef unaligned_int_type<7U, etl::endian::big> be_int56_t; + typedef be_int56_t net_int56_t; + + #if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type<5U, etl::endianness::value()> host_uint40_t; + typedef unaligned_uint_type<6U, etl::endianness::value()> host_uint48_t; + typedef unaligned_uint_type<7U, etl::endianness::value()> host_uint56_t; + typedef unaligned_int_type<5U, etl::endianness::value()> host_int40_t; + typedef unaligned_int_type<6U, etl::endianness::value()> host_int48_t; + typedef unaligned_int_type<7U, etl::endianness::value()> host_int56_t; + #endif +#endif + + template + ETL_CONSTANT int unaligned_int_type_ext::Endian; + + template + ETL_CONSTANT size_t unaligned_int_type_ext::Size; + + template + ETL_CONSTANT typename unaligned_int_type_ext::value_type unaligned_int_type_ext::Min_Value; + + template + ETL_CONSTANT typename unaligned_int_type_ext::value_type unaligned_int_type_ext::Max_Value; + + // 24 bit integers, external buffer. + typedef unaligned_uint_type_ext<3U, etl::endian::little> le_uint24_ext_t; + typedef unaligned_uint_type_ext<3U, etl::endian::big> be_uint24_ext_t; + typedef be_uint24_ext_t net_uint24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endian::little> le_int24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endian::big> be_int24_ext_t; + typedef be_int24_ext_t net_int24_ext_t; +#if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type_ext<3U, etl::endianness::value()> host_uint24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endianness::value()> host_int24_ext_t; +#endif + +#if ETL_USING_64BIT_TYPES + // 40, 48 and 56 bit integers, external buffer. + typedef unaligned_uint_type_ext<5U, etl::endian::little> le_uint40_ext_t; + typedef unaligned_uint_type_ext<5U, etl::endian::big> be_uint40_ext_t; + typedef be_uint40_ext_t net_uint40_ext_t; + typedef unaligned_int_type_ext<5U, etl::endian::little> le_int40_ext_t; + typedef unaligned_int_type_ext<5U, etl::endian::big> be_int40_ext_t; + typedef be_int40_ext_t net_int40_ext_t; + + typedef unaligned_uint_type_ext<6U, etl::endian::little> le_uint48_ext_t; + typedef unaligned_uint_type_ext<6U, etl::endian::big> be_uint48_ext_t; + typedef be_uint48_ext_t net_uint48_ext_t; + typedef unaligned_int_type_ext<6U, etl::endian::little> le_int48_ext_t; + typedef unaligned_int_type_ext<6U, etl::endian::big> be_int48_ext_t; + typedef be_int48_ext_t net_int48_ext_t; + + typedef unaligned_uint_type_ext<7U, etl::endian::little> le_uint56_ext_t; + typedef unaligned_uint_type_ext<7U, etl::endian::big> be_uint56_ext_t; + typedef be_uint56_ext_t net_uint56_ext_t; + typedef unaligned_int_type_ext<7U, etl::endian::little> le_int56_ext_t; + typedef unaligned_int_type_ext<7U, etl::endian::big> be_int56_ext_t; + typedef be_int56_ext_t net_int56_ext_t; + + #if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type_ext<5U, etl::endianness::value()> host_uint40_ext_t; + typedef unaligned_uint_type_ext<6U, etl::endianness::value()> host_uint48_ext_t; + typedef unaligned_uint_type_ext<7U, etl::endianness::value()> host_uint56_ext_t; + typedef unaligned_int_type_ext<5U, etl::endianness::value()> host_int40_ext_t; + typedef unaligned_int_type_ext<6U, etl::endianness::value()> host_int48_ext_t; + typedef unaligned_int_type_ext<7U, etl::endianness::value()> host_int56_ext_t; + #endif +#endif + #if ETL_HAS_CONSTEXPR_ENDIANNESS // Host order typedef unaligned_type host_char_t; diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index 578d0cfb..21b8502f 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -367,6 +367,208 @@ namespace CHECK_EQUAL(f, etl::be_float_t::value_from(be_f.data())); CHECK_EQUAL(d, etl::le_double_t::value_from(le_d.data())); } + /// Unsigned integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_uint24) + { + CHECK_EQUAL(3U, sizeof(etl::le_uint24_t)); + CHECK_EQUAL(3U, sizeof(etl::be_uint24_t)); + CHECK_EQUAL(3U, size_t(etl::le_uint24_t::Size)); + + CHECK((etl::is_same::value)); + CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_t::Max_Value); + + etl::le_uint24_t le_v(0x123456U); + etl::be_uint24_t be_v(0x123456U); + + CHECK_EQUAL(uint32_t(0x123456), le_v.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v.value()); + + // Check the storage byte order. + CHECK_EQUAL(0x56, int(le_v.data()[0])); + CHECK_EQUAL(0x34, int(le_v.data()[1])); + CHECK_EQUAL(0x12, int(le_v.data()[2])); + + CHECK_EQUAL(0x12, int(be_v.data()[0])); + CHECK_EQUAL(0x34, int(be_v.data()[1])); + CHECK_EQUAL(0x56, int(be_v.data()[2])); + + // Copy construction, including from the other endianness. + etl::le_uint24_t le_v2(le_v); + etl::be_uint24_t be_v2(be_v); + etl::le_uint24_t le_v3(be_v); + etl::be_uint24_t be_v3(le_v); + + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), le_v3.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v3.value()); + + // Assignment. + le_v2 = 0xFFFFFFU; + be_v2 = 0xFFFFFFU; + CHECK_EQUAL(uint32_t(0xFFFFFF), le_v2.value()); + CHECK_EQUAL(uint32_t(0xFFFFFF), be_v2.value()); + + // Boundary values. + etl::le_uint24_t le_zero(0U); + etl::be_uint24_t be_zero(0U); + CHECK_EQUAL(uint32_t(0), le_zero.value()); + CHECK_EQUAL(uint32_t(0), be_zero.value()); + + // Values that do not fit are truncated, like a narrowing cast. + etl::le_uint24_t le_truncated(0xAB123456U); + etl::be_uint24_t be_truncated(0xAB123456U); + CHECK_EQUAL(uint32_t(0x123456), le_truncated.value()); + CHECK_EQUAL(uint32_t(0x123456), be_truncated.value()); + } + + //************************************************************************* + TEST(test_uint24_decode_buffer) + { + const std::array buffer = {0xAA, 0xBB, 0xCC, 0xDD}; + + etl::le_uint24_t le_v(buffer.data(), buffer.size()); + etl::be_uint24_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint32_t(0xCCBBAA), le_v.value()); + CHECK_EQUAL(uint32_t(0xAABBCC), be_v.value()); + + // One byte short. + CHECK_THROW(etl::le_uint24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_uint40_uint48_uint56) + { + CHECK_EQUAL(5U, sizeof(etl::be_uint40_t)); + CHECK_EQUAL(6U, sizeof(etl::be_uint48_t)); + CHECK_EQUAL(7U, sizeof(etl::be_uint56_t)); + + etl::be_uint48_t be_v(0x0123456789ABULL); + etl::le_uint48_t le_v(0x0123456789ABULL); + + CHECK_EQUAL(uint64_t(0x0123456789AB), be_v.value()); + CHECK_EQUAL(uint64_t(0x0123456789AB), le_v.value()); + + CHECK_EQUAL(0x01, int(be_v.data()[0])); + CHECK_EQUAL(0xAB, int(le_v.data()[0])); + + CHECK_EQUAL(uint64_t(0x000000FFFFFFFFFF), etl::be_uint40_t::Max_Value); + CHECK_EQUAL(uint64_t(0x00FFFFFFFFFFFFFF), etl::be_uint56_t::Max_Value); + } +#endif + + //************************************************************************* + /// Signed integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_int24) + { + CHECK_EQUAL(3U, sizeof(etl::le_int24_t)); + CHECK_EQUAL(3U, sizeof(etl::be_int24_t)); + CHECK_EQUAL(3U, size_t(etl::le_int24_t::Size)); + + CHECK((etl::is_same::value)); + CHECK_EQUAL(int32_t(8388607), etl::le_int24_t::Max_Value); + CHECK_EQUAL(int32_t(-8388608), etl::le_int24_t::Min_Value); + + etl::le_int24_t le_v(0x123456); + etl::be_int24_t be_v(0x123456); + + CHECK_EQUAL(int32_t(0x123456), le_v.value()); + CHECK_EQUAL(int32_t(0x123456), be_v.value()); + + // Check the storage byte order. + CHECK_EQUAL(0x56, int(le_v.data()[0])); + CHECK_EQUAL(0x12, int(le_v.data()[2])); + CHECK_EQUAL(0x12, int(be_v.data()[0])); + CHECK_EQUAL(0x56, int(be_v.data()[2])); + + // Negative values are sign extended on reading. + etl::le_int24_t le_n(-2); + etl::be_int24_t be_n(-2); + + CHECK_EQUAL(int32_t(-2), le_n.value()); + CHECK_EQUAL(int32_t(-2), be_n.value()); + + CHECK_EQUAL(0xFE, int(le_n.data()[0])); + CHECK_EQUAL(0xFF, int(le_n.data()[1])); + CHECK_EQUAL(0xFF, int(le_n.data()[2])); + + CHECK_EQUAL(0xFF, int(be_n.data()[0])); + CHECK_EQUAL(0xFF, int(be_n.data()[1])); + CHECK_EQUAL(0xFE, int(be_n.data()[2])); + + // Copy construction, including from the other endianness. + etl::le_int24_t le_n2(be_n); + etl::be_int24_t be_n2(le_n); + + CHECK_EQUAL(int32_t(-2), le_n2.value()); + CHECK_EQUAL(int32_t(-2), be_n2.value()); + + // Assignment. + le_n2 = -1; + be_n2 = -1; + CHECK_EQUAL(int32_t(-1), le_n2.value()); + CHECK_EQUAL(int32_t(-1), be_n2.value()); + + // Boundary values. + etl::le_int24_t le_min(etl::le_int24_t::Min_Value); + etl::be_int24_t be_min(etl::be_int24_t::Min_Value); + etl::le_int24_t le_max(etl::le_int24_t::Max_Value); + etl::be_int24_t be_max(etl::be_int24_t::Max_Value); + + CHECK_EQUAL(int32_t(-8388608), le_min.value()); + CHECK_EQUAL(int32_t(-8388608), be_min.value()); + CHECK_EQUAL(int32_t(8388607), le_max.value()); + CHECK_EQUAL(int32_t(8388607), be_max.value()); + + etl::le_int24_t le_zero(0); + CHECK_EQUAL(int32_t(0), le_zero.value()); + + // Values that do not fit are truncated, like a narrowing cast. + etl::be_int24_t be_truncated(0x7F123456); + CHECK_EQUAL(int32_t(0x123456), be_truncated.value()); + } + + //************************************************************************* + TEST(test_int24_decode_buffer) + { + const std::array buffer = {0x80, 0x00, 0x00, 0xDD}; + + etl::le_int24_t le_v(buffer.data(), buffer.size()); + etl::be_int24_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(int32_t(0x000080), le_v.value()); + CHECK_EQUAL(int32_t(-8388608), be_v.value()); + + // One byte short. + CHECK_THROW(etl::le_int24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_int24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_int40_int48_int56) + { + CHECK_EQUAL(5U, sizeof(etl::be_int40_t)); + CHECK_EQUAL(6U, sizeof(etl::be_int48_t)); + CHECK_EQUAL(7U, sizeof(etl::be_int56_t)); + + etl::be_int48_t be_v(-1099511627775LL); + etl::le_int48_t le_v(-1099511627775LL); + + CHECK_EQUAL(int64_t(-1099511627775LL), be_v.value()); + CHECK_EQUAL(int64_t(-1099511627775LL), le_v.value()); + + CHECK_EQUAL(int64_t(549755813887LL), etl::be_int40_t::Max_Value); + CHECK_EQUAL(int64_t(-549755813888LL), etl::be_int40_t::Min_Value); + CHECK_EQUAL(int64_t(36028797018963967LL), etl::be_int56_t::Max_Value); + CHECK_EQUAL(int64_t(-36028797018963968LL), etl::be_int56_t::Min_Value); + } +#endif //************************************************************************* // The following tests demonstrate the 'decode' direction: given a raw byte diff --git a/test/test_unaligned_type_ext.cpp b/test/test_unaligned_type_ext.cpp index 1ffa050a..f858b5bb 100644 --- a/test/test_unaligned_type_ext.cpp +++ b/test/test_unaligned_type_ext.cpp @@ -49,6 +49,8 @@ namespace using storage_i16_t = std::array; using storage_i32_t = std::array; using storage_i64_t = std::array; + using storage_24_t = std::array; + using storage_48_t = std::array; SUITE(test_unaligned_type_ext) { @@ -506,6 +508,148 @@ namespace CHECK_EQUAL(sizeof(uint64_t), etl::net_uint64_ext_t::Size); } + //************************************************************************* + /// Integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_uint24) + { + storage_24_t store_le1; + storage_24_t store_be1; + storage_24_t store_le2; + storage_24_t store_be2; + + CHECK_EQUAL(3U, size_t(etl::le_uint24_ext_t::Size)); + CHECK((etl::is_same::value)); + CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_ext_t::Max_Value); + CHECK_EQUAL(uint32_t(0), etl::le_uint24_ext_t::Min_Value); + + etl::le_uint24_ext_t le_v1(store_le1.data()); + le_v1 = 0x123456U; + etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data()); + + CHECK_EQUAL(uint32_t(0x123456), le_v1.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v1.value()); + + // The external buffers hold the requested byte order. + CHECK_EQUAL(0x56, int(store_le1[0])); + CHECK_EQUAL(0x12, int(store_le1[2])); + CHECK_EQUAL(0x12, int(store_be1[0])); + CHECK_EQUAL(0x56, int(store_be1[2])); + + // Copy construct from the other endianness. + etl::le_uint24_ext_t le_v2(be_v1, store_le2.data()); + etl::be_uint24_ext_t be_v2(le_v1, store_be2.data()); + + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + + // Assignment. + le_v2 = le_v1; + be_v2 = le_v1; + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + + // Values that do not fit are truncated. + be_v2 = 0xABCDEF12U; + CHECK_EQUAL(uint32_t(0xCDEF12), be_v2.value()); + } + + //************************************************************************* + TEST(test_move_construction_uint24) + { + storage_24_t store_le1; + storage_24_t store_be1; + + etl::le_uint24_ext_t le_v1(0x123456U, store_le1.data()); + etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data()); + + etl::le_uint24_ext_t le_v2(etl::move(le_v1)); + etl::be_uint24_ext_t be_v2(etl::move(be_v1)); + + CHECK_TRUE(nullptr == le_v1.data()); + CHECK_TRUE(nullptr == be_v1.data()); + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + } + + //************************************************************************* + TEST(test_int24) + { + storage_24_t store_le1; + storage_24_t store_be1; + storage_24_t store_le2; + storage_24_t store_be2; + + CHECK_EQUAL(3U, size_t(etl::le_int24_ext_t::Size)); + CHECK((etl::is_same::value)); + CHECK_EQUAL(int32_t(8388607), etl::le_int24_ext_t::Max_Value); + CHECK_EQUAL(int32_t(-8388608), etl::le_int24_ext_t::Min_Value); + + etl::le_int24_ext_t le_v1(store_le1.data()); + le_v1 = -2; + etl::be_int24_ext_t be_v1(-2, store_be1.data()); + + // Negative values are sign extended on reading. + CHECK_EQUAL(int32_t(-2), le_v1.value()); + CHECK_EQUAL(int32_t(-2), be_v1.value()); + + CHECK_EQUAL(0xFE, int(store_le1[0])); + CHECK_EQUAL(0xFF, int(store_le1[2])); + CHECK_EQUAL(0xFF, int(store_be1[0])); + CHECK_EQUAL(0xFE, int(store_be1[2])); + + // Copy construct from the other endianness. + etl::le_int24_ext_t le_v2(be_v1, store_le2.data()); + etl::be_int24_ext_t be_v2(le_v1, store_be2.data()); + + CHECK_EQUAL(int32_t(-2), le_v2.value()); + CHECK_EQUAL(int32_t(-2), be_v2.value()); + + // Boundary values. + le_v2 = etl::le_int24_ext_t::Min_Value; + be_v2 = etl::be_int24_ext_t::Max_Value; + + CHECK_EQUAL(int32_t(-8388608), le_v2.value()); + CHECK_EQUAL(int32_t(8388607), be_v2.value()); + + le_v2 = 0; + CHECK_EQUAL(int32_t(0), le_v2.value()); + } + + //************************************************************************* + TEST(test_move_construction_int24) + { + storage_24_t store_le1; + storage_24_t store_be1; + + etl::le_int24_ext_t le_v1(-1234567, store_le1.data()); + etl::be_int24_ext_t be_v1(-1234567, store_be1.data()); + + etl::le_int24_ext_t le_v2(etl::move(le_v1)); + etl::be_int24_ext_t be_v2(etl::move(be_v1)); + + CHECK_TRUE(nullptr == le_v1.data()); + CHECK_TRUE(nullptr == be_v1.data()); + CHECK_EQUAL(int32_t(-1234567), le_v2.value()); + CHECK_EQUAL(int32_t(-1234567), be_v2.value()); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_uint48_int48) + { + storage_48_t store_u; + storage_48_t store_i; + + etl::be_uint48_ext_t be_u(0x0123456789ABULL, store_u.data()); + etl::be_int48_ext_t be_i(-1099511627775LL, store_i.data()); + + CHECK_EQUAL(6U, size_t(etl::be_uint48_ext_t::Size)); + CHECK_EQUAL(uint64_t(0x0123456789AB), be_u.value()); + CHECK_EQUAL(int64_t(-1099511627775LL), be_i.value()); + } +#endif + #if ETL_HAS_CONSTEXPR_ENDIANNESS //************************************************************************* TEST(test_host_sizes) @@ -1528,6 +1672,106 @@ namespace CHECK_EQUAL(0x12, bev0); CHECK_EQUAL(0x34, bev1); } + + //************************************************************************* + TEST(test_shared_storage_copy_construction_uint24) + { + storage_24_t store; + + etl::be_uint24_ext_t be_v(0x123456, store.data()); + + // Reinterpret the same buffer with the opposite endianness. + etl::le_uint24_ext_t le_v(be_v, store.data()); + + CHECK_EQUAL(0x56, int(store[0])); + CHECK_EQUAL(0x34, int(store[1])); + CHECK_EQUAL(0x12, int(store[2])); + CHECK_EQUAL(0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_construction_int24) + { + storage_24_t store; + + etl::be_int24_ext_t be_v(-0x123456, store.data()); + + etl::le_int24_ext_t le_v(be_v, store.data()); + + CHECK_EQUAL(-0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_uint24) + { + storage_24_t store; + + etl::be_uint24_ext_t be_v(0x123456, store.data()); + etl::le_uint24_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(0x56, int(store[0])); + CHECK_EQUAL(0x34, int(store[1])); + CHECK_EQUAL(0x12, int(store[2])); + CHECK_EQUAL(0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_int24) + { + storage_24_t store; + + etl::be_int24_ext_t be_v(-0x123456, store.data()); + etl::le_int24_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(-0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_uint32) + { + storage_i32_t store; + + etl::be_uint32_ext_t be_v(0x12345678, store.data()); + etl::le_uint32_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(0x78, int(store[0])); + CHECK_EQUAL(0x56, int(store[1])); + CHECK_EQUAL(0x34, int(store[2])); + CHECK_EQUAL(0x12, int(store[3])); + CHECK_EQUAL(0x12345678, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_same_endianness) + { + storage_i32_t store; + + etl::le_uint32_ext_t le_v1(0x12345678, store.data()); + etl::le_uint32_ext_t le_v2(store.data()); + + le_v2 = le_v1; + + CHECK_EQUAL(0x12345678, le_v2.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_float) + { + storage_f_t store; + + etl::be_float_ext_t be_v(3.1415927f, store.data()); + etl::le_float_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(3.1415927f, le_v.value()); + } } } // namespace