Various bugfixes (#1428)

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-05-06 19:26:10 +02:00 committed by GitHub
parent c9198d089c
commit 079b3345d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 433 additions and 30 deletions

View File

@ -1466,9 +1466,7 @@ namespace etl
//*********************************************************************
size_type find(const_pointer s, size_type pos, size_type n) const
{
size_t sz = etl::strlen(s);
return find_impl(s, s + n, sz, pos);
return find_impl(s, s + n, n, pos);
}
//*********************************************************************

View File

@ -1742,7 +1742,7 @@ namespace etl
{
count = 1U;
if ((value & 0xFFFFFFFFF0000000ULL) == 0U)
if ((value & 0xFFFFFFFF00000000ULL) == 0U)
{
value <<= 32U;
count += 32U;
@ -1795,7 +1795,7 @@ namespace etl
{
typedef typename etl::make_unsigned<T>::type unsigned_t;
return static_cast<T>(count_trailing_ones(static_cast<unsigned_t>(value)));
return static_cast<T>(count_leading_zeros(static_cast<unsigned_t>(value)));
}
#if ETL_USING_8BIT_TYPES
@ -1927,8 +1927,8 @@ namespace etl
if ((value & 0xFFFF0000UL) == 0xFFFF0000UL)
{
value <<= 8U;
count += 8U;
value <<= 16U;
count += 16U;
}
if ((value & 0xFF000000UL) == 0xFF000000UL)
@ -1988,14 +1988,14 @@ namespace etl
if ((value & 0xFFFFFFFF00000000ULL) == 0xFFFFFFFF00000000ULL)
{
value <<= 8U;
count += 8U;
value <<= 32U;
count += 32U;
}
if ((value & 0xFFFF000000000000ULL) == 0xFFFF000000000000ULL)
{
value <<= 8U;
count += 8U;
value <<= 16U;
count += 16U;
}
if ((value & 0xFF00000000000000ULL) == 0xFF00000000000000ULL)

View File

@ -1206,7 +1206,7 @@ namespace etl
{
this->clear();
for (typename etl::icircular_buffer<T>::const_iterator itr = other.begin(); itr != other.end(); ++itr)
for (typename etl::icircular_buffer<T>::iterator itr = other.begin(); itr != other.end(); ++itr)
{
this->push(etl::move(*itr));
}

View File

@ -605,10 +605,10 @@ namespace etl
}
//***************************************************
reference operator[](size_t i)
const_reference operator[](size_t i) const
{
iterator result(*this);
result += i;
const_iterator result(*this);
result += static_cast<difference_type>(i);
return *result;
}

View File

@ -206,7 +206,7 @@ namespace etl
//*******************************************
/// Get the error.
//*******************************************
ETL_CONSTEXPR14 TError&& error() const&& ETL_NOEXCEPT
ETL_CONSTEXPR14 const TError&& error() const&& ETL_NOEXCEPT
{
return etl::move(error_value);
}
@ -1309,7 +1309,7 @@ namespace etl
template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result<F, void, const TError&&>::type>::type>
auto transform_error(F&& f) const&& -> expected<void, U>
{
return transform_error_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), *this);
return transform_error_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), etl::move(*this));
}
#endif

View File

