diff --git a/include/etl/bit_stream.h b/include/etl/bit_stream.h index 2894d1cf..bcf732a4 100644 --- a/include/etl/bit_stream.h +++ b/include/etl/bit_stream.h @@ -38,6 +38,9 @@ SOFTWARE. #include "algorithm.h" #include "iterator.h" #include "memory.h" +#include "delegate.h" +#include "span.h" +#include "endianness.h" #include "private/minmax_push.h" @@ -58,7 +61,7 @@ namespace etl //*************************************************************************** bit_stream() : pdata(ETL_NULLPTR), - length(0U) + length_chars(0U) { restart(); } @@ -68,7 +71,7 @@ namespace etl //*************************************************************************** bit_stream(void* begin_, void* end_) : pdata(reinterpret_cast(begin_)), - length(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) + length_chars(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) { restart(); } @@ -78,7 +81,7 @@ namespace etl //*************************************************************************** bit_stream(void* begin_, size_t length_) : pdata(reinterpret_cast(begin_)), - length(length_) + length_chars(length_) { restart(); } @@ -89,7 +92,7 @@ namespace etl void set_stream(void* begin_, size_t length_) { pdata = reinterpret_cast(begin_); - length = length_; + length_chars = length_; restart(); } @@ -106,9 +109,9 @@ namespace etl //*************************************************************************** void restart() { - bits_in_byte = 8; - byte_index = 0U; - bits_remaining = CHAR_BIT * length; + bits_available_in_char = 8; + char_index = 0U; + bits_remaining = CHAR_BIT * length_chars; } //*************************************************************************** @@ -120,7 +123,7 @@ namespace etl } //*************************************************************************** - /// Puts a boolean to the stream + /// Writes a boolean to the stream //*************************************************************************** bool put(bool value) { @@ -144,26 +147,26 @@ namespace etl //*************************************************************************** template typename etl::enable_if::value, bool>::type - put(T value, uint_least8_t width = CHAR_BIT * sizeof(T)) + put(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) { - return put_integral(static_cast(value), width); + return put_integral(static_cast(value), nbits); } #if ETL_USING_64BIT_TYPES //*************************************************************************** /// For 64bit integral types //*************************************************************************** - bool put(int64_t value, uint_least8_t width = CHAR_BIT * sizeof(int64_t)) + bool put(int64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(int64_t)) { - return put_integral(uint64_t(value), width); + return put_integral(uint64_t(value), nbits); } //*************************************************************************** /// For 64bit integral types //*************************************************************************** - bool put(uint64_t value, uint_least8_t width = CHAR_BIT * sizeof(uint64_t)) + bool put(uint64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t)) { - return put_integral(value, width); + return put_integral(value, nbits); } #endif @@ -215,28 +218,28 @@ namespace etl //*************************************************************************** template typename etl::enable_if::value, bool>::type - get(T& value, uint_least8_t width = CHAR_BIT * sizeof(T)) + get(T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) { bool success = false; - uint_least8_t bits = width; + uint_least8_t bits = nbits; if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= width) + if (bits_remaining >= nbits) { value = 0; // Get the bits from the stream. - while (width != 0) + while (nbits != 0) { - unsigned char mask_width = static_cast(etl::min(width, bits_in_byte)); + unsigned char mask_width = static_cast(etl::min(nbits, bits_available_in_char)); typedef typename etl::make_unsigned::type chunk_t; chunk_t chunk = get_chunk(mask_width); - width -= mask_width; - value |= static_cast(chunk << width); + nbits -= mask_width; + value |= static_cast(chunk << nbits); } success = true; @@ -264,10 +267,10 @@ namespace etl if (pdata != ETL_NULLPTR) { - uint_least8_t width = CHAR_BIT * sizeof(T); + uint_least8_t nbits = CHAR_BIT * sizeof(T); // Do we have enough bits? - if (bits_remaining >= width) + if (bits_remaining >= nbits) { // Temporary storage. etl::uninitialized_buffer_of data; @@ -291,10 +294,10 @@ namespace etl //*************************************************************************** size_t size() const { - size_t s = byte_index; + size_t s = char_index; // Current byte is used? - if (bits_in_byte != CHAR_BIT) + if (bits_available_in_char != CHAR_BIT) { ++s; } @@ -307,7 +310,7 @@ namespace etl //*************************************************************************** size_t bits() const { - return (length * CHAR_BIT) - bits_remaining; + return (length_chars * CHAR_BIT) - bits_remaining; } //*************************************************************************** @@ -331,25 +334,25 @@ namespace etl //*************************************************************************** /// For unsigned integral types //*************************************************************************** - bool put_integral(uint32_t value, uint_least8_t width) + bool put_integral(uint32_t value, uint_least8_t nbits) { bool success = false; if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= width) + if (bits_remaining >= nbits) { // Send the bits to the stream. - while (width != 0) + while (nbits != 0) { - unsigned char mask_width = static_cast(etl::min(width, bits_in_byte)); - width -= mask_width; - uint32_t mask = ((uint32_t(1U) << mask_width) - 1U) << width; + unsigned char mask_width = static_cast(etl::min(nbits, bits_available_in_char)); + nbits -= mask_width; + 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) >> width) << (bits_in_byte - mask_width); + uint32_t chunk = ((value & mask) >> nbits) << (bits_available_in_char - mask_width); put_chunk(static_cast(chunk), mask_width); } @@ -365,25 +368,25 @@ namespace etl //*************************************************************************** /// For unsigned integral types. 64bit //*************************************************************************** - bool put_integral(uint64_t value, uint_least8_t width) + bool put_integral(uint64_t value, uint_least8_t nbits) { bool success = false; if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= width) + if (bits_remaining >= nbits) { // Send the bits to the stream. - while (width != 0) + while (nbits != 0) { - unsigned char mask_width = static_cast(etl::min(width, bits_in_byte)); - width -= mask_width; - uint64_t mask = ((uint64_t(1U) << mask_width) - 1U) << width; + unsigned char mask_width = static_cast(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) >> width) << (bits_in_byte - mask_width); + uint64_t chunk = ((value & mask) >> nbits) << (bits_available_in_char - mask_width); put_chunk(static_cast(chunk), mask_width); } @@ -399,41 +402,41 @@ namespace etl //*************************************************************************** /// Put a data chunk to the stream //*************************************************************************** - void put_chunk(unsigned char chunk, unsigned char width) + void put_chunk(unsigned char chunk, unsigned char nbits) { // Clear if new byte. - if (bits_in_byte == 8) + if (bits_available_in_char == 8U) { - pdata[byte_index] = 0U; + pdata[char_index] = 0U; } - pdata[byte_index] |= chunk; - step(width); + pdata[char_index] |= chunk; + step(nbits); } //*************************************************************************** /// Get a data chunk from the stream //*************************************************************************** - unsigned char get_chunk(unsigned char width) + unsigned char get_chunk(unsigned char nbits) { - unsigned char value = pdata[byte_index]; + unsigned char value = pdata[char_index]; - value >>= (bits_in_byte - width); + value >>= (bits_available_in_char - nbits); unsigned char mask; - if (width == CHAR_BIT) + if (nbits == CHAR_BIT) { mask = etl::integral_limits::max; } else { - mask = (1U << width) - 1; + mask = (1U << nbits) - 1; } value &= mask; - step(width); + step(nbits); return value; } @@ -443,7 +446,7 @@ namespace etl //*************************************************************************** bool get_bit() { - bool result = (pdata[byte_index] & (1U << (bits_in_byte - 1U))) != 0U; + bool result = (pdata[char_index] & (1U << (bits_available_in_char - 1U))) != 0U; step(1U); @@ -492,27 +495,547 @@ namespace etl //*************************************************************************** /// Step the specified number of bits along the stream. - /// The width will never be larger than 'bits_in_byte'. + /// The nbits will never be larger than 'bits_available_in_char'. //*************************************************************************** - void step(unsigned char width) + void step(unsigned char nbits) { - bits_in_byte -= width; + bits_available_in_char -= nbits; - if (bits_in_byte == 0) + if (bits_available_in_char == 0) { - ++byte_index; - bits_in_byte = 8; + ++char_index; + bits_available_in_char = 8; } - bits_remaining -= width; + bits_remaining -= nbits; } 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 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_remaining; ///< The number of bits still available in the bitstream buffer. }; + + //*************************************************************************** + /// Writes bits streams. + //*************************************************************************** + class bit_stream_writer + { + public: + + typedef const unsigned char* const_iterator; + typedef char callback_parameter_type; + typedef etl::delegate callback_type; + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + bit_stream_writer(etl::span span_, callback_type callback_ = callback_type()) + : pdata(reinterpret_cast(span_.begin())) + , length_chars(span_.size_bytes()) + , callback(callback_) + { + restart(); + } + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + bit_stream_writer(etl::span span_, callback_type callback_ = callback_type()) + : pdata(span_.begin()) + , length_chars(span_.size_bytes()) + , callback(callback_) + { + restart(); + } + + //*************************************************************************** + /// Construct from range. + //*************************************************************************** + bit_stream_writer(void* begin_, void* end_, callback_type callback_ = callback_type()) + : pdata(reinterpret_cast(begin_)) + , length_chars(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) + , callback(callback_) + { + restart(); + } + + //*************************************************************************** + /// Construct from begin and length. + //*************************************************************************** + bit_stream_writer(void* begin_, size_t length_chars_, callback_type callback_ = callback_type()) + : pdata(reinterpret_cast(begin_)) + , length_chars(length_chars_) + , callback(callback_) + { + restart(); + } + + //*************************************************************************** + /// Sets the indexes back to the beginning of the stream. + //*************************************************************************** + void restart() + { + bits_available_in_char = 8; + char_index = 0U; + bits_remaining = 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 true if the bitsteam indexes have been reset. + //*************************************************************************** + bool empty() const + { + return (bits_remaining == length_chars); + } + + //*************************************************************************** + /// Returns true if the bitsteam indexes have reached the end. + //*************************************************************************** + bool full() const + { + return (bits_remaining == 0U); + } + + //*************************************************************************** + /// Writes a boolean to the stream + //*************************************************************************** + void write_unchecked(bool value) + { + unsigned char chunk = value ? 1 : 0; + write_integral(uint32_t(chunk), 1); + } + + //*************************************************************************** + /// Writes a boolean to the stream + //*************************************************************************** + bool write(bool value) + { + bool success = false; + + if (available<1U>() > 0U) + { + write_unchecked(value); + success = true; + } + + return success; + } + + //*************************************************************************** + /// For integral types + //*************************************************************************** + template + typename etl::enable_if::value, void>::type + write_unchecked(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + write_integral(static_cast(value), nbits); + } + + //*************************************************************************** + /// For integral types + //*************************************************************************** + template + typename etl::enable_if::value, bool>::type + write(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + bool success = false; + + if (available(nbits) > 0U) + { + write_unchecked(value, nbits); + success = true; + } + + return success; + } + + #if ETL_USING_64BIT_TYPES + //*************************************************************************** + /// For signed 64bit integral types + //*************************************************************************** + void write_unchecked(int64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(int64_t)) + { + write_integral(uint64_t(value), nbits); + } + + //*************************************************************************** + /// For signed 64bit integral types + //*************************************************************************** + bool write(int64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t)) + { + bool success = false; + + if (available(nbits) > 0U) + { + write_unchecked(value, nbits); + success = true; + } + + return success; + } + + //*************************************************************************** + /// For unsigned 64bit integral types + //*************************************************************************** + void write_unchecked(uint64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(int64_t)) + { + write_integral(uint64_t(value), nbits); + } + + //*************************************************************************** + /// For unsigned 64bit integral types + //*************************************************************************** + bool write(uint64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t)) + { + bool success = false; + + if (available(nbits) > 0U) + { + write_unchecked(value, nbits); + success = true; + } + + return success; + } + #endif + + //*************************************************************************** + /// For floating point types + //*************************************************************************** + template + typename etl::enable_if::value, void>::type + write_unchecked(T value) + { + unsigned char data[sizeof(T)]; + to_bytes(value, data); + + for (size_t i = 0UL; i < sizeof(T); ++i) + { + write_integral(uint32_t(data[i]), CHAR_BIT); + } + } + + //*************************************************************************** + /// For floating point types + //*************************************************************************** + template + typename etl::enable_if::value, bool>::type + write(T value) + { + bool success = false; + + if (available()) + { + write_unchecked(value); + success = true; + } + + return success; + } + + //*************************************************************************** + /// Skip n bits, up to the maximum space available. + /// Returns true if the skip was possible. + /// Returns false if the full skip size was not possible. + //*************************************************************************** + bool skip(size_t nbits) + { + bool success = true; + + if (nbits > available_bits()) + { + nbits = available_bits(); + success = false; + } + + while (nbits > bits_available_in_char) + { + step(bits_available_in_char); + nbits -= bits_available_in_char; + } + + if (nbits != 0U) + { + step(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 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 'bit nbits' available in the stream. + /// Compile time. + //*************************************************************************** + template + size_t available() const + { + return bits_remaining / Nbits; + } + + //*************************************************************************** + /// The number of T available in the stream. + /// Compile time. + //*************************************************************************** + template + size_t available() const + { + return available(); + } + + //*************************************************************************** + /// The number of 'bit width' available in the stream. + /// Run time. + //*************************************************************************** + size_t available(size_t nbits) const + { + return bits_remaining / nbits; + } + + //*************************************************************************** + /// The number of bits left in the stream. + //*************************************************************************** + size_t available_bits() const + { + return bits_remaining; + } + + //*************************************************************************** + /// 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 the used portion of the stream. + //*************************************************************************** + etl::span used_data() const + { + return etl::span(reinterpret_cast(pdata), reinterpret_cast(pdata) + size_bytes()); + } + + //*************************************************************************** + /// Returns a span of whole the stream. + //*************************************************************************** + etl::span data() const + { + return etl::span(reinterpret_cast(pdata), reinterpret_cast(pdata) + length_chars); + } + + //*************************************************************************** + /// Finalizes the stream and calls the callback, if set. + //*************************************************************************** + void finalize() + { + if (bits_remaining != 0U) + { + callback.call_if(pdata[char_index]); + } + } + + //*************************************************************************** + /// Sets the function to call afer every write. + //*************************************************************************** + void set_callback(callback_type callback_) + { + callback = callback_; + } + + //*************************************************************************** + /// Gets the function to call afer every write. + //*************************************************************************** + callback_type get_callback() const + { + return callback; + } + + private: + + //*************************************************************************** + /// For unsigned integral types + //*************************************************************************** + void write_integral(uint32_t value, uint_least8_t nbits) + { + // Send the bits to the stream. + while (nbits != 0) + { + unsigned char mask_width = static_cast(etl::min(nbits, bits_available_in_char)); + nbits -= mask_width; + 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); + + write_chunk(static_cast(chunk), mask_width); + } + } + + #if ETL_USING_64BIT_TYPES + //*************************************************************************** + /// For unsigned integral types. 64bit + //*************************************************************************** + void write_integral(uint64_t value, uint_least8_t nbits) + { + // Send the bits to the stream. + while (nbits != 0) + { + unsigned char mask_width = static_cast(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); + + write_chunk(static_cast(chunk), mask_width); + } + } + #endif + + //*************************************************************************** + /// Write a data chunk to the stream + //*************************************************************************** + void write_chunk(unsigned 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); + } + + //*************************************************************************** + /// Convert to bytes. + //*************************************************************************** + template + void to_bytes(T value, unsigned char* data) + { + unsigned char* pf = reinterpret_cast(&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_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_remaining -= nbits; + } + + unsigned char* const pdata; ///< The start of the bitstream buffer. + const size_t length_chars; ///< The length, in unsigned 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_remaining; ///< The number of bits still available in the bitstream buffer. + callback_type callback; ///< An optional callback on every filled byte on the write buffer. + }; + + //*************************************************************************** + /// Default implementation of the write function. + /// For integral and floating point types only. + /// Overload this to support custom types. + //*************************************************************************** + template + void write_unchecked(etl::bit_stream_writer& stream, const T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + stream.write_unchecked(value, nbits); + } + + //*************************************************************************** + /// Implementation of the write function. + //*************************************************************************** + template + bool write(etl::bit_stream_writer& stream, const T& value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + return stream.write(value, nbits); + } } #include "private/minmax_pop.h" diff --git a/include/etl/byte_stream.h b/include/etl/byte_stream.h index c63bcfa7..2b6592c0 100644 --- a/include/etl/byte_stream.h +++ b/include/etl/byte_stream.h @@ -72,6 +72,18 @@ namespace etl { } + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + byte_stream_writer(etl::span span_, etl::endian buffer_endianness_, callback_type callback_ = callback_type()) + : pdata(reinterpret_cast(span_.begin())) + , pcurrent(reinterpret_cast(span_.begin())) + , length(span_.size_bytes()) + , buffer_endianness(buffer_endianness_) + , callback(callback_) + { + } + //*************************************************************************** /// Construct from range. //*************************************************************************** @@ -370,6 +382,22 @@ namespace etl callback = callback_; } + //*************************************************************************** + /// Gets the function to call afer every write. + //*************************************************************************** + callback_type get_callback() const + { + return callback; + } + + //*************************************************************************** + /// Gets the endianness of the stream. + //*************************************************************************** + etl::endian get_endianness() const + { + return buffer_endianness; + } + private: //*************************************************************************** diff --git a/test/test_bit_stream_writer.cpp b/test/test_bit_stream_writer.cpp new file mode 100644 index 00000000..bff30452 --- /dev/null +++ b/test/test_bit_stream_writer.cpp @@ -0,0 +1,746 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2022 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. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/bit_stream.h" + +#include +#include + +namespace +{ + //*********************************** + struct Object + { + int16_t i; + double d; + uint8_t c; + }; + + bool operator ==(const Object& lhs, const Object& rhs) + { + return (lhs.i == rhs.i) && + (lhs.d == rhs.d) && + (lhs.c == rhs.c); + } + + std::ostream& operator << (std::ostream& os, const Object& object) + { + os << object.i << "," << object.d << "," << (int)object.c; + return os; + } +} + +namespace etl +{ + //*********************************** + bool write(etl::bit_stream_writer& stream, const Object& object) + { + bool success = true; + + if (!stream.write(object.i, 14)) + { + success = false; + } + + if (!stream.write(object.d)) + { + success = false; + } + + if (!stream.write(object.c)) + { + success = false; + } + + return success; + } +} + +namespace +{ + SUITE(test_bit_stream) + { + //************************************************************************* + TEST(test_bit_stream_writer_construction) + { + std::array storage; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK_EQUAL(storage.size(), bit_stream.available(CHAR_BIT)); + CHECK_EQUAL(storage.size(), bit_stream.available()); + CHECK_EQUAL(storage.size(), bit_stream.available()); + + CHECK_EQUAL(storage.size() * CHAR_BIT, bit_stream.available_bits()); + + CHECK_EQUAL(0U, bit_stream.size_bits()); + CHECK_EQUAL(0U, bit_stream.size_bytes()); + + CHECK_EQUAL(storage.size(), bit_stream.data().size()); + CHECK_EQUAL(0U, bit_stream.used_data().size()); + } + + //************************************************************************* + TEST(test_write_bool) + { + unsigned char storage = 0; + unsigned char expected_data = 0x5AU; + + etl::bit_stream_writer bit_stream(&storage, 1U); + + CHECK(bit_stream.write(false)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(true)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(false)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(true)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(true)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(false)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(true)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + CHECK(bit_stream.write(false)); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(true)); + + CHECK_EQUAL(1U, bit_stream.data().size()); + CHECK_EQUAL(1U, bit_stream.used_data().size()); + + CHECK_EQUAL(int(expected_data), int(storage)); + } + + //************************************************************************* + TEST(test_write_int8_t) + { + std::array storage; + + std::array expected_data; + std::iota(expected_data.begin(), expected_data.end(), 0); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + for (size_t i = 0UL; i < 256UL; ++i) + { + CHECK(bit_stream.write(int8_t(i))); + CHECK_EQUAL(i + 1U, bit_stream.used_data().size()); + } + + // One too many. + CHECK(!bit_stream.write(int8_t(0))); + + CHECK_EQUAL(256U, bit_stream.data().size()); + CHECK_EQUAL(256U, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_uint8_t) + { + std::array storage; + + std::array expected_data; + std::iota(expected_data.begin(), expected_data.end(), 0); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + for (size_t i = 0UL; i < 256UL; ++i) + { + CHECK(bit_stream.write(uint8_t(i))); + CHECK_EQUAL(i + 1U, bit_stream.used_data().size()); + } + + // One too many. + CHECK(!bit_stream.write(int8_t(0))); + + CHECK_EQUAL(256U, bit_stream.data().size()); + CHECK_EQUAL(256U, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_int16_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x01), + char(0x5A), char(0xA5), + char(0xA5), char(0x5A), + char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(int16_t(0x0001))); + CHECK_EQUAL(sizeof(int16_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(int16_t(0x5AA5))); + CHECK_EQUAL(sizeof(int16_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(int16_t(0xA55A))); + CHECK_EQUAL(sizeof(int16_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(int16_t(0xFFFF))); + CHECK_EQUAL(sizeof(int16_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(int16_t(0))); + + CHECK_EQUAL(sizeof(int16_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(int16_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_uint16_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x01), + char(0x5A), char(0xA5), + char(0xA5), char(0x5A), + char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(uint16_t(0x0001))); + CHECK_EQUAL(sizeof(uint16_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint16_t(0x5AA5))); + CHECK_EQUAL(sizeof(uint16_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint16_t(0xA55A))); + CHECK_EQUAL(sizeof(uint16_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint16_t(0xFFFF))); + CHECK_EQUAL(sizeof(uint16_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(uint16_t(0))); + + CHECK_EQUAL(sizeof(uint16_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(uint16_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_int32_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x01), + char(0x5A), char(0xA5), char(0xA5), char(0x5A), + char(0xA5), char(0x5A), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(int32_t(0x00000001))); + CHECK_EQUAL(sizeof(int32_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(int32_t(0x5AA5A55A))); + CHECK_EQUAL(sizeof(int32_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(int32_t(0xA55A5AA5))); + CHECK_EQUAL(sizeof(int32_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(int32_t(0xFFFFFFFF))); + CHECK_EQUAL(sizeof(int32_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(int32_t(0))); + + CHECK_EQUAL(sizeof(int32_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(int32_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_uint32_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x01), + char(0x5A), char(0xA5), char(0xA5), char(0x5A), + char(0xA5), char(0x5A), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(uint32_t(0x00000001))); + CHECK_EQUAL(sizeof(uint32_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint32_t(0x5AA5A55A))); + CHECK_EQUAL(sizeof(uint32_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint32_t(0xA55A5AA5))); + CHECK_EQUAL(sizeof(uint32_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint32_t(0xFFFFFFFF))); + CHECK_EQUAL(sizeof(uint32_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(uint32_t(0))); + + CHECK_EQUAL(sizeof(uint32_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(uint32_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_int64_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x01), + char(0x5A), char(0xA5), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0x5A), char(0xA5), + char(0xA5), char(0x5A), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0xA5), char(0x5A), + char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(int64_t(0x0000000000000001LL))); + CHECK_EQUAL(sizeof(int64_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(int64_t(0x5AA5A55AA55A5AA5LL))); + CHECK_EQUAL(sizeof(int64_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(int64_t(0xA55A5AA55AA5A55ALL))); + CHECK_EQUAL(sizeof(int64_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(int64_t(0xFFFFFFFFFFFFFFFFLL))); + CHECK_EQUAL(sizeof(int64_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(int64_t(0))); + + CHECK_EQUAL(sizeof(int64_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(int64_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_uint64_t) + { + // Tests assume big endian. + std::array storage; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x01), + char(0x5A), char(0xA5), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0x5A), char(0xA5), + char(0xA5), char(0x5A), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0xA5), char(0x5A), + char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + + CHECK(expected_data.size() == storage.size()); + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + CHECK(bit_stream.write(uint64_t(0x0000000000000001LL))); + CHECK_EQUAL(sizeof(uint64_t) * 1, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint64_t(0x5AA5A55AA55A5AA5LL))); + CHECK_EQUAL(sizeof(uint64_t) * 2, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint64_t(0xA55A5AA55AA5A55ALL))); + CHECK_EQUAL(sizeof(uint64_t) * 3, bit_stream.used_data().size()); + CHECK(bit_stream.write(uint64_t(0xFFFFFFFFFFFFFFFFLL))); + CHECK_EQUAL(sizeof(uint64_t) * 4, bit_stream.used_data().size()); + + // One too many. + CHECK(!bit_stream.write(uint64_t(0))); + + CHECK_EQUAL(sizeof(uint64_t) * 4, bit_stream.data().size()); + CHECK_EQUAL(sizeof(uint64_t) * 4, bit_stream.used_data().size()); + + for (size_t i = 0UL; i < storage.size(); ++i) + { + CHECK_EQUAL(int(expected_data[i]), int(storage[i])); + } + } + + //************************************************************************* + TEST(test_write_int8_t_5bits) + { + std::array storage; + std::array write_data = { int8_t(0x01), int8_t(0xF5), int8_t(0x05), int8_t(0xFF) }; // 1, -11, 10, -1 + std::array expected_data = { char(0x0D), char(0x4B), char(0xF0), char(0x00) }; // 1, -11, 10, -1 + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream + CHECK(bit_stream.write(write_data[0], 5)); + CHECK_EQUAL(1, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[1], 5)); + CHECK_EQUAL(2, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[2], 5)); + CHECK_EQUAL(2, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[3], 5)); + CHECK_EQUAL(3, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + // 4th byte not used. + } + + //************************************************************************* + TEST(test_write_int16_t_10bits) + { + std::array storage; + std::array write_data = { int16_t(0x0001), int16_t(0xA55A), int16_t(0x5AA5), int16_t(0xFFFF) }; + std::array expected_data = { char(0x00), char(0x55), char(0xAA), char(0x97), + char(0xFF), char(0x00), char(0x00), char(0x00) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream + CHECK(bit_stream.write(write_data[0], 10)); + CHECK_EQUAL(2, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[1], 10)); + CHECK_EQUAL(3, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[2], 10)); + CHECK_EQUAL(4, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[3], 10)); + CHECK_EQUAL(5, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + // 6th to 8th bytes not used. + } + + //************************************************************************* + TEST(test_write_uint32_t_22bits) + { + std::array storage; + std::array write_data = { uint32_t(0x00000001UL), uint32_t(0xA55AA55AUL), uint32_t(0x5AA55AA5UL), uint32_t(0xFFFFFFFFUL) }; + std::array expected_data = { char(0x00), char(0x00), char(0x05), char(0xAA), + char(0x55), char(0xA9), char(0x56), char(0xA9), + char(0x7F), char(0xFF), char(0xFF), char(0x00), + char(0x00), char(0x00), char(0x00), char(0x00) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream + CHECK(bit_stream.write(write_data[0], 22)); + CHECK_EQUAL(3, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[1], 22)); + CHECK_EQUAL(6, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[2], 22)); + CHECK_EQUAL(9, bit_stream.used_data().size()); + CHECK(bit_stream.write(write_data[3], 22)); + CHECK_EQUAL(11, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + CHECK_EQUAL((int)expected_data[5], (int)storage[5]); + CHECK_EQUAL((int)expected_data[6], (int)storage[6]); + CHECK_EQUAL((int)expected_data[7], (int)storage[7]); + CHECK_EQUAL((int)expected_data[8], (int)storage[8]); + CHECK_EQUAL((int)expected_data[9], (int)storage[9]); + CHECK_EQUAL((int)expected_data[10], (int)storage[10]); + // 12th to 16th to bytes not used. + } + + //************************************************************************* + TEST(test_write_int64_t_47bits) + { + std::array storage; + std::array write_data = { int64_t(0x0000000000000001LL), int64_t(0xA55AA55AA55AA55ALL), int64_t(0x5AA55AA55AA55AA5LL), int64_t(0xFFFFFFFFFFFFFFFFLL) }; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x00), + char(0x00), char(0x02), char(0x95), char(0x6A), + char(0x95), char(0x6A), char(0x95), char(0x6A), + char(0xD5), char(0x2A), char(0xD5), char(0x2A), + char(0xD5), char(0x2F), char(0xFF), char(0xFF), + char(0xFF), char(0xFF), char(0xFF), char(0xF0) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream + bit_stream.write(write_data[0], 47); + CHECK_EQUAL(6, bit_stream.used_data().size()); + bit_stream.write(write_data[1], 47); + CHECK_EQUAL(12, bit_stream.used_data().size()); + bit_stream.write(write_data[2], 47); + CHECK_EQUAL(18, bit_stream.used_data().size()); + bit_stream.write(write_data[3], 47); + CHECK_EQUAL(24, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + CHECK_EQUAL((int)expected_data[5], (int)storage[5]); + CHECK_EQUAL((int)expected_data[6], (int)storage[6]); + CHECK_EQUAL((int)expected_data[7], (int)storage[7]); + CHECK_EQUAL((int)expected_data[8], (int)storage[8]); + CHECK_EQUAL((int)expected_data[9], (int)storage[9]); + CHECK_EQUAL((int)expected_data[10], (int)storage[10]); + CHECK_EQUAL((int)expected_data[11], (int)storage[11]); + CHECK_EQUAL((int)expected_data[12], (int)storage[12]); + CHECK_EQUAL((int)expected_data[13], (int)storage[13]); + CHECK_EQUAL((int)expected_data[14], (int)storage[14]); + CHECK_EQUAL((int)expected_data[15], (int)storage[15]); + CHECK_EQUAL((int)expected_data[16], (int)storage[16]); + CHECK_EQUAL((int)expected_data[17], (int)storage[17]); + CHECK_EQUAL((int)expected_data[18], (int)storage[18]); + CHECK_EQUAL((int)expected_data[19], (int)storage[19]); + CHECK_EQUAL((int)expected_data[20], (int)storage[20]); + CHECK_EQUAL((int)expected_data[21], (int)storage[21]); + CHECK_EQUAL((int)expected_data[22], (int)storage[22]); + CHECK_EQUAL((int)expected_data[23], (int)storage[23]); + // 25th to 32nd bytes not used. + } + + //************************************************************************* + TEST(test_write_uint64_t_47bits) + { + std::array storage; + std::array write_data = { uint64_t(0x0000000000000001ULL), uint64_t(0xA55AA55AA55AA55AULL), uint64_t(0x5AA55AA55AA55AA5ULL), uint64_t(0xFFFFFFFFFFFFFFFFULL) }; + std::array expected_data = { char(0x00), char(0x00), char(0x00), char(0x00), + char(0x00), char(0x02), char(0x95), char(0x6A), + char(0x95), char(0x6A), char(0x95), char(0x6A), + char(0xD5), char(0x2A), char(0xD5), char(0x2A), + char(0xD5), char(0x2F), char(0xFF), char(0xFF), + char(0xFF), char(0xFF), char(0xFF), char(0xF0) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream + bit_stream.write(write_data[0], 47); + CHECK_EQUAL(6, bit_stream.used_data().size()); + bit_stream.write(write_data[1], 47); + CHECK_EQUAL(12, bit_stream.used_data().size()); + bit_stream.write(write_data[2], 47); + CHECK_EQUAL(18, bit_stream.used_data().size()); + bit_stream.write(write_data[3], 47); + CHECK_EQUAL(24, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + CHECK_EQUAL((int)expected_data[5], (int)storage[5]); + CHECK_EQUAL((int)expected_data[6], (int)storage[6]); + CHECK_EQUAL((int)expected_data[7], (int)storage[7]); + CHECK_EQUAL((int)expected_data[8], (int)storage[8]); + CHECK_EQUAL((int)expected_data[9], (int)storage[9]); + CHECK_EQUAL((int)expected_data[10], (int)storage[10]); + CHECK_EQUAL((int)expected_data[11], (int)storage[11]); + CHECK_EQUAL((int)expected_data[12], (int)storage[12]); + CHECK_EQUAL((int)expected_data[13], (int)storage[13]); + CHECK_EQUAL((int)expected_data[14], (int)storage[14]); + CHECK_EQUAL((int)expected_data[15], (int)storage[15]); + CHECK_EQUAL((int)expected_data[16], (int)storage[16]); + CHECK_EQUAL((int)expected_data[17], (int)storage[17]); + CHECK_EQUAL((int)expected_data[18], (int)storage[18]); + CHECK_EQUAL((int)expected_data[19], (int)storage[19]); + CHECK_EQUAL((int)expected_data[20], (int)storage[20]); + CHECK_EQUAL((int)expected_data[21], (int)storage[21]); + CHECK_EQUAL((int)expected_data[22], (int)storage[22]); + CHECK_EQUAL((int)expected_data[23], (int)storage[23]); + // 25th to 32nd bytes not used. + } + + //************************************************************************* + TEST(test_write_multiple_full_size) + { + char c1 = 90; // 0x5A + char c2 = -91; // 0xA5 + unsigned short s1 = 4660; // 0x1234 + unsigned short s2 = 22136; // 0x5678 + int32_t i1 = 2309737967L; // 0x89ABCDEF + int32_t i2 = 4275878552L; // 0xFEDCBA98 + + std::array storage; + std::array expected_data = { char(0x5A), + char(0x12), char(0x34), + char(0x89), char(0xAB), char(0xCD), char(0xEF), + char(0xFE), char(0xDC), char(0xBA), char(0x98), + char(0x56), char(0x78), + char(0xA5) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream. + bit_stream.write(c1); + CHECK_EQUAL(1, bit_stream.used_data().size()); + bit_stream.write(s1); + CHECK_EQUAL(3, bit_stream.used_data().size()); + bit_stream.write(i1); + CHECK_EQUAL(7, bit_stream.used_data().size()); + bit_stream.write(i2); + CHECK_EQUAL(11, bit_stream.used_data().size()); + bit_stream.write(s2); + CHECK_EQUAL(13, bit_stream.used_data().size()); + bit_stream.write(c2); + CHECK_EQUAL(14, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + CHECK_EQUAL((int)expected_data[5], (int)storage[5]); + CHECK_EQUAL((int)expected_data[6], (int)storage[6]); + CHECK_EQUAL((int)expected_data[7], (int)storage[7]); + CHECK_EQUAL((int)expected_data[8], (int)storage[8]); + CHECK_EQUAL((int)expected_data[9], (int)storage[9]); + CHECK_EQUAL((int)expected_data[10], (int)storage[10]); + CHECK_EQUAL((int)expected_data[11], (int)storage[11]); + CHECK_EQUAL((int)expected_data[12], (int)storage[12]); + CHECK_EQUAL((int)expected_data[13], (int)storage[13]); + } + + ////************************************************************************* + TEST(test_write_multiple_variable_size) + { + char c1 = 90; // 0x5A 6 bits + char c2 = -91; // 0xA5 7 bits + unsigned short s1 = 4660; // 0x1234 13 bits + unsigned short s2 = 22136; // 0x5678 11 bits + int32_t i1 = 2309737967L; // 0x89ABCDEF 23 bits + int32_t i2 = 4275878552L; // 0xFEDCBA98 25 bits + + std::array storage; + std::array expected_data = { char(0x6A), char(0x46), char(0x8A), char(0xF3), + char(0x7B), char(0xDB), char(0x97), char(0x53), + char(0x19), char(0xE1), char(0x28) }; + + etl::bit_stream_writer bit_stream(storage.data(), storage.size()); + + // Insert into the stream. + bit_stream.write(c1, 6); + CHECK_EQUAL(1, bit_stream.used_data().size()); + bit_stream.write(s1, 13); + CHECK_EQUAL(3, bit_stream.used_data().size()); + bit_stream.write(i1, 23); + CHECK_EQUAL(6, bit_stream.used_data().size()); + bit_stream.write(i2, 25); + CHECK_EQUAL(9, bit_stream.used_data().size()); + bit_stream.write(s2, 11); + CHECK_EQUAL(10, bit_stream.used_data().size()); + bit_stream.write(c2, 7); + CHECK_EQUAL(11, bit_stream.used_data().size()); + + CHECK_EQUAL((int)expected_data[0], (int)storage[0]); + CHECK_EQUAL((int)expected_data[1], (int)storage[1]); + CHECK_EQUAL((int)expected_data[2], (int)storage[2]); + CHECK_EQUAL((int)expected_data[3], (int)storage[3]); + CHECK_EQUAL((int)expected_data[4], (int)storage[4]); + CHECK_EQUAL((int)expected_data[5], (int)storage[5]); + CHECK_EQUAL((int)expected_data[6], (int)storage[6]); + CHECK_EQUAL((int)expected_data[7], (int)storage[7]); + CHECK_EQUAL((int)expected_data[8], (int)storage[8]); + CHECK_EQUAL((int)expected_data[9], (int)storage[9]); + CHECK_EQUAL((int)expected_data[10], (int)storage[10]); + } + + ////************************************************************************* + //TEST(put_get_object_global) + //{ + // std::array storage; + + // etl::bit_stream bit_stream(storage.data(), storage.size()); + + // Object object1 = { -1234, 2.71578369, 250 }; + // Object object2 = { 5678, 5.24685744, 126 }; + + // CHECK(etl::bit_stream_put(bit_stream, object1)); + // CHECK(etl::bit_stream_put(bit_stream, object2)); + + // Object object1a; + // Object object2a; + + // bit_stream.restart(); + + // CHECK(etl::bit_stream_get(bit_stream, object1a)); + // CHECK(etl::bit_stream_get(bit_stream, object2a)); + + // CHECK_EQUAL(object1, object1a); + // CHECK_EQUAL(object2, object2a); + //} + + ////************************************************************************* + //TEST(put_get_multiple_float) + //{ + + // float f = 3.1415927f; + // double d = 3.1415927; + + // std::array storage; + + // etl::bit_stream bit_stream(storage.data(), storage.size()); + + // bit_stream.put(f); + // bit_stream.put(d); + + // bit_stream.restart(); + + // float rf; + // double rd; + + // CHECK(bit_stream.get(rf)); + // CHECK_CLOSE(f, rf, 0.1f); + + // CHECK(bit_stream.get(rd)); + // CHECK_CLOSE(f, rd, 0.1f); + //} + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index ac4d0cf3..0ba83986 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -10912,6 +10912,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index f37e8a61..534b29c5 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -3254,6 +3254,9 @@ Tests\Sanity Checks\Source + + Tests\Binary +