756 lines
17 KiB
C
756 lines
17 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cache.c
|
|
|
|
Abstract:
|
|
|
|
Implements a cache mechanism to speed up OpenRegKeyStr.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 11-Sep-2000
|
|
|
|
Revisions:
|
|
|
|
<alias> <date> <comments>
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "regp.h"
|
|
|
|
#define DBG_REG "Reg"
|
|
|
|
typedef struct {
|
|
HKEY Key;
|
|
BOOL Unicode;
|
|
UINT RefCount;
|
|
UINT ClosesNeeded;
|
|
REGSAM Sam;
|
|
UINT KeyStringBytes;
|
|
BYTE KeyString[];
|
|
} REGKEYCACHE, *PREGKEYCACHE;
|
|
|
|
typedef struct {
|
|
HKEY Key;
|
|
BOOL Unicode;
|
|
UINT RefCount;
|
|
UINT ClosesNeeded;
|
|
REGSAM Sam;
|
|
UINT KeyStringBytes;
|
|
CHAR KeyString[MAX_REGISTRY_KEY];
|
|
} WORKITEMA, *PWORKITEMA;
|
|
|
|
typedef struct {
|
|
HKEY Key;
|
|
BOOL Unicode;
|
|
UINT RefCount;
|
|
UINT ClosesNeeded;
|
|
REGSAM Sam;
|
|
UINT KeyStringBytes;
|
|
WCHAR KeyString[MAX_REGISTRY_KEY];
|
|
} WORKITEMW, *PWORKITEMW;
|
|
|
|
GROWLIST g_KeyCache = GROWLIST_INIT;
|
|
UINT g_MaxCacheCount = 0;
|
|
UINT g_CacheAddPos;
|
|
CHAR g_LastParent[MAX_REGISTRY_KEY];
|
|
UINT g_LastParentBytes;
|
|
UINT g_LastParentUse;
|
|
WCHAR g_LastParentW[MAX_REGISTRY_KEY];
|
|
UINT g_LastParentBytesW;
|
|
UINT g_LastParentUseW;
|
|
|
|
BOOL
|
|
pRemoveItemFromCache (
|
|
IN UINT Item
|
|
);
|
|
|
|
//
|
|
// Implementation
|
|
//
|
|
|
|
VOID
|
|
RegInitializeCache (
|
|
IN UINT InitialCacheSize
|
|
)
|
|
{
|
|
if (InitialCacheSize > 64) {
|
|
InitialCacheSize = 64;
|
|
}
|
|
|
|
g_MaxCacheCount = InitialCacheSize;
|
|
g_CacheAddPos = 0;
|
|
g_LastParentUse = 0;
|
|
g_LastParentUseW = 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
RegTerminateCache (
|
|
VOID
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
pRemoveItemFromCache (u);
|
|
}
|
|
|
|
FreeGrowList (&g_KeyCache);
|
|
g_MaxCacheCount = 0;
|
|
g_LastParentUse = 0;
|
|
g_LastParentUseW = 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pRemoveItemFromCache (
|
|
IN UINT Item
|
|
)
|
|
{
|
|
PREGKEYCACHE cacheItem;
|
|
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, Item);
|
|
|
|
if (!cacheItem) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (cacheItem->RefCount == 0) {
|
|
|
|
while (cacheItem->ClosesNeeded) {
|
|
CloseRegKeyWorker (cacheItem->Key);
|
|
cacheItem->ClosesNeeded--;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RegRecordParentInCacheA (
|
|
IN PCSTR KeyString,
|
|
IN PCSTR StringEnd
|
|
)
|
|
{
|
|
HKEY key;
|
|
UINT byteCount;
|
|
CHAR lowerStr[MAX_REGISTRY_KEY];
|
|
HKEY rootKey;
|
|
UINT end;
|
|
|
|
if (!g_MaxCacheCount) {
|
|
return;
|
|
}
|
|
|
|
if (StringEnd <= KeyString) {
|
|
return;
|
|
}
|
|
|
|
byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
|
|
|
|
if (byteCount >= (MAX_REGISTRY_KEY * sizeof (CHAR))) {
|
|
return;
|
|
}
|
|
|
|
if (g_LastParentUse && g_LastParentUse < 3 && g_LastParentBytes == byteCount) {
|
|
|
|
StringCopyABA (lowerStr, KeyString, StringEnd);
|
|
CharLowerA (lowerStr);
|
|
|
|
if (StringMatchA (g_LastParent, lowerStr)) {
|
|
g_LastParentUse++;
|
|
|
|
if (g_LastParentUse == 3) {
|
|
//
|
|
// Stimulate the cache
|
|
//
|
|
rootKey = ConvertRootStringToKeyA (lowerStr, &end);
|
|
if (rootKey) {
|
|
if (lowerStr[end]) {
|
|
#ifdef DEBUG
|
|
key = OpenRegKeyWorkerA (rootKey, &lowerStr[end], __FILE__, __LINE__);
|
|
#else
|
|
key = OpenRegKeyWorkerA (rootKey, &lowerStr[end]);
|
|
#endif
|
|
|
|
if (key) {
|
|
RegAddKeyToCacheA (lowerStr, key, g_OpenSam);
|
|
CloseRegKey (key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
StringCopyABA (g_LastParent, KeyString, StringEnd);
|
|
CharLowerA (g_LastParent);
|
|
g_LastParentBytes = byteCount;
|
|
|
|
g_LastParentUse = 1;
|
|
}
|
|
|
|
|
|
HKEY
|
|
RegGetKeyFromCacheA (
|
|
IN PCSTR KeyString,
|
|
IN PCSTR KeyEnd, OPTIONAL
|
|
IN REGSAM Sam,
|
|
IN BOOL IncRefCount
|
|
)
|
|
{
|
|
UINT stringBytes;
|
|
PCSTR end;
|
|
CHAR lowerStr[MAX_REGISTRY_KEY];
|
|
UINT u;
|
|
UINT count;
|
|
PREGKEYCACHE cacheItem;
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
if (!count) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!KeyEnd) {
|
|
end = StackStringCopyA (lowerStr, KeyString);
|
|
} else {
|
|
if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
|
|
KeyEnd = KeyString + MAX_REGISTRY_KEY;
|
|
}
|
|
|
|
end = StringCopyABA (lowerStr, KeyString, KeyEnd);
|
|
}
|
|
|
|
CharLowerA (lowerStr);
|
|
|
|
stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
|
|
|
|
//
|
|
// Scan the cache sequentially (it should be small), and return a match
|
|
// if one is found. Stored strings are always in lower case.
|
|
//
|
|
|
|
u = g_CacheAddPos;
|
|
|
|
do {
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
|
|
|
|
if (!cacheItem) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((cacheItem->KeyStringBytes == stringBytes) &&
|
|
(!cacheItem->Unicode) &&
|
|
((cacheItem->Sam & Sam) == Sam) &&
|
|
(StringMatchA ((PCSTR) cacheItem->KeyString, lowerStr))
|
|
) {
|
|
|
|
if (IncRefCount) {
|
|
cacheItem->RefCount++;
|
|
}
|
|
|
|
return cacheItem->Key;
|
|
}
|
|
|
|
u++;
|
|
if (u >= count) {
|
|
u = 0;
|
|
}
|
|
|
|
} while (u != g_CacheAddPos);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
RegAddKeyToCacheA (
|
|
IN PCSTR KeyString,
|
|
IN HKEY Key,
|
|
IN REGSAM Sam
|
|
)
|
|
{
|
|
PREGKEYCACHE cacheItem;
|
|
PREGKEYCACHE lastAddItem;
|
|
WORKITEMA workItem;
|
|
PCSTR end;
|
|
UINT minStructSize;
|
|
UINT pos;
|
|
UINT count;
|
|
UINT u;
|
|
|
|
if (!g_MaxCacheCount || !Key) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Scan the cache for the existing Key
|
|
//
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
|
|
|
|
if (cacheItem->Key == Key) {
|
|
cacheItem->RefCount++;
|
|
|
|
g_CacheAddPos = u;
|
|
cacheItem->Sam |= Sam;
|
|
|
|
if (cacheItem->KeyStringBytes == 0 && *KeyString && !cacheItem->Unicode) {
|
|
|
|
//
|
|
// This key was added before we knew the name. Update the name
|
|
// now.
|
|
//
|
|
|
|
DEBUGMSG ((DBG_REG, "Updating empty-named key %s", KeyString));
|
|
|
|
minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
|
|
CopyMemory (&workItem, cacheItem, minStructSize);
|
|
|
|
end = StackStringCopyA (workItem.KeyString, KeyString);
|
|
CharLowerA (workItem.KeyString);
|
|
workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
|
|
workItem.ClosesNeeded++;
|
|
|
|
minStructSize += workItem.KeyStringBytes + sizeof (CHAR);
|
|
GrowListSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
|
|
|
|
} else if (*KeyString == 0) {
|
|
cacheItem->ClosesNeeded++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the new cache item
|
|
//
|
|
|
|
workItem.Key = Key;
|
|
workItem.Unicode = FALSE;
|
|
workItem.RefCount = 1;
|
|
workItem.ClosesNeeded = 1;
|
|
workItem.Sam = Sam;
|
|
end = StackStringCopyA (workItem.KeyString, KeyString);
|
|
CharLowerA (workItem.KeyString);
|
|
workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
|
|
|
|
minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (CHAR);
|
|
|
|
//
|
|
// Put work item into grow list
|
|
//
|
|
|
|
if (count < g_MaxCacheCount) {
|
|
g_CacheAddPos = count;
|
|
GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
|
|
} else {
|
|
|
|
//
|
|
// Look for a closed key to discard. If cache is too full, then
|
|
// increase the cache size. If the cache size hits 64, then don't
|
|
// cache this add.
|
|
//
|
|
|
|
lastAddItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, g_CacheAddPos);
|
|
|
|
if (lastAddItem) {
|
|
|
|
for (pos = 0 ; pos < count ; pos++) {
|
|
if (pos == g_CacheAddPos) {
|
|
continue;
|
|
}
|
|
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, pos);
|
|
if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
|
|
break;
|
|
}
|
|
|
|
if (cacheItem->Unicode) {
|
|
continue;
|
|
}
|
|
|
|
if (cacheItem->RefCount) {
|
|
continue;
|
|
}
|
|
|
|
if (!StringPrefixA ((PCSTR) lastAddItem->KeyString, (PCSTR) cacheItem->KeyString)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pos == count) {
|
|
if (g_MaxCacheCount == 64) {
|
|
DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
|
|
return;
|
|
}
|
|
|
|
g_MaxCacheCount++;
|
|
GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
|
|
|
|
} else {
|
|
pRemoveItemFromCache (pos);
|
|
GrowListSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
|
|
}
|
|
|
|
g_CacheAddPos = pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
RegDecrementRefCount (
|
|
IN HKEY Key
|
|
)
|
|
{
|
|
UINT u;
|
|
UINT count;
|
|
PREGKEYCACHE cacheItem;
|
|
|
|
if (!g_MaxCacheCount) {
|
|
return FALSE;
|
|
}
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
|
|
if (cacheItem->Key == Key) {
|
|
if (cacheItem->RefCount == 0) {
|
|
|
|
//
|
|
// The caller is tried to close the key more times than what
|
|
// it was opened.
|
|
//
|
|
|
|
if (cacheItem->Unicode) {
|
|
DEBUGMSGW ((
|
|
DBG_WHOOPS,
|
|
"Reg key %s ref count == 0; trying to close it too many times",
|
|
cacheItem->KeyString
|
|
));
|
|
} else {
|
|
DEBUGMSGA ((
|
|
DBG_WHOOPS,
|
|
"Reg key %s ref count == 0; trying to close it too many times",
|
|
cacheItem->KeyString
|
|
));
|
|
}
|
|
} else {
|
|
cacheItem->RefCount--;
|
|
}
|
|
|
|
//
|
|
// Return TRUE to postpone the close
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
RegRecordParentInCacheW (
|
|
IN PCWSTR KeyString,
|
|
IN PCWSTR StringEnd
|
|
)
|
|
{
|
|
HKEY key;
|
|
UINT byteCount;
|
|
WCHAR lowerStr[MAX_REGISTRY_KEY];
|
|
HKEY rootKey;
|
|
UINT end;
|
|
|
|
if (!g_MaxCacheCount) {
|
|
return;
|
|
}
|
|
|
|
if (StringEnd <= KeyString) {
|
|
return;
|
|
}
|
|
|
|
byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
|
|
|
|
if (byteCount >= (MAX_REGISTRY_KEY * sizeof (WCHAR))) {
|
|
return;
|
|
}
|
|
|
|
if (g_LastParentUseW && g_LastParentUseW < 3 && g_LastParentBytesW == byteCount) {
|
|
|
|
StringCopyABW (lowerStr, KeyString, StringEnd);
|
|
CharLowerW (lowerStr);
|
|
|
|
if (StringMatchW (g_LastParentW, lowerStr)) {
|
|
g_LastParentUseW++;
|
|
|
|
if (g_LastParentUseW == 3) {
|
|
//
|
|
// Stimulate the cache
|
|
//
|
|
|
|
rootKey = ConvertRootStringToKeyW (lowerStr, &end);
|
|
if (rootKey) {
|
|
if (lowerStr[end]) {
|
|
|
|
#ifdef DEBUG
|
|
key = OpenRegKeyWorkerW (rootKey, &lowerStr[end], __FILE__, __LINE__);
|
|
#else
|
|
key = OpenRegKeyWorkerW (rootKey, &lowerStr[end]);
|
|
#endif
|
|
|
|
if (key) {
|
|
RegAddKeyToCacheW (lowerStr, key, g_OpenSam);
|
|
CloseRegKey (key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
StringCopyABW (g_LastParentW, KeyString, StringEnd);
|
|
CharLowerW (g_LastParentW);
|
|
g_LastParentBytesW = byteCount;
|
|
|
|
g_LastParentUseW = 1;
|
|
}
|
|
|
|
|
|
HKEY
|
|
RegGetKeyFromCacheW (
|
|
IN PCWSTR KeyString,
|
|
IN PCWSTR KeyEnd, OPTIONAL
|
|
IN REGSAM Sam,
|
|
IN BOOL IncRefCount
|
|
)
|
|
{
|
|
UINT stringBytes;
|
|
PCWSTR end;
|
|
WCHAR lowerStr[MAX_REGISTRY_KEY];
|
|
UINT u;
|
|
UINT count;
|
|
PREGKEYCACHE cacheItem;
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
if (!count) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!KeyEnd) {
|
|
end = StackStringCopyW (lowerStr, KeyString);
|
|
} else {
|
|
if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
|
|
KeyEnd = KeyString + MAX_REGISTRY_KEY;
|
|
}
|
|
|
|
end = StringCopyABW (lowerStr, KeyString, KeyEnd);
|
|
}
|
|
|
|
CharLowerW (lowerStr);
|
|
|
|
stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
|
|
|
|
//
|
|
// Scan the cache sequentially (it should be small), and return a match
|
|
// if one is found. Stored strings are always in lower case.
|
|
//
|
|
|
|
u = g_CacheAddPos;
|
|
|
|
do {
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
|
|
|
|
if (!cacheItem) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((cacheItem->KeyStringBytes == stringBytes) &&
|
|
(cacheItem->Unicode) &&
|
|
((cacheItem->Sam & Sam) == Sam) &&
|
|
(StringMatchW ((PCWSTR) cacheItem->KeyString, lowerStr))
|
|
) {
|
|
|
|
if (IncRefCount) {
|
|
cacheItem->RefCount++;
|
|
}
|
|
|
|
return cacheItem->Key;
|
|
}
|
|
|
|
u++;
|
|
if (u >= count) {
|
|
u = 0;
|
|
}
|
|
|
|
} while (u != g_CacheAddPos);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
RegAddKeyToCacheW (
|
|
IN PCWSTR KeyString,
|
|
IN HKEY Key,
|
|
IN REGSAM Sam
|
|
)
|
|
{
|
|
PREGKEYCACHE cacheItem;
|
|
PREGKEYCACHE lastAddItem;
|
|
WORKITEMW workItem;
|
|
PCWSTR end;
|
|
UINT minStructSize;
|
|
UINT pos;
|
|
UINT count;
|
|
UINT u;
|
|
|
|
if (!g_MaxCacheCount || !Key) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Scan the cache for the existing Key
|
|
//
|
|
|
|
count = GrowListGetSize (&g_KeyCache);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
|
|
|
|
if (cacheItem->Key == Key) {
|
|
cacheItem->RefCount++;
|
|
|
|
g_CacheAddPos = u;
|
|
cacheItem->Sam |= Sam;
|
|
|
|
if (cacheItem->KeyStringBytes == 0 && *KeyString && cacheItem->Unicode) {
|
|
|
|
//
|
|
// This key was added before we knew the name. Update the name
|
|
// now.
|
|
//
|
|
|
|
minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
|
|
CopyMemory (&workItem, cacheItem, minStructSize);
|
|
|
|
end = StackStringCopyW (workItem.KeyString, KeyString);
|
|
CharLowerW (workItem.KeyString);
|
|
workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
|
|
workItem.ClosesNeeded++;
|
|
|
|
minStructSize += workItem.KeyStringBytes + sizeof (WCHAR);
|
|
GrowListSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
|
|
|
|
} else if (*KeyString == 0) {
|
|
cacheItem->ClosesNeeded++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the new cache item
|
|
//
|
|
|
|
workItem.Key = Key;
|
|
workItem.Unicode = TRUE;
|
|
workItem.RefCount = 1;
|
|
workItem.ClosesNeeded = 1;
|
|
workItem.Sam = Sam;
|
|
end = StackStringCopyW (workItem.KeyString, KeyString);
|
|
CharLowerW (workItem.KeyString);
|
|
workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
|
|
|
|
minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (WCHAR);
|
|
|
|
//
|
|
// Put work item into grow list
|
|
//
|
|
|
|
if (count < g_MaxCacheCount) {
|
|
g_CacheAddPos = count;
|
|
GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
|
|
} else {
|
|
|
|
//
|
|
// Look for a closed key to discard. If cache is too full, then
|
|
// increase the cache size. If the cache size hits 64, then don't
|
|
// cache this add.
|
|
//
|
|
|
|
lastAddItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, g_CacheAddPos);
|
|
|
|
if (lastAddItem) {
|
|
|
|
for (pos = 0 ; pos < count ; pos++) {
|
|
if (pos == g_CacheAddPos) {
|
|
continue;
|
|
}
|
|
|
|
cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, pos);
|
|
if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
|
|
break;
|
|
}
|
|
|
|
if (!cacheItem->Unicode) {
|
|
continue;
|
|
}
|
|
|
|
if (cacheItem->RefCount) {
|
|
continue;
|
|
}
|
|
|
|
if (!StringPrefixW ((PCWSTR) lastAddItem->KeyString, (PCWSTR) cacheItem->KeyString)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pos == count) {
|
|
if (g_MaxCacheCount == 64) {
|
|
DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
|
|
return;
|
|
}
|
|
|
|
g_MaxCacheCount++;
|
|
GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
|
|
|
|
} else {
|
|
pRemoveItemFromCache (pos);
|
|
GrowListSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
|
|
}
|
|
|
|
g_CacheAddPos = pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|