Merge branch 'hotfix/byte_stream_reader-with-const-char-data' into development

This commit is contained in:
John Wellbelove 2021-09-29 18:50:01 +01:00
commit b47e58a8fe
3 changed files with 306 additions and 135 deletions

View File

@ -48,121 +48,23 @@ SOFTWARE.
namespace etl
{
namespace private_byte_stream
{
class byte_stream_common
{
public:
typedef const char* const_iterator;
//***************************************************************************
/// Sets the index back to the position in the stream. Default = 0.
//***************************************************************************
void restart(size_t n = 0U)
{
pcurrent = const_cast<char*>(pdata + n);
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pcurrent;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator cbegin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator cend() const
{
return pcurrent;
}
//***************************************************************************
/// Returns a span of the used portion of the stream.
//***************************************************************************
etl::span<char> used_data() const
{
return etl::span<char>(pdata, pcurrent);
}
//***************************************************************************
/// Returns a span of the free portion of the stream.
//***************************************************************************
etl::span<char> free_data() const
{
return etl::span<char>(pcurrent, pdata + length);
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<char> data() const
{
return etl::span<char>(pdata, pdata + length);
}
protected:
byte_stream_common(char* pdata_, size_t length_, etl::endian buffer_endianness_)
: pdata(pdata_)
, pcurrent(pdata_)
, length(length_)
, buffer_endianness(buffer_endianness_)
{
}
void copy_value(const char* source, char* destination, size_t length) const
{
const etl::endian platform_endianness = etl::endianness::value();
if (buffer_endianness == platform_endianness)
{
etl::copy(source, source + length, destination);
}
else
{
etl::reverse_copy(source, source + length, destination);
}
}
char* const pdata; ///< The start of the byte stream buffer.
char* pcurrent; ///< The current position in the byte stream buffer.
const size_t length; ///< The length of the byte stream buffer.
const etl::endian buffer_endianness; ///< The endianness of the buffer data.
};
}
//***************************************************************************
/// Encodes a byte stream.
//***************************************************************************
class byte_stream_writer : public private_byte_stream::byte_stream_common
class byte_stream_writer
{
public:
typedef const char* const_iterator;
//***************************************************************************
/// Construct from span.
//***************************************************************************
byte_stream_writer(etl::span<char> span_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(span_.begin(), span_.size_bytes(), buffer_endianness_)
: pdata(span_.begin())
, pcurrent(span_.begin())
, length(span_.size_bytes())
, buffer_endianness(buffer_endianness_)
{
}
@ -170,7 +72,10 @@ namespace etl
/// Construct from range.
//***************************************************************************
byte_stream_writer(char* begin_, char* end_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, etl::distance(begin_, end_), buffer_endianness_)
: pdata(begin_)
, pcurrent(begin_)
, length(etl::distance(begin_, end_))
, buffer_endianness(buffer_endianness_)
{
}
@ -178,7 +83,10 @@ namespace etl
/// Construct from begin and length.
//***************************************************************************
byte_stream_writer(char* begin_, size_t length_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, length_, buffer_endianness_)
: pdata(begin_)
, pcurrent(begin_)
, length(length_)
, buffer_endianness(buffer_endianness_)
{
}
@ -187,7 +95,10 @@ namespace etl
//***************************************************************************
template <typename T, size_t Size>
byte_stream_writer(T(&begin_)[Size], etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, begin_ + (Size * sizeof(T)), buffer_endianness_)
: pdata(begin_)
, pcurrent(begin_)
, length(begin_ + (Size * sizeof(T)))
, buffer_endianness(buffer_endianness_)
{
}
@ -249,6 +160,70 @@ namespace etl
return success;
}
//***************************************************************************
/// Sets the index back to the position in the stream. Default = 0.
//***************************************************************************
void restart(size_t n = 0U)
{
pcurrent = const_cast<char*>(pdata + n);
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pcurrent;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator cbegin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator cend() const
{
return pcurrent;
}
//***************************************************************************
/// Returns a span of the used portion of the stream.
//***************************************************************************
etl::span<char> used_data() const
{
return etl::span<char>(pdata, pcurrent);
}
//***************************************************************************
/// Returns a span of the free portion of the stream.
//***************************************************************************
etl::span<char> free_data() const
{
return etl::span<char>(pcurrent, pdata + length);
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<char> data() const
{
return etl::span<char>(pdata, pdata + length);
}
//***************************************************************************
/// Returns <b>true</b> if the byte stream index has reached the end.
//***************************************************************************
@ -311,37 +286,79 @@ namespace etl
copy_value(pv, pcurrent, sizeof(T));
pcurrent += sizeof(T);
}
//*********************************
void copy_value(const char* source, char* destination, size_t length) const
{
const etl::endian platform_endianness = etl::endianness::value();
if (buffer_endianness == platform_endianness)
{
etl::copy(source, source + length, destination);
}
else
{
etl::reverse_copy(source, source + length, destination);
}
}
char* const pdata; ///< The start of the byte stream buffer.
char* pcurrent; ///< The current position in the byte stream buffer.
const size_t length; ///< The length of the byte stream buffer.
const etl::endian buffer_endianness; ///< The endianness of the buffer data.
};
//***************************************************************************
/// Decodes byte streams.
/// Data must be stored in the stream in network order.
//***************************************************************************
class byte_stream_reader : public private_byte_stream::byte_stream_common
class byte_stream_reader
{
public:
typedef const char* const_iterator;
//***************************************************************************
/// Construct from span.
//***************************************************************************
byte_stream_reader(etl::span<char> span_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(span_.begin(), span_.size_bytes(), buffer_endianness_)
: pdata(span_.begin())
, pcurrent(span_.begin())
, length(span_.size_bytes())
, buffer_endianness(buffer_endianness_)
{
}
//***************************************************************************
/// Construct from span.
//***************************************************************************
byte_stream_reader(etl::span<const char> span_, etl::endian buffer_endianness_ = etl::endian::big)
: pdata(span_.begin())
, pcurrent(span_.begin())
, length(span_.size_bytes())
, buffer_endianness(buffer_endianness_)
{
}
//***************************************************************************
/// Construct from range.
//***************************************************************************
byte_stream_reader(char* begin_, char* end_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, etl::distance(begin_, end_), buffer_endianness_)
byte_stream_reader(const char* begin_, const char* end_, etl::endian buffer_endianness_ = etl::endian::big)
: pdata(begin_)
, pcurrent(begin_)
, length(etl::distance(begin_, end_))
, buffer_endianness(buffer_endianness_)
{
}
//***************************************************************************
/// Construct from begin and length.
//***************************************************************************
byte_stream_reader(char* begin_, size_t length_, etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, length_, buffer_endianness_)
byte_stream_reader(const char* begin_, size_t length_, etl::endian buffer_endianness_ = etl::endian::big)
: pdata(begin_)
, pcurrent(begin_)
, length(length_)
, buffer_endianness(buffer_endianness_)
{
}
@ -350,7 +367,22 @@ namespace etl
//***************************************************************************
template <typename T, size_t Size>
byte_stream_reader(T(&begin_)[Size], etl::endian buffer_endianness_ = etl::endian::big)
: byte_stream_common(begin_, begin_ + (Size * sizeof(T)), buffer_endianness_)
: pdata(begin_)
, pcurrent(begin_)
, length(begin_ + (Size * sizeof(T)))
, buffer_endianness(buffer_endianness_)
{
}
//***************************************************************************
/// Construct from const array.
//***************************************************************************
template <typename T, size_t Size>
byte_stream_reader(const T(&begin_)[Size], etl::endian buffer_endianness_ = etl::endian::big)
: pdata(begin_)
, pcurrent(begin_)
, length(begin_ + (Size * sizeof(T)))
, buffer_endianness(buffer_endianness_)
{
}
@ -376,17 +408,17 @@ namespace etl
/// Read a byte range from the stream.
//***************************************************************************
template <typename T>
typename etl::enable_if<sizeof(T) == 1U, etl::optional<etl::span<T> > >::type
typename etl::enable_if<sizeof(T) == 1U, etl::optional<etl::span<const T> > >::type
read(size_t n)
{
etl::optional<etl::span<T> > result;
etl::optional<etl::span<const T> > result;
// Do we have enough room?
if (available<T>() >= n)
{
char* pend = pcurrent + (n * sizeof(T));
const char* pend = pcurrent + (n * sizeof(T));
result = etl::span<T>(reinterpret_cast<T*>(pcurrent), reinterpret_cast<T*>(pend));
result = etl::span<const T>(reinterpret_cast<const T*>(pcurrent), reinterpret_cast<const T*>(pend));
pcurrent = pend;
}
@ -397,11 +429,9 @@ namespace etl
/// Read a range of T from the stream.
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value || etl::is_floating_point<T>::value, etl::optional<etl::span<T> > >::type
typename etl::enable_if<etl::is_integral<T>::value || etl::is_floating_point<T>::value, etl::optional<etl::span<const T> > >::type
read(etl::span<T> range)
{
etl::optional<etl::span<T> > result;
// Do we have enough room?
if (available<T>() >= range.size())
{
@ -412,14 +442,16 @@ namespace etl
*destination++ = from_bytes<T>();
}
result = range;
return etl::optional<etl::span<const T> >(etl::span<const T>(range.begin(), range.end()));
}
return result;
return etl::optional<etl::span<const T> >();
}
//***************************************************************************
/// Skip n items of T, up to the maximum space available.
/// Returns <b>true</b> if the skip was possible.
/// Returns <b>false</b> if the full skip size was not possible.
//***************************************************************************
template <typename T>
bool skip(size_t n)
@ -438,6 +470,70 @@ namespace etl
}
}
//***************************************************************************
/// Sets the index back to the position in the stream. Default = 0.
//***************************************************************************
void restart(size_t n = 0U)
{
pcurrent = pdata + n;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pcurrent;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator cbegin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator cend() const
{
return pcurrent;
}
//***************************************************************************
/// Returns a span of the used portion of the stream.
//***************************************************************************
etl::span<const char> used_data() const
{
return etl::span<const char>(pdata, pcurrent);
}
//***************************************************************************
/// Returns a span of the free portion of the stream.
//***************************************************************************
etl::span<const char> free_data() const
{
return etl::span<const char>(pcurrent, pdata + length);
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<const char> data() const
{
return etl::span<const char>(pdata, pdata + length);
}
//***************************************************************************
/// Returns <b>true</b> if the byte stream is empty.
//***************************************************************************
@ -460,7 +556,7 @@ namespace etl
template <typename T>
size_t available() const
{
return (length - etl::distance(pdata, reinterpret_cast<char* const>(pcurrent))) / sizeof(T);
return (length - etl::distance(pdata, reinterpret_cast<const char* const>(pcurrent))) / sizeof(T);
}
private:
@ -488,6 +584,26 @@ namespace etl
return value;
}
//*********************************
void copy_value(const char* source, char* destination, size_t length) const
{
const etl::endian platform_endianness = etl::endianness::value();
if (buffer_endianness == platform_endianness)
{
etl::copy(source, source + length, destination);
}
else
{
etl::reverse_copy(source, source + length, destination);
}
}
const char* const pdata; ///< The start of the byte stream buffer.
const char* pcurrent; ///< The current position in the byte stream buffer.
const size_t length; ///< The length of the byte stream buffer.
const etl::endian buffer_endianness; ///< The endianness of the buffer data.
};
//***************************************************************************

View File

@ -41,9 +41,12 @@ SOFTWARE.
#include "array.h"
///\defgroup span span
/// A wrapper for arrays
///\ingroup containers
#if ETL_CPP11_SUPPORTED && ETL_USING_STL
#include <array>
#endif
namespace etl
{
static ETL_CONSTANT size_t dynamic_extent = etl::integral_limits<size_t>::max;
@ -80,22 +83,44 @@ namespace etl
}
//*************************************************************************
/// Construct from std::array or etl::array or other type that supports
/// Construct from etl::array.
//*************************************************************************
template <typename U, size_t N>
ETL_CONSTEXPR span(const etl::array<U, N>& a) ETL_NOEXCEPT
: mbegin(a.data())
, mend(a.data() + a.size())
{
}
#if ETL_CPP11_SUPPORTED && ETL_USING_STL
//*************************************************************************
/// Construct from std::array.
//*************************************************************************
template <typename U, size_t N>
ETL_CONSTEXPR span(const std::array<U, N>& a) ETL_NOEXCEPT
: mbegin(a.data())
, mend(a.data() + a.size())
{
}
#endif
//*************************************************************************
/// Construct from a container or other type that supports
/// data() and size() member functions.
//*************************************************************************
template <typename TSpan>
ETL_CONSTEXPR span(TSpan& a) ETL_NOEXCEPT
template <typename TContainer>
ETL_CONSTEXPR span(TContainer& a) ETL_NOEXCEPT
: mbegin(a.data())
, mend(a.data() + a.size())
{
}
//*************************************************************************
/// Construct from std::array or etl::array or other type that supports
/// Construct from a container or other type that supports
/// data() and size() member functions.
//*************************************************************************
template <typename TSpan>
ETL_CONSTEXPR span(const TSpan& a) ETL_NOEXCEPT
template <typename TContainer>
ETL_CONSTEXPR span(const TContainer& a) ETL_NOEXCEPT
: mbegin(a.data())
, mend(a.data() + a.size())
{

View File

@ -881,7 +881,7 @@ namespace
etl::byte_stream_reader reader(storage.data(), writer.size_bytes());
etl::optional<etl::span<char>> output = reader.read<char>(4U);
etl::optional<etl::span<const char>> output = reader.read<char>(4U);
CHECK_EQUAL(4U, output.value().size());
CHECK_EQUAL(int(put_data[0]), int(output.value()[0]));
CHECK_EQUAL(int(put_data[1]), int(output.value()[1]));
@ -909,8 +909,8 @@ namespace
etl::span<int32_t> output(get_data.begin(), get_data.end());
etl::optional<etl::span<int32_t>> result = reader.read<int32_t>(output);
CHECK_EQUAL(4U, result.value().size());
etl::optional<etl::span<const int32_t> > result = reader.read<int32_t>(output);
CHECK(result.has_value());
CHECK_EQUAL(put_data[0], get_data[0]);
CHECK_EQUAL(put_data[1], get_data[1]);
CHECK_EQUAL(put_data[2], get_data[2]);
@ -1071,5 +1071,35 @@ namespace
// Skip five int16_t (too many)
CHECK(!reader.skip<int16_t>(5U));
}
//*************************************************************************
TEST(read_span_int32_t)
{
std::array<char, 4 * sizeof(int32_t)> storage;
std::array<int32_t, 4> put_data = { int32_t(0x00000001), int32_t(0xA55AA55A), int32_t(0x5AA55AA5), int32_t(0xFFFFFFFF) };
std::array<int32_t, 4> get_data = { int32_t(0x00000000), int32_t(0x00000000), int32_t(0x00000000), int32_t(0x00000000) };
etl::span<int32_t> input(put_data.begin(), put_data.end());
etl::byte_stream_writer writer(storage.data(), storage.size());
writer.write(input);
const char* read_only_storage = reinterpret_cast<const char*>(storage.data());
etl::byte_stream_reader reader(read_only_storage, storage.size() * sizeof(int32_t));
etl::optional<etl::span<const int32_t> > result = reader.read(etl::span<int32_t>(get_data.begin(), get_data.end()));
CHECK(result.has_value());
CHECK_EQUAL(int32_t(0x00000001), result.value()[0]);
CHECK_EQUAL(int32_t(0xA55AA55A), result.value()[1]);
CHECK_EQUAL(int32_t(0x5AA55AA5), result.value()[2]);
CHECK_EQUAL(int32_t(0xFFFFFFFF), result.value()[3]);
CHECK_EQUAL(int32_t(0x00000001), get_data[0]);
CHECK_EQUAL(int32_t(0xA55AA55A), get_data[1]);
CHECK_EQUAL(int32_t(0x5AA55AA5), get_data[2]);
CHECK_EQUAL(int32_t(0xFFFFFFFF), get_data[3]);
}
};
}