fsfw/contrib/fsfw_contrib/etl-20.39.4/include/etl/bit_stream.h

1410 lines
47 KiB
C++

///\file
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2018 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_BIT_STREAM_INCLUDED
#define ETL_BIT_STREAM_INCLUDED
#include "platform.h"
#include "type_traits.h"
#include "nullptr.h"
#include "endianness.h"
#include "integral_limits.h"
#include "binary.h"
#include "algorithm.h"
#include "iterator.h"
#include "memory.h"
#include "delegate.h"
#include "span.h"
#include "optional.h"
#include "exception.h"
#include "error_handler.h"
#include <stdint.h>
#include <limits.h>
#include "private/minmax_push.h"
namespace etl
{
//***************************************************************************
/// Encodes and decodes bitstreams.
/// Data must be stored in the stream in network order.
//***************************************************************************
class bit_stream
{
public:
typedef const unsigned char* const_iterator;
//***************************************************************************
/// Default constructor.
//***************************************************************************
bit_stream()
: pdata(ETL_NULLPTR)
, length_chars(0U)
{
restart();
}
//***************************************************************************
/// Construct from range.
//***************************************************************************
bit_stream(void* begin_, void* end_)
: pdata(reinterpret_cast<unsigned char*>(begin_))
, length_chars(etl::distance(reinterpret_cast<unsigned char*>(begin_), reinterpret_cast<unsigned char*>(end_)))
{
restart();
}
//***************************************************************************
/// Construct from begin and length.
//***************************************************************************
bit_stream(void* begin_, size_t length_)
: pdata(reinterpret_cast<unsigned char*>(begin_))
, length_chars(length_)
{
restart();
}
//***************************************************************************
/// Construct from begin and length.
//***************************************************************************
void set_stream(void* begin_, size_t length_)
{
pdata = reinterpret_cast<unsigned char*>(begin_);
length_chars = length_;
restart();
}
//***************************************************************************
/// Construct from range.
//***************************************************************************
void set_stream(void* begin_, void* end_)
{
set_stream(begin_, etl::distance(reinterpret_cast<unsigned char*>(begin_), reinterpret_cast<unsigned char*>(end_)));
}
//***************************************************************************
/// Sets the indexes back to the beginning of the stream.
//***************************************************************************
void restart()
{
bits_available_in_char = CHAR_BIT;
char_index = 0U;
bits_available = CHAR_BIT * length_chars;
}
//***************************************************************************
/// Returns <b>true</b> if the bitsteam indexes have reached the end.
//***************************************************************************
bool at_end() const
{
return (bits_available == 0U);
}
//***************************************************************************
/// Writes a boolean to the stream
//***************************************************************************
bool put(bool value)
{
bool success = false;
if (pdata != ETL_NULLPTR)
{
if (bits_available > 0)
{
unsigned char chunk = value ? 1 : 0;
put_integral(uint32_t(chunk), 1);
success = true;
}
}
return success;
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, bool>::type
put(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
return put_integral(static_cast<uint32_t>(value), nbits);
}
#if ETL_USING_64BIT_TYPES
//***************************************************************************
/// For 64bit integral types
//***************************************************************************
bool put(int64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(int64_t))
{
return put_integral(uint64_t(value), nbits);
}
//***************************************************************************
/// For 64bit integral types
//***************************************************************************
bool put(uint64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t))
{
return put_integral(value, nbits);
}
#endif
//***************************************************************************
/// For floating point types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_floating_point<T>::value, bool>::type
put(T value)
{
bool success = true;
unsigned char data[sizeof(T)];
to_bytes(value, data);
for (size_t i = 0UL; i < sizeof(T); ++i)
{
if (!put_integral(uint32_t(data[i]), CHAR_BIT))
{
success = false;
}
}
return success;
}
//***************************************************************************
/// For bool types
//***************************************************************************
bool get(bool& value)
{
bool success = false;
if (pdata != ETL_NULLPTR)
{
// Do we have enough bits?
if (bits_available > 0U)
{
value = get_bit();
success = true;
}
}
return success;
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, bool>::type
get(T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
bool success = false;
uint_least8_t bits = nbits;
if (pdata != ETL_NULLPTR)
{
// Do we have enough bits?
if (bits_available >= nbits)
{
value = 0;
// Get the bits from the stream.
while (nbits != 0)
{
unsigned char mask_width = static_cast<unsigned char>(etl::min(nbits, bits_available_in_char));
typedef typename etl::make_unsigned<T>::type chunk_t;
chunk_t chunk = get_chunk(mask_width);
nbits -= mask_width;
value |= static_cast<T>(chunk << nbits);
}
success = true;
}
}
// Sign extend if signed type and not already full bit width.
if (etl::is_signed<T>::value && (bits != (CHAR_BIT * sizeof(T))))
{
typedef typename etl::make_signed<T>::type ST;
value = etl::sign_extend<ST, ST>(value, bits);
}
return success;
}
//***************************************************************************
/// For floating point types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_floating_point<T>::value, bool>::type
get(T& value)
{
bool success = false;
if (pdata != ETL_NULLPTR)
{
uint_least8_t nbits = CHAR_BIT * sizeof(T);
// Do we have enough bits?
if (bits_available >= nbits)
{
// Temporary storage.
etl::uninitialized_buffer_of<T, 1U> data;
for (size_t i = 0UL; i < sizeof(T); ++i)
{
get(data.raw[i], CHAR_BIT);
}
from_bytes(reinterpret_cast<const unsigned char*>(data.raw), value);
success = true;
}
}
return success;
}
//***************************************************************************
/// Returns the number of bytes used in the stream.
//***************************************************************************
size_t size() const
{
size_t s = char_index;
// Current byte is used?
if (bits_available_in_char != CHAR_BIT)
{
++s;
}
return s;
}
//***************************************************************************
/// Returns the number of bits used in the stream.
//***************************************************************************
size_t bits() const
{
return (length_chars * CHAR_BIT) - bits_available;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pdata + size();
}
private:
//***************************************************************************
/// For unsigned integral types
//***************************************************************************
bool put_integral(uint32_t value, uint_least8_t nbits)
{
bool success = false;
if (pdata != ETL_NULLPTR)
{
// Do we have enough bits?
if (bits_available >= nbits)
{
// Send the bits to the stream.
while (nbits != 0)
{
unsigned char mask_width = static_cast<unsigned char>(etl::min(nbits, bits_available_in_char));
nbits -= mask_width;
uint32_t mask = ((1U << mask_width) - 1U) << nbits;
//uint32_t mask = ((uint32_t(1U) << mask_width) - 1U) << nbits;
// Move chunk to lowest char bits.
// Chunks are never larger than one char.
uint32_t chunk = ((value & mask) >> nbits) << (bits_available_in_char - mask_width);
put_chunk(static_cast<unsigned char>(chunk), mask_width);
}
success = true;
}
}
return success;
}
#if ETL_USING_64BIT_TYPES
//***************************************************************************
/// For unsigned integral types. 64bit
//***************************************************************************
bool put_integral(uint64_t value, uint_least8_t nbits)
{
bool success = false;
if (pdata != ETL_NULLPTR)
{
// Do we have enough bits?
if (bits_available >= nbits)
{
// Send the bits to the stream.
while (nbits != 0)
{
unsigned char mask_width = static_cast<unsigned char>(etl::min(nbits, bits_available_in_char));
nbits -= mask_width;
uint64_t mask = ((uint64_t(1U) << mask_width) - 1U) << nbits;
// Move chunk to lowest char bits.
// Chunks are never larger than one char.
uint64_t chunk = ((value & mask) >> nbits) << (bits_available_in_char - mask_width);
put_chunk(static_cast<unsigned char>(chunk), mask_width);
}
success = true;
}
}
return success;
}
#endif
//***************************************************************************
/// Put a data chunk to the stream
//***************************************************************************
void put_chunk(unsigned char chunk, unsigned char nbits)
{
// Clear if new byte.
if (bits_available_in_char == 8U)
{
pdata[char_index] = 0U;
}
pdata[char_index] |= chunk;
step(nbits);
}
//***************************************************************************
/// Get a data chunk from the stream
//***************************************************************************
unsigned char get_chunk(unsigned char nbits)
{
unsigned char value = pdata[char_index];
value >>= (bits_available_in_char - nbits);
unsigned char mask;
if (nbits == CHAR_BIT)
{
mask = etl::integral_limits<unsigned char>::max;
}
else
{
mask = (1U << nbits) - 1;
}
value &= mask;
step(nbits);
return value;
}
//***************************************************************************
/// Get a bool from the stream
//***************************************************************************
bool get_bit()
{
bool result = (pdata[char_index] & (1U << (bits_available_in_char - 1U))) != 0U;
step(1U);
return result;
}
//***************************************************************************
/// Helper function for floating point types
//***************************************************************************
template <typename T>
void from_bytes(const unsigned char* data, T& value)
{
etl::uninitialized_buffer_of<T, 1U> temp;
// Network to host.
if (etl::endianness::value() == etl::endian::little)
{
etl::reverse_copy(data, data + sizeof(T), temp.raw);
}
else
{
etl::copy(data, data + sizeof(T), temp.raw);
}
value = *reinterpret_cast<T*>(temp.raw);
}
//***************************************************************************
/// Helper function for floating point types
//***************************************************************************
template <typename T>
void to_bytes(T value, unsigned char* data)
{
unsigned char* pf = reinterpret_cast<unsigned char*>(&value);
// Host to network.
if (etl::endianness::value() == etl::endian::little)
{
etl::reverse_copy(pf, pf + sizeof(T), data);
}
else
{
etl::copy(pf, pf + sizeof(T), data);
}
}
//***************************************************************************
/// Step the specified number of bits along the stream.
/// The nbits will never be larger than 'bits_available_in_char'.
//***************************************************************************
void step(unsigned char nbits)
{
bits_available_in_char -= nbits;
if (bits_available_in_char == 0)
{
++char_index;
bits_available_in_char = 8;
}
bits_available -= nbits;
}
unsigned char *pdata; ///< The start of the bitstream buffer.
size_t length_chars; ///< The length, in char, of the bitstream buffer.
unsigned char bits_available_in_char; ///< The number of available bits in the current char.
size_t char_index; ///< The index of the char in the bitstream buffer.
size_t bits_available; ///< The number of bits still available in the bitstream buffer.
};
//***************************************************************************
/// Writes bits streams.
//***************************************************************************
class bit_stream_writer
{
public:
typedef char value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef etl::span<value_type> callback_parameter_type;
typedef etl::delegate<void(callback_parameter_type)> callback_type;
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_writer(const etl::span<char, Length>& span_, etl::endian stream_endianness_, callback_type callback_ = callback_type())
: pdata(span_.begin())
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
, callback(callback_)
{
restart();
}
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_writer(const etl::span<unsigned char, Length>& span_, etl::endian stream_endianness_, callback_type callback_ = callback_type())
: pdata(reinterpret_cast<char*>(span_.begin()))
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
, callback(callback_)
{
restart();
}
//***************************************************************************
/// Construct from range.
//***************************************************************************
bit_stream_writer(void* begin_, void* end_, etl::endian stream_endianness_, callback_type callback_ = callback_type())
: pdata(reinterpret_cast<char*>(begin_))
, length_chars(etl::distance(reinterpret_cast<unsigned char*>(begin_), reinterpret_cast<unsigned char*>(end_)))
, stream_endianness(stream_endianness_)
, callback(callback_)
{
restart();
}
//***************************************************************************
/// Construct from begin and length.
//***************************************************************************
bit_stream_writer(void* begin_, size_t length_chars_, etl::endian stream_endianness_, callback_type callback_ = callback_type())
: pdata(reinterpret_cast<char*>(begin_))
, length_chars(length_chars_)
, stream_endianness(stream_endianness_)
, callback(callback_)
{
restart();
}
//***************************************************************************
/// Sets the indexes back to the beginning of the stream.
//***************************************************************************
void restart()
{
bits_available_in_char = CHAR_BIT;
char_index = 0U;
bits_available = CHAR_BIT * length_chars;
}
//***************************************************************************
/// Returns the maximum capacity in bits.
//***************************************************************************
size_t capacity_bytes() const
{
return length_chars;
}
//***************************************************************************
size_t capacity_bits() const
{
return length_chars * CHAR_BIT;
}
//***************************************************************************
/// Returns <b>true</b> if the bitsteam indexes have been reset.
//***************************************************************************
bool empty() const
{
return (bits_available == length_chars);
}
//***************************************************************************
/// Returns <b>true</b> if the bitsteam indexes have reached the end.
//***************************************************************************
bool full() const
{
return (bits_available == 0U);
}
//***************************************************************************
/// Writes a boolean to the stream
//***************************************************************************
void write_unchecked(bool value)
{
unsigned char chunk = value ? 1 : 0;
write_data<unsigned char>(chunk, 1);
}
//***************************************************************************
/// Writes a boolean to the stream
//***************************************************************************
bool write(bool value)
{
bool success = (available<1U>() > 0U);
if (success)
{
write_unchecked(value);
}
return success;
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, void>::type
write_unchecked(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
typedef typename etl::unsigned_type<T>::type unsigned_t;
write_data<unsigned_t>(static_cast<unsigned_t>(value), nbits);
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, bool>::type
write(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
bool success = (available(nbits) > 0U);
if (success)
{
write_unchecked(value, nbits);
}
return success;
}
//***************************************************************************
/// Skip n bits, 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.
//***************************************************************************
bool skip(size_t nbits)
{
bool success = (nbits <= available_bits());
if (success)
{
while (nbits > bits_available_in_char)
{
step(bits_available_in_char);
nbits -= bits_available_in_char;
}
if (nbits != 0U)
{
step(static_cast<unsigned char>(nbits));
}
}
return success;
}
//***************************************************************************
/// Returns the number of bytes used in the stream.
//***************************************************************************
size_t size_bytes() const
{
size_t s = char_index;
// Is the current byte partially used?
if (bits_available_in_char != CHAR_BIT)
{
++s;
}
return s;
}
//***************************************************************************
/// Returns the number of bits used in the stream.
//***************************************************************************
size_t size_bits() const
{
return capacity_bits() - available_bits();
}
//***************************************************************************
/// The number of multiples of 'Nbits' available in the stream.
/// Compile time.
//***************************************************************************
template <size_t Nbits>
size_t available() const
{
return bits_available / Nbits;
}
//***************************************************************************
/// The number of T available in the stream.
/// Compile time.
//***************************************************************************
template <typename T>
size_t available() const
{
return available<CHAR_BIT * sizeof(T)>();
}
//***************************************************************************
/// The number of 'bit width' available in the stream.
/// Run time.
//***************************************************************************
size_t available(size_t nbits) const
{
return bits_available / nbits;
}
//***************************************************************************
/// The number of bits left in the stream.
//***************************************************************************
size_t available_bits() const
{
return bits_available;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
iterator begin()
{
return pdata;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator cbegin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
iterator end()
{
return pdata + size_bytes();
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pdata + size_bytes();
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator cend() const
{
return pdata + size_bytes();
}
//***************************************************************************
/// Returns a span of the used portion of the stream.
//***************************************************************************
etl::span<char> used_data()
{
return etl::span<char>(pdata, pdata + size_bytes());
}
//***************************************************************************
/// Returns a span of the used portion of the stream.
//***************************************************************************
etl::span<const char> used_data() const
{
return etl::span<const char>(pdata, pdata + size_bytes());
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<char> data()
{
return etl::span<char>(pdata, pdata + length_chars);
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<const char> data() const
{
return etl::span<const char>(pdata, pdata + length_chars);
}
//***************************************************************************
/// Flush the last byte, if partially filled, to the callback, if valid.
//***************************************************************************
void flush()
{
if (callback.is_valid())
{
if (bits_available_in_char != 0U)
{
char_index = 1U; // Indicate that the first char is actually 'full'.
flush_full_bytes();
}
restart();
}
}
//***************************************************************************
/// 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;
}
private:
//***************************************************************************
/// Write a value to the stream.
/// It will be passed one of five unsigned types.
//***************************************************************************
template <typename T>
void write_data(T value, uint_least8_t nbits)
{
// Make sure that we are not writing more bits than should be available.
nbits = (nbits > (CHAR_BIT * sizeof(T))) ? (CHAR_BIT * sizeof(T)) : nbits;
if (stream_endianness == etl::endian::little)
{
value = etl::reverse_bits(value);
value = value >> ((CHAR_BIT * sizeof(T)) - nbits);
}
// Send the bits to the stream.
while (nbits != 0)
{
unsigned char mask_width = static_cast<unsigned char>(etl::min(nbits, bits_available_in_char));
nbits -= mask_width;
T mask = ((T(1U) << mask_width) - 1U) << nbits;
// Move chunk to lowest char bits.
// Chunks are never larger than one char.
T chunk = ((value & mask) >> nbits) << (bits_available_in_char - mask_width);
write_chunk(static_cast<char>(chunk), mask_width);
}
if (callback.is_valid())
{
flush_full_bytes();
}
}
//***************************************************************************
/// Write a data chunk to the stream
//***************************************************************************
void write_chunk(char chunk, unsigned char nbits)
{
// Clear if new byte.
if (bits_available_in_char == CHAR_BIT)
{
pdata[char_index] = 0U;
}
pdata[char_index] |= chunk;
step(nbits);
}
//***************************************************************************
/// Flush full bytes to the callback, if valid.
/// Resets the stream to empty or the last unfilled byte.
//***************************************************************************
void flush_full_bytes()
{
// Is the first byte fully filled?
if (char_index > 0U)
{
callback(callback_parameter_type(pdata, pdata + char_index));
bits_available = CHAR_BIT * length_chars;
if (bits_available_in_char != 0U)
{
// Move a partially filled last byte to the start of the buffer.
pdata[0] = pdata[char_index];
bits_available -= (CHAR_BIT - bits_available_in_char);
}
char_index = 0U;
}
}
//***************************************************************************
/// Step the specified number of bits along the stream.
/// The width will never be larger than 'bits_available_in_char'.
//***************************************************************************
void step(unsigned char nbits)
{
bits_available_in_char -= nbits;
if (bits_available_in_char == 0)
{
++char_index;
bits_available_in_char = CHAR_BIT;
}
bits_available -= nbits;
}
char* const pdata; ///< The start of the bitstream buffer.
const size_t length_chars; ///< The length of the bitstream buffer.
const etl::endian stream_endianness; ///< The endianness of the stream data.
unsigned char bits_available_in_char; ///< The number of available bits in the current char.
size_t char_index; ///< The index of the current char in the bitstream buffer.
size_t bits_available; ///< The number of bits still available in the bitstream buffer.
callback_type callback; ///< An optional callback on every filled byte in buffer.
};
//***************************************************************************
/// Implementation of the write function.
/// For bool types only.
//***************************************************************************
inline void write_unchecked(etl::bit_stream_writer& stream, bool value)
{
stream.write_unchecked(value);
}
//***************************************************************************
/// Implementation of the write function.
/// For bool types only.
//***************************************************************************
inline bool write(etl::bit_stream_writer& stream, bool value)
{
return stream.write(value);
}
//***************************************************************************
/// Default implementation of the write function.
/// For integral types only (but not bool).
/// Overload this to support custom types.
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, void>::type
write_unchecked(etl::bit_stream_writer& stream, const T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
stream.write_unchecked(value, nbits);
}
//***************************************************************************
/// Default implementation of the write function.
/// For integral types only (but not bool).
/// Overload this to support custom types.
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value, bool>::type
write(etl::bit_stream_writer& stream, const T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
return stream.write(value, nbits);
}
//***************************************************************************
/// Reads bit streams
//***************************************************************************
class bit_stream_reader
{
public:
typedef char value_type;
typedef const char* const_iterator;
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_reader(const etl::span<char, Length>& span_, etl::endian stream_endianness_)
: pdata(span_.begin())
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_reader(const etl::span<unsigned char, Length>& span_, etl::endian stream_endianness_)
: pdata(reinterpret_cast<const char*>(span_.begin()))
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_reader(const etl::span<const char, Length>& span_, etl::endian stream_endianness_)
: pdata(span_.begin())
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Construct from span.
//***************************************************************************
template <size_t Length>
bit_stream_reader(const etl::span<const unsigned char, Length>& span_, etl::endian stream_endianness_)
: pdata(reinterpret_cast<const char*>(span_.begin()))
, length_chars(span_.size_bytes())
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Construct from range.
//***************************************************************************
bit_stream_reader(void* begin_, void* end_, etl::endian stream_endianness_)
: pdata(reinterpret_cast<const char*>(begin_))
, length_chars(etl::distance(reinterpret_cast<const char*>(begin_), reinterpret_cast<const char*>(end_)))
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Construct from begin and length.
//***************************************************************************
bit_stream_reader(void* begin_, size_t length_, etl::endian stream_endianness_)
: pdata(reinterpret_cast<const char*>(begin_))
, length_chars(length_)
, stream_endianness(stream_endianness_)
{
restart();
}
//***************************************************************************
/// Sets the indexes back to the beginning of the stream.
//***************************************************************************
void restart()
{
bits_available_in_char = CHAR_BIT;
char_index = 0U;
bits_available = CHAR_BIT * length_chars;
}
//***************************************************************************
/// For bool types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_same<bool, T>::value, bool>::type
read_unchecked()
{
return get_bit();
}
//***************************************************************************
/// For bool types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_same<bool, T>::value, etl::optional<bool> >::type
read()
{
etl::optional<bool> result;
if (bits_available > 0U)
{
result = read_unchecked<bool>();
}
return result;
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value && !etl::is_same<bool, T>::value, T>::type
read_unchecked(uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
typedef typename etl::unsigned_type<T>::type unsigned_t;
T value = read_value<unsigned_t>(nbits, etl::is_signed<T>::value);
return static_cast<T>(value);
}
//***************************************************************************
/// For integral types
//***************************************************************************
template <typename T>
typename etl::enable_if<etl::is_integral<T>::value && !etl::is_same<bool, T>::value, etl::optional<T> >::type
read(uint_least8_t nbits = CHAR_BIT * sizeof(T))
{
etl::optional<T> result;
// Do we have enough bits?
if (bits_available >= nbits)
{
result = read_unchecked<T>(nbits);
}
return result;
}
//***************************************************************************
/// Returns the number of bytes in the stream buffer.
//***************************************************************************
size_t size_bytes() const
{
return length_chars;
}
//***************************************************************************
/// Returns the number of bits in the stream buffer.
//***************************************************************************
size_t size_bits() const
{
return length_chars * CHAR_BIT;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator begin() const
{
return pdata;
}
//***************************************************************************
/// Returns start of the stream.
//***************************************************************************
const_iterator cbegin() const
{
return pdata;
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator end() const
{
return pdata + size_bytes();
}
//***************************************************************************
/// Returns end of the stream.
//***************************************************************************
const_iterator cend() const
{
return pdata + size_bytes();
}
//***************************************************************************
/// Returns a span of whole the stream.
//***************************************************************************
etl::span<const char> data() const
{
return etl::span<const char>(pdata, pdata + length_chars);
}
//***************************************************************************
/// Skip n bits, 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.
//***************************************************************************
bool skip(size_t nbits)
{
bool success = (nbits <= bits_available);
if (success)
{
while (nbits > bits_available_in_char)
{
nbits -= bits_available_in_char;
step(bits_available_in_char);
}
if (nbits != 0U)
{
step(static_cast<unsigned char>(nbits));
}
}
return success;
}
private:
//***************************************************************************
/// Read a value from the stream.
/// It will be passed one of five unsigned types.
//***************************************************************************
template <typename T>
T read_value(uint_least8_t nbits, bool is_signed)
{
// Make sure that we are not reading more bits than should be available.
nbits = (nbits > (CHAR_BIT * sizeof(T))) ? (CHAR_BIT * sizeof(T)) : nbits;
T value = 0;
uint_least8_t bits = nbits;
// Get the bits from the stream.
while (nbits != 0)
{
unsigned char mask_width = static_cast<unsigned char>(etl::min(nbits, bits_available_in_char));
T chunk = get_chunk(mask_width);
nbits -= mask_width;
value |= static_cast<T>(chunk << nbits);
}
if (stream_endianness == etl::endian::little)
{
value = value << ((CHAR_BIT * sizeof(T)) - bits);
value = etl::reverse_bits(value);
}
if (is_signed && (bits != (CHAR_BIT * sizeof(T))))
{
value = etl::sign_extend<T, T>(value, bits);
}
return value;
}
//***************************************************************************
/// Get a data chunk from the stream
//***************************************************************************
unsigned char get_chunk(unsigned char nbits)
{
unsigned char value = pdata[char_index];
value >>= (bits_available_in_char - nbits);
unsigned char mask;
if (nbits == CHAR_BIT)
{
mask = etl::integral_limits<unsigned char>::max;
}
else
{
mask = (1U << nbits) - 1;
}
value &= mask;
step(nbits);
return value;
}
//***************************************************************************
/// Get a bool from the stream
//***************************************************************************
bool get_bit()
{
bool result = (pdata[char_index] & (1U << (bits_available_in_char - 1U))) != 0U;
step(1U);
return result;
}
//***************************************************************************
/// Step the specified number of bits along the stream.
/// The nbits will never be larger than 'bits_available_in_char'.
//***************************************************************************
void step(unsigned char nbits)
{
bits_available_in_char -= nbits;
if (bits_available_in_char == 0)
{
++char_index;
bits_available_in_char = 8;
}
bits_available -= nbits;
}
const char* pdata; ///< The start of the bitstream buffer.
size_t length_chars; ///< The length, in char, of the bitstream buffer.
const etl::endian stream_endianness; ///< The endianness of the stream data.
unsigned char bits_available_in_char; ///< The number of available bits in the current char.
size_t char_index; ///< The index of the char in the bitstream buffer.
size_t bits_available; ///< The number of bits still available in the bitstream buffer.
};
//***************************************************************************
/// Read an unchecked type from a stream.
//***************************************************************************
template <typename T>
T read_unchecked(etl::bit_stream_reader& stream)
{
return stream.read_unchecked<T>();
}
template <typename T>
T read_unchecked(etl::bit_stream_reader& stream, uint_least8_t nbits)
{
return stream.read_unchecked<T>(nbits);
}
//***************************************************************************
/// Read a checked type from a stream.
//***************************************************************************
template <typename T>
etl::optional<T> read(etl::bit_stream_reader& stream)
{
return stream.read<T>();
}
template <typename T>
etl::optional<T> read(etl::bit_stream_reader& stream, uint_least8_t nbits)
{
return stream.read<T>(nbits);
}
//***************************************************************************
/// Read an unchecked bool from a stream.
//***************************************************************************
template <>
inline bool read_unchecked<bool>(etl::bit_stream_reader& stream)
{
return stream.read_unchecked<bool>();
}
//***************************************************************************
/// Read a bool from a stream.
//***************************************************************************
template <>
inline etl::optional<bool> read<bool>(etl::bit_stream_reader& stream)
{
return stream.read<bool>();
}
}
#include "private/minmax_pop.h"
#endif