make lumpnumcache case sensitive + optimize + replace quickncase hash with FNV1a

lumpnumcache can be case sensitive W_CheckNumForName should also look for uppercase stuff and W_LumpExists is case sensitive anyways
this avoids having to always account for case (toupper/tolower) during hash and string comparisons and even allows us to even directly compare our strings instead of using fastcmp (std::strings allow this!)
from some benchmarking externally this makes this faster quite a bit
This commit is contained in:
Alug 2026-02-10 09:21:58 -05:00 committed by NepDisk
parent 71175d82ca
commit adce4197e0
14 changed files with 61 additions and 66 deletions

View file

@ -866,7 +866,7 @@ void readlevelheader(MYFILE *f, char * name)
if (mapheaderinfo[num]->lumpname == NULL)
{
mapheaderinfo[num]->lumpname = Z_StrDup(name);
mapheaderinfo[num]->lumpnamehash = quickncasehash(mapheaderinfo[num]->lumpname, MAXMAPLUMPNAME);
mapheaderinfo[num]->lumpnamehash = FNV1a_QuickCaseHash(mapheaderinfo[num]->lumpname, MAXMAPLUMPNAME);
}
do

View file

@ -726,7 +726,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
{
cupheader_t *cup = kartcupheaders;
cupheader_t *prev = NULL;
UINT32 hash = quickncasehash(word2, MAXCUPNAME);
UINT32 hash = FNV1a_QuickCaseHash(word2, MAXCUPNAME);
while (cup)
{

View file

@ -412,22 +412,6 @@ extern boolean capslock;
// i_system.c, replace getchar() once the keyboard has been appropriated
INT32 I_GetKey(void);
/* http://www.cse.yorku.ca/~oz/hash.html */
static inline
UINT32 quickncasehash (const char *p, size_t n)
{
size_t i = 0;
UINT32 x = 5381;
while (i < n && p[i])
{
x = (x * 33) ^ tolower(p[i]);
i++;
}
return x;
}
// Standard Hashing Functions (tm)
// see W_MakeFileHash for hashing files
#include "xxhash.h"

View file

@ -367,7 +367,7 @@ struct mapheader_t
{
// Core game information, not user-modifiable directly
char *lumpname; ///< Lump name can be really long
UINT32 lumpnamehash; ///< quickncasehash(->lumpname, MAXMAPLUMPNAME)
UINT32 lumpnamehash; ///< FNV1a_QuickCaseHash(->lumpname, MAXMAPLUMPNAME)
lumpnum_t lumpnum; ///< Lump number for the map, used by vres_GetMap
void *thumbnailPic; ///< Lump data for the level select thumbnail.

View file

@ -1028,7 +1028,7 @@ INT32 G_MapNumber(const char * name)
#endif
{
INT32 map;
UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME);
UINT32 hash = FNV1a_QuickCaseHash(name, MAXMAPLUMPNAME);
for (map = 0; map < nummapheaders; ++map)
{

View file

@ -16,6 +16,7 @@
#include "doomdef.h"
#include "doomtype.h"
#include "r_textures.h"
#include "m_misc.h"
#include "w_wad.h"
#include "z_zone.h"
@ -62,7 +63,7 @@ static brightmapStorage_t *K_GetBrightmapStorageByIndex(size_t checkIndex)
--------------------------------------------------*/
static brightmapStorage_t *K_GetBrightmapStorageByTextureName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, 8);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, 8);
size_t i;
if (maxBrightmapStorage == 0)
@ -119,7 +120,7 @@ static boolean K_BRIGHTLumpParser(char *data, size_t size)
{
bms = K_NewBrightmap();
strncpy(bms->textureName, tkn, 8);
bms->textureHash = quickncasehash(tkn, 8);
bms->textureHash = FNV1a_QuickCaseHash(tkn, 8);
}
Z_Free(tkn);
@ -129,7 +130,7 @@ static boolean K_BRIGHTLumpParser(char *data, size_t size)
if (tkn && pos <= size)
{
strncpy(bms->brightmapName, tkn, 8);
bms->brightmapHash = quickncasehash(tkn, 8);
bms->brightmapHash = FNV1a_QuickCaseHash(tkn, 8);
}
else
{

View file

@ -58,7 +58,7 @@ void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPrope
memcpy(prop->key, key, keyLength + 1);
prop->key[keyLength] = '\0';
prop->hash = quickncasehash(prop->key, keyLength);
prop->hash = FNV1a_QuickCaseHash(prop->key, keyLength);
prop->type = type;
switch (type)
@ -101,7 +101,7 @@ void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPrope
mapUserProperty_t *K_UserPropertyFind(mapUserProperties_t *user, const char *key)
{
const size_t keyLength = strlen(key);
const UINT32 hash = quickncasehash(key, keyLength);
const UINT32 hash = FNV1a_QuickCaseHash(key, keyLength);
size_t i;
if (user->length == 0)

View file

@ -22,6 +22,7 @@
#include "doomtype.h"
#include "m_fixed.h"
#include "m_random.h"
#include "m_misc.h"
#include "p_local.h"
#include "p_mobj.h"
#include "r_textures.h"
@ -98,7 +99,7 @@ t_splash_t *K_GetSplashByIndex(size_t checkIndex)
--------------------------------------------------*/
t_splash_t *K_GetSplashByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numSplashDefs == 0)
@ -167,7 +168,7 @@ t_footstep_t *K_GetFootstepByIndex(size_t checkIndex)
--------------------------------------------------*/
t_footstep_t *K_GetFootstepByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numFootstepDefs == 0)
@ -236,7 +237,7 @@ t_overlay_t *K_GetOverlayByIndex(size_t checkIndex)
--------------------------------------------------*/
t_overlay_t *K_GetOverlayByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numOverlayDefs == 0)
@ -305,7 +306,7 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex)
--------------------------------------------------*/
terrain_t *K_GetTerrainByName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, TERRAIN_NAME_LEN);
size_t i;
if (numTerrainDefs > 0)
@ -352,7 +353,7 @@ size_t K_GetDefaultTerrainID(void)
--------------------------------------------------*/
terrain_t *K_GetTerrainForTextureName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, 8);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, 8);
size_t i;
if (numTerrainFloorDefs > 0)
@ -380,7 +381,7 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName)
--------------------------------------------------*/
size_t K_GetTerrainIDForTextureName(const char *checkName)
{
UINT32 checkHash = quickncasehash(checkName, 8);
UINT32 checkHash = FNV1a_QuickCaseHash(checkName, 8);
size_t i;
if (numTerrainFloorDefs > 0)
@ -1933,7 +1934,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
t_splash_t *s = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numSplashDefs; i++)
{
@ -1974,7 +1975,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
t_footstep_t *fs = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numFootstepDefs; i++)
{
@ -2015,7 +2016,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
t_overlay_t *o = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numOverlayDefs; i++)
{
@ -2056,7 +2057,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
terrain_t *t = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numTerrainDefs; i++)
{
@ -2108,7 +2109,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
t_floor_t *f = NULL;
tknHash = quickncasehash(tkn, 8);
tknHash = FNV1a_QuickCaseHash(tkn, 8);
for (i = 0; i < numTerrainFloorDefs; i++)
{
@ -2188,7 +2189,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
terrain_t *t = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numTerrainDefs; i++)
{
@ -2227,7 +2228,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
{
t_footstep_t *fs = NULL;
tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN);
tknHash = FNV1a_QuickCaseHash(tkn, TERRAIN_NAME_LEN);
for (i = 0; i < numFootstepDefs; i++)
{

View file

@ -2537,3 +2537,22 @@ const char * M_Ftrim (double f)
return &dig[1];/* skip the 0 */
}
}
#define FNV1A_OFFSET_BASIS 0x811C9DC5
#define FNV1A_PRIME 0x01000193
// Behaves like the old quickncasehash
// Stops either if it encounters null terminator
// or reaches size
UINT32 FNV1a_QuickCaseHash(const char *message, size_t size)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
for (size_t i = 0; i < size && message[i]; i++)
{
hash ^= tolower(message[i]);
hash *= FNV1A_PRIME;
}
return hash;
}

