From 8c410135080ae37650bf535a6d0206cd1f89fd5b Mon Sep 17 00:00:00 2001 From: EminY <57416756+emincan2000@users.noreply.github.com> Date: Tue, 27 Oct 2026 20:50:37 +0300 Subject: [PATCH] Fix find and rfind position handling in string and string_view --- include/etl/basic_string.h | 32 ++++++++++++++++++++++---------- include/etl/string_view.h | 13 ++++++++++--- test/test_string_char.cpp | 27 +++++++++++++++++++++++++++ test/test_string_view.cpp | 21 +++++++++++++++++++++ 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/include/etl/basic_string.h b/include/etl/basic_string.h index 59db587a..01e3c5ef 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()) @@ -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 2cb20e37..d5d0bcd0 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..1d9cd145 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) { 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) {