diff --git a/include/seastar/core/sstring.hh b/include/seastar/core/sstring.hh index 6b22fc189cb..7bb9ae68bd3 100644 --- a/include/seastar/core/sstring.hh +++ b/include/seastar/core/sstring.hh @@ -256,20 +256,18 @@ public: return npos; } - size_t find(const basic_sstring& s, size_t pos = 0) const noexcept { - const char_type* it = str() + pos; - const char_type* end = str() + size(); - const char_type* c_str = s.str(); - + size_t find(const char_type* c_str, size_t pos, size_t len2) const noexcept { + assert(c_str != nullptr || len2 == 0); if (pos > size()) { return npos; } - const size_t len2 = s.size(); if (len2 == 0) { return pos; } + const char_type* it = str() + pos; + const char_type* end = str() + size(); size_t len1 = end - it; if (len1 < len2) { return npos; @@ -296,6 +294,23 @@ public: } } + constexpr size_t find(const char_type* s, size_t pos = 0) const noexcept { + return find(s, pos, traits_type::length(s)); + } + + size_t find(const basic_sstring& s, size_t pos = 0) const noexcept { + return find(s.str(), pos, s.size()); + } + + template>, + int> = 0> + size_t find(const StringViewLike& sv_like, size_type pos = 0) const noexcept { + std::basic_string_view sv = sv_like; + return find(sv.data(), pos, sv.size()); + } + /** * find_last_of find the last occurrence of c in the string. * When pos is specified, the search only includes characters @@ -446,6 +461,29 @@ public: replace(p, p, beg, end); } + + /** + * Returns a read/write reference to the data at the first + * element of the string. + * This function shall not be called on empty strings. + */ + reference + front() noexcept { + assert(!empty()); + return *str(); + } + + /** + * Returns a read-only (constant) reference to the data at the first + * element of the string. + * This function shall not be called on empty strings. + */ + const_reference + front() const noexcept { + assert(!empty()); + return *str(); + } + /** * Returns a read/write reference to the data at the last * element of the string. @@ -558,6 +596,42 @@ public: } } + constexpr bool starts_with(std::basic_string_view sv) const noexcept { + return size() > sv.size() && compare(0, sv.size(), sv) == 0; + } + + constexpr bool starts_with(char_type c) const noexcept { + return !empty() && traits_type::eq(front(), c); + } + + constexpr bool starts_with(const char_type* s) const noexcept { + return starts_with(std::basic_string_view(s)); + } + + constexpr bool ends_with(std::basic_string_view sv) const noexcept { + return size() > sv.size() && compare(size() - sv.size(), npos, sv) == 0; + } + + constexpr bool ends_with(char_type c) const noexcept { + return !empty() && traits_type::eq(back(), c); + } + + constexpr bool ends_with(const char_type* s) const noexcept { + return ends_with(std::basic_string_view(s)); + } + + constexpr bool contains(std::basic_string_view sv) const noexcept { + return find(sv) != npos; + } + + constexpr bool contains(char_type c) const noexcept { + return find(c) != npos; + } + + constexpr bool contains(const char_type* s) const noexcept { + return find(s) != npos; + } + void swap(basic_sstring& x) noexcept { contents tmp; tmp = x.u; diff --git a/tests/unit/sstring_test.cc b/tests/unit/sstring_test.cc index acc009a6862..2cc78383891 100644 --- a/tests/unit/sstring_test.cc +++ b/tests/unit/sstring_test.cc @@ -25,6 +25,7 @@ #include #include +using namespace std::literals; using namespace seastar; BOOST_AUTO_TEST_CASE(test_make_sstring) { @@ -51,6 +52,12 @@ BOOST_AUTO_TEST_CASE(test_add_literal_to_sstring) { BOOST_REQUIRE_EQUAL("x" + sstring("y"), sstring("xy")); } +BOOST_AUTO_TEST_CASE(test_front) { + sstring s("abcde"); + BOOST_CHECK_EQUAL(s.front(), 'a'); + BOOST_CHECK_EQUAL(std::as_const(s).front(), 'a'); +} + BOOST_AUTO_TEST_CASE(test_find_sstring) { BOOST_REQUIRE_EQUAL(sstring("abcde").find('b'), 1u); BOOST_REQUIRE_EQUAL(sstring("babcde").find('b',1), 2u); @@ -99,6 +106,52 @@ BOOST_AUTO_TEST_CASE(test_str_not_find_sstring) { BOOST_REQUIRE_EQUAL(sstring("abc").find("abcde"), sstring::npos); } +BOOST_AUTO_TEST_CASE(test_str_starts_with) { + BOOST_CHECK(sstring("abcdefg").starts_with("ab"sv)); + BOOST_CHECK(sstring("abcde").starts_with('a')); + BOOST_CHECK(sstring("abcde").starts_with("ab")); + BOOST_CHECK(sstring("abcdefg").starts_with("")); + + BOOST_CHECK(!sstring("abcde").starts_with("cde")); + BOOST_CHECK(!sstring("abcde").starts_with('b')); + BOOST_CHECK(!sstring("abcdefg").starts_with("cde"sv)); + BOOST_CHECK(!sstring("abcdefg").starts_with("ab\0"sv)); +} + +BOOST_AUTO_TEST_CASE(test_str_ends_with) { + BOOST_CHECK(sstring("abcdefg").ends_with("efg"sv)); + BOOST_CHECK(sstring("abcde").ends_with('e')); + BOOST_CHECK(sstring("abcde").ends_with("de")); + BOOST_CHECK(sstring("abcdefg").ends_with("")); + + BOOST_CHECK(!sstring("abcde").ends_with("abc")); + BOOST_CHECK(!sstring("abcde").ends_with('b')); + BOOST_CHECK(!sstring("abcdefg").ends_with("abc"sv)); + BOOST_CHECK(!sstring("abcdefg").ends_with("efg\0"sv)); +} + +BOOST_AUTO_TEST_CASE(test_str_contains) { + BOOST_CHECK(sstring("abcde").starts_with("ab"sv)); + BOOST_CHECK(sstring("abcde").starts_with('a')); + BOOST_CHECK(sstring("abcde").starts_with("ab")); + BOOST_CHECK(sstring("abcde").starts_with("")); + + BOOST_CHECK(sstring("abcde").contains("bc"sv)); + BOOST_CHECK(sstring("abcde").contains("bc")); + BOOST_CHECK(sstring("abcde").contains('c')); + + BOOST_CHECK(sstring("abcde").contains("de"sv)); + BOOST_CHECK(sstring("abcde").contains("de")); + + BOOST_CHECK(!sstring("abcde").contains("bad")); + BOOST_CHECK(!sstring("abcde").contains("bce")); + BOOST_CHECK(!sstring("abcde").contains("x")); + BOOST_CHECK(!sstring("abcde").contains('x')); + BOOST_CHECK(!sstring("abcde").contains("ab\0"sv)); + BOOST_CHECK(!sstring("abcde").contains("bc\0"sv)); + BOOST_CHECK(!sstring("abcde").contains("de\0"sv)); +} + BOOST_AUTO_TEST_CASE(test_substr_sstring) { BOOST_REQUIRE_EQUAL(sstring("abcde").substr(1,2), "bc"); BOOST_REQUIRE_EQUAL(sstring("abc").substr(1,2), "bc");