diff --git a/include/etl/bit_stream.h b/include/etl/bit_stream.h index 5b2aea68..4f23b60b 100644 --- a/include/etl/bit_stream.h +++ b/include/etl/bit_stream.h @@ -40,12 +40,41 @@ SOFTWARE. #include "memory.h" #include "delegate.h" #include "span.h" -#include "endianness.h" +#include "optional.h" +#include "exception.h" +#include "error_handler.h" #include "private/minmax_push.h" namespace etl { + //*************************************************************************** + /// Exception base for bit streams + //*************************************************************************** + class bit_stream_exception : public etl::exception + { + public: + + bit_stream_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + ///\ingroup string + /// String empty exception. + //*************************************************************************** + class bit_stream_overflow : public etl::bit_stream_exception + { + public: + + bit_stream_overflow(string_type file_name_, numeric_type line_number_) + : bit_stream_exception(ETL_ERROR_TEXT("bit_stream:overflow", ETL_BIT_STREAM_FILE_ID"A"), file_name_, line_number_) + { + } + }; + //*************************************************************************** /// Encodes and decodes bitstreams. /// Data must be stored in the stream in network order. @@ -109,9 +138,9 @@ namespace etl //*************************************************************************** void restart() { - bits_available_in_char = 8; + bits_available_in_char = CHAR_BIT; char_index = 0U; - bits_remaining = CHAR_BIT * length_chars; + bits_available = CHAR_BIT * length_chars; } //*************************************************************************** @@ -119,7 +148,7 @@ namespace etl //*************************************************************************** bool at_end() const { - return (bits_remaining == 0U); + return (bits_available == 0U); } //*************************************************************************** @@ -131,7 +160,7 @@ namespace etl if (pdata != ETL_NULLPTR) { - if (bits_remaining > 0) + if (bits_available > 0) { unsigned char chunk = value ? 1 : 0; put_integral(uint32_t(chunk), 1); @@ -203,7 +232,7 @@ namespace etl if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining > 0) + if (bits_available > 0U) { value = get_bit(); success = true; @@ -226,7 +255,7 @@ namespace etl if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= nbits) + if (bits_available >= nbits) { value = 0; @@ -270,7 +299,7 @@ namespace etl uint_least8_t nbits = CHAR_BIT * sizeof(T); // Do we have enough bits? - if (bits_remaining >= nbits) + if (bits_available >= nbits) { // Temporary storage. etl::uninitialized_buffer_of data; @@ -310,7 +339,7 @@ namespace etl //*************************************************************************** size_t bits() const { - return (length_chars * CHAR_BIT) - bits_remaining; + return (length_chars * CHAR_BIT) - bits_available; } //*************************************************************************** @@ -341,7 +370,7 @@ namespace etl if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= nbits) + if (bits_available >= nbits) { // Send the bits to the stream. while (nbits != 0) @@ -375,7 +404,7 @@ namespace etl if (pdata != ETL_NULLPTR) { // Do we have enough bits? - if (bits_remaining >= nbits) + if (bits_available >= nbits) { // Send the bits to the stream. while (nbits != 0) @@ -507,14 +536,14 @@ namespace etl bits_available_in_char = 8; } - bits_remaining -= nbits; + 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_remaining; ///< The number of bits still available in the bitstream buffer. + size_t bits_available; ///< The number of bits still available in the bitstream buffer. }; //*************************************************************************** @@ -524,16 +553,16 @@ namespace etl { public: - typedef const unsigned char* const_iterator; - - typedef etl::span callback_parameter_type; + typedef char value_type; + typedef const value_type* const_iterator; + typedef etl::span 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())) + : pdata(span_.begin()) , length_chars(span_.size_bytes()) , callback(callback_) { @@ -544,7 +573,7 @@ namespace etl /// Construct from span. //*************************************************************************** bit_stream_writer(etl::span span_, callback_type callback_ = callback_type()) - : pdata(span_.begin()) + : pdata(reinterpret_cast(span_.begin())) , length_chars(span_.size_bytes()) , callback(callback_) { @@ -555,7 +584,7 @@ namespace etl /// Construct from range. //*************************************************************************** bit_stream_writer(void* begin_, void* end_, callback_type callback_ = callback_type()) - : pdata(reinterpret_cast(begin_)) + : pdata(reinterpret_cast(begin_)) , length_chars(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) , callback(callback_) { @@ -566,7 +595,7 @@ namespace etl /// Construct from begin and length. //*************************************************************************** bit_stream_writer(void* begin_, size_t length_chars_, callback_type callback_ = callback_type()) - : pdata(reinterpret_cast(begin_)) + : pdata(reinterpret_cast(begin_)) , length_chars(length_chars_) , callback(callback_) { @@ -578,9 +607,9 @@ namespace etl //*************************************************************************** void restart() { - bits_available_in_char = 8; + bits_available_in_char = CHAR_BIT; char_index = 0U; - bits_remaining = CHAR_BIT * length_chars; + bits_available = CHAR_BIT * length_chars; } //*************************************************************************** @@ -602,7 +631,7 @@ namespace etl //*************************************************************************** bool empty() const { - return (bits_remaining == length_chars); + return (bits_available == length_chars); } //*************************************************************************** @@ -610,7 +639,7 @@ namespace etl //*************************************************************************** bool full() const { - return (bits_remaining == 0U); + return (bits_available == 0U); } //*************************************************************************** @@ -627,12 +656,15 @@ namespace etl //*************************************************************************** bool write(bool value) { - bool success = false; + bool success = (available<1U>() > 0U); - if (available<1U>() > 0U) + if (success) { write_unchecked(value); - success = true; + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -655,12 +687,15 @@ namespace etl typename etl::enable_if::value, bool>::type write(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) { - bool success = false; + bool success = (available(nbits) > 0U); - if (available(nbits) > 0U) + if (success) { write_unchecked(value, nbits); - success = true; + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -680,12 +715,15 @@ namespace etl //*************************************************************************** bool write(int64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t)) { - bool success = false; + bool success = (available(nbits) > 0U); - if (available(nbits) > 0U) + if (success) { write_unchecked(value, nbits); - success = true; + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -704,12 +742,15 @@ namespace etl //*************************************************************************** bool write(uint64_t value, uint_least8_t nbits = CHAR_BIT * sizeof(uint64_t)) { - bool success = false; + bool success = (available(nbits) > 0U); - if (available(nbits) > 0U) + if (success) { write_unchecked(value, nbits); - success = true; + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -739,12 +780,15 @@ namespace etl typename etl::enable_if::value, bool>::type write(T value) { - bool success = false; - - if (available()) + bool success = available(); + + if (success) { write_unchecked(value); - success = true; + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -757,23 +801,24 @@ namespace etl //*************************************************************************** bool skip(size_t nbits) { - bool success = true; + bool success = (nbits <= available_bits()); - if (nbits > available_bits()) + if (success) { - 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(static_cast(nbits)); + } } - - while (nbits > bits_available_in_char) + else { - step(bits_available_in_char); - nbits -= bits_available_in_char; - } - - if (nbits != 0U) - { - step(static_cast(nbits)); + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); } return success; @@ -804,13 +849,13 @@ namespace etl } //*************************************************************************** - /// The number of 'bit nbits' available in the stream. + /// The number of multiples of 'Nbits' available in the stream. /// Compile time. //*************************************************************************** template size_t available() const { - return bits_remaining / Nbits; + return bits_available / Nbits; } //*************************************************************************** @@ -829,7 +874,7 @@ namespace etl //*************************************************************************** size_t available(size_t nbits) const { - return bits_remaining / nbits; + return bits_available / nbits; } //*************************************************************************** @@ -837,7 +882,7 @@ namespace etl //*************************************************************************** size_t available_bits() const { - return bits_remaining; + return bits_available; } //*************************************************************************** @@ -877,7 +922,7 @@ namespace etl //*************************************************************************** etl::span used_data() const { - return etl::span(reinterpret_cast(pdata), reinterpret_cast(pdata) + size_bytes()); + return etl::span(pdata, pdata + size_bytes()); } //*************************************************************************** @@ -885,7 +930,7 @@ namespace etl //*************************************************************************** etl::span data() const { - return etl::span(reinterpret_cast(pdata), reinterpret_cast(pdata) + length_chars); + return etl::span(pdata, pdata + length_chars); } //*************************************************************************** @@ -897,11 +942,11 @@ namespace etl { if (bits_available_in_char != 0U) { - char_index = 1U; + char_index = 1U; // Indicate that the first char is actually 'full'. flush_full_bytes(); - char_index = 0U; - bits_available_in_char = CHAR_BIT; } + + restart(); } } @@ -939,7 +984,7 @@ namespace etl // 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); + write_chunk(static_cast(chunk), mask_width); } if (callback.is_valid()) @@ -965,7 +1010,7 @@ namespace etl // 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); + write_chunk(static_cast(chunk), mask_width); } size_t end_index = char_index; @@ -980,7 +1025,7 @@ namespace etl //*************************************************************************** /// Write a data chunk to the stream //*************************************************************************** - void write_chunk(unsigned char chunk, unsigned char nbits) + void write_chunk(char chunk, unsigned char nbits) { // Clear if new byte. if (bits_available_in_char == CHAR_BIT) @@ -994,19 +1039,22 @@ namespace etl //*************************************************************************** /// Flush full bytes to the callback, if valid. - /// Resets the + /// 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((char*)pdata, (char*)pdata + char_index)); + 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; @@ -1046,15 +1094,15 @@ namespace etl bits_available_in_char = CHAR_BIT; } - bits_remaining -= nbits; + bits_available -= 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. + char* const pdata; ///< The start of the bitstream buffer. + const size_t length_chars; ///< The length 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 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. }; //*************************************************************************** @@ -1069,13 +1117,331 @@ namespace etl } //*************************************************************************** - /// Implementation of the write function. + /// Default implementation of the write function. + /// For integral and floating point types only. + /// Overload this to support custom types. //*************************************************************************** template bool 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 const char* const_iterator; + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + bit_stream_reader(etl::span span_) + : pdata(span_.begin()) + , length_chars(span_.size_bytes()) + { + restart(); + } + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + bit_stream_reader(etl::span span_) + : pdata(reinterpret_cast(span_.begin())) + , length_chars(span_.size_bytes()) + { + restart(); + } + + //*************************************************************************** + /// Construct from range. + //*************************************************************************** + bit_stream_reader(void* begin_, void* end_) + : pdata(reinterpret_cast(begin_)) + , length_chars(etl::distance(reinterpret_cast(begin_), reinterpret_cast(end_))) + { + restart(); + } + + //*************************************************************************** + /// Construct from begin and length. + //*************************************************************************** + bit_stream_reader(void* begin_, size_t length_) + : pdata(reinterpret_cast(begin_)) + , length_chars(length_) + { + 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 etl::enable_if::value, bool>::type + read_unchecked() + { + return get_bit(); + } + + //*************************************************************************** + /// For bool types + //*************************************************************************** + template + typename etl::enable_if::value, etl::optional>::type + read() + { + etl::optional result; + + if (bits_available > 0U) + { + result = read_unchecked(); + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); + } + + return result; + } + + //*************************************************************************** + /// For integral types + //*************************************************************************** + template + typename etl::enable_if::value && !etl::is_same::value, T>::type + read_unchecked(uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + T value = 0; + uint_least8_t bits = nbits; + + // Get the bits from the stream. + while (nbits != 0) + { + 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); + + nbits -= mask_width; + value |= static_cast(chunk << nbits); + } + + // Sign extend if signed type and not already full bit width. + if (etl::is_signed::value && (bits != (CHAR_BIT * sizeof(T)))) + { + typedef typename etl::make_signed::type ST; + value = etl::sign_extend(value, bits); + } + + return value; + } + + //*************************************************************************** + /// For integral types + //*************************************************************************** + template + typename etl::enable_if::value && !etl::is_same::value, etl::optional>::type + read(uint_least8_t nbits = CHAR_BIT * sizeof(T)) + { + etl::optional result; + + // Do we have enough bits? + if (bits_available >= nbits) + { + result = read_unchecked(nbits); + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); + } + + return result; + } + + //*************************************************************************** + /// For floating point types + //*************************************************************************** + template + typename etl::enable_if::value, T>::type + read_unchecked() + { + T value = 0; + + // Temporary storage. + etl::uninitialized_buffer_of data; + + for (size_t i = 0UL; i < sizeof(T); ++i) + { + get(data.raw[i], CHAR_BIT); + } + + from_bytes(reinterpret_cast(data.raw), value); + + return success; + } + + //*************************************************************************** + /// For floating point types + //*************************************************************************** + template + typename etl::enable_if::value, etl::optional>::type + read() + { + uint_least8_t nbits = CHAR_BIT * sizeof(T); + + etl::optional result; + + // Do we have enough bits? + if (bits_available >= nbits) + { + result = read_unchecked(); + } + else + { + ETL_ASSERT_FAIL(ETL_ERROR(etl::bit_stream_overflow)); + } + + return result; + } + + //*************************************************************************** + /// Returns the number of bytes used in the stream. + //*************************************************************************** + size_t size_bytes() 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 size_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_bytes(); + } + + private: + + //*************************************************************************** + /// 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::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 + void from_bytes(const unsigned char* data, T& value) + { + etl::uninitialized_buffer_of 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(temp.raw); + } + + //*************************************************************************** + /// 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; + } + + 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. + }; } #include "private/minmax_pop.h" diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 662cf521..e3c8c776 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -95,5 +95,7 @@ SOFTWARE. #define ETL_MEM_CAST_FILE_ID "62" #define ETL_SINGLETON_FILE_ID "63" #define ETL_SUCCESSOR_FILE_ID "64" +#define ETL_BIT_STREAM_FILE_ID "65" +#define ETL_BYTE_STREAM_FILE_ID "66" #endif diff --git a/test/test_atomic.cpp b/test/test_atomic.cpp index c805df65..ce1cffa1 100644 --- a/test/test_atomic.cpp +++ b/test/test_atomic.cpp @@ -116,10 +116,8 @@ namespace //************************************************************************* TEST(test_atomic_bool_load) { - bool i; - - std::atomic compare(&i); - etl::atomic test(&i); + std::atomic compare(true); + etl::atomic test(true); CHECK_EQUAL((bool)compare.load(), (bool)test.load()); } diff --git a/test/test_bit_stream_reader.cpp b/test/test_bit_stream_reader.cpp new file mode 100644 index 00000000..5ad5ae20 --- /dev/null +++ b/test/test_bit_stream_reader.cpp @@ -0,0 +1,804 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#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 read(etl::bit_stream_reader& stream, Object& object) + { + bool success = true; + + //if (!stream.object.i, 14)) + //{ + // success = false; + //} + + //if (!stream.get(object.d)) + //{ + // success = false; + //} + + //if (!stream.get(object.c)) + //{ + // success = false; + //} + + return success; + } +} + +namespace +{ + SUITE(test_bit_stream_reader) + { + //************************************************************************* + TEST(test_read_bool) + { + char storage = 0x5AU; + + etl::bit_stream_reader bit_stream(&storage, 1); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(false, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(true, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(false, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(true, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(true, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(false, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(true, result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(false, result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int8_t) + { + std::array storage = { char(0x01), char(0x5A), char(0xA5), char(0xFF) }; + std::array expected = { int8_t(0x01), int8_t(0x5A), int8_t(0xA5), int8_t(0xFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[0]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[1]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[2]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[3]), int(result.value())); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int8_t_5bits) + { + std::array storage = { char(0x0E), char(0x8B), char(0xF0) }; + std::array expected = { int8_t(0x01), int8_t(0xFA), int8_t(0x05), int8_t(0xFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[0]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[1]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[2]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[3]), int(result.value())); + + // One too many. + CHECK_THROW(bit_stream.read(5U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint8_t) + { + std::array storage = { char(0x01), char(0x5A), char(0xA5), char(0xFF) }; + std::array expected = { uint8_t(0x01), uint8_t(0x5A), uint8_t(0xA5), uint8_t(0xFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[0]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[1]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[2]), int(result.value())); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[3]), int(result.value())); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint8_t_5bits) + { + std::array storage = { char(0x0E), char(0x8B), char(0xF0) }; + std::array expected = { uint8_t(0x01), uint8_t(0x1A), uint8_t(0x05), uint8_t(0x1F) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[0]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[1]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[2]), int(result.value())); + + result.reset(); + result = bit_stream.read(5U); + CHECK(result.has_value()); + CHECK_EQUAL(int(expected[3]), int(result.value())); + + // One too many. + CHECK_THROW(bit_stream.read(5U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int16_t) + { + std::array storage = { char(0x00), char(0x01), char(0xA5), char(0x5A), char(0x5A), char(0xA5), char(0xFF), char(0xFF) }; + std::array expected = { int16_t(0x0001), int16_t(0xA55A), int16_t(0x5AA5), int16_t(0xFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int16_t_10bits) + { + std::array storage = { char(0x00), char(0x55), char(0xAA), char(0x97), char(0xFF) }; + std::array expected = { int16_t(0x0001), int16_t(0x015A), int16_t(0xFEA5), int16_t(0xFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(10U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint16_t) + { + std::array storage = { char(0x00), char(0x01), char(0xA5), char(0x5A), char(0x5A), char(0xA5), char(0xFF), char(0xFF) }; + std::array expected = { uint16_t(0x0001), uint16_t(0xA55A), uint16_t(0x5AA5), uint16_t(0xFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint16_t_10bits) + { + std::array storage = { char(0x00), char(0x55), char(0xAA), char(0x97), char(0xFF) }; + std::array expected = { uint16_t(0x0001), uint16_t(0x015A), uint16_t(0x02A5), uint16_t(0x03FF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(10U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(10U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int32_t) + { + std::array storage = { char(0x00), char(0x00), char(0x00), char(0x01), + char(0xA5), char(0x5A), char(0xA5), char(0x5A), + char(0x5A), char(0xA5), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + std::array expected = { int32_t(0x00000001), int32_t(0xA55AA55A), int32_t(0x5AA55AA5), int32_t(0xFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int32_t_22bits) + { + std::array storage = { char(0x00), char(0x00), char(0x05), char(0xAA), + char(0x55), char(0xA9), char(0x56), char(0xA9), + char(0x7F), char(0xFF), char(0xFF) }; + std::array expected = { int32_t(0x00000001), int32_t(0x001AA55A), int32_t(0xFFE55AA5), int32_t(0xFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(22U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint32_t) + { + std::array storage = { char(0x00), char(0x00), char(0x00), char(0x01), + char(0xA5), char(0x5A), char(0xA5), char(0x5A), + char(0x5A), char(0xA5), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + std::array expected = { uint32_t(0x00000001), uint32_t(0xA55AA55A), uint32_t(0x5AA55AA5), uint32_t(0xFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint32_t_22bits) + { + std::array storage = { char(0x00), char(0x00), char(0x05), char(0xAA), + char(0x55), char(0xA9), char(0x56), char(0xA9), + char(0x7F), char(0xFF), char(0xFF) }; + std::array expected = { uint32_t(0x00000001), uint32_t(0x001AA55A), uint32_t(0x00255AA5), uint32_t(0x003FFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(22U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(22U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int64_t) + { + std::array storage = { char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x01), + char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), + char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + std::array expected = { int64_t(0x0000000000000001), int64_t(0xA55AA55AA55AA55A), int64_t(0x5AA55AA55AA55AA5), int64_t(0xFFFFFFFFFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_int64_t_47bits) + { + std::array storage = { 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) }; + std::array expected = { int64_t(0x0000000000000001), int64_t(0x0000255AA55AA55A), int64_t(0xFFFFDAA55AA55AA5), int64_t(0xFFFFFFFFFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(47U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint64_t) + { + std::array storage = { char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x00), char(0x01), + char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), + char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), char(0x5A), char(0xA5), + char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF) }; + std::array expected = { uint64_t(0x0000000000000001), uint64_t(0xA55AA55AA55AA55A), uint64_t(0x5AA55AA55AA55AA5), uint64_t(0xFFFFFFFFFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_uint64_t_47bits) + { + std::array storage = { 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) }; + std::array expected = { uint64_t(0x0000000000000001), uint64_t(0x0000255AA55AA55A), uint64_t(0x00005AA55AA55AA5), uint64_t(0x00007FFFFFFFFFFF) }; + + etl::bit_stream_reader bit_stream(storage.data(), storage.size()); + + etl::optional result; + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[0], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[1], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[2], result.value()); + + result.reset(); + result = bit_stream.read(47U); + CHECK(result.has_value()); + CHECK_EQUAL(expected[3], result.value()); + + // One too many. + CHECK_THROW(bit_stream.read(47U), etl::bit_stream_overflow); + } + + //************************************************************************* + TEST(test_read_multiple_full_size) + { + int8_t c1 = 90; // 0x5A + uint16_t s1 = 4660; // 0x1234 + int32_t i1 = 2309737967L; // 0x89ABCDEF + int32_t i2 = 4275878552L; // 0xFEDCBA98 + uint16_t s2 = 22136; // 0x5678 + int8_t c2 = -91; // 0xA5 + + std::array storage = { 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_reader bit_stream(storage.data(), storage.size()); + + auto result_c1 = bit_stream.read(); + CHECK(result_c1.has_value()); + CHECK_EQUAL(int8_t(0x5A), result_c1.value()); + + auto result_s1 = bit_stream.read(); + CHECK(result_s1.has_value()); + CHECK_EQUAL(uint16_t(0x1234), result_s1.value()); + + auto result_i1 = bit_stream.read(); + CHECK(result_i1.has_value()); + CHECK_EQUAL(int32_t(0x89ABCDEF), result_i1.value()); + + auto result_i2 = bit_stream.read(); + CHECK(result_i2.has_value()); + CHECK_EQUAL(int32_t(0xFEDCBA98), result_i2.value()); + + auto result_s2 = bit_stream.read(); + CHECK(result_s2.has_value()); + CHECK_EQUAL(uint16_t(0x5678), result_s2.value()); + + auto result_c2 = bit_stream.read(); + CHECK(result_c2.has_value()); + CHECK_EQUAL(int8_t(0xA5), result_c2.value()); + } + + //************************************************************************* + TEST(test_read_multiple_variable_size) + { + int8_t c1 = 90; // 0x5A 6 bits + uint16_t s1 = 4660; // 0x1234 13 bits + int32_t i1 = 2309737967L; // 0x89ABCDEF 23 bits + int32_t i2 = 4275878552L; // 0xFEDCBA98 25 bits + uint16_t s2 = 22136; // 0x5678 11 bits + int8_t c2 = -91; // 0xA5 7 bits + + std::array storage = { 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_reader bit_stream(storage.data(), storage.size()); + + auto result_c1 = bit_stream.read(6U); + CHECK(result_c1.has_value()); + CHECK_EQUAL(int8_t(0x1A), result_c1.value()); + + auto result_s1 = bit_stream.read(13U); + CHECK(result_s1.has_value()); + CHECK_EQUAL(uint16_t(0x1234), result_s1.value()); + + auto result_i1 = bit_stream.read(23U); + CHECK(result_i1.has_value()); + CHECK_EQUAL(int32_t(0x002BCDEF), result_i1.value()); + + auto result_i2 = bit_stream.read(25U); + CHECK(result_i2.has_value()); + CHECK_EQUAL(int32_t(0x00DCBA98), result_i2.value()); + + auto result_s2 = bit_stream.read(11U); + CHECK(result_s2.has_value()); + CHECK_EQUAL(uint16_t(0x0678), result_s2.value()); + + auto result_c2 = bit_stream.read(7U); + CHECK(result_c2.has_value()); + CHECK_EQUAL(int8_t(0x25), result_c2.value()); + } + + //************************************************************************* + TEST(test_read_object) + { + + } + + //************************************************************************* + TEST(test_read_multiple_floating_point) + { + + } + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index fc3a7179..d4583102 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -10911,6 +10911,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index 08042a11..cfdeff65 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -3251,6 +3251,9 @@ Tests\Atomic + + Tests\Binary + @@ -3413,4 +3416,4 @@ Resource Files - + \ No newline at end of file