///\file /****************************************************************************** The MIT License(MIT) Embedded Template Library. https://github.com/ETLCPP/etl https://www.etlcpp.com Copyright(c) 2021 John Wellbelove Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #ifndef ETL_BYTE_STREAM_INCLUDED #define ETL_BYTE_STREAM_INCLUDED #include "platform.h" #include "type_traits.h" #include "nullptr.h" #include "endianness.h" #include "integral_limits.h" #include "algorithm.h" #include "iterator.h" #include "memory.h" #include "span.h" #include "iterator.h" #include "optional.h" #include "delegate.h" #include "exception.h" #include "error_handler.h" #include #include namespace etl { //*************************************************************************** /// Encodes a byte stream. //*************************************************************************** class byte_stream_writer { public: typedef char* iterator; typedef const char* const_iterator; typedef etl::span callback_parameter_type; typedef etl::delegate callback_type; //*************************************************************************** /// Construct from span. //*************************************************************************** byte_stream_writer(etl::span span_, etl::endian stream_endianness_, callback_type callback_ = callback_type()) : pdata(span_.begin()) , pcurrent(span_.begin()) , stream_length(span_.size_bytes()) , stream_endianness(stream_endianness_) , callback(callback_) { } //*************************************************************************** /// Construct from span. //*************************************************************************** byte_stream_writer(etl::span span_, etl::endian stream_endianness_, callback_type callback_ = callback_type()) : pdata(reinterpret_cast(span_.begin())) , pcurrent(reinterpret_cast(span_.begin())) , stream_length(span_.size_bytes()) , stream_endianness(stream_endianness_) , callback(callback_) { } //*************************************************************************** /// Construct from range. //*************************************************************************** byte_stream_writer(void* begin_, void* end_, etl::endian stream_endianness_, callback_type callback_ = callback_type()) : pdata(reinterpret_cast(begin_)) , pcurrent(reinterpret_cast(begin_)) , stream_length(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) , stream_endianness(stream_endianness_) , callback(callback_) { } //*************************************************************************** /// Construct from begin and length. //*************************************************************************** byte_stream_writer(void* begin_, size_t length_, etl::endian stream_endianness_, callback_type callback_ = callback_type()) : pdata(reinterpret_cast(begin_)) , pcurrent(reinterpret_cast(begin_)) , stream_length(length_) , stream_endianness(stream_endianness_) , callback(callback_) { } //*************************************************************************** /// Construct from array. //*************************************************************************** template byte_stream_writer(T(&begin_)[Size], etl::endian stream_endianness_, callback_type callback_ = callback_type()) : pdata(begin_) , pcurrent(begin_) , stream_length(begin_ + (Size * sizeof(T))) , stream_endianness(stream_endianness_) , callback(callback_) { } //*************************************************************************** /// Writes a boolean to the stream //*************************************************************************** void write_unchecked(bool value) { *pcurrent++ = value ? 1 : 0; } //*************************************************************************** /// Writes a boolean to the stream //*************************************************************************** bool write(bool value) { bool success = (available() > 0U); if (success) { write_unchecked(value); } return success; } //*************************************************************************** /// Write a value to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, void>::type write_unchecked(T value) { to_bytes(value); } //*************************************************************************** /// Write a value to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, bool>::type write(T value) { bool success = (available() > 0U); if (success) { write_unchecked(value); } return success; } //*************************************************************************** /// Write a range of T to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, void>::type write_unchecked(const etl::span& range) { typename etl::span::iterator itr = range.begin(); while (itr != range.end()) { to_bytes(*itr); ++itr; } } //*************************************************************************** /// Write a range of T to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, bool>::type write(const etl::span& range) { bool success = (available() >= range.size()); if (success) { write_unchecked(range); } return success; } //*************************************************************************** /// Write a range of T to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, void>::type write_unchecked(const T* start, size_t length) { while (length-- != 0U) { to_bytes(*start); ++start; } } //*************************************************************************** /// Write a range of T to the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, bool>::type write(const T* start, size_t length) { bool success = (available() >= length); if (success) { write_unchecked(start, length); } return success; } //*************************************************************************** /// Skip n items of T, if the total space is available. /// Returns true if the skip was possible. /// Returns false if the skip size was not possible. //*************************************************************************** template bool skip(size_t n) { bool success = (available() >= n); if (success) { step(n * sizeof(T)); } return success; } //*************************************************************************** /// 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. //*************************************************************************** iterator begin() { return pdata; } //*************************************************************************** /// Returns start of the stream. //*************************************************************************** const_iterator begin() const { return pdata; } //*************************************************************************** /// Returns end of the stream. //*************************************************************************** iterator end() { return pcurrent; } //*************************************************************************** /// 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 used_data() { return etl::span(pdata, pcurrent); } //*************************************************************************** /// Returns a span of the used portion of the stream. //*************************************************************************** etl::span used_data() const { return etl::span(pdata, pcurrent); } //*************************************************************************** /// Returns a span of the free portion of the stream. //*************************************************************************** etl::span free_data() { return etl::span(pcurrent, pdata + stream_length); } //*************************************************************************** /// Returns a span of the free portion of the stream. //*************************************************************************** etl::span free_data() const { return etl::span(pcurrent, pdata + stream_length); } //*************************************************************************** /// Returns a span of whole the stream. //*************************************************************************** etl::span data() { return etl::span(pdata, pdata + stream_length); } //*************************************************************************** /// Returns a span of whole the stream. //*************************************************************************** etl::span data() const { return etl::span(pdata, pdata + stream_length); } //*************************************************************************** /// Returns true if the byte stream index has reached the end. //*************************************************************************** bool full() const { return size_bytes() == capacity(); } //*************************************************************************** /// Returns true if the byte stream is empty. //*************************************************************************** bool empty() const { return size_bytes() == 0U; } //*************************************************************************** /// Returns the number of bytes used in the stream. //*************************************************************************** size_t size_bytes() const { return etl::distance(pdata, pcurrent); } //*************************************************************************** /// Returns the maximum number of bytes in the stream. //*************************************************************************** size_t capacity() const { return stream_length; } //*************************************************************************** /// The number of T left in the stream. //*************************************************************************** template size_t available() const { return (capacity() - size_bytes()) / sizeof(T); } //*************************************************************************** /// The number of bytes left in the stream. //*************************************************************************** size_t available_bytes() const { return available(); } //*************************************************************************** /// Sets the function to call after every write. //*************************************************************************** void set_callback(callback_type callback_) { callback = callback_; } //*************************************************************************** /// Gets the function to call after every write. //*************************************************************************** callback_type get_callback() const { return callback; } //*************************************************************************** /// Gets the endianness of the stream. //*************************************************************************** etl::endian get_endianness() const { return stream_endianness; } private: //*************************************************************************** /// to_bytes //*************************************************************************** template typename etl::enable_if::type to_bytes(const T value) { *pcurrent = static_cast(value); step(1U); } //********************************* template typename etl::enable_if::type to_bytes(const T value) { const char* pv = reinterpret_cast(&value); copy_value(pv, pcurrent, sizeof(T)); step(sizeof(T)); } //********************************* void step(size_t n) { callback.call_if(etl::span(pcurrent, pcurrent + n)); pcurrent += n; } //********************************* void copy_value(const char* source, char* destination, size_t length) const { const etl::endian platform_endianness = etl::endianness::value(); if (stream_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 stream_length; ///< The length of the byte stream buffer. const etl::endian stream_endianness; ///< The endianness of the stream data. callback_type callback; ///< An optional callback on every step on the write buffer. }; //*************************************************************************** /// Decodes byte streams. /// Data must be stored in the stream in network order. //*************************************************************************** class byte_stream_reader { public: typedef char* iterator; typedef const char* const_iterator; //*************************************************************************** /// Construct from span. //*************************************************************************** byte_stream_reader(etl::span span_, etl::endian stream_endianness_) : pdata(span_.begin()) , pcurrent(span_.begin()) , stream_length(span_.size_bytes()) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Construct from span. //*************************************************************************** byte_stream_reader(etl::span span_, etl::endian stream_endianness_) : pdata(span_.begin()) , pcurrent(span_.begin()) , stream_length(span_.size_bytes()) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Construct from range. //*************************************************************************** byte_stream_reader(const void* begin_, const void* end_, etl::endian stream_endianness_) : pdata(reinterpret_cast(begin_)) , pcurrent(reinterpret_cast(begin_)) , stream_length(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Construct from begin and length. //*************************************************************************** byte_stream_reader(const void* begin_, size_t length_, etl::endian stream_endianness_) : pdata(reinterpret_cast(begin_)) , pcurrent(reinterpret_cast(begin_)) , stream_length(length_) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Construct from array. //*************************************************************************** template byte_stream_reader(T(&begin_)[Size], etl::endian stream_endianness_) : pdata(begin_) , pcurrent(begin_) , stream_length(begin_ + (Size * sizeof(T))) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Construct from const array. //*************************************************************************** template byte_stream_reader(const T(&begin_)[Size], etl::endian stream_endianness_) : pdata(begin_) , pcurrent(begin_) , stream_length(begin_ + (Size * sizeof(T))) , stream_endianness(stream_endianness_) { } //*************************************************************************** /// Read a value from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, T>::type read_unchecked() { return from_bytes(); } //*************************************************************************** /// Read a value from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, etl::optional >::type read() { etl::optional result; // Do we have enough room? if (available() > 0U) { result = read_unchecked(); } return result; } //*************************************************************************** /// Read a byte range from the stream. //*************************************************************************** template typename etl::enable_if >::type read_unchecked(size_t n) { etl::span result; const char* pend = pcurrent + (n * sizeof(T)); result = etl::span(reinterpret_cast(pcurrent), reinterpret_cast(pend)); pcurrent = pend; return result; } //*************************************************************************** /// Read a byte range from the stream. //*************************************************************************** template typename etl::enable_if > >::type read(size_t n) { etl::optional > result; // Do we have enough room? if (available() >= n) { result = read_unchecked(n); } return result; } //*************************************************************************** /// Read a range of T from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, etl::span >::type read_unchecked(etl::span range) { typename etl::span::iterator destination = range.begin(); while (destination != range.end()) { *destination++ = from_bytes(); } return etl::span(range.begin(), range.end()); } //*************************************************************************** /// Read a range of T from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, etl::optional > >::type read(etl::span range) { // Do we have enough room? if (available() >= range.size()) { return etl::optional >(read_unchecked(range)); } return etl::optional >(); } //*************************************************************************** /// Read a range of T from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, etl::span >::type read_unchecked(T* start, size_t length) { T* destination = start; for (size_t i = 0; i < length; ++i) { *destination++ = from_bytes(); } return etl::span(start, length); } //*************************************************************************** /// Read a range of T from the stream. //*************************************************************************** template typename etl::enable_if::value || etl::is_floating_point::value, etl::optional > >::type read(T* start, size_t length) { // Do we have enough room? if (available() >= length) { return etl::optional >(read_unchecked(start, length)); } return etl::optional >(); } //*************************************************************************** /// Skip n items of T, up to the maximum space available. /// Returns true if the skip was possible. /// Returns false if the full skip size was not possible. //*************************************************************************** template bool skip(size_t n) { if (n <= available()) { pcurrent += (n * sizeof(T)); return true; } else { return false; } } //*************************************************************************** /// 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 used_data() const { return etl::span(pdata, pcurrent); } //*************************************************************************** /// Returns a span of the free portion of the stream. //*************************************************************************** etl::span free_data() const { return etl::span(pcurrent, pdata + stream_length); } //*************************************************************************** /// Returns a span of whole the stream. //*************************************************************************** etl::span data() const { return etl::span(pdata, pdata + stream_length); } //*************************************************************************** /// Returns true if the byte stream is empty. //*************************************************************************** bool empty() const { return available() == 0U; } //*************************************************************************** /// Returns the number of bytes used in the stream. //*************************************************************************** size_t size_bytes() const { return stream_length; } //*************************************************************************** /// The number of T left in the stream. //*************************************************************************** template size_t available() const { size_t used = etl::distance(pdata, pcurrent); return (stream_length - used) / sizeof(T); } //*************************************************************************** /// The number of bytes left in the stream. //*************************************************************************** size_t available_bytes() const { return available(); } private: //*************************************************************************** /// from_bytes //*************************************************************************** template typename etl::enable_if::type from_bytes() { return static_cast(*pcurrent++); } //********************************* template typename etl::enable_if::type from_bytes() { T value; char* pv = reinterpret_cast(&value); copy_value(pcurrent, pv, sizeof(T)); pcurrent += sizeof(T); return value; } //********************************* void copy_value(const char* source, char* destination, size_t length) const { const etl::endian platform_endianness = etl::endianness::value(); if (stream_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 stream_length; ///< The length of the byte stream buffer. const etl::endian stream_endianness; ///< The endianness of the stream data. }; //*************************************************************************** /// Default implementation of the write function. /// For integral and floating point types only. /// Overload this to support custom types. //*************************************************************************** template void write_unchecked(etl::byte_stream_writer& stream, const T& value) { stream.write_unchecked(value); } //*************************************************************************** /// Implementation of the write function. //*************************************************************************** template bool write(etl::byte_stream_writer& stream, const T& value) { return stream.write(value); } //*************************************************************************** /// Default implementation of the read function. /// For integral and floating point types only. /// Overload this to support custom types. //*************************************************************************** template T read_unchecked(etl::byte_stream_reader& stream) { return stream.read_unchecked(); } //*************************************************************************** /// Implementation of the read function. //*************************************************************************** template etl::optional read(etl::byte_stream_reader& stream) { return stream.read(); } } #endif