From 55dcb84bc06c197d62e771d0e2081ea6fdca19b0 Mon Sep 17 00:00:00 2001 From: Eminy <57416756+emincan2000@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:44:10 +0300 Subject: [PATCH 1/3] Fix find and rfind position handling in string and string_view (#1637) * Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove * Updated sponsors pages * Refactor the documentation css to group styles for light and dark themes * Update Codacy badge in README.md Updated Codacy badge link to reflect new project status. * Fix Codacy badge display * refresh codacy integration status * Expose more Zephyr Kconfigs for error handling (#1622) * zephyr: make `ETL_DEBUG` default to on if Zephyr assertions are enabled this is the more appropriate default: ETL_DEBUG controls some container assertions, so it should follow the Zephyr assertions by default * zephyr: expose new error-checking configs * zephyr: add Kconfig choices for error handling * Fix find and rfind position handling in string and string_view * Default the rfind string_view position to npos The string_view overload defaulted pos to 0, so rfind(view) only found a match at the start of the string. The other rfind overloads and std::basic_string use npos. --------- Co-authored-by: Sergei Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove Co-authored-by: Eden Frosst --- docs/strings/string.md | 1 + include/etl/basic_string.h | 34 ++++++--- include/etl/string_view.h | 13 +++- test/test_string_char.cpp | 32 +++++++++ test/test_string_char_external_buffer.cpp | 5 ++ test/test_string_u16.cpp | 5 ++ test/test_string_u16_external_buffer.cpp | 5 ++ test/test_string_u32.cpp | 5 ++ test/test_string_u32_external_buffer.cpp | 5 ++ test/test_string_u8.cpp | 5 ++ test/test_string_u8_external_buffer.cpp | 5 ++ test/test_string_view.cpp | 21 ++++++ test/test_string_wchar_t.cpp | 5 ++ test/test_string_wchar_t_external_buffer.cpp | 5 ++ zephyr/CMakeLists.txt | 12 +++- zephyr/Kconfig | 72 +++++++++++++++++++- 16 files changed, 212 insertions(+), 18 deletions(-) diff --git a/docs/strings/string.md b/docs/strings/string.md index b884fc58..5f53f8b1 100644 --- a/docs/strings/string.md +++ b/docs/strings/string.md @@ -707,6 +707,7 @@ size_t find(char c, size_t pos = 0) const; ```cpp size_t rfind(const etl::istring& str, size_t pos = npos) const; +size_t rfind(const etl::string_view& view, size_t pos = npos) const; size_t rfind(const char* s, size_t pos = npos) const; size_t rfind(const char* s, size_t pos, size_t n) const; size_t rfind(char c, size_t pos = npos) const; diff --git a/include/etl/basic_string.h b/include/etl/basic_string.h index 59db587a..d8256886 100644 --- a/include/etl/basic_string.h +++ b/include/etl/basic_string.h @@ -1476,6 +1476,11 @@ namespace etl //********************************************************************* size_type find(T c, size_type position = 0) const { + if (position >= size()) + { + return npos; + } + const_iterator i = etl::find(begin() + position, end(), c); if (i != end()) @@ -1504,7 +1509,7 @@ namespace etl ///\param pos The position to start searching from. //********************************************************************* template - size_type rfind(const etl::basic_string_view& view, size_type pos = 0) const + size_type rfind(const etl::basic_string_view& view, size_type pos = npos) const { return rfind_impl(view.rbegin(), view.rend(), view.size(), pos); } @@ -1544,12 +1549,8 @@ namespace etl //********************************************************************* size_type rfind(T c, size_type position = npos) const { - if (position >= size()) - { - position = size(); - } - - position = size() - position; + // Reverse offset of the first character to check, which is 'position' itself. + position = (position >= size()) ? 0 : (size() - position - 1); const_reverse_iterator i = etl::find(rbegin() + static_cast(position), rend(), c); @@ -2851,11 +2852,16 @@ namespace etl template size_type find_impl(TIterator first, TIterator last, size_type sz, size_type pos = 0) const { - if ((pos + sz) > size()) + if ((pos > size()) || (sz > (size() - pos))) { return npos; } + if (sz == 0) + { + return pos; + } + const_iterator iposition = etl::search(begin() + pos, end(), first, last); if (iposition == end()) @@ -2879,12 +2885,18 @@ namespace etl return npos; } - if (pos >= size()) + if (pos > (size() - sz)) { - pos = size(); + pos = size() - sz; } - pos = size() - pos; + if (sz == 0) + { + return pos; + } + + // Reverse offset of the last character of a match starting at 'pos'. + pos = size() - (pos + sz); const_reverse_iterator iposition = etl::search(rbegin() + static_cast(pos), rend(), rfirst, rlast); diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 0827636c..0b65639e 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -554,11 +554,16 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type find(etl::basic_string_view view, size_type position = 0) const ETL_NOEXCEPT { - if ((size() < view.size())) + if ((position > size()) || (view.size() > (size() - position))) { return npos; } + if (view.empty()) + { + return position; + } + const_iterator iposition = etl::search(begin() + position, end(), view.begin(), view.end()); if (iposition == end()) @@ -598,9 +603,11 @@ namespace etl position = etl::min(position, size() - view.size()); - const_iterator iposition = etl::find_end(begin(), begin() + position + view.size(), view.begin(), view.end()); + const_iterator search_end = begin() + position + view.size(); + const_iterator iposition = etl::find_end(begin(), search_end, view.begin(), view.end()); - if (iposition == end()) + // find_end returns the end of the searched range when there is no match. + if ((iposition == search_end) && !view.empty()) { return npos; } diff --git a/test/test_string_char.cpp b/test/test_string_char.cpp index b8271af4..ac660ce7 100644 --- a/test/test_string_char.cpp +++ b/test/test_string_char.cpp @@ -3815,6 +3815,33 @@ namespace CHECK_EQUAL(TextL::npos, position2); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_rfind_all_positions) + { + const value_t the_haystack[] = STR("A haystack with a needle and another needle"); + + TextSTD compare_haystack(the_haystack); + TextL haystack(the_haystack); + + const value_t* needles[] = {STR("needle"), STR("e"), STR("")}; + + // Every position up to past the end, where the search must not read outside the string. + for (size_t position = 0UL; position <= compare_haystack.size() + 2UL; ++position) + { + for (size_t i = 0UL; i < 3UL; ++i) + { + CHECK_EQUAL(compare_haystack.find(needles[i], position), haystack.find(needles[i], position)); + CHECK_EQUAL(compare_haystack.rfind(needles[i], position), haystack.rfind(needles[i], position)); + } + + CHECK_EQUAL(compare_haystack.find(STR('e'), position), haystack.find(STR('e'), position)); + CHECK_EQUAL(compare_haystack.rfind(STR('e'), position), haystack.rfind(STR('e'), position)); + } + + CHECK_EQUAL(TextL::npos, haystack.find(STR('e'), TextL::npos)); + CHECK_EQUAL(TextL::npos, haystack.find(STR("needle"), TextL::npos)); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains_string) { @@ -3991,6 +4018,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_char_external_buffer.cpp b/test/test_string_char_external_buffer.cpp index ee0789af..eba9ccdf 100644 --- a/test/test_string_char_external_buffer.cpp +++ b/test/test_string_char_external_buffer.cpp @@ -4362,6 +4362,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u16.cpp b/test/test_string_u16.cpp index cced6e37..4f145fef 100644 --- a/test/test_string_u16.cpp +++ b/test/test_string_u16.cpp @@ -4063,6 +4063,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u16_external_buffer.cpp b/test/test_string_u16_external_buffer.cpp index df59bf00..b3a98a25 100644 --- a/test/test_string_u16_external_buffer.cpp +++ b/test/test_string_u16_external_buffer.cpp @@ -4464,6 +4464,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u32.cpp b/test/test_string_u32.cpp index 9a704319..1f63c9ec 100644 --- a/test/test_string_u32.cpp +++ b/test/test_string_u32.cpp @@ -4063,6 +4063,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u32_external_buffer.cpp b/test/test_string_u32_external_buffer.cpp index 6b956c3f..0ab1863d 100644 --- a/test/test_string_u32_external_buffer.cpp +++ b/test/test_string_u32_external_buffer.cpp @@ -4464,6 +4464,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u8.cpp b/test/test_string_u8.cpp index 36df7389..9f35de2d 100644 --- a/test/test_string_u8.cpp +++ b/test/test_string_u8.cpp @@ -4008,6 +4008,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_u8_external_buffer.cpp b/test/test_string_u8_external_buffer.cpp index 41c02cbc..32d1fa10 100644 --- a/test/test_string_u8_external_buffer.cpp +++ b/test/test_string_u8_external_buffer.cpp @@ -4383,6 +4383,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index ad234834..b8d1760f 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -925,6 +925,27 @@ namespace CHECK(View::npos == view.rfind(s4, 0, 11)); } + //************************************************************************* + TEST(test_find_rfind_all_positions) + { + const std::string compare_text("abcdabcdab"); + View view(compare_text.c_str()); + + const char* needles[] = {"cd", "d", "x", ""}; + + // Every position up to past the end, where the search must not read outside the view. + for (size_t position = 0U; position <= compare_text.size() + 2U; ++position) + { + for (size_t i = 0U; i < 4U; ++i) + { + CHECK_EQUAL(compare_text.find(needles[i], position), view.find(needles[i], position)); + CHECK_EQUAL(compare_text.rfind(needles[i], position), view.rfind(needles[i], position)); + } + } + + CHECK_EQUAL(View::npos, view.find("cd", View::npos)); + } + //************************************************************************* TEST(test_find_first_of) { diff --git a/test/test_string_wchar_t.cpp b/test/test_string_wchar_t.cpp index 56688bfa..577f4261 100644 --- a/test/test_string_wchar_t.cpp +++ b/test/test_string_wchar_t.cpp @@ -4064,6 +4064,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/test/test_string_wchar_t_external_buffer.cpp b/test/test_string_wchar_t_external_buffer.cpp index 99ecedc0..d6260e50 100644 --- a/test/test_string_wchar_t_external_buffer.cpp +++ b/test/test_string_wchar_t_external_buffer.cpp @@ -4467,6 +4467,11 @@ namespace position2 = haystack.rfind(needle_view, haystack.size() - 10); CHECK_EQUAL(position1, position2); + // The default position searches the whole string. + position1 = compare_haystack.rfind(compare_needle); + position2 = haystack.rfind(needle_view); + CHECK_EQUAL(position1, position2); + View pin_view(STR("pin")); position2 = haystack.rfind(pin_view); CHECK_EQUAL(TextL::npos, position2); diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 60e5c2fb..4c360584 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -3,6 +3,16 @@ if(CONFIG_ETL) zephyr_link_libraries(etl::etl) zephyr_compile_definitions_ifdef(CONFIG_ETL_DEBUG ETL_DEBUG) - zephyr_compile_definitions_ifdef(CONFIG_ETL_CHECK_PUSH_POP ETL_CHECK_PUSH_POP) + zephyr_compile_definitions_ifdef(CONFIG_ETL_LOG_ERRORS ETL_LOG_ERRORS) + zephyr_compile_definitions_ifdef(CONFIG_ETL_USE_ASSERT_FUNCTION ETL_USE_ASSERT_FUNCTION) + zephyr_compile_definitions_ifdef(CONFIG_ETL_THROW_EXCEPTIONS ETL_THROW_EXCEPTIONS) + zephyr_compile_definitions_ifdef(CONFIG_ETL_NO_CHECKS ETL_NO_CHECKS) + + zephyr_compile_definitions_ifdef(CONFIG_ETL_VERBOSE_ERRORS ETL_VERBOSE_ERRORS) + zephyr_compile_definitions_ifdef(CONFIG_ETL_MINIMAL_ERRORS ETL_MINIMAL_ERRORS) + + zephyr_compile_definitions_ifdef(CONFIG_ETL_CHECK_PUSH_POP ETL_CHECK_PUSH_POP) + zephyr_compile_definitions_ifdef(CONFIG_ETL_CHECK_INDEX_OPERATOR ETL_CHECK_INDEX_OPERATOR) + zephyr_compile_definitions_ifdef(CONFIG_ETL_CHECK_EXTRA ETL_CHECK_EXTRA) endif() diff --git a/zephyr/Kconfig b/zephyr/Kconfig index ad8cd12d..b5ac8b47 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -8,12 +8,78 @@ if ETL config ETL_DEBUG bool "Build ETL with debugging checks" - default y if DEBUG + default y if ASSERT + +choice ETL_ERROR_MODE + prompt "ETL error handling mode" + help + What ETL does when one of its checks fails. + +config ETL_ASSERT_ERRORS + bool "Assert in ETL_DEBUG builds, otherwise ignore" + help + With ETL_DEBUG, a failed check calls assert(). Without it, nothing + is reported, but checks that guard an operation still skip it. + With ETL_LOG_ERRORS, a failed check calls the error handler instead, + with or without ETL_DEBUG. + +config ETL_USE_ASSERT_FUNCTION + bool "Call a user assert function" + help + A failed check calls the function registered with + etl::set_assert_function(), and checks that guard an operation + still skip it. Until one is registered, it calls assert(false). + +config ETL_THROW_EXCEPTIONS + bool "Throw an exception" + depends on CPP_EXCEPTIONS + help + A failed check throws an etl::exception. + +config ETL_NO_CHECKS + bool "No checks" + help + Check conditions are not evaluated. + +endchoice # ETL_ERROR_MODE + +config ETL_LOG_ERRORS + bool "Call the ETL error handler" + depends on !ETL_USE_ASSERT_FUNCTION && !ETL_NO_CHECKS + help + A failed check calls the callback registered with + etl::error_handler::set_callback(). With ETL_THROW_EXCEPTIONS it + then throws; otherwise, checks that guard an operation still skip + it. With no callback registered, errors are discarded. + +choice ETL_ERROR_DETAIL + prompt "ETL error detail level" + help + What information the etl::exception for a failed check carries, + whether it is thrown or passed to the error handler or assert + function. Unused when a failed check calls plain assert(). + +config ETL_TERSE_ERRORS + bool "Short error code only" + +config ETL_VERBOSE_ERRORS + bool "Message, file and line" + +config ETL_MINIMAL_ERRORS + bool "Nothing" + +endchoice # ETL_ERROR_DETAIL config ETL_CHECK_PUSH_POP bool "Enable push/pop checks in ETL" + depends on !ETL_NO_CHECKS -config ETL_LOG_ERRORS - bool "Call ETL error handler instead of assert" +config ETL_CHECK_INDEX_OPERATOR + bool "Enable bounds checks on ETL operator[]" + depends on !ETL_NO_CHECKS + +config ETL_CHECK_EXTRA + bool "Enable extra bounds and empty checks in ETL containers" + depends on !ETL_NO_CHECKS endif # ETL From cdc32071c89ebf88ed68c081da33f987017707ff Mon Sep 17 00:00:00 2001 From: AlvyneZ <60469361+AlvyneZ@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:44:58 +0300 Subject: [PATCH 2/3] Fix indirect_vector range overloads matching integral arguments (#1636) * Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove * Updated sponsors pages * Refactor the documentation css to group styles for light and dark themes * Update Codacy badge in README.md Updated Codacy badge link to reflect new project status. * Fix Codacy badge display * refresh codacy integration status * Expose more Zephyr Kconfigs for error handling (#1622) * zephyr: make `ETL_DEBUG` default to on if Zephyr assertions are enabled this is the more appropriate default: ETL_DEBUG controls some container assertions, so it should follow the Zephyr assertions by default * zephyr: expose new error-checking configs * zephyr: add Kconfig choices for error handling * Fix indirect_vector range overloads matching integral arguments Calls such as indirect_vector::insert(position, 2, 7), assign(5, 7) and the constructor indirect_vector(5, 7) chose the iterator range templates, because two ints deduce TIterator = int exactly. They then failed to compile inside the template, for example at etl::distance(int, int). Constrain the range versions of assign, insert and the indirect_vector and indirect_vector_ext constructors with etl::enable_if, as etl::vector does, so that integral arguments select the (count, value) overloads. Add tests for each case. --------- Co-authored-by: Sergei Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove Co-authored-by: Eden Frosst --- include/etl/indirect_vector.h | 9 ++--- test/test_indirect_vector.cpp | 36 +++++++++++++++++++ test/test_indirect_vector_external_buffer.cpp | 16 +++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/include/etl/indirect_vector.h b/include/etl/indirect_vector.h index 2fcac9ba..217e6380 100644 --- a/include/etl/indirect_vector.h +++ b/include/etl/indirect_vector.h @@ -724,7 +724,7 @@ namespace etl ///\param last The iterator to the last element + 1. //********************************************************************* template - void assign(TIterator first, TIterator last) + typename etl::enable_if::value, void>::type assign(TIterator first, TIterator last) { ETL_STATIC_ASSERT((etl::is_same::type, typename etl::remove_cv< typename etl::iterator_traits< TIterator>::value_type>::type>::value), @@ -1071,7 +1071,7 @@ namespace etl ///\param last The last + 1 element to add. //********************************************************************* template - iterator insert(const_iterator position, TIterator first, TIterator last) + typename etl::enable_if::value, iterator>::type insert(const_iterator position, TIterator first, TIterator last) { size_t count = size_t(etl::distance(first, last)); @@ -1462,7 +1462,7 @@ namespace etl ///\param last The iterator to the last element + 1. //************************************************************************* template - indirect_vector(TIterator first, TIterator last) + indirect_vector(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) : etl::iindirect_vector(lookup_vector, storage_pool) { this->assign(first, last); @@ -1613,7 +1613,8 @@ namespace etl ///\param last The iterator to the last element + 1. //************************************************************************* template - indirect_vector_ext(TIterator first, TIterator last, etl::ivector& lookup_, etl::ipool& pool_) + indirect_vector_ext(TIterator first, TIterator last, etl::ivector& lookup_, etl::ipool& pool_, + typename etl::enable_if::value, int>::type = 0) : etl::iindirect_vector(lookup_, pool_) { ETL_ASSERT(lookup_.capacity() <= pool_.capacity(), ETL_ERROR(indirect_vector_buffer_missmatch)); diff --git a/test/test_indirect_vector.cpp b/test/test_indirect_vector.cpp index f9973441..75dc1293 100644 --- a/test/test_indirect_vector.cpp +++ b/test/test_indirect_vector.cpp @@ -1201,6 +1201,42 @@ namespace CHECK_THROW(data.insert(data.begin() + static_cast(offset), INSERT_SIZE, INITIAL_VALUE), etl::vector_full); } + //************************************************************************* + TEST(test_constructor_two_parameter_same_type_not_iterator) + { + std::vector compare_data(5, 7); + etl::indirect_vector data(5, 7); + + CHECK_EQUAL(compare_data.size(), data.size()); + CHECK(std::equal(data.begin(), data.end(), compare_data.begin())); + } + + //************************************************************************* + TEST(test_assign_two_parameter_same_type_not_iterator) + { + std::vector compare_data; + etl::indirect_vector data; + + compare_data.assign(5, 7); + data.assign(5, 7); + + CHECK_EQUAL(compare_data.size(), data.size()); + CHECK(std::equal(data.begin(), data.end(), compare_data.begin())); + } + + //************************************************************************* + TEST(test_insert_three_parameter_same_type_not_iterator) + { + std::vector compare_data(3UL, 1); + etl::indirect_vector data(3UL, 1); + + compare_data.insert(compare_data.begin(), 2, 7); + data.insert(data.begin(), 2, 7); + + CHECK_EQUAL(compare_data.size(), data.size()); + CHECK(std::equal(data.begin(), data.end(), compare_data.begin())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_position_range) { diff --git a/test/test_indirect_vector_external_buffer.cpp b/test/test_indirect_vector_external_buffer.cpp index a699834a..0223f7ee 100644 --- a/test/test_indirect_vector_external_buffer.cpp +++ b/test/test_indirect_vector_external_buffer.cpp @@ -335,6 +335,22 @@ namespace CHECK_EQUAL(4U, *data2[3]); } + //************************************************************************* + TEST(test_constructor_two_parameter_same_type_not_iterator) + { + LookupInt lookup; + PoolInt pool; + + etl::indirect_vector_ext data(5, 7, lookup, pool); + + CHECK_EQUAL(5U, data.size()); + CHECK_EQUAL(7, data[0]); + CHECK_EQUAL(7, data[1]); + CHECK_EQUAL(7, data[2]); + CHECK_EQUAL(7, data[3]); + CHECK_EQUAL(7, data[4]); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_assignment) { From 0de445f737dd6425b0df37a282b8c258ad9ef7d1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 29 Oct 2026 14:27:37 +0100 Subject: [PATCH 3/3] Update atomic and fix other documentation (#1631) * Updated atomic documentation * Modified fetch_add and fetch_sub documentation * Various documantation fixes --------- Co-authored-by: John Wellbelove --- docs/maths/math.md | 4 +- docs/types/type-list.md | 4 +- docs/utilities/atomic.md | 276 ++++++++++++++++++++++++++++++++- docs/utilities/compare.md | 32 +++- docs/utilities/cyclic-value.md | 6 +- 5 files changed, 306 insertions(+), 16 deletions(-) diff --git a/docs/maths/math.md b/docs/maths/math.md index 3ec1cc88..71e6cc6d 100644 --- a/docs/maths/math.md +++ b/docs/maths/math.md @@ -38,7 +38,7 @@ ETL_CONSTEXPR bool is_zero(T value) **Returns** `true` if `value` is Zero. -## is_exactly_zero +## is_exactly_equal ```cpp template @@ -260,7 +260,7 @@ A wrapper around `::powl` in ``. If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns `::pow` with casts to `double`. otherwise -`::powl(base, intpart)` +`::powl(base, exponent)` ## modf diff --git a/docs/types/type-list.md b/docs/types/type-list.md index 04062874..1aa70f20 100644 --- a/docs/types/type-list.md +++ b/docs/types/type-list.md @@ -126,7 +126,6 @@ struct type_list_contains ``` **Description** Defines `value` as `true` if the `type_list` contains the specified type, otherwise `false`. -From: `C++17` --- @@ -135,7 +134,8 @@ template constexpr bool type_list_contains_v ``` **Description** -Defined as `true` if the `type_list` contains the specified type, otherwise `false`. +Defined as `true` if the `type_list` contains the specified type, otherwise `false`. +From: `C++17` ## type_list_has_duplicates_of ```cpp diff --git a/docs/utilities/atomic.md b/docs/utilities/atomic.md index 6319a78d..52c4d3f6 100644 --- a/docs/utilities/atomic.md +++ b/docs/utilities/atomic.md @@ -4,14 +4,14 @@ title: atomic {{< callout type="info">}} Header: `atomic.h` - Since: `TBC` + Since: `11.0.0` Similar to: [atomic](https://en.cppreference.com/cpp/atomic/atomic) {{< /callout >}} This header attempts to replicate some of the types from `std::atomic`. -If `ETL_CPP11_SUPPORTED` is defined as `1` in the profile then `etl::atomic` will be defined in terms of `std::atomic`. -Otherwise it will be implemented in terms of the built-in support, if available, from the compiler. For example, early GCC and Arm compilers will use the `__sync` built-ins. +If `ETL_USING_CPP11` and `ETL_USING_STL` are defined as `1` then `etl::atomic` will be defined in terms of `std::atomic`. +Otherwise it will be implemented in terms of the built-in support, if available, from the compiler. For example, early GCC and Arm compilers will use the `__sync` built-ins, later versions use `__atomic` built-ins. If a type cannot be handled by the built-ins then the types are wrapped by `etl::mutex`. @@ -19,3 +19,273 @@ If there is an ETL atomic type available for your platform then `ETL_HAS_ATOMIC` it will be set to `0`. From: `20.40.0` `etl::atomic` supports the `is_always_lock_free` property. + +For GCC and clang built-ins: +Integrals and pointers are lock free. +All other types are not lock free. + +--- + +```cpp +template +struct atomic; +``` +```cpp +template +struct atomic; +``` + +## Memory order + +```cpp +memory_order_relaxed +memory_order_consume +memory_order_acquire +memory_order_release +memory_order_acq_rel +memory_order_seq_cst +``` + +## Constructors + +```cpp +ETL_CONSTEXPR atomic() +``` +**Description** +Value initialises `T`. + +--- + +```cpp +ETL_CONSTEXPR atomic(T v) +``` +**Description** +Initialises `T` with `v`. + +## Assignment + +```cpp +T operator=(T v) +T operator=(T v) volatile +``` +**Description** +Stores the value 'v'. + +## Increment + +```cpp + T operator++() + T operator++() volatile +``` +**Description** +Pre-increments the atomic value by one. + +--- + +```cpp + T operator++(int) + T operator++(int) volatile + ``` +**Description** +Post-increments the atomic value by one. + + ## Decrement + ```cpp + T operator--() + T operator--() volatile + ``` +**Description** +Pre-decrements the atomic value by one. + +--- + + ```cpp + T operator--(int) + T operator--(int) volatile +``` +**Description** +Post-decrements the atomic value by one. + +## Add + +```cpp +T operator+=(T v) +T operator+=(T v) volatile +``` +**Description** +Adds to the atomic value. + +## Subtract +```cpp +T operator-=(T v) +T operator-=(T v) volatile +``` +**Description** +Subtracts from the atomic value. + +## And + +```cpp +T operator&=(T v) +T operator&=(T v) volatile +``` +**Description** +Performs bitwise AND with the atomic value. + +## Or + +```cpp +T operator|=(T v) +T operator|=(T v) volatile +``` +**Description** +Performs bitwise OR with the atomic value. + +## Exclusive or + +```cpp +T operator^=(T v) +T operator^=(T v) volatile +``` +**Description** +Performs bitwise XOR with the atomic value. + +## Conversion operator + +```cpp +operator T() const +operator T() volatile const +``` +**Description** +Loads a value from an atomic object. + +## Is lock free + +```cpp +bool is_lock_free() const +bool is_lock_free() const volatile +``` +**Description** +Checks if the atomic object is lock-free. + +## Store + +```cpp +void store(T v, etl::memory_order order = etl::memory_order_seq_cst) +void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically replaces the value of the atomic object with a non-atomic argument. + +## Load + +```cpp +T load(etl::memory_order order = etl::memory_order_seq_cst) const +T load(etl::memory_order order = etl::memory_order_seq_cst) const volatile +``` +**Description** +Atomically obtains the value of the atomic object. + +## Fetch add + +```cpp +T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` + +```cpp +T* fetch_add(ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) +T* fetch_add(ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically adds the argument to the value stored in the atomic object and obtains the value held previously. + +## Fetch subtract + +```cpp +T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` + +```cpp +T* fetch_sub(ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) +T* fetch_sub(ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically subtracts the argument to the value stored in the atomic object and obtains the value held previously. + +## Fetch or + +```cpp +T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically performs bitwise OR between the argument and the value of the atomic object and obtains the value held previously. + +## Fetch and + +```cpp +T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically performs bitwise AND between the argument and the value of the atomic object and obtains the value held previously. + +## Fetch exclusive or + +```cpp +T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically performs bitwise XOR between the argument and the value of the atomic object and obtains the value held previously. + +## Fetch max + +```cpp +T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically performs max between the argument and the value of the atomic object and obtains the value held previously. + +## Fetch min + +```cpp +T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) +T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically performs min between the argument and the value of the atomic object and obtains the value held previously. + +## Exchange + +```cpp +T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) +T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile +``` +**Description** +Atomically replaces the value of the atomic object and obtains the value held previously. + +## Compare exchange weak + +```cpp +bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) +bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile +bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) +bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile +``` +**Description** +Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. + +## Compare exchange strong + +```cpp +bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) +bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile +bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) +bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile +``` +**Description** +Atomically compares the value of the atomic object with non-atomic argument and performs atomic exchange if equal or atomic load if not. diff --git a/docs/utilities/compare.md b/docs/utilities/compare.md index 54d904e5..e4b647b7 100644 --- a/docs/utilities/compare.md +++ b/docs/utilities/compare.md @@ -28,54 +28,72 @@ second_argument_type; cmp_result; ``` -`cmp_result` has the values `Less`, `Equal`, and `Greater`. +`cmp_result` defines the values `Less`, `Equal`, and `Greater`. ## Members ```cpp static bool lt(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Less than. + **Return** -`true` if `lhs < rhs`, otherwise `false`. +`true` if `TLess()(lhs, rhs)`, otherwise `false`. --- ```cpp static bool lte(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Less than or equal. + **Return** -`true` if `lhs <= rhs`, otherwise `false`. +`true` if `lhs` is less than or equal to `rhs`, otherwise `false`. --- ```cpp static bool gt(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Greater than. + **Return** -`true` if `lhs > rhs`, otherwise `false`. +`true` if `lhs` is greater than `rhs`, otherwise `false`. --- ```cpp static bool gte(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Greater than or equal. + **Return** -`true` if `lhs >= rhs`, otherwise `false`. +`true` if `lhs` is greater than or equal to `rhs`, otherwise `false`. --- ```cpp static bool eq(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Equal. + **Return** -`true` if `lhs == rhs`, otherwise `false`. +`true` if neither value compares less than the other, otherwise `false`. --- ```cpp static bool ne(first_argument_type lhs, second_argument_type rhs) ``` +**Description** +Not equal. + **Return** -`true` if `lhs != rhs`, otherwise `false`. +`true` if if either value compare less than the other, otherwise `false`. --- diff --git a/docs/utilities/cyclic-value.md b/docs/utilities/cyclic-value.md index d29a7b42..b2554bd9 100644 --- a/docs/utilities/cyclic-value.md +++ b/docs/utilities/cyclic-value.md @@ -221,10 +221,12 @@ Checks equality of two cyclic values. Previously the first and last limits were also considered. <= 20.49.0 - `etl::cyclic_value<2, 7>(3) != etl::cyclic_value<2, 6>(3)` + `etl::cyclic_value(3) != etl::cyclic_value(3)` + `etl::cyclic_value(2, 7, 3) != etl::cyclic_value(2, 6, 3)` > 20.49.0 - `etl::cyclic_value<2, 7>(3) == etl::cyclic_value<2, 6>(3)` + `etl::cyclic_value(3) == etl::cyclic_value(3)` + `etl::cyclic_value(2, 7, 3) == etl::cyclic_value(2, 6, 3)` {{< /callout >}} ---