From a26fed1a893d4ba088aa7e047b234c4052469ac1 Mon Sep 17 00:00:00 2001 From: rolandreichweinbmw Date: Mon, 3 Mar 2025 09:52:12 +0100 Subject: [PATCH] etl::span: Add advance(), copy(), reinterpret_as() (#1024) * etl::span: Add advance(), copy(), reinterpret_as() * Added further tests for span::reinterpret_as * Fix size of unaligned_type on Windows Multiple inheritance leads to additional 1 byte for the second base class. Fixing it by not inheriting but aggregating via typedef. --- include/etl/file_error_numbers.h | 2 + include/etl/span.h | 93 ++++++++++++++++++++++ include/etl/unaligned_type.h | 36 +++++---- test/test_span_dynamic_extent.cpp | 126 ++++++++++++++++++++++++++++++ test/test_span_fixed_extent.cpp | 70 +++++++++++++++++ test/test_unaligned_type.cpp | 20 +++++ 6 files changed, 330 insertions(+), 17 deletions(-) diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 64d94d57..2369f68c 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -105,4 +105,6 @@ SOFTWARE. #define ETL_BASE64_FILE_ID "72" #define ETL_SINGLETON_BASE_FILE_ID "73" #define ETL_UNALIGNED_TYPE_FILE_ID "74" +#define ETL_SPAN_FILE_ID "75" + #endif diff --git a/include/etl/span.h b/include/etl/span.h index a02fa099..7b09adf4 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -32,6 +32,10 @@ SOFTWARE. #define ETL_SPAN_INCLUDED #include "platform.h" + +#include "error_handler.h" +#include "exception.h" +#include "alignment.h" #include "iterator.h" #include "algorithm.h" #include "circular_iterator.h" @@ -55,6 +59,34 @@ SOFTWARE. namespace etl { + //*************************************************************************** + ///\ingroup span + /// Exception base for span + //*************************************************************************** + class span_exception : public exception + { + public: + + span_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + ///\ingroup span + /// Bad alignment exception. + //*************************************************************************** + class span_alignment_exception : public span_exception + { + public: + + span_alignment_exception(string_type file_name_, numeric_type line_number_) + : span_exception(ETL_ERROR_TEXT("span:alignment", ETL_SPAN_FILE_ID"A"), file_name_, line_number_) + { + } + }; + //*************************************************************************** /// Span - Fixed Extent //*************************************************************************** @@ -426,6 +458,18 @@ namespace etl : etl::span(pbegin + offset, pbegin + offset + count); } + //************************************************************************* + /// Reinterpret the span as a span with different element type. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::span reinterpret_as() const + { + ETL_ASSERT(etl::is_aligned::value>(pbegin), ETL_ERROR(span_alignment_exception)); + + return etl::span(reinterpret_cast(pbegin), + Extent * sizeof(element_type) / sizeof(TNew)); + } + private: pointer pbegin; @@ -812,6 +856,28 @@ namespace etl : etl::span(pbegin + offset, pbegin + offset + count); } + //************************************************************************* + /// Moves the pointer to the first element of the span further by a specified number of elements. + ///\tparam elements Number of elements to move forward + //************************************************************************* + void advance(size_t elements) ETL_NOEXCEPT + { + elements = etl::min(elements, size()); + pbegin += elements; + } + + //************************************************************************* + /// Reinterpret the span as a span with different element type. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::span reinterpret_as() const + { + ETL_ASSERT(etl::is_aligned::value>(pbegin), ETL_ERROR(span_alignment_exception)); + + return etl::span(reinterpret_cast(pbegin), + (pend - pbegin) * sizeof(element_type) / sizeof(TNew)); + } + private: pointer pbegin; @@ -884,6 +950,33 @@ namespace etl etl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } + //************************************************************************* + /// Copy complete element data from one span to another. If the destination + /// span is bigger than the source span, only the initial part of + /// destination span is overwritten. + ///\param src Source + ///\param dst Destination + ///\return true, if copy was successful (including empty source span, or + /// spans pointing to the same address) + ///\return false, if the destination span is shorter than the source span. + //************************************************************************* + template + typename etl::enable_if::type, typename etl::remove_cv::type>::value && + !etl::is_const::value, bool>::type + copy(const etl::span& src, const etl::span& dst) + { + if (src.empty() || (src.begin() == dst.begin())) + { + return true; + } + if (src.size() > dst.size()) + { + return false; + } + (void) etl::copy(src.begin(), src.end(), dst.begin()); + return true; + } + //************************************************************************* /// Template deduction guides. //************************************************************************* diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 8910f864..87519dbf 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -449,7 +449,6 @@ namespace etl //************************************************************************* template ETL_PACKED_CLASS(unaligned_type) : public private_unaligned_type::unaligned_type_storage - , public private_unaligned_type::unaligned_copy::value ? false : true> { public: @@ -457,6 +456,8 @@ namespace etl typedef T value_type; + typedef private_unaligned_type::unaligned_copy::value ? false : true> 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; @@ -480,7 +481,7 @@ namespace etl //************************************************************************* unaligned_type(T value) { - this->copy_value_to_store(value, this->storage); + unaligned_copy::copy_value_to_store(value, this->storage); } //************************************************************************* @@ -506,7 +507,7 @@ namespace etl //************************************************************************* unaligned_type(const unaligned_type& other) { - this->copy_store_to_store(other.data(), Endian, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); } //************************************************************************* @@ -515,7 +516,7 @@ namespace etl template unaligned_type(const unaligned_type& other) { - this->copy_store_to_store(other.data(), Endian_Other, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); } //************************************************************************* @@ -523,7 +524,7 @@ namespace etl //************************************************************************* unaligned_type& operator =(T value) { - this->copy_value_to_store(value, this->storage); + unaligned_copy::copy_value_to_store(value, this->storage); return *this; } @@ -533,7 +534,7 @@ namespace etl //************************************************************************* unaligned_type& operator =(const unaligned_type& other) { - this->copy_store_to_store(other.data(), Endian_, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); return *this; } @@ -544,7 +545,7 @@ namespace etl template unaligned_type& operator =(const unaligned_type& other) { - this->copy_store_to_store(other.data(), Endian_Other, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); return *this; } @@ -556,7 +557,7 @@ namespace etl { T value = T(); - this->copy_store_to_value(this->storage, value); + unaligned_copy::copy_store_to_value(this->storage, value); return value; } @@ -568,7 +569,7 @@ namespace etl { T value = T(); - this->copy_store_to_value(this->storage, value); + unaligned_copy::copy_store_to_value(this->storage, value); return value; } @@ -589,7 +590,6 @@ namespace etl //************************************************************************* template ETL_PACKED_CLASS(unaligned_type_ext) : public private_unaligned_type::unaligned_type_storage_ext - , public private_unaligned_type::unaligned_copy::value ? false : true> { public: @@ -600,6 +600,8 @@ namespace etl typedef T value_type; + typedef private_unaligned_type::unaligned_copy::value ? false : true> 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; @@ -625,7 +627,7 @@ namespace etl unaligned_type_ext(T value, pointer storage_) : private_unaligned_type::unaligned_type_storage_ext(storage_) { - this->copy_value_to_store(value, this->storage); + unaligned_copy::copy_value_to_store(value, this->storage); } //************************************************************************* @@ -635,7 +637,7 @@ namespace etl unaligned_type_ext(const unaligned_type_ext& other, pointer storage_) : private_unaligned_type::unaligned_type_storage_ext(storage_) { - this->copy_store_to_store(other.data(), Endian_Other, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); } #if ETL_USING_CPP11 @@ -670,7 +672,7 @@ namespace etl //************************************************************************* unaligned_type_ext& operator =(T value) { - this->copy_value_to_store(value, this->storage); + unaligned_copy::copy_value_to_store(value, this->storage); return *this; } @@ -680,7 +682,7 @@ namespace etl //************************************************************************* unaligned_type_ext& operator =(const unaligned_type_ext& other) { - this->copy_store_to_store(other.data(), Endian, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); return *this; } @@ -691,7 +693,7 @@ namespace etl template unaligned_type_ext& operator =(const unaligned_type_ext& other) { - this->copy_store_to_store(other.data(), Endian_Other, this->storage); + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); return *this; } @@ -735,7 +737,7 @@ namespace etl { T value = T(); - this->copy_store_to_value(this->storage, value); + unaligned_copy::copy_store_to_value(this->storage, value); return value; } @@ -747,7 +749,7 @@ namespace etl { T value = T(); - this->copy_store_to_value(this->storage, value); + unaligned_copy::copy_store_to_value(this->storage, value); return value; } diff --git a/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index dd71bae2..ee85f843 100644 --- a/test/test_span_dynamic_extent.cpp +++ b/test/test_span_dynamic_extent.cpp @@ -30,6 +30,7 @@ SOFTWARE. #include "etl/span.h" #include "etl/array.h" +#include "etl/unaligned_type.h" #include #include @@ -1244,6 +1245,131 @@ namespace } } + //************************************************************************* + TEST(test_advance) + { + { + uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + etl::span data0 = data; + + CHECK_EQUAL(data0.size(), 5); + data0.advance(1); + CHECK_EQUAL(data0.size(), 4); + CHECK_EQUAL(data0[0], 0x02); + data0.advance(2); + CHECK_EQUAL(data0.size(), 2); + CHECK_EQUAL(data0[0], 0x04); + data0.advance(1); + CHECK_EQUAL(data0.size(), 1); + CHECK_EQUAL(data0[0], 0x05); + data0.advance(1); + CHECK_EQUAL(data0.size(), 0); + } + { + const uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + etl::span data0 = data; + + CHECK_EQUAL(data0.size(), 5); + data0.advance(1); + CHECK_EQUAL(data0.size(), 4); + CHECK_EQUAL(data0[0], 0x02); + data0.advance(2); + CHECK_EQUAL(data0.size(), 2); + CHECK_EQUAL(data0[0], 0x04); + data0.advance(1); + CHECK_EQUAL(data0.size(), 1); + CHECK_EQUAL(data0[0], 0x05); + data0.advance(1); + CHECK_EQUAL(data0.size(), 0); + data0.advance(1); + CHECK_EQUAL(data0.size(), 0); + data0.advance(100); + CHECK_EQUAL(data0.size(), 0); + } + } + + //************************************************************************* + TEST(test_reinterpret_as) + { + { + uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + etl::span data0 = data; + + etl::span data1 = data0.reinterpret_as(); + + CHECK_EQUAL(data1.size(), 2); + CHECK(data1[0] == 0x102); + CHECK(data1[1] == 0x304); + } + { + uint32_t data[] = { 0x01020304 }; + etl::span data0 = data; + etl::span data1 = data0.reinterpret_as(); + data1 = data1.first(3); + etl::span data2 = data1.reinterpret_as(); + CHECK_EQUAL(data2.size(), 0); + } + { + uint32_t data[] = { 0x01020304, 0x06070809 }; + etl::span data0 = data; + etl::span data1 = data0.reinterpret_as(); + data1 = data1.first(6); + etl::span data2 = data1.reinterpret_as(); + CHECK_EQUAL(data2.size(), 1); + + auto it = data2.begin(); + CHECK_NOT_EQUAL(it, data2.end()); + ++it; + CHECK_EQUAL(it, data2.end()); + } + } + + //************************************************************************* + TEST(test_reinterpret_as_aligned) + { + uint32_t data[] = { 0x01020304, 0x020406080, 0x03400560}; + etl::span data0 = data; + CHECK_EQUAL(data0.size(), 3); + + etl::span data1 = data0.reinterpret_as(); + CHECK_EQUAL(data1.size(), 12); + + etl::span data2 = data1.subspan(2).reinterpret_as(); + CHECK_EQUAL(data2.size(), 5); + + CHECK_THROW(data2 = data1.subspan(1).reinterpret_as(), etl::span_alignment_exception); + } + + //************************************************************************* + TEST(test_copy) + { + uint8_t src[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + uint8_t dst[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + etl::span data0 = src; + etl::span data1 = dst; + + CHECK_EQUAL(etl::copy(data0, data1), true); + CHECK(std::equal(data0.begin(), data0.end(), data1.begin())); + + data1 = data1.subspan(1); + + CHECK_EQUAL(etl::copy(data0, data1), true); + CHECK(std::equal(data0.begin(), data0.end(), data1.begin())); + + data1 = data1.subspan(1); + + CHECK_EQUAL(etl::copy(data0, data1), false); + + data0 = data0.subspan(0, 0); + + CHECK_EQUAL(etl::copy(data0, data1), true); + + data0 = src; + data1 = src; + + CHECK_EQUAL(etl::copy(data0, data1), true); + } + #include "etl/private/diagnostic_pop.h" }; } diff --git a/test/test_span_fixed_extent.cpp b/test/test_span_fixed_extent.cpp index 640126ee..1440fde7 100644 --- a/test/test_span_fixed_extent.cpp +++ b/test/test_span_fixed_extent.cpp @@ -30,6 +30,7 @@ SOFTWARE. #include "etl/span.h" #include "etl/array.h" +#include "etl/unaligned_type.h" #include #include @@ -1166,6 +1167,75 @@ namespace } } + //************************************************************************* + TEST(test_reinterpret_as) + { + uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + etl::span data0 = data; + + etl::span data1 = data0.reinterpret_as(); + + CHECK_EQUAL(data1.size(), 2); + CHECK(data1[0] == 0x102); + CHECK(data1[1] == 0x304); + } + + //************************************************************************* + TEST(test_reinterpret_as_aligned) + { + uint32_t data[] = { 0x01020304, 0x020406080, 0x03400560}; + etl::span data0 = data; + CHECK_EQUAL(data0.size(), 3); + + etl::span data1 = data0.reinterpret_as(); + CHECK_EQUAL(data1.size(), 12); + + etl::span data2 = data1.subspan(2).reinterpret_as(); + CHECK_EQUAL(data2.size(), 5); + + CHECK_THROW(data2 = data1.subspan(1).reinterpret_as(), etl::span_alignment_exception); + } + + //************************************************************************* + TEST(test_copy) + { + uint8_t src[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + uint8_t dst[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + { + etl::span data0 = src; + etl::span data1 = dst; + + CHECK_EQUAL(etl::copy(data0, data1), true); + CHECK(std::equal(data0.begin(), data0.end(), data1.begin())); + } + { + etl::span data0 = src; + etl::span data1(&dst[1], 5); + + CHECK_EQUAL(etl::copy(data0, data1), true); + CHECK(std::equal(data0.begin(), data0.end(), data1.begin())); + } + + { + etl::span data0 = src; + etl::span data1(&dst[2], 4); + + CHECK_EQUAL(etl::copy(data0, data1), false); + } + { + etl::span data0(&src[0], 0); + etl::span data1 = dst; + + CHECK_EQUAL(etl::copy(data0, data1), true); + } + { + etl::span data0 = src; + etl::span data1 = src; + + CHECK_EQUAL(etl::copy(data0, data1), true); + } + } + #include "etl/private/diagnostic_pop.h" }; } diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index be9ea1ec..e80eeaeb 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -208,6 +208,16 @@ namespace CHECK_EQUAL(sizeof(uint32_t), etl::le_uint32_t::Size); CHECK_EQUAL(sizeof(int64_t), etl::le_int64_t::Size); CHECK_EQUAL(sizeof(uint64_t), etl::le_uint64_t::Size); + + // check if net size equals gross size on platform + CHECK_EQUAL(sizeof(int8_t), sizeof(etl::le_int8_t)); + CHECK_EQUAL(sizeof(uint8_t), sizeof(etl::le_uint8_t)); + CHECK_EQUAL(sizeof(int16_t), sizeof(etl::le_int16_t)); + CHECK_EQUAL(sizeof(uint16_t), sizeof(etl::le_uint16_t)); + CHECK_EQUAL(sizeof(int32_t), sizeof(etl::le_int32_t)); + CHECK_EQUAL(sizeof(uint32_t), sizeof(etl::le_uint32_t)); + CHECK_EQUAL(sizeof(int64_t), sizeof(etl::le_int64_t)); + CHECK_EQUAL(sizeof(uint64_t), sizeof(etl::le_uint64_t)); } //************************************************************************* @@ -236,6 +246,16 @@ namespace CHECK_EQUAL(sizeof(uint32_t), etl::be_uint32_t::Size); CHECK_EQUAL(sizeof(int64_t), etl::be_int64_t::Size); CHECK_EQUAL(sizeof(uint64_t), etl::be_uint64_t::Size); + + // check if net size equals gross size on platform + CHECK_EQUAL(sizeof(int8_t), sizeof(etl::be_int8_t)); + CHECK_EQUAL(sizeof(uint8_t), sizeof(etl::be_uint8_t)); + CHECK_EQUAL(sizeof(int16_t), sizeof(etl::be_int16_t)); + CHECK_EQUAL(sizeof(uint16_t), sizeof(etl::be_uint16_t)); + CHECK_EQUAL(sizeof(int32_t), sizeof(etl::be_int32_t)); + CHECK_EQUAL(sizeof(uint32_t), sizeof(etl::be_uint32_t)); + CHECK_EQUAL(sizeof(int64_t), sizeof(etl::be_int64_t)); + CHECK_EQUAL(sizeof(uint64_t), sizeof(etl::be_uint64_t)); } //*************************************************************************