From 09e7b3ff1f2c8bff867c25aea32bd1fd55f5c8c9 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 22 Jan 2022 13:47:39 +0200 Subject: [PATCH 1/4] Handled SIZE == 0 edge cases (#501) --- include/etl/bitset.h | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/include/etl/bitset.h b/include/etl/bitset.h index 2093d26b..0bc4ac82 100644 --- a/include/etl/bitset.h +++ b/include/etl/bitset.h @@ -260,7 +260,10 @@ namespace etl size_t index; element_t mask; - if (SIZE == 1) + if (SIZE == 0) { + return false; + } + else if (SIZE == 1) { index = 0; mask = element_t(1) << position; @@ -297,7 +300,10 @@ namespace etl size_t index; element_t bit; - if (SIZE == 1) + if (SIZE == 0) { + return *this; + } + else if (SIZE == 1) { index = 0; bit = element_t(1) << position; @@ -453,7 +459,11 @@ namespace etl size_t index; element_t bit; - if (SIZE == 1) + if (SIZE == 0) + { + return *this; + } + else if (SIZE == 1) { index = 0; bit = element_t(1) << position; @@ -494,7 +504,12 @@ namespace etl size_t index; element_t bit; - if (SIZE == 1) + + if (SIZE == 0) + { + return *this; + } + else if (SIZE == 1) { index = 0; bit = element_t(1) << position; @@ -580,7 +595,11 @@ namespace etl size_t index; size_t bit; - if (SIZE == 1) + if (SIZE == 0) + { + return ibitset::npos; + } + else if (SIZE == 1) { index = 0; bit = position; @@ -692,7 +711,10 @@ namespace etl //************************************************************************* ibitset& operator<<=(size_t shift) { - if (SIZE == 1) + if (SIZE == 0) { + return *this; + } + else if (SIZE == 1) { pdata[0] <<= shift; } @@ -720,7 +742,10 @@ namespace etl //************************************************************************* ibitset& operator>>=(size_t shift) { - if (SIZE == 1) + if (SIZE == 0) { + return *this; + } + else if (SIZE == 1) { pdata[0] >>= shift; } From b5a5f0e83ae76e86e1dc939f7323479a620f6194 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 22 Jan 2022 13:48:08 +0200 Subject: [PATCH 2/4] Fixed bug where invert changed bits over the TOP_MASK (#500) --- include/etl/bitset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etl/bitset.h b/include/etl/bitset.h index 0bc4ac82..b98cadaf 100644 --- a/include/etl/bitset.h +++ b/include/etl/bitset.h @@ -848,7 +848,7 @@ namespace etl { for (size_t i = 0UL; i < SIZE; ++i) { - pdata[i] = ~pdata[i]; + pdata[i] = ~pdata[i] & (i == SIZE - 1 ? TOP_MASK : ALL_SET); } } From 1f7ebe3ee8307f1bdd41d61b53c0e00c1fd79431 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 22 Jan 2022 13:48:34 +0200 Subject: [PATCH 3/4] Added to_string, to_ulong and to_ullong to bitset (#499) * Added to_string, to_ulong and to_ullong to bitset * Reverted whitspace changes * Arduino has NAN defined, but not nan, nanf nor nanl (it got __builtin_nan, but I don't think this is what we want here...) * Added missing typenames --- include/etl/bitset.h | 71 +++++++++++++++++++ .../determine_compiler_language_support.h | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/include/etl/bitset.h b/include/etl/bitset.h index b98cadaf..2475fdb8 100644 --- a/include/etl/bitset.h +++ b/include/etl/bitset.h @@ -51,6 +51,7 @@ SOFTWARE. #include "static_assert.h" #include "error_handler.h" #include "span.h" +#include "string.h" #include "private/minmax_push.h" @@ -120,6 +121,20 @@ namespace etl } }; + //*************************************************************************** + /// Bitset overflow exception. + ///\ingroup bitset + //*************************************************************************** + class bitset_overflow : public bitset_exception + { + public: + + bitset_overflow(string_type file_name_, numeric_type line_number_) + : bitset_exception(ETL_ERROR_TEXT("bitset:overflow", ETL_BITSET_FILE_ID"C"), file_name_, line_number_) + { + } + }; + //************************************************************************* /// The base class for etl::bitset ///\ingroup bitset @@ -809,6 +824,46 @@ namespace etl } #endif + //************************************************************************* + /// to_ulong + /// Returns an unsigned long represented by the bitset data. + //************************************************************************* + unsigned long to_ulong() const + { + for (size_t i = sizeof(long) / sizeof(element_t); i < SIZE; ++i) + ETL_ASSERT(!(pdata[i]), ETL_ERROR(etl::bitset_overflow)); + + unsigned long out = 0; + + for (size_t i = 0UL; i < SIZE; i++) + { + out += static_cast(pdata[i] & ((i < SIZE - 1) ? ALL_SET : TOP_MASK)) << (BITS_PER_ELEMENT * i); + } + + return out; + } + +#if ETL_CPP11_SUPPORTED + //************************************************************************* + /// to_ullong + /// Returns an unsigned long long represented by the bitset data. + //************************************************************************* + unsigned long long to_ullong() const + { + for (size_t i = sizeof(long long) / sizeof(element_t); i < SIZE; ++i) + ETL_ASSERT(!(pdata[i]), ETL_ERROR(etl::bitset_overflow)); + + unsigned long long out = 0; + + for (size_t i = 0ULL; i < SIZE; i++) + { + out += static_cast(pdata[i] & ((i < SIZE - 1) ? ALL_SET : TOP_MASK)) << (BITS_PER_ELEMENT * i); + } + + return out; + } +#endif + protected: //************************************************************************* @@ -1080,6 +1135,22 @@ namespace etl return *this; } + //************************************************************************* + /// Returns a string representing the bitset. + //************************************************************************* + template > + STRINGT to_string(typename STRINGT::value_type zero = typename STRINGT::value_type('0'), typename STRINGT::value_type one = typename STRINGT::value_type('1')) const + { + STRINGT result; + + result.assign(MAXN, '\0'); + for (size_t i = MAXN; i > 0; --i) { + result[MAXN - i] = test(i - 1) ? one : zero; + } + + return result; + } + //************************************************************************* /// operator = //************************************************************************* diff --git a/include/etl/profiles/determine_compiler_language_support.h b/include/etl/profiles/determine_compiler_language_support.h index f2cbe9b1..e9adf379 100644 --- a/include/etl/profiles/determine_compiler_language_support.h +++ b/include/etl/profiles/determine_compiler_language_support.h @@ -79,7 +79,7 @@ SOFTWARE. #endif // NAN not defined or Rowley CrossWorks -#if !defined(NAN) || defined(__CROSSWORKS_ARM) || defined(ETL_COMPILER_ARM5) +#if !defined(NAN) || defined(__CROSSWORKS_ARM) || defined(ETL_COMPILER_ARM5) || defined(ARDUINO) #define ETL_NO_CPP_NAN_SUPPORT #endif From e98270ca608175eb45f2d0cafbae82efcdbaebcd Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 22 Jan 2022 16:16:44 +0000 Subject: [PATCH 4/4] Bitset updates --- include/etl/bitset.h | 181 ++++++++++++++++++++++++------------------- test/test_bitset.cpp | 75 ++++++++++++++++++ 2 files changed, 175 insertions(+), 81 deletions(-) diff --git a/include/etl/bitset.h b/include/etl/bitset.h index 2475fdb8..871ed2b5 100644 --- a/include/etl/bitset.h +++ b/include/etl/bitset.h @@ -275,7 +275,8 @@ namespace etl size_t index; element_t mask; - if (SIZE == 0) { + if (SIZE == 0) + { return false; } else if (SIZE == 1) @@ -433,6 +434,11 @@ namespace etl typename etl::enable_if::value, T>::type value() const { + for (size_t i = sizeof(long long) / sizeof(element_t); i < SIZE; ++i) + { + ETL_ASSERT_AND_RETURN_VALUE(!(pdata[i]), ETL_ERROR(etl::bitset_overflow), T(0)); + } + T v = T(0); const bool OK = (sizeof(T) * CHAR_BIT) >= (SIZE * BITS_PER_ELEMENT); @@ -453,6 +459,22 @@ namespace etl return v; } + //************************************************************************* + /// Put to a unsigned long. + //************************************************************************* + unsigned long to_ulong() const + { + return value(); + } + + //************************************************************************* + /// Put to a unsigned long long. + //************************************************************************* + unsigned long long to_ullong() const + { + return value(); + } + //************************************************************************* /// Resets the bitset. //************************************************************************* @@ -518,7 +540,6 @@ namespace etl { size_t index; element_t bit; - if (SIZE == 0) { @@ -546,6 +567,11 @@ namespace etl //************************************************************************* bool all() const { + if (SIZE == 0UL) + { + return true; + } + // All but the last. for (size_t i = 0UL; i < (SIZE - 1); ++i) { @@ -726,26 +752,37 @@ namespace etl //************************************************************************* ibitset& operator<<=(size_t shift) { - if (SIZE == 0) { - return *this; - } - else if (SIZE == 1) + if (SIZE != 0UL) { - pdata[0] <<= shift; - } - else - { - size_t source = NBITS - shift - 1; - size_t destination = NBITS - 1; - - for (size_t i = 0UL; i < (NBITS - shift); ++i) + // Just one element? + if (SIZE == 1UL) { - set(destination--, test(source--)); + pdata[0] <<= shift; } - - for (size_t i = 0UL; i < shift; ++i) + else if (shift == BITS_PER_ELEMENT) { - reset(destination--); + for (size_t i = (SIZE - 1); i > 0U; --i) + { + pdata[i] = pdata[i - 1]; + } + + pdata[0] = 0; + } + else + { + // TODO Optimise. + size_t source = NBITS - shift - 1; + size_t destination = NBITS - 1; + + for (size_t i = 0UL; i < (NBITS - shift); ++i) + { + set(destination--, test(source--)); + } + + for (size_t i = 0UL; i < shift; ++i) + { + reset(destination--); + } } } @@ -757,26 +794,38 @@ namespace etl //************************************************************************* ibitset& operator>>=(size_t shift) { - if (SIZE == 0) { - return *this; - } - else if (SIZE == 1) + if (SIZE != 0UL) { - pdata[0] >>= shift; - } - else - { - size_t source = shift; - size_t destination = 0; - - for (size_t i = 0UL; i < (NBITS - shift); ++i) + // Just one element? + if (SIZE == 1UL) { - set(destination++, test(source++)); + pdata[0] >>= shift; } - - for (size_t i = 0UL; i < shift; ++i) + // Shift is the size of an element? + else if (shift == BITS_PER_ELEMENT) { - reset(destination++); + for (size_t i = 0UL; i < (SIZE - 1); ++i) + { + pdata[i] = pdata[i + 1]; + } + + pdata[SIZE - 1] = 0; + } + else + { + // TODO Optimise. + size_t source = shift; + size_t destination = 0UL; + + for (size_t i = 0UL; i < (NBITS - shift); ++i) + { + set(destination++, test(source++)); + } + + for (size_t i = 0UL; i < shift; ++i) + { + reset(destination++); + } } } @@ -824,46 +873,6 @@ namespace etl } #endif - //************************************************************************* - /// to_ulong - /// Returns an unsigned long represented by the bitset data. - //************************************************************************* - unsigned long to_ulong() const - { - for (size_t i = sizeof(long) / sizeof(element_t); i < SIZE; ++i) - ETL_ASSERT(!(pdata[i]), ETL_ERROR(etl::bitset_overflow)); - - unsigned long out = 0; - - for (size_t i = 0UL; i < SIZE; i++) - { - out += static_cast(pdata[i] & ((i < SIZE - 1) ? ALL_SET : TOP_MASK)) << (BITS_PER_ELEMENT * i); - } - - return out; - } - -#if ETL_CPP11_SUPPORTED - //************************************************************************* - /// to_ullong - /// Returns an unsigned long long represented by the bitset data. - //************************************************************************* - unsigned long long to_ullong() const - { - for (size_t i = sizeof(long long) / sizeof(element_t); i < SIZE; ++i) - ETL_ASSERT(!(pdata[i]), ETL_ERROR(etl::bitset_overflow)); - - unsigned long long out = 0; - - for (size_t i = 0ULL; i < SIZE; i++) - { - out += static_cast(pdata[i] & ((i < SIZE - 1) ? ALL_SET : TOP_MASK)) << (BITS_PER_ELEMENT * i); - } - - return out; - } -#endif - protected: //************************************************************************* @@ -903,8 +912,10 @@ namespace etl { for (size_t i = 0UL; i < SIZE; ++i) { - pdata[i] = ~pdata[i] & (i == SIZE - 1 ? TOP_MASK : ALL_SET); + pdata[i] = ~pdata[i]; } + + pdata[SIZE - 1] &= TOP_MASK; } //************************************************************************* @@ -1138,13 +1149,21 @@ namespace etl //************************************************************************* /// Returns a string representing the bitset. //************************************************************************* - template > - STRINGT to_string(typename STRINGT::value_type zero = typename STRINGT::value_type('0'), typename STRINGT::value_type one = typename STRINGT::value_type('1')) const +#if ETL_CPP11_SUPPORTED + template > +#else + template +#endif + TString to_string(typename TString::value_type zero = typename TString::value_type('0'), typename TString::value_type one = typename TString::value_type('1')) const { - STRINGT result; + TString result; - result.assign(MAXN, '\0'); - for (size_t i = MAXN; i > 0; --i) { + result.resize(MAXN, '\0'); + + ETL_ASSERT_AND_RETURN_VALUE((result.size() == MAXN), ETL_ERROR(etl::bitset_overflow), result); + + for (size_t i = MAXN; i > 0; --i) + { result[MAXN - i] = test(i - 1) ? one : zero; } @@ -1255,7 +1274,7 @@ namespace etl private: - element_t data[ARRAY_SIZE]; + element_t data[ARRAY_SIZE > 0U ? ARRAY_SIZE : 1U]; }; //*************************************************************************** diff --git a/test/test_bitset.cpp b/test/test_bitset.cpp index 744224d1..d22ffb35 100644 --- a/test/test_bitset.cpp +++ b/test/test_bitset.cpp @@ -749,6 +749,26 @@ namespace CHECK(data2 == shift11); } + //************************************************************************* + TEST(test_shift_left_operator_shift_element_size) + { + etl::bitset<60> data(0x12345678UL); + etl::bitset<60> shift(0x1234567800UL); + + data <<= 8U; + CHECK_EQUAL(shift.value(), data.value()); + } + + //************************************************************************* + TEST(test_shift_left_operator_overflow) + { + etl::bitset<31> data(0x7FFFFFFFUL); + etl::bitset<31> shifted(0x7FFFFFFEUL); + + data <<= 1U; + CHECK(data == shifted); + } + //************************************************************************* TEST(test_shift_left_copy_operator) { @@ -790,6 +810,16 @@ namespace CHECK(data2 == shift11); } + //************************************************************************* + TEST(test_shift_right_operator_shift_element_size) + { + etl::bitset<60> data(0x12345678UL); + etl::bitset<60> shift(0x123456UL); + + data >>= 8U; + CHECK_EQUAL(shift.value(), data.value()); + } + //************************************************************************* TEST(test_shift_right_copy_operator) { @@ -1091,5 +1121,50 @@ namespace CHECK_EQUAL(0x34U, s[2]); CHECK_EQUAL(0x12U, s[3]); } + + //************************************************************************* + TEST(test_to_string) + { + etl::bitset<32> b(0x12345678UL); + + etl::string<32> text = b.to_string('.', '*'); + std::string stdtext = b.to_string('.', '*'); + + CHECK_THROW(etl::string<32> text1 = b.to_string>('.', '*'), etl::bitset_overflow); + CHECK_EQUAL("...*..*...**.*...*.*.**..****...", text.c_str()); + CHECK_EQUAL("...*..*...**.*...*.*.**..****...", stdtext.c_str()); + } + + //************************************************************************* + TEST(test_issue_497_count_inverted_bits) + { + etl::bitset<5U> bits; + std::bitset<5U> stdbits; + etl::bitset<5U> negbits = ~bits; + std::bitset<5U> stdnegbits = ~stdbits; + + CHECK_EQUAL(stdbits.count(), bits.count()); + CHECK_EQUAL(stdbits.all(), bits.all()); + CHECK_EQUAL(stdnegbits.count(), negbits.count()); + CHECK_EQUAL(stdnegbits.all(), negbits.all()); + } + + //************************************************************************* + TEST(test_issue_497_shifted_zero_sized_bitset) + { + etl::bitset<0> bits; + std::bitset<0> stdbits; + + CHECK_EQUAL(stdbits.size(), bits.size()); + CHECK_EQUAL(stdbits.none(), bits.none()); + CHECK_EQUAL(stdbits.all(), bits.all()); + CHECK_EQUAL(stdbits.any(), bits.any()); + + CHECK_EQUAL(stdbits.count(), bits.count()); + CHECK_EQUAL(stdbits.to_ulong(), bits.to_ulong()); + CHECK_EQUAL(stdbits.to_ullong(), bits.to_ullong()); + + etl::bitset<0> shiftbits = bits << 1; // No exception. + } }; }