@ -116,15 +116,15 @@ namespace etl
};
//***************************************************************************
/// Unsorted exception for the list.
///\ingroup list
/// Unsorted exception for the forward_list.
///\ingroup forward_list
//***************************************************************************
class forward_list_no_pool : public forward_list_exception
{
public:
forward_list_no_pool(string_type file_name_, numeric_type line_number_)
: forward_list_exception(ETL_ERROR_TEXT("list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_)
: forward_list_exception(ETL_ERROR_TEXT("forward_list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_)
{
}
};

View File

@ -479,7 +479,7 @@ namespace etl
//*********************************
const_iterator end() const
{
return accumulator.begin();
return accumulator.end();
}
//*********************************
@ -487,7 +487,7 @@ namespace etl
//*********************************
const_iterator cend() const
{
return accumulator.cbegin();
return accumulator.cend();
}
//*********************************

View File

@ -890,7 +890,10 @@ namespace etl
template <typename TLink>
typename etl::enable_if< etl::is_same<TLink, etl::bidirectional_link<TLink::ID> >::value, void>::type link_clear_range(TLink* start)
{
etl::link_clear_range(*start);
if (start != ETL_NULLPTR)
{
etl::link_clear_range(*start);
}
}
#if ETL_USING_CPP17

View File

@ -589,9 +589,9 @@ namespace etl
return npos;
}
position = etl::min(position, size());
position = etl::min(position, size() - view.size());
const_iterator iposition = etl::find_end(begin(), begin() + position, view.begin(), view.end());
const_iterator iposition = etl::find_end(begin(), begin() + position + view.size(), view.begin(), view.end());
if (iposition == end())
{

View File

@ -499,19 +499,19 @@ namespace etl
};
#if ETL_HAS_NATIVE_CHAR8_T
template <>
struct is_signed<char8_t> : true_type
struct is_signed<char8_t> : false_type
{
};
#endif
#if ETL_HAS_NATIVE_CHAR16_T
template <>
struct is_signed<char16_t> : true_type
struct is_signed<char16_t> : false_type
{
};
#endif
#if ETL_HAS_NATIVE_CHAR32_T
template <>
struct is_signed<char32_t> : true_type
struct is_signed<char32_t> : false_type
{
};
#endif
@ -569,6 +569,24 @@ namespace etl
struct is_unsigned<unsigned long long> : true_type
{
};
#if ETL_HAS_NATIVE_CHAR8_T
template <>
struct is_unsigned<char8_t> : true_type
{
};
#endif
#if ETL_HAS_NATIVE_CHAR16_T
template <>
struct is_unsigned<char16_t> : true_type
{
};
#endif
#if ETL_HAS_NATIVE_CHAR32_T
template <>
struct is_unsigned<char32_t> : true_type
{
};
#endif
template <typename T>
struct is_unsigned<const T> : is_unsigned<T>
{

View File

@ -377,7 +377,7 @@ namespace etl
inline bool operator==(const pair<T1, T2>& a, const pair<T1, T2>& b)
{
#include "private/diagnostic_float_equal_push.h"
return (a.first == b.first) && !(a.second < b.second) && !(a.second > b.second);
return (a.first == b.first) && (a.second == b.second);
#include "private/diagnostic_pop.h"
}

View File

@ -3040,6 +3040,108 @@ namespace
CHECK_ARRAY_EQUAL(expected.data(), output.data(), expected.size());
}
//*************************************************************************
TEST(test_count_leading_zeros_signed_32)
{
// int32_t(-1) = 0xFFFFFFFF → 0 leading zeros
CHECK_EQUAL(0, int(etl::count_leading_zeros(int32_t(-1))));
// int32_t(1) = 0x00000001 → 31 leading zeros
CHECK_EQUAL(31, int(etl::count_leading_zeros(int32_t(1))));
// int32_t(0) = 0x00000000 → 32 leading zeros
CHECK_EQUAL(32, int(etl::count_leading_zeros(int32_t(0))));
// int32_t(256) = 0x00000100 → 23 leading zeros
CHECK_EQUAL(23, int(etl::count_leading_zeros(int32_t(256))));
// Verify against unsigned version
for (int i = 0; i < 32; ++i)
{
int32_t value = int32_t(1) << i;
CHECK_EQUAL(int(etl::count_leading_zeros(uint32_t(value))), int(etl::count_leading_zeros(value)));
}
}
//*************************************************************************
TEST(test_count_leading_zeros_signed_64)
{
CHECK_EQUAL(0, int(etl::count_leading_zeros(int64_t(-1))));
CHECK_EQUAL(63, int(etl::count_leading_zeros(int64_t(1))));
CHECK_EQUAL(64, int(etl::count_leading_zeros(int64_t(0))));
for (int i = 0; i < 64; ++i)
{
int64_t value = int64_t(1) << i;
CHECK_EQUAL(int(etl::count_leading_zeros(uint64_t(value))), int(etl::count_leading_zeros(value)));
}
}
//*************************************************************************
TEST(test_count_leading_zeros_64_specific_values)
{
// Value that specifically triggers the upper-32-bit mask path
// Bit 35 set: 0x0000000800000000 → 28 leading zeros
CHECK_EQUAL(28, int(etl::count_leading_zeros(uint64_t(0x0000000800000000ULL))));
// Bit 31 set: 0x0000000080000000 → 32 leading zeros
CHECK_EQUAL(32, int(etl::count_leading_zeros(uint64_t(0x0000000080000000ULL))));
// All zeros in upper 32: 0x00000000FFFFFFFF → 32 leading zeros
CHECK_EQUAL(32, int(etl::count_leading_zeros(uint64_t(0x00000000FFFFFFFFULL))));
// Upper half has bit: 0x0000000100000000 → 31 leading zeros
CHECK_EQUAL(31, int(etl::count_leading_zeros(uint64_t(0x0000000100000000ULL))));
}
//*************************************************************************
TEST(test_count_leading_ones_32_specific_values)
{
// 0xFFFF0000 → upper 16 bits set → exactly 16 leading ones
CHECK_EQUAL(16, int(etl::count_leading_ones(uint32_t(0xFFFF0000UL))));
// 0xFFFF8000 → 17 leading ones
CHECK_EQUAL(17, int(etl::count_leading_ones(uint32_t(0xFFFF8000UL))));
// 0xFFFFF000 → 20 leading ones
CHECK_EQUAL(20, int(etl::count_leading_ones(uint32_t(0xFFFFF000UL))));
// 0xFFFFFF00 → 24 leading ones
CHECK_EQUAL(24, int(etl::count_leading_ones(uint32_t(0xFFFFFF00UL))));
// 0xFFFFFFFE → 31 leading ones
CHECK_EQUAL(31, int(etl::count_leading_ones(uint32_t(0xFFFFFFFEUL))));
// 0xFFFFFFFF → 32 leading ones
CHECK_EQUAL(32, int(etl::count_leading_ones(uint32_t(0xFFFFFFFFUL))));
// 0x80000000 → 1 leading one
CHECK_EQUAL(1, int(etl::count_leading_ones(uint32_t(0x80000000UL))));
// Verify against reference for boundary values
for (uint32_t i = 0; i < 33; ++i)
{
// Create value with exactly 'i' leading ones
uint32_t value = (i == 0) ? 0U : (i == 32) ? 0xFFFFFFFFUL : ~((1UL << (32U - i)) - 1UL);
CHECK_EQUAL(int(test_leading_ones(value)), int(etl::count_leading_ones(value)));
}
}
//*************************************************************************
TEST(test_count_leading_ones_64_specific_values)
{
// 0xFFFFFFFF00000000 → 32 leading ones
CHECK_EQUAL(32, int(etl::count_leading_ones(uint64_t(0xFFFFFFFF00000000ULL))));
// 0xFFFFFFFFFFFF0000 → 48 leading ones
CHECK_EQUAL(48, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFF0000ULL))));
// 0xFFFF000000000000 → 16 leading ones
CHECK_EQUAL(16, int(etl::count_leading_ones(uint64_t(0xFFFF000000000000ULL))));
// 0xFFFFFFFFFFFFFF00 → 56 leading ones
CHECK_EQUAL(56, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFF00ULL))));
// 0xFFFFFFFFFFFFFFFE → 63 leading ones
CHECK_EQUAL(63, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFFFEULL))));
// 0xFFFFFFFFFFFFFFFF → 64 leading ones
CHECK_EQUAL(64, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFFFFULL))));
// Verify against reference for boundary values
for (uint64_t i = 0; i < 65; ++i)
{
uint64_t value = (i == 0) ? 0ULL : (i == 64) ? 0xFFFFFFFFFFFFFFFFULL : ~((1ULL << (64ULL - i)) - 1ULL);
CHECK_EQUAL(int(test_leading_ones(value)), int(etl::count_leading_ones(value)));
}
}
}
} // namespace

