From 6be29a14fdeb2e57db06f6065121e2415a66adce Mon Sep 17 00:00:00 2001 From: fhgffy <102001626+fhgffy@users.noreply.github.com> Date: Mon, 5 Oct 2026 02:25:25 -0400 Subject: [PATCH] Fix find of an empty substring at the end of a string_view --- include/nonstd/string_view.hpp | 3 +- test/string-view.t.cpp | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/nonstd/string_view.hpp b/include/nonstd/string_view.hpp index 65d31ee..1a43919 100644 --- a/include/nonstd/string_view.hpp +++ b/include/nonstd/string_view.hpp @@ -873,7 +873,8 @@ class basic_string_view nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return assert( v.size() == 0 || v.data() != nssv_nullptr ) - , pos >= size() + , v.empty() && pos <= size() ? pos + : pos >= size() ? npos : to_pos( #if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER detail::search( substr(pos), v ) diff --git a/test/string-view.t.cpp b/test/string-view.t.cpp index b8ae9c3..4be6176 100644 --- a/test/string-view.t.cpp +++ b/test/string-view.t.cpp @@ -584,6 +584,65 @@ CASE( "string_view: Allows to search for a C-string substring, starting at posit EXPECT( sv.find("world", 7 ) == string_view::npos ); } +CASE( "string_view: Finds empty substrings through size() via find()" ) +{ + string_view views[] = { string_view(), string_view(""), string_view("abc") }; + + for ( size_type i = 0; i < sizeof( views ) / sizeof( views[0] ); ++i ) + { + string_view sv = views[i]; + size_type positions[] = { 0, sv.size() / 2, sv.size(), sv.size() + 1, string_view::npos }; + + for ( size_type j = 0; j < sizeof( positions ) / sizeof( positions[0] ); ++j ) + { + size_type pos = positions[j]; + size_type expected = pos <= sv.size() ? pos : string_view::npos; + + EXPECT( sv.find( string_view(), pos ) == expected ); + EXPECT( sv.find( string_view(""), pos ) == expected ); + EXPECT( sv.find( "", pos ) == expected ); + EXPECT( sv.find( "ignored", pos, 0 ) == expected ); + } + } +} + +CASE( "string_view: Does not find nonempty substrings at or past size() via find()" ) +{ + string_view sv("abc"); + + EXPECT( sv.find( string_view("c"), sv.size() ) == string_view::npos ); + EXPECT( sv.find( "c", sv.size() ) == string_view::npos ); + EXPECT( sv.find( "c", sv.size(), 1 ) == string_view::npos ); + EXPECT( sv.find( 'c', sv.size() ) == string_view::npos ); + EXPECT( sv.find( "c", sv.size() + 1 ) == string_view::npos ); + EXPECT( sv.find( "c", string_view::npos ) == string_view::npos ); +} + +CASE( "string_view: Finds empty wide substrings at size() via find()" ) +{ + nonstd::wstring_view sv(L"abc", 3); + + EXPECT( nonstd::wstring_view().find( nonstd::wstring_view() ) == size_type( 0 ) ); + EXPECT( sv.find( nonstd::wstring_view(), sv.size() ) == sv.size() ); + EXPECT( sv.find( L"", sv.size() ) == sv.size() ); + EXPECT( sv.find( L"ignored", sv.size(), 0 ) == sv.size() ); + EXPECT( sv.find( L"", sv.size() + 1 ) == nonstd::wstring_view::npos ); +} + +CASE( "string_view: Allows to constexpr-find empty substrings via find() (C++14)" ) +{ +#if nssv_STD_SV_OR( nssv_HAVE_CONSTEXPR_14 ) + static_assert( string_view().find( string_view() ) == 0, "" ); + static_assert( string_view( "abc" ).find( string_view(), 3 ) == 3, "" ); + static_assert( string_view( "abc" ).find( "", 3 ) == 3, "" ); + static_assert( string_view( "abc" ).find( "ignored", 3, 0 ) == 3, "" ); + static_assert( string_view( "abc" ).find( "", 4 ) == string_view::npos, "" ); + static_assert( string_view( "abc" ).find( "", string_view::npos ) == string_view::npos, "" ); +#else + EXPECT( !!"C++14 constexpr is not available (no C++14)" ); +#endif +} + CASE( "string_view: Allows to search backwards for a string_view substring, starting at position pos (default: npos) via rfind(), (1)" ) { char hello[] = "hello world";