diff --git a/include/cista/containers/string.h b/include/cista/containers/string.h index af11da61..4b82369f 100644 --- a/include/cista/containers/string.h +++ b/include/cista/containers/string.h @@ -336,6 +336,62 @@ struct generic_string { return *this; } + constexpr bool starts_with(generic_string const& s) const noexcept { + return starts_with(s.data(), static_cast(s.size())); + } + constexpr bool starts_with(std::string_view const& sv) const noexcept { + return starts_with(sv.data(), static_cast(sv.size())); + } + constexpr bool starts_with(char const* s) const noexcept { + return starts_with(s, mstrlen(s)); + } + constexpr bool starts_with(char const* s, msize_t size_s) const noexcept { + if (size_s > size()) { + return false; + } + if (size_s == 0) { + return true; + } + if (empty()) { + return false; + } + return !std::memcmp(s, data(), size_s); + } + constexpr bool starts_with(char ch) const noexcept { + if (empty()) { + return false; + } + return data()[0] == ch; + } + + constexpr bool ends_with(generic_string const& s) const noexcept { + return ends_with(s.data(), static_cast(s.size())); + } + constexpr bool ends_with(std::string_view const& sv) const noexcept { + return ends_with(sv.data(), static_cast(sv.size())); + } + constexpr bool ends_with(char const* s) const noexcept { + return ends_with(s, mstrlen(s)); + } + constexpr bool ends_with(char const* s, msize_t size_s) const noexcept { + if (size_s > size()) { + return false; + } + if (size_s == 0) { + return true; + } + if (empty()) { + return false; + } + return !std::memcmp(s, data() + size() - size_s, size_s); + } + constexpr bool ends_with(char ch) const noexcept { + if (size() == 0) { + return false; + } + return data()[size() - 1] == ch; + } + struct heap { bool is_short_{false}; bool self_allocated_{false}; diff --git a/include/cista/next_power_of_2.h b/include/cista/next_power_of_2.h index f1380996..37333195 100644 --- a/include/cista/next_power_of_2.h +++ b/include/cista/next_power_of_2.h @@ -8,9 +8,13 @@ constexpr TemplateSizeType next_power_of_two(TemplateSizeType n) noexcept { n |= n >> 1U; n |= n >> 2U; n |= n >> 4U; - n |= n >> 8U; - n |= n >> 16U; - if constexpr (sizeof(TemplateSizeType) > 32U) { + if constexpr (sizeof(TemplateSizeType) > 1U) { + n |= n >> 8U; + } + if constexpr (sizeof(TemplateSizeType) > 2U) { + n |= n >> 16U; + } + if constexpr (sizeof(TemplateSizeType) > 4U) { n |= n >> 32U; } ++n; diff --git a/test/string_test.cc b/test/string_test.cc index 7c6d839b..53bfe9b1 100644 --- a/test/string_test.cc +++ b/test/string_test.cc @@ -104,3 +104,79 @@ TEST_CASE("string hash") { auto h = cista::hash(str, cista::BASE_HASH); CHECK(cista::BASE_HASH == h); } + +TEST_CASE("string starts_with") { + string s = "abacaba"; + + CHECK(s.starts_with("abac") == true); + CHECK(s.starts_with("abacaba") == true); + CHECK(s.starts_with("abacaba_") == false); + CHECK(s.starts_with("a") == true); + CHECK(s.starts_with("") == true); + CHECK(s.starts_with("abad") == false); + + CHECK(s.starts_with(string{"abac"}) == true); + CHECK(s.starts_with(string{"abacaba"}) == true); + CHECK(s.starts_with(string{"abacaba_"}) == false); + CHECK(s.starts_with(string{"a"}) == true); + CHECK(s.starts_with(string{""}) == true); + CHECK(s.starts_with(string{"abad"}) == false); + + CHECK(s.starts_with(std::string{"abac"}) == true); + CHECK(s.starts_with(std::string{"abacaba"}) == true); + CHECK(s.starts_with(std::string{"abacaba_"}) == false); + CHECK(s.starts_with(std::string{"a"}) == true); + CHECK(s.starts_with(std::string{""}) == true); + CHECK(s.starts_with(std::string{"\0", 1}) == false); + CHECK(s.starts_with(std::string{"abad"}) == false); + + CHECK(s.starts_with(std::string_view{"abac"}) == true); + CHECK(s.starts_with(std::string_view{"abacaba"}) == true); + CHECK(s.starts_with(std::string_view{"abacaba_"}) == false); + CHECK(s.starts_with(std::string_view{"a"}) == true); + CHECK(s.starts_with(std::string_view{""}) == true); + CHECK(s.starts_with(std::string_view{"\0", 1}) == false); + CHECK(s.starts_with(std::string_view{"abad"}) == false); + + CHECK(s.starts_with('a') == true); + CHECK(s.starts_with('b') == false); + CHECK(s.starts_with('\0') == false); +} + +TEST_CASE("string ends_with") { + string s = "abacaba"; + + CHECK(s.ends_with("caba") == true); + CHECK(s.ends_with("abacaba") == true); + CHECK(s.ends_with("abacaba_") == false); + CHECK(s.ends_with("a") == true); + CHECK(s.ends_with("") == true); + CHECK(s.ends_with("daba") == false); + + CHECK(s.ends_with(string{"caba"}) == true); + CHECK(s.ends_with(string{"abacaba"}) == true); + CHECK(s.ends_with(string{"abacaba_"}) == false); + CHECK(s.ends_with(string{"a"}) == true); + CHECK(s.ends_with(string{""}) == true); + CHECK(s.ends_with(string{"daba"}) == false); + + CHECK(s.ends_with(std::string{"caba"}) == true); + CHECK(s.ends_with(std::string{"abacaba"}) == true); + CHECK(s.ends_with(std::string{"abacaba_"}) == false); + CHECK(s.ends_with(std::string{"a"}) == true); + CHECK(s.ends_with(std::string{""}) == true); + CHECK(s.ends_with(std::string{"\0", 1}) == false); + CHECK(s.ends_with(std::string{"daba"}) == false); + + CHECK(s.ends_with(std::string_view{"caba"}) == true); + CHECK(s.ends_with(std::string_view{"abacaba"}) == true); + CHECK(s.ends_with(std::string_view{"abacaba_"}) == false); + CHECK(s.ends_with(std::string_view{"a"}) == true); + CHECK(s.ends_with(std::string_view{""}) == true); + CHECK(s.ends_with(std::string_view{"\0", 1}) == false); + CHECK(s.ends_with(std::string_view{"daba"}) == false); + + CHECK(s.ends_with('a') == true); + CHECK(s.ends_with('b') == false); + CHECK(s.ends_with('\0') == false); +}