View File

@ -1072,5 +1072,30 @@ namespace
CHECK(!is_equal);
}
//*************************************************************************
TEST(test_move_assignment_actually_moves)
{
DataM data;
data.push(ItemM("A"));
data.push(ItemM("B"));
data.push(ItemM("C"));
DataM data2;
data2 = std::move(data);
CHECK_EQUAL(3U, data2.size());
// If the move was correct, data2 should hold the moved-to values
CHECK_EQUAL("A", data2[0].value);
CHECK_EQUAL("B", data2[1].value);
CHECK_EQUAL("C", data2[2].value);
// Original should be moved-from (empty strings with move-only type)
for (size_t i = 0; i < data.size(); ++i)
{
CHECK(data[i].value.empty());
}
}
}
} // namespace

View File

@ -2272,6 +2272,29 @@ namespace
CHECK(std::equal(blank_data.begin(), blank_data.end(), data.begin()));
}
//*************************************************************************
TEST(test_const_iterator_subscript)
{
DataNDC data(initial_data.begin(), initial_data.end());
const DataNDC& cdata = data;
// Access via const_iterator operator[]
DataNDC::const_iterator cit = cdata.begin();
// Verify operator[] returns values matching sequential access
for (size_t i = 0; i < cdata.size(); ++i)
{
CHECK(cit[i] == cdata[i]);
}
// Verify const_iterator operator[] returns const_reference
// (This is a compile-time check - if the fix is reverted,
// the type would be non-const reference which is incorrect for const_iterator)
const NDC& ref = cit[0];
CHECK(ref == cdata[0]);
}
}
} // namespace

