/* $Id: imagecache.c 3140 2018-03-14 21:28:10Z bird $ */ /** @file * kBuild specific executable image cache for Windows. */ /* * Copyright (c) 2012 knut st. osmundsen * * This file is part of kBuild. * * kBuild is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * kBuild is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kBuild. If not, see * */ /* No GNU coding style here! */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "makeint.h" #include /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ typedef struct EXECCACHEENTRY { /** The name hash value. */ unsigned uHash; /** The name length. */ unsigned cchName; /** Pointer to the next name with the same hash. */ struct EXECCACHEENTRY *pNext; /** When it was last referenced. */ unsigned uLastRef; /** The module handle. */ HMODULE hmod1; /** The module handle. */ HMODULE hmod2; /** The executable path. */ char szName[1]; } EXECCACHEENTRY; typedef EXECCACHEENTRY *PEXECCACHEENTRY; /******************************************************************************* * Global Variables * *******************************************************************************/ /** The number of cached images. */ static unsigned g_cCached; /** Used noting when entries was last used. * Increased on each kmk_cache_exec_image call. */ static unsigned g_uNow; /** The size of the hash table. */ #define EXECCACHE_HASHTAB_SIZE 128 /** The hash table. */ static PEXECCACHEENTRY g_apHashTab[EXECCACHE_HASHTAB_SIZE]; /* sdbm: This algorithm was created for sdbm (a public-domain reimplementation of ndbm) database library. it was found to do well in scrambling bits, causing better distribution of the keys and fewer splits. it also happens to be a good general hashing function with good distribution. the actual function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below is the faster version used in gawk. [there is even a faster, duff-device version] the magic constant 65599 was picked out of thin air while experimenting with different constants, and turns out to be a prime. this is one of the algorithms used in berkeley db (see sleepycat) and elsewhere. */ static unsigned execcache_calc_hash(const char *psz, unsigned *pcch) { unsigned char *puch = (unsigned char *)psz; unsigned hash = 0; int ch; while ((ch = *puch++)) hash = ch + (hash << 6) + (hash << 16) - hash; *pcch = (unsigned)(puch - psz - 1); return hash; } extern void kmk_cache_exec_image(const char *pszExec) { /* * Lookup the name. */ unsigned cchName; const unsigned uHash = execcache_calc_hash(pszExec, &cchName); PEXECCACHEENTRY *ppCur = &g_apHashTab[uHash % EXECCACHE_HASHTAB_SIZE]; PEXECCACHEENTRY pCur = *ppCur; while (pCur) { if ( pCur->uHash == uHash && pCur->cchName == cchName && !memcmp(pCur->szName, pszExec, cchName)) { pCur->uLastRef = ++g_uNow; return; } ppCur = &pCur->pNext; pCur = pCur->pNext; } /* * Not found, create a new entry. */ pCur = xmalloc(sizeof(*pCur) + cchName); pCur->uHash = uHash; pCur->cchName = cchName; pCur->pNext = NULL; pCur->uLastRef = ++g_uNow; memcpy(pCur->szName, pszExec, cchName + 1); pCur->hmod1 = LoadLibraryEx(pszExec, NULL, LOAD_LIBRARY_AS_DATAFILE); if (pCur->hmod1 != NULL) pCur->hmod2 = LoadLibraryEx(pszExec, NULL, DONT_RESOLVE_DLL_REFERENCES); else pCur->hmod2 = NULL; *ppCur = pCur; g_cCached++; }