Skip to content

Commit 6cb9661

Browse files
committed
perf: stricmp optimizations
* backported FastASCIIToLower from CSGO and applied it appropriately to strtools * backported _V_stricmp conditional structure from CSGO but adjusted it to use a fast ASCII lookup table and also use tolower on non-ASCII characters for general safety * select a few choice cases to migrate from stricmp to V_stricmp CSGO applies it to everything but these are few hotspots
1 parent ead772a commit 6cb9661

File tree

5 files changed

+65
-28
lines changed

5 files changed

+65
-28
lines changed

src/tier1/strtools.cpp

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,20 @@
8080
#endif
8181
#include "tier0/memdbgon.h"
8282

83-
static int FastToLower( char c )
84-
{
85-
int i = (unsigned char) c;
86-
if ( i < 0x80 )
87-
{
88-
// Brutally fast branchless ASCII tolower():
89-
i += (((('A'-1) - i) & (i - ('Z'+1))) >> 26) & 0x20;
90-
}
91-
else
92-
{
93-
i += isupper( i ) ? 0x20 : 0;
94-
}
95-
return i;
96-
}
83+
#define USE_FAST_CASE_CONVERSION 1
84+
#if USE_FAST_CASE_CONVERSION
85+
/// Faster conversion of an ascii char to upper case. This function does not obey locale or any language
86+
/// setting. It should not be used to convert characters for printing, but it is a better choice
87+
/// for internal strings such as used for hash table keys, etc. It's meant to be inlined and used
88+
/// in places like the various dictionary classes. Not obeying locale also protects you from things
89+
/// like your hash values being different depending on the locale setting.
90+
#define FastASCIIToUpper( c ) ( ( ( (c) >= 'a' ) && ( (c) <= 'z' ) ) ? ( (c) - 32 ) : (c) )
91+
/// similar to FastASCIIToLower
92+
#define FastASCIIToLower( c ) ( ( ( (c) >= 'A' ) && ( (c) <= 'Z' ) ) ? ( (c) + 32 ) : (c) )
93+
#else
94+
#define FastASCIIToLower tolower
95+
#define FastASCIIToUpper toupper
96+
#endif
9797

9898
void _V_memset (const char* file, int line, void *dest, int fill, int count)
9999
{
@@ -260,6 +260,17 @@ char *V_strnlwr(char *s, size_t count)
260260
return pRet;
261261
}
262262

263+
static constexpr uint8 lowerAsciiLookup[128] = {
264+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
265+
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
266+
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
267+
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
268+
0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
269+
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
270+
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
271+
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
272+
};
273+
263274
int V_stricmp( const char *str1, const char *str2 )
264275
{
265276
// It is not uncommon to compare a string to itself. See
@@ -272,6 +283,7 @@ int V_stricmp( const char *str1, const char *str2 )
272283
}
273284
const unsigned char *s1 = (const unsigned char*)str1;
274285
const unsigned char *s2 = (const unsigned char*)str2;
286+
#if 0
275287
for ( ; *s1; ++s1, ++s2 )
276288
{
277289
if ( *s1 != *s2 )
@@ -291,6 +303,31 @@ int V_stricmp( const char *str1, const char *str2 )
291303
}
292304
}
293305
return *s2 ? -1 : 0;
306+
#else
307+
while (true)
308+
{
309+
unsigned char c1 = *s1++;
310+
unsigned char c2 = *s2++;
311+
if (c1 == c2)
312+
{
313+
if ( !c1 ) return 0;
314+
}
315+
else if ((((uint32)c1 | (uint32)c2) & 0xffffff80) == 0)
316+
{
317+
if (int32 res = lowerAsciiLookup[c1] - lowerAsciiLookup[c2])
318+
{
319+
return res;
320+
}
321+
}
322+
else
323+
{
324+
if (int32 res = tolower(c1) - tolower(c2))
325+
{
326+
return res;
327+
}
328+
}
329+
}
330+
#endif
294331
}
295332

296333
int V_strnicmp( const char *str1, const char *str2, int n )
@@ -348,7 +385,7 @@ const char *StringAfterPrefix( const char *str, const char *prefix )
348385
if ( !*prefix )
349386
return str;
350387
}
351-
while ( FastToLower( *str++ ) == FastToLower( *prefix++ ) );
388+
while ( tolower( *str++ ) == tolower( *prefix++ ) );
352389
return NULL;
353390
}
354391

