From cf5e4b72e19818524bb81951723e251bec2ce624 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 19 Mar 2022 14:19:52 +0100 Subject: [PATCH] Core/StringExt: Add overloads of EndsWith --- include/Nazara/Core/StringExt.hpp | 3 ++ src/Nazara/Core/StringExt.cpp | 69 ++++++++++++++++++++++++++++- tests/Engine/Core/StringExtTest.cpp | 37 ++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/include/Nazara/Core/StringExt.hpp b/include/Nazara/Core/StringExt.hpp index 8e5522f7c..648a5a934 100644 --- a/include/Nazara/Core/StringExt.hpp +++ b/include/Nazara/Core/StringExt.hpp @@ -21,6 +21,9 @@ namespace Nz NAZARA_CORE_API std::size_t ComputeCharacterCount(const std::string_view& str); inline bool EndsWith(const std::string_view& str, const std::string_view& s); + NAZARA_CORE_API bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, CaseIndependent); + NAZARA_CORE_API bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, UnicodeAware); + NAZARA_CORE_API bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, CaseIndependent, UnicodeAware); NAZARA_CORE_API std::string FromUtf16String(const std::u16string_view& u16str); NAZARA_CORE_API std::string FromUtf32String(const std::u32string_view& u32str); diff --git a/src/Nazara/Core/StringExt.cpp b/src/Nazara/Core/StringExt.cpp index 97085effa..a30ca88ba 100644 --- a/src/Nazara/Core/StringExt.cpp +++ b/src/Nazara/Core/StringExt.cpp @@ -96,6 +96,67 @@ namespace Nz { return utf8::distance(str.data(), str.data() + str.size()); } + + bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, CaseIndependent) + { + NAZARA_USE_ANONYMOUS_NAMESPACE + + if (rhs.size() > lhs.size()) + return false; + + return std::equal(lhs.end() - rhs.size(), lhs.end(), rhs.begin(), rhs.end(), [](char c1, char c2) + { + return ToLower(c1) == ToLower(c2); + }); + } + + bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, UnicodeAware) + { + if (lhs.empty()) + return lhs == rhs; + else if (rhs.empty()) + return true; + + utf8::iterator it(lhs.data() + lhs.size() - rhs.size(), lhs.data() + lhs.size() - rhs.size(), lhs.data() + lhs.size()); + utf8::iterator it2(rhs.data(), rhs.data(), rhs.data() + rhs.size()); + do + { + if (it2.base() >= rhs.data() + rhs.size()) + return true; + + if (*it != *it2) + return false; + + ++it2; + } + while (*it++); + + return true; + } + + bool EndsWith(const std::string_view& lhs, const std::string_view& rhs, CaseIndependent, UnicodeAware) + { + if (lhs.empty()) + return lhs == rhs; + else if (rhs.empty()) + return true; + + utf8::iterator it(lhs.data() + lhs.size() - rhs.size(), lhs.data() + lhs.size() - rhs.size(), lhs.data() + lhs.size()); + utf8::iterator it2(rhs.data(), rhs.data(), rhs.data() + rhs.size()); + do + { + if (it2.base() >= rhs.data() + rhs.size()) + return true; + + if (Unicode::GetLowercase(*it) != Unicode::GetLowercase(*it2)) + return false; + + ++it2; + } + while (*it++); + + return true; + } std::string FromUtf16String(const std::u16string_view& u16str) { @@ -271,8 +332,10 @@ namespace Nz bool StartsWith(const std::string_view& lhs, const std::string_view& rhs, UnicodeAware) { - if (lhs.empty() || rhs.empty()) + if (lhs.empty()) return lhs == rhs; + else if (rhs.empty()) + return true; utf8::iterator it(lhs.data(), lhs.data(), lhs.data() + lhs.size()); utf8::iterator it2(rhs.data(), rhs.data(), rhs.data() + rhs.size()); @@ -293,8 +356,10 @@ namespace Nz bool StartsWith(const std::string_view& lhs, const std::string_view& rhs, CaseIndependent, UnicodeAware) { - if (lhs.empty() || rhs.empty()) + if (lhs.empty()) return lhs == rhs; + else if (rhs.empty()) + return true; utf8::iterator it(lhs.data(), lhs.data(), lhs.data() + lhs.size()); utf8::iterator it2(rhs.data(), rhs.data(), rhs.data() + rhs.size()); diff --git a/tests/Engine/Core/StringExtTest.cpp b/tests/Engine/Core/StringExtTest.cpp index 5de13d34e..618714b3f 100644 --- a/tests/Engine/Core/StringExtTest.cpp +++ b/tests/Engine/Core/StringExtTest.cpp @@ -7,11 +7,32 @@ SCENARIO("String", "[CORE][STRING]") WHEN("Checking if string ends with") { + CHECK(Nz::EndsWith("", "")); + CHECK(Nz::EndsWith("Nazara Engine", "")); CHECK(Nz::EndsWith("Nazara Engine", "Engine")); + CHECK_FALSE(Nz::EndsWith("Nazara Engine", "engine")); CHECK_FALSE(Nz::EndsWith("Nazara Engine", " ngine")); CHECK_FALSE(Nz::EndsWith("Nazara Engine", "NazaraEngine")); CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Nazara")); CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Sir Nazara van Engine")); + + CHECK(Nz::EndsWith("", "", Nz::CaseIndependent{})); + CHECK(Nz::EndsWith("Nazara Engine", "", Nz::CaseIndependent{})); + CHECK(Nz::EndsWith("Nazara Engine", "Engine", Nz::CaseIndependent{})); + CHECK(Nz::EndsWith("Nazara Engine", "engine", Nz::CaseIndependent{})); + CHECK(Nz::EndsWith("Nazara engine", "EnGiNe", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::EndsWith("Nazara Engine", " ngine", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::EndsWith("Nazara Engine", "NazaraEngine", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Nazara", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::EndsWith("Nazara Engine", "Sir Nazara van Engine", Nz::CaseIndependent{})); + + CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::UnicodeAware{})); + CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"R\u00E9", Nz::UnicodeAware{})); + CHECK_FALSE(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::UnicodeAware{})); + + CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::CaseIndependent{}, Nz::UnicodeAware{})); + CHECK(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"R\u00C9", Nz::CaseIndependent{}, Nz::UnicodeAware{})); + CHECK_FALSE(Nz::EndsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::CaseIndependent{}, Nz::UnicodeAware{})); } WHEN("Converting string back and forth") @@ -90,9 +111,25 @@ SCENARIO("String", "[CORE][STRING]") WHEN("Checking if string starts with") { + CHECK(Nz::StartsWith("Nazara Engine", "")); CHECK(Nz::StartsWith("Nazara Engine", "Nazara")); + CHECK_FALSE(Nz::StartsWith("Nazara Engine", "Navara")); + CHECK_FALSE(Nz::StartsWith("Nazara Engine", "NaZaRa")); + CHECK_FALSE(Nz::StartsWith("Nazara Engine", "Long long johnson")); + + CHECK(Nz::StartsWith("NAZARA Engine", "", Nz::CaseIndependent{})); + CHECK(Nz::StartsWith("NAZARA Engine", "Nazara", Nz::CaseIndependent{})); CHECK(Nz::StartsWith("NAZARA Engine", "NaZaRa", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::StartsWith("NAZARA Engine", "NavaRa", Nz::CaseIndependent{})); + CHECK_FALSE(Nz::StartsWith("NAZARA Engine", "Long long johnson", Nz::CaseIndependent{})); + + CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::UnicodeAware{})); + CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"L'\u00CEle", Nz::UnicodeAware{})); + CHECK_FALSE(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::UnicodeAware{})); + + CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"", Nz::CaseIndependent{}, Nz::UnicodeAware{})); CHECK(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"l'\u00EEle", Nz::CaseIndependent{}, Nz::UnicodeAware{})); + CHECK_FALSE(Nz::StartsWith(u8"L'\u00CEle de R\u00E9", u8"Long long j\u00F4hnson", Nz::CaseIndependent{}, Nz::UnicodeAware{})); } WHEN("Converting between lower and upper")