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.
This commit is contained in:
rolandreichweinbmw 2025-03-03 09:52:12 +01:00 committed by John Wellbelove
parent 90e432cd43
commit a26fed1a89
6 changed files with 330 additions and 17 deletions

View File

@ -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

View File

@ -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<element_type, etl::dynamic_extent>(pbegin + offset, pbegin + offset + count);
}
//*************************************************************************
/// Reinterpret the span as a span with different element type.
//*************************************************************************
template<typename TNew>
ETL_NODISCARD ETL_CONSTEXPR14 etl::span<TNew, etl::dynamic_extent> reinterpret_as() const
{
ETL_ASSERT(etl::is_aligned<etl::alignment_of<TNew>::value>(pbegin), ETL_ERROR(span_alignment_exception));
return etl::span<TNew, etl::dynamic_extent>(reinterpret_cast<TNew*>(pbegin),
Extent * sizeof(element_type) / sizeof(TNew));
}
private:
pointer pbegin;
@ -812,6 +856,28 @@ namespace etl
: etl::span<element_type, etl::dynamic_extent>(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<typename TNew>
ETL_NODISCARD ETL_CONSTEXPR14 etl::span<TNew, etl::dynamic_extent> reinterpret_as() const
{
ETL_ASSERT(etl::is_aligned<etl::alignment_of<TNew>::value>(pbegin), ETL_ERROR(span_alignment_exception));
return etl::span<TNew, etl::dynamic_extent>(reinterpret_cast<TNew*>(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 T1, size_t N1, typename T2, size_t N2>
typename etl::enable_if<etl::is_same<typename etl::remove_cv<T1>::type, typename etl::remove_cv<T2>::type>::value &&
!etl::is_const<T2>::value, bool>::type
copy(const etl::span<T1, N1>& src, const etl::span<T2, N2>& 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.
//*************************************************************************

View File

@ -449,7 +449,6 @@ namespace etl
//*************************************************************************
template <typename T, int Endian_>
ETL_PACKED_CLASS(unaligned_type) : public private_unaligned_type::unaligned_type_storage<sizeof(T)>
, public private_unaligned_type::unaligned_copy<sizeof(T), Endian_, etl::is_floating_point<T>::value ? false : true>
{
public:
@ -457,6 +456,8 @@ namespace etl
typedef T value_type;
typedef private_unaligned_type::unaligned_copy<sizeof(T), Endian_, etl::is_floating_point<T>::value ? false : true> unaligned_copy;
typedef typename private_unaligned_type::unaligned_type_storage<sizeof(T)>::storage_type storage_type;
typedef typename private_unaligned_type::unaligned_type_storage<sizeof(T)>::pointer pointer;
typedef typename private_unaligned_type::unaligned_type_storage<sizeof(T)>::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<T, Endian>& 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 <int Endian_Other>
unaligned_type(const unaligned_type<T, Endian_Other>& 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<T, Endian_>& 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 <int Endian_Other>
unaligned_type& operator =(const unaligned_type<T, Endian_Other>& 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 <typename T, int Endian_>
ETL_PACKED_CLASS(unaligned_type_ext) : public private_unaligned_type::unaligned_type_storage_ext<sizeof(T)>
, public private_unaligned_type::unaligned_copy<sizeof(T), Endian_, etl::is_floating_point<T>::value ? false : true>
{
public:
@ -600,6 +600,8 @@ namespace etl
typedef T value_type;
typedef private_unaligned_type::unaligned_copy<sizeof(T), Endian_, etl::is_floating_point<T>::value ? false : true> unaligned_copy;
typedef typename private_unaligned_type::unaligned_type_storage_ext<sizeof(T)>::storage_type storage_type;
typedef typename private_unaligned_type::unaligned_type_storage_ext<sizeof(T)>::pointer pointer;
typedef typename private_unaligned_type::unaligned_type_storage_ext<sizeof(T)>::const_pointer const_pointer;
@ -625,7 +627,7 @@ namespace etl
unaligned_type_ext(T value, pointer storage_)
: private_unaligned_type::unaligned_type_storage_ext<Size>(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<T, Endian_Other>& other, pointer storage_)
: private_unaligned_type::unaligned_type_storage_ext<Size>(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<T, Endian>& 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 <int Endian_Other>
unaligned_type_ext& operator =(const unaligned_type_ext<T, Endian_Other>& 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;
}

View File

@ -30,6 +30,7 @@ SOFTWARE.
#include "etl/span.h"
#include "etl/array.h"
#include "etl/unaligned_type.h"
#include <array>
#include <vector>
@ -1244,6 +1245,131 @@ namespace
}
}
//*************************************************************************
TEST(test_advance)
{
{
uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
etl::span<uint8_t> 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<const uint8_t> 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<uint8_t> data0 = data;
etl::span<etl::be_uint16_t> data1 = data0.reinterpret_as<etl::be_uint16_t>();
CHECK_EQUAL(data1.size(), 2);
CHECK(data1[0] == 0x102);
CHECK(data1[1] == 0x304);
}
{
uint32_t data[] = { 0x01020304 };
etl::span<uint32_t> data0 = data;
etl::span<uint8_t> data1 = data0.reinterpret_as<uint8_t>();
data1 = data1.first(3);
etl::span<int32_t> data2 = data1.reinterpret_as<int32_t>();
CHECK_EQUAL(data2.size(), 0);
}
{
uint32_t data[] = { 0x01020304, 0x06070809 };
etl::span<uint32_t> data0 = data;
etl::span<uint8_t> data1 = data0.reinterpret_as<uint8_t>();
data1 = data1.first(6);
etl::span<int32_t> data2 = data1.reinterpret_as<int32_t>();
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<uint32_t> data0 = data;
CHECK_EQUAL(data0.size(), 3);
etl::span<uint8_t> data1 = data0.reinterpret_as<uint8_t>();
CHECK_EQUAL(data1.size(), 12);
etl::span<uint16_t> data2 = data1.subspan(2).reinterpret_as<uint16_t>();
CHECK_EQUAL(data2.size(), 5);
CHECK_THROW(data2 = data1.subspan(1).reinterpret_as<uint16_t>(), 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<uint8_t> data0 = src;
etl::span<uint8_t> 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"
};
}

View File

@ -30,6 +30,7 @@ SOFTWARE.
#include "etl/span.h"
#include "etl/array.h"
#include "etl/unaligned_type.h"
#include <array>
#include <vector>
@ -1166,6 +1167,75 @@ namespace
}
}
//*************************************************************************
TEST(test_reinterpret_as)
{
uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
etl::span<uint8_t, 5> data0 = data;
etl::span<etl::be_uint16_t> data1 = data0.reinterpret_as<etl::be_uint16_t>();
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<uint32_t, 3> data0 = data;
CHECK_EQUAL(data0.size(), 3);
etl::span<uint8_t> data1 = data0.reinterpret_as<uint8_t>();
CHECK_EQUAL(data1.size(), 12);
etl::span<uint16_t> data2 = data1.subspan(2).reinterpret_as<uint16_t>();
CHECK_EQUAL(data2.size(), 5);
CHECK_THROW(data2 = data1.subspan(1).reinterpret_as<uint16_t>(), 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<uint8_t, 5> data0 = src;
etl::span<uint8_t, 6> data1 = dst;
CHECK_EQUAL(etl::copy(data0, data1), true);
CHECK(std::equal(data0.begin(), data0.end(), data1.begin()));
}
{
etl::span<uint8_t, 5> data0 = src;
etl::span<uint8_t, 5> data1(&dst[1], 5);
CHECK_EQUAL(etl::copy(data0, data1), true);
CHECK(std::equal(data0.begin(), data0.end(), data1.begin()));
}
{
etl::span<uint8_t, 5> data0 = src;
etl::span<uint8_t, 4> data1(&dst[2], 4);
CHECK_EQUAL(etl::copy(data0, data1), false);
}
{
etl::span<uint8_t, 0> data0(&src[0], 0);
etl::span<uint8_t, 6> data1 = dst;
CHECK_EQUAL(etl::copy(data0, data1), true);
}
{
etl::span<uint8_t, 5> data0 = src;
etl::span<uint8_t, 5> data1 = src;
CHECK_EQUAL(etl::copy(data0, data1), true);
}
}
#include "etl/private/diagnostic_pop.h"
};
}

View File

@ -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));
}
//*************************************************************************