@@ -638,7 +675,7 @@ char const* V_stristr( char const* pStr, char const* pSearch )
638675
while (*pLetter != 0)
639676
{
640677
// Skip over non-matches
641-
if (FastToLower((unsigned char)*pLetter) == FastToLower((unsigned char)*pSearch))
678+
if (FastASCIIToLower((unsigned char)*pLetter) == FastASCIIToLower((unsigned char)*pSearch))
642679
{
643680
// Check for match
644681
char const* pMatch = pLetter + 1;
@@ -649,7 +686,7 @@ char const* V_stristr( char const* pStr, char const* pSearch )
649686
if (*pMatch == 0)
650687
return 0;
651688

652-
if (FastToLower((unsigned char)*pMatch) != FastToLower((unsigned char)*pTest))
689+
if (FastASCIIToLower((unsigned char)*pMatch) != FastASCIIToLower((unsigned char)*pTest))
653690
break;
654691

655692
++pMatch;
@@ -696,7 +733,7 @@ char const* V_strnistr( char const* pStr, char const* pSearch, int n )
696733
return 0;
697734

698735
// Skip over non-matches
699-
if (FastToLower(*pLetter) == FastToLower(*pSearch))
736+
if (FastASCIIToLower(*pLetter) == FastASCIIToLower(*pSearch))
700737
{
701738
int n1 = n - 1;
702739

@@ -712,7 +749,7 @@ char const* V_strnistr( char const* pStr, char const* pSearch, int n )
712749
if (*pMatch == 0)
713750
return 0;
714751

715-
if (FastToLower(*pMatch) != FastToLower(*pTest))
752+
if (FastASCIIToLower(*pMatch) != FastASCIIToLower(*pTest))
716753
break;
717754

718755
++pMatch;
@@ -1421,7 +1458,7 @@ int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInByt
14211458
size_t nMaxUTF8 = cubDestSizeInBytes;
14221459
char *pIn = (char *)pUCS2;
14231460
char *pOut = (char *)pUnicode;
1424-
if ( conv_t > 0 )
1461+
if ( conv_t != nullptr )
14251462
{
14261463
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
14271464
iconv_close( conv_t );
@@ -1461,7 +1498,7 @@ int _V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, i
14611498
size_t nMaxUCS2 = cubDestSizeInBytes;
14621499
char *pIn = (char*)pUnicode;
14631500
char *pOut = pUCS2;
1464-
if ( conv_t > 0 )
1501+
if ( conv_t != nullptr )
14651502
{
14661503
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 );
14671504
iconv_close( conv_t );
@@ -1509,7 +1546,7 @@ int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes )
15091546
size_t nMaxUTF8 = cubDestSizeInBytes - 1;
15101547
char *pIn = (char *)pUCS2;
15111548
char *pOut = (char *)pUTF8;
1512-
if ( conv_t > 0 )
1549+
if ( conv_t != nullptr )
15131550
{
15141551
const size_t nBytesToWrite = nMaxUTF8;
15151552
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
@@ -1554,7 +1591,7 @@ int _V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDes
15541591
size_t nMaxUTF8 = cubDestSizeInBytes;
15551592
char *pIn = (char *)pUTF8;
15561593
char *pOut = (char *)pUCS2;
1557-
if ( conv_t > 0 )
1594+
if ( conv_t != nullptr )
15581595
{
15591596
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
15601597
iconv_close( conv_t );
@@ -2275,7 +2312,7 @@ bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pR
22752312
// Strip out common parts of the path
22762313
const char *pLastCommonPath = NULL;
22772314
const char *pLastCommonDir = NULL;
2278-
while ( *pPath && ( FastToLower( *pPath ) == FastToLower( *pDir ) ||
2315+
while ( *pPath && ( tolower( *pPath ) == tolower( *pDir ) ||
22792316
( PATHSEPARATOR( *pPath ) && ( PATHSEPARATOR( *pDir ) || (*pDir == 0) ) ) ) )
22802317
{
22812318
if ( PATHSEPARATOR( *pPath ) )

src/vgui2/src/LocalizedStringTable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ bool CLocalizedStringTable::SymLess(localizedstring_t const &i1, localizedstring
731731
const char *str2 = (i2.nameIndex == INVALID_LOCALIZE_STRING_INDEX) ? i2.pszValueString :
732732
&g_StringTable.m_Names[i2.nameIndex];
733733

734-
return stricmp(str1, str2) < 0;
734+
return V_stricmp(str1, str2) < 0;
735735
}
736736

737737

src/vgui2/vgui_controls/Panel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6343,7 +6343,7 @@ PanelAnimationMapEntry *Panel::FindPanelAnimationEntry( char const *scriptname,
63436343
{
63446344
PanelAnimationMapEntry *e = &map->entries[ i ];
63456345

6346-
if ( !stricmp( e->name(), scriptname ) )
6346+
if ( !V_stricmp( e->name(), scriptname ) )
63476347
{
63486348
return e;
63496349
}

src/vguimatsurface/TextureDictionary.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ int CTextureDictionary::FindTextureIdForTextureFile( char const *pFileName )
983983
if ( !mat )
984984
continue;
985985

986-
if ( !stricmp( mat->GetName(), pFileName ) )
986+
if ( ! V_stricmp( mat->GetName(), pFileName ) )
987987
return i;
988988
}
989989

src/vstdlib/KeyValuesSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate
235235
hash_item_t *item = &m_HashTable[hash];
236236
while (1)
237237
{
238-
if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex ))
238+
if (!V_stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex ))
239239
{
240240
return (HKeySymbol)item->stringIndex;
241241
}

0 commit comments

Comments
 (0)