View file

@ -123,6 +123,9 @@ const char * M_Ftrim (double);
// counting bits, for weapon ammo code, usually
FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
// Hashes some message using FNV-1a
UINT32 FNV1a_QuickCaseHash(const char *message, size_t size);
#include "w_wad.h"
extern char configfile[MAX_WADPATH];

View file

@ -1160,7 +1160,7 @@ Rloadflats (INT32 i, INT32 w)
// Set texture properties.
memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
texture->hash = quickncasehash(texture->name, 8);
texture->hash = FNV1a_QuickCaseHash(texture->name, 8);
#ifndef NO_PNG_LUMPS
W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0);
@ -1261,7 +1261,7 @@ Rloadtextures (INT32 i, INT32 w)
// Set texture properties.
memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
texture->hash = quickncasehash(texture->name, 8);
texture->hash = FNV1a_QuickCaseHash(texture->name, 8);
#ifndef NO_PNG_LUMPS
if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength))
@ -1800,7 +1800,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture)
// Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily.
resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL);
memcpy(resultTexture->name, newTextureName, 8);
resultTexture->hash = quickncasehash(newTextureName, 8);
resultTexture->hash = FNV1a_QuickCaseHash(newTextureName, 8);
resultTexture->width = newTextureWidth;
resultTexture->height = newTextureHeight;
resultTexture->type = TEXTURETYPE_TEXTURE;
@ -2014,7 +2014,7 @@ INT32 R_CheckTextureNumForName(const char *name, UINT8 type)
PaletteTextureHack(&name);
hash = quickncasehash(name, 8);
hash = FNV1a_QuickCaseHash(name, 8);
for (i = 0; i < tidcachelen; i++)
if (tidcache[i].type == type && tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8))

