diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 64d94d573..2369f68cf 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 8d03e5fba..7e4ad35ad 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/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index dd71bae22..14d2c9559 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,108 @@ 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); + } + + //************************************************************************* + 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 640126eea..1440fde70 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" }; }