Skip to content

Commit dd51b7e

Browse files
authored
refactor(string): Implement generic startsWith, endsWith functions for C strings (#1898)
1 parent 8d675a8 commit dd51b7e

File tree

4 files changed

+170
-36
lines changed

4 files changed

+170
-36
lines changed

Core/GameEngine/Include/Common/UnicodeString.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,30 @@ class UnicodeString
306306
*/
307307
int compareNoCase(const WideChar* s) const;
308308

309+
/**
310+
return true iff self starts with the given string.
311+
*/
312+
Bool startsWith(const WideChar* p) const;
313+
inline Bool startsWith(const UnicodeString& stringSrc) const { return startsWith(stringSrc.str()); }
314+
315+
/**
316+
return true iff self starts with the given string. (case insensitive)
317+
*/
318+
Bool startsWithNoCase(const WideChar* p) const;
319+
inline Bool startsWithNoCase(const UnicodeString& stringSrc) const { return startsWithNoCase(stringSrc.str()); }
320+
321+
/**
322+
return true iff self ends with the given string.
323+
*/
324+
Bool endsWith(const WideChar* p) const;
325+
Bool endsWith(const UnicodeString& stringSrc) const { return endsWith(stringSrc.str()); }
326+
327+
/**
328+
return true iff self ends with the given string. (case insensitive)
329+
*/
330+
Bool endsWithNoCase(const WideChar* p) const;
331+
Bool endsWithNoCase(const UnicodeString& stringSrc) const { return endsWithNoCase(stringSrc.str()); }
332+
309333
/**
310334
conceptually similar to strtok():
311335

Core/GameEngine/Source/Common/System/AsciiString.cpp

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -477,57 +477,25 @@ void AsciiString::format_va(const char* format, va_list args)
477477
// -----------------------------------------------------
478478
Bool AsciiString::startsWith(const char* p) const
479479
{
480-
if (*p == 0)
481-
return true; // everything starts with the empty string
482-
483-
int lenThis = getLength();
484-
int lenThat = strlen(p);
485-
if (lenThis < lenThat)
486-
return false; // that must be smaller than this
487-
488-
return strncmp(peek(), p, lenThat) == 0;
480+
return ::startsWith(peek(), p);
489481
}
490482

491483
// -----------------------------------------------------
492484
Bool AsciiString::startsWithNoCase(const char* p) const
493485
{
494-
if (*p == 0)
495-
return true; // everything starts with the empty string
496-
497-
int lenThis = getLength();
498-
int lenThat = strlen(p);
499-
if (lenThis < lenThat)
500-
return false; // that must be smaller than this
501-
502-
return strnicmp(peek(), p, lenThat) == 0;
486+
return ::startsWithNoCase(peek(), p);
503487
}
504488

505489
// -----------------------------------------------------
506490
Bool AsciiString::endsWith(const char* p) const
507491
{
508-
if (*p == 0)
509-
return true; // everything ends with the empty string
510-
511-
int lenThis = getLength();
512-
int lenThat = strlen(p);
513-
if (lenThis < lenThat)
514-
return false; // that must be smaller than this
515-
516-
return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
492+
return ::endsWith(peek(), p);
517493
}
518494

519495
// -----------------------------------------------------
520496
Bool AsciiString::endsWithNoCase(const char* p) const
521497
{
522-
if (*p == 0)
523-
return true; // everything ends with the empty string
524-
525-
int lenThis = getLength();
526-
int lenThat = strlen(p);
527-
if (lenThis < lenThat)
528-
return false; // that must be smaller than this
529-
530-
return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
498+
return ::endsWithNoCase(peek(), p);
531499
}
532500

533501
//-----------------------------------------------------------------------------

Core/GameEngine/Source/Common/System/UnicodeString.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,30 @@ void UnicodeString::format_va(const WideChar* format, va_list args)
406406
}
407407
}
408408

409+
// -----------------------------------------------------
410+
Bool UnicodeString::startsWith(const WideChar* p) const
411+
{
412+
return ::startsWith(peek(), p);
413+
}
414+
415+
// -----------------------------------------------------
416+
Bool UnicodeString::startsWithNoCase(const WideChar* p) const
417+
{
418+
return ::startsWithNoCase(peek(), p);
419+
}
420+
421+
// -----------------------------------------------------
422+
Bool UnicodeString::endsWith(const WideChar* p) const
423+
{
424+
return ::endsWith(peek(), p);
425+
}
426+
427+
// -----------------------------------------------------
428+
Bool UnicodeString::endsWithNoCase(const WideChar* p) const
429+
{
430+
return ::endsWithNoCase(peek(), p);
431+
}
432+
409433
//-----------------------------------------------------------------------------
410434
Bool UnicodeString::nextToken(UnicodeString* tok, UnicodeString delimiters)
411435
{

Core/Libraries/Source/WWVegas/WWLib/stringex.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "bittype.h"
2222
#include <string.h>
23+
#include <ctype.h>
2324

2425

2526
// Declaration
@@ -55,6 +56,14 @@ template<size_t Size> size_t strlmove_t(char (&dst)[Size], const char *src);
5556
template<size_t Size> size_t strlmcat_t(char (&dst)[Size], const char *src);
5657
#endif
5758

59+
template<typename T> int strncmp_t(const T *str1, const T *str2, size_t maxcount);
60+
template<typename T> int strnicmp_t(const T* str1, const T* str2, size_t maxcount);
61+
62+
template<typename T> bool startsWith(const T *str, const T *prefix);
63+
template<typename T> bool startsWithNoCase(const T *str, const T *prefix);
64+
template<typename T> bool endsWithNoCase(const T *str, const T *suffix);
65+
template<typename T> bool endsWithNoCase(const T *str, const T *suffix);
66+
5867

5968
// Implementation
6069

@@ -182,3 +191,112 @@ template<size_t Size> size_t strlcat_t(char (&dst)[Size], const char *src) { ret
182191
template<size_t Size> size_t strlmove_t(char (&dst)[Size], const char *src) { return strlmove_t(dst, src, Size); }
183192
template<size_t Size> size_t strlmcat_t(char (&dst)[Size], const char *src) { return strlmcat_t(dst, src, Size); }
184193
#endif
194+
195+
// Templated strncmp.
196+
// Compares up to maxcount chars or a null byte is encountered, whichever comes first.
197+
// Returns < 0 if str1 is less than str2, 0 is str1 and str2 are equal and > 0 if str2 is greater than str1.
198+
template<typename T> int strncmp_t(const T *str1, const T *str2, const size_t maxcount)
199+
{
200+
for (size_t i = 0; i < maxcount; ++i)
201+
{
202+
const T c1 = str1[i];
203+
const T c2 = str2[i];
204+
const int diff = (int)c1 - (int)c2;
205+
if (diff != 0)
206+
{
207+
return diff;
208+
}
209+
if (c1 == T(0)) // both c1 and c2 are null terminators
210+
{
211+
return 0;
212+
}
213+
}
214+
return 0;
215+
}
216+
217+
// Lower case conversion helpers
218+
inline char tolower_t(char c)
219+
{
220+
// cast to unsigned char for correct behavior of tolower()
221+
return (char)tolower((unsigned char)c);
222+
}
223+
224+
inline wchar_t tolower_t(wchar_t c)
225+
{
226+
return (wchar_t)towlower(c);
227+
}
228+
229+
// Templated strnicmp.
230+
// Case insensitively compares up to maxcount chars or a null byte is encountered, whichever comes first.
231+
// Returns < 0 if str1 is less than str2, 0 is str1 and str2 are equal and > 0 if str2 is greater than str1.
232+
template<typename T> int strnicmp_t(const T *str1, const T *str2, const size_t maxcount)
233+
{
234+
for (size_t i = 0; i < maxcount; ++i)
235+
{
236+
const T c1 = tolower_t(str1[i]);
237+
const T c2 = tolower_t(str2[i]);
238+
const int diff = (int)c1 - (int)c2;
239+
if (diff != 0)
240+
{
241+
return diff;
242+
}
243+
if (c1 == T(0)) // both c1 and c2 are null terminators
244+
{
245+
return 0;
246+
}
247+
}
248+
return 0;
249+
}
250+
251+
template<typename T> inline bool startsWith(const T *str, const T *prefix)
252+
{
253+
if (*prefix == T(0))
254+
return true; // everything starts with the empty string
255+
256+
const size_t strlen = strlen_t(str);
257+
const size_t prefixlen = strlen_t(prefix);
258+
if (strlen < prefixlen)
259+
return false; // prefix must be as long or shorter than str
260+
261+
return strncmp_t(str, prefix, prefixlen) == 0;
262+
}
263+
264+
template<typename T> inline bool startsWithNoCase(const T *str, const T *prefix)
265+
{
266+
if (*prefix == T(0))
267+
return true; // everything starts with the empty string
268+
269+
const size_t strlen = strlen_t(str);
270+
const size_t prefixlen = strlen_t(prefix);
271+
if (strlen < prefixlen)
272+
return false; // prefix must be as long or shorter than str
273+
274+
return strnicmp_t(str, prefix, prefixlen) == 0;
275+
}
276+
277+
template<typename T> inline bool endsWith(const T *str, const T *suffix)
278+
{
279+
if (*suffix == T(0))
280+
return true; // everything ends with the empty string
281+
282+
const size_t strlen = strlen_t(str);
283+
const size_t suffixlen = strlen_t(suffix);
284+
if (strlen < suffixlen)
285+
return false; // suffix must be as long or shorter than str
286+
287+
return strncmp_t(str + strlen - suffixlen, suffix, suffixlen) == 0;
288+
}
289+
290+
template<typename T> inline bool endsWithNoCase(const T *str, const T *suffix)
291+
{
292+
if (*suffix == T(0))
293+
return true; // everything ends with the empty string
294+
295+
const size_t strlen = strlen_t(str);
296+
const size_t suffixlen = strlen_t(suffix);
297+
if (strlen < suffixlen)
298+
return false; // suffix must be as long or shorter than str
299+
300+
return strnicmp_t(str + strlen - suffixlen, suffix, suffixlen) == 0;
301+
}
302+

0 commit comments

Comments
 (0)