mirror of
https://github.com/ETLCPP/etl.git
synced 2026-06-27 21:08:44 +08:00
558 lines
17 KiB
C++
558 lines
17 KiB
C++
///\file
|
|
|
|
/******************************************************************************
|
|
The MIT License(MIT)
|
|
Embedded Template Library.
|
|
https://github.com/ETLCPP/etl
|
|
https://www.etlcpp.com
|
|
Copyright(c) 2018 jwellbelove
|
|
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 <stdint.h>
|
|
#include <limits.h>
|
|
|
|
#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 "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(0U)
|
|
{
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from range.
|
|
//***************************************************************************
|
|
bit_stream(char* begin_, char* end_)
|
|
: pdata(reinterpret_cast<unsigned char*>(begin_)),
|
|
length(etl::distance(begin_, end_))
|
|
{
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from range.
|
|
//***************************************************************************
|
|
bit_stream(unsigned char* begin_, unsigned char* end_)
|
|
: pdata(begin_),
|
|
length(etl::distance(begin_, end_))
|
|
{
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from begin and length.
|
|
//***************************************************************************
|
|
bit_stream(char* begin_, size_t length_)
|
|
: pdata(reinterpret_cast<unsigned char*>(begin_)),
|
|
length(length_)
|
|
{
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from begin and length.
|
|
//***************************************************************************
|
|
bit_stream(unsigned char* begin_, size_t length_)
|
|
: pdata(begin_),
|
|
length(length_)
|
|
{
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from begin and length.
|
|
//***************************************************************************
|
|
void set_stream(char* begin_, size_t length_)
|
|
{
|
|
pdata = reinterpret_cast<unsigned char*>(begin_);
|
|
length = length_;
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from begin and length.
|
|
//***************************************************************************
|
|
void set_stream(unsigned char* begin_, size_t length_)
|
|
{
|
|
pdata = begin_;
|
|
length = length_;
|
|
restart();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from range.
|
|
//***************************************************************************
|
|
void set_stream(char* begin_, char* end_)
|
|
{
|
|
set_stream(begin_, etl::distance(begin_, end_));
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Construct from range.
|
|
//***************************************************************************
|
|
void set_stream(unsigned char* begin_, unsigned char* end_)
|
|
{
|
|
set_stream(begin_, etl::distance(begin_, end_));
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Sets the indexes back to the beginning of the stream.
|
|
//***************************************************************************
|
|
void restart()
|
|
{
|
|
bits_in_byte = 8;
|
|
byte_index = 0U;
|
|
bits_remaining = CHAR_BIT * length;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Returns <b>true</b> if the bitsteam indexes have reached the end.
|
|
//***************************************************************************
|
|
bool at_end() const
|
|
{
|
|
return (bits_remaining == 0U);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Puts a boolean to the stream
|
|
//***************************************************************************
|
|
bool put(bool value)
|
|
{
|
|
bool success = false;
|
|
|
|
if (pdata != ETL_NULLPTR)
|
|
{
|
|
if (bits_remaining > 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 width = CHAR_BIT * sizeof(T))
|
|
{
|
|
return put_integral(static_cast<uint32_t>(value), width);
|
|
}
|
|
|
|
#if ETL_USING_64BIT_TYPES
|
|
//***************************************************************************
|
|
/// For 64bit integral types
|
|
//***************************************************************************
|
|
bool put(int64_t value, uint_least8_t width = CHAR_BIT * sizeof(int64_t))
|
|
{
|
|
return put_integral(uint64_t(value), width);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// For 64bit integral types
|
|
//***************************************************************************
|
|
bool put(uint64_t value, uint_least8_t width = CHAR_BIT * sizeof(uint64_t))
|
|
{
|
|
return put_integral(value, width);
|
|
}
|
|
#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 = 0; 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_remaining > 0)
|
|
{
|
|
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 width = CHAR_BIT * sizeof(T))
|
|
{
|
|
bool success = false;
|
|
uint_least8_t bits = width;
|
|
|
|
if (pdata != ETL_NULLPTR)
|
|
{
|
|
// Do we have enough bits?
|
|
if (bits_remaining >= width)
|
|
{
|
|
value = 0;
|
|
|
|
// Get the bits from the stream.
|
|
while (width != 0)
|
|
{
|
|
unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
|
|
unsigned char chunk = get_chunk(mask_width);
|
|
|
|
width -= mask_width;
|
|
value |= static_cast<T>(chunk) << width;
|
|
}
|
|
|
|
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 width = CHAR_BIT * sizeof(T);
|
|
|
|
// Do we have enough bits?
|
|
if (bits_remaining >= width)
|
|
{
|
|
// Temporary storage.
|
|
unsigned char data[sizeof(T)];
|
|
|
|
for (size_t i = 0; i < sizeof(T); ++i)
|
|
{
|
|
get(data[i], CHAR_BIT);
|
|
}
|
|
|
|
from_bytes(data, value);
|
|
|
|
bits_remaining -= width;
|
|
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Returns the number of bytes used in the stream.
|
|
//***************************************************************************
|
|
size_t size() const
|
|
{
|
|
size_t s = byte_index;
|
|
|
|
// Current byte is used?
|
|
if (bits_in_byte != CHAR_BIT)
|
|
{
|
|
++s;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Returns the number of bits used in the stream.
|
|
//***************************************************************************
|
|
size_t bits() const
|
|
{
|
|
return (length * CHAR_BIT) - bits_remaining;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// 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 width)
|
|
{
|
|
bool success = false;
|
|
|
|
if (pdata != ETL_NULLPTR)
|
|
{
|
|
// Do we have enough bits?
|
|
if (bits_remaining >= width)
|
|
{
|
|
// Send the bits to the stream.
|
|
while (width != 0)
|
|
{
|
|
unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
|
|
width -= mask_width;
|
|
uint32_t mask = ((uint32_t(1U) << mask_width) - 1U) << width;
|
|
|
|
// Move chunk to lowest char bits.
|
|
// Chunks are never larger than one char.
|
|
uint32_t chunk = ((value & mask) >> width) << (bits_in_byte - 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 width)
|
|
{
|
|
bool success = false;
|
|
|
|
if (pdata != ETL_NULLPTR)
|
|
{
|
|
// Do we have enough bits?
|
|
if (bits_remaining >= width)
|
|
{
|
|
// Send the bits to the stream.
|
|
while (width != 0)
|
|
{
|
|
unsigned char mask_width = static_cast<unsigned char>(etl::min(width, bits_in_byte));
|
|
width -= mask_width;
|
|
uint64_t mask = ((uint64_t(1U) << mask_width) - 1U) << width;
|
|
|
|
// Move chunk to lowest char bits.
|
|
// Chunks are never larger than one char.
|
|
uint64_t chunk = ((value & mask) >> width) << (bits_in_byte - 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 width)
|
|
{
|
|
// Clear if new byte.
|
|
if (bits_in_byte == 8)
|
|
{
|
|
pdata[byte_index] = 0U;
|
|
}
|
|
|
|
pdata[byte_index] |= chunk;
|
|
step(width);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Get a data chunk from the stream
|
|
//***************************************************************************
|
|
unsigned char get_chunk(unsigned char width)
|
|
{
|
|
unsigned char value = pdata[byte_index];
|
|
|
|
value >>= (bits_in_byte - width);
|
|
|
|
unsigned char mask;
|
|
|
|
if (width == CHAR_BIT)
|
|
{
|
|
mask = etl::integral_limits<unsigned char>::max;
|
|
}
|
|
else
|
|
{
|
|
mask = (1U << width) - 1;
|
|
}
|
|
|
|
value &= mask;
|
|
|
|
step(width);
|
|
|
|
return value;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Get a bool from the stream
|
|
//***************************************************************************
|
|
bool get_bit()
|
|
{
|
|
bool result = (pdata[byte_index] & (1 << (bits_in_byte - 1))) != 0;
|
|
|
|
step(1U);
|
|
|
|
return result;
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Helper function for floating point types
|
|
//***************************************************************************
|
|
template <typename T>
|
|
void from_bytes(const unsigned char* data, T& value)
|
|
{
|
|
unsigned char temp[sizeof(T)];
|
|
|
|
// Network to host.
|
|
if (etl::endianness::value() == etl::endian::little)
|
|
{
|
|
etl::reverse_copy(data, data + sizeof(T), temp);
|
|
}
|
|
else
|
|
{
|
|
etl::copy(data, data + sizeof(T), temp);
|
|
}
|
|
|
|
value = *reinterpret_cast<T*>(temp);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// 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 width will never be larger than 'bits_in_byte'.
|
|
//***************************************************************************
|
|
void step(unsigned char width)
|
|
{
|
|
bits_in_byte -= width;
|
|
|
|
if (bits_in_byte == 0)
|
|
{
|
|
++byte_index;
|
|
bits_in_byte = 8;
|
|
}
|
|
|
|
bits_remaining -= width;
|
|
}
|
|
|
|
unsigned char *pdata; ///< The start of the bitstream buffer.
|
|
size_t length; ///< The length, in unsigned char, of the bitstream buffer.
|
|
unsigned char bits_in_byte; ///< The number of available bits in the current char.
|
|
size_t byte_index; ///< The index of the char in the bitstream buffer.
|
|
size_t bits_remaining; ///< The number of bits still available in the bitstream buffer.
|
|
};
|
|
}
|
|
|
|
#include "private/minmax_pop.h"
|
|
|
|
#endif
|