From 2aec4501eb4c742ba7830c2cce16bd64db7ac55d Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Thu, 16 Dec 2021 22:10:36 -0800
Subject: [PATCH] Hash name lookup for textures and lumps

---
 src/doomdef.h    | 16 ++++++++++++++++
 src/r_textures.c | 12 ++++++++++--
 src/r_textures.h |  1 +
 src/w_wad.c      | 19 ++++++++++++++++---
 src/w_wad.h      |  1 +
 5 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/src/doomdef.h b/src/doomdef.h
index 7e7e35599..e1a7a247a 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -530,6 +530,22 @@ 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;
+}
+
 #ifndef min // Double-Check with WATTCP-32's cdefs.h
 #define min(x, y) (((x) < (y)) ? (x) : (y))
 #endif
diff --git a/src/r_textures.c b/src/r_textures.c
index a53c2e738..645a57b9b 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -59,6 +59,7 @@ INT32 *texturetranslation;
 // Painfully simple texture id cacheing to make maps load faster. :3
 static struct {
 	char name[9];
+	UINT32 hash;
 	INT32 id;
 } *tidcache = NULL;
 static INT32 tidcachelen = 0;
@@ -788,6 +789,7 @@ Rloadflats (INT32 i, INT32 w)
 
 			// Set texture properties.
 			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
+			texture->hash = quickncasehash(texture->name, 8);
 
 #ifndef NO_PNG_LUMPS
 			if (Picture_IsLumpPNG(header, lumplength))
@@ -886,6 +888,7 @@ Rloadtextures (INT32 i, INT32 w)
 
 			// Set texture properties.
 			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
+			texture->hash = quickncasehash(texture->name, 8);
 
 #ifndef NO_PNG_LUMPS
 			if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength))
@@ -1401,6 +1404,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);
 			M_Memcpy(resultTexture->name, newTextureName, 8);
+			resultTexture->hash = quickncasehash(newTextureName, 8);
 			resultTexture->width = newTextureWidth;
 			resultTexture->height = newTextureHeight;
 			resultTexture->type = TEXTURETYPE_COMPOSITE;
@@ -1627,19 +1631,22 @@ void R_ClearTextureNumCache(boolean btell)
 INT32 R_CheckTextureNumForName(const char *name)
 {
 	INT32 i;
+	UINT32 hash;
 
 	// "NoTexture" marker.
 	if (name[0] == '-')
 		return 0;
 
+	hash = quickncasehash(name, 8);
+
 	for (i = 0; i < tidcachelen; i++)
-		if (!strncasecmp(tidcache[i].name, name, 8))
+		if (tidcache[i].hash == hash && !strncasecmp(tidcache[i].name, name, 8))
 			return tidcache[i].id;
 
 	// Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier
 	//for (i = 0; i < numtextures; i++) <- old
 	for (i = (numtextures - 1); i >= 0; i--) // <- new
-		if (!strncasecmp(textures[i]->name, name, 8))
+		if (textures[i]->hash == hash && !strncasecmp(textures[i]->name, name, 8))
 		{
 			tidcachelen++;
 			Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache);
@@ -1648,6 +1655,7 @@ INT32 R_CheckTextureNumForName(const char *name)
 #ifndef ZDEBUG
 			CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name);
 #endif
+			tidcache[tidcachelen-1].hash = hash;
 			tidcache[tidcachelen-1].id = i;
 			return i;
 		}
diff --git a/src/r_textures.h b/src/r_textures.h
index 95796d89c..69b59426a 100644
--- a/src/r_textures.h
+++ b/src/r_textures.h
@@ -54,6 +54,7 @@ typedef struct
 {
 	// Keep name for switch changing, etc.
 	char name[8];
+	UINT32 hash;
 	UINT8 type; // TEXTURETYPE_
 	INT16 width, height;
 	boolean holes;
diff --git a/src/w_wad.c b/src/w_wad.c
index e49e0ce82..9c631da65 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -361,6 +361,7 @@ static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const
 	lumpinfo->size = ftell(handle);
 	fseek(handle, 0, SEEK_SET);
 	strcpy(lumpinfo->name, lumpname);
+	lumpinfo->hash = quickncasehash(lumpname, 8);
 
 	// Allocate the lump's long name.
 	lumpinfo->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
@@ -459,6 +460,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen
 			lump_p->compression = CM_NOCOMPRESSION;
 		memset(lump_p->name, 0x00, 9);
 		strncpy(lump_p->name, fileinfo->name, 8);
+		lump_p->hash = quickncasehash(lump_p->name, 8);
 
 		// Allocate the lump's long name.
 		lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
@@ -634,6 +636,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
 
 		memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary?
 		strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
+		lump_p->hash = quickncasehash(lump_p->name, 8);
 
 		lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL);
 		strlcpy(lump_p->longname, trimname, dotpos - trimname + 1);
@@ -1225,12 +1228,14 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
 {
 	UINT16 i;
 	static char uname[8 + 1];
+	UINT32 hash;
 
 	if (!TestValidLump(wad,0))
 		return INT16_MAX;
 
 	strlcpy(uname, name, sizeof uname);
 	strupr(uname);
+	hash = quickncasehash(uname, 8);
 
 	//
 	// scan forward
@@ -1241,7 +1246,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
 	{
 		lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
 		for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
-			if (!strncmp(lump_p->name, uname, sizeof(uname) - 1))
+			if (lump_p->hash == hash && !strncmp(lump_p->name, uname, sizeof(uname) - 1))
 				return i;
 	}
 
@@ -1444,15 +1449,20 @@ lumpnum_t W_CheckNumForLongName(const char *name)
 // TODO: Make it search through cache first, maybe...?
 lumpnum_t W_CheckNumForMap(const char *name)
 {
+	UINT32 hash = quickncasehash(name, 8);
 	UINT16 lumpNum, end;
 	UINT32 i;
+	lumpinfo_t *p;
 	for (i = numwadfiles - 1; i < numwadfiles; i--)
 	{
 		if (wadfiles[i]->type == RET_WAD)
 		{
 			for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++)
-				if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+			{
+				p = wadfiles[i]->lumpinfo + lumpNum;
+				if (p->hash == hash && !strncmp(name, p->name, 8))
 					return (i<<16) + lumpNum;
+			}
 		}
 		else if (W_FileHasFolders(wadfiles[i]))
 		{
@@ -1463,8 +1473,11 @@ lumpnum_t W_CheckNumForMap(const char *name)
 				continue;
 			// Now look for the specified map.
 			for (; lumpNum < end; lumpNum++)
-				if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8))
+			{
+				p = wadfiles[i]->lumpinfo + lumpNum;
+				if (p->hash == hash && !strnicmp(name, p->name, 8))
 					return (i<<16) + lumpNum;
+			}
 		}
 	}
 	return LUMPERROR;
diff --git a/src/w_wad.h b/src/w_wad.h
index a41ba1724..9b8a66aa7 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -67,6 +67,7 @@ typedef struct
 	unsigned long position; // filelump_t filepos
 	unsigned long disksize; // filelump_t size
 	char name[9];           // filelump_t name[] e.g. "LongEntr"
+	UINT32 hash;
 	char *longname;         //                   e.g. "LongEntryName"
 	char *fullname;         //                   e.g. "Folder/Subfolder/LongEntryName.extension"
 	char *diskpath;         // path to the file  e.g. "/usr/games/srb2/Addon/Folder/Subfolder/LongEntryName.extension"