View File

@ -1616,5 +1616,26 @@ namespace
Error result = exp.error_or(Error("default"));
CHECK_EQUAL("real_error", result.e);
}
//*************************************************************************
TEST(test_unexpected_error_const_rvalue_ref)
{
const etl::unexpected<Error> ue(Error("test_error"));
// Move from const rvalue — should get const Error&&
const Error&& ref = std::move(ue).error();
CHECK_EQUAL("test_error", ref.e);
}
//*************************************************************************
TEST(test_transform_error_void_const_rvalue)
{
const etl::expected<void, Error> exp(etl::unexpected<Error>(Error("original")));
auto result = std::move(exp).transform_error([](const Error& e) { return Error(e.e + "_transformed"); });
CHECK_FALSE(result.has_value());
CHECK_EQUAL("original_transformed", result.error().e);
}
}
} // namespace

View File

@ -1444,6 +1444,28 @@ namespace
CHECK_EQUAL(ItemNDC("F"), *itr++);
}
#endif
//*************************************************************************
TEST(test_forward_list_exception_types)
{
// Verify exception class hierarchy is correct
CHECK(true == (std::is_base_of<etl::exception, etl::forward_list_exception>::value));
CHECK(true == (std::is_base_of<etl::forward_list_exception, etl::forward_list_full>::value));
CHECK(true == (std::is_base_of<etl::forward_list_exception, etl::forward_list_empty>::value));
CHECK(true == (std::is_base_of<etl::forward_list_exception, etl::forward_list_iterator>::value));
#if defined(ETL_VERBOSE_ERRORS)
// When verbose errors are enabled, check the error text contains "forward_list"
etl::forward_list_full ex_full(__FILE__, __LINE__);
etl::forward_list_empty ex_empty(__FILE__, __LINE__);
std::string full_msg(ex_full.what());
std::string empty_msg(ex_empty.what());
CHECK(full_msg.find("forward_list") != std::string::npos);
CHECK(empty_msg.find("forward_list") != std::string::npos);
#endif
}
}
#include "etl/private/diagnostic_pop.h"
} // namespace

