Skip to content

Commit a7b34b7

Browse files
committed
Added implementation of a 'natural' string compare function originally contributed to tacentview by github user ClangPan.
1 parent 7ac84ec commit a7b34b7

File tree

3 files changed

+88
-9
lines changed

3 files changed

+88
-9
lines changed

Modules/Foundation/Inc/Foundation/tStandard.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,24 @@ inline const void* tMemsrch(const void* haystack, int haystackNumBytes, const vo
5353
// straight UTF conversions (not null-terminated). String termination is not part of UTF, but it's common to support it.
5454
// Null-terminated versions of the functions have an 's' appended. The 'c' versions are for dealing with individual
5555
// codepoints. Note these functions return exactly -1 if a < b, 0 if equal, and 1 if a > b. This is in contrast to the
56-
// standard strcmp functions that only guarantee returning < 0, 0, or > 0. That is, implementations are free to return
57-
// either the ASCII difference of the strings or normalize the returns to -1, 0, 1.
56+
// standard strcmp functions that only guarantee returning < 0, 0, or > 0. That is, standard implementations are free to
57+
// return either the ASCII difference of the strings or normalize the returns to -1, 0, 1, but it's not helpful as you
58+
// can't be sure of which choice an implementation may have made.
5859
const int tCharInvalid = 0xFF;
5960
inline int tStrcmp(const char* a, const char* b) { tAssert(a && b); int r = strcmp(a, b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
61+
inline int tStrcmp(const char8_t* a, const char8_t* b) { return tStrcmp((const char*)a, (const char*)b); }
6062
inline int tStrncmp(const char* a, const char* b, int n) { tAssert(a && b && n >= 0); int r = strncmp(a, b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
61-
inline int tStrcmp(const char8_t* a, const char8_t* b) { tAssert(a && b); int r = strcmp((const char*)a, (const char*)b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
62-
inline int tStrncmp(const char8_t* a, const char8_t* b, int n) { tAssert(a && b && n >= 0); int r = strncmp((const char*)a, (const char*)b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
63+
inline int tStrncmp(const char8_t* a, const char8_t* b, int n) { return tStrncmp((const char*)a, (const char*)b, n); }
6364
#if defined(PLATFORM_WINDOWS)
6465
inline int tStricmp(const char* a, const char* b) { tAssert(a && b); int r = stricmp(a, b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
66+
inline int tStricmp(const char8_t* a, const char8_t* b) { return tStricmp((const char*)a, (const char*)b); }
6567
inline int tStrnicmp(const char* a, const char* b, int n) { tAssert(a && b && n >= 0); int r = strnicmp(a, b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
66-
inline int tStricmp(const char8_t* a, const char8_t* b) { tAssert(a && b); int r = stricmp((const char*)a, (const char*)b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
67-
inline int tStrnicmp(const char8_t* a, const char8_t* b, int n) { tAssert(a && b && n >= 0); int r = strnicmp((const char*)a, (const char*)b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
68+
inline int tStrnicmp(const char8_t* a, const char8_t* b, int n) { return tStrnicmp((const char*)a, (const char*)b, n); }
6869
#else
6970
inline int tStricmp(const char* a, const char* b) { tAssert(a && b); int r = strcasecmp(a, b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
71+
inline int tStricmp(const char8_t* a, const char8_t* b) { return tStricmp((const char*)a, (const char*)b); }
7072
inline int tStrnicmp(const char* a, const char* b, int n) { tAssert(a && b && n >= 0); int r = strncasecmp(a, b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
71-
inline int tStricmp(const char8_t* a, const char8_t* b) { tAssert(a && b); int r = strcasecmp((const char*)a, (const char*)b); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
72-
inline int tStrnicmp(const char8_t* a, const char8_t* b, int n) { tAssert(a && b && n >= 0); int r = strncasecmp((const char*)a, (const char*)b, n); return (r < 0) ? -1 : ((r > 0) ? 1 : 0); }
73+
inline int tStrnicmp(const char8_t* a, const char8_t* b, int n) { return tStrnicmp((const char*)a, (const char*)b, n); }
7374
#endif
7475

7576
// These are similar to the above but assume the strings represent filesystem paths and so choose between a case
@@ -80,6 +81,12 @@ int tPstrncmp(const char* a, const char* b, int n);
8081
int tPstrcmp(const char8_t* a, const char8_t* b);
8182
int tPstrncmp(const char8_t* a, const char8_t* b, int n);
8283

84+
// These do a 'natural' string compare by treating groups of base 10 digits as separate objects to be compared by
85+
// numeric value rather than alpha-numerically based on the encoding. This results in strings like "page10" coming
86+
// after "page2" because 10 > 2.
87+
int tNstrcmp(const char* a, const char* b);
88+
inline int tNstrcmp(const char8_t* a, const char8_t* b) { return tNstrcmp((const char*)a, (const char*)b); }
89+
8390
inline int tStrlen(const char* s) { tAssert(s); return int(strlen(s)); }
8491
inline constexpr int tStrlenCT(const char* s) { return *s ? 1 + tStrlenCT(s + 1) : 0; }
8592
inline char* tStrcpy(char* dst, const char* src) { tAssert(dst && src); return strcpy(dst, src); }

Modules/Foundation/Src/tStandard.cpp

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Tacent functions and types that are standard across all platforms. Includes global functions like itoa which are not
44
// available on some platforms, but are common enough that they should be.
55
//
6-
// Copyright (c) 2004-2006, 2015, 2023 Tristan Grimmer.
6+
// Copyright (c) 2004-2006, 2015, 2023, 2024 Tristan Grimmer.
77
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
88
// granted, provided that the above copyright notice and this permission notice appear in all copies.
99
//
@@ -62,6 +62,77 @@ void* tStd::tMemsrch(void* haystack, int haystackNumBytes, void* needle, int nee
6262
}
6363

6464

65+
int tStd::tNstrcmp(const char* a, const char* b)
66+
{
67+
// This implementation of tNstrcmp was written by GitHub user ClangPan.
68+
enum class Mode
69+
{
70+
String,
71+
Number
72+
};
73+
Mode mode = Mode::String;
74+
75+
while (*a && *b)
76+
{
77+
if (mode == Mode::String)
78+
{
79+
char aChar, bChar;
80+
while ((aChar = tolower(*a)) && (bChar = tolower(*b))) // We lowercase the chars for proper comparison
81+
{
82+
// Check if the chars are digits
83+
const bool aDigit = isdigit(aChar), bDigit = isdigit(bChar);
84+
85+
// If both chars are digits, we continue in NUMBER mode
86+
if (aDigit && bDigit)
87+
{
88+
mode = Mode::Number;
89+
break;
90+
}
91+
92+
// If only the left char is a digit, we have a result
93+
if (aDigit) return -1;
94+
95+
// If only the right char is a digit, we have a result
96+
if (bDigit) return +1;
97+
98+
// compute the difference of both characters
99+
const int diff = aChar - bChar;
100+
101+
// If they differ we have a result
102+
if (diff != 0) return diff;
103+
104+
// Otherwise process the next characters
105+
++a; ++b;
106+
}
107+
}
108+
else
109+
{
110+
char *end; // Represents the end of the number string
111+
112+
// Get the left number
113+
unsigned long aInt = strtoul((char*) a, &end, 10);
114+
a = end;
115+
116+
// Get the right number
117+
unsigned long bInt = strtoul((char*) b, &end, 10);
118+
b = end;
119+
120+
// if the difference is not equal to zero, we have a comparison result
121+
const long diff = aInt - bInt;
122+
if (diff != 0) return diff;
123+
124+
// otherwise we process the next substring in STRING mode
125+
mode = Mode::String;
126+
}
127+
}
128+
129+
if (*b) return -1;
130+
if (*a) return +1;
131+
132+
return 0;
133+
}
134+
135+
65136
bool tStd::tStrtob(const char* str)
66137
{
67138
tString lower(str);

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ Credits are found directly in the code where appropriate. Here is a list of some
168168
* Xiaolin Wu for the Wu colour quantizer.
169169
* Derrick Coetzee for the Scolorq spatial colour quantizer.
170170
* Khronos Group and Mark Callow for KTX-Software.
171+
* GitHub user ClangPan for the implementation of tNstrcmp.
171172
172173
173174
### Legal

0 commit comments

Comments
 (0)