View file

@ -1373,7 +1373,7 @@ musicdef_t *S_FindMusicDef(const char *name, UINT8 *i)
if (!name || !name[0])
return NULL;
hash = quickncasehash (name, 6);
hash = FNV1a_QuickCaseHash (name, 6);
for (def = musicdefstart; def; def = def->next)
{
@ -1454,7 +1454,7 @@ ReadMusicDefFields
break;
STRBUFCPY(def->name[i], value);
strlwr(def->name[i]);
def->hash[i] = quickncasehash (def->name[i], 6);
def->hash[i] = FNV1a_QuickCaseHash (def->name[i], 6);
i++;
} while ((value = strtok(NULL," ,")) != NULL);

View file

@ -107,8 +107,8 @@ struct LumpnumStringEquals
{
bool operator()(const std::string& str1, const std::string& str2) const
{
// fasticmp should be plenty fast and ignores case
return fasticmp(str1.c_str(), str2.c_str());
//return fastcmp(str1.c_str(), str2.c_str());
return str1 == str2;
}
};
@ -455,12 +455,12 @@ boolean W_MakeFileHash(const char *filename, boolean openwad, UINT64 *ret)
}
// Invalidates the cache of lump numbers. Call this whenever a wad is added.
static void W_InvalidateLumpnumCache(void)
FUNCINLINE static ATTRINLINE void W_InvalidateLumpnumCache(void)
{
lumpnumcache.clear();
}
UINT32 W_HashLumpName(const char *name, size_t len)
FUNCINLINE static ATTRINLINE UINT32 W_HashLumpName(const char *name, size_t len)
{
char uname[len + 1];
strlcpy(uname, name, len + 1);
@ -1456,7 +1456,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
return INT16_MAX;
}
static lumpnum_t CheckLumpInCache(const char *name)
FUNCINLINE static ATTRINLINE lumpnum_t CheckLumpInCache(const char *name)
{
auto it = lumpnumcache.find(name);
if (it != lumpnumcache.end())
@ -1465,9 +1465,9 @@ static lumpnum_t CheckLumpInCache(const char *name)
return LUMPERROR;
}
static void AddLumpToCache(lumpnum_t lumpnum, const char *name)
FUNCINLINE static ATTRINLINE void AddLumpToCache(lumpnum_t lumpnum, const char *name)
{
lumpnumcache.insert({name, lumpnum});
lumpnumcache.emplace(name, lumpnum);
}
//
@ -1709,21 +1709,10 @@ UINT8 W_LumpExists(const char *name)
UINT32 hash;
size_t namelen;
// Check the lumpnumcache first.
lumpnum_t cachenum = CheckLumpInCache(name);
if (cachenum != LUMPERROR)
{
// ok ok, we did find a lump in our lumpcache BUTT
// the lumpcache map is case insensitive
// extract the lumpinfo out of the lumpnum
// so we can do one more extra case ~sensitive~ name compare
// otherwise we gotta fall through to our manual lump search below
// lumpnum == (i << 16) | check
UINT16 wadnum = (cachenum >> 16) & 0xFFFF;
UINT16 lumpid = cachenum & 0xFFFF;
lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[lumpid];
if (fastcmp(lump_p->longname, name))
return true;
}
return cachenum;
namelen = strlen(name);
hash = W_HashLumpName(name, namelen);

View file

@ -166,8 +166,6 @@ boolean W_MakeFileHash(const char *filename, boolean openwad, UINT64 *ret);
// W_InitMultipleFiles exits if a file was not found, but not if all is okay.
INT32 W_InitMultipleFiles(char **filenames, boolean addons);
UINT32 W_HashLumpName(const char *name, size_t len);
const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
const char *W_CheckNameForNum(lumpnum_t lumpnum);