View File

@ -332,5 +332,59 @@ namespace
isEqual = std::equal(output2.begin(), output2.end(), histogram.begin());
CHECK(isEqual);
}
//*************************************************************************
TEST(test_sparse_histogram_iteration)
{
StringHistogram histogram;
// Empty histogram: begin == end
CHECK(histogram.begin() == histogram.end());
CHECK(histogram.cbegin() == histogram.cend());
// Add items
histogram.add(std::string("apple"));
histogram.add(std::string("banana"));
histogram.add(std::string("apple"));
// Non-empty: begin != end
CHECK(histogram.begin() != histogram.end());
CHECK(histogram.cbegin() != histogram.cend());
// Count elements by iterating
size_t count = 0;
for (auto it = histogram.begin(); it != histogram.end(); ++it)
{
++count;
}
CHECK_EQUAL(2U, count); // "apple" and "banana"
// Same with cbegin/cend
count = 0;
for (auto it = histogram.cbegin(); it != histogram.cend(); ++it)
{
++count;
}
CHECK_EQUAL(2U, count);
// Verify we can find the expected values
bool found_apple = false;
bool found_banana = false;
for (auto it = histogram.begin(); it != histogram.end(); ++it)
{
if (it->first == "apple")
{
CHECK_EQUAL(2, it->second);
found_apple = true;
}
if (it->first == "banana")
{
CHECK_EQUAL(1, it->second);
found_banana = true;
}
}
CHECK(found_apple);
CHECK(found_banana);
}
}
} // namespace

View File

@ -1845,5 +1845,15 @@ namespace
CHECK(c.etl_left == nullptr);
CHECK(c.etl_right == nullptr);
}
//*************************************************************************
TEST(test_link_clear_range_bidirectional_nullptr)
{
// Passing nullptr should be a no-op, not a crash
BData* null_ptr = nullptr;
etl::link_clear_range<BLink0>(null_ptr);
// If we get here without crashing, the test passes
CHECK(true);
}
}
} // namespace

View File

@ -5475,5 +5475,32 @@ namespace
CHECK(text1 == sstream_view);
}
#endif
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_find_char_pointer_n_shorter_than_strlen)
{
const value_t haystack_str[] = STR("Hello World");
TextSTD compare(haystack_str);
TextL text(haystack_str);
// Haystack is "Hello World" (size 11).
// search_str is "Worldly" (strlen 7).
// We search for first 5 chars ("World") starting at pos=6.
// Old code: (6 + 7 = 13) > 11 → premature npos (BUG)
// Fixed: (6 + 5 = 11) <= 11 → proceeds to search → finds at 6
const value_t search_str[] = STR("Worldly");
size_t pos_std = compare.find(search_str, 6, 5);
size_t pos_etl = text.find(search_str, 6, 5);
CHECK_EQUAL(6U, pos_std);
CHECK_EQUAL(pos_std, pos_etl);
// pos=5 also triggers: (5 + 7 = 12) > 11 with old code
pos_std = compare.find(search_str, 5, 5);
pos_etl = text.find(search_str, 5, 5);
CHECK_EQUAL(6U, pos_std);
CHECK_EQUAL(pos_std, pos_etl);
}
}
} // namespace

View File

