https://git.reactos.org/?p=reactos.git;a=commitdiff;h=58092fb4da352491e2f87…
commit 58092fb4da352491e2f87fecc52ff5a652ab6669
Author: Mark Jansen <mark.jansen(a)reactos.org>
AuthorDate: Sun Sep 6 21:30:23 2020 +0200
Commit: Mark Jansen <mark.jansen(a)reactos.org>
CommitDate: Mon Sep 7 22:13:43 2020 +0200
[ATL][ATL_APITEST] Add CString::Tokenize + testcase
---
modules/rostests/apitests/atl/CString.cpp | 3 ++
modules/rostests/apitests/atl/CString.inl | 87 +++++++++++++++++++++++++++++++
sdk/lib/atl/cstringt.h | 64 +++++++++++++++++++++++
3 files changed, 154 insertions(+)
diff --git a/modules/rostests/apitests/atl/CString.cpp
b/modules/rostests/apitests/atl/CString.cpp
index 89782499521..fdb9d55020f 100644
--- a/modules/rostests/apitests/atl/CString.cpp
+++ b/modules/rostests/apitests/atl/CString.cpp
@@ -190,4 +190,7 @@ START_TEST(CString)
test_bstrW();
test_bstrA();
+
+ test_tokenizeW();
+ test_tokenizeA();
}
diff --git a/modules/rostests/apitests/atl/CString.inl
b/modules/rostests/apitests/atl/CString.inl
index 193d8d40ba7..c0d661cfb8e 100644
--- a/modules/rostests/apitests/atl/CString.inl
+++ b/modules/rostests/apitests/atl/CString.inl
@@ -439,3 +439,90 @@ TEST_NAMEX(bstr)
::SysFreeString(bstr);
}
}
+
+TEST_NAMEX(tokenize)
+{
+ CStringX str(_X("%#First Second#Third"));
+ const XCHAR* Tokens = _X("% #");
+ CStringX res;
+
+ int nCurPos = 0;
+
+ // All 'current' tokens are skipped
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X("First"), "Expected str to be 'First', was:
%s\n", dbgstrx(res));
+ ok_dec(nCurPos, 8);
+
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X("Second"), "Expected str to be 'Second', was:
%s\n", dbgstrx(res));
+ ok_dec(nCurPos, 15);
+
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X("Third"), "Expected str to be 'Third', was:
%s\n", dbgstrx(res));
+ ok_dec(nCurPos, 21);
+
+ // The final 'token' is empty, and nCurPos is set to -1
+ // (Calling with nCurPos=-1 will assert)
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+
+ str =_X("StartWithToken#%#");
+ nCurPos = 0;
+
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X("StartWithToken"), "Expected str to be
'StartWithToken', was: %s\n", dbgstrx(res));
+ ok_dec(nCurPos, 15);
+
+ // Ending with tokens acts as if there were no tokens at the end
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+
+
+ str = _X("");
+ nCurPos = 0;
+
+ // Calling with an empty string returns 'no tokens'
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+
+ str = _X("");
+ nCurPos = 20;
+
+ // Calling with a current position outside the strings acts the same as 'no
tokens left'
+ res = str.Tokenize(Tokens, nCurPos);
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+
+
+ str = _X("test");
+ nCurPos = 2;
+
+ // Calling with a NULL pszTokens returns a substring starting at 'nCurPos',
but not updating nCurPos!
+ res = str.Tokenize(NULL, nCurPos);
+ ok(res == _X("st"), "Expected str to be 'st', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, 2);
+
+
+ // Calling with an empty pszTokens behaves exactly the same
+ res = str.Tokenize(_X(""), nCurPos);
+ ok(res == _X("st"), "Expected str to be 'st', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, 2);
+
+ nCurPos = 8;
+
+ // Calling with a NULL pszTokens and an nCurPos out of bounds returns 'no tokens
left'
+ res = str.Tokenize(NULL, nCurPos);
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+
+ nCurPos = 8;
+
+ // Calling with an empty pszTokens behaves exactly the same
+ res = str.Tokenize(_X(""), nCurPos);
+ ok(res == _X(""), "Expected str to be 'st', was: %s\n",
dbgstrx(res));
+ ok(res == _X(""), "Expected str to be '', was: %s\n",
dbgstrx(res));
+ ok_dec(nCurPos, -1);
+}
diff --git a/sdk/lib/atl/cstringt.h b/sdk/lib/atl/cstringt.h
index ad89dc6270a..0afe3f4763f 100644
--- a/sdk/lib/atl/cstringt.h
+++ b/sdk/lib/atl/cstringt.h
@@ -140,6 +140,20 @@ public:
return ::_wcsicmp(psz1, psz2);
}
+ static int __cdecl StringSpanIncluding(
+ _In_z_ LPCWSTR pszBlock,
+ _In_z_ LPCWSTR pszSet)
+ {
+ return (int)::wcsspn(pszBlock, pszSet);
+ }
+
+ static int __cdecl StringSpanExcluding(
+ _In_z_ LPCWSTR pszBlock,
+ _In_z_ LPCWSTR pszSet)
+ {
+ return (int)::wcscspn(pszBlock, pszSet);
+ }
+
static int __cdecl FormatV(
_In_opt_z_ LPWSTR pszDest,
_In_z_ LPCWSTR pszFormat,
@@ -289,6 +303,20 @@ public:
return ::_stricmp(psz1, psz2);
}
+ static int __cdecl StringSpanIncluding(
+ _In_z_ LPCSTR pszBlock,
+ _In_z_ LPCSTR pszSet)
+ {
+ return (int)::strspn(pszBlock, pszSet);
+ }
+
+ static int __cdecl StringSpanExcluding(
+ _In_z_ LPCSTR pszBlock,
+ _In_z_ LPCSTR pszSet)
+ {
+ return (int)::strcspn(pszBlock, pszSet);
+ }
+
static int __cdecl FormatV(
_In_opt_z_ LPSTR pszDest,
_In_z_ LPCSTR pszFormat,
@@ -804,6 +832,42 @@ public:
}
+ CStringT Tokenize(_In_z_ PCXSTR pszTokens, _Inout_ int& iStart) const
+ {
+ ATLASSERT(iStart >= 0);
+
+ if (iStart < 0)
+ AtlThrow(E_INVALIDARG);
+
+ if (!pszTokens || !pszTokens[0])
+ {
+ if (iStart < CThisSimpleString::GetLength())
+ {
+ return Mid(iStart);
+ }
+ iStart = -1;
+ return CStringT();
+ }
+
+ if (iStart < CThisSimpleString::GetLength())
+ {
+ int iRangeOffset =
StringTraits::StringSpanIncluding(CThisSimpleString::GetString() + iStart, pszTokens);
+
+ if (iRangeOffset + iStart < CThisSimpleString::GetLength())
+ {
+ int iNewStart = iStart + iRangeOffset;
+ int nCount =
StringTraits::StringSpanExcluding(CThisSimpleString::GetString() + iNewStart, pszTokens);
+
+ iStart = iNewStart + nCount + 1;
+
+ return Mid(iNewStart, nCount);
+ }
+ }
+
+ iStart = -1;
+ return CStringT();
+ }
+
static PCXSTR DefaultTrimChars()
{
static XCHAR str[] = { ' ', '\t', '\r', '\n', 0
};