diff --git a/.gitignore b/.gitignore index 0455c1a..478a1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,8 @@ # Visual Studio specifics .vs x64 +Debug +Release + +# CppStrings specifics +archive diff --git a/cpp-strings-tests/cpp-strings-tests.cpp b/cpp-strings-tests/cpp-strings-tests.cpp new file mode 100644 index 0000000..e0f3236 --- /dev/null +++ b/cpp-strings-tests/cpp-strings-tests.cpp @@ -0,0 +1,5695 @@ +/** + Library cppstrings + "What if c++ strings where as easy to use as Python strings?" + Unit tests part of the library. + + Copyright (C) 2025 Philippe Schmouker + contact - ph (dot) schmouker (at) gmail (dot) com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "CppUnitTest.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +#include "cppstrings.h" + + +namespace cppstringstests +{ + + //===== PART 1 ============================================ + TEST_CLASS(cppstringstests_PART_1) + { + public: + + TEST_METHOD(_cs) + { + using namespace pcs; + using namespace pcs; + auto abcd = "abcD"_cs; + auto wabcd = L"abcD"_cs; + Assert::AreEqual(abcd.c_str(), pcs::CppString(abcd).c_str()); + Assert::AreEqual(wabcd.c_str(), pcs::CppWString(wabcd).c_str()); + } + + TEST_METHOD(is_alpha) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isalpha(ch), pcs::is_alpha(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswalpha(ch), pcs::is_alpha(ch)); + } + + TEST_METHOD(is_ascii) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual(ch < 128, pcs::is_ascii(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual(int(ch) < 128, pcs::is_ascii(ch)); + } + + TEST_METHOD(is_decimal) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isdigit(ch), pcs::is_decimal(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswdigit(ch), pcs::is_decimal(ch)); + } + + TEST_METHOD(is_id_continue) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isdigit(ch) || (const bool)std::isalpha(ch) || ch == '_', pcs::is_id_continue(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswdigit(ch) || (const bool)std::iswalpha(ch) || ch == L'_', pcs::is_id_continue(ch)); + } + + TEST_METHOD(is_id_start) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isalpha(ch) || ch == '_', pcs::is_id_start(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswalpha(ch) || ch == L'_', pcs::is_id_start(ch)); + } + + TEST_METHOD(is_lower) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::islower(static_cast(ch)), pcs::is_lower(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswlower(ch), pcs::is_lower(ch)); + } + + TEST_METHOD(is_printable) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isprint(static_cast(ch)), pcs::is_printable(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswprint(ch), pcs::is_printable(ch)); + } + + TEST_METHOD(is_punctuation) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::ispunct(static_cast(ch)), pcs::is_punctuation(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswpunct(ch), pcs::is_punctuation(ch)); + } + + TEST_METHOD(is_space) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isspace(static_cast(ch)), pcs::is_space(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswspace(ch), pcs::is_space(ch)); + } + + TEST_METHOD(is_upper) + { + for (int ch = 0; ch <= 255; ++ch) + Assert::AreEqual((const bool)std::isupper(static_cast(ch)), pcs::is_upper(char(ch))); + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) + Assert::AreEqual((const bool)std::iswupper(ch), pcs::is_upper(ch)); + } + + TEST_METHOD(swap_case) + { + for (int ch = 0; ch <= 255; ++ch) { + const char sw_ch = pcs::swap_case(static_cast(ch)); + if (std::islower(ch)) + Assert::IsTrue((const bool)std::isupper(static_cast(sw_ch))); + else if (std::isupper(ch)) + Assert::IsTrue((const bool)std::islower(static_cast(sw_ch)), std::format(L"ch {}, sw_ch {}", ch, sw_ch).c_str()); + else + Assert::AreEqual(sw_ch, static_cast(ch)); + } + + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) { + const wchar_t sw_ch = pcs::swap_case(ch); + if (std::islower(ch)) + Assert::IsTrue((const bool)std::isupper(sw_ch)); + else if (std::isupper(ch)) + Assert::IsTrue((const bool)std::islower(sw_ch), std::format(L"ch {}, sw_ch {}", ch, sw_ch).c_str()); + else + Assert::AreEqual(sw_ch, ch); + } + } + + TEST_METHOD(to_lower) + { + for (int ch = 0; ch <= 255; ++ch) { + const char l_ch = pcs::to_lower(static_cast(ch)); + if (std::isupper(ch)) + Assert::IsTrue(std::islower(l_ch)); + else + Assert::AreEqual(l_ch, static_cast(ch)); + } + + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) { + const wchar_t l_ch = pcs::to_lower(ch); + if (std::isupper(ch)) + Assert::IsTrue(std::iswlower(l_ch)); + else + Assert::AreEqual(l_ch, ch); + } + } + + TEST_METHOD(to_upper) + { + for (int ch = 0; ch <= 255; ++ch) { + const char l_ch = pcs::to_upper(static_cast(ch)); + if (std::islower(ch)) + Assert::IsTrue(std::isupper(l_ch)); + else + Assert::AreEqual(l_ch, static_cast(ch)); + } + + for (wchar_t ch = 0; ch < L'\uffff'; ++ch) { + const wchar_t l_ch = pcs::to_upper(ch); + if (std::islower(ch)) + Assert::IsTrue(std::iswupper(l_ch)); + else + Assert::AreEqual(l_ch, ch); + } + } + }; + + + //===== PART 2 ============================================ + TEST_CLASS(cppstringstests_PART_2) + { + public: + + TEST_METHOD(constructor_01) + { + using namespace pcs; + std::map table{ {'a', "b"_cs}, {'b', "a"_cs} }; + Assert::AreEqual("b"_cs.c_str(), table['a'].c_str()); + Assert::AreEqual("a"_cs.c_str(), table['b'].c_str()); + + std::map wtable{ {L'a', L"b"_cs}, {L'b', L"a"_cs} }; + Assert::AreEqual(L"b"_cs.c_str(), wtable['a'].c_str()); + Assert::AreEqual(L"a"_cs.c_str(), wtable['b'].c_str()); + } + + TEST_METHOD(constructor_02) + { + pcs::CppString keys("abcdE"); + pcs::CppString values("ABCDe"); + pcs::CppString::TransTable t(keys, values); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString('D').c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString('e').c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wt(pcs::CppWString(L"abcdE"), pcs::CppWString(L"ABCDe")); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString(L'D').c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString(L'e').c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(constructor_03) + { + pcs::CppString::TransTable t(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wt(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(constructor_04) + { + pcs::CppString::TransTable t(pcs::CppString("abc"), { pcs::CppString("AA"), pcs::CppString("BBB"), pcs::CppString("C") }); + Assert::AreEqual(pcs::CppString("AA").c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString("BBB").c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString('z').c_str(), t['z'].c_str()); + + pcs::CppWString::TransTable wt(pcs::CppWString(L"abc"), { pcs::CppWString(L"AA"), pcs::CppWString(L"BBB"), pcs::CppWString(L"C") }); + Assert::AreEqual(pcs::CppWString(L"AA").c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L"BBB").c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L"C").c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString(L'9').c_str(), wt[L'9'].c_str()); + } + + TEST_METHOD(constructor_05) + { + using namespace pcs; + pcs::CppString::TransTable t(pcs::CppString("abc"), { pcs::CppString("AA"), pcs::CppString("BBB"), pcs::CppString("C") }, "dE"_cs); + Assert::AreEqual(pcs::CppString("AA").c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString("BBB").c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('z').c_str(), t['z'].c_str()); + + pcs::CppWString::TransTable wt(pcs::CppWString(L"abc"), { pcs::CppWString(L"AA"), pcs::CppWString(L"BBB"), pcs::CppWString(L"C") }, L"dE"_cs); + Assert::AreEqual(pcs::CppWString(L"AA").c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L"BBB").c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L"C").c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'e').c_str(), wt[L'e'].c_str()); + } + + TEST_METHOD(constructor_06) + { + pcs::CppString::TransTable t("abC", "ABc"); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['C'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['c'].c_str()); + + pcs::CppWString::TransTable wt(L"abC", L"ABc"); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'C'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'c'].c_str()); + } + + TEST_METHOD(constructor_07) + { + pcs::CppString::TransTable t("abc", "ABC", "dE"); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('e').c_str(), t['e'].c_str()); + + pcs::CppWString::TransTable wt(L"abc", L"ABC", L"dE"); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'A'].c_str()); + } + + TEST_METHOD(constructor_08) + { + std::string keys("abC"); + std::vector values{ "AA", "BBB", "c" }; + pcs::CppString::TransTable t(keys.begin(), keys.end(), values.begin(), values.end()); + Assert::AreEqual(pcs::CppString("AA").c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString("BBB").c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['C'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['c'].c_str()); + + std::wstring wkeys(L"abC"); + std::vector wvalues{ L"AA", L"BBB", L"c" }; + pcs::CppWString::TransTable wt(wkeys.begin(), wkeys.end(), wvalues.begin(), wvalues.end()); + Assert::AreEqual(pcs::CppWString(L"AA").c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L"BBB").c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'C'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'c'].c_str()); + } + + TEST_METHOD(constructor_09) + { + std::string keys("abC"); + std::vector values{ "AA", "BBB", "c" }; + std::string not_translated("dE"); + pcs::CppString::TransTable t(keys.begin(), keys.end(), values.begin(), values.end(), not_translated.cbegin(), not_translated.cend()); + Assert::AreEqual(pcs::CppString("AA").c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString("BBB").c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['C'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('c').c_str(), t['c'].c_str()); + + std::wstring wkeys(L"abC"); + std::vector wvalues{ L"AA", L"BBB", L"c" }; + std::wstring wnot_translated(L"dE"); + pcs::CppWString::TransTable wt(wkeys.begin(), wkeys.end(), wvalues.begin(), wvalues.end(), wnot_translated.cbegin(), wnot_translated.cend()); + Assert::AreEqual(pcs::CppWString(L"AA").c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L"BBB").c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'C'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'c').c_str(), wt[L'c'].c_str()); + } + + TEST_METHOD(constructor_empty) + { + pcs::CppString::TransTable t; + Assert::IsTrue(t.get_table().empty()); + + pcs::CppWString::TransTable wt; + Assert::IsTrue(wt.get_table().empty()); + } + + TEST_METHOD(constructor_copy) + { + pcs::CppString::TransTable ct(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + pcs::CppString::TransTable t(ct); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wct(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + pcs::CppWString::TransTable wt(wct); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(constructor_move) + { + pcs::CppString::TransTable mt(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + pcs::CppString::TransTable t(std::move(mt)); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wmt(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + pcs::CppWString::TransTable wt(std::move(wmt)); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(assign_copy) + { + pcs::CppString::TransTable ct(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + pcs::CppString::TransTable t = ct; + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wct(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + pcs::CppWString::TransTable wt = wct; + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(assign_move) + { + pcs::CppString::TransTable mt(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + pcs::CppString::TransTable t = std::move(mt); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wmt(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + pcs::CppWString::TransTable wt = std::move(wmt); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(assign_map) + { + pcs::CppString::TransTable ct(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + pcs::CppString::TransTable t = ct.get_table(); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString('f').c_str(), t['f'].c_str()); + + pcs::CppWString::TransTable wct(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + pcs::CppWString::TransTable wt = wct.get_table(); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'f').c_str(), wt[L'f'].c_str()); + } + + TEST_METHOD(indexing) + { + pcs::CppString::TransTable t(pcs::CppString("abc"), pcs::CppString("ABC"), pcs::CppString("dE")); + Assert::AreEqual(pcs::CppString('A').c_str(), t['a'].c_str()); + Assert::AreEqual(pcs::CppString('B').c_str(), t['b'].c_str()); + Assert::AreEqual(pcs::CppString('C').c_str(), t['c'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['d'].c_str()); + Assert::AreEqual(pcs::CppString().c_str(), t['E'].c_str()); + Assert::AreEqual(pcs::CppString(',').c_str(), t[','].c_str()); + + pcs::CppWString::TransTable wt(pcs::CppWString(L"abc"), pcs::CppWString(L"ABC"), pcs::CppWString(L"dE")); + Assert::AreEqual(pcs::CppWString(L'A').c_str(), wt[L'a'].c_str()); + Assert::AreEqual(pcs::CppWString(L'B').c_str(), wt[L'b'].c_str()); + Assert::AreEqual(pcs::CppWString(L'C').c_str(), wt[L'c'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'd'].c_str()); + Assert::AreEqual(pcs::CppWString().c_str(), wt[L'E'].c_str()); + Assert::AreEqual(pcs::CppWString(L'\n').c_str(), wt[L'\n'].c_str()); + } + }; + + + //===== PART 3 ============================================ + TEST_CLASS(cppstringstests_PART_3) + { + public: + + TEST_METHOD(constructor_empty) + { + pcs::CppString s; + Assert::IsTrue(s.empty()); + + pcs::CppWString ws; + Assert::IsTrue(ws.empty()); + } + + TEST_METHOD(constructor_copy) + { + pcs::CppString cs("ABCde"); + pcs::CppString s(cs); + Assert::AreEqual(cs.c_str(), s.c_str()); + + pcs::CppWString wcs(L"abcDE"); + pcs::CppWString ws(wcs); + Assert::AreEqual(wcs.c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_move) + { + pcs::CppString cs("ABCde"); + pcs::CppString s(std::move(cs)); + Assert::AreEqual(pcs::CppString("ABCde").c_str(), s.c_str()); + + pcs::CppWString wcs(L"abcDE"); + pcs::CppWString ws(std::move(wcs)); + Assert::AreEqual(pcs::CppWString(L"abcDE").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_06) + { + pcs::CppString s(10, '-'); + Assert::AreEqual(std::size_t(10), s.size()); + Assert::AreEqual(pcs::CppString("----------").c_str(), s.c_str()); + + pcs::CppWString ws(8, '='); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"========").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_07) + { + pcs::CppString cs("abcDEfgh"); + pcs::CppString s(cs, 3); + Assert::AreEqual(std::size_t(5), s.size()); + Assert::AreEqual(pcs::CppString("DEfgh").c_str(), s.c_str()); + + pcs::CppWString wcs(L"ABCdefGH"); + pcs::CppWString ws(wcs, 4); + Assert::AreEqual(std::size_t(4), ws.size()); + Assert::AreEqual(pcs::CppWString(L"efGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_08) + { + pcs::CppString cs("abcDEfgh"); + pcs::CppString s(cs, 3, 2); + Assert::AreEqual(std::size_t(2), s.size()); + Assert::AreEqual(pcs::CppString("DE").c_str(), s.c_str()); + + pcs::CppWString wcs(L"ABCdefGH"); + pcs::CppWString ws(wcs, 4, 6); + Assert::AreEqual(std::size_t(4), ws.size()); + Assert::AreEqual(pcs::CppWString(L"efGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_09) + { + pcs::CppString s("abcDEfgh"); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + pcs::CppWString ws(L"ABCdefGH"); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + + pcs::CppString s0(nullptr); + Assert::AreEqual(std::size_t(0), s0.size()); + Assert::AreEqual("", s0.c_str()); + + pcs::CppWString ws0(nullptr); + Assert::AreEqual(std::size_t(0), ws0.size()); + Assert::AreEqual(L"", ws0.c_str()); + } + + TEST_METHOD(constructor_10) + { + pcs::CppString s("abcDEfgh", 5); + Assert::AreEqual(std::size_t(5), s.size()); + Assert::AreEqual(pcs::CppString("abcDE").c_str(), s.c_str()); + + pcs::CppWString ws(L"ABCdefGH", 7); + Assert::AreEqual(std::size_t(7), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefG").c_str(), ws.c_str()); + + pcs::CppString s0(nullptr, 0); + Assert::AreEqual(std::size_t(0), s0.size()); + Assert::AreEqual("", s0.c_str()); + + pcs::CppWString ws0(nullptr, 7); + Assert::AreEqual(std::size_t(7), ws0.size()); + Assert::AreEqual(L"", ws0.c_str()); + } + + TEST_METHOD(constructor_11) + { + pcs::CppString s({ 'a', 'b', 'c', 'D' }); + Assert::AreEqual(std::size_t(4), s.size()); + Assert::AreEqual(pcs::CppString("abcD").c_str(), s.c_str()); + + pcs::CppWString ws({ L'A', L'B', L'C', L'd', L'e' }); + Assert::AreEqual(std::size_t(5), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCde").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_12) + { + std::string cs("abcDEfgh"); + pcs::CppString s(cs); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + std::wstring wcs(L"ABCdefGH"); + pcs::CppWString ws(wcs); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_13) + { + std::string cs("abcDEfgh"); + pcs::CppString s(cs, cs.get_allocator()); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + std::wstring wcs(L"ABCdefGH"); + pcs::CppWString ws(wcs, wcs.get_allocator()); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_14) + { + std::string cs("abcDEfgh"); + pcs::CppString s(std::move(cs)); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + std::wstring wcs(L"ABCdefGH"); + pcs::CppWString ws(std::move(wcs)); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_15) + { + std::string cs("abcDEfgh"); + pcs::CppString s(std::move(cs), cs.get_allocator()); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + std::wstring wcs(L"ABCdefGH"); + pcs::CppWString ws(std::move(wcs), wcs.get_allocator()); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_16) + { + std::string cs("abcDEfgh"); + pcs::CppString s(cs.cbegin(), cs.cend()); + Assert::AreEqual(std::size_t(8), s.size()); + Assert::AreEqual(pcs::CppString("abcDEfgh").c_str(), s.c_str()); + + std::wstring wcs(L"ABCdefGH"); + pcs::CppWString ws(wcs.begin(), wcs.end()); + Assert::AreEqual(std::size_t(8), ws.size()); + Assert::AreEqual(pcs::CppWString(L"ABCdefGH").c_str(), ws.c_str()); + } + + TEST_METHOD(constructor_19) + { + pcs::CppString s('z'); + Assert::AreEqual(std::size_t(1), s.size()); + Assert::AreEqual(pcs::CppString("z").c_str(), s.c_str()); + + pcs::CppWString ws(L'Z'); + Assert::AreEqual(std::size_t(1), ws.size()); + Assert::AreEqual(pcs::CppWString(L"Z").c_str(), ws.c_str()); + } + }; + + + //===== PART 4 ======================================== + TEST_CLASS(cppstringstests_PART_4) + { + public: + + TEST_METHOD(slice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::Slice slc(0, 5, 1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::Slice slc(0, 5UL, char(1)); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::Slice slc(0, 11); // Slice here defaults to Slice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::Slice slc(0, 3); // Slice here defaults to Slice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::Slice slc(0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + ; + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::Slice slc; + + long index{ slc.begin(txt) }; + Assert::AreEqual(long(0), index); + + long k{ 0 }; + for (long index = slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + + k = 0; + for (long index = slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + } + + { + pcs::Slice slc(-1, 0, -1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + + k = txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + } + + { + pcs::Slice slc(-2, 1, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + } + + { + pcs::Slice slc(-2, -3, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(2, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(2, *slc); + } + + { + pcs::Slice slc(-4, -2, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + } + + { + pcs::Slice slc(-4, -2, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::Slice slc(5, -7, 0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(5, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::Slice slc(5, -7, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::Slice slc(-11, 7, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + } + + TEST_METHOD(startslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StartSlice slc; + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartSlice slc(0); // Slice here defaults to Slice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartSlice slc(-1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartSlice slc(-2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartSlice slc(-4); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartSlice slc(5); + + int index{ slc.begin(txt) }; + Assert::AreEqual(5, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5 , *slc); + } + + { + pcs::StartSlice slc(-11); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + } + + TEST_METHOD(startstepslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StartStepSlice slc(0, 1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartStepSlice slc(0, char(1)); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartStepSlice slc(0, 11); // StartStepSlice here defaults to StartStepSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k += 11) + Assert::AreEqual(*slc, k); + Assert::AreEqual(11, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k += 11) + Assert::AreEqual(*slc, k); + Assert::AreEqual(11, *slc); + } + + { + pcs::StartStepSlice slc(0, 3); // StartStepSlice here defaults to StartStepSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k += 3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k += 3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + } + + { + pcs::StartStepSlice slc(0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + ; + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartStepSlice slc; + + long index{ slc.begin(txt) }; + Assert::AreEqual(long(0), index); + + long k{ 0 }; + for (long index = slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + + k = 0; + for (long index = slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + } + + { + pcs::StartStepSlice slc(-1, -1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + + k = (long long)txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + } + + { + pcs::StartStepSlice slc(-2, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StartStepSlice slc(-9, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StartStepSlice slc(-4, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StartStepSlice slc(-4, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartStepSlice slc(5, -7); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, k -= 7) + Assert::AreEqual(*slc, k); + Assert::AreEqual(-3, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, k -= 7) + Assert::AreEqual(*slc, k); + Assert::AreEqual(-3, *slc); + } + + { + pcs::StartStepSlice slc(5, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StartStepSlice slc(-11, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + } + + TEST_METHOD(startstopslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StartStopSlice slc(0, 5); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartStopSlice slc(0, 5UL); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StartStopSlice slc(0, 11); // StartStopSlice here defaults to StartStopSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartStopSlice slc(0, 3); // StartStopSlice here defaults to StartStopSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StartStopSlice slc(0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + ; + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartStopSlice slc; + + long index{ slc.begin(txt) }; + Assert::AreEqual(long(0), index); + + long k{ 0 }; + for (long index = slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + + k = 0; + for (long index = slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + } + + { + pcs::StartStopSlice slc(-1, 0); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + + k = txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + } + + { + pcs::StartStopSlice slc(-2, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StartStopSlice slc(-2, 3); + + int index{ slc.begin(txt) }; + Assert::AreEqual(3, index); + + int k{ int(txt.size()) - 2 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = int(txt.size()) - 2; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StartStopSlice slc(-4, -2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(1, index); + + int k{ int(txt.size()) - 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(int(txt.size() - 2), *slc); + + k = int(txt.size()) - 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(int(txt.size() - 2), *slc); + } + + { + pcs::StartStopSlice slc(5, -7); + + int index{ slc.begin(txt) }; + Assert::AreEqual(5, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StartStopSlice slc(-11, 7); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + } + + TEST_METHOD(stepslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StepSlice slc; + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StepSlice slc(1); // StepSlice here defaults to StepSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StepSlice slc(3); // StepSlice here defaults to StepSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k+=3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k+=3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + } + + { + pcs::StepSlice slc(0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + ; + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StepSlice slc; + + long index{ slc.begin(txt) }; + Assert::AreEqual(long(0), index); + + long k{ 0 }; + for (long index = slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + + k = 0; + for (long index = slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + } + + { + pcs::StepSlice slc(-1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + + k = txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0LL, *slc); + } + + { + pcs::StepSlice slc(-2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, k-=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, k-=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StepSlice slc(-4); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, k-=4) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, k-=4) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StepSlice slc(-11); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, k-=11) + Assert::AreEqual(*slc, k); + Assert::AreEqual(-7, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, k-=11) + Assert::AreEqual(*slc, k); + Assert::AreEqual(-7, *slc); + } + } + + TEST_METHOD(stopslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StopSlice slc; + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StopSlice slc(7); // Slice here defaults to Slice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StopSlice slc(-1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + } + + { + pcs::StopSlice slc(-2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StopSlice slc(-4); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + } + + { + pcs::StopSlice slc(5); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StopSlice slc(-11); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + } + + TEST_METHOD(stopstepslice_iteration) + { + pcs::CppString txt("aBcDe"); + + { + pcs::StopStepSlice slc( 5, 1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StopStepSlice slc(5UL, char(1)); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(0LL, index); + + long long k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5LL, *slc); + } + + { + pcs::StopStepSlice slc(11, 3); // StopStepSlice here defaults to StopStepSlice + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k+=3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k+=3) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + } + + { + pcs::StopStepSlice slc(3, 2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k+=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k+=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4, *slc); + } + + { + pcs::StopStepSlice slc(7); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + ; + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5, *slc); + } + + { + pcs::StopStepSlice slc; + + long index{ slc.begin(txt) }; + Assert::AreEqual(long(0), index); + + long k{ 0 }; + for (long index = slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + + k = 0; + for (long index = slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(5L, *slc); + } + + { + pcs::StopStepSlice slc(-1, -1); + + long long index{ slc.begin(txt) }; + Assert::AreEqual(4LL, index); + + long long k{ (long long)txt.size() - 1 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + + k = txt.size() - 1; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(4LL, *slc); + } + + { + pcs::StopStepSlice slc(-2, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StopStepSlice slc(-4, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(1, *slc); + } + + { + pcs::StopStepSlice slc(-2, 1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(3, *slc); + } + + { + pcs::StopStepSlice slc(-7, 0); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, ++k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StopStepSlice slc(-7, -1); + + int index{ slc.begin(txt) }; + Assert::AreEqual(4, index); + + int k{ 4 }; + for (slc.begin(txt); !slc.end(); slc++, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + + k = 4; + for (slc.begin(txt); !slc.end(); ++slc, --k) + Assert::AreEqual(*slc, k); + Assert::AreEqual(0, *slc); + } + + { + pcs::StopStepSlice slc(7, 2); + + int index{ slc.begin(txt) }; + Assert::AreEqual(0, index); + + int k{ 0 }; + for (slc.begin(txt); !slc.end(); slc++, k+=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + + k = 0; + for (slc.begin(txt); !slc.end(); ++slc, k+=2) + Assert::AreEqual(*slc, k); + Assert::AreEqual(6, *slc); + } + + } + + }; + + + //===== PART 5 ======================================== + TEST_CLASS(cppstringstests_PART_5) + { + public: + + TEST_METHOD(capitalize) + { + pcs::CppString s("abc def GHi jKl 032, JHGF/"); + Assert::AreEqual(pcs::CppString("Abc def ghi jkl 032, jhgf/").c_str(), s.capitalize().c_str()); + + s = "capitalize Capitalize. CAPITALIZE"; + Assert::AreEqual(pcs::CppString("Capitalize capitalize. capitalize").c_str(), s.capitalize().c_str()); + + pcs::CppWString ws(L"123AHBl!"); + Assert::AreEqual(pcs::CppWString(L"123ahbl!").c_str(), ws.capitalize().c_str()); + } + + TEST_METHOD(center) + { + pcs::CppString s("zyxwvutsrqp"); + Assert::AreEqual(pcs::CppString(" zyxwvutsrqp ").c_str(), s.center(15).c_str(), L"--1--"); + Assert::AreEqual(pcs::CppString(" zyxwvutsrqp ").c_str(), s.center(14).c_str(), L"--2--"); + Assert::AreEqual(pcs::CppString(" zyxwvutsrqp ").c_str(), s.center(13).c_str(), L"--3--"); + Assert::AreEqual(pcs::CppString("zyxwvutsrqp ").c_str(), s.center(12).c_str(), L"--4--"); + Assert::AreEqual(pcs::CppString("zyxwvutsrqp").c_str(), s.center(11).c_str(), L"--5--"); + Assert::AreEqual(pcs::CppString("zyxwvutsrqp").c_str(), s.center(10).c_str(), L"--6--"); + + pcs::CppWString ws(L"zyxwvutsrqp"); + Assert::AreEqual(pcs::CppWString(L"##zyxwvutsrqp##").c_str(), ws.center(15, L'#').c_str(), L"--11--"); + Assert::AreEqual(pcs::CppWString(L"#zyxwvutsrqp##").c_str(), ws.center(14, L'#').c_str(), L"--12--"); + Assert::AreEqual(pcs::CppWString(L"#zyxwvutsrqp#").c_str(), ws.center(13, L'#').c_str(), L"--13--"); + Assert::AreEqual(pcs::CppWString(L"zyxwvutsrqp#").c_str(), ws.center(12, L'#').c_str(), L"--14--"); + Assert::AreEqual(pcs::CppWString(L"zyxwvutsrqp").c_str(), ws.center(11, L'#').c_str(), L"--15--"); + Assert::AreEqual(pcs::CppWString(L"zyxwvutsrqp").c_str(), ws.center(10, L'#').c_str(), L"--16--"); + } + + TEST_METHOD(contains) + { + using namespace pcs; + pcs::CppString text("Abcd. Efgh ij!"); + for (std::size_t index = 0; index < text.size(); ++index) { + Assert::IsTrue(text.contains(text.substr(index))); + for (std::size_t count = 0; count < text.size() - index; ++count) + Assert::IsTrue(text.contains(text.substr(index, count))); + } + Assert::IsFalse(text.contains("zzz"_cs)); + Assert::IsFalse(text.contains("abc"_cs)); + Assert::IsFalse(text.contains("Abcd. Efgh ij!!"_cs)); + Assert::IsTrue(text.contains(""_cs)); + + pcs::CppWString wtext(L"Abcd. Efgh ij!"); + for (std::size_t index = 0; index < wtext.size(); ++index) { + Assert::IsTrue(wtext.contains(wtext.substr(index))); + for (std::size_t count = 0; count < text.size() - index; ++count) + Assert::IsTrue(wtext.contains(wtext.substr(index, count))); + } + Assert::IsFalse(wtext.contains(L"zzz"_cs)); + Assert::IsFalse(wtext.contains(L"abc"_cs)); + Assert::IsFalse(wtext.contains(L"Abcd. Efgh ij!!"_cs)); + Assert::IsTrue(wtext.contains(L""_cs)); + + for (std::size_t index = 0; index < text.size(); ++index) { + Assert::IsTrue(text.contains(text.substr(index).c_str())); + for (std::size_t count = 0; count < text.size() - index; ++count) + Assert::IsTrue(text.contains(text.substr(index, count).c_str())); + } + Assert::IsFalse(text.contains("z")); + Assert::IsFalse(text.contains("a")); + Assert::IsFalse(text.contains("Abcd. Efgh ij!!")); + Assert::IsTrue(text.contains("")); + Assert::IsTrue(text.contains(nullptr)); + + for (std::size_t index = 0; index < wtext.size(); ++index) { + Assert::IsTrue(wtext.contains(wtext.substr(index).c_str())); + for (std::size_t count = 0; count < text.size() - index; ++count) + Assert::IsTrue(wtext.contains(wtext.substr(index, count).c_str())); + } + Assert::IsFalse(wtext.contains(L"zzz")); + Assert::IsFalse(wtext.contains(L"abc")); + Assert::IsFalse(wtext.contains(L"Abcd. Efgh ij!!")); + Assert::IsTrue(wtext.contains(L"")); + Assert::IsTrue(wtext.contains(nullptr)); + + for (auto const ch : text) + Assert::IsTrue(text.contains(ch)); + Assert::IsFalse(text.contains('z')); + Assert::IsFalse(text.contains('a')); + + for (auto const wch : wtext) + Assert::IsTrue(wtext.contains(wch)); + Assert::IsFalse(wtext.contains(L'z')); + Assert::IsFalse(wtext.contains(L'a')); + } + + TEST_METHOD(contains_n) + { + using namespace pcs; + pcs::CppString text("Abcd. Efgh ij!"); + for (std::size_t index = 0; index < text.size(); ++index) { + Assert::IsTrue(text.contains_n(text.substr(index), index)); + for (std::size_t count = 0; count < text.size() - index; ++count) { + Assert::IsTrue(text.contains_n(text.substr(index, count), index, count)); + Assert::IsTrue(text.contains_n(text.substr(index, count), index, count + 1)); + if (count > 0) + Assert::IsFalse(text.contains_n(text.substr(index, count), index, count - 1)); + } + } + Assert::IsFalse(text.contains_n("zzz"_cs, 0)); + Assert::IsFalse(text.contains_n("abc"_cs, 0)); + Assert::IsFalse(text.contains_n("Abcd. Efgh ij!!"_cs, 0)); + Assert::IsTrue(text.contains_n(""_cs, 6)); + Assert::IsFalse(text.contains_n(". Ef"_cs, 10, 4)); + Assert::IsFalse(text.contains_n(". Ef"_cs, 4, 3)); + + pcs::CppWString wtext(L"Abcd. Efgh ij!"); + for (std::size_t index = 0; index < wtext.size(); ++index) { + Assert::IsTrue(wtext.contains_n(wtext.substr(index), index)); + for (std::size_t count = 0; count < text.size() - index; ++count) { + Assert::IsTrue(wtext.contains_n(wtext.substr(index, count), index, count)); + Assert::IsTrue(wtext.contains_n(wtext.substr(index, count), index, count + 1)); + if (count > 0) + Assert::IsFalse(wtext.contains_n(wtext.substr(index, count), index, count - 1)); + } + } + Assert::IsFalse(wtext.contains_n(L"zzz"_cs, 0)); + Assert::IsFalse(wtext.contains_n(L"abc"_cs, 0)); + Assert::IsFalse(wtext.contains_n(L"Abcd. Efgh ij!!"_cs, 0)); + Assert::IsTrue(wtext.contains_n(L""_cs, 6)); + Assert::IsFalse(wtext.contains_n(L". Ef"_cs, 10, 4)); + Assert::IsFalse(wtext.contains_n(L". Ef"_cs, 4, 3)); + + for (std::size_t index = 0; index < text.size(); ++index) { + Assert::IsTrue(text.contains_n(text.substr(index), index)); + for (std::size_t count = 0; count < text.size() - index; ++count) { + Assert::IsTrue(text.contains_n(text.substr(index, count).c_str(), index, count)); + Assert::IsTrue(text.contains_n(text.substr(index, count).c_str(), index, count + 1)); + if (count > 0) + Assert::IsFalse(text.contains_n(text.substr(index, count).c_str(), index, count - 1)); + } + } + Assert::IsFalse(text.contains_n("z", 0)); + Assert::IsFalse(text.contains_n("a", 0)); + Assert::IsFalse(text.contains_n("Abcd. Efgh ij!!", 0)); + Assert::IsTrue(text.contains_n("", 6)); + Assert::IsTrue(text.contains_n(nullptr, 5)); + Assert::IsFalse(text.contains_n(". Ef", 10, 4)); + Assert::IsFalse(text.contains_n(". Ef", 4, 3)); + + for (std::size_t index = 0; index < text.size(); ++index) { + Assert::IsTrue(wtext.contains_n(wtext.substr(index), index)); + for (std::size_t count = 0; count < wtext.size() - index; ++count) { + Assert::IsTrue(wtext.contains_n(wtext.substr(index, count).c_str(), index, count)); + Assert::IsTrue(wtext.contains_n(wtext.substr(index, count).c_str(), index, count + 1)); + if (count > 0) + Assert::IsFalse(wtext.contains_n(wtext.substr(index, count).c_str(), index, count - 1)); + } + } + Assert::IsFalse(wtext.contains_n(L"z", 0)); + Assert::IsFalse(wtext.contains_n(L"a", 0)); + Assert::IsFalse(wtext.contains_n(L"Abcd. Efgh ij!!", 0)); + Assert::IsTrue(wtext.contains_n(L"", 6)); + Assert::IsTrue(wtext.contains_n(nullptr, 3)); + Assert::IsFalse(wtext.contains_n(L". Ef", 10, 4)); + Assert::IsFalse(wtext.contains_n(L". Ef", 4, 3)); + + for (auto const ch : text) + Assert::IsTrue(text.contains_n(ch, 0)); + Assert::IsFalse(text.contains_n('z', 0, 21)); + Assert::IsFalse(text.contains_n('a', 0)); + + for (auto const wch : wtext) + Assert::IsTrue(wtext.contains_n(wch, 0)); + Assert::IsFalse(wtext.contains_n(L'z', 0)); + Assert::IsFalse(wtext.contains_n(L'a', 0, 21)); + } + + TEST_METHOD(count) + { + pcs::CppString s("abcabcabcdefabca bca bcabca"); + Assert::AreEqual(pcs::CppString::size_type(3), s.count("abca")); + Assert::AreEqual(pcs::CppString::size_type(6), s.count("bca")); + Assert::AreEqual(pcs::CppString::size_type(0), s.count("A")); + Assert::AreEqual(pcs::CppString::size_type(2), s.count("abca", 4)); + Assert::AreEqual(pcs::CppString::size_type(5), s.count("bca", 2)); + Assert::AreEqual(pcs::CppString::size_type(0), s.count("A", 3)); + Assert::AreEqual(pcs::CppString::size_type(1), s.count("abca", 4, s.size() - 5)); + Assert::AreEqual(pcs::CppString::size_type(4), s.count("bca", 2, s.size() - 2)); + Assert::AreEqual(pcs::CppString::size_type(0), s.count("A", 3, s.size() + 4)); + + pcs::CppWString ws(L"abcabcabcdefabca bca bcabca"); + Assert::AreEqual(pcs::CppString::size_type(3), ws.count(L"abca")); + Assert::AreEqual(pcs::CppString::size_type(6), ws.count(L"bca")); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count(L"A")); + Assert::AreEqual(pcs::CppString::size_type(2), ws.count(L"abca", 4)); + Assert::AreEqual(pcs::CppString::size_type(5), ws.count(L"bca", 2)); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count(L"A", 3)); + Assert::AreEqual(pcs::CppString::size_type(1), ws.count(L"abca", 4, s.size() - 5)); + Assert::AreEqual(pcs::CppString::size_type(4), ws.count(L"bca", 2, s.size() - 2)); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count(L"A", 3, s.size() + 4)); + } + + TEST_METHOD(count_n) + { + pcs::CppString s("abcabcabcdefabca bca bcabca"); + const pcs::CppString::size_type len{ s.size() }; + Assert::AreEqual(pcs::CppString::size_type(3), s.count_n("abca", 0, len)); + Assert::AreEqual(pcs::CppString::size_type(6), s.count_n("bca", 0, len)); + Assert::AreEqual(pcs::CppString::size_type(0), s.count_n("A", 0, len)); + Assert::AreEqual(pcs::CppString::size_type(2), s.count_n("abca", 4, len - 4)); + Assert::AreEqual(pcs::CppString::size_type(5), s.count_n("bca", 2, len - 2)); + Assert::AreEqual(pcs::CppString::size_type(0), s.count_n("A", 3, len - 3)); + Assert::AreEqual(pcs::CppString::size_type(1), s.count_n("abca", 4, len - 5)); + Assert::AreEqual(pcs::CppString::size_type(4), s.count_n("bca", 2, len - 3)); + Assert::AreEqual(pcs::CppString::size_type(0), s.count_n("A", 3, len + 4)); + + pcs::CppWString ws(L"abcabcabcdefabca bca bcabca"); + const pcs::CppWString::size_type wlen{ ws.size() }; + Assert::AreEqual(pcs::CppString::size_type(3), ws.count_n(L"abca", 0, wlen)); + Assert::AreEqual(pcs::CppString::size_type(6), ws.count_n(L"bca", 0, wlen)); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count_n(L"A", 0, wlen)); + Assert::AreEqual(pcs::CppString::size_type(2), ws.count_n(L"abca", 4, wlen - 4)); + Assert::AreEqual(pcs::CppString::size_type(5), ws.count_n(L"bca", 2, wlen - 2)); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count_n(L"A", 3, wlen - 3)); + Assert::AreEqual(pcs::CppString::size_type(1), ws.count_n(L"abca", 4, wlen - 5)); + Assert::AreEqual(pcs::CppString::size_type(4), ws.count_n(L"bca", 2, wlen - 3)); + Assert::AreEqual(pcs::CppString::size_type(0), ws.count_n(L"A", 3, wlen + 4)); + } + + TEST_METHOD(endswith) + { + pcs::CppString s("abcabcabcdefabca bca bcabca"); + const pcs::CppString::size_type len{ s.size() }; + Assert::IsTrue(s.endswith("abca")); + Assert::IsFalse(s.endswith("abcabca")); + Assert::IsTrue(s.endswith("abc", len - 1)); + Assert::IsFalse(s.endswith("bcabca", len - 1)); + Assert::IsTrue(s.endswith("abca", len - 4, len)); + Assert::IsFalse(s.endswith("abca", len - 4, len - 2)); + Assert::IsTrue(s.endswith({ "def", "ghi", "abca" }, len - 4, len)); + Assert::IsFalse(s.endswith({ "def", "ghi" }, len - 4, len)); + Assert::IsFalse(s.endswith({ "def", "ghi", "abca" }, len - 4, len - 2)); + + pcs::CppWString ws(L"abcabcabcdefabca bca bcabca"); + const pcs::CppWString::size_type wlen{ ws.size() }; + Assert::IsTrue(ws.endswith(L"abca")); + Assert::IsFalse(ws.endswith(L"abcabca")); + Assert::IsTrue(ws.endswith(L"abc", wlen - 1)); + Assert::IsFalse(ws.endswith(L"bcabca", wlen - 1)); + Assert::IsTrue(ws.endswith(L"abca", wlen - 4, wlen)); + Assert::IsFalse(ws.endswith(L"abca", wlen - 4, wlen - 2)); + Assert::IsTrue(ws.endswith({ L"def", L"ghi", L"abca" }, len - 4, len)); + Assert::IsFalse(ws.endswith({ L"def", L"ghi" }, len - 4, len)); + Assert::IsFalse(ws.endswith({ L"def", L"ghi", L"abca" }, len - 4, len - 2)); + } + + TEST_METHOD(endswith_n) + { + pcs::CppString s("abcabcabcdefabca bca bcabca"); + const pcs::CppString::size_type len{ s.size() }; + Assert::IsTrue(s.endswith_n("abc", len - 1)); + Assert::IsFalse(s.endswith_n("bcabca", len - 1)); + Assert::IsTrue(s.endswith_n("abca", len - 4, 4)); + Assert::IsFalse(s.endswith_n("abca", len - 4, 3)); + Assert::IsTrue(s.endswith_n({ "def", "ghi", "abca" }, len - 4, 4)); + Assert::IsFalse(s.endswith_n({ "def", "ghi" }, len - 4, 4)); + Assert::IsFalse(s.endswith_n({ "def", "ghi", "abca" }, len - 4, 3)); + + pcs::CppWString ws(L"abcabcabcdefabca bca bcabca"); + const pcs::CppWString::size_type wlen{ ws.size() }; + Assert::IsTrue(ws.endswith_n(L"abc", wlen - 1)); + Assert::IsFalse(ws.endswith_n(L"bcabca", wlen - 1)); + Assert::IsTrue(ws.endswith_n(L"abca", wlen - 4, 4)); + Assert::IsFalse(ws.endswith_n(L"abca", wlen - 4, 3)); + Assert::IsTrue(ws.endswith_n({ L"def", L"ghi", L"abca" }, len - 4, 4)); + Assert::IsFalse(ws.endswith_n({ L"def", L"ghi" }, len - 4, 4)); + Assert::IsFalse(ws.endswith_n({ L"def", L"ghi", L"abca" }, len - 4, 3)); + } + + TEST_METHOD(expand_tabs) + { + pcs::CppString s("a\tbc\tdef\tghij\t\r\tk\nl\tm\r\nno\tpqr \ts."); + pcs::CppString ts = s.expand_tabs(4); + Assert::AreEqual(pcs::CppString("a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), ts.c_str()); + ts = s.expand_tabs(3); + Assert::AreEqual(pcs::CppString("a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), ts.c_str()); + ts = s.expand_tabs(2); + Assert::AreEqual(pcs::CppString("a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), ts.c_str()); + ts = s.expand_tabs(1); + Assert::AreEqual(pcs::CppString("a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), ts.c_str()); + + pcs::CppWString ws(L"a\tbc\tdef\tghij\t\r\tk\nl\tm\r\nno\tpqr \ts."); + pcs::CppWString wts = ws.expand_tabs(4); + Assert::AreEqual(pcs::CppWString(L"a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), wts.c_str()); + wts = ws.expand_tabs(3); + Assert::AreEqual(pcs::CppWString(L"a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), wts.c_str()); + wts = ws.expand_tabs(2); + Assert::AreEqual(pcs::CppWString(L"a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), wts.c_str()); + wts = ws.expand_tabs(1); + Assert::AreEqual(pcs::CppWString(L"a bc def ghij \r k\nl m\r\nno pqr s.").c_str(), wts.c_str()); + } + + TEST_METHOD(find) + { + size_t found_pos; + + pcs::CppString test_str{ "ABC0123456789.ABC0123456789." }; + for (int c = 0; c <= 255; ++c) { + char ch{ char(c) }; + Assert::AreEqual(test_str.MyBaseClass::find(ch), test_str.find(ch)); + + found_pos = test_str.substr(2).MyBaseClass::find(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(ch, 2)); + else + Assert::AreEqual(found_pos, test_str.find(ch, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(ch, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.find(ch, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + pcs::CppString s(ch); + Assert::AreEqual(test_str.MyBaseClass::find(s), test_str.find(s)); + found_pos = test_str.substr(2).MyBaseClass::find(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(s, 2)); + else + Assert::AreEqual(found_pos, test_str.find(s, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(s, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.find(s, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + if (c > 0) { + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::find(str), test_str.find(str)); + + found_pos = test_str.substr(2).MyBaseClass::find(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(str, 2)); + else + Assert::AreEqual(found_pos, test_str.find(str, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find(str, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.find(str, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + } + } + Assert::AreEqual(size_t(14), test_str.find("A", 1)); + Assert::AreEqual(pcs::CppString::npos, test_str.find("A", 15)); + Assert::AreEqual(size_t(0), test_str.find("")); + Assert::AreEqual(size_t(27), test_str.find(".", 14)); + Assert::AreEqual(pcs::CppString::npos, test_str.find(".", 28)); + Assert::AreEqual(size_t(13), test_str.find(".", 13)); + + pcs::CppWString wtest{ L"ABC0123456789.ABC0123456789." }; + for (int wc = 0; wc <=0xffff; ++wc) { + wchar_t wch{ wchar_t(wc) }; + + found_pos = wtest.substr(2).MyBaseClass::find(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(wch, 2)); + else + Assert::AreEqual(found_pos, wtest.find(wch, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(wch, 2, pcs::CppWString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.find(wch, 2, pcs::CppWString::size_type(5 + 2 - 1)) - 2); + + pcs::CppWString ws(wch); + Assert::AreEqual(wtest.MyBaseClass::find(ws), wtest.find(ws)); + + found_pos = wtest.substr(2).MyBaseClass::find(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(ws, 2)); + else + Assert::AreEqual(found_pos, wtest.find(ws, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(ws, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.find(ws, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + + if (wc > 0) { + wchar_t wstr[2]{ wch, 0 }; + Assert::AreEqual(wtest.MyBaseClass::find(wstr), wtest.find(wstr)); + + found_pos = wtest.substr(2).MyBaseClass::find(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(wstr, 2)); + else + Assert::AreEqual(found_pos, wtest.find(wstr, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find(wstr, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.find(wstr, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + } + } + Assert::AreEqual(size_t(14), wtest.find(L"A", 1)); + Assert::AreEqual(pcs::CppString::npos, wtest.find(L"A", 15)); + Assert::AreEqual(size_t(0), wtest.find(L"")); + Assert::AreEqual(size_t(27), wtest.find(L".", 14)); + Assert::AreEqual(pcs::CppString::npos, wtest.find(L".", 28)); + Assert::AreEqual(size_t(13), wtest.find(L".", 13)); + } + + TEST_METHOD(find_n) + { + size_t found_pos; + + pcs::CppString test_str{ "ABC0123456789.ABC0123456789." }; + for (int c = 0; c <= 255; ++c) { + char ch{ char(c) }; + Assert::AreEqual(test_str.MyBaseClass::find(ch), test_str.find_n(ch, size_t(-1))); + + found_pos = test_str.substr(2).MyBaseClass::find(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(ch, 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).find_n(ch, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(ch, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.find_n(ch, 2, pcs::CppString::size_type(5)) - 2); + + pcs::CppString s(ch); + Assert::AreEqual(test_str.MyBaseClass::find(s), test_str.find_n(s, size_t(-1))); + found_pos = test_str.substr(2).MyBaseClass::find(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(s, 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).find_n(s, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(s, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.find_n(s, 2, pcs::CppString::size_type(5)) - 2); + + if (c > 0) { + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::find(str), test_str.find_n(str, size_t(-1))); + + found_pos = test_str.substr(2).MyBaseClass::find(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(str, test_str.size() - 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).find_n(str, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::find(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.find_n(str, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.find_n(str, 2, pcs::CppString::size_type(5)) - 2); + } + } + Assert::AreEqual(size_t(14), test_str.find_n("A", 1, test_str.size() - 1)); + Assert::AreEqual(pcs::CppString::npos, test_str.find_n("A", 15, 1)); + Assert::AreEqual(size_t(0), test_str.find_n("", size_t(-1))); + Assert::AreEqual(size_t(27), test_str.find_n(".", 14, test_str.size() - 14)); + Assert::AreEqual(pcs::CppString::npos, test_str.find_n(".", 28, 1)); + Assert::AreEqual(size_t(13), test_str.find_n(".", 13, test_str.size() - 13)); + + pcs::CppWString wtest{ L"ABC0123456789.ABC0123456789." }; + for (int wc = 0; wc <= 0xffff; ++wc) { + wchar_t wch{ wchar_t(wc) }; + Assert::AreEqual(wtest.MyBaseClass::find(wch), wtest.find_n(wch, size_t(-1))); + + found_pos = wtest.substr(2).MyBaseClass::find(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(wch, 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).find_n(wch, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(wch, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.find_n(wch, 2, pcs::CppString::size_type(5)) - 2); + + pcs::CppWString ws(wch); + Assert::AreEqual(wtest.MyBaseClass::find(ws), wtest.find_n(ws, size_t(-1))); + found_pos = wtest.substr(2).MyBaseClass::find(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(ws, 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).find_n(ws, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(ws, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.find_n(ws, 2, pcs::CppString::size_type(5)) - 2); + + if (wc > 0) { + wchar_t wstr[2]{ wch, 0 }; + Assert::AreEqual(wtest.MyBaseClass::find(wstr), wtest.find_n(wstr, size_t(-1))); + + found_pos = wtest.substr(2).MyBaseClass::find(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(wstr, wtest.size() - 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).find_n(wstr, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::find(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.find_n(wstr, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.find_n(wstr, 2, pcs::CppString::size_type(5)) - 2); + } + } + Assert::AreEqual(size_t(14), wtest.find_n(L"A", 1, wtest.size() - 1)); + Assert::AreEqual(pcs::CppString::npos, wtest.find_n(L"A", 15, 1)); + Assert::AreEqual(size_t(0), wtest.find_n(L"", size_t(-1))); + Assert::AreEqual(size_t(27), wtest.find_n(L".", 14, wtest.size() - 14)); + Assert::AreEqual(pcs::CppString::npos, wtest.find_n(L".", 28, 1)); + Assert::AreEqual(size_t(13), wtest.find_n(L".", 13, wtest.size() - 13)); + + } + + TEST_METHOD(format) + { + pcs::CppString s; + s.format("{:d}, {:d}, {:s}", 1, 2, "Abc"); + Assert::AreEqual("1, 2, Abc", s.c_str()); + + const int x{ 5 }; + const unsigned long long y{ 6 }; + const double pi{ 3.141592653529 }; + const std::string t{ "abc." }; + Assert::AreEqual("56 3.1415927abc.", (s.format("{}{}{:10.7f}{:s}", x, y, pi, t)).c_str()); + + pcs::CppWString ws; + ws.format(L"{:d}, {:d}, {}", 1, 2, L"Abc"); + Assert::AreEqual(L"1, 2, Abc", ws.c_str()); + + const std::wstring wt{ L"abc." }; + Assert::AreEqual(L"56 3.1415927abc.", (ws.format(L"{}{}{:10.7f}{:s}", x, y, pi, wt)).c_str()); + } + + TEST_METHOD(index_char) + { + using string_type = pcs::CppString; + + string_type test_str{ "ABC0123456789." }; + char ch{ '3' }; + Assert::AreEqual(test_str.MyBaseClass::find(ch), test_str.index(ch)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(ch), test_str.index(ch, 2) - 2); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::find(ch), test_str.index(ch, 2, string_type::size_type(5 + 2 - 1)) - 2); + try { + const string_type::size_type pos = test_str.index('z'); + Assert::IsTrue(pos != pcs::CppString::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index('z', 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index('z', 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.MyBaseClass::find(s), test_str.index(s)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(s), test_str.index(s, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(s), test_str.index(s, 3, string_type::size_type(5 + 3 - 1)) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.index(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::find(str), test_str.index(str)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(str), test_str.index(str, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(str), test_str.index(str, 3, string_type::size_type(5 + 3 - 1)) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.index(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(index_wchart) + { + using string_type = pcs::CppWString; + + string_type test_str( L"ABC0123456789." ); + wchar_t ch{ L'3' }; + Assert::AreEqual(test_str.MyBaseClass::find(ch), test_str.index(ch)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(ch), test_str.index(ch, 2) - 2); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::find(ch), test_str.index(ch, 2, string_type::size_type(5 + 2 - 1)) - 2); + try { + const string_type::size_type pos = test_str.index('z'); + Assert::IsTrue(pos != pcs::CppString::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index('z', 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index('z', 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.MyBaseClass::find(s), test_str.index(s)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(s), test_str.index(s, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(s), test_str.index(s, 3, string_type::size_type(5 + 3 - 1)) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.index(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + wchar_t str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::find(str), test_str.index(str)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::find(str), test_str.index(str, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(str), test_str.index(str, 3, string_type::size_type(5 + 3 - 1)) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.index(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.index(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(index_n_char) + { + using string_type = pcs::CppString; + + pcs::CppString test_str{ "ABC0123456789." }; + char ch{ '3' }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(ch), test_str.index_n(ch, 20)); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::find(ch), test_str.index_n(ch, 2, 5) - 2); + try { + const string_type::size_type pos = test_str.index_n('z', 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n('z', 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + pcs::CppString s(ch); + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(s), test_str.index_n(s, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(s), test_str.index_n(s, 3, 5) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.index_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(str), test_str.index_n(str, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(str), test_str.index_n(str, 3, 5) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.index_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(index_n_wchar_t) + { + using string_type = pcs::CppWString; + + string_type test_str{ L"ABC0123456789." }; + wchar_t ch{ L'3'}; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(ch), test_str.index_n(ch, 20)); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::find(ch), test_str.index_n(ch, 2, 5) - 2); + try { + const string_type::size_type pos = test_str.index_n('z', 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n('z', 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(s), test_str.index_n(s, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(s), test_str.index_n(s, 3, 5) - 3); + try { + const string_type::size_type pos = test_str.index_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + wchar_t str[2]{ ch, 0 }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::find(str), test_str.index_n(str, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::find(str), test_str.index_n(str, 3, 5) - 3); + str[0] = L'z'; + try { + const string_type::size_type pos = test_str.index_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.index_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(isalnum) + { + Assert::IsTrue(pcs::CppString("aA25").isalnum()); + Assert::IsFalse(pcs::CppString("0123456789az,").isalnum()); + Assert::IsTrue(pcs::CppWString(L"aA25").isalnum()); + Assert::IsFalse(pcs::CppWString(L"0123456789az,").isalnum()); + } + + TEST_METHOD(isalpha) + { + Assert::IsFalse(pcs::CppString().isalpha()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(3, ch); + Assert::AreEqual(pcs::is_alpha(ch), s.isalpha()); + } + + Assert::IsFalse(pcs::CppWString().isalpha()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(3, wch); + Assert::AreEqual(pcs::is_alpha(wch), ws.isalpha()); + } + } + + TEST_METHOD(isascii) + { + Assert::IsTrue(pcs::CppString().isascii()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(3, ch); + Assert::AreEqual(pcs::is_ascii(ch), s.isascii()); + } + + Assert::IsTrue(pcs::CppWString().isascii()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(3, wch); + Assert::AreEqual(pcs::is_ascii(wch), ws.isascii()); + } + } + + TEST_METHOD(isdecimal) + { + Assert::IsFalse(pcs::CppString().isdecimal()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_decimal(ch), s.isdecimal()); + } + + Assert::IsFalse(pcs::CppWString().isdecimal()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_decimal(wch), ws.isdecimal()); + } + } + + TEST_METHOD(isdigit) + { + Assert::IsFalse(pcs::CppString().isdigit()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_digit(ch), s.isdigit()); + } + + Assert::IsFalse(pcs::CppWString().isdigit()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_digit(wch), ws.isdigit()); + } + } + + TEST_METHOD(isidentifier) + { + Assert::IsTrue(pcs::CppString("_").isidentifier()); + Assert::IsTrue(pcs::CppString("A").isidentifier()); + Assert::IsTrue(pcs::CppString("b").isidentifier()); + Assert::IsTrue(pcs::CppString("_abcdefghijklmnopqrstuvwxyz0123456789_").isidentifier()); + Assert::IsTrue(pcs::CppString("abcdefghijklmnopqrstuvwxyz0123456789_").isidentifier()); + Assert::IsTrue(pcs::CppString("abcdefghijklmnopqrstuvwxyz_0123456789").isidentifier()); + Assert::IsTrue(pcs::CppString("_0123456789").isidentifier()); + Assert::IsTrue(pcs::CppString("__").isidentifier()); + Assert::IsFalse(pcs::CppString("_abcdefghijklmnopqrstuvwxyz0123456789.").isidentifier()); + Assert::IsFalse(pcs::CppString("0a").isidentifier()); + + Assert::IsTrue(pcs::CppWString(L"_").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"A").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"b").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"_0123456789abcdefghijklmnopqrstuvwxyz_").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"abcdefghijk0123456789lmnopqrstuvwxyz_").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"abcdefghijk0123456789_lmnopqrstuvwxyz").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"_0123456789").isidentifier()); + Assert::IsTrue(pcs::CppWString(L"__").isidentifier()); + Assert::IsFalse(pcs::CppWString(L"_0123456789abcdefghijklmnopqrstuvwxyz.").isidentifier()); + Assert::IsFalse(pcs::CppWString(L"9z").isidentifier()); + } + + TEST_METHOD(islower) + { + Assert::IsFalse(pcs::CppString().islower()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_lower(ch), s.islower()); + } + + Assert::IsFalse(pcs::CppWString().islower()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_lower(wch), ws.islower()); + } + } + + TEST_METHOD(isnumeric) + { + Assert::IsFalse(pcs::CppString().isnumeric()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_numeric(ch), s.isnumeric()); + } + + Assert::IsFalse(pcs::CppWString().isnumeric()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_numeric(wch), ws.isnumeric()); + } + } + + TEST_METHOD(isprintable) + { + Assert::IsTrue(pcs::CppString().isprintable()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_printable(ch), s.isprintable()); + } + + Assert::IsTrue(pcs::CppWString().isprintable()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_printable(wch), ws.isprintable()); + } + } + + TEST_METHOD(ispunctuation) + { + Assert::IsFalse(pcs::CppString().ispunctuation()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(3, ch); + Assert::IsFalse(s.ispunctuation()); + Assert::AreEqual(pcs::is_punctuation(ch), pcs::CppString(ch).ispunctuation()); + } + + Assert::IsFalse(pcs::CppWString().ispunctuation()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(3, wch); + Assert::IsFalse(ws.ispunctuation()); + Assert::AreEqual(pcs::is_punctuation(wch), pcs::CppWString(wch).ispunctuation()); + } + } + + TEST_METHOD(isspace) + { + Assert::IsFalse(pcs::CppString().isspace()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_space(ch), s.isspace()); + } + + Assert::IsFalse(pcs::CppWString().isspace()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_space(wch), ws.isspace()); + } + } + + TEST_METHOD(istitle) + { + pcs::CppString s("abc, defgh ijklm nop. qrs 1 tuvwx2345 = yz!"); + Assert::IsFalse(s.istitle()); + Assert::IsTrue(s.title().istitle()); + + pcs::CppWString ws(L"abc, defgh ijklm nop. qrs 1 tuvwx2345 = yz!"); + Assert::IsFalse(ws.istitle()); + Assert::IsTrue(ws.title().istitle()); + } + + TEST_METHOD(isupper) + { + Assert::IsFalse(pcs::CppString().isupper()); + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_upper(ch), s.isupper()); + } + + Assert::IsFalse(pcs::CppWString().isupper()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_upper(wch), ws.isupper()); + } + } + + TEST_METHOD(is_words_sep) + { + Assert::IsFalse(pcs::CppString().is_words_sep()); + for (int c = 32; c <= 255; ++c) { + const char ch{ char(c) }; + pcs::CppString s(5, ch); + Assert::AreEqual(pcs::is_space(ch) || pcs::is_punctuation(ch), s.is_words_sep()); + } + + Assert::IsFalse(pcs::CppWString().is_words_sep()); + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + pcs::CppWString ws(5, wch); + Assert::AreEqual(pcs::is_space(wch) || pcs::is_punctuation(wch), ws.is_words_sep()); + } + } + + TEST_METHOD(join) + { + pcs::CppString s("##"); + std::array arr{ "abcd", "efg" }; + Assert::AreEqual(pcs::CppString("abcd##efg").c_str(), s.join(arr).c_str()); + Assert::AreEqual(pcs::CppString("abcd##efg##123456789").c_str(), s.join(std::array{ "abcd", "efg", "123456789" }).c_str()); + + pcs::CppWString ws(L"##"); + std::array warr{ L"abcd", L"efg" }; + Assert::AreEqual(pcs::CppWString(L"abcd##efg").c_str(), ws.join(warr).c_str()); + Assert::AreEqual(pcs::CppWString(L"abcd##efg##123456789").c_str(), ws.join(std::array{ L"abcd", L"efg", L"123456789" }).c_str()); + + std::vector vec{ "abcd", "efg" }; + Assert::AreEqual(pcs::CppString("abcd##efg").c_str(), s.join(vec).c_str()); + vec.push_back("123456789"); + Assert::AreEqual(pcs::CppString("abcd##efg##123456789").c_str(), s.join(vec).c_str()); + + std::vector wvec{ L"abcd", L"efg" }; + Assert::AreEqual(pcs::CppWString(L"abcd##efg").c_str(), ws.join(wvec).c_str()); + wvec.push_back(L"123456789"); + Assert::AreEqual(pcs::CppWString(L"abcd##efg##123456789").c_str(), ws.join(wvec).c_str()); + + using namespace pcs; + Assert::AreEqual(pcs::CppString("abcd##efg").c_str(), s.join(pcs::CppString("abcd"), pcs::CppString("efg")).c_str()); + Assert::AreEqual(pcs::CppString("abcd##efg##123456789").c_str(), s.join(pcs::CppString("abcd"), pcs::CppString("efg"), pcs::CppString("123456789")).c_str()); + Assert::AreEqual(pcs::CppString("abcd##efg##123456789##0").c_str(), s.join("abcd", "efg", "123456789", "0").c_str()); + Assert::AreEqual(pcs::CppString("abcd# #efg# #123456789# #0").c_str(), "# #"_cs.join("abcd", "efg", "123456789", "0").c_str()); + Assert::AreEqual("abcdE", "##"_cs.join("abcdE").c_str()); + Assert::AreEqual("##", "##"_cs.join().c_str()); + Assert::AreEqual("", "##"_cs.join("").c_str()); + + Assert::AreEqual(pcs::CppWString(L"abcd##efg").c_str(), ws.join(pcs::CppWString(L"abcd"), pcs::CppWString(L"efg")).c_str()); + Assert::AreEqual(pcs::CppWString(L"abcd##efg##123456789").c_str(), ws.join(pcs::CppWString(L"abcd"), pcs::CppWString(L"efg"), pcs::CppWString(L"123456789")).c_str()); + Assert::AreEqual(pcs::CppWString(L"abcd##efg##123456789##0").c_str(), ws.join(L"abcd"_cs, L"efg"_cs, L"123456789"_cs, L"0"_cs).c_str()); + Assert::AreEqual(pcs::CppWString(L"abcd# #efg# #123456789# #0").c_str(), L"# #"_cs.join(L"abcd", L"efg"_cs, L"123456789"_cs, L"0"_cs).c_str()); + Assert::AreEqual(pcs::CppWString(L"abcdE").c_str(), L"##"_cs.join(L"abcdE").c_str()); + Assert::AreEqual(pcs::CppWString(L"##").c_str(), L"##"_cs.join().c_str()); + Assert::AreEqual(pcs::CppWString(L"").c_str(), L"##"_cs.join(L"").c_str()); + } + + TEST_METHOD(ljust) + { + pcs::CppString s("abc"); + Assert::AreEqual("abc", s.ljust(1).c_str()); + Assert::AreEqual("abc", s.ljust(2).c_str()); + Assert::AreEqual("abc", s.ljust(3).c_str()); + Assert::AreEqual(" abc", s.ljust(4).c_str()); + Assert::AreEqual(" abc", s.ljust(5).c_str()); + Assert::AreEqual("abc", s.ljust(1).c_str()); + Assert::AreEqual("abc", s.ljust(2).c_str()); + Assert::AreEqual("abc", s.ljust(3).c_str()); + Assert::AreEqual(".abc", s.ljust(4, '.').c_str()); + Assert::AreEqual("..abc", s.ljust(5, '.').c_str()); + + pcs::CppWString ws(L"abc"); + Assert::AreEqual(L"abc", ws.ljust(1).c_str()); + Assert::AreEqual(L"abc", ws.ljust(2).c_str()); + Assert::AreEqual(L"abc", ws.ljust(3).c_str()); + Assert::AreEqual(L" abc", ws.ljust(4).c_str()); + Assert::AreEqual(L" abc", ws.ljust(5).c_str()); + Assert::AreEqual(L"abc", ws.ljust(1).c_str()); + Assert::AreEqual(L"abc", ws.ljust(2).c_str()); + Assert::AreEqual(L"abc", ws.ljust(3).c_str()); + Assert::AreEqual(L".abc", ws.ljust(4, '.').c_str()); + Assert::AreEqual(L"..abc", ws.ljust(5, '.').c_str()); + } + + TEST_METHOD(lower) + { + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + constexpr int N{ 5 }; + pcs::CppString s(N, ch); + s.lower(); + for (int i=0; i < N; ++i) + Assert::AreEqual(pcs::to_lower(ch), s[i]); + Assert::AreEqual(char(std::tolower(ch)), pcs::CppString::lower(ch)); + } + + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + constexpr int N{ 5 }; + pcs::CppWString ws(N, wch); + ws.lower(); + for (int i = 0; i < N; ++i) + Assert::AreEqual(pcs::to_lower(wch), ws[i]); + Assert::AreEqual(wchar_t(std::tolower(wch)), pcs::CppWString::lower(wch)); + } + + pcs::CppString s(255, '\0'); + for (int i : std::views::iota(0, 256)) + s[i] = pcs::CppString::value_type(i); + pcs::CppString res{ s.lower() }; + for (auto const [cs, cr] : std::views::zip(s, res)) { + if (std::islower(cs)) + Assert::IsTrue(std::islower(cr)); + else if (std::isupper(cs)) + Assert::IsTrue(std::islower(cr)); + else + Assert::AreEqual(cs, cr); + } + + pcs::CppWString ws(0xffff, '\0'); + for (int i : std::views::iota(0, 0x1'0000)) + ws[i] = pcs::CppWString::value_type(i); + pcs::CppWString wres{ ws.lower() }; + for (auto const [wcs, wcr] : std::views::zip(ws, wres)) { + if (std::islower(wcs)) + Assert::IsTrue(std::islower(wcr)); + else if (std::isupper(wcs)) + Assert::IsTrue(std::islower(wcr)); + else + Assert::AreEqual(wcs, wcr); + } + + } + + TEST_METHOD(lstrip) + { + using namespace pcs; + pcs::CppString s("abcd"); + Assert::AreEqual("cd", s.lstrip("baCD").c_str()); + Assert::AreEqual("abcd", s.lstrip("xyz").c_str()); + Assert::AreEqual("abcd", s.lstrip("").c_str()); + Assert::AreEqual("cd", "abbabaabcd"_cs.lstrip("baCD").c_str()); + Assert::AreEqual("cdab", "abbabaabcdab"_cs.lstrip("baCD").c_str()); + Assert::AreEqual("abcd", " abcd"_cs.lstrip().c_str()); + Assert::AreEqual("abcd", " abcd"_cs.lstrip().c_str()); + Assert::AreEqual("abcd", " abcd"_cs.lstrip().c_str()); + Assert::AreEqual("a bcd", " a bcd"_cs.lstrip().c_str()); + Assert::AreEqual("a bcd ", " a bcd "_cs.lstrip().c_str()); + + pcs::CppWString ws(L"abcd"); + Assert::AreEqual(L"cd", ws.lstrip(L"baCD").c_str()); + Assert::AreEqual(L"abcd", ws.lstrip(L"xyz").c_str()); + Assert::AreEqual(L"abcd", ws.lstrip(L"").c_str()); + Assert::AreEqual(L"cd", L"abbabaabcd"_cs.lstrip(L"baCD").c_str()); + Assert::AreEqual(L"cdab", L"abbabaabcdab"_cs.lstrip(L"baCD").c_str()); + Assert::AreEqual(L"abcd", L" abcd"_cs.lstrip().c_str()); + Assert::AreEqual(L"abcd", L" abcd"_cs.lstrip().c_str()); + Assert::AreEqual(L"abcd", L" abcd"_cs.lstrip().c_str()); + Assert::AreEqual(L"a bcd", L" a bcd"_cs.lstrip().c_str()); + Assert::AreEqual(L"a bcd ", L" a bcd "_cs.lstrip().c_str()); + } + + TEST_METHOD(operator_slice) + { + pcs::CppString text("AbcdefGhijklm"); + int text_size{ int(text.size()) }; + + Assert::AreEqual(text(0, text_size).c_str(), text(pcs::Slice()).c_str()); + Assert::AreEqual(text.c_str(), text(0, text_size).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StartSlice(0)).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StopSlice(123)).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StepSlice(1)).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StartStopSlice(0, 111)).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StartStepSlice(0, 1)).c_str()); + Assert::AreEqual(text.c_str(), text(pcs::StopStepSlice(text_size, 1)).c_str()); + + Assert::AreEqual("AceGikm", text(pcs::Slice(0, text_size + 2, 2)).c_str()); + Assert::AreEqual("behk", text(pcs::Slice(1, text_size, 3)).c_str()); + + Assert::AreEqual("", text(pcs::Slice(5, 4, 1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(text_size, text_size + 1, 1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(text_size + 2, text_size + 5, 1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(5, 3, 2)).c_str()); + + pcs::CppString reversed_text{ text }; + std::ranges::reverse(reversed_text); + Assert::AreEqual(reversed_text.c_str(), text(text_size, 0, -1).c_str()); + Assert::AreEqual(reversed_text.c_str(), text(pcs::StartStepSlice(text_size, -1)).c_str()); + Assert::AreEqual(reversed_text.c_str(), text(pcs::StopStepSlice(0, -1)).c_str()); + Assert::AreEqual(reversed_text.c_str(), text(pcs::StepSlice(-1)).c_str()); + + Assert::AreEqual("mkiGec", text(pcs::Slice(text_size, 0, -2)).c_str()); + Assert::AreEqual("mjGd", text(pcs::Slice(text_size-1, 1, -3)).c_str()); + + Assert::AreEqual("", text(pcs::Slice(4, 5, -1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(text_size + 1, text_size, -1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(text_size + 5, text_size + 2, -1)).c_str()); + Assert::AreEqual("", text(pcs::Slice(3, 5, -2)).c_str()); + + + pcs::CppWString wtext(L"AbcdefGhijklm"); + text_size = int(wtext.size()); + + Assert::AreEqual(wtext(0, text_size).c_str(), wtext(pcs::Slice()).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(0, text_size).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StartSlice(0)).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StopSlice(123)).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StepSlice(1)).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StartStopSlice(0, 111)).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StartStepSlice(0, 1)).c_str()); + Assert::AreEqual(wtext.c_str(), wtext(pcs::StopStepSlice(text_size, 1)).c_str()); + + Assert::AreEqual(L"AceGikm", wtext(pcs::Slice(0, text_size + 2, 2)).c_str()); + Assert::AreEqual(L"behk", wtext(pcs::Slice(1, text_size, 3)).c_str()); + + Assert::AreEqual(L"", wtext(pcs::Slice(5, 4, 1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(text_size, text_size + 1, 1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(text_size + 2, text_size + 5, 1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(5, 3, 2)).c_str()); + + pcs::CppWString wreversed_text{ wtext }; + std::ranges::reverse(wreversed_text); + Assert::AreEqual(wreversed_text.c_str(), wtext(text_size, 0, -1).c_str()); + Assert::AreEqual(wreversed_text.c_str(), wtext(pcs::StartStepSlice(text_size, -1)).c_str()); + Assert::AreEqual(wreversed_text.c_str(), wtext(pcs::StopStepSlice(0, -1)).c_str()); + Assert::AreEqual(wreversed_text.c_str(), wtext(pcs::StepSlice(-1)).c_str()); + + Assert::AreEqual(L"mkiGec", wtext(pcs::Slice(text_size, 0, -2)).c_str()); + Assert::AreEqual(L"mjGd", wtext(pcs::Slice(text_size - 1, 1, -3)).c_str()); + + Assert::AreEqual(L"", wtext(pcs::Slice(4, 5, -1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(text_size + 1, text_size, -1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(text_size + 5, text_size + 2, -1)).c_str()); + Assert::AreEqual(L"", wtext(pcs::Slice(3, 5, -2)).c_str()); + } + + TEST_METHOD(operator_times) + { + pcs::CppString text("Abcd,"); + Assert::AreEqual("", (text * -1).c_str()); + Assert::AreEqual("", (text * 0).c_str()); + Assert::AreEqual(text.c_str(), (text * 1).c_str()); + Assert::AreEqual((text + text).c_str(), (text * 2).c_str()); + Assert::AreEqual((text + text + text).c_str(), (text * 3).c_str()); + + pcs::CppWString wtext(L"Abcd,"); + Assert::AreEqual(L"", (wtext * -1).c_str()); + Assert::AreEqual(L"", (wtext * 0).c_str()); + Assert::AreEqual(wtext.c_str(), (wtext * 1).c_str()); + Assert::AreEqual((wtext + wtext).c_str(), (wtext * 2).c_str()); + Assert::AreEqual((wtext + wtext + wtext).c_str(), (wtext * 3).c_str()); + } + + TEST_METHOD(partition) + { + pcs::CppString s("abcd#123efg"); + std::vector res{ s.partition("#123") }; + Assert::AreEqual("abcd", res[0].c_str()); + Assert::AreEqual("#123", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + + s = "abcd#123"; + res = s.partition("#123"); + Assert::AreEqual("abcd", res[0].c_str()); + Assert::AreEqual("#123", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + res = s.partition("XYZ"); + Assert::AreEqual("abcd#123", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + using namespace pcs; + res = ""_cs.partition("A"); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + pcs::CppWString ws(L"abcd#123efg"); + std::vector wres{ ws.partition(L"#123") }; + Assert::AreEqual(L"abcd", wres[0].c_str()); + Assert::AreEqual(L"#123", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + + ws = L"abcd#123"; + wres = ws.partition(L"#123"); + Assert::AreEqual(L"abcd", wres[0].c_str()); + Assert::AreEqual(L"#123", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = ws.partition(L"XYZ"); + Assert::AreEqual(L"abcd#123", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = L""_cs.partition(L"A"); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + } + + TEST_METHOD(removeprefix) + { + using namespace pcs; + pcs::CppString s("abcd"); + Assert::AreEqual("cd", s.removeprefix("ab").c_str()); + Assert::AreEqual("abcd", s.removeprefix("ba").c_str()); + Assert::AreEqual("abcd", s.removeprefix("").c_str()); + Assert::AreEqual("abaabcd", "abbabaabcd"_cs.removeprefix("abb").c_str()); + Assert::AreEqual("cdab", "abcdab"_cs.removeprefix("ab").c_str()); + + pcs::CppWString ws(L"abcd"); + Assert::AreEqual(L"cd", ws.removeprefix(L"ab").c_str()); + Assert::AreEqual(L"abcd", ws.removeprefix(L"ba").c_str()); + Assert::AreEqual(L"abcd", ws.removeprefix(L"").c_str()); + Assert::AreEqual(L"abaabcd", L"abbabaabcd"_cs.removeprefix(L"abb").c_str()); + Assert::AreEqual(L"cdab", L"abcdab"_cs.removeprefix(L"ab").c_str()); + } + + TEST_METHOD(removesuffix) + { + using namespace pcs; + pcs::CppString s("abcd"); + Assert::AreEqual("ab", s.removesuffix("cd").c_str()); + Assert::AreEqual("abcd", s.removesuffix("dc").c_str()); + Assert::AreEqual("abcd", s.removesuffix("").c_str()); + Assert::AreEqual("abbaba", "abbabaabcd"_cs.removesuffix("abcd").c_str()); + Assert::AreEqual("abcd", "abcdab"_cs.removesuffix("ab").c_str()); + + pcs::CppWString ws(L"abcd"); + Assert::AreEqual(L"ab", ws.removesuffix(L"cd").c_str()); + Assert::AreEqual(L"abcd", ws.removesuffix(L"dc").c_str()); + Assert::AreEqual(L"abcd", ws.removesuffix(L"").c_str()); + Assert::AreEqual(L"abbaba", L"abbabaabcd"_cs.removesuffix(L"abcd").c_str()); + Assert::AreEqual(L"abcd", L"abcdab"_cs.removesuffix(L"ab").c_str()); + } + + TEST_METHOD(replace) + { + pcs::CppString s("abbaa"); + Assert::AreEqual("abbaa", s.replace("e", "fff").c_str()); + Assert::AreEqual("AAbbAAAA", s.replace("a", "AA").c_str()); + Assert::AreEqual("aBBaa", s.replace("b", "B").c_str()); + + Assert::AreEqual("abbaa", s.replace("e", "fff", 0).c_str()); + Assert::AreEqual("abbaa", s.replace("a", "AA", 0).c_str()); + Assert::AreEqual("abbaa", s.replace("b", "B", 0).c_str()); + + Assert::AreEqual("abbaa", s.replace("e", "fff", 1).c_str()); + Assert::AreEqual("AAbbaa", s.replace("a", "AA", 1).c_str()); + Assert::AreEqual("aBbaa", s.replace("b", "B", 1).c_str()); + + Assert::AreEqual("abbaa", s.replace("e", "fff", 2).c_str()); + Assert::AreEqual("AAbbAAAA", s.replace("a", "AA", 3).c_str()); + Assert::AreEqual("aBBaa", s.replace("b", "B", 5).c_str()); + + pcs::CppWString ws(L"abbaa"); + Assert::AreEqual(L"abbaa", ws.replace(L"e", L"fff").c_str()); + Assert::AreEqual(L"AAbbAAAA", ws.replace(L"a", L"AA").c_str()); + Assert::AreEqual(L"aBBaa", ws.replace(L"b", L"B").c_str()); + + Assert::AreEqual(L"abbaa", ws.replace(L"e", L"fff", 0).c_str()); + Assert::AreEqual(L"abbaa", ws.replace(L"a", L"AA", 0).c_str()); + Assert::AreEqual(L"abbaa", ws.replace(L"b", L"B", 0).c_str()); + + Assert::AreEqual(L"abbaa", ws.replace(L"e", L"fff", 1).c_str()); + Assert::AreEqual(L"AAbbaa", ws.replace(L"a", L"AA", 1).c_str()); + Assert::AreEqual(L"aBbaa", ws.replace(L"b", L"B", 1).c_str()); + + Assert::AreEqual(L"abbaa", ws.replace(L"e", L"fff", 2).c_str()); + Assert::AreEqual(L"AAbbAAAA", ws.replace(L"a", L"AA", 3).c_str()); + Assert::AreEqual(L"aBBaa", ws.replace(L"b", L"B", 5).c_str()); + } + + TEST_METHOD(rfind) + { + size_t found_pos; + + pcs::CppString test_str{ "ABC0123456789." }; + for (int c = 0; c <= 255; ++c) { + char ch{ char(c) }; + Assert::AreEqual(test_str.MyBaseClass::rfind(ch), test_str.rfind(ch)); + + found_pos = test_str.substr(2).MyBaseClass::rfind(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(ch, 2)); + else + Assert::AreEqual(found_pos, test_str.rfind(ch, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(ch, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.rfind(ch, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + pcs::CppString s(ch); + Assert::AreEqual(test_str.MyBaseClass::rfind(s), test_str.rfind(s)); + found_pos = test_str.substr(2).MyBaseClass::rfind(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(s, 2)); + else + Assert::AreEqual(found_pos, test_str.rfind(s, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(s, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.rfind(s, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + if (c > 0) { + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::rfind(str), test_str.rfind(str)); + + found_pos = test_str.substr(2).MyBaseClass::rfind(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(str, 2)); + else + Assert::AreEqual(found_pos, test_str.rfind(str, 2) - 2); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind(str, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, test_str.rfind(str, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + } + } + Assert::AreEqual(pcs::CppString::npos, test_str.rfind("A", 1)); + Assert::AreEqual(test_str.size(), test_str.rfind("")); + Assert::AreEqual(pcs::CppString::npos, test_str.rfind(".", 14)); + Assert::AreEqual(size_t(13), test_str.rfind(".", 13)); + + pcs::CppWString wtest{ L"ABC0123456789." }; + for (int wc = 0; wc <= 0xffff; ++wc) { + wchar_t wch{ wchar_t(wc) }; + + found_pos = wtest.substr(2).MyBaseClass::rfind(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(wch, 2)); + else + Assert::AreEqual(found_pos, wtest.rfind(wch, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(wch, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.rfind(wch, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + pcs::CppWString ws(wch); + Assert::AreEqual(wtest.MyBaseClass::rfind(ws), wtest.rfind(ws)); + + found_pos = wtest.substr(2).MyBaseClass::rfind(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(ws, 2)); + else + Assert::AreEqual(found_pos, wtest.rfind(ws, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(ws, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.rfind(ws, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + + if (wc > 0) { + wchar_t wstr[2]{ wch, 0 }; + Assert::AreEqual(wtest.MyBaseClass::rfind(wstr), wtest.rfind(wstr)); + + found_pos = wtest.substr(2).MyBaseClass::rfind(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(wstr, 2)); + else + Assert::AreEqual(found_pos, wtest.rfind(wstr, 2) - 2); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind(wstr, 2, pcs::CppString::size_type(5 + 2 - 1))); + else + Assert::AreEqual(found_pos, wtest.rfind(wstr, 2, pcs::CppString::size_type(5 + 2 - 1)) - 2); + } + } + Assert::AreEqual(pcs::CppString::npos, wtest.rfind(L"A", 1)); + Assert::AreEqual(wtest.size(), wtest.rfind(L"")); + Assert::AreEqual(pcs::CppString::npos, wtest.rfind(L".", 14)); + Assert::AreEqual(size_t(13), wtest.rfind(L".", 13)); + } + + TEST_METHOD(rfind_n) + { + size_t found_pos; + + pcs::CppString test_str{ "ABC0123456789.ABC0123456789." }; + for (int c = 0; c <= 255; ++c) { + char ch{ char(c) }; + Assert::AreEqual(test_str.MyBaseClass::rfind(ch), test_str.rfind_n(ch, size_t(-1))); + + found_pos = test_str.substr(2).MyBaseClass::rfind(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(ch, 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).rfind_n(ch, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(ch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(ch, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.rfind_n(ch, 2, pcs::CppString::size_type(5)) - 2); + + pcs::CppString s(ch); + Assert::AreEqual(test_str.MyBaseClass::rfind(s), test_str.rfind_n(s, size_t(-1))); + found_pos = test_str.substr(2).MyBaseClass::rfind(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(s, 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).rfind_n(s, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(s); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(s, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.rfind_n(s, 2, pcs::CppString::size_type(5)) - 2); + + if (c > 0) { + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::rfind(str), test_str.rfind_n(str, size_t(-1))); + + found_pos = test_str.substr(2).MyBaseClass::rfind(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(str, test_str.size() - 2)); + else + Assert::AreEqual(found_pos, test_str.substr(2).rfind_n(str, test_str.size() - 2)); + + found_pos = test_str.substr(2, 5).MyBaseClass::rfind(str); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, test_str.rfind_n(str, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, test_str.rfind_n(str, 2, pcs::CppString::size_type(5)) - 2); + } + } + Assert::AreEqual(size_t(14), test_str.rfind_n("A", 1, test_str.size() - 1)); + Assert::AreEqual(pcs::CppString::npos, test_str.rfind_n("A", 15, 1)); + Assert::AreEqual(size_t(0), test_str.rfind_n("", size_t(-1))); + Assert::AreEqual(size_t(27), test_str.rfind_n(".", 14, test_str.size() - 14)); + Assert::AreEqual(pcs::CppString::npos, test_str.rfind_n(".", 28, 1)); + Assert::AreEqual(size_t(27), test_str.rfind_n(".", 13, test_str.size() - 13)); + + pcs::CppWString wtest{ L"ABC0123456789.ABC0123456789." }; + for (int wc = 0; wc <= 0xffff; ++wc) { + wchar_t wch{ wchar_t(wc) }; + Assert::AreEqual(wtest.MyBaseClass::rfind(wch), wtest.rfind_n(wch, size_t(-1))); + + found_pos = wtest.substr(2).MyBaseClass::rfind(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(wch, 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).rfind_n(wch, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(wch); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(wch, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.rfind_n(wch, 2, pcs::CppString::size_type(5)) - 2); + + pcs::CppWString ws(wch); + Assert::AreEqual(wtest.MyBaseClass::rfind(ws), wtest.rfind_n(ws, size_t(-1))); + found_pos = wtest.substr(2).MyBaseClass::rfind(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(ws, 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).rfind_n(ws, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(ws); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(ws, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.rfind_n(ws, 2, pcs::CppString::size_type(5)) - 2); + + if (wc > 0) { + wchar_t wstr[2]{ wch, 0 }; + Assert::AreEqual(wtest.MyBaseClass::rfind(wstr), wtest.rfind_n(wstr, size_t(-1))); + + found_pos = wtest.substr(2).MyBaseClass::rfind(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(wstr, wtest.size() - 2)); + else + Assert::AreEqual(found_pos, wtest.substr(2).rfind_n(wstr, wtest.size() - 2)); + + found_pos = wtest.substr(2, 5).MyBaseClass::rfind(wstr); + if (found_pos == pcs::CppString::npos) + Assert::AreEqual(found_pos, wtest.rfind_n(wstr, 2, pcs::CppString::size_type(5))); + else + Assert::AreEqual(found_pos, wtest.rfind_n(wstr, 2, pcs::CppString::size_type(5)) - 2); + } + } + Assert::AreEqual(size_t(14), wtest.rfind_n(L"A", 1, wtest.size() - 1)); + Assert::AreEqual(pcs::CppString::npos, wtest.rfind_n(L"A", 15, 1)); + Assert::AreEqual(size_t(0), wtest.rfind_n(L"", size_t(-1))); + Assert::AreEqual(size_t(27), wtest.rfind_n(L".", 14, wtest.size() - 14)); + Assert::AreEqual(pcs::CppString::npos, wtest.rfind_n(L".", 28, 1)); + Assert::AreEqual(size_t(27), wtest.rfind_n(L".", 13, wtest.size() - 13)); + } + + TEST_METHOD(rindex_char) + { + using string_type = pcs::CppString; + + string_type test_str{ "ABC0123456789." }; + char ch{ '3' }; + Assert::AreEqual(test_str.MyBaseClass::rfind(ch), test_str.rindex(ch)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(ch), test_str.rindex(ch, 2) - 2); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::rfind(ch), test_str.rindex(ch, 2, string_type::size_type(5 + 2 - 1)) - 2); + + try { + const string_type::size_type pos = test_str.rindex('z'); + Assert::IsTrue(pos != pcs::CppString::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex('z', 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex('z', 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.MyBaseClass::rfind(s), test_str.rindex(s)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(s), test_str.rindex(s, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(s), test_str.rindex(s, 3, string_type::size_type(5 + 3 - 1)) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.rindex(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::rfind(str), test_str.rindex(str)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(str), test_str.rindex(str, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(str), test_str.rindex(str, 3, string_type::size_type(5 + 3 - 1)) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.rindex(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(rindex_wchart) + { + using string_type = pcs::CppWString; + + string_type test_str(L"ABC0123456789."); + wchar_t ch{ L'3' }; + Assert::AreEqual(test_str.MyBaseClass::rfind(ch), test_str.rindex(ch)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(ch), test_str.rindex(ch, 2) - 2); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::rfind(ch), test_str.rindex(ch, 2, string_type::size_type(5 + 2 - 1)) - 2); + try { + const string_type::size_type pos = test_str.rindex('z'); + Assert::IsTrue(pos != pcs::CppString::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex('z', 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex('z', 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.MyBaseClass::rfind(s), test_str.rindex(s)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(s), test_str.rindex(s, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(s), test_str.rindex(s, 3, string_type::size_type(5 + 3 - 1)) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.rindex(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + wchar_t str[2]{ ch, 0 }; + Assert::AreEqual(test_str.MyBaseClass::rfind(str), test_str.rindex(str)); + Assert::AreEqual(test_str.substr(2).MyBaseClass::rfind(str), test_str.rindex(str, 2) - 2); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(str), test_str.rindex(str, 3, string_type::size_type(5 + 3 - 1)) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.rindex(s); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + try { + const string_type::size_type pos = test_str.rindex(s, 2, string_type::size_type(5 + 2 - 1)); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(rindex_n_char) + { + using string_type = pcs::CppString; + + pcs::CppString test_str{ "ABC0123456789." }; + char ch{ '3' }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(ch), test_str.rindex_n(ch, 20)); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::rfind(ch), test_str.rindex_n(ch, 2, 5) - 2); + try { + const string_type::size_type pos = test_str.rindex_n('z', 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n('z', 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + pcs::CppString s(ch); + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(s), test_str.rindex_n(s, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(s), test_str.rindex_n(s, 3, 5) - 3); + s = 'z'; + try { + const string_type::size_type pos = test_str.rindex_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + char str[2]{ ch, 0 }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(str), test_str.rindex_n(str, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(str), test_str.rindex_n(str, 3, 5) - 3); + str[0] = 'z'; + try { + const string_type::size_type pos = test_str.rindex_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(rindex_n_wchar_t) + { + using string_type = pcs::CppWString; + + string_type test_str{ L"ABC0123456789." }; + wchar_t ch{ L'3' }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(ch), test_str.rindex_n(ch, 20)); + Assert::AreEqual(test_str.substr(2, 5).MyBaseClass::rfind(ch), test_str.rindex_n(ch, 2, 5) - 2); + try { + const string_type::size_type pos = test_str.rindex_n('z', 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n('z', 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + string_type s(ch); + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(s), test_str.rindex_n(s, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(s), test_str.rindex_n(s, 3, 5) - 3); + try { + const string_type::size_type pos = test_str.rindex_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + + wchar_t str[2]{ ch, 0 }; + Assert::AreEqual(test_str.substr(0, 20).MyBaseClass::rfind(str), test_str.rindex_n(str, 20)); + Assert::AreEqual(test_str.substr(3, 5).MyBaseClass::rfind(str), test_str.rindex_n(str, 3, 5) - 3); + str[0] = L'z'; + try { + const string_type::size_type pos = test_str.rindex_n(s, 20); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + try { + const string_type::size_type pos = test_str.rindex_n(s, 2, 5); + Assert::IsTrue(pos != string_type::npos); + } + catch (const string_type::NotFoundException e) { /* ok case! */ } + } + + TEST_METHOD(rjust) + { + pcs::CppString s("abc"); + Assert::AreEqual("abc", s.rjust(1).c_str()); + Assert::AreEqual("abc", s.rjust(2).c_str()); + Assert::AreEqual("abc", s.rjust(3).c_str()); + Assert::AreEqual("abc ", s.rjust(4).c_str()); + Assert::AreEqual("abc ", s.rjust(5).c_str()); + Assert::AreEqual("abc", s.rjust(1).c_str()); + Assert::AreEqual("abc", s.rjust(2).c_str()); + Assert::AreEqual("abc", s.rjust(3).c_str()); + Assert::AreEqual("abc.", s.rjust(4, '.').c_str()); + Assert::AreEqual("abc..", s.rjust(5, '.').c_str()); + + pcs::CppWString ws(L"abc"); + Assert::AreEqual(L"abc", ws.rjust(1).c_str()); + Assert::AreEqual(L"abc", ws.rjust(2).c_str()); + Assert::AreEqual(L"abc", ws.rjust(3).c_str()); + Assert::AreEqual(L"abc ", ws.rjust(4).c_str()); + Assert::AreEqual(L"abc ", ws.rjust(5).c_str()); + Assert::AreEqual(L"abc", ws.rjust(1).c_str()); + Assert::AreEqual(L"abc", ws.rjust(2).c_str()); + Assert::AreEqual(L"abc", ws.rjust(3).c_str()); + Assert::AreEqual(L"abc.", ws.rjust(4, '.').c_str()); + Assert::AreEqual(L"abc..", ws.rjust(5, '.').c_str()); + } + + TEST_METHOD(rpartition) + { + pcs::CppString s("abcd#123efg#123hij"); + std::vector res{ s.rpartition("#123") }; + Assert::AreEqual("abcd#123efg", res[0].c_str()); + Assert::AreEqual("#123", res[1].c_str()); + Assert::AreEqual("hij", res[2].c_str()); + + s = "abcd#123fgh#123"; + res = s.rpartition("#123"); + Assert::AreEqual("abcd#123fgh", res[0].c_str()); + Assert::AreEqual("#123", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + res = s.rpartition("XYZ"); + Assert::AreEqual("abcd#123fgh#123", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + using namespace pcs; + res = ""_cs.rpartition("A"); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + pcs::CppWString ws(L"abcd#123efg#123hij"); + std::vector wres{ ws.rpartition(L"#123") }; + Assert::AreEqual(L"abcd#123efg", wres[0].c_str()); + Assert::AreEqual(L"#123", wres[1].c_str()); + Assert::AreEqual(L"hij", wres[2].c_str()); + + ws = L"abcd#123fgh#123"; + wres = ws.rpartition(L"#123"); + Assert::AreEqual(L"abcd#123fgh", wres[0].c_str()); + Assert::AreEqual(L"#123", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = ws.rpartition(L"XYZ"); + Assert::AreEqual(L"abcd#123fgh#123", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = L""_cs.rpartition(L"A"); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + } + + TEST_METHOD(rsplit) + { + pcs::CppString s(" abcd efg hij klmn "); + std::vector res{ s.rsplit() }; + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit(0); + Assert::AreEqual(pcs::CppString::size_type(1), res.size()); + Assert::AreEqual(" abcd efg hij klmn ", res[0].c_str()); + + res = s.rsplit(1); + Assert::AreEqual(pcs::CppString::size_type(2), res.size()); + Assert::AreEqual(" abcd efg hij klmn ", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + + res = s.rsplit(2); + Assert::AreEqual(pcs::CppString::size_type(3), res.size()); + Assert::AreEqual(" abcd efg hij klmn", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + res = s.rsplit(3); + Assert::AreEqual(pcs::CppString::size_type(4), res.size()); + Assert::AreEqual(" abcd efg hij", res[0].c_str()); + Assert::AreEqual("klmn", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + + res = s.rsplit(4); + Assert::AreEqual(pcs::CppString::size_type(5), res.size()); + Assert::AreEqual(" abcd efg ", res[0].c_str()); + Assert::AreEqual("hij", res[1].c_str()); + Assert::AreEqual("klmn", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + + res = s.rsplit(5); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual(" abcd efg ", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("hij", res[2].c_str()); + Assert::AreEqual("klmn", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + + res = s.rsplit(6); + Assert::AreEqual(pcs::CppString::size_type(7), res.size()); + Assert::AreEqual(" abcd efg", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("hij", res[3].c_str()); + Assert::AreEqual("klmn", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("", res[6].c_str()); + + res = s.rsplit(7); + Assert::AreEqual(pcs::CppString::size_type(8), res.size()); + Assert::AreEqual(" abcd", res[0].c_str()); + Assert::AreEqual("efg", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("hij", res[4].c_str()); + Assert::AreEqual("klmn", res[5].c_str()); + Assert::AreEqual("", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + + res = s.rsplit(8); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit(9); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit(10); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + s = pcs::CppString(5, ' '); //" "_cs; + res = s.rsplit(); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + + + using namespace pcs; + s = " abcd#123efg#123hij #123#123klmn "_cs; + res = s.rsplit("#123"); + Assert::AreEqual(pcs::CppString::size_type(5), res.size()); + Assert::AreEqual(" abcd", res[0].c_str()); + Assert::AreEqual("efg", res[1].c_str()); + Assert::AreEqual("hij ", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("klmn ", res[4].c_str()); + + s = "#123#123abcd#123123efg#123hij #123#123klmn #123#123"_cs; + res = s.rsplit("#123"); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("123efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit("#123", 1); + Assert::AreEqual(pcs::CppString::size_type(2), res.size()); + Assert::AreEqual("#123#123abcd#123123efg#123hij #123#123klmn #123", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + + res = s.rsplit("#123", 2); + Assert::AreEqual(pcs::CppString::size_type(3), res.size()); + Assert::AreEqual("#123#123abcd#123123efg#123hij #123#123klmn ", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + + res = s.rsplit("#123", 3); + Assert::AreEqual(pcs::CppString::size_type(4), res.size()); + Assert::AreEqual("#123#123abcd#123123efg#123hij #123", res[0].c_str()); + Assert::AreEqual("klmn ", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + + res = s.rsplit("#123", 4); + Assert::AreEqual(pcs::CppString::size_type(5), res.size()); + Assert::AreEqual("#123#123abcd#123123efg#123hij ", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("klmn ", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + + res = s.rsplit("#123", 5); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual("#123#123abcd#123123efg", res[0].c_str()); + Assert::AreEqual("hij ", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("klmn ", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + + res = s.rsplit("#123", 6); + Assert::AreEqual(pcs::CppString::size_type(7), res.size()); + Assert::AreEqual("#123#123abcd", res[0].c_str()); + Assert::AreEqual("123efg", res[1].c_str()); + Assert::AreEqual("hij ", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("klmn ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("", res[6].c_str()); + + res = s.rsplit("#123", 7); + Assert::AreEqual(pcs::CppString::size_type(8), res.size()); + Assert::AreEqual("#123", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("123efg", res[2].c_str()); + Assert::AreEqual("hij ", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("klmn ", res[5].c_str()); + Assert::AreEqual("", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + + res = s.rsplit("#123", 8); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("123efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit("#123", 9); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("123efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.rsplit("#123", 10); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("123efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + + pcs::CppWString ws(L" abcd efg hij klmn "); + std::vector wres{ ws.rsplit() }; + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(0); + Assert::AreEqual(pcs::CppWString::size_type(1), wres.size()); + Assert::AreEqual(L" abcd efg hij klmn ", wres[0].c_str()); + + wres = ws.rsplit(1); + Assert::AreEqual(pcs::CppWString::size_type(2), wres.size()); + Assert::AreEqual(L" abcd efg hij klmn ", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + + wres = ws.rsplit(2); + Assert::AreEqual(pcs::CppWString::size_type(3), wres.size()); + Assert::AreEqual(L" abcd efg hij klmn", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = ws.rsplit(3); + Assert::AreEqual(pcs::CppWString::size_type(4), wres.size()); + Assert::AreEqual(L" abcd efg hij", wres[0].c_str()); + Assert::AreEqual(L"klmn", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + + wres = ws.rsplit(4); + Assert::AreEqual(pcs::CppWString::size_type(5), wres.size()); + Assert::AreEqual(L" abcd efg ", wres[0].c_str()); + Assert::AreEqual(L"hij", wres[1].c_str()); + Assert::AreEqual(L"klmn", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + + wres = ws.rsplit(5); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L" abcd efg ", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"hij", wres[2].c_str()); + Assert::AreEqual(L"klmn", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + + wres = ws.rsplit(6); + Assert::AreEqual(pcs::CppWString::size_type(7), wres.size()); + Assert::AreEqual(L" abcd efg", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"hij", wres[3].c_str()); + Assert::AreEqual(L"klmn", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"", wres[6].c_str()); + + wres = ws.rsplit(7); + Assert::AreEqual(pcs::CppWString::size_type(8), wres.size()); + Assert::AreEqual(L" abcd", wres[0].c_str()); + Assert::AreEqual(L"efg", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"hij", wres[4].c_str()); + Assert::AreEqual(L"klmn", wres[5].c_str()); + Assert::AreEqual(L"", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + + wres = ws.rsplit(8); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(9); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(10); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + ws = pcs::CppWString(5, ' '); //L" "_cs; + wres = ws.rsplit(); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + + ws = L" abcd#123efg#123hij #123#123klmn "_cs; + wres = ws.rsplit(L"#123"); + Assert::AreEqual(pcs::CppWString::size_type(5), wres.size()); + Assert::AreEqual(L" abcd", wres[0].c_str()); + Assert::AreEqual(L"efg", wres[1].c_str()); + Assert::AreEqual(L"hij ", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"klmn ", wres[4].c_str()); + + ws = L"#123#123abcd#123123efg#123hij #123#123klmn #123#123"_cs; + wres = ws.rsplit(L"#123"); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"123efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(L"#123", 1); + Assert::AreEqual(pcs::CppWString::size_type(2), wres.size()); + Assert::AreEqual(L"#123#123abcd#123123efg#123hij #123#123klmn #123", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + + wres = ws.rsplit(L"#123", 2); + Assert::AreEqual(pcs::CppWString::size_type(3), wres.size()); + Assert::AreEqual(L"#123#123abcd#123123efg#123hij #123#123klmn ", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + + wres = ws.rsplit(L"#123", 3); + Assert::AreEqual(pcs::CppWString::size_type(4), wres.size()); + Assert::AreEqual(L"#123#123abcd#123123efg#123hij #123", wres[0].c_str()); + Assert::AreEqual(L"klmn ", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + + wres = ws.rsplit(L"#123", 4); + Assert::AreEqual(pcs::CppWString::size_type(5), wres.size()); + Assert::AreEqual(L"#123#123abcd#123123efg#123hij ", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"klmn ", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + + wres = ws.rsplit(L"#123", 5); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L"#123#123abcd#123123efg", wres[0].c_str()); + Assert::AreEqual(L"hij ", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"klmn ", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + + wres = ws.rsplit(L"#123", 6); + Assert::AreEqual(pcs::CppWString::size_type(7), wres.size()); + Assert::AreEqual(L"#123#123abcd", wres[0].c_str()); + Assert::AreEqual(L"123efg", wres[1].c_str()); + Assert::AreEqual(L"hij ", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"klmn ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"", wres[6].c_str()); + + wres = ws.rsplit(L"#123", 7); + Assert::AreEqual(pcs::CppWString::size_type(8), wres.size()); + Assert::AreEqual(L"#123", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"123efg", wres[2].c_str()); + Assert::AreEqual(L"hij ", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"klmn ", wres[5].c_str()); + Assert::AreEqual(L"", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + + wres = ws.rsplit(L"#123", 8); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"123efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(L"#123", 9); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"123efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.rsplit(L"#123", 10); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"123efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + } + + TEST_METHOD(rstrip) + { + pcs::CppString s(" abcd efg "); + pcs::CppString sres{ s.rstrip() }; + Assert::AreEqual(" abcd efg", sres.c_str()); + + using namespace pcs; + s = " abcd efg hij"_cs; + sres = s.rstrip(); + Assert::AreEqual(" abcd efg hij", sres.c_str()); + + s = ""_cs; + sres = s.rstrip(); + Assert::AreEqual("", sres.c_str()); + + s = " "_cs; + sres = s.rstrip(); + Assert::AreEqual("", sres.c_str()); + + s = "#124abcd#124efg#1241#24#142"_cs; + sres = s.rstrip("#124"); + Assert::AreEqual("#124abcd#124efg", sres.c_str()); + + s = "#124abcd#124efg#124#124hij"_cs; + sres = s.rstrip("#124"); + Assert::AreEqual("#124abcd#124efg#124#124hij", sres.c_str()); + + s = "#124#142#241124#421#"; + sres = s.rstrip("#124"); + Assert::AreEqual("", sres.c_str()); + + pcs::CppWString ws(L" abcd efg "); + pcs::CppWString wsres{ ws.rstrip() }; + Assert::AreEqual(L" abcd efg", wsres.c_str()); + + ws = L" abcd efg hij"_cs; + wsres = ws.rstrip(); + Assert::AreEqual(L" abcd efg hij", wsres.c_str()); + + ws = L""_cs; + wsres = ws.rstrip(); + Assert::AreEqual(L"", wsres.c_str()); + + ws = L" "_cs; + wsres = ws.rstrip(); + Assert::AreEqual(L"", wsres.c_str()); + + ws = L"#124abcd#124efg#124#124#124"_cs; + wsres = ws.rstrip(L"#124"); + Assert::AreEqual(L"#124abcd#124efg", wsres.c_str()); + + ws = L"#124abcd#124efg#124#124hij"_cs; + wsres = ws.rstrip(L"#124"); + Assert::AreEqual(L"#124abcd#124efg#124#124hij", wsres.c_str()); + + ws = L"#124#124#124#124#124"; + wsres = ws.rstrip(L"#124"); + Assert::AreEqual(L"", wsres.c_str()); + } + + TEST_METHOD(split) + { + pcs::CppString s(" abcd efg hij klmn "); + std::vector res{ s.rsplit() }; + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split(0); + Assert::AreEqual(pcs::CppString::size_type(1), res.size()); + Assert::AreEqual(" abcd efg hij klmn ", res[0].c_str()); + + res = s.split(1); + Assert::AreEqual(pcs::CppString::size_type(2), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd efg hij klmn ", res[1].c_str()); + + res = s.split(2); + Assert::AreEqual(pcs::CppString::size_type(3), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg hij klmn ", res[2].c_str()); + + res = s.split(3); + Assert::AreEqual(pcs::CppString::size_type(4), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual(" hij klmn ", res[3].c_str()); + + res = s.split(4); + Assert::AreEqual(pcs::CppString::size_type(5), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual(" hij klmn ", res[4].c_str()); + + res = s.split(5); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij klmn ", res[5].c_str()); + + res = s.split(6); + Assert::AreEqual(pcs::CppString::size_type(7), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + + res = s.split(7); + Assert::AreEqual(pcs::CppString::size_type(8), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual(" ", res[7].c_str()); + + res = s.split(8); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split(9); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split(10); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("abcd", res[1].c_str()); + Assert::AreEqual("efg", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("hij", res[5].c_str()); + Assert::AreEqual("klmn", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + s = pcs::CppString(5, ' '); + res = s.split(); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + + + using namespace pcs; + s = " abcd#124efg#124hij #124#124klmn "_cs; + res = s.split("#124"); + Assert::AreEqual(" abcd", res[0].c_str()); + Assert::AreEqual("efg", res[1].c_str()); + Assert::AreEqual("hij ", res[2].c_str()); + Assert::AreEqual("", res[3].c_str()); + Assert::AreEqual("klmn ", res[4].c_str()); + + s = "#124#124abcd#124124efg#124hij #124#124klmn #124#124"_cs; + res = s.split("#124"); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split("#124", 1); + Assert::AreEqual(pcs::CppString::size_type(2), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("#124abcd#124124efg#124hij #124#124klmn #124#124", res[1].c_str()); + + res = s.split("#124", 2); + Assert::AreEqual(pcs::CppString::size_type(3), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd#124124efg#124hij #124#124klmn #124#124", res[2].c_str()); + + res = s.split("#124", 3); + Assert::AreEqual(pcs::CppString::size_type(4), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg#124hij #124#124klmn #124#124", res[3].c_str()); + + res = s.split("#124", 4); + Assert::AreEqual(pcs::CppString::size_type(5), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij #124#124klmn #124#124", res[4].c_str()); + + res = s.split("#124", 5); + Assert::AreEqual(pcs::CppString::size_type(6), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("#124klmn #124#124", res[5].c_str()); + + res = s.split("#124", 6); + Assert::AreEqual(pcs::CppString::size_type(7), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn #124#124", res[6].c_str()); + + res = s.split("#124", 7); + Assert::AreEqual(pcs::CppString::size_type(8), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("#124", res[7].c_str()); + + res = s.split("#124", 8); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split("#124", 9); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + res = s.split("#124", 10); + Assert::AreEqual(pcs::CppString::size_type(9), res.size()); + Assert::AreEqual("", res[0].c_str()); + Assert::AreEqual("", res[1].c_str()); + Assert::AreEqual("abcd", res[2].c_str()); + Assert::AreEqual("124efg", res[3].c_str()); + Assert::AreEqual("hij ", res[4].c_str()); + Assert::AreEqual("", res[5].c_str()); + Assert::AreEqual("klmn ", res[6].c_str()); + Assert::AreEqual("", res[7].c_str()); + Assert::AreEqual("", res[8].c_str()); + + + pcs::CppWString ws(L" abcd efg hij klmn "); + std::vector wres{ ws.split() }; + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(0); + Assert::AreEqual(pcs::CppWString::size_type(1), wres.size()); + Assert::AreEqual(L" abcd efg hij klmn ", wres[0].c_str()); + + wres = ws.split(1); + Assert::AreEqual(pcs::CppWString::size_type(2), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd efg hij klmn ", wres[1].c_str()); + + + wres = ws.split(2); + Assert::AreEqual(pcs::CppWString::size_type(3), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg hij klmn ", wres[2].c_str()); + + wres = ws.split(3); + Assert::AreEqual(pcs::CppWString::size_type(4), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L" hij klmn ", wres[3].c_str()); + + wres = ws.split(4); + Assert::AreEqual(pcs::CppWString::size_type(5), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L" hij klmn ", wres[4].c_str()); + + wres = ws.split(5); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij klmn ", wres[5].c_str()); + + wres = ws.split(6); + Assert::AreEqual(pcs::CppWString::size_type(7), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + + wres = ws.split(7); + Assert::AreEqual(pcs::CppWString::size_type(8), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L" ", wres[7].c_str()); + + wres = ws.split(8); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(9); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(10); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"abcd", wres[1].c_str()); + Assert::AreEqual(L"efg", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"hij", wres[5].c_str()); + Assert::AreEqual(L"klmn", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + ws = pcs::CppWString(5, ' '); //L" "_cs; + wres = ws.split(); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + + ws = L" abcd#124efg#124hij #124#124klmn "_cs; + wres = ws.split(L"#124"); + Assert::AreEqual(L" abcd", wres[0].c_str()); + Assert::AreEqual(L"efg", wres[1].c_str()); + Assert::AreEqual(L"hij ", wres[2].c_str()); + Assert::AreEqual(L"", wres[3].c_str()); + Assert::AreEqual(L"klmn ", wres[4].c_str()); + + ws = L"#124#124abcd#124124efg#124hij #124#124klmn #124#124"_cs; + wres = ws.split(L"#124"); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(L"#124", 1); + Assert::AreEqual(pcs::CppWString::size_type(2), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"#124abcd#124124efg#124hij #124#124klmn #124#124", wres[1].c_str()); + + wres = ws.split(L"#124", 2); + Assert::AreEqual(pcs::CppWString::size_type(3), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd#124124efg#124hij #124#124klmn #124#124", wres[2].c_str()); + + wres = ws.split(L"#124", 3); + Assert::AreEqual(pcs::CppWString::size_type(4), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg#124hij #124#124klmn #124#124", wres[3].c_str()); + + wres = ws.split(L"#124", 4); + Assert::AreEqual(pcs::CppWString::size_type(5), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij #124#124klmn #124#124", wres[4].c_str()); + + wres = ws.split(L"#124", 5); + Assert::AreEqual(pcs::CppWString::size_type(6), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"#124klmn #124#124", wres[5].c_str()); + + wres = ws.split(L"#124", 6); + Assert::AreEqual(pcs::CppWString::size_type(7), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn #124#124", wres[6].c_str()); + + wres = ws.split(L"#124", 7); + Assert::AreEqual(pcs::CppWString::size_type(8), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"#124", wres[7].c_str()); + + wres = ws.split(L"#124", 8); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(L"#124", 9); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + + wres = ws.split(L"#124", 10); + Assert::AreEqual(pcs::CppWString::size_type(9), wres.size()); + Assert::AreEqual(L"", wres[0].c_str()); + Assert::AreEqual(L"", wres[1].c_str()); + Assert::AreEqual(L"abcd", wres[2].c_str()); + Assert::AreEqual(L"124efg", wres[3].c_str()); + Assert::AreEqual(L"hij ", wres[4].c_str()); + Assert::AreEqual(L"", wres[5].c_str()); + Assert::AreEqual(L"klmn ", wres[6].c_str()); + Assert::AreEqual(L"", wres[7].c_str()); + Assert::AreEqual(L"", wres[8].c_str()); + } + + TEST_METHOD(splitline) + { +#pragma warning(push) +#pragma warning(disable: 4566) // to get no warning when current page code is not compatible with next unicode points + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r" }; + std::vector lines{ wtext.splitlines() }; + std::vector expected{ "", "abc", "cde", "efg", "ghi", "ijk", "klm", "mno", "", "opq", "qrs", "stu", "uvw", "wxy", "zzz", "." }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r\n" }; + std::vector lines{ wtext.splitlines() }; + std::vector expected{ "", "abc", "cde", "efg", "ghi", "ijk", "klm", "mno", "", "opq", "qrs", "stu", "uvw", "wxy", "zzz", "." }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\n\r" }; + std::vector lines{ wtext.splitlines() }; + std::vector expected{ "", "abc", "cde", "efg", "ghi", "ijk", "klm", "mno", "", "opq", "qrs", "stu", "uvw", "wxy", "zzz", ".", "" }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r" }; + std::vector lines{ wtext.splitlines(true) }; + std::vector expected{ + "\v", "abc\013", "cde\f", "efg\x0c", "ghi\x1c", "ijk\x1d", "klm\x1d", "mno\r\n", + "\n", "opq\r", "qrs\v", "stu\r", "uvw\n", "wxy\r\n", "zzz\x0c", ".\r" + }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r\n" }; + std::vector lines{ wtext.splitlines(true) }; + std::vector expected{ + "\v", "abc\013", "cde\f", "efg\x0c", "ghi\x1c", "ijk\x1d", "klm\x1d", "mno\r\n", + "\n", "opq\r", "qrs\v", "stu\r", "uvw\n", "wxy\r\n", "zzz\x0c", ".\r\n" + }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + { + pcs::CppString wtext{ "\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\n\r" }; + std::vector lines{ wtext.splitlines(true) }; + std::vector expected{ + "\v", "abc\013", "cde\f", "efg\x0c", "ghi\x1c", "ijk\x1d", "klm\x1d", "mno\r\n", + "\n", "opq\r", "qrs\v", "stu\r", "uvw\n", "wxy\r\n", "zzz\x0c", ".\n", "\r" + }; + auto exp_it{ expected.cbegin() }; + auto lin_it{ lines.cbegin() }; + for (; lin_it != lines.cend() && exp_it != expected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != lines.cend()); + Assert::IsFalse(exp_it != expected.cend()); + } + + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r" }; + std::vector wlines{ wtext.splitlines() }; + std::vector wexpected{ L"", L"abc", L"cde", L"efg", L"ghi", L"ijk", L"klm", L"mno", L"", L"opq", L"qrs", L"stu", L"uvw", L"wxy", L"zzz", L"."}; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r\n" }; + std::vector wlines{ wtext.splitlines() }; + std::vector wexpected{ L"", L"abc", L"cde", L"efg", L"ghi", L"ijk", L"klm", L"mno", L"", L"opq", L"qrs", L"stu", L"uvw", L"wxy", L"zzz", L"."}; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\n\r" }; + std::vector wlines{ wtext.splitlines() }; + std::vector wexpected{ L"", L"abc", L"cde", L"efg", L"ghi", L"ijk", L"klm", L"mno", L"", L"opq", L"qrs", L"stu", L"uvw", L"wxy", L"zzz", L".", L""}; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r" }; + std::vector wlines{ wtext.splitlines(true) }; + std::vector wexpected{ + L"\v", L"abc\013", L"cde\f", L"efg\x0c", L"ghi\x1c", L"ijk\x1d", L"klm\x1d", L"mno\r\n", + L"\n", L"opq\r", L"qrs\v", L"stu\r", L"uvw\n", L"wxy\r\n", L"zzz\x0c", L".\r" + }; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\r\n" }; + std::vector wlines{ wtext.splitlines(true) }; + std::vector wexpected{ + L"\v", L"abc\013", L"cde\f", L"efg\x0c", L"ghi\x1c", L"ijk\x1d", L"klm\x1d", L"mno\r\n", + L"\n", L"opq\r", L"qrs\v", L"stu\r", L"uvw\n", L"wxy\r\n", L"zzz\x0c", L".\r\n" + }; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + + { + pcs::CppWString wtext{ L"\vabc\013cde\fefg\x0cghi\x1cijk\x1dklm\x1dmno\r\n\nopq\rqrs\vstu\ruvw\nwxy\r\nzzz\x0c.\n\r" }; + std::vector wlines{ wtext.splitlines(true) }; + std::vector wexpected{ + L"\v", L"abc\013", L"cde\f", L"efg\x0c", L"ghi\x1c", L"ijk\x1d", L"klm\x1d", L"mno\r\n", + L"\n", L"opq\r", L"qrs\v", L"stu\r", L"uvw\n", L"wxy\r\n", L"zzz\x0c", L".\n", L"\r" + }; + auto exp_it{ wexpected.cbegin() }; + auto lin_it{ wlines.cbegin() }; + for (; lin_it != wlines.cend() && exp_it != wexpected.cend(); ++lin_it, ++exp_it) { + Assert::AreEqual(exp_it->c_str(), lin_it->c_str()); + } + Assert::IsFalse(lin_it != wlines.cend()); + Assert::IsFalse(exp_it != wexpected.cend()); + } + +#pragma warning(pop) + + } + + TEST_METHOD(startswith) + { + pcs::CppString text("Abcdef"); + const size_t len{ text.size() }; + + Assert::IsTrue(text.startswith("A")); + Assert::IsTrue(text.startswith("Ab")); + Assert::IsTrue(text.startswith("Abc")); + Assert::IsTrue(text.startswith("Abcd")); + Assert::IsTrue(text.startswith("Abcde")); + Assert::IsTrue(text.startswith("Abcdef")); + Assert::IsFalse(text.startswith("Abcdefg")); + Assert::IsFalse(text.startswith("a")); + Assert::IsFalse(text.startswith("def")); + + Assert::IsTrue(text.startswith("b", 1)); + Assert::IsTrue(text.startswith("bc", 1)); + Assert::IsTrue(text.startswith("bcd", 1)); + Assert::IsFalse(text.startswith("bcdefg", 1)); + Assert::IsFalse(text.startswith("A", 1)); + Assert::IsFalse(text.startswith("def", 1)); + + Assert::IsTrue(text.startswith("c", 2)); + Assert::IsTrue(text.startswith("cd", 2)); + Assert::IsTrue(text.startswith("cde", 2)); + Assert::IsFalse(text.startswith("cdefg", 2)); + Assert::IsFalse(text.startswith("Ab", 2)); + Assert::IsFalse(text.startswith("def", 2)); + + Assert::IsTrue(text.startswith("d", 3)); + Assert::IsTrue(text.startswith("de", 3)); + Assert::IsTrue(text.startswith("def", 3)); + Assert::IsFalse(text.startswith("defg", 3)); + Assert::IsFalse(text.startswith("Abc", 3)); + Assert::IsFalse(text.startswith("ef", 3)); + + Assert::IsTrue(text.startswith("e", 4)); + Assert::IsTrue(text.startswith("ef", 4)); + Assert::IsFalse(text.startswith("efg", 4)); + Assert::IsFalse(text.startswith("Abcd", 4)); + Assert::IsFalse(text.startswith("f", 4)); + + Assert::IsTrue(text.startswith("f", 5)); + Assert::IsFalse(text.startswith("fg", 5)); + Assert::IsFalse(text.startswith("Abcde", 5)); + Assert::IsFalse(text.startswith("g", 5)); + + Assert::IsTrue(text.startswith("A", 0, 0)); + Assert::IsFalse(text.startswith("b", 0, 0)); + Assert::IsTrue(text.startswith("b", 1, 3)); + Assert::IsTrue(text.startswith("bc", 1, 3)); + Assert::IsTrue(text.startswith("bcd", 1, 3)); + Assert::IsFalse(text.startswith("bcde", 1, 3)); + + Assert::IsTrue(text.startswith("", 5, 2)); + Assert::IsTrue(text.startswith("", 15, 16)); + + Assert::IsTrue(text.startswith({ "ghi", "abca", "Abcd" }, 0, len - 2)); + Assert::IsFalse(text.startswith({ "def", "ghi" }, 0, len - 4)); + Assert::IsFalse(text.startswith({ "def", "ghi", "Abcd" }, 0, len - 4)); + + + pcs::CppWString wtext(L"Abcdef"); + + Assert::IsTrue(wtext.startswith(L"A")); + Assert::IsTrue(wtext.startswith(L"Ab")); + Assert::IsTrue(wtext.startswith(L"Abc")); + Assert::IsTrue(wtext.startswith(L"Abcd")); + Assert::IsTrue(wtext.startswith(L"Abcde")); + Assert::IsTrue(wtext.startswith(L"Abcdef")); + Assert::IsFalse(wtext.startswith(L"Abcdefg")); + Assert::IsFalse(wtext.startswith(L"a")); + Assert::IsFalse(wtext.startswith(L"def")); + + Assert::IsTrue(wtext.startswith(L"b", 1)); + Assert::IsTrue(wtext.startswith(L"bc", 1)); + Assert::IsTrue(wtext.startswith(L"bcd", 1)); + Assert::IsFalse(wtext.startswith(L"bcdefg", 1)); + Assert::IsFalse(wtext.startswith(L"A", 1)); + Assert::IsFalse(wtext.startswith(L"def", 1)); + + Assert::IsTrue(wtext.startswith(L"c", 2)); + Assert::IsTrue(wtext.startswith(L"cd", 2)); + Assert::IsTrue(wtext.startswith(L"cde", 2)); + Assert::IsFalse(wtext.startswith(L"cdefg", 2)); + Assert::IsFalse(wtext.startswith(L"Ab", 2)); + Assert::IsFalse(wtext.startswith(L"def", 2)); + + Assert::IsTrue(wtext.startswith(L"d", 3)); + Assert::IsTrue(wtext.startswith(L"de", 3)); + Assert::IsTrue(wtext.startswith(L"def", 3)); + Assert::IsFalse(wtext.startswith(L"defg", 3)); + Assert::IsFalse(wtext.startswith(L"Abc", 3)); + Assert::IsFalse(wtext.startswith(L"ef", 3)); + + Assert::IsTrue(wtext.startswith(L"e", 4)); + Assert::IsTrue(wtext.startswith(L"ef", 4)); + Assert::IsFalse(wtext.startswith(L"efg", 4)); + Assert::IsFalse(wtext.startswith(L"Abcd", 4)); + Assert::IsFalse(wtext.startswith(L"f", 4)); + + Assert::IsTrue(wtext.startswith(L"f", 5)); + Assert::IsFalse(wtext.startswith(L"fg", 5)); + Assert::IsFalse(wtext.startswith(L"Abcde", 5)); + Assert::IsFalse(wtext.startswith(L"g", 5)); + + Assert::IsTrue(wtext.startswith(L"A", 0, 0)); + Assert::IsFalse(wtext.startswith(L"b", 0, 0)); + Assert::IsTrue(wtext.startswith(L"b", 1, 3)); + Assert::IsTrue(wtext.startswith(L"bc", 1, 3)); + Assert::IsTrue(wtext.startswith(L"bcd", 1, 3)); + Assert::IsFalse(wtext.startswith(L"bcde", 1, 3)); + + Assert::IsTrue(wtext.startswith(L"", 5, 2)); + Assert::IsTrue(wtext.startswith(L"", 15, 16)); + + Assert::IsTrue(wtext.startswith({ L"ghi", L"abca", L"Abcd" }, 0, len - 2)); + Assert::IsFalse(wtext.startswith({ L"def", L"ghi" }, 0, len - 4)); + Assert::IsFalse(wtext.startswith({ L"def", L"ghi", L"Abcd" }, 0, len - 4)); + } + + TEST_METHOD(startswith_n) + { + pcs::CppString text("Abcdef"); + const size_t len{ text.size() }; + + Assert::IsTrue(text.startswith_n("A", 2)); + Assert::IsTrue(text.startswith_n("Ab", 2)); + Assert::IsTrue(text.startswith_n("Abc", 3)); + Assert::IsTrue(text.startswith_n("Abcd", 5)); + Assert::IsTrue(text.startswith_n("Abcde", 5)); + Assert::IsTrue(text.startswith_n("Abcdef", 7)); + Assert::IsFalse(text.startswith_n("Abcdefg", 11)); + Assert::IsFalse(text.startswith_n("a", 2)); + Assert::IsFalse(text.startswith_n("def", len)); + + Assert::IsTrue(text.startswith_n("b", 1, 1)); + Assert::IsTrue(text.startswith_n("bc", 1, 3)); + Assert::IsTrue(text.startswith_n("bcd", 1, 3)); + Assert::IsFalse(text.startswith_n("bcdefg", 1, 5)); + Assert::IsFalse(text.startswith_n("A", 1, 8)); + Assert::IsFalse(text.startswith_n("def", 1, 2)); + + Assert::IsTrue(text.startswith_n("c", 2, 1)); + Assert::IsTrue(text.startswith_n("cd", 2, 2)); + Assert::IsTrue(text.startswith_n("cde", 2, 4)); + Assert::IsFalse(text.startswith_n("cdefg", 2, 6)); + Assert::IsFalse(text.startswith_n("Ab", 2, 2)); + Assert::IsFalse(text.startswith_n("def", 2, 5)); + + Assert::IsTrue(text.startswith_n("d", 3, 2)); + Assert::IsTrue(text.startswith_n("de", 3, 2)); + Assert::IsTrue(text.startswith_n("def", 3, 4)); + Assert::IsFalse(text.startswith_n("defg", 3, 5)); + Assert::IsFalse(text.startswith_n("Abc", 3, 1)); + Assert::IsFalse(text.startswith_n("ef", 3, 2)); + + Assert::IsTrue(text.startswith_n("e", 4, 1)); + Assert::IsTrue(text.startswith_n("ef", 4, 3)); + Assert::IsFalse(text.startswith_n("efg", 4, 5)); + Assert::IsFalse(text.startswith_n("Abcd", 4, 7)); + Assert::IsFalse(text.startswith_n("f", 4, 9)); + + Assert::IsTrue(text.startswith_n("f", 5, 2)); + Assert::IsFalse(text.startswith_n("fg", 5, 1)); + Assert::IsFalse(text.startswith_n("Abcde", 5, 8)); + Assert::IsFalse(text.startswith_n("g", 5, 11)); + + Assert::IsTrue(text.startswith_n("A", 0, 1)); + Assert::IsFalse(text.startswith_n("b", 0, 2)); + Assert::IsTrue(text.startswith_n("b", 1, 3)); + Assert::IsTrue(text.startswith_n("bc", 1, 3)); + Assert::IsTrue(text.startswith_n("bcd", 1, 3)); + Assert::IsFalse(text.startswith_n("bcde", 1, 3)); + + Assert::IsTrue(text.startswith_n("", 5, 2)); + Assert::IsTrue(text.startswith_n("", 15, 16)); + + Assert::IsTrue(text.startswith_n({ "ghi", "abca", "Abcd" }, 0, len - 2)); + Assert::IsFalse(text.startswith_n({ "def", "ghi" }, 0, len - 4)); + Assert::IsFalse(text.startswith_n({ "def", "ghi", "Abcd" }, 0, len - 4)); + + + pcs::CppWString wtext(L"Abcdef"); + const size_t wlen{ wtext.size() }; + + Assert::IsTrue(wtext.startswith_n(L"A", 2)); + Assert::IsTrue(wtext.startswith_n(L"Ab", 2)); + Assert::IsTrue(wtext.startswith_n(L"Abc", 3)); + Assert::IsTrue(wtext.startswith_n(L"Abcd", 5)); + Assert::IsTrue(wtext.startswith_n(L"Abcde", 5)); + Assert::IsTrue(wtext.startswith_n(L"Abcdef", 7)); + Assert::IsFalse(wtext.startswith_n(L"Abcdefg", 11)); + Assert::IsFalse(wtext.startswith_n(L"a", 2)); + Assert::IsFalse(wtext.startswith_n(L"def", wlen)); + + Assert::IsTrue(wtext.startswith_n(L"b", 1, 1)); + Assert::IsTrue(wtext.startswith_n(L"bc", 1, 3)); + Assert::IsTrue(wtext.startswith_n(L"bcd", 1, 3)); + Assert::IsFalse(wtext.startswith_n(L"bcdefg", 1, 5)); + Assert::IsFalse(wtext.startswith_n(L"A", 1, 8)); + Assert::IsFalse(wtext.startswith_n(L"def", 1, 2)); + + Assert::IsTrue(wtext.startswith_n(L"c", 2, 1)); + Assert::IsTrue(wtext.startswith_n(L"cd", 2, 2)); + Assert::IsTrue(wtext.startswith_n(L"cde", 2, 4)); + Assert::IsFalse(wtext.startswith_n(L"cdefg", 2, 6)); + Assert::IsFalse(wtext.startswith_n(L"Ab", 2, 2)); + Assert::IsFalse(wtext.startswith_n(L"def", 2, 5)); + + Assert::IsTrue(wtext.startswith_n(L"d", 3, 2)); + Assert::IsTrue(wtext.startswith_n(L"de", 3, 2)); + Assert::IsTrue(wtext.startswith_n(L"def", 3, 4)); + Assert::IsFalse(wtext.startswith_n(L"defg", 3, 5)); + Assert::IsFalse(wtext.startswith_n(L"Abc", 3, 1)); + Assert::IsFalse(wtext.startswith_n(L"ef", 3, 2)); + + Assert::IsTrue(wtext.startswith_n(L"e", 4, 1)); + Assert::IsTrue(wtext.startswith_n(L"ef", 4, 3)); + Assert::IsFalse(wtext.startswith_n(L"efg", 4, 5)); + Assert::IsFalse(wtext.startswith_n(L"Abcd", 4, 7)); + Assert::IsFalse(wtext.startswith_n(L"f", 4, 9)); + + Assert::IsTrue(wtext.startswith_n(L"f", 5, 2)); + Assert::IsFalse(wtext.startswith_n(L"fg", 5, 1)); + Assert::IsFalse(wtext.startswith_n(L"Abcde", 5, 8)); + Assert::IsFalse(wtext.startswith_n(L"g", 5, 11)); + + Assert::IsTrue(wtext.startswith_n(L"A", 0, 1)); + Assert::IsFalse(wtext.startswith_n(L"b", 0, 2)); + Assert::IsTrue(wtext.startswith_n(L"b", 1, 3)); + Assert::IsTrue(wtext.startswith_n(L"bc", 1, 3)); + Assert::IsTrue(wtext.startswith_n(L"bcd", 1, 3)); + Assert::IsFalse(wtext.startswith_n(L"bcde", 1, 3)); + + Assert::IsTrue(wtext.startswith_n(L"", 5, 2)); + Assert::IsTrue(wtext.startswith_n(L"", 15, 16)); + + Assert::IsTrue(wtext.startswith_n({ L"ghi", L"abca", L"Abcd" }, 0, wlen - 2)); + Assert::IsFalse(wtext.startswith_n({ L"def", L"ghi" }, 0, wlen - 4)); + Assert::IsFalse(wtext.startswith_n({ L"def", L"ghi", L"Abcd" }, 0, wlen - 4)); + } + + TEST_METHOD(strip) + { + pcs::CppString text("abcdefedcbaea"); + + Assert::AreEqual("bcdefedcbae", text.strip("a").c_str()); + Assert::AreEqual("cdefedcbae", text.strip("ba").c_str()); + Assert::AreEqual("defedcbae", text.strip("acb").c_str()); + Assert::AreEqual("efedcbae", text.strip("dacb").c_str()); + Assert::AreEqual("f", text.strip("abcde").c_str()); + Assert::AreEqual("bcdefedcb", text.strip("ea").c_str()); + Assert::AreEqual("cdefedc", text.strip("eba").c_str()); + Assert::AreEqual("abcdefedcbaea", text.strip("ghijZ").c_str()); + Assert::AreEqual("abcdefedcbaea", text.strip("ABc").c_str()); + + pcs::CppWString wtext(L"abcdefedcbaea"); + + Assert::AreEqual(L"bcdefedcbae", wtext.strip(L"a").c_str()); + Assert::AreEqual(L"cdefedcbae", wtext.strip(L"ba").c_str()); + Assert::AreEqual(L"defedcbae", wtext.strip(L"acb").c_str()); + Assert::AreEqual(L"efedcbae", wtext.strip(L"dacb").c_str()); + Assert::AreEqual(L"f", wtext.strip(L"abcde").c_str()); + Assert::AreEqual(L"bcdefedcb", wtext.strip(L"ea").c_str()); + Assert::AreEqual(L"cdefedc", wtext.strip(L"eba").c_str()); + Assert::AreEqual(L"abcdefedcbaea", wtext.strip(L"ghijZ").c_str()); + Assert::AreEqual(L"abcdefedcbaea", wtext.strip(L"ABc").c_str()); + } + + TEST_METHOD(substr) + { + pcs::CppString text("AbcDefGhi"); + + Assert::AreEqual("AbcDefGhi", text.substr(0, 9).c_str()); + Assert::AreEqual("bcDefGhi", text.substr(1, 8).c_str()); + Assert::AreEqual("cDefGhi", text.substr(2, 7).c_str()); + Assert::AreEqual("DefGhi", text.substr(3, 6).c_str()); + Assert::AreEqual("efGhi", text.substr(4, 5).c_str()); + Assert::AreEqual("fGhi", text.substr(5, 4).c_str()); + Assert::AreEqual("Ghi", text.substr(6, 3).c_str()); + Assert::AreEqual("hi", text.substr(7, 2).c_str()); + Assert::AreEqual("i", text.substr(8, 1).c_str()); + Assert::AreEqual("", text.substr(9, 0).c_str()); + + Assert::AreEqual("AbcDefGhi", text.substr(0).c_str()); + Assert::AreEqual("bcDefGhi", text.substr(1).c_str()); + Assert::AreEqual("cDefGhi", text.substr(2).c_str()); + Assert::AreEqual("DefGhi", text.substr(3).c_str()); + Assert::AreEqual("efGhi", text.substr(4).c_str()); + Assert::AreEqual("fGhi", text.substr(5).c_str()); + Assert::AreEqual("Ghi", text.substr(6).c_str()); + Assert::AreEqual("hi", text.substr(7).c_str()); + Assert::AreEqual("i", text.substr(8).c_str()); + Assert::AreEqual("", text.substr(9).c_str()); + + Assert::AreEqual("AbcDefGh", text.substr(0, 8).c_str()); + Assert::AreEqual("bcDefGh", text.substr(1, 7).c_str()); + Assert::AreEqual("cDefG", text.substr(2, 5).c_str()); + Assert::AreEqual("DefGh", text.substr(3, 5).c_str()); + Assert::AreEqual("efGh", text.substr(4, 4).c_str()); + Assert::AreEqual("fG", text.substr(5, 2).c_str()); + Assert::AreEqual("G", text.substr(6, 1).c_str()); + Assert::AreEqual("h", text.substr(7, 1).c_str()); + Assert::AreEqual("i", text.substr(8, 3).c_str()); + Assert::AreEqual("", text.substr(9, 0).c_str()); + + Assert::AreEqual("", text.substr(10, 15).c_str()); + Assert::AreEqual("", text.substr(21).c_str()); + + pcs::CppWString wtext(L"AbcDefGhi"); + + Assert::AreEqual(L"AbcDefGhi", wtext.substr(0, 9).c_str()); + Assert::AreEqual(L"bcDefGhi", wtext.substr(1, 8).c_str()); + Assert::AreEqual(L"cDefGhi", wtext.substr(2, 7).c_str()); + Assert::AreEqual(L"DefGhi", wtext.substr(3, 6).c_str()); + Assert::AreEqual(L"efGhi", wtext.substr(4, 5).c_str()); + Assert::AreEqual(L"fGhi", wtext.substr(5, 4).c_str()); + Assert::AreEqual(L"Ghi", wtext.substr(6, 3).c_str()); + Assert::AreEqual(L"hi", wtext.substr(7, 2).c_str()); + Assert::AreEqual(L"i", wtext.substr(8, 1).c_str()); + Assert::AreEqual(L"", wtext.substr(9, 0).c_str()); + + Assert::AreEqual(L"AbcDefGhi", wtext.substr(0).c_str()); + Assert::AreEqual(L"bcDefGhi", wtext.substr(1).c_str()); + Assert::AreEqual(L"cDefGhi", wtext.substr(2).c_str()); + Assert::AreEqual(L"DefGhi", wtext.substr(3).c_str()); + Assert::AreEqual(L"efGhi", wtext.substr(4).c_str()); + Assert::AreEqual(L"fGhi", wtext.substr(5).c_str()); + Assert::AreEqual(L"Ghi", wtext.substr(6).c_str()); + Assert::AreEqual(L"hi", wtext.substr(7).c_str()); + Assert::AreEqual(L"i", wtext.substr(8).c_str()); + Assert::AreEqual(L"", wtext.substr(9).c_str()); + + Assert::AreEqual(L"AbcDefGh", wtext.substr(0, 8).c_str()); + Assert::AreEqual(L"bcDefGh", wtext.substr(1, 7).c_str()); + Assert::AreEqual(L"cDefG", wtext.substr(2, 5).c_str()); + Assert::AreEqual(L"DefGh", wtext.substr(3, 5).c_str()); + Assert::AreEqual(L"efGh", wtext.substr(4, 4).c_str()); + Assert::AreEqual(L"fG", wtext.substr(5, 2).c_str()); + Assert::AreEqual(L"G", wtext.substr(6, 1).c_str()); + Assert::AreEqual(L"h", wtext.substr(7, 1).c_str()); + Assert::AreEqual(L"i", wtext.substr(8, 3).c_str()); + Assert::AreEqual(L"", wtext.substr(9, 0).c_str()); + + Assert::AreEqual(L"", wtext.substr(10, 15).c_str()); + Assert::AreEqual(L"", wtext.substr(21).c_str()); + } + + TEST_METHOD(swapcase) + { + pcs::CppString s(255, '\0'); + for (int i : std::views::iota(0, 256)) + s[i] = pcs::CppString::value_type(i); + pcs::CppString res{ s.swapcase() }; + for (auto const [s, r] : std::views::zip(s, res)) { + if (std::islower(s)) + Assert::IsTrue(std::isupper(r)); + else if (std::isupper(s)) + Assert::IsTrue(std::islower(r)); + else + Assert::AreEqual(s, r); + } + + pcs::CppWString ws(0xffff, '\0'); + for (int i : std::views::iota(0, 0x1'0000)) + ws[i] = pcs::CppWString::value_type(i); + pcs::CppWString wres{ ws.swapcase() }; + for (auto const [ws, wr] : std::views::zip(ws, wres)) { + if (std::islower(ws)) + Assert::IsTrue(std::isupper(wr)); + else if (std::isupper(ws)) + Assert::IsTrue(std::islower(wr)); + else + Assert::AreEqual(ws, wr); + } + } + + TEST_METHOD(title) + { + pcs::CppString text("to bE TiTlEd - cheCKing,errors, in Case oF aNy fOUNd"); + pcs::CppString expected("To Be Titled - Checking,errors, In Case Of Any Found"); + pcs::CppString res{ text.title() }; + Assert::AreEqual(expected.c_str(), res.c_str()); + + pcs::CppWString wtext(L"to bE TiTlEd - cheCKing,errors, in Case oF aNy fOUNd\u2026\x86"); + pcs::CppWString wexpected(L"To Be Titled - Checking,errors, In Case Of Any Found\u2026\x86"); + pcs::CppWString wres{ wtext.title() }; + Assert::AreEqual(wexpected.c_str(), wres.c_str()); + + } + + TEST_METHOD(translate) + { + pcs::CppString::TransTable trans_table("oizeaslbgOIZEASLG", "012345789012345789"); + pcs::CppString text("This is a big 'Oiseau' that can be seen in 'Le Zoo'"); + pcs::CppString expected("Th15 15 4 819 '01534u' th4t c4n 83 533n 1n '73 200'"); + Assert::AreEqual(expected.c_str(), text.translate(trans_table).c_str()); + + pcs::CppWString::TransTable wtrans_table(L"oizeaslbgOIZEASLG", L"012345789012345789"); + pcs::CppWString wtext(L"This is a big 'Oiseau' that can be seen in 'Le Zoo'"); + pcs::CppWString wexpected(L"Th15 15 4 819 '01534u' th4t c4n 83 533n 1n '73 200'"); + Assert::AreEqual(wexpected.c_str(), wtext.translate(wtrans_table).c_str()); + } + + TEST_METHOD(upper) + { + for (int c = 0; c <= 255; ++c) { + const char ch{ char(c) }; + constexpr int N{ 5 }; + pcs::CppString s(N, ch); + s.upper(); + for (int i = 0; i < N; ++i) + Assert::AreEqual(pcs::to_upper(ch), s[i]); + Assert::AreEqual(char(std::toupper(ch)), pcs::CppString::upper(ch)); + } + + for (int c = 0; c <= 0xffff; ++c) { + const wchar_t wch{ wchar_t(c) }; + constexpr int N{ 5 }; + pcs::CppWString ws(N, wch); + ws.upper(); + for (int i = 0; i < N; ++i) + Assert::AreEqual(pcs::to_upper(wch), ws[i]); + Assert::AreEqual(wchar_t(std::toupper(wch)), pcs::CppWString::upper(wch)); + } + + pcs::CppString s(255, '\0'); + for (int i : std::views::iota(0, 256)) + s[i] = pcs::CppString::value_type(i); + pcs::CppString res{ s.upper() }; + for (auto const [cs, cr] : std::views::zip(s, res)) { + if (std::islower(cs)) + Assert::IsTrue(std::isupper(cr)); + else if (std::isupper(cs)) + Assert::IsTrue(std::isupper(cr)); + else + Assert::AreEqual(cs, cr); + } + + pcs::CppWString ws(0xffff, '\0'); + for (int i : std::views::iota(0, 0x1'0000)) + ws[i] = pcs::CppWString::value_type(i); + pcs::CppWString wres{ ws.upper() }; + for (auto const [wcs, wcr] : std::views::zip(ws, wres)) { + if (std::islower(wcs)) + Assert::IsTrue(std::isupper(wcr)); + else if (std::isupper(wcs)) + Assert::IsTrue(std::isupper(wcr)); + else + Assert::AreEqual(wcs, wcr); + } + + } + + TEST_METHOD(zfill) + { + pcs::CppString s("1.23"); + Assert::AreEqual("1.23", s.zfill(0).c_str()); + Assert::AreEqual("1.23", s.zfill(1).c_str()); + Assert::AreEqual("1.23", s.zfill(2).c_str()); + Assert::AreEqual("1.23", s.zfill(3).c_str()); + Assert::AreEqual("1.23", s.zfill(4).c_str()); + Assert::AreEqual("01.23", s.zfill(5).c_str()); + Assert::AreEqual("001.23", s.zfill(6).c_str()); + + s = '+' + s; + Assert::AreEqual("+1.23", s.zfill(0).c_str()); + Assert::AreEqual("+1.23", s.zfill(1).c_str()); + Assert::AreEqual("+1.23", s.zfill(2).c_str()); + Assert::AreEqual("+1.23", s.zfill(3).c_str()); + Assert::AreEqual("+1.23", s.zfill(4).c_str()); + Assert::AreEqual("+1.23", s.zfill(5).c_str()); + Assert::AreEqual("+01.23", s.zfill(6).c_str()); + Assert::AreEqual("+001.23", s.zfill(7).c_str()); + + s[0] = '-'; + Assert::AreEqual("-1.23", s.zfill(0).c_str()); + Assert::AreEqual("-1.23", s.zfill(1).c_str()); + Assert::AreEqual("-1.23", s.zfill(2).c_str()); + Assert::AreEqual("-1.23", s.zfill(3).c_str()); + Assert::AreEqual("-1.23", s.zfill(4).c_str()); + Assert::AreEqual("-1.23", s.zfill(5).c_str()); + Assert::AreEqual("-01.23", s.zfill(6).c_str()); + Assert::AreEqual("-001.23", s.zfill(7).c_str()); + + s[0] = '*'; + Assert::AreEqual("*1.23", s.zfill(0).c_str()); + Assert::AreEqual("*1.23", s.zfill(1).c_str()); + Assert::AreEqual("*1.23", s.zfill(2).c_str()); + Assert::AreEqual("*1.23", s.zfill(3).c_str()); + Assert::AreEqual("*1.23", s.zfill(4).c_str()); + Assert::AreEqual("*1.23", s.zfill(5).c_str()); + Assert::AreEqual("0*1.23", s.zfill(6).c_str()); + Assert::AreEqual("00*1.23", s.zfill(7).c_str()); + + + pcs::CppWString ws(L"1.23"); + Assert::AreEqual(L"1.23", ws.zfill(0).c_str()); + Assert::AreEqual(L"1.23", ws.zfill(1).c_str()); + Assert::AreEqual(L"1.23", ws.zfill(2).c_str()); + Assert::AreEqual(L"1.23", ws.zfill(3).c_str()); + Assert::AreEqual(L"1.23", ws.zfill(4).c_str()); + Assert::AreEqual(L"01.23", ws.zfill(5).c_str()); + Assert::AreEqual(L"001.23", ws.zfill(6).c_str()); + + ws = L'+' + ws; + Assert::AreEqual(L"+1.23", ws.zfill(0).c_str()); + Assert::AreEqual(L"+1.23", ws.zfill(1).c_str()); + Assert::AreEqual(L"+1.23", ws.zfill(2).c_str()); + Assert::AreEqual(L"+1.23", ws.zfill(3).c_str()); + Assert::AreEqual(L"+1.23", ws.zfill(4).c_str()); + Assert::AreEqual(L"+1.23", ws.zfill(5).c_str()); + Assert::AreEqual(L"+01.23", ws.zfill(6).c_str()); + Assert::AreEqual(L"+001.23", ws.zfill(7).c_str()); + + ws[0] = L'-'; + Assert::AreEqual(L"-1.23", ws.zfill(0).c_str()); + Assert::AreEqual(L"-1.23", ws.zfill(1).c_str()); + Assert::AreEqual(L"-1.23", ws.zfill(2).c_str()); + Assert::AreEqual(L"-1.23", ws.zfill(3).c_str()); + Assert::AreEqual(L"-1.23", ws.zfill(4).c_str()); + Assert::AreEqual(L"-1.23", ws.zfill(5).c_str()); + Assert::AreEqual(L"-01.23", ws.zfill(6).c_str()); + Assert::AreEqual(L"-001.23", ws.zfill(7).c_str()); + + ws[0] = L'*'; + Assert::AreEqual(L"*1.23", ws.zfill(0).c_str()); + Assert::AreEqual(L"*1.23", ws.zfill(1).c_str()); + Assert::AreEqual(L"*1.23", ws.zfill(2).c_str()); + Assert::AreEqual(L"*1.23", ws.zfill(3).c_str()); + Assert::AreEqual(L"*1.23", ws.zfill(4).c_str()); + Assert::AreEqual(L"*1.23", ws.zfill(5).c_str()); + Assert::AreEqual(L"0*1.23", ws.zfill(6).c_str()); + Assert::AreEqual(L"00*1.23", ws.zfill(7).c_str()); + } + + }; +} diff --git a/cpp-strings-tests/cpp-strings-tests.vcxproj b/cpp-strings-tests/cpp-strings-tests.vcxproj new file mode 100644 index 0000000..089d18e --- /dev/null +++ b/cpp-strings-tests/cpp-strings-tests.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + {3B180A14-A496-4E9D-8D9B-57D871C8E005} + Win32Proj + cppstringstests + 10.0 + NativeUnitTestProject + + + + DynamicLibrary + true + v143 + Unicode + false + + + DynamicLibrary + false + v143 + true + Unicode + false + + + DynamicLibrary + true + v143 + Unicode + false + + + DynamicLibrary + false + v143 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + true + $(VCInstallDir)UnitTest\include;$(ProjectDir)..\cpp-strings + _DEBUG;%(PreprocessorDefinitions) + true + + + stdcpplatest + stdclatest + true + false + + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + true + $(VCInstallDir)UnitTest\include;$(ProjectDir)..\cpp-strings + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + stdcpplatest + stdclatest + true + false + + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + true + true + true + $(VCInstallDir)UnitTest\include;$(ProjectDir)..\cpp-strings + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + stdcpplatest + stdclatest + true + false + + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + true + true + true + $(VCInstallDir)UnitTest\include;$(ProjectDir)..\cpp-strings + NDEBUG;%(PreprocessorDefinitions) + true + + + stdcpplatest + stdclatest + true + false + + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + \ No newline at end of file diff --git a/cpp-strings-tests/cpp-strings-tests.vcxproj.filters b/cpp-strings-tests/cpp-strings-tests.vcxproj.filters new file mode 100644 index 0000000..bcfea1c --- /dev/null +++ b/cpp-strings-tests/cpp-strings-tests.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/cpp-strings-tests/cpp-strings-tests.vcxproj.user b/cpp-strings-tests/cpp-strings-tests.vcxproj.user new file mode 100644 index 0000000..966b4ff --- /dev/null +++ b/cpp-strings-tests/cpp-strings-tests.vcxproj.user @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/cpp-strings/cpp-strings.sln b/cpp-strings/cpp-strings.sln index c5029ce..ad5321e 100644 --- a/cpp-strings/cpp-strings.sln +++ b/cpp-strings/cpp-strings.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.5.33414.496 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp-strings", "cpp-strings.vcxproj", "{520980FD-2242-443E-89DB-B8E41D9606B5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp-strings-tests", "..\cpp-strings-tests\cpp-strings-tests.vcxproj", "{3B180A14-A496-4E9D-8D9B-57D871C8E005}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -21,6 +23,14 @@ Global {520980FD-2242-443E-89DB-B8E41D9606B5}.Release|x64.Build.0 = Release|x64 {520980FD-2242-443E-89DB-B8E41D9606B5}.Release|x86.ActiveCfg = Release|Win32 {520980FD-2242-443E-89DB-B8E41D9606B5}.Release|x86.Build.0 = Release|Win32 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Debug|x64.ActiveCfg = Debug|x64 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Debug|x64.Build.0 = Debug|x64 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Debug|x86.ActiveCfg = Debug|Win32 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Debug|x86.Build.0 = Debug|Win32 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Release|x64.ActiveCfg = Release|x64 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Release|x64.Build.0 = Release|x64 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Release|x86.ActiveCfg = Release|Win32 + {3B180A14-A496-4E9D-8D9B-57D871C8E005}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/cpp-strings/cpp-strings.vcxproj b/cpp-strings/cpp-strings.vcxproj index 7fe9606..7b32c1d 100644 --- a/cpp-strings/cpp-strings.vcxproj +++ b/cpp-strings/cpp-strings.vcxproj @@ -76,6 +76,9 @@ true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp20 + stdc17 + false Console @@ -90,6 +93,9 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp20 + stdc17 + false Console @@ -104,9 +110,10 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpplatest + stdcpp20 stdc17 - true + false + true Console @@ -121,9 +128,10 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpplatest + stdcpp20 stdc17 - true + false + true Console @@ -136,6 +144,7 @@ + diff --git a/cpp-strings/cppstrings.cpp b/cpp-strings/cppstrings.cpp index 3ec760f..253031d 100644 --- a/cpp-strings/cppstrings.cpp +++ b/cpp-strings/cppstrings.cpp @@ -2,7 +2,7 @@ Library cppstrings "What if c++ strings where as easy to use as Python strings?" - Copyright (C) 2023 Philippe Schmouker + Copyright (C) 2023-2025 Philippe Schmouker contact - ph (dot) schmouker (at) gmail (dot) com This program is free software: you can redistribute it and/or modify @@ -29,11 +29,17 @@ using namespace pcs; */ int main() { - CppString s = "-5.1"; - CppWString ws{ L"-5.2"cs }; + CppString s = "-5.1"_cs; + CppWString ws{ L"-5.2"_cs }; std::cout << ws.isupper() << std::endl; std::cout << s.zfill(10).c_str() << std::endl; + s.format("{} {}", 1, 3.14); + ws.format(L"{} abc", 2); + + std::cout << s.c_str() << " / "; + std::wcout << ws.c_str() << std::endl; + return 0; } diff --git a/cpp-strings/cppstrings.h b/cpp-strings/cppstrings.h index 051abbd..e368f9f 100644 --- a/cpp-strings/cppstrings.h +++ b/cpp-strings/cppstrings.h @@ -3,7 +3,7 @@ Library cppstrings "What if c++ strings where as easy to use as Python strings?" - Copyright (C) 2023 Philippe Schmouker + Copyright (C) 2023-2025 Philippe Schmouker contact - ph (dot) schmouker (at) gmail (dot) com This program is free software: you can redistribute it and/or modify @@ -22,19 +22,21 @@ //============================================================= #include +#include #include #include #include #include +#include #include #include #include #include -#include #include #include + namespace pcs // i.e. "pythonic c++ strings" { //============================================================= @@ -51,12 +53,46 @@ namespace pcs // i.e. "pythonic c++ strings" using CppString = CppStringT; //!< Specialization of basic class with template argument 'char' using CppWString = CppStringT; //!< Specialization of basic class with template argument 'wchar_t' + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4455) // to avoid boring warnings with litteral operators definitions +#endif + // litteral operators -#pragma warning(disable: 4455) - inline const CppString operator""cs(const char* str, std::size_t len); //!< Forms a CppString literal. - inline const CppString operator""csv(const char* str, std::size_t len); //!< Forms a CppString view literal. - inline const CppWString operator""cs(const wchar_t* str, std::size_t len); //!< Forms a CppWString literal. - inline const CppWString operator""csv(const wchar_t* str, std::size_t len); //!< Forms a CppWString view literal. + inline CppString operator""_cs(const char* str, std::size_t len); //!< Forms a CppString literal. + inline CppWString operator""_cs(const wchar_t* str, std::size_t len); //!< Forms a CppWString literal. + + + // slices -- to be used with operator CppStringT::operator(). + template + requires std::is_signed_v + class Slice; //!< Base class for slices, with start, stop and step specified values + + template + requires std::is_signed_v + struct StartSlice; //!< struct of slices with default stop and step values + + template + requires std::is_signed_v + struct StopSlice; //!< struct of slices with default start and step values + + template + requires std::is_signed_v + struct StepSlice; //!< struct of slices with default start and stop values + + template + requires std::is_signed_v + struct StartStopSlice; //!< struct of slices with default step values + + template + requires std::is_signed_v + struct StartStepSlice; //!< struct of slices with default stop values + + template + requires std::is_signed_v + struct StopStepSlice; //!< struct of slices with default start values + // chars classifications -- not to be directly called, see respective specializations at the very end of this module. template @@ -66,10 +102,13 @@ namespace pcs // i.e. "pythonic c++ strings" inline const bool is_ascii(const CharT ch) noexcept; //!< Returns true if character ch gets ASCII code, or false otherwise. template - inline const bool is_id_continue(const CharT ch) noexcept; //!< Returns true if character is a continuing char for identifiers, or false otherwise. + inline const bool is_decimal(const CharT ch) noexcept; //!< Returns true if character is a decimal digit, or false otherwise. template - inline const bool is_decimal(const CharT ch) noexcept; //!< Returns true if character is a decimal digit, or false otherwise. + inline const bool is_digit(const CharT ch) noexcept; //!< Returns true if character is a decimal digit, or false otherwise. + + template + inline const bool is_id_continue(const CharT ch) noexcept; //!< Returns true if character is a continuing char for identifiers, or false otherwise. template inline const bool is_id_start(const CharT ch) noexcept; //!< Returns true if character is a starting char for identifiers, or false otherwise. @@ -77,6 +116,9 @@ namespace pcs // i.e. "pythonic c++ strings" template inline const bool is_lower(const CharT ch) noexcept; //!< Returns true if character is lowercase, or false otherwise. + template + inline const bool is_numeric(const CharT ch) noexcept; //!< Returns true if character is a decimal digit, or false otherwise. + template inline const bool is_printable(const CharT ch) noexcept; //!< Returns true if character ch is printable, or false otherwise. @@ -102,29 +144,29 @@ namespace pcs // i.e. "pythonic c++ strings" //===== CppStringT<> ====================================== /** \brief This is the templated base class for all CppString classes. * - * Users should instantiate any specialization of this base class + * Users should instantiate any specialization of this base class * rather than this base class itself: * - \see CppString for CppStringT. * - \see CppWString for CppStringT. * - * This base class inherits from std::basic_string. As such, - * it gets direct access to all public methods of its base class. - * \see https://en.cppreference.com/w/cpp/string/basic_string for - * a full list of such methods, for instance. + * This base class inherits from std::basic_string. As such, + * it gets direct access to all public methods of its base class. + * \see https://en.cppreference.com/w/cpp/string/basic_string for a + * full list of such methods, for instance. * - * You may specialize it by your own with any of the next char - * types: + * You may specialize it by your own with any of the next char types: * - char8_t (C++20) * - char16_t (C++11) * - char32_t (C++11) + * Caution: templated method format() may be difficult to specialize + * with these types --> let us know if you succeed! */ template - class CppStringT : public std::basic_string + class CppStringT : public std::basic_string { public: //=== Wrappers ======================================== - using MyBaseClass = std::basic_string; - using MyStringView = std::basic_string_view; + using MyBaseClass = std::basic_string; using traits_type = MyBaseClass::traits_type; using value_type = MyBaseClass::value_type; @@ -147,16 +189,16 @@ namespace pcs // i.e. "pythonic c++ strings" { public: //--- wrappers ------------------------------------ - using key_type = CharT; + using key_type = CharT; using value_type = CppStringT; //--- Constructors / destructor ------------------- - /** \brief Creates a TransTable from a standard map. */ + /** \brief Creates a TransTable from a standard map (#1). */ inline TransTable(const std::map trans_table) : m_table{ trans_table } {} - /** \brief Creates a TransTable from two strings. + /** \brief Creates a TransTable from two strings (#2). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with @@ -167,10 +209,10 @@ namespace pcs // i.e. "pythonic c++ strings" assert(keys.size() == values.size()); auto val_it = values.cbegin(); for (const auto k : keys) - m_table[k] = value_type(*val_it++); + m_table[k] = CppStringT(*val_it++); } - /** \brief Creates a TransTable from three strings. + /** \brief Creates a TransTable from three strings (#3). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with @@ -183,27 +225,26 @@ namespace pcs // i.e. "pythonic c++ strings" assert(keys.size() == values.size()); auto val_it = values.cbegin(); for (const auto k : keys) - m_table[k] = value_type(*val_it++); + m_table[k] = CppStringT(*val_it++); for (const auto k : not_translated) m_table[k] = CppStringT(); } - /** \brief Creates a TransTable from two initalization lists. + /** \brief Creates a TransTable from a string and an initalization list (#4). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with * the i-th character in values. */ - inline TransTable(const std::initializer_list keys, - const std::initializer_list values) + inline TransTable(const CppStringT& keys, const std::initializer_list& values) { assert(keys.size() == values.size()); - auto val_it = values.cbegin(); + auto val_it = values.begin(); for (const auto k : keys) - m_table[(*k)[0]] = *val_it++; + m_table[k] = *val_it++; } - /** \brief Creates a TransTable from three initalization lists. + /** \brief Creates a TransTable from a string, an initalization list and a string (#5). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with @@ -211,19 +252,17 @@ namespace pcs // i.e. "pythonic c++ strings" * contained in string not_translated are associated in the * translation table with the empty string. */ - inline TransTable(const std::initializer_list keys, - const std::initializer_list values, - const CppStringT& not_translated) + inline TransTable(const CppStringT& keys, const std::initializer_list values, const CppStringT& not_translated) { assert(keys.size() == values.size()); - auto val_it = values.cbegin(); + auto val_it = values.begin(); for (const auto k : keys) - m_table[(*k)[0]] = *val_it++; + m_table[k] = *val_it++; for (const auto k : not_translated) m_table[k] = CppStringT(); } - /** \brief Creates a TransTable from two pointers to null-terminated lists of characters. + /** \brief Creates a TransTable from two pointers to null-terminated lists of characters (#6). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with @@ -235,13 +274,13 @@ namespace pcs // i.e. "pythonic c++ strings" m_table[*keys++] = value_type(*values++); } - /** \brief Creates a TransTable from three pointers to null-terminated lists of characters. + /** \brief Creates a TransTable from three pointers to null-terminated lists of characters (#7). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with - * the i -th character in values. Finally, the characters - * contained in string not_translated are associated in the - * translation table with the empty string. + * the i -th entry in values. Finally, the characters contained + * in string not_translated are associated in the translation + * table with the empty string. */ inline TransTable(const CharT* keys, const CharT* values, const CharT* not_translated) { @@ -251,11 +290,11 @@ namespace pcs // i.e. "pythonic c++ strings" m_table[*not_translated++] = CppStringT(); } - /** \brief Creates a TransTable from two containers iterators. + /** \brief Creates a TransTable from two containers iterators (#8). * - * Both containers should have the same size. The i-th - * character in key is associated in the translation table with - * the i-th character in values. + * Both containers should have the same size. The i-th + * character in key is associated in the translation + * table with the i-th entry in values. */ template inline TransTable(KeyIt first_key, KeyIt last_key, ValueIt first_value, ValueIt last_value) @@ -266,7 +305,7 @@ namespace pcs // i.e. "pythonic c++ strings" m_table[*key_it++] = value_type(*val_it++); } - /** \brief Creates a TransTable from three containers iterators. + /** \brief Creates a TransTable from three containers iterators (#9). * * Both containers should have the same size. The i-th * character in key is associated in the translation table with @@ -274,26 +313,27 @@ namespace pcs // i.e. "pythonic c++ strings" * contained in string not_translated are associated in the * translation table with the empty string. */ - template - inline TransTable(KeyIt first_key, KeyIt last_key, + template + inline TransTable(Key1It first_key, Key1It last_key, ValueIt first_value, ValueIt last_value, - KeyIt first_not_translated, KeyIt last_not_translated) + Key2It first_not_translated, Key2It last_not_translated) { - KeyIt key_it{ first_key }; + Key1It key1_it{ first_key }; ValueIt val_it{ first_value }; - while (key_it != last_key && val_it != last_value) - m_table[*key_it++] = value_type(*val_it++); - key_it = first_not_translated; - while (key_it != last_not_translated) - m_table[*key_it++] = CppStringT(); + while (key1_it != last_key && val_it != last_value) + m_table[*key1_it++] = value_type(*val_it++); + Key2It key2_it{ first_not_translated }; + while (key2_it != last_not_translated) + m_table[*key2_it++] = CppStringT(); } - /** \brief Creates a TransTable from two string views. + /** \brief Creates a TransTable from two string views (#10). * * Parameters keys and values must have the same size. The i-th * character in key is associated in the translation table with * the i-th character in values. */ + /**/ template explicit TransTable(const StringViewLike& keys, const StringViewLike& values) { @@ -303,30 +343,11 @@ namespace pcs // i.e. "pythonic c++ strings" m_table[(*k)[0]] = value_type(*val_it++); } - /** \brief Creates a TransTable from three string views. - * - * Parameters keys and values must have the same size. The i-th - * character in key is associated in the translation table with - * the i -th character in values. Finally, the characters - * contained in string not_translated are associated in the - * translation table with the empty string. - */ - template - TransTable(const StringViewLike& keys, const StringViewLike& values, const StringViewLike& not_translated) - { - assert(keys.size() == values.size()); - auto val_it = values.cbegin(); - for (const auto k : keys) - m_table[k] = value_type(*val_it++); - for (const auto k : not_translated) - m_table[k] = CppStringT(); - } - - inline TransTable() noexcept = default; //!< Default empty constructor. - inline TransTable(const TransTable&) noexcept = default; //!< Default copy constructor. - inline TransTable(TransTable&&) noexcept = default; //!< Default move constructor. + inline TransTable() noexcept = default; //!< Default empty constructor. + inline TransTable(const TransTable&) noexcept = default; //!< Default copy constructor. + inline TransTable(TransTable&&) noexcept = default; //!< Default move constructor. - inline ~TransTable() noexcept = default; //!< Default descrtuctor + inline ~TransTable() noexcept = default; //!< Default descrtuctor //--- operators ----------------------------------- inline TransTable& operator= (const TransTable&) noexcept = default; //!< Default copy assignment @@ -339,47 +360,69 @@ namespace pcs // i.e. "pythonic c++ strings" return *this; } - /** \biref Indexing operator. */ - inline CppStringT operator[] (const key_type ch) const noexcept + /** \brief Indexing operator. */ + [[nodiscard]] + inline value_type operator[] (const key_type ch) noexcept { - try { - return m_table[ch]; + auto it = m_table.find(ch); + if (it != m_table.end()) [[likely]] { + return it->second; } - catch (...) { - return CppStringT(); + else [[unlikely]] { + return ch; } } + + inline std::map& get_table() noexcept //!< for tests purposes + { + return m_table; + } + + private: + std::map m_table{}; // the internal storage of the translation table. Access it via the indexing operator. + }; - //=== Constructors / Destructor =================== - inline CppStringT() : MyBaseClass() {} - inline CppStringT(const CppStringT& other) : MyBaseClass(other) {} - inline CppStringT(const CppStringT& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} - inline CppStringT(CppStringT&& other) : MyBaseClass(other) {} - inline CppStringT(CppStringT&& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} - inline CppStringT(MyBaseClass::size_type count, CharT ch) : MyBaseClass(count, ch) {} - inline CppStringT(const CppStringT& other, size_type pos) : MyBaseClass(other, pos) {} - inline CppStringT(const CppStringT& other, size_type pos, size_type count) noexcept : MyBaseClass(other, pos, count) {} - inline CppStringT(const CharT* s) : MyBaseClass(s) {} - inline CppStringT(const CharT* s, size_type count) : MyBaseClass(s, count) {} - inline CppStringT(std::initializer_list ilist) : MyBaseClass(ilist) {} - - inline CppStringT(const MyBaseClass& other) : MyBaseClass(other) {} - inline CppStringT(const MyBaseClass& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} - inline CppStringT(MyBaseClass&& other) : MyBaseClass(other) {} - inline CppStringT(MyBaseClass&& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} + //=== Constructors / Destructor ======================= + inline CppStringT() : MyBaseClass() {} // #1 + inline CppStringT(const CppStringT& other) : MyBaseClass(other) {} // #2 + inline CppStringT(const CppStringT& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} // #3 + inline CppStringT(CppStringT&& other) noexcept : MyBaseClass(other) {} // #4 + inline CppStringT(CppStringT&& other, const AllocatorT& alloc) noexcept : MyBaseClass(other, alloc) {} // #5 + inline CppStringT(MyBaseClass::size_type count, CharT ch) : MyBaseClass(count, ch) {} // #6 + inline CppStringT(const CppStringT& other, size_type pos) : MyBaseClass(other, pos) {} // #7 + inline CppStringT(const CppStringT& other, size_type pos, size_type count) noexcept : MyBaseClass(other, pos, count) {} // #8 + inline CppStringT(const CharT* s) // #9 + : MyBaseClass(s ? s : CppStringT().c_str()) + {} + inline CppStringT(const CharT* s, size_type count) // #10 + : MyBaseClass(s ? s : CppStringT().c_str(), count) + {} + inline CppStringT(std::initializer_list ilist) : MyBaseClass(ilist) {} // #11 + + inline CppStringT(const CharT ch) : MyBaseClass(1, ch) {} // #19 + + inline CppStringT(const MyBaseClass& other) : MyBaseClass(other) {} // #12 + inline CppStringT(const MyBaseClass& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} // #13 + inline CppStringT(MyBaseClass&& other) : MyBaseClass(other) {} // #14 + inline CppStringT(MyBaseClass&& other, const AllocatorT& alloc) : MyBaseClass(other, alloc) {} // #15 template - inline CppStringT(InputIt first, InputIt last) : MyBaseClass(first, last) {} + inline CppStringT(InputIt first, InputIt last) : MyBaseClass(first, last) {} // #16 template - explicit CppStringT(const StringViewLike& svl) : MyBaseClass(svl) {} + explicit CppStringT(StringViewLike& svl) : MyBaseClass(svl) {} // #17 template - CppStringT(const StringViewLike& svl, size_type pos, size_type n) : MyBaseClass(svl, pos, n) {} + CppStringT(StringViewLike& svl, size_type pos, size_type n) : MyBaseClass(svl, pos, n) {} // #18 - inline ~CppStringT() = default; + inline ~CppStringT() noexcept = default; + + + //=== Assignment operators ============================ + CppStringT& operator= (const CppStringT&) noexcept = default; //!< Default copy assignment + CppStringT& operator= (CppStringT&&) noexcept = default; //!< Default move assignment //=== Exceptions ====================================== @@ -396,45 +439,105 @@ namespace pcs // i.e. "pythonic c++ strings" //=== Methods ========================================= //--- capitalize() ------------------------------------ - /** \brief In-place modifies the string with its first character capitalized and the rest lowercased. Returns a reference to the string*/ - inline CppStringT& capitalize() noexcept + /** \brief Returns a copy of the string with its first character capitalized and the rest lowercased. */ + inline CppStringT capitalize() noexcept { - if (!this->empty()) { - this->lower(); - (*this)[0] = pcs::to_upper((*this)[0]); + CppStringT res(*this); + if (!res.empty()) [[likely]] { + res.lower(); + res[0] = pcs::to_upper(res[0]); } - return *this; + return res; } //--- center() ---------------------------------------- - /** \brief Returns the string centered in a string of length width. + /** \brief Returns a copy of the string centered in a string of length width. * * Padding is done using the specified fillchar (default is an ASCII space). * A copy of the original string is returned if width is less than or equal * to the length of the string. The original string remains unchanged. */ - inline CppStringT center(const size_type width, const value_type fillch = value_type(' ')) const noexcept + [[nodiscard]] + CppStringT center(const size_type width, const value_type fillch = value_type(' ')) const noexcept { - const size_type l{ this->size() }; - if (l <= width) + const size_type len{ this->size() }; + if (width <= len) [[unlikely]] return CppStringT(*this); - const size_type half{ (width - l) / 2 }; - return CppStringT(fillch, half) + *this + CppStringT(fillch, width - half - l); + + const size_type half{ (width - len) / 2 }; + return CppStringT(half, fillch) + *this + CppStringT(width - half - len, fillch); + } + + + //--- contains() -------------------------------------- + /** \brief Returns true if this string contains the passed string, or false otherwise. + * + * This is a c++ implementation of Python keyword 'in' applied to strings. + */ + [[nodiscard]] + constexpr bool contains(const CppStringT& substr) const noexcept + { + if (substr.empty()) [[unlikely]] + // the empty string is always contained in any string + return true; + +#if (defined(_HAS_CXX23) && _HAS_CXX23) || (!defined(_HAS_CXX23) && __cplusplus >= 202302L) + // c++23 and above already defines this method + return MyBaseClass::contains(substr); +#else + // up to c++20, we have to implement this method + const size_type substr_width{ substr.size() }; + const size_type width{ this->size() }; + + if (substr_width > width) [[unlikely]] + return false; + + for (size_type index = 0; index <= width - substr_width; ++index) [[likely]] { + if (substr == this->substr(index, substr_width)) + return true; + } + + return false; +#endif + } + + + //--- contains_n() ------------------------------------ + /** \brief Returns true if the passed string is found within the slice str[start:start+count-1], or false otherwise. + * + * This is a c++ implementation of Python keyword 'in' applied to Python sliced strings. + */ + [[nodiscard]] + inline constexpr bool contains_n(const CppStringT& sub, const size_type start, const size_type count = -1) const noexcept + { + try { + return this->substr(start, count).contains(sub); + } + catch (...) { + return false; + } } //--- count() ----------------------------------------- /** \brief Returns the number of non-overlapping occurrences of substring sub in the range [start, end]. */ - inline constexpr size_type count(const CppStringT& sub, const size_type start = 0, const size_type end = 0) const noexcept + [[nodiscard]] + constexpr size_type count(const CppStringT& sub, const size_type start = 0, const size_type end = -1) const noexcept { - const size_type length{ this->size() }; - const size_type end_{ (end == 0) ? length : end }; - size_type n = 0; - size_type start_ = start; - while ((start_ = find(sub, start_, end_)) != CppStringT::npos) + CppStringT tmp{ this->substr(start, std::min(this->size(), end) - start + 1) }; + + size_type start_{ 0 }; + size_type end_{ tmp.size() }; + + while ((start_ = tmp.find(sub, start_, end_)) != CppStringT::npos) { + start_ += sub.size(); + end_ -= start_; + tmp = tmp.substr(start_, std::min(tmp.size(), end_) + 1); + start_ = 0; n++; + } return n; } @@ -442,12 +545,14 @@ namespace pcs // i.e. "pythonic c++ strings" //--- count_n() --------------------------------------- /** \brief Returns the number of non-overlapping occurrences of substring sub in the range [start, start+length-1]. */ + [[nodiscard]] inline constexpr size_type count_n(const CppStringT& sub, const size_type start, const size_type length) const noexcept { return count(sub, start, start + length - 1); } /** \brief Returns the number of non-overlapping occurrences of substring sub in the range [0, length-1]. */ + [[nodiscard]] inline constexpr size_type count_n(const CppStringT& sub, const size_type length) const noexcept { return count(sub, 0, length - 1); @@ -455,64 +560,69 @@ namespace pcs // i.e. "pythonic c++ strings" //--- endswith() -------------------------------------- - /** Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start position and stops at end position. */ + /** \brief Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start position and stops at end position. */ + [[nodiscard]] inline const bool endswith(const CppStringT& suffix, const size_type start, const size_type end) const noexcept { - return endswith(std::span{ suffix }, start, end); + return this->substr(start, end - start + 1).MyBaseClass::ends_with(suffix); } - /** Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start position and stops at end of string. */ - inline const bool endswith(const CppStringT& suffix, const size_type start) const noexcept + /** \brief Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start of string and stops at end position. */ + [[nodiscard]] + inline const bool endswith(const CppStringT& suffix, const size_type end) const noexcept { - return endswith(std::span{ suffix }, start, this->size() - 1); + return this->substr(0, end).MyBaseClass::ends_with(suffix); } - /** Returns true if the string ends with the specified suffix, otherwise returns false. Test runs on the whole string. */ + /** \brief Returns true if the string ends with the specified suffix, otherwise returns false. Test runs on the whole string. */ + [[nodiscard]] inline const bool endswith(const CppStringT& suffix) const noexcept { - return this->ends_with(suffix); + return static_cast(MyBaseClass::ends_with(suffix)); } - /** Returns true if the string ends with any of the specified suffixes, otherwise returns false. Test begins at start position and stops at end of string. */ - inline const bool endswith(const std::span& suffixes, const size_type start, const size_type end) const noexcept + /** \brief Returns true if the string ends with any of the specified suffixes, otherwise returns false. Test begins at start position and stops at end position. */ + [[nodiscard]] + const bool endswith(const std::initializer_list& suffixes, const size_type start, const size_type end) const noexcept { - if (start > end) + if (start > end) [[unlikely]] return false; - else - return std::any_of(suffixes.cbegin(), suffixes.cend(), this->substr(start, end).ends_with); - /* + + CppStringT tmp(this->substr(start, end - start + 1)); for (auto& suffix : suffixes) { - if (this->substr(start, end).ends_with(suffix)) + if (tmp.ends_with(suffix)) [[unlikely]] return true; } - return false; - */ } //--- endswith_n() ------------------------------------ - /** Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start position and stops after count positions. */ + /** \brief Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at start position and stops after count positions. */ + [[nodiscard]] inline const bool endswith_n(const CppStringT& suffix, const size_type start, const size_type count) const noexcept { - return endswith(std::span{ suffix }, start, start + count - 1); + return endswith(suffix, start, start + count - 1); } - /** Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at position 0 and stops after count positions. */ + /** \brief Returns true if the string ends with the specified suffix, otherwise returns false. Test begins at position 0 and stops after count positions. */ + [[nodiscard]] inline const bool endswith_n(const CppStringT& suffix, const size_type count) const noexcept { - return endswith(std::span{ suffix }, 0, count - 1); + return endswith(suffix, 0, count - 1); } - /** Returns true if the string ends with any of the specified suffixes, otherwise returns false. Test begins at start position and stops after count positions. */ - inline const bool endswith_n(const std::span& suffixes, const size_type start, const size_type count) const noexcept + /** \brief Returns true if the string ends with any of the specified suffixes, otherwise returns false. Test begins at start position and stops after count positions. */ + [[nodiscard]] + inline const bool endswith_n(const std::initializer_list& suffixes, const size_type start, const size_type count) const noexcept { return endswith(suffixes, start, start + count - 1); } //--- expand_tabs() ----------------------------------- - /** Returns a copy of the string where all tab characters are replaced by one or more spaces, depending on the current column and the given tab size. */ + /** \brief Returns a copy of the string where all tab characters are replaced by one or more spaces, depending on the current column and the given tab size. */ + [[nodiscard]] CppStringT expand_tabs(const size_type tabsize = 8) const noexcept { const size_type tabsize_{ tabsize == 0 ? 1 : tabsize }; @@ -520,17 +630,17 @@ namespace pcs // i.e. "pythonic c++ strings" std::size_t current_pos{ 0 }; for (const value_type ch : *this) { - if (ch == value_type('\t')) { + if (ch == value_type('\t')) [[unlikely]] { do { ret += value_type(' '); current_pos++; } while (current_pos % tabsize_ != 0); } - else if (ch == value_type('\n') || ch == value_type('\r')) { + else if (ch == value_type('\n') || ch == value_type('\r')) [[unlikely]] { ret += ch; current_pos = 0; } - else { + else [[likely]] { ret += ch; current_pos++; } @@ -541,88 +651,142 @@ namespace pcs // i.e. "pythonic c++ strings" //--- find() ------------------------------------------ - /** Returns the lowest index in the string where substring sub is found within the slice str[start:end], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the lowest index in the string where substring sub is found within the slice str[start:end], or -1 (i.e. 'npos') if sub is not found. * * Note: this method should be used only if you need to know the position of - * sub. To check if sub is a substring or not, use the method contains_n(). + * sub. To check if sub is a substring or not, use the method contains(). + * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. * * \see find_n(), rfind() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ - inline constexpr size_type find(const CppStringT& sub, const size_type start, const size_type end) const noexcept + [[nodiscard]] + constexpr size_type find(const CppStringT& sub, const size_type start = 0, const size_type end = -1) const noexcept { - if (start > end) + const size_type end_{ (end == -1) ? this->size() : end }; + + if (start > end_) [[unlikely]] return CppStringT::npos; - else - return find_n(sub, start, end - start + 1); + else [[likely]] + return find_n(sub, start, end_ - start + 1); } //--- find_n() ---------------------------------------- - /** Returns the lowest index in the string where substring sub is found within the slice str[start:start+count-1], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the lowest index in the string where substring sub is found within the slice str[start:start+count-1], or -1 (i.e. 'npos') if sub is not found. * * Note: this method should be used only if you need to know the position of * sub. To check if sub is a substring or not, use the method contains_n(). * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. The + * returned position is 0. + * * \see find(), rfind() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type find_n(const CppStringT& sub, const size_type start, const size_type count) const noexcept { + constexpr size_type npos{ CppStringT::npos }; + try { - return this->substr(start, count).find(sub); + const CppStringT sub_str{ this->substr(start, count) }; + const size_type found_pos{ sub_str.MyBaseClass::find(sub) }; + + return (found_pos == npos) ? npos : found_pos + start; } catch (...) { - return CppStringT::npos; + return npos; } } - /** Returns the lowest index in the string where substring sub is found within the slice str[0:count-1], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the lowest index in the string where substring sub is found within the slice str[0:count-1], or -1 (i.e. 'npos') if sub is not found. * * Note: this method should be used only if you need to know the position of * sub. To check if sub is a substring or not, use the method contains_n(). * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. The + * returned position is 0. + * * \see find(), rfind() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type find_n(const CppStringT& sub, const size_type count) const noexcept { return find_n(sub, 0, count); } + //--- format() ---------------------------------------- + /** \brief Formats this string according to c++20 std::format() specification. Returns this string. */ + template + inline CppStringT& format( + const std::basic_format_string...> frmt, + ArgsT&&... args + ) + { + return *this; + } + + template + inline CppStringT& format( //!< specialzation for char's + const std::basic_format_string... > frmt, + ArgsT&&... args + ) + { + return *this = std::vformat(frmt.get(), std::make_format_args(args...)); + } + + template + inline CppStringT& format( //!< specialzation for wchar_t's + const std::basic_format_string... > frmt, + ArgsT&&... args + ) + { + return *this = std::vformat(frmt.get(), std::make_wformat_args(args...)); + } + + //--- index() ----------------------------------------- - /** Like find(), but raises NotFoundException when the substring is not found. + /** \brief Like find(const CppStringT&), but raises NotFoundException when the substring sub is not found. * * \see index_n(), rindex() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ - inline constexpr size_type index(const CppStringT& sub, const size_type start, const size_type end) const + [[nodiscard]] + inline constexpr size_type index(const CppStringT& sub, const size_type start = 0, const size_type end = -1) const { const size_type ret_value = find(sub, start, end); - if (size_type == CppStringT::npos) - throw NotFoundException(std::format("substring \"{}\" not found in string \"{}\"", sub, this->c_str())); + if (ret_value == CppStringT::npos) + throw NotFoundException("substring not found in string."); else return ret_value; } //--- index_n() --------------------------------------- - /** Like find_n(sub, start, count), but raises NotFoundException when the substring is not found. + /** \brief Like find_n(sub, start, count), but raises NotFoundException when the substring is not found. * * \see index_n(), rindex() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type index_n(const CppStringT& sub, const size_type start, const size_type count) const { return index(sub, start, start + count - 1); } - /** Like find_n(sub, count), but raises NotFoundException when the substring is not found. + /** \brief Like find_n(sub, count), but raises NotFoundException when the substring is not found. * * \see index_n(), rindex() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type index_n(const CppStringT& sub, const size_type count) const { return index(sub, 0, count); @@ -631,58 +795,70 @@ namespace pcs // i.e. "pythonic c++ strings" //--- isalnum() --------------------------------------- /** \brief Returns true if all characters in the string are alphanumeric and there is at least one character, or false otherwise. */ + [[nodiscard]] inline const bool isalnum() const noexcept { - return this->isalpha() || this->isdecimal() || this->isdigit() || this->isnumeric(); + if (this->empty()) [[unlikely]] + return false; + else [[likely]] + return std::all_of( + this->cbegin(), + this->cend(), + [](const value_type ch) { return pcs::is_alpha(ch) || pcs::is_decimal(ch) || pcs::is_digit(ch) || pcs::is_numeric(ch); } + ); } //--- isalpha() -------------------------------------- /** \brief Returns true if all characters in the string are alphabetic and there is at least one character, or false otherwise. */ + [[nodiscard]] inline const bool isalpha() const noexcept { - return !this->empty() && std::all_of(this->cbegin(), this->cend(), [](const value_type ch) { return pcs::is_alpha(ch); }); + return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_alpha); } //--- isascii() --------------------------------------- /** \brief Returns true if the string is empty or all characters in the string are ASCII, or false otherwise. */ - #if defined(isascii) // may be already defined in header file - #undef isascii - #endif +#if defined(isascii) // may be already defined in header file +#undef isascii +#endif + [[nodiscard]] inline const bool isascii() const noexcept { - return this->empty() || std::all_of(this->cbegin(), this->cend(), pcs::is_ascii); + return this->empty() || std::all_of(this->cbegin(), this->cend(), pcs::is_ascii); } //--- isdecimal() ------------------------------------- /** \brief Returns true if all characters in the string are decimal characters and there is at least one character, or false otherwise. * - * Decimal characters are those that can be used to form numbers in - * base 10, e.g. U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal + * Decimal characters are those that can be used to form numbers in + * base 10, e.g. U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal * character is a character in the Unicode General Category “Nd”. */ + [[nodiscard]] inline const bool isdecimal() const noexcept { - return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_decimal); + return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_decimal); } //--- isdigit() --------------------------------------- /** \brief Returns true if all characters in the string are digits and there is at least one character, or false otherwise. * - * Digits include decimal characters and digits that need special - * handling, such as the compatibility superscript digits. This - * covers digits which cannot be used to form numbers in base 10, - * like the Kharosthi numbers. Formally, a digit is a character + * Digits include decimal characters and digits that need special + * handling, such as the compatibility superscript digits. This + * covers digits which cannot be used to form numbers in base 10, + * like the Kharosthi numbers. Formally, a digit is a character * that has the property value Numeric_Type=Digit or Numeric_Type * =Decimal. - * + * * CAUTION: current implementation of library cpp-strings does * not implement above algorithm. It just returns the same result * as 'isdecimal()' which is NOT what Python str library does. */ + [[nodiscard]] inline const bool isdigit() const noexcept { return isdecimal(); @@ -693,33 +869,35 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns true if the string is not empty and is a valid identifier according to the language definition, or false otherwise. * * CAUTION: the current implementation of this method does not deal with the proper c++ - * defintiion of identifiers (see https://en.cppreference.com/w/cpp/language/identifiers + * defintiion of identifiers (see https://en.cppreference.com/w/cpp/language/identifiers * and https://www.unicode.org/reports/tr31/#Table_Lexical_Classes_for_Identifiers). * * While the specification of identifiers in c++ is this one: - * + * * identifier ::= XID_Start XID_Continue* * XID_Start ::= ID_Start XID_Continue* * ID_Start ::= * XID_Continue ::= * ID_Continue ::= ID_Start | - * + * * the currently implemented rule is this simpler one: - * + * * identifier ::= ID_Start id_continue* * id_continue ::= ID_Start | */ + [[nodiscard]] inline const bool isidentifier() const noexcept { - return !this->empty() && pcs::is_id_start((*this)[0]) && (this->size() == 1 || std::all_of(this->cbegin() + 1, this->cend(), pcs::is_id_continue)); + return !this->empty() && pcs::is_id_start((*this)[0]) && (this->size() == 1 || std::all_of(this->cbegin() + 1, this->cend(), pcs::is_id_continue)); } //--- islower() --------------------------------------- /** \brief Returns true if all cased characters in the string are lowercase and there is at least one cased character, or false otherwise. */ + [[nodiscard]] inline const bool islower() const noexcept { - return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_lower); + return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_lower); } @@ -728,11 +906,12 @@ namespace pcs // i.e. "pythonic c++ strings" * * CAUTION: current implementation just returns isdecimal() result, * while the description of isnumeric() should be this one: - * Numeric characters include digit characters, and all characters + * Numeric characters include digit characters, and all characters * that have the Unicode numeric value property. Formally, numeric * characters are those with the property value Numeric_Type=Digit, * Numeric_Type=Decimal or Numeric_Type=Numeric. */ + [[nodiscard]] inline const bool isnumeric() const noexcept { return isdecimal(); @@ -742,18 +921,20 @@ namespace pcs // i.e. "pythonic c++ strings" //--- isprintable() ----------------------------------- /** \brief Returns true if all characters in the string are printable or if the string is empty, or false otherwise. * - * Nonprintable characters are those characters defined in the Unicode - * character database as “Other” or “Separator”, excepting the ASCII + * Nonprintable characters are those characters defined in the Unicode + * character database as "Other" or "Separator", excepting the ASCII * space (0x20) which is considered printable. */ + [[nodiscard]] inline const bool isprintable() const noexcept { - return this->empty() || std::all_of(this->cbegin(), this->cend(), pcs::is_printable); + return this->empty() || std::all_of(this->cbegin(), this->cend(), pcs::is_printable); } //--- ispunctuation() --------------------------------- /** \brief Returns true if the string contains only one character and if this character belongs to the ASCII punctuation set. */ + [[nodiscard]] inline const bool ispunctuation() const noexcept { return this->size() == 1 && pcs::is_punctuation((*this)[0]); @@ -762,59 +943,110 @@ namespace pcs // i.e. "pythonic c++ strings" //--- isspace() --------------------------------------- /** \brief Returns true if there are only whitespace characters in the string and there is at least one character, or false otherwise. */ + [[nodiscard]] inline const bool isspace() const noexcept { - return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_space); + return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_space); } //--- istitle() --------------------------------------- /** \brief Returns true if the string is a titlecased string and there is at least one character, or false otherwise. * - * For instance uppercase characters may only follow uncased + * For instance uppercase characters may only follow uncased * characters and lowercase characters only cased ones. + * + * CAUTION: current implementation only tests for uppercase + * characters following whitespaces and lowercase characters + * anywhere else. */ + [[nodiscard]] inline const bool istitle() const noexcept { - return !this->empty && this->title() == *this; + return !this->empty() && this->title() == *this; } //--- isupper() --------------------------------------- /** \brief Returns true if all cased characters in the string are uppercase and there is at least one cased character, or false otherwise. */ + [[nodiscard]] inline const bool isupper() const noexcept { - return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_upper); + return !this->empty() && std::all_of(this->cbegin(), this->cend(), pcs::is_upper); } //--- is_words_sep() ---------------------------------- /** \brief Returns true if there are only whitespace and punctuation characters in the string and there is at least one character, or false otherwise. */ + [[nodiscard]] inline const bool is_words_sep() const noexcept { - return isspace() || ispunctuation(); + return !this->empty() && + std::all_of( + this->cbegin(), + this->cend(), + [](const value_type ch) { return pcs::is_space(ch) || pcs::is_punctuation(ch); } + ); } //--- join() ------------------------------------------ - /** \brief Returns a string which is the concatenation of the strings in the parameters list. + /** \brief Returns a string which is the concatenation of the strings in the array parameter. + * + * The separator between elements is the string to which this method is applied. + */ + template + [[nodiscard]] + CppStringT join(const std::array& strs) const noexcept + { + if (strs.empty()) [[unlikely]] + return CppStringT(); + + auto str_it = strs.cbegin(); + CppStringT res{ *str_it++ }; + while (str_it != strs.cend()) [[likely]] + res += *this + *str_it++; + return res; + } + + /** \brief Returns a string which is the concatenation of the strings in the vector parameter. + * + * The separator between elements is the string to which this method is applied. + */ + [[nodiscard]] + CppStringT join(const std::vector& strs) const noexcept + { + if (strs.empty()) [[unlikely]] + return CppStringT(); + + auto str_it = strs.cbegin(); + CppStringT res{ *str_it++ }; + while (str_it != strs.cend()) [[likely]] + res += *this + *str_it++; + return res; + } + + /** \brief Returns a string which is the concatenation of the strings in the parameters list. * * The separator between elements is the string to which this method is applied. */ template - inline CppStringT join(const CppStringT& first, const NextCppStringsT... others) const noexcept + [[nodiscard]] + inline CppStringT join(const CppStringT& first, const NextCppStringsT&... others) const noexcept requires (sizeof...(others) > 0) { return first + *this + this->join(others...); } /** \brief Single parameter signature. Returns a copy of this parameter. */ - inline const CppStringT join(const CppStringT& s) const noexcept + [[nodiscard]] + inline CppStringT join(const CppStringT& s) const noexcept { return s; } /** \brief Empty parameters list signature. Returns a copy of current string. */ + [[nodiscard]] inline const CppStringT join() const noexcept { return *this; @@ -824,15 +1056,16 @@ namespace pcs // i.e. "pythonic c++ strings" //--- ljust() ----------------------------------------- /** \brief Returns the string left justified in a string of length width. * - * Padding is done using the specified fillchar (default is an ASCII space). + * Padding is done using the specified fillchar (default is an ASCII space). * The original string is returned if width is less than or equal to len(s). */ + [[nodiscard]] inline CppStringT ljust(const size_type width, const value_type fillch = value_type(' ')) const noexcept { - if (this->size() >= width) + if (this->size() >= width) [[unlikely]] return *this; - else - return *this + CppStringT(width - this->size(), fillch); + else [[likely]] + return CppStringT(width - this->size(), fillch) + *this; } @@ -844,9 +1077,12 @@ namespace pcs // i.e. "pythonic c++ strings" */ inline CppStringT& lower() noexcept { - std::transform(this->begin(), this->end(), - this->begin(), - [](value_type ch) { return this->lower(ch); }); + std::transform( + this->begin(), + this->end(), + this->begin(), + [&](value_type ch) { return this->lower(ch); } + ); return *this; } @@ -855,44 +1091,117 @@ namespace pcs // i.e. "pythonic c++ strings" * Notice: uses the currently set std::locale, which is the "C" one * by default or any other one as previously set by the user. */ + [[nodiscard]] static inline const value_type lower(const value_type ch) noexcept { return value_type(std::tolower(ch)); } - + //--- lstrip() ---------------------------------------- /** \brief Returns a copy of the string with leading characters removed. * - * The passed string specifies the set of characters to be removed. - * The chars argument is not a prefix; rather, all combinations of + * The passed string specifies the set of characters to be removed. + * The chars argument is not a prefix; rather, all combinations of * its values are stripped. * To remove a prefix, rather call method 'removeprefix()'. */ - inline CppStringT lstrip(const CppStringT& prefix) const noexcept + [[nodiscard]] + inline CppStringT lstrip(const CppStringT& removedchars) const noexcept { - for (auto it = this->cbegin(); it != this->cend(); ++it) - if (std::none_of(prefix.cbegin(), prefix.cend(), [it](const value_type ch) { *it == ch; })) + for (auto it = this->cbegin(); it != this->cend(); ++it) [[likely]] + if (std::none_of(removedchars.cbegin(), removedchars.cend(), [it](const value_type ch) { return *it == ch; })) [[likely]] return CppStringT(it, this->cend()); return CppStringT(); } /** \brief Returns a copy of the string with leading whitespaces removed. */ + [[nodiscard]] inline CppStringT lstrip() const noexcept { - for (auto it = this->cbegin(); it != this->cend(); ++it) - if (*it != value_type(' ')) + for (auto it = this->cbegin(); it != this->cend(); ++it) [[likely]] + if (*it != value_type(' ')) [[unlikely]] return CppStringT(it, this->cend()); return CppStringT(); } + //--- operator () ------------------------------------- + /** \brief Generates a new string according to the specified slice. + * + * A slice is a range specified as [start, stop, step]. It may + * also be specified as [start, stop] in which case step = 1, + * or as [stop] in wich case start = 0 and step = 1. + * Values may be negative: negative step means reverse running + * and negative start or stop is relative to the end of the + * string. + * Notice: the stop value specifies an out of bounds index. + * \see class Slice and all its inheriting classes. + */ + template + requires std::is_signed_v + [[nodiscard]] + CppStringT operator() (Slice slice) const noexcept + { + // optimization on 1 by 1 step + if (slice.step() == 1) [[likely]] { + slice.begin(*this); + if (slice.start() < slice.stop()) [[likely]] + return this->substr(size_type(slice.start()), size_type(slice.stop() - slice.start() + 1)); + else [[unlikely]] + return CppStringT(); + } + + CppStringT res{}; + + // optimization on reversed 1 by 1 step + if (slice.step() == -1) { + slice.begin(*this); + if (slice.stop() < slice.start()) [[likely]] { + res = this->substr(size_type(slice.stop()), size_type(slice.start() - slice.stop() + 1)); + std::ranges::reverse(res); // notice: may use vectorization if available + } + return res; + } + + // finally, no trivial optimization -- and naive implementation... + for (slice.begin(*this); !slice.end(); ++slice) + res += (*this)[size_type(*slice)]; + + return res; + } + + /** \brief Generates a new string according to the specified slicing values. */ + [[nodiscard]] + inline CppStringT operator() (const long long start, const long long stop, const long long step = 1) const noexcept + { + Slice slice(start, stop, step); + return (*this)(slice); + } + + + //--- operator * -------------------------------------- + /** \brief Generates a new string with count times the content of this string. */ + [[nodiscard]] + CppStringT operator* (std::int64_t count) const noexcept + { + if (count <= 0) [[unlikely]] + return CppStringT(); + + CppStringT res(*this); + while (--count) [[likely]] + res += *this; + return res; + } + + //--- partition() ------------------------------------- - /** Split the string at the first occurrence of sep, and returns a 3-items vector containing the part before the separator, the separator itself, and the part after the separator. + /** \brief Splits the string at the first occurrence of sep, and returns a 3-items vector containing the part before the separator, the separator itself, and the part after the separator. * * If the separator is not found, returns a 3-items vector - * containing the string itself, followed by two empty strings. + * containing the string itself, followed by two empty strings. */ + [[nodiscard]] std::vector partition(const CppStringT& sep) const noexcept { const size_type sep_index = find(sep); @@ -910,6 +1219,7 @@ namespace pcs // i.e. "pythonic c++ strings" //--- removeprefix() ---------------------------------- /** \brief If the string starts with the prefix string, returns a new string with the prefix removed. Otherwise, returns a copy of the original string. */ + [[nodiscard]] inline CppStringT removeprefix(const CppStringT& prefix) const noexcept { if (this->startswith(prefix)) { @@ -923,11 +1233,12 @@ namespace pcs // i.e. "pythonic c++ strings" //--- removesuffix() ---------------------------------- /** \brief If the string ends with the suffix string, returns a new string with the suffix removed. Otherwise, returns a copy of the original string. */ + [[nodiscard]] inline CppStringT removesuffix(const CppStringT& suffix) const noexcept { if (this->endswith(suffix)) { const size_type suffix_length = suffix.size(); - return this->substr(0, this->size() - suffix_length + 1); + return this->substr(0, this->size() - suffix_length); } else return *this; @@ -935,92 +1246,94 @@ namespace pcs // i.e. "pythonic c++ strings" //--- replace() --------------------------------------- - /** \brief Returns a copy of the string with all occurrences of substring old replaced by new. */ - CppStringT replace(const CppStringT& old, const CppStringT& new_) const noexcept - { - if (!this->contains(old)) - return *this; - - CppStringT res{}; - size_type last_index = 0; - size_type current_index = 0; - while ((current_index = this->find(old)) != CppStringT::npos) { - res += this->substr(last_index, current_index - last_index) + new_; - last_index = current_index; - } - - if (last_index != this->size()) - res += this->substr(last_index, this->size - last_index); - - return res; - } - - /** \brief Returns a copy of the string with first count occurrences of substring old replaced by new. */ - CppStringT replace(const CppStringT& old, const CppStringT& new_, size_type count) const noexcept + /** \brief Returns a copy of the string with first count occurrences of substring 'old' replaced by 'new_'. */ + [[nodiscard]] + CppStringT replace(const CppStringT& old, const CppStringT& new_, size_type count = -1) const noexcept { - if (!this->contains(old) || count == 0) + if (old == new_ || old.empty()) [[unlikely]] return *this; CppStringT res{}; - size_type last_index = 0; + size_type prev_index = 0; size_type current_index = 0; - while (count > 0 && (current_index = this->find(old)) != CppStringT::npos) { - res += this->substr(last_index, current_index - last_index) + new_; - last_index = current_index; + while (count > 0 && (current_index = this->find(old, prev_index)) != CppStringT::npos) { + res += this->substr(prev_index, current_index - prev_index) + new_; + prev_index = current_index + 1; --count; } - if (last_index != this->size()) - res += this->substr(last_index, this->size - last_index); + if (prev_index < this->size()) [[likely]] + res += this->substr(prev_index, this->size() - prev_index); return res; } //--- rfind() ----------------------------------------- - /** Returns the highest index in the string where substring sub is found within the slice str[start:end], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the highest index in the string where substring sub is found within the slice str[start:end], or -1 (i.e. 'npos') if sub is not found. * * Note that this is an offset from the start of the string, not the end. * * Note: this method should be used only if you need to know the position * of sub. To check if sub is a substring or not, use the method contains(). * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. The + * returned position is the size of the string. + * * \see find(), find_n() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type rfind(const CppStringT& sub, const size_type start, const size_type end) const noexcept { - if (start > end) + if (start > end) [[unlikely]] return CppStringT::npos; - else - return this->substr(start, end - start + 1).rfind(sub); + else if (sub.empty()) [[unlikely]] + return 0; + else [[likely]] { + const size_type found_pos{ this->substr(start, end - start + 1).rfind(sub) }; + return (found_pos == CppStringT::npos) ? CppStringT::npos : found_pos + start; + } } - /** Returns the highest index in the string where substring sub is found starting at start position in string, or -1 (i.e. 'npos') if sub is not found. + + /** \brief Returns the highest index in the string where substring sub is found starting at start position in string, or -1 (i.e. 'npos') if sub is not found. * * Note that this is an offset from the start of the string, not the end. * * Note: this method should be used only if you need to know the position * of sub. To check if sub is a substring or not, use the method contains(). * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. The + * returned position is the size of the string. + * * \see find(), find_n() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type rfind(const CppStringT& sub, const size_type start) const noexcept { - return rfind(sub, start, this->size() - start + 1); + return rfind(sub, start, this->size() - 1); } - /** Returns the highest index in the string where substring sub is found in the whole string, or -1 (i.e. 'npos') if sub is not found. + + /** \brief Returns the highest index in the string where C-substring sub is found in the whole string, or -1 (i.e. 'npos') if sub is not found. * * Note that this is an offset from the start of the string, not the end. * * Note: this method should be used only if you need to know the position * of sub. To check if sub is a substring or not, use the method contains(). * + * CAUTION: empty substrings are considered to be in the string if start and + * end positions are both less than the string size and if start <= end. The + * returned position is the size of the string. + * * \see find(), find_n() and rfind_n(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type rfind(const CppStringT& sub) const noexcept { return MyBaseClass::rfind(sub); @@ -1028,7 +1341,7 @@ namespace pcs // i.e. "pythonic c++ strings" //--- rfind_n() --------------------------------------- - /** Returns the highest index in the string where substring sub is found within the slice str[start:start+count-1], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the highest index in the string where substring sub is found within the slice str[start:start+count-1], or -1 (i.e. 'npos') if sub is not found. * * Note: this method should be used only if you need to know the position * of sub. To check if sub is a substring or not, use the method contains_n(). @@ -1036,12 +1349,13 @@ namespace pcs // i.e. "pythonic c++ strings" * \see find(), find_n() and rfind(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type rfind_n(const CppStringT& sub, const size_type start, const size_type count) const noexcept { return rfind(sub, start, start + count - 1); } - /** Returns the highest index in the string where substring sub is found within the slice str[0:count-1], or -1 (i.e. 'npos') if sub is not found. + /** \brief Returns the highest index in the string where substring sub is found within the slice str[0:count-1], or -1 (i.e. 'npos') if sub is not found. * * Note: this method should be used only if you need to know the position * of sub. To check if sub is a substring or not, use the method contains_n(). @@ -1049,42 +1363,49 @@ namespace pcs // i.e. "pythonic c++ strings" * \see find(), find_n() and rfind(). * \see index(), index_n(), rindex() and rindex_n(). */ + [[nodiscard]] inline constexpr size_type rfind_n(const CppStringT& sub, const size_type count) const noexcept { - return rfind(sub, 0, count); + if (count == 0) [[unlikely]] + return CppStringT::npos; + else [[likely]] + return rfind(sub, 0, count - 1); } //--- rindex() ---------------------------------------- - /** Like rfind(sub, start, end), but raises NotFoundException when the substring is not found. + /** \brief Like rfind(sub, start, end), but raises NotFoundException when the substring is not found. * * \see index(), index_n() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type rindex(const CppStringT& sub, const size_type start, const size_type end) const { const size_type ret_value = rfind(sub, start, end); - if (size_type == CppStringT::npos) - throw NotFoundException(std::format("substring \"{}\" not found in string \"{}\"", sub, this->c_str())); + if (ret_value == CppStringT::npos) + throw NotFoundException("substring not found in string"); else return ret_value; } - /** Like rfind(sub, start), but raises NotFoundException when the substring is not found. + /** \brief Like rfind(sub, start), but raises NotFoundException when the substring is not found. * * \see index(), index_n() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type rindex(const CppStringT& sub, const size_type start) const { return rindex(sub, start, this->size() - 1); } - /** Like rfind(sub), but raises NotFoundException when the substring is not found. + /** \brief Like rfind(sub), but raises NotFoundException when the substring is not found. * * \see index(), index_n() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type rindex(const CppStringT& sub) const { return rindex(sub, 0, this->size() - 1); @@ -1092,21 +1413,23 @@ namespace pcs // i.e. "pythonic c++ strings" //--- rindex_n() -------------------------------------- - /** Like rfind_n(sub, start, count), but raises NotFoundException when the substring is not found. + /** \brief Like rfind_n(sub, start, count), but raises NotFoundException when the substring is not found. * * \see index_n(), rindex() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type rindex_n(const CppStringT& sub, const size_type start, const size_type count) const { return rindex(sub, start, start + count - 1); } - /** Like rfind_n(sub, count), but raises NotFoundException when the substring is not found. + /** \brief Like rfind_n(sub, count), but raises NotFoundException when the substring is not found. * * \see index_n(), rindex() and rindex_n(). * \see find(), find_n(), rfind() and rfind_n(). */ + [[nodiscard]] inline constexpr size_type rindex_n(const CppStringT& sub, const size_type count) const { return rindex(sub, 0, count); @@ -1119,21 +1442,23 @@ namespace pcs // i.e. "pythonic c++ strings" * Padding is done using the specified fillchar (default is an ASCII space). * The original string is returned if width is less than or equal to len(s). */ + [[nodiscard]] inline CppStringT rjust(const size_type width, const value_type fillch = value_type(' ')) const noexcept { - if (this->size() >= width) + if (this->size() >= width) [[unlikely]] return *this; - else - return CppStringT(width - this->size(), fillch) + *this; + else [[likely]] + return *this + CppStringT(width - this->size(), fillch); } //--- rpartition() ------------------------------------- - /** Splits the string at the last occurrence of sep, and returns a 3-items vector containing the part before the separator, the separator itself, and the part after the separator. + /** \brief Splits the string at the last occurrence of sep, and returns a 3-items vector containing the part before the separator, the separator itself, and the part after the separator. * * If the separator is not found, returns a 3-items vector * containing the string itself, followed by two empty strings. */ + [[nodiscard]] std::vector rpartition(const CppStringT& sep) const noexcept { const size_type sep_index = rfind(sep); @@ -1152,107 +1477,50 @@ namespace pcs // i.e. "pythonic c++ strings" //--- rsplit() ---------------------------------------- /** \brief Returns a vector of the words in the whole string, as seperated with whitespace strings. * - * Notice: runs of consecutive whitespace are regarded as a single - * separator, and the result will contain no empty strings at the - * start or end if the string has leading or trailing whitespace. - * Consequently, splitting an empty string or a string consisting - * of just whitespace with a whitespace separator returns an ampty - * vector. + * Notice: consecutive whitespaces are each regarded as a + * single separator. So, they each separate empty strings. */ - inline std::vector rsplit() const noexcept + [[nodiscard]] + inline std::vector rsplit() noexcept { return split(); } /** \brief Returns a vector of the words in the whole string, using sep as the delimiter string. */ - inline std::vector rsplit(const CppStringT& sep) const noexcept + [[nodiscard]] + inline std::vector rsplit(const CppStringT& sep) noexcept { return split(sep); } /** \brief Returns a vector of the words in the string, as seperated with whitespace strings. At most maxsplit splits are done, the rightmost ones. */ - std::vector rsplit(const size_type maxsplit) const noexcept + [[nodiscard]] + inline std::vector rsplit(const size_type maxsplit) noexcept { - std::vector res{}; - - if (maxsplit == 0) { - res.push_back(*this); - } - else { - const CppStringT whitespace(value_type(' ')); - std::vector all_words{ this->split(whitespace) }; - - size_type count = maxsplit; - auto word_it = all_words.crbegin(); - size_type last_split_index = all_words.size(); - while (count > 0 && word_it != all_words.crend()) { - if (!word_it->empty()) { - res.insert(res.cbegin(), *word_it); - --count; - --last_split_index; - } - word_it++; - } - - size_type chars_count = last_split_index; - while (last_split_index > 0) { - chars_count += all_words[last_split_index].size(); - --last_split_index; - } - if (chars_count > 0) - res.insert(res.cbegin(), this->substr(0, chars_count)); - /* - constexpr CppStringT spc2(" "); - constexpr CppStringT spc(value_type(' ')); - CppStringT tmp = *this; - while (tmp.contains(spc2)) - tmp = tmp.replace(spc2, spc); - - res = tmp->rsplit(spc, maxsplit); - */ - } - - return res; + return rsplit(CppStringT(value_type(' ')), maxsplit); } /** \brief Returns a vector of the words in the string, using sep as the delimiter string. At most maxsplit splits are done, the rightmost ones. */ - std::vector rsplit(const CppStringT& sep, const size_type maxsplit) const noexcept + [[nodiscard]] + std::vector rsplit(const CppStringT& sep, const size_type maxsplit) noexcept { std::vector res{}; - if (maxsplit == 0) { + if (maxsplit == 0) [[unlikely]] { res.push_back({ *this }); } - else { - std::vector all_words{ this->split(sep) }; - - size_type count = maxsplit; - auto word_it = all_words.crbegin(); - size_type last_split_index = all_words.size(); - while (count > 0 && word_it != all_words.crend()) { - res.insert(res.cbegin(), *word_it); - --count; - --last_split_index; - word_it++; - } - - size_type chars_count = last_split_index; - while (last_split_index > 0) { - chars_count += all_words[last_split_index].size(); - --last_split_index; - } - if (chars_count > 0) - res.insert(res.cbegin(), this->substr(0, chars_count)); - /* - std::vector indexes{}; - CppStringT tmp = *this; - size_type count = maxsplit; - size_type index; - while ((index = tmp.rfind(sep)) != CppStringT::npos && count > 0) { + else [[likely]] { + const size_type sep_size{ sep.size() }; + std::vector indexes{}; + CppStringT tmp{ *this }; + size_type count{ maxsplit }; + size_type index{ 0 }; + + while ((index = tmp.rfind(sep)) != CppStringT::npos && count > 0) { indexes.insert(indexes.begin(), index); if (index == 0) break; - tmp = tmp.substr(0, index-1); + tmp = tmp.substr(0, index); count--; } @@ -1260,12 +1528,12 @@ namespace pcs // i.e. "pythonic c++ strings" res.push_back(*this); else { index = 0; - for (const size_type ndx: indexes) { + for (const size_type ndx : indexes) { res.push_back(this->substr(index, ndx - index)); - index = ndx + 1; + index = ndx + sep_size; + } } res.push_back(this->substr(index, this->size() - index)); - */ } return res; @@ -1280,20 +1548,22 @@ namespace pcs // i.e. "pythonic c++ strings" * its values are stripped. * To remove a suffix, rather call method 'removesuffix()'. */ - inline CppStringT rstrip(const CppStringT& prefix) const noexcept + [[nodiscard]] + inline CppStringT rstrip(const CppStringT& removedchars) const noexcept { for (auto it = this->crbegin(); it != this->crend(); ++it) - if (std::none_of(prefix.cbegin(), prefix.cend(), [it](const value_type ch) { *it == ch; })) - return CppStringT(this->cbegin(), it); + if (std::none_of(removedchars.cbegin(), removedchars.cend(), [it](const value_type ch) { return *it == ch; })) + return CppStringT(this->cbegin(), this->cbegin() + this->size() - (it - this->crbegin())); return CppStringT(); } /** \brief Returns a copy of the string with trailing whitespaces removed. */ + [[nodiscard]] inline CppStringT rstrip() const noexcept { for (auto it = this->crbegin(); it != this->crend(); ++it) if (*it != value_type(' ')) - return CppStringT(this->cbegin(), it); + return CppStringT(this->cbegin(), this->cbegin() + this->size() - (it - this->crbegin())); return CppStringT(); } @@ -1301,99 +1571,77 @@ namespace pcs // i.e. "pythonic c++ strings" //--- split() ----------------------------------------- /** \brief Returns a vector of the words in the whole string, as seperated with whitespace strings. * - * Notice: runs of consecutive whitespace are regarded as a single - * separator, and the result will contain no empty strings at the - * start or end if the string has leading or trailing whitespace. - * Consequently, splitting an empty string or a string consisting - * of just whitespace with a whitespace separator returns an ampty - * vector. + * Notice: consecutive whitespaces are each regarded as a + * single separator. So, they each separate empty strings. */ - inline std::vector split() const noexcept + [[nodiscard]] + inline std::vector split() noexcept { - std::vector res; - constexpr CppStringT whitespace(value_type(' ')); + std::vector res; + const CppStringT whitespace(value_type(' ')); for (const auto& word : *this | std::views::split(whitespace)) - if (!word.empty()) - res.push_back(CppStringT(word.begin(), word.end())); + res.push_back(CppStringT(word.begin(), word.end())); return res; } /** \brief Returns a vector of the words in the whole string, using sep as the delimiter string. * * Notice: consecutive delimiters are not grouped together and are - * deemed to delimit empty strings (for example, "1,,2".split(",") - * returns {"1", "", "2"}). The sep argument may consist of multiple - * characters (for example, "1<>2<>3".split("<>") returns {"1", "2", - * "3"]). Splitting an empty string with a specified separator + * deemed to delimit empty strings (for example, "1,,2".split(",") + * returns {"1", "", "2"}). The sep argument may consist of multiple + * characters (for example, "1<>2<>3".split("<>") returns {"1", "2", + * "3"]). Splitting an empty string with a specified separator * returns {""}. */ - inline std::vector split(const CppStringT& sep) const noexcept + [[nodiscard]] + inline std::vector split(const CppStringT& sep) noexcept { - std::vector res; + std::vector res; for (const auto& word : *this | std::views::split(sep)) res.push_back(CppStringT(word.begin(), word.end())); return res; } /** \brief Returns a vector of the words in the string, as seperated with whitespace strings. At most maxsplit splits are done, the leftmost ones. */ - std::vector split(const size_type maxsplit) const noexcept + [[nodiscard]] + inline std::vector split(const size_type maxsplit) noexcept { - std::vector res{}; - - if (maxsplit == 0) { - res.push_back(*this); - } - else { - const CppStringT whitespace(value_type(' ')); - std::vector all_words{ this->split(whitespace) }; - - size_type count = maxsplit; - auto word_it = all_words.cbegin(); - while (count > 0 && word_it != all_words.cend()) { - if (!word_it->empty()) { - res.insert(res.cbegin(), *word_it); - --count; - } - word_it++; - } - - size_type chars_count = 0; - for (auto it = word_it; it != all_words.cend(); ++it) { - chars_count += it->size() + 1; - } - if (chars_count > 0) - res.insert(res.cbegin(), this->substr(this->cbegin() + chars_count - 1, this->cend())); - } - - return res; + return split(CppStringT(value_type(' ')), maxsplit); } /** \brief Returns a vector of the words in the string, using sep as the delimiter string. At most maxsplit splits are done, the leftmost ones. */ - std::vector split(const CppStringT& sep, const size_type maxsplit) const noexcept + [[nodiscard]] + std::vector split(const CppStringT& sep, const size_type maxsplit) noexcept { std::vector res{}; - if (maxsplit == 0) { + if (maxsplit == 0) [[unlikely]] { res.push_back(*this); } - else { - const CppStringT whitespace(value_type(' ')); - std::vector all_words{ this->split(sep) }; - - size_type count = maxsplit; - auto word_it = all_words.cbegin(); - while (count > 0 && word_it != all_words.cend()) { - res.insert(res.cbegin(), *word_it); - --count; - word_it++; + else [[likely]] { + const size_type sep_size{ sep.size() }; + std::vector indexes{}; + size_type count{ maxsplit }; + size_type index{ 0 }; + + while ((index = this->find(sep, index)) != CppStringT::npos && count > 0) { + indexes.push_back(index); + if (index == this->size()) + break; + index += sep_size; + count--; } - size_type chars_count = 0; - for (auto it = word_it; it != all_words.cend(); ++it) { - chars_count += it->size() + 1; + if (indexes.size() == 0) + res.push_back(*this); + else { + index = 0; + for (const size_type ndx : indexes) { + res.push_back(this->substr(index, ndx - index)); + index = ndx + sep_size; + } } - if (chars_count > 0) - res.insert(res.cbegin(), this->substr(this->cbegin() + chars_count - 1, this->cend())); + res.push_back(this->substr(index, this->size() - index)); } return res; @@ -1414,54 +1662,51 @@ namespace pcs // i.e. "pythonic c++ strings" * \x1c File Separator * \x1d Group Separator * \x1e Record Separator + * Next separators values, detected by Python method splitlines(), are currently NOT detected by CppStrings * \x85 Next Line (C1 Control Code) * \u2028 Line Separator * \u2029 Paragraph Separator */ + [[nodiscard]] std::vector splitlines(const bool keep_end = false) const noexcept { std::vector res{}; CppStringT current{}; bool prev_cr = false; - for (const value_type& ch : *this) { + for (const value_type ch : *this) { switch (ch) { - case value_type('\v'): // Line Tabulation - case value_type('\x0b'): // Line Tabulation - case value_type('\f'): // Form Feed - case value_type('\x0c'): // Form Feed - case value_type('\x1c'): // File Separator - case value_type('\x1d'): // Group Separator - case value_type('\x1e'): // Record Separator - case value_type('\x85'): // Next Line (C1 Control Code) -#pragma warning(push) -#pragma warning(disable: 4566) // to get no warning when current page code is not compatible with next unicode points - case value_type('\u2028'): // Line Separator - case value_type('\u2029'): // Paragraph Separator -#pragma warning(pop) - if (prev_cr) { + case 0x0b: [[unlikely]] // Line Tabulation, \v as well as \x0b and \013 + case 0x0c: [[unlikely]] // Form Feed, \f as well as \x0c and \014 + case 0x1c: [[unlikely]] // File Separator, or \034 + case 0x1d: [[unlikely]] // Group Separator, or \035 + case 0x1e: [[unlikely]] // Record Separator, or \036 + //case L'\u0085': [[unlikely]] // Next Line (C1 Control Code), or \0205 + //case L'\u2028': [[unlikely]] // Line Separator + //case L'\u2029': [[unlikely]] // Paragraph Separator + if (prev_cr) [[unlikely]] { res.push_back(current); current.clear(); } - if (keep_end) + if (keep_end) [[unlikely]] current += ch; res.push_back(current); current.clear(); prev_cr = false; break; - case value_type('\r'): // Line Feed - if (prev_cr) { + case value_type('\r'): [[unlikely]] // Line Feed + if (prev_cr) [[unlikely]] { res.push_back(current); current.clear(); } - if (keep_end) + if (keep_end) [[unlikely]] current += ch; prev_cr = true; break; - case value_type('\n'): // Line Feed - if (keep_end) + case value_type('\n'): [[unlikely]] // Carriage return + if (keep_end) [[unlikely]] current += ch; res.push_back(current); current.clear(); @@ -1469,74 +1714,83 @@ namespace pcs // i.e. "pythonic c++ strings" break; - default: - if (prev_cr) { + default: [[likely]] + if (prev_cr) [[unlikely]] { res.push_back(current); current.clear(); + prev_cr = false; } current += ch; break; } } + if (prev_cr) [[unlikely]] { + res.push_back(current); + } + return res; } //--- startswith() ------------------------------------ - /** Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at start position and stops at end position. */ - inline const bool startswith(const CppStringT& suffix, const size_type start, const size_type end) const noexcept + /** \brief Returns true if the string starts with the specified prefix, otherwise returns false. Test begins at start position and stops at end position. */ + [[nodiscard]] + inline const bool startswith(const CppStringT& prefix, const size_type start, const size_type end) const noexcept { - return startswith(std::span{ suffix }, start, end); + return this->substr(start, end - start + 1).MyBaseClass::starts_with(prefix); } - /** Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at start position and stops at end of string. */ - inline const bool startswith(const CppStringT& suffix, const size_type start) const noexcept + /** \brief Returns true if the string starts with the specified prefix, otherwise returns false. Test begins at start position and stops at end of string. */ + [[nodiscard]] + inline const bool startswith(const CppStringT& prefix, const size_type start) const noexcept { - return startswith(std::span{ suffix }, start, this->size() - 1); + return startswith(prefix, start, this->size() - 1); } - /** Returns true if the string starts with the specified suffix, otherwise returns false. Test runs on the whole string. */ - inline const bool startswith(const CppStringT& suffix) const noexcept + /** \brief Returns true if the string starts with the specified prefix, otherwise returns false. Test runs on the whole string. */ + [[nodiscard]] + inline const bool startswith(const CppStringT& prefix) const noexcept { - return this->starts_with(suffix); + return this->starts_with(prefix); } - /** Returns true if the string starts with any of the specified suffixes, otherwise returns false. Test begins at start position and stops at end of string. */ - inline const bool startswith(const std::span& suffixes, const size_type start, const size_type end) const noexcept + /** \brief Returns true if the string starts with any of the specified prefixes, otherwise returns false. Test begins at start position and stops at end of string. */ + [[nodiscard]] + inline const bool startswith(const std::initializer_list& prefixes, const size_type start, const size_type end) const noexcept { if (start > end) return false; - else - return std::any_of(suffixes.cbegin(), suffixes.cend(), this->substr(start, end).starts_with); - /* - for (auto& suffix : suffixes) { - if (this->substr(start, end).starts_with(suffix)) + + CppStringT tmp(this->substr(start, end)); + for (auto& prefix : prefixes) { + if (tmp.starts_with(prefix)) return true; } - return false; - */ } //--- startswith_n() ---------------------------------- - /** Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at start position and stops after count positions. */ - inline const bool startswith_n(const CppStringT& suffix, const size_type start, const size_type count) const noexcept + /** \brief Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at start position and stops after count positions. */ + [[nodiscard]] + inline const bool startswith_n(const CppStringT& prefix, const size_type start, const size_type count) const noexcept { - return startswith(std::span{ suffix }, start, start + count - 1); + return this->substr(start, count).MyBaseClass::starts_with(prefix); } - /** Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at position 0 and stops after count positions. */ - inline const bool startswith_n(const CppStringT& suffix, const size_type count) const noexcept + /** \brief Returns true if the string starts with the specified suffix, otherwise returns false. Test begins at position 0 and stops after count positions. */ + [[nodiscard]] + inline const bool startswith_n(const CppStringT& prefix, const size_type count) const noexcept { - return startswith(std::span{ suffix }, 0, count - 1); + return this->substr(0, count).MyBaseClass::starts_with(prefix); } - /** Returns true if the string starts with any of the specified suffixes, otherwise returns false. Test begins at start position and stops after count positions. */ - inline const bool startswith_n(const std::span& suffixes, const size_type start, const size_type count) const noexcept + /** \brief Returns true if the string starts with any of the specified suffixes, otherwise returns false. Test begins at start position and stops after count positions. */ + [[nodiscard]] + inline const bool startswith_n(const std::initializer_list& prefix, const size_type start, const size_type count) const noexcept { - return startswith(suffixes, start, start + count - 1); + return startswith(prefix, start, count); } @@ -1547,12 +1801,14 @@ namespace pcs // i.e. "pythonic c++ strings" * The chars argument is not a prefix; rather, all combinations of * its values are stripped. */ - inline CppStringT strip(const CppStringT& prefix) const noexcept + [[nodiscard]] + inline CppStringT strip(const CppStringT& removedchars) const noexcept { - return this->rstrip(prefix).lstrip(prefix); + return this->rstrip(removedchars).lstrip(removedchars); } /** \brief Returns a copy of the string with the leading and trailing whitespaces removed. */ + [[nodiscard]] inline CppStringT strip() const noexcept { return this->rstrip().lstrip(); @@ -1560,12 +1816,14 @@ namespace pcs // i.e. "pythonic c++ strings" //--- substr() ---------------------------------------- - /** \brief Returns acopy of the string, starting at index start and ending after count characters. */ - inline CppStringT substr(const size_type start, const size_type count) const noexcept + /** \brief Returns a copy of the string, starting at index start and ending after count characters. */ + [[nodiscard]] + inline CppStringT substr(const size_type start, const size_type count = -1) const noexcept { - if (start > this->size()) - return *this; - const size_type width = std::min(count, this->size() - start); + if (start > this->size()) [[unlikely]] + return CppStringT(); + + const size_type width{ std::min(count, this->size() - start + 1) }; return CppStringT(MyBaseClass::substr(start, width)); } @@ -1575,30 +1833,27 @@ namespace pcs // i.e. "pythonic c++ strings" * * Note that it is not necessarily true that s.swapcase().swapcase() == s. */ + [[nodiscard]] inline CppStringT swapcase() const noexcept { - /* - CppStringT res(*this); - std::transform(this->cbegin(), this->cend(), res.begin(), pcs::swap_case); - return res; - */ CppStringT res; - std::ranges::copy(std::views::transform(*this, pcs::swap_case), std::back_inserter(res)); + std::ranges::transform(*this, std::back_inserter(res), [](const value_type c) -> value_type { return pcs::swap_case(c); }); return res; } //--- title() ----------------------------------------- /** \brief Returns a titlecased copy of the string where words start with an uppercase character and the remaining characters are lowercase. */ + [[nodiscard]] CppStringT title() const noexcept { - constexpr CppStringT whitespace(value_type(' ')); + const CppStringT whitespace(value_type(' ')); CppStringT res(*this); std::vector words = res.split(whitespace); for (CppStringT& word : words) - word.capitalize(); + word = word.capitalize(); return whitespace.join(words); } @@ -1607,28 +1862,19 @@ namespace pcs // i.e. "pythonic c++ strings" //--- translate() ------------------------------------- /** \brief Returns a copy of the string in which each character has been mapped through the given translation table. * - * The table must be of type CppStringT::TransTable. When a character + * The table must be of type CppStringT::TransTable. When a character * to be translated is not available as an entry in the tranlation * table, it is set as is in the resulting string. */ - CppStringT translate(const TransTable& table) const noexcept + [[nodiscard]] + CppStringT translate(TransTable& table) noexcept { - /* CppStringT res{}; for (auto ch : *this) { try { res += table[ch]; } catch (...) { res += ch; } } return res; - */ - - CppStringT res{}; - auto _translate = [&](auto const ch) { - try { return table[ch]; } - catch (...) { return ch; } - }; - std::ranges::copy(std::views::transform(*this, _translate), std::back_inserter(res)); - return res; } @@ -1640,10 +1886,9 @@ namespace pcs // i.e. "pythonic c++ strings" */ inline CppStringT& upper() noexcept { - std::transform(this->begin(), this->end(), - this->begin(), - [](value_type ch) { return this->upper(ch); }); - return *this; + CppStringT res{}; + std::ranges::transform(*this, std::back_inserter(res), [&](const value_type ch) -> value_type { return this->upper(ch); }); + return *this = res; } /** \brief Returns uppercase conversion of the character. @@ -1651,6 +1896,7 @@ namespace pcs // i.e. "pythonic c++ strings" * Notice: uses the currently set std::locale, which is the "C" one * by default or any other one as previously set by the user. */ + [[nodiscard]] static inline const value_type upper(const value_type ch) noexcept { return value_type(std::toupper(ch)); @@ -1660,114 +1906,368 @@ namespace pcs // i.e. "pythonic c++ strings" //--- zfill() ----------------------------------------- /** \brief Returns a copy of the string left filled with ASCII '0' digits to make a string of length width. * - * A leading sign prefix ('+'/'-') is handled by inserting the padding - * after the sign character rather than before. The original string is + * A leading sign prefix ('+'/'-') is handled by inserting the padding + * after the sign character rather than before. The original string is * returned if width is less than or equal to len(s). */ + [[nodiscard]] inline CppStringT zfill(const size_type width) const noexcept { - if (this->size() >= width) + if (this->size() >= width) [[unlikely]] return *this; const size_type padding_width = width - this->size(); - if ((*this)[0] == '+' || (*this)[0] == '-') - return (*this)[0] + this->substr(1, this->size() - 1).ljust(padding_width, value_type('0')); - else - return this->ljust(padding_width, value_type('0')); + if ((*this)[0] == '-' || (*this)[0] == '+') [[unlikely]] + return (*this)[0] + this->substr(1, this->size() - 1).ljust(width - 1, value_type('0')); + else [[likely]] + return this->ljust(width, value_type('0')); } - - private: - std::map m_table{}; // the itnernal storage of the translation table. Access it via the indexing operator. - }; //===== litteral operators ============================ /** \brief Forms a CppString literal. */ - inline const CppString operator""cs(const char* str, std::size_t len) + inline CppString operator""_cs(const char* str, std::size_t len) { return CppString(CppString::MyBaseClass(str, len)); } - /** \brief Forms a CppString view literal. */ - inline const CppString operator""csv(const char* str, std::size_t len) - { - return CppString(CppString::MyStringView(str, len)); - } - /** \brief Forms a CppWString literal. */ - inline const CppWString operator""cs(const wchar_t* str, std::size_t len) + inline CppWString operator""_cs(const wchar_t* str, std::size_t len) { return CppWString(CppWString::MyBaseClass(str, len)); } - /** \brief Forms a CppWString view literal. */ - inline const CppWString operator""csv(const wchar_t* str, std::size_t len) + + //===== Slices ======================================== + //--- slices base ------------------------------------- + /** \brief Base class for slices, with start, stop and step specified values. */ + template + requires std::is_signed_v + class Slice { - return CppWString(CppWString::MyStringView(str, len)); - } + public: + static constexpr IntT DEFAULT{ std::numeric_limits::min() }; + + //--- Constructors / Destructor ------------------- + Slice(const IntT start = DEFAULT, const IntT stop = DEFAULT, const IntT step = DEFAULT) noexcept //!< Valued constructor + : _start(start) + , _stop(stop) + , _step(step) + {} + + virtual ~Slice() noexcept = default; //!< Default destructor. + + + //--- iterating ----------------------------------- + template +#if (defined(_HAS_CXX20) && _HAS_CXX20) + requires std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v +#else + requires std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v +#endif + inline const IntT begin(const CppStringT& str) noexcept //!< starts iterating on specified CppString. + { + return _prepare_iterating(IntT(str.size())); + } + + [[nodiscard]] + inline const bool end() const noexcept //!< returns true when iterating is over, or false otherwise. + { + return _step == 0 ? true : _step > 0 ? _index >= _stop : _index <= _stop; + } + + inline Slice operator++() noexcept //!< iterates one step, pre-increment. Caution: index may be out of bounds. Check '!end()' before dereferencing the slice. + { + _index += _step; + return *this; + } + + inline Slice operator++(int) noexcept //!< iterates one step, post-increment. Caution: index may be out of bounds. Check '!end()' before dereferencing the slice. + { + _index += _step; + return *this; + } + + [[nodiscard]] + inline const IntT operator*() noexcept //!< dereferences the slice. + { + return _index; + } + + //--- properties ---------------------------------- + inline IntT start() { return _start; } //!< Returns the start index of this slide + inline IntT stop() { return _stop; } //!< Returns the stop index of this slide + inline IntT step() { return _step; } //!< Returns the step value of this slide + + + private: + IntT _start{ 0 }; + IntT _stop{ DEFAULT }; + IntT _step{ 1 }; + + IntT _index{ 0 }; + + [[nodiscard]] + const IntT _prepare_iterating(const IntT str_size) noexcept + { + if (_start == DEFAULT) { + if (_step < 0 && _step != DEFAULT) [[unlikely]] + _start = str_size - 1; + else [[likely]] + _start = 0; + } + else if (_start < 0) { + _start += str_size; + if (_start < 0) [[unlikely]] + _start = 0; + } + else if (_start >= str_size) { + if (_step < 0 && _step != DEFAULT) + _start = str_size - 1; + } + + if (_stop == DEFAULT) { + if (_step < 0 && _step != DEFAULT) [[unlikely]] + _stop = 0; + else + _stop = str_size; + } + else if (_stop < 0) { + _stop += str_size; + if (_stop < 0) + _stop = 0; + } + else if (_stop > str_size) + _stop = str_size; + + if (_step == DEFAULT) [[likely]] + _step = 1; + if (_step < 0) [[unlikely]] { + if (_start <= _stop) + _step = 0; // will force end() to true + } + else [[unlikely]] { + if (_start >= _stop) + _step = 0; // will force end() to true + } + + return _index = _start; + } + }; + + + /** \brief Class of slices with default stop and step values. */ + template + requires std::is_signed_v + struct StartSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StartSlice(const IntT start = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(start, MyBaseClass::DEFAULT, 1) + {} + + virtual ~StartSlice() noexcept = default; //!< Default destructor. + }; + + + /** \brief Class of slices with default start and step values. */ + template + requires std::is_signed_v + struct StopSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StopSlice(const IntT stop = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(MyBaseClass::DEFAULT, stop, 1) + {} + + virtual ~StopSlice() noexcept = default; //!< Default destructor. + }; + + + /** \brief Class of slices with default start and stop values. */ + template + requires std::is_signed_v + struct StepSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StepSlice(const IntT step = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(MyBaseClass::DEFAULT, MyBaseClass::DEFAULT, step) + {} + + virtual ~StepSlice() noexcept = default; //!< Default destructor. + }; + + + /** \brief Class of slices with default step values. */ + template + requires std::is_signed_v + struct StartStopSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StartStopSlice(const IntT start = MyBaseClass::DEFAULT, const IntT stop = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(start, stop, 1) + {} + + virtual ~StartStopSlice() noexcept = default; //!< Default destructor. + }; + + + /** \brief Class of slices with default stop values. */ + template + requires std::is_signed_v + struct StartStepSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StartStepSlice(const IntT start = MyBaseClass::DEFAULT, const IntT step = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(start, MyBaseClass::DEFAULT, step) + {} + + virtual ~StartStepSlice() noexcept = default; //!< Default destructor. + + }; + + + /** \brief Class of slices with default start values. */ + template + requires std::is_signed_v + struct StopStepSlice : public Slice + { + using MyBaseClass = Slice; + + //--- Constructors / Destructor ------------------- + inline StopStepSlice(const IntT stop = MyBaseClass::DEFAULT, const IntT step = MyBaseClass::DEFAULT) noexcept //!< Valued constructor + : MyBaseClass(MyBaseClass::DEFAULT, stop, step) + {} + + virtual ~StopStepSlice() noexcept = default; //!< Default destructor. + }; //===== templated chars classes =========================== //--- is_alpha() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_alpha(const CharT ch) noexcept - { return false; } + { + return false; + } /** \brief Returns true if character ch is alphabetic, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_alpha(const char ch) noexcept - { return std::isalpha(static_cast(ch)); } + { + return static_cast(std::isalpha(static_cast(ch))); + } /** \brief Returns true if character ch is alphabetic, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_alpha(const wchar_t ch) noexcept - { return std::iswalpha(ch); } + { + return static_cast(std::iswalpha(ch)); + } //--- is_ascii() ------------------------------------------ /** \brief Returns true if character has code point in the range U+0000-U+007F. */ template + [[nodiscard]] inline const bool is_ascii(const CharT ch) noexcept - { return CharT(0x00) <= ch && ch <= CharT(0x7f); } + { + return CharT(0x00) <= ch && ch <= CharT(0x7f); + } //--- is_decimal() ---------------------------------------- /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_decimal(const CharT ch) noexcept - { return false; } + { + return false; + } /** \brief Returns true if character is a decimal digit, or false otherwise. */ template<> + [[nodiscard]] inline const bool is_decimal(const char ch) noexcept - { return std::isdigit(static_cast(ch)); } + { + return static_cast(std::isdigit(static_cast(ch))); + } /** \brief Returns true if character is a decimal digit, or false otherwise. */ template<> + [[nodiscard]] inline const bool is_decimal(const wchar_t ch) noexcept - { return std::iswdigit(ch); } + { + return (const bool)std::iswdigit(ch); + } + + + //--- is_digit() ------------------------------------------ + /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ + template + [[nodiscard]] + inline const bool is_digit(const CharT ch) noexcept + { + return pcs::is_decimal(ch); + } + + /** \brief Returns true if character is a decimal digit, or false otherwise. */ + template<> + [[nodiscard]] + inline const bool is_digit(const char ch) noexcept + { + return pcs::is_decimal(ch); + } + + /** \brief Returns true if character is a decimal digit, or false otherwise. */ + template<> + [[nodiscard]] + inline const bool is_digit(const wchar_t ch) noexcept + { + return pcs::is_decimal(ch); + } //--- is_id_continue() ------------------------------------ /** \brief Returns true if character is a continuing char for identifiers, or false otherwise. */ template + [[nodiscard]] inline const bool is_id_continue(const CharT ch) noexcept - { return pcs::is_id_start(ch) || pcs::is_decimal(ch); } + { + return pcs::is_id_start(ch) || pcs::is_decimal(ch); + } //--- is_id_start() --------------------------------------- /** \brief Returns true if character is a starting char for identifiers, or false otherwise. */ template + [[nodiscard]] inline const bool is_id_start(const CharT ch) noexcept - { return pcs::is_alpha(ch) || ch == CharT('_'); } + { + return pcs::is_alpha(ch) || ch == CharT('_'); + } //--- is_lower() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_lower(const CharT ch) noexcept { return false; @@ -1775,6 +2275,7 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns true if character ch is lowercase, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_lower(const char ch) noexcept { return std::islower(static_cast(ch)); @@ -1782,70 +2283,121 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns true if character ch is lowercase, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_lower(const wchar_t ch) noexcept { return std::iswlower(ch); } + //--- is_numeric() ---------------------------------------- + /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ + template + [[nodiscard]] + inline const bool is_numeric(const CharT ch) noexcept + { + return pcs::is_decimal(ch); + } + + /** \brief Returns true if character is a decimal digit, or false otherwise. */ + template<> + [[nodiscard]] + inline const bool is_numeric(const char ch) noexcept + { + return pcs::is_decimal(ch); + } + + /** \brief Returns true if character is a decimal digit, or false otherwise. */ + template<> + [[nodiscard]] + inline const bool is_numeric(const wchar_t ch) noexcept + { + return pcs::is_decimal(ch); + } + + //--- is_printable() -------------------------------------- /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_printable(const CharT ch) noexcept - { return false; } - + { + return false; + } + /** \brief Returns true if character ch is printable, or false otherwise. */ template<> + [[nodiscard]] inline const bool is_printable(const char ch) noexcept { - return std::isprint(static_cast(ch)); + return static_cast(std::isprint(static_cast(ch))); } /** \brief Returns true if character ch is punctuation, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_printable(const wchar_t ch) noexcept { - return std::iswprint(ch); + return static_cast(std::iswprint(ch)); } //--- is_punctuation() ------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_punctuation(const CharT ch) noexcept - { return false; } + { + return false; + } /** \brief Returns true if character ch is punctuation, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_punctuation(const char ch) noexcept - { return std::ispunct(static_cast(ch)); } + { + return static_cast(std::ispunct(static_cast(ch))); + } /** \brief Returns true if character ch is punctuation, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_punctuation(const wchar_t ch) noexcept - { return std::iswpunct(ch); } + { + return static_cast(std::iswpunct(ch)); + } //--- is_space() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_space(const CharT ch) noexcept - { return false; } + { + return false; + } /** \brief Returns true if character ch is alphabetic, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_space(const char ch) noexcept - { return std::isspace(static_cast(ch)); } + { + return static_cast(std::isspace(static_cast(ch))); + } /** \brief Returns true if character ch is alphabetic, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_space(const wchar_t ch) noexcept - { return std::iswspace(ch); } + { + return static_cast(std::iswspace(ch)); + } //--- is_upper() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const bool is_upper(const CharT ch) noexcept { return false; @@ -1853,22 +2405,25 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns true if character ch is uppercase, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_upper(const char ch) noexcept { - return std::isupper(static_cast(ch)); + return static_cast(std::isupper(static_cast(ch))); } /** \brief Returns true if character ch is uppercase, or false otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const bool is_upper(const wchar_t ch) noexcept { - return std::iswupper(ch); + return static_cast(std::iswupper(ch)); } //--- swap_case() ----------------------------------------- /** \brief Returns the swapped case form of character ch if it exists, or ch itself otherwise. */ template + [[nodiscard]] inline const CharT swap_case(const CharT ch) noexcept { if (pcs::is_lower(ch)) @@ -1883,6 +2438,7 @@ namespace pcs // i.e. "pythonic c++ strings" //--- to_lower() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const CharT to_lower(const CharT ch) noexcept { return ch; @@ -1890,6 +2446,7 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns the lowercase form of character ch if it exists, or ch itself otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const char to_lower(const char ch) noexcept { return std::tolower(static_cast(ch)); @@ -1897,6 +2454,7 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns the lowercase form of character ch if it exists, or ch itself otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const wchar_t to_lower(const wchar_t ch) noexcept { return std::towlower(ch); @@ -1906,6 +2464,7 @@ namespace pcs // i.e. "pythonic c++ strings" //--- to_upper() ------------------------------------------ /** \brief SHOULD NEVER BE USED. Use next specializations instead. */ template + [[nodiscard]] inline const CharT to_upper(const CharT ch) noexcept { return ch; @@ -1913,6 +2472,7 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns the uppercase form of character ch if it exists, or ch itself otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const char to_upper(const char ch) noexcept { return std::toupper(static_cast(ch)); @@ -1920,10 +2480,14 @@ namespace pcs // i.e. "pythonic c++ strings" /** \brief Returns the uppercase form of character ch if it exists, or ch itself otherwise. Conforms to the current locale settings. */ template<> + [[nodiscard]] inline const wchar_t to_upper(const wchar_t ch) noexcept { return std::towupper(ch); } +#if defined(_MSC_VER) +# pragma warning(pop) // to avoid boring warnings with litteral operators definitions +#endif -} // end of namespace pcs // (pythonic c++ strings) \ No newline at end of file +} // end of namespace pcs // (pythonic c++ strings)