@ -877,7 +877,7 @@ namespace
CHECK(2U == view.rfind(s2, 5));
CHECK(View::npos == view.rfind(s4));
CHECK(1U == view.rfind(s3, 5, 2));
CHECK(5U == view.rfind(s3, 5, 2));
CHECK(View::npos == view.rfind(s4, 0, 11));
}
@ -1183,5 +1183,40 @@ namespace
CHECK_TRUE(u32view == u32sstream_view);
}
#endif
//*************************************************************************
TEST(test_rfind_boundary_positions)
{
etl::string_view sv("abcabc");
// rfind "abc" with position=3: should find the second "abc" at position 3
size_t pos = sv.rfind(etl::string_view("abc"), 3);
CHECK_EQUAL(3U, pos);
// rfind "abc" with position=2: should find only the first "abc" at position 0
pos = sv.rfind(etl::string_view("abc"), 2);
CHECK_EQUAL(0U, pos);
// rfind "abc" with position=0: should find at position 0
pos = sv.rfind(etl::string_view("abc"), 0);
CHECK_EQUAL(0U, pos);
// rfind with npos (search entire string)
pos = sv.rfind(etl::string_view("abc"));
CHECK_EQUAL(3U, pos);
// rfind something not found
pos = sv.rfind(etl::string_view("xyz"));
CHECK_EQUAL(etl::string_view::npos, pos);
// Compare with std::string to verify exact behavior
std::string std_sv("abcabc");
for (size_t p = 0; p <= std_sv.size(); ++p)
{
size_t std_pos = std_sv.rfind("abc", p);
size_t etl_pos = sv.rfind(etl::string_view("abc"), p);
CHECK_EQUAL(std_pos, etl_pos);
}
}
}
} // namespace

View File

@ -2247,6 +2247,25 @@ namespace
CHECK_FALSE((etl::is_object_v<int&>));
CHECK_FALSE((etl::is_object_v<int&&>));
CHECK_FALSE((etl::is_object_v<decltype(f)>));
#endif
}
//*************************************************************************
TEST(test_is_signed_unsigned_char_types)
{
#if ETL_HAS_NATIVE_CHAR8_T
CHECK_FALSE(etl::is_signed<char8_t>::value);
CHECK_TRUE(etl::is_unsigned<char8_t>::value);
#endif
#if ETL_HAS_NATIVE_CHAR16_T
CHECK_FALSE(etl::is_signed<char16_t>::value);
CHECK_TRUE(etl::is_unsigned<char16_t>::value);
#endif
#if ETL_HAS_NATIVE_CHAR32_T
CHECK_FALSE(etl::is_signed<char32_t>::value);
CHECK_TRUE(etl::is_unsigned<char32_t>::value);
#endif
}
}

View File

@ -1047,5 +1047,51 @@ namespace
CHECK_EQUAL(expect1c, result1g);
#endif
}
//*************************************************************************
TEST(test_pair_equality_uses_equality_operator)
{
// Basic equality
etl::pair<int, int> p1(1, 2);
etl::pair<int, int> p2(1, 2);
etl::pair<int, int> p3(1, 3);
etl::pair<int, int> p4(2, 2);
CHECK_TRUE(p1 == p2);
CHECK_FALSE(p1 == p3); // different second
CHECK_FALSE(p1 == p4); // different first
// Custom type where operator== and operator< can disagree
// The old code used !(a<b) && !(a>b), which is NOT equivalent to a==b
// for types that don't define a total order consistent with equality.
struct WeirdType
{
int value;
bool equal_flag;
bool operator==(const WeirdType& other) const
{
return equal_flag && other.equal_flag;
}
bool operator<(const WeirdType& other) const
{
return value < other.value;
}
bool operator>(const WeirdType& other) const
{
return value > other.value;
}
};
WeirdType w1{1, false};
WeirdType w2{1, false}; // same value, but equal_flag is false
// With proper ==: w1 == w2 should be false (both equal_flags are false)
// With old !(w1<w2)&&!(w1>w2): would be true (same value)
etl::pair<int, WeirdType> pw1(0, w1);
etl::pair<int, WeirdType> pw2(0, w2);
CHECK_FALSE(pw1 == pw2); // This would FAIL with the old < > based comparison
}
}
} // namespace