diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 8f7d4c00..dd5d3e45 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -36,8 +36,12 @@ concurrency: jobs: build-windows-msvc-stl: - name: Windows - STL + name: Windows - STL - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -50,7 +54,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -58,8 +62,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl: - name: Windows - No STL + name: Windows - No STL - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout uses: actions/checkout@v6.0.3 @@ -71,7 +79,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -79,8 +87,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-stl-force-cpp03: - name: Windows - STL - Force C++03 + name: Windows - STL - Force C++03 - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -93,7 +105,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -101,8 +113,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl-force-cpp03: - name: Windows - No STL - Force C++03 + name: Windows - No STL - Force C++03 - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -115,7 +131,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln diff --git a/.gitignore b/.gitignore index b5cc3865..035a6dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -423,3 +423,4 @@ include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build docs/Messaging test/vs2026/Build +test/include diff --git a/docs/_index.md b/docs/_index.md index dc6fde75..c31c6a6d 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -118,9 +118,9 @@ CRC calculations (8, 16, 32 & 64-bit) Checksums and hash functions - Variants (type-safe unions) - Extensive template support utilities -- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user’s choice. +- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user's choice. - Thoroughly Tested: -Over 10,000 unit tests +Over **14,000** tests Tested with Visual Studio 2022, GCC 12, and Clang 14 Continuous integration via GitHub Actions. - Readable and Well-Documented: Clean, maintainable source code with clear documentation. @@ -128,6 +128,7 @@ Continuous integration via GitHub Actions. - Archived: A snapshot of the ETL is preserved in the Arctic Code Vault for long-term digital preservation. ## Support the ETL + Maintaining the ETL can take a lot of man-hours of work, but unfortunately it doesn't pay the bills. When I have to take on paying work, the ETL gets a lot less attention. So if you have found the library is an important component in your work and you would like to help out, then please consider by supporting the project. --- diff --git a/docs/maths/math.md b/docs/maths/math.md new file mode 100644 index 00000000..3ec1cc88 --- /dev/null +++ b/docs/maths/math.md @@ -0,0 +1,306 @@ +--- +title: "math" +--- + +{{< callout type="info">}} + Header: `math.h` + From: `20.50.0` +{{< /callout >}} + +Various mathematical utilities + +## is_nan + +```cpp +template +ETL_CONSTEXPR bool is_nan(T value) +``` +**Returns** +`true` if `value` is `NAN` (Not A Number). +Always returns `false` if `T` is an integral. + +## is_infinity + +```cpp +template +ETL_CONSTEXPR bool is_infinity(T value) +``` +**Returns** +`true` if `value` is infinity. +Always returns `false` if `T` is an integral. + +## is_zero + +```cpp +template +ETL_CONSTEXPR bool is_zero(T value) +``` +**Returns** +`true` if `value` is Zero. + +## is_exactly_zero + +```cpp +template +ETL_CONSTEXPR bool is_exactly_equal(T value1, T value2) +``` +**Returns** +`true` if `value1` is equal to `value2`. + +## floor + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float floor(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floorf` in ``. + +**Returns** +`::floorf(value)` + +--- + +```cpp +ETL_NODISCARD +double floor(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floor` in ``. + +**Returns** +`::floor(value)` + +--- + +```cpp +ETL_NODISCARD +long double floor(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floorl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::floor` with casts to `double`. +otherwise +`::floorl(value)` + +## ceil + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float ceil(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceilf` in ``. + +**Returns** +`::ceilf(value)` + +--- + +```cpp +ETL_NODISCARD +double ceil(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceil` in ``. + +**Returns** +`::ceil(value)` + +--- + +```cpp +ETL_NODISCARD +long double ceil(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceill` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::ceil` with casts to `double`. +otherwise +`::ceill(value)` + +## log10 + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float log10(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10f` in ``. + +**Returns** +`::log10f(value)` + +--- + +```cpp +ETL_NODISCARD +double log10(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10` in ``. + +**Returns** +`::log10(value)` + +--- + +```cpp +ETL_NODISCARD +long double log10(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10l` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::log10` with casts to `double`. +otherwise +`::log10l(value)` + +## round + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float round(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundf` in ``. + +**Returns** +`::roundf(value)` + +--- + +```cpp +ETL_NODISCARD +double round(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::round` in ``. + +**Returns** +`::round(value)` + +--- + +```cpp +ETL_NODISCARD +long double round(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::round` with casts to `double`. +otherwise +`::roundl(value)` + +## pow + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float pow(float base, float exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powf` in ``. + +**Returns** +`::powf(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +double pow(double base, double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::pow` in ``. + +**Returns** +`::pow(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +long double pow(long double base, long double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::pow` with casts to `double`. +otherwise +`::powl(base, intpart)` + +## modf + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float modf(float value, float* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modff` in ``. + +**Returns** +`::modff(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +double modf(double value, double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modf` in ``. + +**Returns** +`::modf(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +long double modf(long double value, long double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modfl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::modf` with casts to `double`. +otherwise +`::modfl(value, intpart)` diff --git a/docs/types/unaligned-type.md b/docs/types/unaligned-type.md index 0f6689a9..8ae1378f 100644 --- a/docs/types/unaligned-type.md +++ b/docs/types/unaligned-type.md @@ -166,6 +166,50 @@ Constructs with the supplied value. --- +```cpp +unaligned_type(const void* address) +``` +**Description** +Constructs from the `Size` bytes at the address. +From: `20.39.5` + +--- + +```cpp +unaligned_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. +From: `20.39.5` + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_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. +From: `20.48.1` + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_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`. If checks are disabled then a smaller buffer size +results in a read beyond the end of the buffer. +From: `20.48.1` + +--- + ```cpp template unaligned_type(const unaligned_type& other) @@ -176,10 +220,11 @@ The endianness is converted, if necessary. ## Member types ```cpp -using pointer = char*; -using const_pointer = const char*; -using iterator = char*; -using const_iterator = const char*; +using storage_type = unsigned char; +using pointer = unsigned char*; +using const_pointer = const unsigned char*; +using iterator = unsigned char*; +using const_iterator = const unsigned char*; using reverse_iterator = etl::reverse_iterator; using const_reverse_iterator = etl::reverse_iterator; ``` @@ -306,7 +351,7 @@ Const reverse iterator to the end of the storage. --- ```cpp -char& operator[](int i) +storage_type& operator[](int i) ``` **Description** Index operator. @@ -314,7 +359,7 @@ Index operator. --- ```cpp -ETL_CONSTEXPR14 const char& operator[](int i) const +ETL_CONSTEXPR14 const storage_type& operator[](int i) const ``` **Description** Const index operator. @@ -345,6 +390,15 @@ Gets the value. --- +```cpp +static ETL_CONSTEXPR14 T 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_type& operator =(const unaligned_type& other) @@ -405,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/docs/utilities/clamped-value.md b/docs/utilities/clamped-value.md index 5bae0543..2396a96a 100644 --- a/docs/utilities/clamped-value.md +++ b/docs/utilities/clamped-value.md @@ -26,7 +26,8 @@ including the header to override automatic detection. When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral type. When it is `1`, `float`, `double`, and `long double` may also be used with -runtime bounds. +runtime bounds. `clamped_value_range` and `referenced_clamped_value` are +available in C++11 and later. ```cpp // C++11 and later @@ -48,6 +49,20 @@ etl::clamped_value float_ct(0.5f); #endif ``` +In C++11 and later, multiple values can share one immutable runtime range: + +```cpp +const etl::clamped_value_range range(-1.5f, 2.5f); + +etl::referenced_clamped_value value1(range, 0.5f); +etl::referenced_clamped_value value2(range, 1.5f); +``` + +`referenced_clamped_value` stores an +`etl::reference_wrapper>`. The range must outlive +every value that refers to it. Construction from a temporary range is deleted. +Copying or swapping a referenced value also copies or swaps its range reference. + ## Types `difference_type` is the signed counterpart of `T` for integral values. For @@ -55,6 +70,9 @@ floating-point values, `difference_type` is `T`, allowing fractional steps. `limits_type` and `difference_limits_type` provide the corresponding `etl::numeric_limits` specializations. +`referenced_clamped_value` also defines `range_type` as +`clamped_value_range`. + ## Constructors ```cpp @@ -76,6 +94,23 @@ runtime-bound specialization. Supplying `Min` and `Max` selects fixed compile-time bounds. NaN bounds and initial values are rejected. Infinite bounds and values are permitted. +### Referenced ranges + +```cpp +clamped_value_range range(min, max); +referenced_clamped_value value(range); +referenced_clamped_value value(range, initial); +``` + +The one-argument value constructor initializes to `range.min()`. The +two-argument form clamps `initial` to the shared range. A range is immutable +after construction, so every referring value observes stable bounds. A +default-constructed range uses the full representable range of `T`. + +`referenced_clamped_value` is non-owning. The referenced range must outlive +the value and any copies of it. The constructors taking `range_type&&` are +deleted to prevent direct construction from a temporary range. + ## Modifiers ```cpp @@ -93,6 +128,10 @@ counterpart of `T` as its step type. For floating-point values, the step type is `T` and may be fractional. Steps saturate in constant time at the minimum or maximum rather than wrapping. NaN steps are rejected. +`referenced_clamped_value` supports `set(T)`, `to_min()`, `to_max()`, and +`advance(difference_type)`. It does not provide `set(min, max)` because its +range is immutable and shared. + Positive steps move toward `max`; negative steps move toward `min`. Infinite steps saturate at a finite bound. Infinite values and bounds remain valid. @@ -122,6 +161,14 @@ ETL_NODISCARD T max() const noexcept; Gets the current value and its bounds. Both compile-time and runtime specializations provide these accessors as const-qualified member functions. +`referenced_clamped_value` additionally provides: + +```cpp +ETL_NODISCARD const range_type& range() const noexcept; +``` + +This returns the exact `clamped_value_range` object referenced by the value. + ## Operators ```cpp @@ -143,6 +190,8 @@ floating-point values, assignment and compound-assignment reject NaN. Prefix increment and decrement return a reference to the updated object. Postfix increment and decrement return a copy of the value before it was updated. All four operations move by one and saturate at the applicable bound. +`referenced_clamped_value` provides the same conversion, assignment, +increment, decrement, and compound-assignment operators. ## Operations @@ -153,6 +202,10 @@ ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs); Swaps clamped values. Runtime values also swap their bounds. +Swapping two `referenced_clamped_value` objects swaps both their current +values and range references. Copy construction and copy assignment preserve +the source's range identity; they do not copy the range itself. + Equality, inequality, and relational comparisons are provided. Comparisons against arithmetic values use the implicit conversion to `T`. Equality compares the current values; runtime bounds do not affect equality. Runtime @@ -169,3 +222,124 @@ assignments, and steps also trigger an ETL assertion. Depending on the ETL error-handler configuration, an assertion may throw `etl::exception`, invoke a configured handler, or terminate. Operations that validate input are only `noexcept` when exceptions are disabled. + +The same validation applies when constructing `clamped_value_range` and +when assigning or advancing a `referenced_clamped_value`. + +## Shared Referenced Ranges + +`clamped_value_range` and `referenced_clamped_value` are available in +C++11 and later. They let independent values share immutable runtime bounds. + +### `clamped_value_range` + +```cpp +template +class clamped_value_range; +``` + +#### Constructors + +```cpp +clamped_value_range(); +clamped_value_range(T min, T max); +``` + +The default constructor uses the full representable range of `T`. The +two-argument constructor stores immutable bounds and rejects reversed or NaN +bounds. + +#### Accessors + +```cpp +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +``` + +### `referenced_clamped_value` + +```cpp +template +class referenced_clamped_value; +``` + +The class stores a current value and an +`etl::reference_wrapper>`. It does not own the +range, which must outlive the value and all copies of it. + +Member types are `range_type`, `difference_type`, `limits_type`, and +`difference_limits_type`, with the same numeric meanings as `clamped_value`. + +#### Constructors + +```cpp +explicit referenced_clamped_value(const range_type& range); +referenced_clamped_value(const range_type& range, T initial); +referenced_clamped_value(const referenced_clamped_value& other); + +referenced_clamped_value(range_type&&) = delete; +referenced_clamped_value(range_type&&, T) = delete; +``` + +The one-argument constructor initializes to `range.min()`. The two-argument +constructor clamps `initial`. Temporary ranges are rejected to prevent an +immediately dangling reference. Copy construction preserves the source value +and range identity. + +#### Modifiers + +```cpp +void set(T value); +void to_min() noexcept; +void to_max() noexcept; +void advance(difference_type n); +``` + +These operations mirror `clamped_value`. There is no `set(min, max)` because +the referenced range is immutable and shared. + +#### Accessors + +```cpp +ETL_NODISCARD T get() const noexcept; +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +ETL_NODISCARD const range_type& range() const noexcept; +ETL_NODISCARD operator T() const noexcept; +``` + +`range()` returns the exact range object held by reference. + +#### Operators + +```cpp +referenced_clamped_value& operator=(const referenced_clamped_value& other) & noexcept; +referenced_clamped_value& operator=(T value) &; +referenced_clamped_value& operator++() & noexcept; +referenced_clamped_value operator++(int) noexcept; +referenced_clamped_value& operator--() & noexcept; +referenced_clamped_value operator--(int) noexcept; +referenced_clamped_value& operator+=(difference_type n) &; +referenced_clamped_value& operator-=(difference_type n) &; +``` + +Copy assignment copies both the current value and range reference. Arithmetic +and value assignment clamp to the referenced bounds. Comparisons compare only +current values, not range identity or bounds. + +#### Swap + +```cpp +void swap(referenced_clamped_value& other) noexcept; +void swap(referenced_clamped_value& lhs, + referenced_clamped_value& rhs) noexcept; +``` + +Swap exchanges both current values and range references, keeping each value +associated with its original bounds. + +#### Errors + +Invalid or NaN range bounds trigger an ETL assertion. NaN initial values, +assignments, and steps are also rejected. Infinite values follow the same +behavior as `clamped_value`. diff --git a/docs/utilities/compare.md b/docs/utilities/compare.md index f6062c73..54d904e5 100644 --- a/docs/utilities/compare.md +++ b/docs/utilities/compare.md @@ -7,23 +7,32 @@ title: "compare" Since: `TBC` {{< /callout >}} -A helper class that defines `<=`, `>`, `>=` in terms of `etl::less` or a user supplied compare type. +A helper for deriving comparison operations from a strict weak ordering. +`etl::compare` exposes boolean predicates and a three-state comparison result. +`etl::three_way_compare` returns `-1`, `0`, or `1` using the same comparison policy. ```cpp template > struct compare ``` +```cpp +template > +ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) +``` + ## Types ```cpp first_argument_type; second_argument_type; -result_type; +cmp_result; ``` +`cmp_result` has the values `Less`, `Equal`, and `Greater`. + ## Members ```cpp -static result_type lt(first_argument_type lhs, second_argument_type rhs) +static bool lt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs < rhs`, otherwise `false`. @@ -31,7 +40,7 @@ static result_type lt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type lte(first_argument_type lhs, second_argument_type rhs) +static bool lte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs <= rhs`, otherwise `false`. @@ -39,7 +48,7 @@ static result_type lte(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gt(first_argument_type lhs, second_argument_type rhs) +static bool gt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs > rhs`, otherwise `false`. @@ -47,14 +56,74 @@ static result_type gt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gte(first_argument_type lhs, second_argument_type rhs) +static bool gte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs >= rhs`, otherwise `false`. +--- + +```cpp +static bool eq(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs == rhs`, otherwise `false`. + +--- + +```cpp +static bool ne(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs != rhs`, otherwise `false`. + +--- + +```cpp +static cmp_result cmp(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`Less`, `Equal`, or `Greater`. + +## Three-way comparison + +`etl::three_way_compare(lhs, rhs)` returns the same ordering as `compare::cmp(lhs, rhs)`, +but as an `int` with the values `-1`, `0`, or `1`. + +## C++20 comparison categories + +When compiling as C++20, `compare.h` makes the comparison categories available in the `etl` +namespace, so ETL and user code can name them regardless of the STL configuration. + +```cpp +class strong_ordering; +class weak_ordering; +class partial_ordering; +``` + +* When the STL is available these are aliases for `std::strong_ordering`, `std::weak_ordering` + and `std::partial_ordering`. +* When the STL is not available, `compare.h` provides its own minimal implementations that + follow the standard semantics: the named values `less`, `equivalent`, `greater` + (plus `equal` for `strong_ordering` and `unordered` for `partial_ordering`), comparison + against the literal `0` only, and the implicit conversions + `strong_ordering` → `weak_ordering` → `partial_ordering`. + +An `unordered` result compares `false` against `0` for all of `<`, `<=`, `==`, `>=` and `>`. + +```cpp +ETL_CONSTEXPR strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT; +``` + +These helpers convert a three-way comparison result, such as the one returned by +`etl::three_way_compare`, into a comparison category. Negative values map to `less`, positive +values to `greater`, and zero to `equal`/`equivalent`. `unordered` cannot be expressed as an +integer result, so use `etl::partial_ordering::unordered` directly for that case. + ## Example 1 ```cpp -using std::less struct Test { int a; @@ -84,6 +153,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 2 @@ -124,6 +198,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 3 @@ -158,3 +237,13 @@ bool operator >=(const Test& lhs, const Test& rhs) return Test::gte(lhs, rhs); } ``` + +## Example 4 +C++20, with or without the STL. + +```cpp +etl::strong_ordering operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::make_strong_ordering(etl::three_way_compare(lhs, rhs)); +} +``` diff --git a/docs/utilities/cyclic-value.md b/docs/utilities/cyclic-value.md index 37a92b65..d29a7b42 100644 --- a/docs/utilities/cyclic-value.md +++ b/docs/utilities/cyclic-value.md @@ -15,6 +15,9 @@ Supports compile time and runtime variants. template class cyclic_value ``` +Static asserts if `Last` < `First`. + +**Example** ```cpp etl::cyclic_value value_ct; // Compile time. Fixed range of 2 to 7 inclusive. @@ -24,126 +27,183 @@ value_rt.set(3, 8); ``` ## Constructor + +### Run time limits + ```cpp -cyclic_value value; +cyclic_value() noexcept; ``` -Creates a runtime cyclic value of type int with default constructed initial defined limits. +Constructs a cyclic value of type `T` with an initial value of `first`. +`constexpr` from C++11. --- ```cpp -cyclic_value value; +cyclic_value(T first, T last); ``` -Creates a compile time cyclic value of type int with fixed limits of N and M. +Constructs a *run time* cyclic value of type `T` with limits of `first` and `last` and an initial value of `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +--- -**Example** ```cpp -cyclic_value value(N, M); +cyclic_value(T first, T last, T initial); ``` -Creates a runtime cyclic value of type int with initial defined limits of N and M. +Constructs a cyclic value of type `T` with limits of `first` and `last` and an initial value of `initial`. +`initial` will be clamped to be in the range `(first, last)`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +### Compile time limits + +```cpp +cyclic_value() noexcept; +``` +Constructs a cyclic value of type `T` default constructed with an initial value of `First`. +`constexpr` from C++11. + +--- + +```cpp +cyclic_value(T initial) noexcept; +``` +Constructs a cyclic value of type `T` constructed with an `initial`. +`initial` will be clamped to be in the range `(First, Last)`. +`constexpr` from C++11. ## Modifiers + ```cpp -cyclic_value& operator ++(); -cyclic_value& operator ++(int); +cyclic_value& operator ++() noexcept; +cyclic_value& operator ++(int) noexcept; ``` -Increments the value. If the value is at the last value then is set to the first. +Increments the value. If the value is at the last value then is set to the first. +`constexpr` from C++14. --- ```cpp -cyclic_value& operator --(); -cyclic_value& operator --(int); +cyclic_value& operator --() noexcept; +cyclic_value& operator --(int) noexcept; ``` -Decrements the value. If the value is at the first value then is set to the last. +Decrements the value. If the value is at the first value then is set to the last. +`constexpr` from C++14. --- ```cpp -void advance(int n); +template +void advance(TStep n) noexcept; ``` -Advances the value by the specified amount, wrapping if necessary. +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator +=(TStep n) noexcept; +``` +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator -=(TStep n) noexcept; +``` +Decrements the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. ## Access + ```cpp T get() const; ``` -Gets the current value +Gets the current value. +`constexpr` from C++11. --- ```cpp -T first() const; +T first() const noexcept; ``` -Sets the current value to the first value +Gets the first value. +`constexpr` from C++11. --- ```cpp -T last() const; +T last() const noexcept; ``` -Sets the current value to the last value +Gets the last value. +`constexpr` from C++11. --- ```cpp -void set(T first, T last); +void set(T first, T last) noexcept; ``` -Sets the new first and last values. Sets the current value to first. +Sets the new *run time* `first` and `last` values. Sets the current value to `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++14. --- ```cpp -void set(T value); +void set(T value) noexcept; ``` -Sets the current value. +Sets the current value. +`constexpr` from C++14. --- ```cpp -void to_first(); +void to_first() noexcept; ``` -Sets the current value to the first value +Sets the current value to the first value. +`constexpr` from C++14. --- ```cpp -void to_last(); +void to_last() noexcept; ``` -Sets the current value to the last value +Sets the current value to the last value. +`constexpr` from C++14. ## Operations ```cpp -void swap(cyclic_value& other); +void swap(cyclic_value& other) noexcept; ``` -Swaps with another cyclic value. +Swaps with another cyclic value. +`constexpr` from C++14. ---- - -```cpp -void swap(cyclic_value& lhs, cyclic_value& rhs); -``` -Swaps with another cyclic value. ## Operators + ```cpp -operator T(); -operator const T() const; +operator T() noexcept; +operator const T() const noexcept; ``` -Conversion operators to `T`. +Conversion operators to `T`. +`constexpr` from C++11. --- ```cpp -cyclic_value& operator =(T t); +cyclic_value& operator =(T t) noexcept; ``` -Sets the current value to `t`. +Sets the current value to `t`. +`constexpr` from C++14. ## Non-member functions ```cpp template -void swap(cyclic_value& lhs, cyclic_value& rhs) +void swap(cyclic_value& lhs, cyclic_value& rhs) noexcept ``` Swaps two cyclic values. @@ -151,14 +211,63 @@ Swaps two cyclic values. ```cpp template -bool operator == (cyclic_value& lhs, cyclic_value& rhs) +bool operator == (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks equality of two cyclic values. +Checks equality of two cyclic values. +`constexpr` from C++11. + +{{< callout type="warning">}} + After version `20.49.0` equality is judged only by the current value. + Previously the first and last limits were also considered. + + <= 20.49.0 + `etl::cyclic_value<2, 7>(3) != etl::cyclic_value<2, 6>(3)` + + > 20.49.0 + `etl::cyclic_value<2, 7>(3) == etl::cyclic_value<2, 6>(3)` +{{< /callout >}} --- ```cpp template -bool operator != (cyclic_value& lhs, cyclic_value& rhs) +bool operator != (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks inequality of two cyclic values. +Checks inequality of two cyclic values. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator < (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator <= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than or equal the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator > (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator >= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than or equal the current value of `rhs`. +`constexpr` from C++11. diff --git a/include/etl/array.h b/include/etl/array.h index c1d7d6e4..a5f48e27 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -137,7 +137,7 @@ namespace etl /// Returns a reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -149,7 +149,7 @@ namespace etl /// Returns a const reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/array_view.h b/include/etl/array_view.h index bb25dac0..7d20c5a2 100644 --- a/include/etl/array_view.h +++ b/include/etl/array_view.h @@ -518,7 +518,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); @@ -531,7 +531,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); diff --git a/include/etl/array_wrapper.h b/include/etl/array_wrapper.h index 2c434a05..97e198ec 100644 --- a/include/etl/array_wrapper.h +++ b/include/etl/array_wrapper.h @@ -271,7 +271,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds)); return ARRAY_[i]; @@ -282,7 +282,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/callback_timer.h b/include/etl/callback_timer.h index 800a34e2..591ace1f 100644 --- a/include/etl/callback_timer.h +++ b/include/etl/callback_timer.h @@ -411,6 +411,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -630,6 +640,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -639,6 +650,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -646,6 +666,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -788,6 +818,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_atomic.h b/include/etl/callback_timer_atomic.h index 98d92651..462601da 100644 --- a/include/etl/callback_timer_atomic.h +++ b/include/etl/callback_timer_atomic.h @@ -174,12 +174,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - // Call the delegate callback. - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -188,6 +185,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } @@ -301,6 +303,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -486,6 +498,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -495,6 +508,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -502,6 +524,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -644,6 +676,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_interrupt.h b/include/etl/callback_timer_interrupt.h index a67754cb..3c6aeb9b 100644 --- a/include/etl/callback_timer_interrupt.h +++ b/include/etl/callback_timer_interrupt.h @@ -178,11 +178,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -191,6 +189,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } @@ -305,6 +308,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -487,6 +500,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -496,6 +510,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -503,6 +526,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -645,6 +678,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_locked.h b/include/etl/callback_timer_locked.h index 5579ec0b..fde93249 100644 --- a/include/etl/callback_timer_locked.h +++ b/include/etl/callback_timer_locked.h @@ -264,6 +264,16 @@ namespace etl unlock = unlock_; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -460,6 +470,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -469,6 +480,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -476,6 +496,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -618,6 +648,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; //******************************************* @@ -716,11 +750,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -729,6 +761,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } diff --git a/include/etl/chrono.h b/include/etl/chrono.h index b452ca4e..ab360108 100644 --- a/include/etl/chrono.h +++ b/include/etl/chrono.h @@ -37,6 +37,7 @@ SOFTWARE. #if ETL_USING_CPP11 + #include "compare.h" #include "hash.h" #include "integral_limits.h" #include "type_traits.h" diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h index d7f3dcf9..37385139 100644 --- a/include/etl/clamped_value.h +++ b/include/etl/clamped_value.h @@ -35,6 +35,7 @@ SOFTWARE. #include "absolute.h" #include "algorithm.h" #include "error_handler.h" +#include "functional.h" #include "limits.h" #include "math.h" #include "static_assert.h" @@ -978,6 +979,400 @@ namespace etl T min_value; T max_value; }; + +#if ETL_USING_CPP11 + //*************************************************************************** + /// An immutable pair of runtime limits that may be shared by multiple + /// referenced_clamped_value objects. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///\ingroup clamped_value + //*************************************************************************** + template + class clamped_value_range + { + public: + + typedef typename private_clamped_value::traits::limits_type limits_type; + + //************************************************************************* + /// Constructs the full representable range of T. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value_range() + : min_value(limits_type::lowest()) + , max_value(limits_type::max()) + { + } + + //************************************************************************* + /// Constructs a range with the supplied limits. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value_range(T min_, T max_) + : min_value(min_) + , max_value(max_) + { + private_clamped_value::validate(min_); + private_clamped_value::validate(max_); + ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value_range: invalid range")); + } + + //************************************************************************* + /// Gets the minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return min_value; + } + + //************************************************************************* + /// Gets the maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return max_value; + } + + private: + + const T min_value; + const T max_value; + }; + + //*************************************************************************** + /// Provides a value clamped to a shared immutable runtime range. + /// The referenced range must outlive this object. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///\ingroup clamped_value + //*************************************************************************** + template + class referenced_clamped_value + { + public: + + /// The referenced range type. + typedef clamped_value_range range_type; + + /// The type used to advance or subtract from the stored value. + typedef typename private_clamped_value::traits::difference_type difference_type; + + /// Numeric limits for the stored value type. + typedef typename private_clamped_value::traits::limits_type limits_type; + + /// Numeric limits for difference_type. + typedef typename private_clamped_value::traits::difference_limits_type difference_limits_type; + + //************************************************************************* + /// Constructs a value initialized to the minimum of the referenced range. + ///\param range_ The range to reference. It must outlive this object. + //************************************************************************* + explicit referenced_clamped_value(const range_type& range_) + : value(range_.min()) + , value_range(etl::cref(range_)) + { + } + + //************************************************************************* + /// Constructs a value clamped to the referenced range. + ///\param range_ The range to reference. It must outlive this object. + ///\param initial The initial value. + //************************************************************************* + referenced_clamped_value(const range_type& range_, T initial) + : value(private_clamped_value::validated_clamp(initial, range_.min(), range_.max())) + , value_range(etl::cref(range_)) + { + } + + //************************************************************************* + /// Prevents construction with a temporary range. + //************************************************************************* + referenced_clamped_value(range_type&&) = delete; + referenced_clamped_value(range_type&&, T) = delete; + + //************************************************************************* + /// Copies the value and its range reference. + ///\param other The value to copy. + //************************************************************************* + referenced_clamped_value(const referenced_clamped_value& other) ETL_NOEXCEPT + : value(other.value) + , value_range(other.value_range) + { + } + + //************************************************************************* + /// Copy assignment. Copies the value and its range reference. + ///\param other The value and range reference to copy. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator=(const referenced_clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this != &other) + { + value = other.value; + value_range = other.value_range; + } + + return *this; + } + + //************************************************************************* + /// Sets the value and clamps it to the referenced range. + ///\param value_ The value to assign. + //************************************************************************* + void set(T value_) + { + value = private_clamped_value::validated_clamp(value_, min(), max()); + } + + //************************************************************************* + /// Resets the value to the minimum of the referenced range. + //************************************************************************* + void to_min() ETL_NOEXCEPT + { + value = min(); + } + + //************************************************************************* + /// Resets the value to the maximum of the referenced range. + //************************************************************************* + void to_max() ETL_NOEXCEPT + { + value = max(); + } + + //************************************************************************* + /// Advances the value and saturates it at the referenced bounds. + ///\param n The difference to add. + //************************************************************************* + void advance(difference_type n) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, min(), max(), n); + } + + //************************************************************************* + /// Converts to the underlying value type. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Advances by one and saturates at the maximum. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min(), max(), difference_type(1)); + return *this; + } + + //************************************************************************* + /// Advances by one and returns the previous value. + ///\return The value before incrementing. + //************************************************************************* + referenced_clamped_value operator++(int) ETL_NOEXCEPT + { + referenced_clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// Subtracts one and saturates at the minimum. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min(), max(), difference_type(-1)); + return *this; + } + + //************************************************************************* + /// Subtracts one and returns the previous value. + ///\return The value before decrementing. + //************************************************************************* + referenced_clamped_value operator--(int) ETL_NOEXCEPT + { + referenced_clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// Assigns a value and clamps it to the referenced range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER + { + set(value_); + return *this; + } + + //************************************************************************* + /// Adds a difference and clamps to the referenced range. + ///\param n The difference to add. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a difference and clamps to the referenced range. + ///\param n The difference to subtract. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, min(), max(), n); + return *this; + } + + //************************************************************************* + /// Gets the current value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD + T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum of the referenced range. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD + T min() const ETL_NOEXCEPT + { + return value_range.get().min(); + } + + //************************************************************************* + /// Gets the maximum of the referenced range. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD + T max() const ETL_NOEXCEPT + { + return value_range.get().max(); + } + + //************************************************************************* + /// Gets the referenced range. + ///\return The referenced range. + //************************************************************************* + ETL_NODISCARD + const range_type& range() const ETL_NOEXCEPT + { + return value_range.get(); + } + + //************************************************************************* + /// Swaps the values and range references. + ///\param other The value to swap with. + //************************************************************************* + void swap(referenced_clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + swap(value_range, other.value_range); + } + + //************************************************************************* + /// Swaps the values and range references. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend void swap(referenced_clamped_value& lhs, referenced_clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Compares current values for equality. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the current values are equal. + //************************************************************************* + friend bool operator==(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + #include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; + #include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Compares current values for inequality. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the current values differ. + //************************************************************************* + friend bool operator!=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Compares current values using less-than. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is less than rhs. + //************************************************************************* + friend bool operator<(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + + //************************************************************************* + /// Compares current values using less-than-or-equal. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is less than or equal to rhs. + //************************************************************************* + friend bool operator<=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + //************************************************************************* + /// Compares current values using greater-than. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is greater than rhs. + //************************************************************************* + friend bool operator>(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + //************************************************************************* + /// Compares current values using greater-than-or-equal. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is greater than or equal to rhs. + //************************************************************************* + friend bool operator>=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + private: + + T value; + etl::reference_wrapper value_range; + }; +#endif } // namespace etl #endif diff --git a/include/etl/compare.h b/include/etl/compare.h index 918465bd..f909291c 100644 --- a/include/etl/compare.h +++ b/include/etl/compare.h @@ -35,6 +35,10 @@ SOFTWARE. #include "functional.h" #include "parameter_type.h" +#if ETL_USING_CPP20 && ETL_USING_STL + #include +#endif + //***************************************************************************** ///\defgroup compare compare /// Comparisons only using less than operator @@ -42,6 +46,295 @@ SOFTWARE. //***************************************************************************** namespace etl { +#if ETL_USING_CPP20 + #if ETL_USING_STL + //*************************************************************************** + /// The STL supplies the C++20 comparison categories, so simply expose them + /// through the etl namespace. This allows ETL code and user code to refer to + /// etl::strong_ordering (etc.) regardless of the STL configuration. + //*************************************************************************** + using std::partial_ordering; + using std::strong_ordering; + using std::weak_ordering; + #else + namespace private_compare + { + //************************************************************************* + /// A type that can only be constructed from the literal 0. + /// Used to constrain the comparison-category operators to comparisons + /// against the literal 0, as required by the standard. + //************************************************************************* + struct only_literal_zero + { + ETL_CONSTEXPR only_literal_zero(only_literal_zero*) ETL_NOEXCEPT {} + }; + + typedef signed char ordering_value_type; + + inline constexpr ordering_value_type ordering_less = -1; + inline constexpr ordering_value_type ordering_equivalent = 0; + inline constexpr ordering_value_type ordering_greater = 1; + inline constexpr ordering_value_type ordering_unordered = 2; + } // namespace private_compare + + //*************************************************************************** + /// Minimal partial-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + /// Unlike the other categories this one has an 'unordered' state, for which + /// every comparison against 0 is false. + //*************************************************************************** + class partial_ordering + { + public: + + static const partial_ordering less; + static const partial_ordering equivalent; + static const partial_ordering greater; + static const partial_ordering unordered; + + friend ETL_CONSTEXPR bool operator==(partial_ordering lhs, partial_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_less; + } + friend ETL_CONSTEXPR bool operator>(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_greater; + } + friend ETL_CONSTEXPR bool operator<=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_less) || (v.value == private_compare::ordering_equivalent); + } + friend ETL_CONSTEXPR bool operator>=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_greater) || (v.value == private_compare::ordering_equivalent); + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR partial_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class weak_ordering; + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr partial_ordering partial_ordering::less(private_compare::ordering_less); + inline constexpr partial_ordering partial_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr partial_ordering partial_ordering::greater(private_compare::ordering_greater); + inline constexpr partial_ordering partial_ordering::unordered(private_compare::ordering_unordered); + + //*************************************************************************** + /// Minimal weak-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class weak_ordering + { + public: + + static const weak_ordering less; + static const weak_ordering equivalent; + static const weak_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(weak_ordering lhs, weak_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR weak_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr weak_ordering weak_ordering::less(private_compare::ordering_less); + inline constexpr weak_ordering weak_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr weak_ordering weak_ordering::greater(private_compare::ordering_greater); + + //*************************************************************************** + /// Minimal strong-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class strong_ordering + { + public: + + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + static const strong_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + ETL_CONSTEXPR operator weak_ordering() const ETL_NOEXCEPT + { + return weak_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(strong_ordering lhs, strong_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR strong_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + private_compare::ordering_value_type value; + }; + + inline constexpr strong_ordering strong_ordering::less(private_compare::ordering_less); + inline constexpr strong_ordering strong_ordering::equal(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::greater(private_compare::ordering_greater); + #endif + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::strong_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equal'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::strong_ordering::less : (value > 0) ? etl::strong_ordering::greater : etl::strong_ordering::equal; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::weak_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::weak_ordering::less : (value > 0) ? etl::weak_ordering::greater : etl::weak_ordering::equivalent; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::partial_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + /// Note that 'unordered' cannot be expressed by an integer comparison result, + /// so use etl::partial_ordering::unordered directly for that case. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::partial_ordering::less : (value > 0) ? etl::partial_ordering::greater : etl::partial_ordering::equivalent; + } +#endif + //*************************************************************************** /// Defines <=, >, >=, ==, !=, <=> in terms of < /// Default implementation of TLess is etl::less @@ -105,7 +398,7 @@ namespace etl #endif ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) { - return etl::compare::cmp(lhs, rhs); + return etl::compare::cmp(lhs, rhs); } } // namespace etl diff --git a/include/etl/cyclic_value.h b/include/etl/cyclic_value.h index 964ed664..6acdd1f1 100644 --- a/include/etl/cyclic_value.h +++ b/include/etl/cyclic_value.h @@ -38,10 +38,198 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" #include "exception.h" +#include "integral_limits.h" +#include "largest.h" +#include "negative.h" +#include "static_assert.h" #include "type_traits.h" namespace etl { + namespace private_cyclic_value + { + template + struct traits + { + typedef typename etl::make_unsigned::type unsigned_type; + }; + + //************************************************************************* + /// Increments a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T increment(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == max_value ? min_value : static_cast(etl::to_unsigned(value) + 1U); + } + + //************************************************************************* + /// Decrements a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T decrement(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == min_value ? max_value : static_cast(etl::to_unsigned(value) - 1U); + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is not the full width of the type. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + typedef typename traits::unsigned_type unsigned_type; + + switch (step) + { + case 0: + { + return value; + } + + case 1: + { + return subtract ? decrement(value, min_value, max_value) : increment(value, min_value, max_value); + } + + default: + { + if (subtract) + { + unsigned_type distance_to_first = static_cast(etl::to_unsigned(value) - etl::to_unsigned(min_value)); + + if (step <= distance_to_first) + { + // Room to subtract the step. + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + // Step would roll over. + step -= distance_to_first + 1U; + value = static_cast(etl::to_unsigned(max_value) - step); + } + } + else + { + unsigned_type distance_to_last = static_cast(etl::to_unsigned(max_value) - etl::to_unsigned(value)); + + if (step <= distance_to_last) + { + // Room to add the step. + value = static_cast(etl::to_unsigned(value) + step); + } + else + { + // Step would roll over. + step -= distance_to_last + 1U; + value = static_cast(etl::to_unsigned(min_value) + step); + } + } + + return value; + } + } + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is the full width of the type. + ///\param value The current value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + if (subtract) + { + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + value = static_cast(etl::to_unsigned(value) + step); + } + + return value; + } + + //************************************************************************* + // Provides information about a range. + // Ensures that Last - First + 1 is only calculated for when the range is not the full width of the type, to avoid overflow errors. + //************************************************************************* + template ::min) && (Last == etl::integral_limits::max))> + struct range_traits; + + //************************************************************************* + // Specialisation for when the range is the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = true; + static const unsigned_type value = 0; // Unused, but required for template specialisation. + }; + + //************************************************************************* + // Specialisation for when the range is not the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = false; + static const unsigned_type value = static_cast(static_cast(Last) - static_cast(First)) + 1U; + }; + + // Out-of-class definitions (required pre-C++17 for ODR-use of static const members). + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + } // namespace private_cyclic_value + + struct cyclic_value_exception : etl::exception + { + cyclic_value_exception(string_type reason_, string_type file_, numeric_type line_) + : etl::exception(reason_, file_, line_) + { + } + }; + + struct cyclic_value_reversed_limits : cyclic_value_exception + { + cyclic_value_reversed_limits(string_type file_, numeric_type line_) + : cyclic_value_exception(ETL_ERROR_TEXT("cyclic_value_exception:reversed limits", ETL_CYCLIC_VALUE_FILE_ID"A"), file_, line_) + { + } + }; + //*************************************************************************** /// Provides a value that cycles between two limits. //*************************************************************************** @@ -59,13 +247,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + ETL_STATIC_ASSERT(First <= Last, "First is not <= Last"); + //************************************************************************* /// Default constructor. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) { } @@ -75,15 +270,15 @@ namespace etl /// Set to an initial value. /// Clamped to the range. //************************************************************************* - ETL_CONSTEXPR14 explicit cyclic_value(T initial) + ETL_CONSTEXPR explicit cyclic_value(T initial) ETL_NOEXCEPT + : value(etl::clamp(initial, First, Last)) { - set(initial); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) { } @@ -91,19 +286,48 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Sets the value. /// Truncates to the First/Last range. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, First, Last); } @@ -111,7 +335,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = First; } @@ -119,7 +343,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = Last; } @@ -128,30 +352,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -159,7 +366,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -167,16 +374,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= Last) ETL_UNLIKELY - { - value = First; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, First, Last); return *this; } @@ -184,7 +384,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -196,16 +396,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= First) ETL_UNLIKELY - { - value = Last; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, First, Last); return *this; } @@ -213,7 +406,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -225,7 +418,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -234,8 +427,8 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - template - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + template + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(other.get()); return *this; @@ -244,7 +437,7 @@ namespace etl //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -252,7 +445,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - static ETL_CONSTEXPR T first() + static ETL_CONSTEXPR T first() ETL_NOEXCEPT { return First; } @@ -260,7 +453,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - static ETL_CONSTEXPR T last() + static ETL_CONSTEXPR T last() ETL_NOEXCEPT { return Last; } @@ -268,39 +461,39 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return lhs.value == rhs.value; - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + typedef private_cyclic_value::range_traits range_info; + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if ETL_IF_CONSTEXPR (range_info::is_full_range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = range_info::value; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, First, Last, step, subtract); + } + } + T value; ///< The current value. }; @@ -315,14 +508,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + //************************************************************************* /// Constructor. - /// Sets 'first' and 'last' to the template parameter values. + /// Sets 'first' and 'last' to the template parameter values which will be zero. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) , first_value(First) , last_value(Last) @@ -335,11 +534,12 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR cyclic_value(T first_, T last_) + ETL_CONSTEXPR14 cyclic_value(T first_, T last_) : value(first_) , first_value(first_) , last_value(last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* @@ -350,16 +550,17 @@ namespace etl ///\param last The last value in the range. //************************************************************************* ETL_CONSTEXPR14 cyclic_value(T first_, T last_, T initial) - : first_value(first_) + : value(etl::clamp(initial, first_, last_)) + , first_value(first_) , last_value(last_) { - set(initial); + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) , first_value(other.first_value) , last_value(other.last_value) @@ -374,6 +575,8 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 void set(T first_, T last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); + first_value = first_; last_value = last_; value = first_; @@ -383,7 +586,7 @@ namespace etl /// Sets the value. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, first_value, last_value); } @@ -391,7 +594,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = first_value; } @@ -399,7 +602,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = last_value; } @@ -408,30 +611,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -439,7 +625,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -447,16 +633,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= last_value) - { - value = first_value; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, first_value, last_value); return *this; } @@ -464,7 +643,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -476,16 +655,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= first_value) - { - value = last_value; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, first_value, last_value); return *this; } @@ -493,7 +665,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -505,7 +677,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -514,7 +686,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; first_value = other.first_value; @@ -522,10 +694,39 @@ namespace etl return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -533,7 +734,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - ETL_CONSTEXPR T first() const + ETL_CONSTEXPR T first() const ETL_NOEXCEPT { return first_value; } @@ -541,7 +742,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - ETL_CONSTEXPR T last() const + ETL_CONSTEXPR T last() const ETL_NOEXCEPT { return last_value; } @@ -549,7 +750,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL @@ -558,36 +759,117 @@ namespace etl swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return (lhs.value == rhs.value) && (lhs.first_value == rhs.first_value) && (lhs.last_value == rhs.last_value); - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + const bool Is_Full_Range = (first_value == etl::integral_limits::min) && (last_value == etl::integral_limits::max); + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if (Is_Full_Range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = static_cast(etl::to_unsigned(last_value) - etl::to_unsigned(first_value)) + 1U; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, first_value, last_value, step, subtract); + } + } + T value; ///< The current value. T first_value; ///< The first value in the range. T last_value; ///< The last value in the range. }; + + //************************************************************************* + /// Swaps the values. + //************************************************************************* + template + ETL_CONSTEXPR14 void swap(etl::cyclic_value& lhs, etl::cyclic_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } } // namespace etl +//************************************************************************* +/// Operator == +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs == rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator==(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return (lhs.get() == rhs.get()); +} + +//************************************************************************* +/// Operator != +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs != rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator!=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(lhs == rhs); +} + +//************************************************************************* +/// Operator < +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is less than rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return lhs.get() < rhs.get(); +} + +//************************************************************************* +/// Operator <= +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is less than or equal to rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(rhs < lhs); +} + +//************************************************************************* +/// Operator > +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is greater than rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return rhs < lhs; +} + +//************************************************************************* +/// Operator >= +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is greater than or equal to rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(lhs < rhs); +} + #endif diff --git a/include/etl/error_handler.h b/include/etl/error_handler.h index 9f7dfba6..e234d9bb 100644 --- a/include/etl/error_handler.h +++ b/include/etl/error_handler.h @@ -367,7 +367,6 @@ namespace etl { \ etl::error_handler::error((e)); \ throw((e)); \ - return; \ } \ } while (false) // If the condition fails, calls the error handler then // throws an exception. @@ -377,7 +376,6 @@ namespace etl { \ etl::error_handler::error((e)); \ throw((e)); \ - return (v); \ } \ } while (false) // If the condition fails, calls the error handler then // throws an exception. @@ -391,13 +389,11 @@ namespace etl do { \ etl::error_handler::error((e)); \ throw((e)); \ - return; \ } while (false) // Calls the error handler then throws an exception. #define ETL_ASSERT_FAIL_AND_RETURN_VALUE(e, v) \ do { \ etl::error_handler::error((e)); \ throw((e)); \ - return (v); \ } while (false) // Calls the error handler then throws an exception. #else #define ETL_ASSERT(b, e) \ diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 945f1d87..1bcddc48 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -112,4 +112,5 @@ SOFTWARE. #define ETL_FORMAT_FILE_ID "79" #define ETL_INPLACE_FUNCTION_FILE_ID "80" #define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81" +#define ETL_CYCLIC_VALUE_FILE_ID "82" #endif diff --git a/include/etl/format.h b/include/etl/format.h index 288454fd..f736d025 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -47,10 +47,6 @@ SOFTWARE. #include "variant.h" #include "visitor.h" -#if ETL_USING_FORMAT_FLOATING_POINT - #include -#endif - #if ETL_USING_CPP11 namespace etl @@ -509,7 +505,8 @@ namespace etl struct basic_format_string { inline ETL_CONSTEVAL basic_format_string(const char* fmt) - : _sv(fmt) + : _str(fmt) + , _length(etl::char_traits::length(fmt)) { #if ETL_USING_CPP20 // Compile-time validation: check_format runs at compile time via consteval. @@ -527,12 +524,22 @@ namespace etl ETL_CONSTEXPR string_view get() const { - return _sv; + return string_view(_str, _length); } private: - string_view _sv; + // The format string is deliberately stored as a pointer plus a length, and + // not as a string_view. + // + // A string_view holds a pair of pointers, so constructing one here would + // initialise a member with the pointer arithmetic 'fmt + length(fmt)'. + // Microsoft's compiler (19.44) miscompiles exactly that case in a consteval + // constructor in debug builds: the resulting end pointer, and therefore the + // size of the view, is wrong. Storing the length as an integer instead + // computes correctly, and the view is formed on demand in get(). + const char* _str; + size_t _length; }; template @@ -1740,65 +1747,6 @@ namespace etl double, long double> long_double_format_type; - #if ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH - //*********************************** - // Math function wrappers to handle toolchains that don't provide - // long double math functions (log10l, floorl, powl, modfl, roundl). - // When ETL_FORMAT_NO_LONG_DOUBLE_MATH is defined, long double overloads - // cast through double. For float and double, the standard functions are - // called directly via the template versions. - //*********************************** - inline long double format_log10(long double value) - { - return static_cast(::log10(static_cast(value))); - } - inline long double format_floor(long double value) - { - return static_cast(::floor(static_cast(value))); - } - inline long double format_pow(long double base, long double exp) - { - return static_cast(::pow(static_cast(base), static_cast(exp))); - } - inline long double format_round(long double value) - { - return static_cast(::round(static_cast(value))); - } - inline long double format_modf(long double value, long double* iptr) - { - double d_iptr; - double result = ::modf(static_cast(value), &d_iptr); - *iptr = static_cast(d_iptr); - return static_cast(result); - } - #endif - - template - T format_log10(T value) - { - return ::log10(value); - } - template - T format_floor(T value) - { - return ::floor(value); - } - template - T format_pow(T base, T exp) - { - return ::pow(base, exp); - } - template - T format_round(T value) - { - return ::round(value); - } - template - T format_modf(T value, T* iptr) - { - return ::modf(value, iptr); - } - template void format_floating_default(OutputIt& it, T value, const format_spec_t& spec) { @@ -1819,7 +1767,7 @@ namespace etl } T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); // Take absolute values to avoid casting negative values to unsigned if (sign) @@ -1829,7 +1777,7 @@ namespace etl } unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1856,20 +1804,20 @@ namespace etl bool sign = signbit(value); T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); while (value >= 0x10 || value <= -0x10) { ++exponent_int; value /= 0x10; - fractional = format_modf(value, &integral); + fractional = etl::math::modf(value, &integral); } while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1)) { --exponent_int; value *= 0x10; - fractional = format_modf(value, &integral); + fractional = etl::math::modf(value, &integral); } // Take absolute values to avoid casting negative values to unsigned @@ -1880,7 +1828,7 @@ namespace etl } unsigned long long int scale = int_pow(0x10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1924,8 +1872,8 @@ namespace etl if (abs_value > static_cast(0)) { - exponent_int = static_cast(format_floor(format_log10(abs_value))); - value = abs_value / format_pow(static_cast(10), static_cast(exponent_int)); + exponent_int = static_cast(etl::math::floor(etl::math::log10(abs_value))); + value = abs_value / etl::math::pow(static_cast(10), static_cast(exponent_int)); // Correct for floating-point rounding in log10/pow if (value >= static_cast(10)) { @@ -1944,10 +1892,10 @@ namespace etl } T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1985,7 +1933,7 @@ namespace etl bool sign = signbit(value); T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); // Take absolute values to avoid casting negative values to unsigned if (sign) @@ -1995,7 +1943,7 @@ namespace etl } unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) diff --git a/include/etl/math.h b/include/etl/math.h index 485ddef4..079b51fe 100644 --- a/include/etl/math.h +++ b/include/etl/math.h @@ -131,6 +131,209 @@ namespace etl return value1 == value2; } #include "private/diagnostic_pop.h" + + //*************************************************************************** + // These functions wrap the C math.h functions. + // The functions are overloaded for the different floating point types. + // They provide a consistent interface for float, double and long double. + // They are in the `math` namespace to avoid conflicts with the C library and ETL functions. + // These take the place of std::floor, std::ceil etc which may not be available `No STL` environments. + // Avoids unpredictable behaviour for global calls of ::floor, ::ceil etc when both and are included. + //*************************************************************************** + namespace math + { + //*************************************************************************** + // floor + //*************************************************************************** + ETL_NODISCARD + inline float floor(float value) ETL_NOEXCEPT + { + return ::floorf(value); + } + + //********************************* + ETL_NODISCARD + inline double floor(double value) ETL_NOEXCEPT + { + return ::floor(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return ::floorl(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return static_cast(::floor(static_cast(value))); + } +#endif + + //*************************************************************************** + // ceil + //*************************************************************************** + ETL_NODISCARD + inline float ceil(float value) ETL_NOEXCEPT + { + return ::ceilf(value); + } + + //********************************* + ETL_NODISCARD + inline double ceil(double value) ETL_NOEXCEPT + { + return ::ceil(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return ::ceill(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return static_cast(::ceil(static_cast(value))); + } +#endif + + //*************************************************************************** + // log10 + //*************************************************************************** + ETL_NODISCARD + inline float log10(float value) ETL_NOEXCEPT + { + return ::log10f(value); + } + + //********************************* + ETL_NODISCARD + inline double log10(double value) ETL_NOEXCEPT + { + return ::log10(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return ::log10l(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return static_cast(::log10(static_cast(value))); + } +#endif + + //*************************************************************************** + // round + //*************************************************************************** + ETL_NODISCARD + inline float round(float value) ETL_NOEXCEPT + { + return ::roundf(value); + } + + //********************************* + ETL_NODISCARD + inline double round(double value) ETL_NOEXCEPT + { + return ::round(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return ::roundl(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return static_cast(::round(static_cast(value))); + } +#endif + + //*************************************************************************** + // power + //*************************************************************************** + ETL_NODISCARD + inline float pow(float base, float exponent) ETL_NOEXCEPT + { + return ::powf(base, exponent); + } + + //********************************* + ETL_NODISCARD + inline double pow(double base, double exponent) ETL_NOEXCEPT + { + return ::pow(base, exponent); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double pow(long double base, long double exponent) ETL_NOEXCEPT + { + return ::powl(base, exponent); + } +#else + //********************************* + ETL_NODISCARD + inline long double pow(long double base, long double exponent) ETL_NOEXCEPT + { + return static_cast(::pow(static_cast(base), static_cast(exponent))); + } +#endif + + //*************************************************************************** + // modf + //*************************************************************************** + inline float modf(float value, float* intpart) ETL_NOEXCEPT + { + return ::modff(value, intpart); + } + + //********************************* + inline double modf(double value, double* intpart) ETL_NOEXCEPT + { + return ::modf(value, intpart); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + return ::modfl(value, intpart); + } +#else + //********************************* + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + double d_iptr; + double result = ::modf(static_cast(value), &d_iptr); + *intpart = static_cast(d_iptr); + + return static_cast(result); + } +#endif + } // namespace math } // namespace etl #endif diff --git a/include/etl/message_timer.h b/include/etl/message_timer.h index a9c0ce31..cb2151ec 100644 --- a/include/etl/message_timer.h +++ b/include/etl/message_timer.h @@ -158,6 +158,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -167,6 +168,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -174,6 +184,16 @@ namespace etl { etl::message_timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -316,6 +336,10 @@ namespace etl etl::timer::id::type tail; etl::message_timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; } // namespace private_message_timer @@ -579,6 +603,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* diff --git a/include/etl/message_timer_atomic.h b/include/etl/message_timer_atomic.h index 8d716d8b..02dc4ab2 100644 --- a/include/etl/message_timer_atomic.h +++ b/include/etl/message_timer_atomic.h @@ -183,11 +183,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { timer.delta = timer.period; @@ -195,6 +193,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -308,6 +311,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -467,6 +480,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -476,6 +490,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -483,6 +506,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -625,6 +658,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/message_timer_interrupt.h b/include/etl/message_timer_interrupt.h index 33ac248c..18aa4d24 100644 --- a/include/etl/message_timer_interrupt.h +++ b/include/etl/message_timer_interrupt.h @@ -189,11 +189,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -202,6 +200,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -316,6 +319,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -473,6 +486,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -482,6 +496,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -489,6 +512,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -631,6 +664,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/message_timer_locked.h b/include/etl/message_timer_locked.h index 92b41488..aa95aa57 100644 --- a/include/etl/message_timer_locked.h +++ b/include/etl/message_timer_locked.h @@ -186,11 +186,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { timer.delta = timer.period; @@ -198,6 +196,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -323,6 +326,16 @@ namespace etl unlock = unlock_; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -481,6 +494,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -490,6 +504,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -497,6 +520,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -639,6 +672,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/platform.h b/include/etl/platform.h index 494b4981..0eb78ae5 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -175,16 +175,17 @@ SOFTWARE. #endif //************************************* -// Helper macro for ETL_FORMAT_NO_LONG_DOUBLE_MATH. -// Define ETL_FORMAT_NO_LONG_DOUBLE_MATH if the toolchain does not provide +// Helper macro for ETL_NO_LONG_DOUBLE_MATH. +// Define ETL_NO_LONG_DOUBLE_MATH if the toolchain does not provide // long double math functions (log10l, floorl, powl, modfl, roundl). // When defined, long double arguments are cast to double for math operations. -#if defined(ETL_FORMAT_NO_LONG_DOUBLE_MATH) - #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 0 - #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 1 +// ETL_FORMAT_NO_LONG_DOUBLE_MATH is deprecated, but still supported for backwards compatibility. +#if defined(ETL_NO_LONG_DOUBLE_MATH) || defined(ETL_FORMAT_NO_LONG_DOUBLE_MATH) + #define ETL_USING_LONG_DOUBLE_MATH 0 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 1 #else - #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 1 - #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 0 + #define ETL_USING_LONG_DOUBLE_MATH 1 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 0 #endif //************************************* diff --git a/include/etl/private/chrono/day.h b/include/etl/private/chrono/day.h index a929c601..bbca6a59 100644 --- a/include/etl/private/chrono/day.h +++ b/include/etl/private/chrono/day.h @@ -215,7 +215,7 @@ namespace etl [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/duration.h b/include/etl/private/chrono/duration.h index bc8d6ba5..fc0b6cb3 100644 --- a/include/etl/private/chrono/duration.h +++ b/include/etl/private/chrono/duration.h @@ -492,7 +492,37 @@ namespace etl common_t l = etl::chrono::duration_cast(lhs); common_t r = etl::chrono::duration_cast(rhs); + #if ETL_USING_STL return (l.count() <=> r.count()); + #else + // The STL returns the ordering category of the representation type. + // Mirror that: a floating point representation is only partially ordered. + if constexpr (etl::is_floating_point::value) + { + if (l.count() < r.count()) + { + return etl::partial_ordering::less; + } + else if (r.count() < l.count()) + { + return etl::partial_ordering::greater; + } + else if ((l.count() <= r.count()) && (r.count() <= l.count())) + { + // Equivalent. Expressed without '==' so that -Wfloat-equal is not triggered. + return etl::partial_ordering::equivalent; + } + else + { + // At least one of the values is NaN. + return etl::partial_ordering::unordered; + } + } + else + { + return etl::make_strong_ordering((l.count() < r.count()) ? -1 : (r.count() < l.count()) ? 1 : 0); + } + #endif } #endif diff --git a/include/etl/private/chrono/month.h b/include/etl/private/chrono/month.h index 662bb632..b166f509 100644 --- a/include/etl/private/chrono/month.h +++ b/include/etl/private/chrono/month.h @@ -228,7 +228,7 @@ namespace etl [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/month_day.h b/include/etl/private/chrono/month_day.h index dcefbff6..e53f8ac8 100644 --- a/include/etl/private/chrono/month_day.h +++ b/include/etl/private/chrono/month_day.h @@ -302,7 +302,7 @@ namespace etl [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT { - return (static_cast(mdl1.month()) <=> static_cast(mdl2.month())); + return mdl1.month() <=> mdl2.month(); } #endif } // namespace chrono diff --git a/include/etl/private/chrono/year.h b/include/etl/private/chrono/year.h index 49ad3530..35f67853 100644 --- a/include/etl/private/chrono/year.h +++ b/include/etl/private/chrono/year.h @@ -244,7 +244,7 @@ namespace etl [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT { - return (static_cast(y1) <=> static_cast(y2)); + return etl::make_strong_ordering(y1.compare(y2)); } #endif diff --git a/include/etl/private/to_string_helper.h b/include/etl/private/to_string_helper.h index a8969eae..4baaaf76 100644 --- a/include/etl/private/to_string_helper.h +++ b/include/etl/private/to_string_helper.h @@ -44,8 +44,6 @@ SOFTWARE. #include "../negative.h" #include "../type_traits.h" -#include - #if ETL_USING_STL && ETL_USING_CPP11 #include // For std::begin, std::end and std::size #endif @@ -347,11 +345,11 @@ namespace etl } // Find the integral part of the floating point - T f_integral = ::floor(scaled); + T f_integral = etl::math::floor(scaled); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. - uworkspace_t fractional = static_cast(::round((scaled - f_integral) * multiplier)); + uworkspace_t fractional = static_cast(etl::math::round((scaled - f_integral) * multiplier)); // Check for a rounding carry to the integral. if (fractional == multiplier) @@ -404,7 +402,7 @@ namespace etl } // Find the integral part of the floating point - T f_integral = ::floor(etl::absolute(value)); + T f_integral = etl::math::floor(etl::absolute(value)); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. diff --git a/include/etl/span.h b/include/etl/span.h index d948c355..71c7e907 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -644,8 +644,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -672,8 +671,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span((pbegin + Extent) - count, (pbegin + Extent)) @@ -735,7 +733,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true) @@ -1003,7 +1001,7 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range)); @@ -1017,7 +1015,7 @@ namespace etl //************************************************************************* /// Returns a reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range)); @@ -1197,7 +1195,7 @@ namespace etl /// span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range)); @@ -1212,8 +1210,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -1228,7 +1225,7 @@ namespace etl /// Obtains a span that is a view over the last COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1242,8 +1239,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pend - count, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1261,7 +1257,7 @@ namespace etl //************************************************************************* template ETL_NODISCARD ETL_CONSTEXPR etl::span< element_type, COUNT != etl::dynamic_extent ? COUNT : etl::dynamic_extent> subspan() const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true) @@ -1305,7 +1301,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range)); ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range)); diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 2cb20e37..0827636c 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -355,7 +355,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::string_view_bounds /// if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds)); diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index b01b308f..a7df81ae 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -4310,13 +4310,53 @@ namespace etl #endif #if ETL_USING_CPP11 + //********************************************* + // Builtin-free noexcept-constructibility/assignability detection. Used when + // the __is_nothrow_constructible/__is_nothrow_assignable builtins are + // unavailable. Mirrors the void_t-based is_constructible/is_assignable + // fallbacks above so that (e.g.) a class with implicit noexcept move + // operations is correctly reported nothrow-(move-)constructible/assignable, + // and a const-qualified type is correctly reported NOT nothrow-assignable + // (the old is_arithmetic||is_pointer fallback ignored const-qualification). + #if !ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + namespace private_type_traits + { + template + struct is_nothrow_constructible_ : etl::false_type + { + }; + + template + struct is_nothrow_constructible_()...))>, T, TArgs...> + : etl::bool_constant()...))> + { + }; + } // namespace private_type_traits + #endif + + #if !ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + namespace private_type_traits + { + template + struct is_nothrow_assignable_ : etl::false_type + { + }; + + template + struct is_nothrow_assignable_() = etl::declval())>, T, U> + : etl::bool_constant() = etl::declval())> + { + }; + } // namespace private_type_traits + #endif + //********************************************* // is_nothrow_constructible template #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE struct is_nothrow_constructible : public etl::bool_constant<__is_nothrow_constructible(T, TArgs...)> #else - struct is_nothrow_constructible : public etl::bool_constant::value || etl::is_pointer::value> + struct is_nothrow_constructible : public private_type_traits::is_nothrow_constructible_, T, TArgs...> #endif { }; @@ -4324,33 +4364,21 @@ namespace etl //********************************************* // is_nothrow_default_constructible template - #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE struct is_nothrow_default_constructible : public etl::is_nothrow_constructible - #else - struct is_nothrow_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> - #endif { }; //********************************************* // is_nothrow_copy_constructible template - #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE struct is_nothrow_copy_constructible : public etl::is_nothrow_constructible::type> - #else - struct is_nothrow_copy_constructible : public etl::bool_constant::value || etl::is_pointer::value> - #endif { }; //********************************************* // is_nothrow_move_constructible template - #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE struct is_nothrow_move_constructible : public etl::is_nothrow_constructible::type> - #else - struct is_nothrow_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> - #endif { }; @@ -4360,7 +4388,7 @@ namespace etl #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE struct is_nothrow_assignable : public etl::bool_constant<__is_nothrow_assignable(T, U)> #else - struct is_nothrow_assignable : public etl::bool_constant::value || etl::is_pointer::value> + struct is_nothrow_assignable : public private_type_traits::is_nothrow_assignable_, T, U> #endif { }; @@ -4368,24 +4396,16 @@ namespace etl //********************************************* // is_nothrow_copy_assignable template - #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE struct is_nothrow_copy_assignable : public etl::is_nothrow_assignable::type, typename etl::add_lvalue_reference::type> - #else - struct is_nothrow_copy_assignable : public etl::bool_constant::value || etl::is_pointer::value> - #endif { }; //********************************************* // is_nothrow_move_assignable template - #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE struct is_nothrow_move_assignable : public etl::is_nothrow_assignable::type, typename etl::add_rvalue_reference::type> - #else - struct is_nothrow_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> - #endif { }; #endif @@ -5175,20 +5195,26 @@ namespace etl #elif ETL_USING_CPP11 namespace private_type_traits { + template + struct is_swappable_helper; + namespace swap_detect { - // Bring std::swap into scope, if it is available, so that the unqualified - // call below finds it in those configurations that do not define etl::swap. - // Otherwise etl::swap is found by ordinary lookup in the enclosing - // namespace. Either way, a swap found by argument dependent lookup takes - // part in overload resolution. - #if ETL_USING_STL || defined(ETL_IN_UNIT_TEST) - using std::swap; - #endif + // A dummy declaration that can never be selected, as it takes no + // arguments, but which makes the name visible to ordinary lookup so that + // the unqualified calls below are well formed. + // Ordinary lookup stops here, so only a swap found by argument dependent + // lookup takes part in overload resolution. + // The generic swap that the standard library (or etl) provides is not + // detected this way, because not every implementation constrains it + // (Microsoft's C++14 std::swap does not), which would make every type + // appear to be swappable. Its viability is determined by the + // is_default_swappable traits below instead. + void swap(); // Selected only if 'swap(declval(), declval())' is a valid expression. template - struct is_swappable_test + struct is_adl_swappable_test { private: @@ -5206,7 +5232,7 @@ namespace etl // Selected only if both 'swap(declval(), declval())' and // 'swap(declval(), declval())' are valid expressions. template - struct is_swappable_with_test + struct is_adl_swappable_with_test { private: @@ -5221,15 +5247,61 @@ namespace etl static ETL_CONSTANT bool value = decltype(test(0))::value; }; + + // Determines whether an expression of type T can be bound to a non-const + // lvalue reference to its referenced type. + // This is not simply 'is_lvalue_reference', because some compilers offer + // an extension that binds a class rvalue to a non-const lvalue reference. + template + void binds_to_lvalue_reference_test(T&); + + template + struct binds_to_lvalue_reference : etl::false_type + { + }; + + template + struct binds_to_lvalue_reference< + T, etl::void_t::type>(etl::declval()))>> : etl::true_type + { + }; } // namespace swap_detect + // Determines whether the generic swap(T&, T&) is viable for T. template - struct is_swappable_helper : etl::bool_constant::value> + struct is_default_swappable : etl::bool_constant::value && etl::is_move_assignable::value> + { + }; + + // The generic swap for arrays requires the element type to be swappable. + template + struct is_default_swappable : is_swappable_helper + { + }; + + // An array of unknown bound cannot be swapped. + template + struct is_default_swappable : etl::false_type + { + }; + + // The generic swap only participates when both arguments are lvalues of + // the same type. + template + struct is_default_swappable_with + : etl::bool_constant::value && etl::is_same::value + && is_default_swappable::type>::value> + { + }; + + template + struct is_swappable_helper : etl::bool_constant::value || is_default_swappable::value> { }; template - struct is_swappable_with_helper : etl::bool_constant::value> + struct is_swappable_with_helper + : etl::bool_constant::value || is_default_swappable_with::value> { }; } // namespace private_type_traits @@ -5271,43 +5343,75 @@ namespace etl #elif ETL_USING_CPP11 namespace private_type_traits { + template + struct is_nothrow_swappable_helper; + namespace swap_detect { - // Only 'true' if the type is swappable and the swap expression is noexcept. - template ::value> - struct is_nothrow_swappable_test + // Only 'true' if the type has an ADL found swap and the swap expression is noexcept. + template ::value> + struct is_nothrow_adl_swappable_test { static ETL_CONSTANT bool value = false; }; template - struct is_nothrow_swappable_test + struct is_nothrow_adl_swappable_test { static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())); }; - // Only 'true' if the types are swappable with each other and both of the - // swap expressions are noexcept. - template ::value> - struct is_nothrow_swappable_with_test + // Only 'true' if the types have ADL found swaps for each other and both of + // the swap expressions are noexcept. + template ::value> + struct is_nothrow_adl_swappable_with_test { static ETL_CONSTANT bool value = false; }; template - struct is_nothrow_swappable_with_test + struct is_nothrow_adl_swappable_with_test { static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())) && noexcept(swap(etl::declval(), etl::declval())); }; } // namespace swap_detect + // Determines whether the generic swap(T&, T&) is viable for T and cannot throw. template - struct is_nothrow_swappable_helper : etl::bool_constant::value> + struct is_nothrow_default_swappable + : etl::bool_constant::value && etl::is_nothrow_move_assignable::value> + { + }; + + template + struct is_nothrow_default_swappable : is_nothrow_swappable_helper + { + }; + + template + struct is_nothrow_default_swappable : etl::false_type { }; template - struct is_nothrow_swappable_with_helper : etl::bool_constant::value> + struct is_nothrow_default_swappable_with + : etl::bool_constant::value && etl::is_same::value + && is_nothrow_default_swappable::type>::value> + { + }; + + // An ADL found swap hides the generic one, so it decides the result. + template + struct is_nothrow_swappable_helper + : etl::bool_constant::value ? swap_detect::is_nothrow_adl_swappable_test::value + : is_nothrow_default_swappable::value> + { + }; + + template + struct is_nothrow_swappable_with_helper + : etl::bool_constant::value ? swap_detect::is_nothrow_adl_swappable_with_test::value + : is_nothrow_default_swappable_with::value> { }; } // namespace private_type_traits diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index cba1874f..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" @@ -335,6 +336,48 @@ namespace etl return ((Endian == ETL_ENDIAN_LITTLE) ? index : (Size - 1U - index)) * 8U; } + //************************************************************************* + /// Gathers the bytes of a store into a value, and scatters a value back out. + /// Unrolled at source level, so byte swap recognition sees no loop. + //************************************************************************* + template + struct gather_bytes + { + static ETL_CONSTEXPR14 TValue from(const unsigned char* store) + { + return static_cast(gather_bytes::from(store) + | (static_cast(store[Index]) << byte_shift(Index))); + } + }; + + template + struct gather_bytes + { + static ETL_CONSTEXPR14 TValue from(const unsigned char* store) + { + return static_cast(static_cast(store[0]) << byte_shift(0)); + } + }; + + template + struct scatter_bytes + { + static ETL_CONSTEXPR14 void into(TValue value, unsigned char* store) + { + scatter_bytes::into(value, store); + store[Index] = static_cast(value >> byte_shift(Index)); + } + }; + + template + struct scatter_bytes + { + static ETL_CONSTEXPR14 void into(TValue value, unsigned char* store) + { + store[0] = static_cast(value >> byte_shift(0)); + } + }; + #if ETL_USING_BUILTIN_BIT_CAST //************************************************************************* /// Unsigned integer type of a given byte size, used as a constexpr-capable @@ -402,36 +445,59 @@ namespace etl // latter is not defined for 'bool'. typedef typename etl::unsigned_type::type unsigned_t; - unsigned_t uvalue = static_cast(value); + private_unaligned_type::scatter_bytes::into(static_cast(value), store); + } - for (size_t i = 0UL; i < Size_; ++i) - { - store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); - } + //******************************* + template + static ETL_CONSTEXPR14 T value_from(const_pointer store) + { + typedef typename etl::unsigned_type::type unsigned_t; + + return static_cast(private_unaligned_type::gather_bytes::from(store)); } //******************************* template static ETL_CONSTEXPR14 void copy_store_to_value(const_pointer store, T & value) { - typedef typename etl::unsigned_type::type unsigned_t; - - unsigned_t uvalue = unsigned_t(0); - - for (size_t i = 0UL; i < Size_; ++i) - { - uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); - } - - value = static_cast(uvalue); + 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]; + } } } }; @@ -462,12 +528,9 @@ namespace etl { typedef typename private_unaligned_type::uint_of_size::type uint_t; - uint_t uvalue = etl::bit_cast(value); + const uint_t uvalue = etl::bit_cast(value); - for (size_t i = 0UL; i < Size_; ++i) - { - store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); - } + private_unaligned_type::scatter_bytes::into(uvalue, store); } #endif @@ -510,12 +573,7 @@ namespace etl { typedef typename private_unaligned_type::uint_of_size::type uint_t; - uint_t uvalue = uint_t(0); - - for (size_t i = 0UL; i < Size_; ++i) - { - uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); - } + const uint_t uvalue = private_unaligned_type::gather_bytes::from(store); value = etl::bit_cast(uvalue); } @@ -553,16 +611,59 @@ namespace etl do_copy_store_to_value(store, value, use_bit_cast_t()); } + //******************************* + template + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + T + value_from(const_pointer store) + { + T value = T(); + + copy_store_to_value(store, value); + + return value; + } + //******************************* // 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]; + } } } }; @@ -714,11 +815,7 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 operator T() const { - T value = T(); - - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + return value_from(this->storage); } //************************************************************************* @@ -726,11 +823,16 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 T value() const { - T value = T(); + return value_from(this->storage); + } - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + //************************************************************************* + /// Get the value directly from a byte buffer. + /// Unlike construction from an address, the bytes are not copied to storage. + //************************************************************************* + static ETL_CONSTEXPR14 T value_from(const_pointer address) + { + return unaligned_copy::template value_from(address); } }; ETL_END_PACKED @@ -741,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. @@ -793,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_) @@ -851,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) @@ -898,11 +1475,7 @@ namespace etl //************************************************************************* operator T() const { - T value = T(); - - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + return value_from(this->storage); } //************************************************************************* @@ -910,11 +1483,16 @@ namespace etl //************************************************************************* T value() const { - T value = T(); + return value_from(this->storage); + } - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + //************************************************************************* + /// Get the value directly from a byte buffer. + /// Unlike construction from an address, the bytes are not copied to storage. + //************************************************************************* + static T value_from(const_pointer address) + { + return unaligned_copy::template value_from(address); } //************************************************************************* @@ -937,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/include/etl/vector.h b/include/etl/vector.h index beac6cdc..61ee6c26 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -393,7 +393,7 @@ namespace etl typename etl::enable_if::value && etl::is_convertible::value_type, T>::value, typename etl::enable_if::value, void>::type>::type - assign(TIterator first, TIterator last) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + assign(TIterator first, TIterator last) { #if ETL_USING_CPP11 ETL_STATIC_ASSERT((etl::is_same::type, @@ -2082,7 +2082,6 @@ namespace etl /// Copy constructor. //************************************************************************* vector_ext(const vector_ext& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2094,7 +2093,7 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - vector_ext& operator=(const vector_ext& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(const vector_ext& rhs) { if (&rhs != this) { @@ -2109,7 +2108,6 @@ namespace etl /// Move constructor. //************************************************************************* vector_ext(vector_ext&& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2123,7 +2121,7 @@ namespace etl //************************************************************************* /// Move assignment operator. //************************************************************************* - vector_ext& operator=(vector_ext&& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(vector_ext&& rhs) { if (&rhs != this) { diff --git a/test/run-syntax-checks.sh b/test/run-syntax-checks.sh index fcac0287..2fd0c804 100755 --- a/test/run-syntax-checks.sh +++ b/test/run-syntax-checks.sh @@ -11,14 +11,14 @@ TitleColour=$'\033[38;2;107;210;255m' HelpColour=$'\033[38;2;250;180;250m' NoColour=$'\033[0m' -ParseGitBranch() +ParseGitBranch() { git rev-parse --abbrev-ref HEAD } SetConfigurationName() { - configuration_name=$1 + configuration_name=$1 } PrintHeader() @@ -147,10 +147,9 @@ RunCheck() local cc_name=$1 local msg=$2 local no_stl=$3 - local builtins=$4 - local user_defined=$5 - local force_03=$6 - local build_dir=$7 + local user_defined=$4 + local force_03=$5 + local build_dir=$6 if [ "$compiler_enabled" != "$cc_name" ] && [ "$compiler_enabled" != "All compilers" ]; then return @@ -171,7 +170,7 @@ RunCheck() rm -rdf bgcc rm -rdf bclang cmake -E make_directory bgcc bclang - CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USE_TYPE_TRAITS_BUILTINS=$builtins -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. + CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. cmake --build $build_dir if [ $? -eq 0 ]; then PassedCompilation @@ -193,25 +192,21 @@ RunStandard() cxx_standard=$std # GCC configurations - RunCheck gcc "STL" OFF OFF OFF OFF bgcc - RunCheck gcc "No STL" ON OFF OFF OFF bgcc - RunCheck gcc "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck gcc "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck gcc "STL" OFF OFF OFF bgcc + RunCheck gcc "No STL" ON OFF OFF bgcc if [ "$has_force_03" = "1" ]; then - RunCheck gcc "STL - Force C++03" OFF OFF OFF ON bgcc - RunCheck gcc "No STL - Force C++03" ON OFF OFF ON bgcc + RunCheck gcc "STL - Force C++03" OFF OFF ON bgcc + RunCheck gcc "No STL - Force C++03" ON OFF ON bgcc fi # Clang configurations - RunCheck clang "STL" OFF OFF OFF OFF bclang - RunCheck clang "No STL" ON OFF OFF OFF bclang - RunCheck clang "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck clang "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck clang "STL" OFF OFF OFF bclang + RunCheck clang "No STL" ON OFF OFF bclang if [ "$has_force_03" = "1" ]; then - RunCheck clang "STL - Force C++03" OFF OFF OFF ON bclang - RunCheck clang "No STL - Force C++03" ON OFF OFF ON bclang + RunCheck clang "STL - Force C++03" OFF OFF ON bclang + RunCheck clang "No STL - Force C++03" ON OFF ON bclang fi } diff --git a/test/syntax_check/clamped_value.h.t.cpp b/test/syntax_check/clamped_value.h.t.cpp index 7651ff8c..31f6d549 100644 --- a/test/syntax_check/clamped_value.h.t.cpp +++ b/test/syntax_check/clamped_value.h.t.cpp @@ -37,3 +37,13 @@ etl::clamped_value clamped_double_run_time; #if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE etl::clamped_value clamped_float_compile_time; #endif + +#if ETL_USING_CPP11 +const etl::clamped_value_range referenced_clamped_range(0, 10); +etl::referenced_clamped_value referenced_clamped_int(referenced_clamped_range); + + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +const etl::clamped_value_range referenced_clamped_float_range(-1.0f, 1.0f); +etl::referenced_clamped_value referenced_clamped_float(referenced_clamped_float_range); + #endif +#endif diff --git a/test/test_callback_timer_atomic.cpp b/test/test_callback_timer_atomic.cpp index 0dcea9ab..06e543f6 100644 --- a/test/test_callback_timer_atomic.cpp +++ b/test/test_callback_timer_atomic.cpp @@ -149,6 +149,35 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_atomic<1, std::atomic_uint32_t>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + using timer_callback_type = etl::icallback_timer_atomic::callback_type; + timer_callback_type restart_in_callback_member = + timer_callback_type::create(); + SUITE(test_callback_timer_atomic) { //************************************************************************* @@ -855,6 +884,54 @@ namespace CHECK_EQUAL(3, timerInsertRemoveTest.removed); } + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_atomic_restart_in_callback_no_circular_reference) + { + etl::callback_timer_atomic<1, std::atomic_uint32_t> timer_controller; + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + } + //************************************************************************* #if REALTIME_TEST diff --git a/test/test_callback_timer_interrupt.cpp b/test/test_callback_timer_interrupt.cpp index b7830cf4..e2d71377 100644 --- a/test/test_callback_timer_interrupt.cpp +++ b/test/test_callback_timer_interrupt.cpp @@ -162,6 +162,33 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_interrupt<1, ScopedGuard>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + callback_type restart_in_callback_member = callback_type::create(); + SUITE(test_callback_timer_interrupt) { //************************************************************************* @@ -1001,5 +1028,55 @@ namespace // Check the CHECK_EQUAL(0U, ScopedGuard::guard_count); } + + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_interrupt_restart_in_callback_no_circular_reference) + { + etl::callback_timer_interrupt<1, ScopedGuard> timer_controller; + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + + CHECK_EQUAL(0U, ScopedGuard::guard_count); + } } } // namespace diff --git a/test/test_callback_timer_locked.cpp b/test/test_callback_timer_locked.cpp index b87c4b8c..2ce71280 100644 --- a/test/test_callback_timer_locked.cpp +++ b/test/test_callback_timer_locked.cpp @@ -184,6 +184,33 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_locked<1>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + callback_type restart_in_callback_member = callback_type::create(); + SUITE(test_callback_timer_locked) { //************************************************************************* @@ -1004,6 +1031,59 @@ namespace CHECK_FALSE(timer_controller.has_active_timer()); } + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_locked_restart_in_callback_no_circular_reference) + { + locks.clear(); + try_lock_type try_lock = try_lock_type::create(); + lock_type lock = lock_type::create(); + unlock_type unlock = unlock_type::create(); + + etl::callback_timer_locked<1> timer_controller(try_lock, lock, unlock); + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + } + //************************************************************************* #if REALTIME_TEST diff --git a/test/test_chrono_duration.cpp b/test/test_chrono_duration.cpp index 81ef8a0b..1372d6f6 100644 --- a/test/test_chrono_duration.cpp +++ b/test/test_chrono_duration.cpp @@ -1052,9 +1052,34 @@ namespace Chrono::seconds s1(2); // 2 seconds Chrono::milliseconds ms1(1500); // 1500 milliseconds - CHECK_TRUE(s1 <=> s1 == std::strong_ordering::equal); - CHECK_TRUE(ms1 <=> s1 == std::strong_ordering::less); - CHECK_TRUE(s1 <=> ms1 == std::strong_ordering::greater); + CHECK_TRUE(s1 <=> s1 == etl::strong_ordering::equal); + CHECK_TRUE(ms1 <=> s1 == etl::strong_ordering::less); + CHECK_TRUE(s1 <=> ms1 == etl::strong_ordering::greater); + + static_assert(etl::is_same ms1), etl::strong_ordering>::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_duration_spaceship_operator_floating_point_representation) + { + typedef Chrono::duration fp_seconds; + + fp_seconds fs1(1.5); + fp_seconds fs2(2.5); + + // A floating point representation is only partially ordered. + static_assert(etl::is_same fs2), etl::partial_ordering>::value, "Must return etl::partial_ordering"); + + CHECK_TRUE((fs1 <=> fs1) == etl::partial_ordering::equivalent); + CHECK_TRUE((fs1 <=> fs2) == etl::partial_ordering::less); + CHECK_TRUE((fs2 <=> fs1) == etl::partial_ordering::greater); + + fp_seconds nan_seconds(etl::numeric_limits::quiet_NaN()); + + CHECK_TRUE((nan_seconds <=> fs1) == etl::partial_ordering::unordered); + CHECK_FALSE((nan_seconds <=> fs1) < 0); + CHECK_FALSE((nan_seconds <=> fs1) == 0); + CHECK_FALSE((nan_seconds <=> fs1) > 0); } #endif diff --git a/test/test_chrono_month_day.cpp b/test/test_chrono_month_day.cpp index fd6ea597..91a61482 100644 --- a/test/test_chrono_month_day.cpp +++ b/test/test_chrono_month_day.cpp @@ -97,9 +97,9 @@ namespace Chrono::month_day md2{Chrono::February, Chrono::day{10}}; Chrono::month_day md3{Chrono::January, Chrono::day{15}}; - CHECK_TRUE((md1 <=> md3) == std::strong_ordering::equal); // Same month and day - CHECK_TRUE((md1 <=> md2) == std::strong_ordering::less); // Different month and day - CHECK_TRUE((md2 <=> md1) == std::strong_ordering::greater); // Same month and day + CHECK_TRUE((md1 <=> md3) == etl::strong_ordering::equal); // Same month and day + CHECK_TRUE((md1 <=> md2) == etl::strong_ordering::less); // Different month and day + CHECK_TRUE((md2 <=> md1) == etl::strong_ordering::greater); // Same month and day } #endif diff --git a/test/test_chrono_month_day_last.cpp b/test/test_chrono_month_day_last.cpp index bd5bee7f..927a9369 100644 --- a/test/test_chrono_month_day_last.cpp +++ b/test/test_chrono_month_day_last.cpp @@ -100,6 +100,20 @@ namespace CHECK_TRUE(month_day_last1 != month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_month_day_last_spaceship_operator) + { + Chrono::month_day_last month_day_last1(Chrono::January); + Chrono::month_day_last month_day_last2(Chrono::February); + Chrono::month_day_last month_day_last3(Chrono::January); + + CHECK_TRUE((month_day_last1 <=> month_day_last3) == etl::strong_ordering::equal); + CHECK_TRUE((month_day_last1 <=> month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((month_day_last2 <=> month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_month_day_last_hashes_are_unique) diff --git a/test/test_chrono_time_point.cpp b/test/test_chrono_time_point.cpp index 0bbea37f..89a00227 100644 --- a/test/test_chrono_time_point.cpp +++ b/test/test_chrono_time_point.cpp @@ -377,6 +377,8 @@ namespace CHECK_TRUE((tp10d <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20d) < 0); CHECK_TRUE((tp20d <=> tp10h) > 0); + + static_assert(etl::is_same tp20d), etl::strong_ordering>::value, "Must return etl::strong_ordering"); #endif } diff --git a/test/test_chrono_year_month.cpp b/test/test_chrono_year_month.cpp index e23bdf6d..ad051386 100644 --- a/test/test_chrono_year_month.cpp +++ b/test/test_chrono_year_month.cpp @@ -97,11 +97,11 @@ namespace Chrono::year_month ym2{Chrono::year(2001), Chrono::January}; Chrono::year_month ym3{Chrono::year(2000), Chrono::February}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day.cpp b/test/test_chrono_year_month_day.cpp index 8f57f822..a83f7ebe 100644 --- a/test/test_chrono_year_month_day.cpp +++ b/test/test_chrono_year_month_day.cpp @@ -107,13 +107,13 @@ namespace Chrono::year_month_day ym3{Chrono::year(2000), Chrono::February, Chrono::day(1)}; Chrono::year_month_day ym4{Chrono::year(2000), Chrono::January, Chrono::day(2)}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym4) == std::strong_ordering::less); - CHECK_TRUE((ym4 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym4) == etl::strong_ordering::less); + CHECK_TRUE((ym4 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day_last.cpp b/test/test_chrono_year_month_day_last.cpp index 53a556af..8edaca2f 100644 --- a/test/test_chrono_year_month_day_last.cpp +++ b/test/test_chrono_year_month_day_last.cpp @@ -160,6 +160,23 @@ namespace CHECK_TRUE(year_month_day_last1 != year_month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_year_month_day_last_spaceship_operator) + { + Chrono::year_month_day_last year_month_day_last1(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last2(Chrono::year(2001), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last3(Chrono::year(2000), Chrono::month_day_last(Chrono::February)); + Chrono::year_month_day_last year_month_day_last4(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last4) == etl::strong_ordering::equal); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last2 <=> year_month_day_last1) == etl::strong_ordering::greater); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last3) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last3 <=> year_month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_year_month_day_last_hashes_are_unique) diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp index cdf81420..09a64781 100644 --- a/test/test_clamped_value.cpp +++ b/test/test_clamped_value.cpp @@ -531,5 +531,157 @@ namespace } #endif #endif + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_referenced_clamped_value_uses_shared_range) + { + const etl::clamped_value_range range(2, 7); + etl::referenced_clamped_value value1(range, 1); + etl::referenced_clamped_value value2(range, 8); + + CHECK_EQUAL(2, value1.get()); + CHECK_EQUAL(7, value2.get()); + CHECK_EQUAL(2, value1.min()); + CHECK_EQUAL(7, value1.max()); + CHECK_EQUAL(&range, &value1.range()); + CHECK_EQUAL(&range, &value2.range()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_arithmetic_saturates) + { + const etl::clamped_value_range range(-2, 3); + etl::referenced_clamped_value value(range, 0); + + value += 2; + CHECK_EQUAL(2, value.get()); + ++value; + ++value; + CHECK_EQUAL(3, value.get()); + value -= 4; + CHECK_EQUAL(-1, value.get()); + --value; + --value; + CHECK_EQUAL(-2, value.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_modifiers_and_postfix_operators) + { + const etl::clamped_value_range range(2, 7); + etl::referenced_clamped_value value(range); + + CHECK_EQUAL(2, value.get()); + value.set(4); + CHECK_EQUAL(4, value.get()); + + etl::referenced_clamped_value previous = value++; + CHECK_EQUAL(4, previous.get()); + CHECK_EQUAL(5, value.get()); + + previous = value--; + CHECK_EQUAL(5, previous.get()); + CHECK_EQUAL(4, value.get()); + + value.to_max(); + CHECK_EQUAL(7, value.get()); + value.to_min(); + CHECK_EQUAL(2, value.get()); + + value = 100; + CHECK_EQUAL(7, value.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_default_range_and_comparisons) + { + const etl::clamped_value_range default_range; + const etl::clamped_value_range range(0, 10); + etl::referenced_clamped_value value1(range, 3); + etl::referenced_clamped_value value2(range, 4); + const int converted = value1; + + CHECK_EQUAL(etl::numeric_limits::lowest(), default_range.min()); + CHECK_EQUAL(etl::numeric_limits::max(), default_range.max()); + CHECK_EQUAL(3, converted); + CHECK(value1 == value1); + CHECK(value1 != value2); + CHECK(value1 < value2); + CHECK(value1 <= value2); + CHECK(value2 > value1); + CHECK(value2 >= value1); + CHECK(value1 < 4); + CHECK(3 == value1); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_copy_assignment_and_swap_ranges) + { + const etl::clamped_value_range range1(0, 10); + const etl::clamped_value_range range2(20, 30); + etl::referenced_clamped_value value1(range1, 4); + etl::referenced_clamped_value value2(range2, 24); + + etl::referenced_clamped_value copy(value1); + CHECK_EQUAL(&range1, ©.range()); + CHECK_EQUAL(4, copy.get()); + + copy = value2; + CHECK_EQUAL(&range2, ©.range()); + CHECK_EQUAL(24, copy.get()); + + swap(value1, value2); + CHECK_EQUAL(&range2, &value1.range()); + CHECK_EQUAL(24, value1.get()); + CHECK_EQUAL(&range1, &value2.range()); + CHECK_EQUAL(4, value2.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_rejects_invalid_range) + { + CHECK_THROW((etl::clamped_value_range(7, 2)), etl::exception); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_rejects_temporary_range) + { + typedef etl::clamped_value_range range_type; + typedef etl::referenced_clamped_value value_type; + + CHECK_FALSE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_constructible::value)); + } + + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_referenced_floating_clamped_value) + { + const etl::clamped_value_range range(-1.5F, 2.5F); + etl::referenced_clamped_value value(range, 0.5F); + + value += 1.25F; + CHECK_CLOSE(1.75F, value.get(), 0.0001F); + value.advance(10.0F); + CHECK_CLOSE(2.5F, value.get(), 0.0001F); + } + + //************************************************************************* + TEST(test_referenced_floating_clamped_value_rejects_nan) + { + const float nan = etl::numeric_limits::quiet_NaN(); + + CHECK_THROW((etl::clamped_value_range(nan, 1.0F)), etl::exception); + + const etl::clamped_value_range range(-1.0F, 1.0F); + CHECK_THROW((etl::referenced_clamped_value(range, nan)), etl::exception); + + etl::referenced_clamped_value value(range, 0.0F); + CHECK_THROW(value = nan, etl::exception); + CHECK_THROW(value.advance(nan), etl::exception); + } + #endif +#endif } } // namespace diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 032cbd41..d0ec3b7c 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -91,6 +91,20 @@ namespace } }; + struct ComparatorOnlyObject + { + int a; + int b; + }; + + struct LessBySecond + { + bool constexpr operator()(const ComparatorOnlyObject& lhs, const ComparatorOnlyObject& rhs) const + { + return lhs.b < rhs.b; + } + }; + typedef etl::compare CompareTest; SUITE(test_compare) @@ -191,5 +205,139 @@ namespace CHECK_EQUAL(CompareInt::Greater, cmp_test_greater); CHECK_EQUAL(CompareInt::Equal, cmp_test_equal); } + +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_strong_ordering) + { + constexpr etl::strong_ordering ordering_less(etl::strong_ordering::less); + constexpr etl::strong_ordering ordering_equal(etl::strong_ordering::equal); + constexpr etl::strong_ordering ordering_greater(etl::strong_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_less <= 0); + CHECK(!(ordering_less == 0)); + CHECK(!(ordering_less > 0)); + CHECK(ordering_equal == 0); + CHECK(ordering_equal <= 0); + CHECK(ordering_equal >= 0); + CHECK(ordering_greater > 0); + CHECK(ordering_greater >= 0); + CHECK(!(ordering_greater == 0)); + + // Reversed operands. + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::strong_ordering::equivalent == etl::strong_ordering::equal, "Equivalent must alias equal"); + static_assert(etl::is_same::value, + "Must convert to weak_ordering"); + CHECK(etl::weak_ordering(etl::strong_ordering::less) < 0); + CHECK(etl::partial_ordering(etl::strong_ordering::greater) > 0); + } + + //************************************************************************* + TEST(test_make_strong_ordering) + { + constexpr etl::strong_ordering ordering_less = etl::make_strong_ordering(-1); + constexpr etl::strong_ordering ordering_equal = etl::make_strong_ordering(0); + constexpr etl::strong_ordering ordering_greater = etl::make_strong_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equal == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_weak_ordering) + { + constexpr etl::weak_ordering ordering_less(etl::weak_ordering::less); + constexpr etl::weak_ordering ordering_equivalent(etl::weak_ordering::equivalent); + constexpr etl::weak_ordering ordering_greater(etl::weak_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::weak_ordering::equivalent == 0, "Equivalent must be zero"); + CHECK(etl::partial_ordering(etl::weak_ordering::less) < 0); + } + + //************************************************************************* + TEST(test_make_weak_ordering) + { + constexpr etl::weak_ordering ordering_less = etl::make_weak_ordering(-1); + constexpr etl::weak_ordering ordering_equivalent = etl::make_weak_ordering(0); + constexpr etl::weak_ordering ordering_greater = etl::make_weak_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::weak_ordering"); + } + + //************************************************************************* + TEST(test_partial_ordering) + { + constexpr etl::partial_ordering ordering_less(etl::partial_ordering::less); + constexpr etl::partial_ordering ordering_equivalent(etl::partial_ordering::equivalent); + constexpr etl::partial_ordering ordering_greater(etl::partial_ordering::greater); + constexpr etl::partial_ordering ordering_unordered(etl::partial_ordering::unordered); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + // An unordered result compares false against zero in every direction. + CHECK(!(ordering_unordered < 0)); + CHECK(!(ordering_unordered <= 0)); + CHECK(!(ordering_unordered == 0)); + CHECK(!(ordering_unordered >= 0)); + CHECK(!(ordering_unordered > 0)); + CHECK(ordering_unordered != 0); + + static_assert(etl::partial_ordering::equivalent == 0, "Equivalent must be zero"); + } + + //************************************************************************* + TEST(test_make_partial_ordering) + { + constexpr etl::partial_ordering ordering_less = etl::make_partial_ordering(-1); + constexpr etl::partial_ordering ordering_equivalent = etl::make_partial_ordering(0); + constexpr etl::partial_ordering ordering_greater = etl::make_partial_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::partial_ordering"); + } + + //************************************************************************* + TEST(test_three_way_compare_custom_less) + { + constexpr ComparatorOnlyObject lhs = {5, 1}; + constexpr ComparatorOnlyObject rhs = {1, 2}; + constexpr ComparatorOnlyObject eq = {9, 1}; + + constexpr int cmp_less = etl::three_way_compare(lhs, rhs); + constexpr int cmp_greater = etl::three_way_compare(rhs, lhs); + constexpr int cmp_equal = etl::three_way_compare(lhs, eq); + + CHECK_EQUAL(-1, cmp_less); + CHECK_EQUAL(1, cmp_greater); + CHECK_EQUAL(0, cmp_equal); + + CHECK(etl::make_strong_ordering(cmp_less) < 0); + CHECK(etl::make_strong_ordering(cmp_greater) > 0); + CHECK(etl::make_strong_ordering(cmp_equal) == 0); + } +#endif } } // namespace diff --git a/test/test_cyclic_value.cpp b/test/test_cyclic_value.cpp index 7077628a..414bba44 100644 --- a/test/test_cyclic_value.cpp +++ b/test/test_cyclic_value.cpp @@ -56,6 +56,15 @@ namespace CHECK_EQUAL(7, value.last()); } + //************************************************************************* + TEST(test_reversed_run_time_initialisation) + { + etl::cyclic_value value(1, 2); + + CHECK_THROW(etl::cyclic_value(7, 2), etl::cyclic_value_reversed_limits); + CHECK_THROW(value.set(7, 2), etl::cyclic_value_reversed_limits); + } + //************************************************************************* TEST(test_copy_constructor_compile_time) { @@ -336,7 +345,7 @@ namespace { etl::cyclic_value value; - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -347,7 +356,7 @@ namespace etl::cyclic_value value; value.set(2, 7); - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -358,7 +367,7 @@ namespace etl::cyclic_value value; value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -370,7 +379,7 @@ namespace value.set(2, 7); value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -408,6 +417,168 @@ namespace CHECK_EQUAL(1U, value); } + //************************************************************************* + TEST(test_add_positive_n_compile_time) + { + etl::cyclic_value value; + + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value += -2; + + CHECK_EQUAL(1U, value); + } + + //************************************************************************* + TEST(test_add_negative_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_decrease_positive_compile_time) + { + etl::cyclic_value value; + + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value -= -2; + + CHECK_EQUAL(2U, value); + } + //************************************************************************* TEST(test_assignment_compile_time) { @@ -493,7 +664,50 @@ namespace CHECK(data2 == compare1); } -#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_comparison_operators) + { + etl::cyclic_value cv1(3); + etl::cyclic_value cv2(3); + etl::cyclic_value cv3(5); + etl::cyclic_value cv4(3); + + CHECK_TRUE(cv1 == cv2); + CHECK_FALSE(cv1 == cv3); + + CHECK_FALSE(cv1 != cv2); + CHECK_TRUE(cv1 != cv3); + + CHECK_FALSE(cv1 < cv2); + CHECK_FALSE(cv2 < cv1); + CHECK_TRUE(cv1 < cv3); + CHECK_FALSE(cv3 < cv1); + CHECK_FALSE(cv1 < cv4); + CHECK_FALSE(cv4 < cv1); + + CHECK_FALSE(cv1 > cv2); + CHECK_FALSE(cv2 > cv1); + CHECK_FALSE(cv1 > cv3); + CHECK_TRUE(cv3 > cv1); + CHECK_FALSE(cv1 > cv4); + CHECK_FALSE(cv4 > cv1); + + CHECK_TRUE(cv1 <= cv2); + CHECK_TRUE(cv2 <= cv1); + CHECK_TRUE(cv1 <= cv3); + CHECK_FALSE(cv3 <= cv1); + CHECK_TRUE(cv1 <= cv4); + CHECK_TRUE(cv4 <= cv1); + + CHECK_TRUE(cv1 >= cv2); + CHECK_TRUE(cv2 >= cv1); + CHECK_FALSE(cv1 >= cv3); + CHECK_TRUE(cv3 >= cv1); + CHECK_TRUE(cv1 >= cv4); + CHECK_TRUE(cv4 >= cv1); + } + +#if ETL_USING_CPP11 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor) { @@ -502,6 +716,7 @@ namespace CHECK(true); } + #if ETL_USING_CPP14 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor_with_range) { @@ -510,6 +725,7 @@ namespace static_assert(cv.last() == 9, "constexpr range ctor last"); CHECK(true); } + #endif //************************************************************************* TEST(test_cyclic_value_constexpr_copy_ctor) @@ -519,6 +735,299 @@ namespace static_assert(cv2.get() == 0, "constexpr copy ctor"); CHECK(true); } + + //************************************************************************* + TEST(test_constexpr_comparison_operators) + { + constexpr etl::cyclic_value cv1(3); + constexpr etl::cyclic_value cv2(3); + constexpr etl::cyclic_value cv3(5); + constexpr etl::cyclic_value cv4(3); + + static_assert((cv1 == cv2), "(cv1 == cv2) failed"); + static_assert(!(cv1 == cv3), "!(cv1 == cv3) failed"); + + static_assert(!(cv1 != cv2), "!(cv1 != cv2) failed"); + static_assert((cv1 != cv3), "(cv1 != cv3) failed"); + + static_assert(!(cv1 < cv2), "!(cv1 < cv2) failed"); + static_assert(!(cv2 < cv1), "!(cv2 < cv1) failed"); + static_assert((cv1 < cv3), "(cv1 < cv3) failed"); + static_assert(!(cv3 < cv1), "!(cv3 < cv1) failed"); + static_assert(!(cv1 < cv4), "!(cv1 < cv4) failed"); + static_assert(!(cv4 < cv1), "!(cv4 < cv1) failed"); + + static_assert(!(cv1 > cv2), "!(cv1 > cv2) failed"); + static_assert(!(cv2 > cv1), "!(cv2 > cv1) failed"); + static_assert(!(cv1 > cv3), "!(cv1 > cv3) failed"); + static_assert((cv3 > cv1), "(cv3 > cv1) failed"); + static_assert(!(cv1 > cv4), "!(cv1 > cv4) failed"); + static_assert(!(cv4 > cv1), "!(cv4 > cv1) failed"); + + static_assert((cv1 <= cv2), "(cv1 <= cv2) failed"); + static_assert((cv2 <= cv1), "(cv2 <= cv1) failed"); + static_assert((cv1 <= cv3), "(cv1 <= cv3) failed"); + static_assert(!(cv3 <= cv1), "!(cv3 <= cv1) failed"); + static_assert((cv1 <= cv4), "(cv1 <= cv4) failed"); + static_assert((cv4 <= cv1), "(cv4 <= cv1) failed"); + + static_assert((cv1 >= cv2), "(cv1 >= cv2) failed"); + static_assert((cv2 >= cv1), "(cv2 >= cv1) failed"); + static_assert(!(cv1 >= cv3), "!(cv1 >= cv3) failed"); + static_assert((cv3 >= cv1), "(cv3 >= cv1) failed"); + static_assert((cv1 >= cv4), "(cv1 >= cv4) failed"); + static_assert((cv4 >= cv1), "(cv4 >= cv1) failed"); + } #endif + + //************************************************************************* + TEST(test_maximum_unsigned_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(255)); + CHECK_EQUAL(255, value); + + value.advance(static_cast(1)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-1)); + CHECK_EQUAL(255, value); + } + + //************************************************************************* + TEST(test_maximum_signed_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(127)); + CHECK_EQUAL(127, int(value)); + + value.advance(static_cast(1)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-1)); + CHECK_EQUAL(127, int(value)); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } } } // namespace diff --git a/test/test_math.cpp b/test/test_math.cpp index b28029e5..2592d7db 100644 --- a/test/test_math.cpp +++ b/test/test_math.cpp @@ -130,5 +130,120 @@ namespace CHECK_TRUE(etl::is_exactly_equal(i1, i1)); CHECK_FALSE(etl::is_exactly_equal(i1, i2)); } + + //************************************************************************* + TEST(test_floor) + { + float f1 = 1.7f; + float f2 = -1.7f; + double d1 = 1.7; + double d2 = -1.7; + long double ld1 = 1.7L; + long double ld2 = -1.7L; + + CHECK_CLOSE(1.0f, etl::math::floor(f1), 0.0001f); + CHECK_CLOSE(-2.0f, etl::math::floor(f2), 0.0001f); + CHECK_CLOSE(1.0, etl::math::floor(d1), 0.0001); + CHECK_CLOSE(-2.0, etl::math::floor(d2), 0.0001); + CHECK_CLOSE(1.0L, etl::math::floor(ld1), 0.0001L); + CHECK_CLOSE(-2.0L, etl::math::floor(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_ceil) + { + float f1 = 1.2f; + float f2 = -1.2f; + double d1 = 1.2; + double d2 = -1.2; + long double ld1 = 1.2L; + long double ld2 = -1.2L; + + CHECK_CLOSE(2.0f, etl::math::ceil(f1), 0.0001f); + CHECK_CLOSE(-1.0f, etl::math::ceil(f2), 0.0001f); + CHECK_CLOSE(2.0, etl::math::ceil(d1), 0.0001); + CHECK_CLOSE(-1.0, etl::math::ceil(d2), 0.0001); + CHECK_CLOSE(2.0L, etl::math::ceil(ld1), 0.0001L); + CHECK_CLOSE(-1.0L, etl::math::ceil(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_log10) + { + float f1 = 100.0f; + double d1 = 1000.0; + long double ld1 = 10000.0L; + + CHECK_CLOSE(2.0f, etl::math::log10(f1), 0.0001f); + CHECK_CLOSE(3.0, etl::math::log10(d1), 0.0001); + CHECK_CLOSE(4.0L, etl::math::log10(ld1), 0.0001L); + } + + //************************************************************************* + TEST(test_round) + { + float f1 = 1.5f; + float f2 = -1.5f; + float f3 = 1.4f; + float f4 = -1.4f; + double d1 = 2.5; + double d2 = -2.5; + double d3 = 2.4; + double d4 = -2.4; + long double ld1 = 3.5L; + long double ld2 = -3.5L; + long double ld3 = 3.4L; + long double ld4 = -3.4L; + + CHECK_CLOSE(2.0f, etl::math::round(f1), 0.0001f); + CHECK_CLOSE(-2.0f, etl::math::round(f2), 0.0001f); + CHECK_CLOSE(1.0f, etl::math::round(f3), 0.0001f); + CHECK_CLOSE(-1.0f, etl::math::round(f4), 0.0001f); + CHECK_CLOSE(3.0, etl::math::round(d1), 0.0001); + CHECK_CLOSE(-3.0, etl::math::round(d2), 0.0001); + CHECK_CLOSE(2.0, etl::math::round(d3), 0.0001); + CHECK_CLOSE(-2.0, etl::math::round(d4), 0.0001); + CHECK_CLOSE(4.0L, etl::math::round(ld1), 0.0001L); + CHECK_CLOSE(-4.0L, etl::math::round(ld2), 0.0001L); + CHECK_CLOSE(3.0L, etl::math::round(ld3), 0.0001L); + CHECK_CLOSE(-3.0L, etl::math::round(ld4), 0.0001L); + } + + //************************************************************************* + TEST(test_pow) + { + float fb = 2.0f; + float fe = 3.0f; + double db = 2.0; + double de = 10.0; + long double ldb = 2.0L; + long double lde = 4.0L; + + CHECK_CLOSE(8.0f, etl::math::pow(fb, fe), 0.0001f); + CHECK_CLOSE(1024.0, etl::math::pow(db, de), 0.0001); + CHECK_CLOSE(16.0L, etl::math::pow(ldb, lde), 0.0001L); + } + + //************************************************************************* + TEST(test_modf) + { + float f1 = 3.75f; + float f_intpart = 0.0f; + double d1 = 3.75; + double d_intpart = 0.0; + long double ld1 = 3.75L; + long double ld_intpart = 0.0L; + + float f_frac = etl::math::modf(f1, &f_intpart); + double d_frac = etl::math::modf(d1, &d_intpart); + long double ld_frac = etl::math::modf(ld1, &ld_intpart); + + CHECK_CLOSE(3.0f, f_intpart, 0.0001f); + CHECK_CLOSE(0.75f, f_frac, 0.0001f); + CHECK_CLOSE(3.0, d_intpart, 0.0001); + CHECK_CLOSE(0.75, d_frac, 0.0001); + CHECK_CLOSE((double)3.0L, (double)ld_intpart, 0.0001); + CHECK_CLOSE((double)0.75L, (double)ld_frac, 0.0001); + } } } // namespace diff --git a/test/test_string_stream.cpp b/test/test_string_stream.cpp index 27ecd72d..e3099c09 100644 --- a/test/test_string_stream.cpp +++ b/test/test_string_stream.cpp @@ -181,10 +181,21 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + // Float + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + + // Double ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + // Long double + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u16.cpp b/test/test_string_stream_u16.cpp index 6489e572..fb2fe545 100644 --- a/test/test_string_stream_u16.cpp +++ b/test/test_string_stream_u16.cpp @@ -181,10 +181,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u32.cpp b/test/test_string_stream_u32.cpp index bde30bf2..7c6a0e4c 100644 --- a/test/test_string_stream_u32.cpp +++ b/test/test_string_stream_u32.cpp @@ -181,10 +181,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u8.cpp b/test/test_string_stream_u8.cpp index 7046a107..53b36205 100644 --- a/test/test_string_stream_u8.cpp +++ b/test/test_string_stream_u8.cpp @@ -184,10 +184,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_wchar_t.cpp b/test/test_string_stream_wchar_t.cpp index bcd8d364..931c9135 100644 --- a/test/test_string_stream_wchar_t.cpp +++ b/test/test_string_stream_wchar_t.cpp @@ -182,10 +182,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_to_string.cpp b/test/test_to_string.cpp index 1870697e..016726e0 100644 --- a/test/test_to_string.cpp +++ b/test/test_to_string.cpp @@ -44,32 +44,35 @@ namespace { typedef etl::format_spec Format; + using String20 = etl::string<20>; + using String64 = etl::string<64>; + SUITE(test_to_string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -107,89 +110,89 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); etl::to_string(uint8_t(0), str, format); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::string<64> str; + String64 str; - CHECK(etl::string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -261,126 +264,126 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); // Positive rollover - CHECK(etl::string<20>(STR(" 2.000000")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("2.000000 ")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 2.000000")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("2.000000 ")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).left())); // Negative rollover - CHECK(etl::string<20>(STR(" -2.000000")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("-2.000000 ")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -2.000000")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-2.000000 ")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_pointer_no_append) { - etl::string<20> str; + String20 str; static const volatile int cvi = 0; @@ -388,8 +391,8 @@ namespace oss.width(10); oss.fill(STR('0')); oss << std::hex << std::right << uintptr_t(&cvi); - std::string temp(oss.str()); - etl::string<20> compare(temp.begin(), temp.end()); + std::string temp(oss.str()); + String20 compare(temp.begin(), temp.end()); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0'))); CHECK(compare == str); @@ -409,7 +412,7 @@ namespace //************************************************************************* TEST(test_pointer_append) { - etl::string<20> str; + String20 str; static const volatile int cvi = 0; @@ -419,7 +422,7 @@ namespace oss << std::hex << std::right << uintptr_t(&cvi); std::string temp(STR("Result ")); temp.append(oss.str()); - etl::string<20> compare(temp.begin(), temp.end()); + String20 compare(temp.begin(), temp.end()); str.assign(STR("Result ")); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0')), true); @@ -442,229 +445,229 @@ namespace //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK_EQUAL(etl::string<20>(STR("-1")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-1")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK_EQUAL(etl::string<20>(STR("-1.234560")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-1.234560")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK_EQUAL(etl::string<20>(STR("-9223372.036854775808")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-9223372.036854775808")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-1.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-1.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("0.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("0.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-0.0004")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-0.0004")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0004")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0004")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0001")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0001")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.4568")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.4568")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_double_formatting_10_decimal_point) { - etl::string<20> result_d; - double value_d = 1.2345; + String20 result_d; + double value_d = 1.2345; Format format = Format().precision(10); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("1.2345000000")).c_str(), result_d.c_str()); + CHECK_EQUAL(String20(STR("1.2345000000")).c_str(), result_d.c_str()); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR(" -123.4568")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR(" -123.4568")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("124.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("124.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-124.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-124.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } @@ -674,48 +677,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -724,30 +727,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u16string.cpp b/test/test_to_u16string.cpp index 22a21c39..cfb109fd 100644 --- a/test/test_to_u16string.cpp +++ b/test/test_to_u16string.cpp @@ -41,32 +41,35 @@ namespace { typedef etl::u16format_spec Format; + using String20 = etl::u16string<20>; + using String64 = etl::u16string<64>; + SUITE(test_to_u16string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u16string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u16string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u16string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u16string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u16string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u16string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u16string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u16string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u16string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u16string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u16string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -104,88 +107,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u16string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u16string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u16string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u16string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u16string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u16string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u16string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u16string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u16string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u16string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u16string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u16string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u16string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u16string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u16string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u16string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u16string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u16string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u16string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u16string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u16string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u16string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u16string<64> str; + String64 str; - CHECK(etl::u16string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -257,328 +259,328 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u16string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u16string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u16string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u16string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u16string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u16string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u16string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u16string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u16string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u16string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u16string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u16string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u16string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u16string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u16string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u16string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u16string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u16string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u16string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u16string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u16string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u16string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u16string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u16string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u16string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u16string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u16string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u16string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u16string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u16string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u16string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u16string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u16string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u16string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u16string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u16string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u16string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u16string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u16string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u16string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u16string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u16string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u16string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u16string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u16string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u16string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -588,48 +590,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u16string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u16string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u16string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u16string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u16string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u16string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u16string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u16string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u16string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u16string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -638,30 +640,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u16string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u16string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u16string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u16string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u16string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u16string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u32string.cpp b/test/test_to_u32string.cpp index 523aa556..9c81ad0d 100644 --- a/test/test_to_u32string.cpp +++ b/test/test_to_u32string.cpp @@ -42,32 +42,35 @@ namespace { typedef etl::u32format_spec Format; + using String20 = etl::u32string<20>; + using String64 = etl::u32string<64>; + SUITE(test_to_u32string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u32string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u32string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u32string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u32string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u32string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u32string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u32string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u32string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u32string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u32string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u32string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -105,88 +108,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u32string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u32string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u32string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u32string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u32string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u32string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u32string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u32string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u32string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u32string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u32string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u32string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u32string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u32string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u32string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u32string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u32string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u32string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u32string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u32string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u32string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u32string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u32string<64> str; + String64 str; - CHECK(etl::u32string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -258,329 +260,329 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u32string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u32string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u32string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u32string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u32string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u32string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u32string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u32string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u32string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u32string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u32string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u32string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u32string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u32string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u32string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u32string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u32string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u32string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u32string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u32string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u32string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u32string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u32string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u32string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u32string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u32string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u32string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u32string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u32string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u32string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u32string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u32string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u32string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u32string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u32string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u32string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u32string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u32string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u32string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u32string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u32string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u32string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u32string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u32string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u32string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u32string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u32string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -590,48 +592,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u32string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u32string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u32string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u32string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u32string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u32string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u32string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u32string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u32string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u32string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -640,30 +642,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u32string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u32string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u32string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u32string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u32string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u32string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u8string.cpp b/test/test_to_u8string.cpp index a1d4eece..de300609 100644 --- a/test/test_to_u8string.cpp +++ b/test/test_to_u8string.cpp @@ -43,32 +43,35 @@ namespace { typedef etl::u8format_spec Format; + using String20 = etl::u8string<20>; + using String64 = etl::u8string<64>; + SUITE(test_to_u8string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u8string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u8string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u8string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u8string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u8string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u8string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u8string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u8string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u8string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u8string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u8string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -106,88 +109,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u8string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u8string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u8string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u8string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u8string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u8string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u8string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u8string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u8string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u8string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u8string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u8string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u8string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u8string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u8string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u8string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u8string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u8string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u8string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u8string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u8string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u8string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u8string<64> str; + String64 str; - CHECK(etl::u8string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -259,328 +261,328 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u8string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u8string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u8string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u8string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u8string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u8string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u8string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u8string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u8string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u8string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u8string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u8string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u8string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u8string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u8string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u8string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u8string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u8string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u8string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u8string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u8string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u8string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u8string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u8string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u8string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u8string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u8string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u8string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u8string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u8string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u8string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u8string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u8string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u8string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u8string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u8string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u8string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u8string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u8string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u8string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u8string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u8string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u8string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u8string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u8string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u8string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -590,48 +592,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u8string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u8string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u8string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u8string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u8string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u8string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u8string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u8string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u8string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u8string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -640,31 +642,49 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u8string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u8string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u8string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u8string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u8string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u8string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_wstring.cpp b/test/test_to_wstring.cpp index feb3b9cd..d2684cb4 100644 --- a/test/test_to_wstring.cpp +++ b/test/test_to_wstring.cpp @@ -44,32 +44,35 @@ namespace { typedef etl::wformat_spec Format; + using String20 = etl::wstring<20>; + using String64 = etl::wstring<64>; + SUITE(test_to_wstring) { //************************************************************************* TEST(test_default_format_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::wstring<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::wstring<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::wstring<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::wstring<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::wstring<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::wstring<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::wstring<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::wstring<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::wstring<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::wstring<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::wstring<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -107,87 +110,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::wstring<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::wstring<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::wstring<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::wstring<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::wstring<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::wstring<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::wstring<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::wstring<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::wstring<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::wstring<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::wstring<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::wstring<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::wstring<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::wstring<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::wstring<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::wstring<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::wstring<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::wstring<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::wstring<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::wstring<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::wstring<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::wstring<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::wstring<64> str; + String64 str; - CHECK(etl::wstring<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -259,119 +262,119 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::wstring<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::wstring<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::wstring<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::wstring<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::wstring<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::wstring<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::wstring<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::wstring<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::wstring<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::wstring<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::wstring<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::wstring<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::wstring<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::wstring<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::wstring<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::wstring<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::wstring<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::wstring<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::wstring<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::wstring<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::wstring<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_pointer_no_append) { - etl::wstring<20> str; + String20 str; static const volatile int cvi = 0; @@ -379,8 +382,8 @@ namespace oss.width(10); oss.fill(STR('0')); oss << std::hex << std::right << uintptr_t(&cvi); - std::wstring temp(oss.str()); - etl::wstring<20> compare(temp.begin(), temp.end()); + std::wstring temp(oss.str()); + String20 compare(temp.begin(), temp.end()); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0'))); CHECK(compare == str); @@ -400,7 +403,7 @@ namespace //************************************************************************* TEST(test_pointer_append) { - etl::wstring<20> str; + String20 str; static const volatile int cvi = 0; @@ -410,7 +413,7 @@ namespace oss << std::hex << std::right << uintptr_t(&cvi); std::wstring temp(STR("Result ")); temp.append(oss.str()); - etl::wstring<20> compare(temp.begin(), temp.end()); + String20 compare(temp.begin(), temp.end()); str.assign(STR("Result ")); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0')), true); @@ -433,217 +436,217 @@ namespace //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::wstring<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::wstring<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::wstring<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::wstring<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::wstring<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::wstring<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::wstring<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::wstring<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::wstring<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::wstring<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::wstring<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::wstring<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::wstring<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::wstring<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::wstring<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::wstring<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::wstring<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::wstring<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::wstring<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::wstring<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::wstring<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::wstring<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::wstring<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::wstring<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::wstring<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::wstring<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -653,48 +656,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::wstring<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::wstring<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::wstring<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::wstring<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::wstring<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::wstring<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::wstring<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::wstring<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::wstring<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::wstring<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -703,30 +706,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::wstring<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::wstring<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::wstring<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::wstring<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::wstring<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::wstring<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index ad1e50c9..21b8502f 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -282,6 +282,294 @@ namespace CHECK_THROW(etl::be_uint8_t be_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); } + //************************************************************************* + // 'value_from' reads a value straight out of a byte buffer, without first + // copying the bytes into an unaligned_type's own storage. + //************************************************************************* + TEST(test_value_from_integral) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; + + CHECK_EQUAL(uint16_t(0x1234), etl::be_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(uint16_t(0x3412), etl::le_uint16_t::value_from(buffer.data())); + + CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data())); + + CHECK_EQUAL(uint64_t(0x123456789ABCDEF0), etl::be_uint64_t::value_from(buffer.data())); + CHECK_EQUAL(uint64_t(0xF0DEBC9A78563412), etl::le_uint64_t::value_from(buffer.data())); + } + + //************************************************************************* + /// The address need not be aligned for the value's type. + //************************************************************************* + TEST(test_value_from_unaligned_address) + { + const std::array buffer = {0xFF, 0x12, 0x34, 0x56, 0x78, 0xFF}; + + CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data() + 1)); + CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data() + 1)); + } + + //************************************************************************* + /// 'value_from' must agree with constructing from the same address and + /// then reading the value back out. + //************************************************************************* + TEST(test_value_from_matches_construction) + { + const std::array buffer = {0xEE, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + + CHECK_EQUAL(etl::be_uint16_t(buffer.data()).value(), etl::be_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint16_t(buffer.data()).value(), etl::le_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(etl::be_uint32_t(buffer.data()).value(), etl::be_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint32_t(buffer.data()).value(), etl::le_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(etl::be_uint64_t(buffer.data()).value(), etl::be_uint64_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint64_t(buffer.data()).value(), etl::le_uint64_t::value_from(buffer.data())); + } + + //************************************************************************* + /// Boundary values must survive the round trip through the store. + //************************************************************************* + TEST(test_value_from_boundary_values) + { + const std::array zeroes = {0x00, 0x00, 0x00, 0x00}; + const std::array ones = {0xFF, 0xFF, 0xFF, 0xFF}; + + CHECK_EQUAL(uint32_t(0), etl::be_uint32_t::value_from(zeroes.data())); + CHECK_EQUAL(uint32_t(0), etl::le_uint32_t::value_from(zeroes.data())); + + CHECK_EQUAL(etl::integral_limits::max, etl::be_uint32_t::value_from(ones.data())); + CHECK_EQUAL(etl::integral_limits::max, etl::le_uint32_t::value_from(ones.data())); + } + + //************************************************************************* + /// Signed types are reconstructed through their unsigned representation. + //************************************************************************* + TEST(test_value_from_negative_values) + { + const std::array buffer = {0xFF, 0xFF, 0xFF, 0xFE}; + + CHECK_EQUAL(int32_t(-2), etl::be_int32_t::value_from(buffer.data())); + CHECK_EQUAL(int16_t(-1), etl::be_int16_t::value_from(buffer.data())); + } + + //************************************************************************* + /// The floating point specialisation provides 'value_from' too. + //************************************************************************* + TEST(test_value_from_float) + { + const float f = 3.1415927f; + const double d = 2.718281828459045; + + etl::be_float_t be_f(f); + etl::le_double_t le_d(d); + + 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 // buffer (e.g. as received from a file, network socket or memory-mapped @@ -625,6 +913,25 @@ namespace CHECK_EQUAL(0x12345678U, be_v.value()); } + //************************************************************************* + /// 'value_from' is usable at compile time for the same reason: it reads + /// the buffer through the 'const unsigned char*' path. + //************************************************************************* + TEST(test_constexpr_value_from_integral) + { + static ETL_CONSTANT unsigned char le_buffer[4] = {0x78, 0x56, 0x34, 0x12}; + static ETL_CONSTANT unsigned char be_buffer[4] = {0x12, 0x34, 0x56, 0x78}; + + constexpr uint32_t le_v = etl::le_uint32_t::value_from(le_buffer); + constexpr uint32_t be_v = etl::be_uint32_t::value_from(be_buffer); + + static_assert(le_v == 0x12345678U, "le_uint32_t constexpr value_from"); + static_assert(be_v == 0x12345678U, "be_uint32_t constexpr value_from"); + + CHECK_EQUAL(0x12345678U, le_v); + CHECK_EQUAL(0x12345678U, be_v); + } + #if ETL_USING_BUILTIN_BIT_CAST //************************************************************************* TEST(test_constexpr_decode_buffer_float) 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 diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 7a69c37e..010846c4 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -6304,7 +6304,26 @@ true true - + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true true @@ -11807,4 +11826,4 @@ - + \ No newline at end of file diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 10afee24..d27cedd4 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -4124,4 +4124,4 @@ Resource Files\Images - + \ No newline at end of file