|
|
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: cache.c
//
// This files contains code for the common cache lists
//
// History:
// 09-02-93 ScottH Created
// 01-31-94 ScottH Split into separate files
//
//---------------------------------------------------------------------------
///////////////////////////////////////////////////// INCLUDES
#include "brfprv.h" // common headers
///////////////////////////////////////////////////// TYPEDEFS
typedef struct tagCITEM { int atomKey; DEBUG_CODE( LPCTSTR pszKey; )
LPVOID pvValue; UINT ucRef; } CITEM; // item for generic cache
#define Cache_EnterCS(this) EnterCriticalSection(&(this)->cs)
#define Cache_LeaveCS(this) LeaveCriticalSection(&(this)->cs)
#define CACHE_GROW 8
#define Cache_Bogus(this) (!(this)->hdpa || !(this)->hdpaFree || !(this)->hdsa)
// Given an index into the DPA, get the pointer to the DSA
//
#define MyGetPtr(this, idpa) DSA_GetItemPtr((this)->hdsa, PtrToUlong(DPA_FastGetPtr((this)->hdpa, idpa)))
#define DSA_GetPtrIndex(hdsa, ptr, cbItem) \
((int)( (DWORD_PTR)(ptr) - (DWORD_PTR)DSA_GetItemPtr(hdsa, 0) ) / (cbItem))
/*----------------------------------------------------------
Purpose: Compare two CRLs by pathname Returns: -1 if <, 0 if ==, 1 if > Cond: -- */ int CALLBACK _export Cache_CompareIndexes( LPVOID lpv1, LPVOID lpv2, LPARAM lParam) { int i1 = PtrToUlong(lpv1); int i2 = PtrToUlong(lpv2); HDSA hdsa = (HDSA)lParam; CITEM * pitem1 = (CITEM *)DSA_GetItemPtr(hdsa, i1); CITEM * pitem2 = (CITEM *)DSA_GetItemPtr(hdsa, i2);
if (pitem1->atomKey < pitem2->atomKey) return -1; else if (pitem1->atomKey == pitem2->atomKey) return 0; else return 1; }
/*----------------------------------------------------------
Purpose: Compare two CRLs by pathname Returns: -1 if <, 0 if ==, 1 if > Cond: -- */ int CALLBACK _export Cache_Compare( LPVOID lpv1, LPVOID lpv2, LPARAM lParam) { // HACK: we know the first param is the address to a struct
// that contains the search criteria. The second is an index
// into the DSA.
//
int i2 = PtrToUlong(lpv2); HDSA hdsa = (HDSA)lParam; CITEM * pitem1 = (CITEM *)lpv1; CITEM * pitem2 = (CITEM *)DSA_GetItemPtr(hdsa, i2);
if (pitem1->atomKey < pitem2->atomKey) return -1; else if (pitem1->atomKey == pitem2->atomKey) return 0; else return 1; }
/*----------------------------------------------------------
Purpose: Initialize the cache structure Returns: TRUE on success Cond: -- */ BOOL PUBLIC Cache_Init( CACHE * pcache) { BOOL bRet;
ASSERT(pcache);
Cache_EnterCS(pcache); { if ((pcache->hdsa = DSA_Create(sizeof(CITEM), CACHE_GROW)) != NULL) { if ((pcache->hdpa = DPA_Create(CACHE_GROW)) == NULL) { DSA_Destroy(pcache->hdsa); pcache->hdsa = NULL; } else { if ((pcache->hdpaFree = DPA_Create(CACHE_GROW)) == NULL) { DPA_Destroy(pcache->hdpa); DSA_Destroy(pcache->hdsa); pcache->hdpa = NULL; pcache->hdsa = NULL; } } } bRet = pcache->hdsa != NULL; } Cache_LeaveCS(pcache);
return bRet; }
/*----------------------------------------------------------
Purpose: Initializes the cache's critical section.
Returns: -- Cond: -- */ void PUBLIC Cache_InitCS( CACHE * pcache) { ASSERT(pcache); ZeroInit(pcache, CACHE); InitializeCriticalSectionAndSpinCount(&pcache->cs, 0); }
/*----------------------------------------------------------
Purpose: Destroy the cache Returns: -- Cond: -- */ void PUBLIC Cache_Term( CACHE * pcache, HWND hwndOwner, PFNFREEVALUE pfnFree) { ASSERT(pcache);
Cache_EnterCS(pcache); { if (pcache->hdpa != NULL) { CITEM * pitem; int idpa; int cItem;
ASSERT(pcache->hdsa != NULL);
cItem = DPA_GetPtrCount(pcache->hdpa); for (idpa = 0; idpa < cItem; idpa++) { pitem = MyGetPtr(pcache, idpa);
if (pfnFree != NULL) pfnFree(pitem->pvValue, hwndOwner);
// Decrement reference count of atomKey
Atom_Delete(pitem->atomKey); } DPA_Destroy(pcache->hdpa); pcache->hdpa = NULL; }
if (pcache->hdpaFree != NULL) { DPA_Destroy(pcache->hdpaFree); pcache->hdpaFree = NULL; }
if (pcache->hdsa != NULL) { DSA_Destroy(pcache->hdsa); pcache->hdsa = NULL; } } Cache_LeaveCS(pcache); }
/*----------------------------------------------------------
Purpose: Deletes the cache's critical section.
Returns: -- Cond: -- */ void PUBLIC Cache_DeleteCS( CACHE * pcache) { // The cache should not be in use now (ie, it should be bogus)
ASSERT(Cache_Bogus(pcache));
if (Cache_Bogus(pcache)) { DeleteCriticalSection(&pcache->cs); } }
/*----------------------------------------------------------
Purpose: Add an item to the cache list. Returns: TRUE on success
Cond: If this fails, pvValue is not automatically freed */ BOOL PUBLIC Cache_AddItem( CACHE * pcache, int atomKey, LPVOID pvValue) { BOOL bRet = FALSE; CITEM * pitem = NULL; int cItem; int cFree;
ASSERT(pcache); Cache_EnterCS(pcache); { VALIDATE_ATOM(atomKey);
if (!Cache_Bogus(pcache)) { int iItem;
// Add a new entry to the cache. The cache has no set size limitation.
//
cFree = DPA_GetPtrCount(pcache->hdpaFree); if (cFree > 0) { // Use a free entry
//
cFree--; iItem = PtrToUlong(DPA_DeletePtr(pcache->hdpaFree, cFree)); pitem = DSA_GetItemPtr(pcache->hdsa, iItem); } else { CITEM itemDummy;
// Allocate a new entry
//
cItem = DSA_GetItemCount(pcache->hdsa); if ((iItem = DSA_InsertItem(pcache->hdsa, cItem+1, &itemDummy)) != -1) pitem = DSA_GetItemPtr(pcache->hdsa, iItem); }
// Fill in the info
//
if (pitem) { pitem->ucRef = 0; pitem->pvValue = pvValue; pitem->atomKey = atomKey; DEBUG_CODE( pitem->pszKey = Atom_GetName(atomKey); )
// Now increment the reference count on this atomKey so it doesn't
// get deleted from beneath us!
Atom_AddRef(atomKey);
// Add the new entry to the ptr list and sort
//
cItem = DPA_GetPtrCount(pcache->hdpa); if (DPA_InsertPtr(pcache->hdpa, cItem+1, IntToPtr(iItem)) == -1) goto Add_Fail; DPA_Sort(pcache->hdpa, Cache_CompareIndexes, (LPARAM)pcache->hdsa);
// Reset the FindFirst/FindNext in case this gets called in the
// middle of an enumeration.
//
pcache->atomPrev = ATOM_ERR; bRet = TRUE; }
Add_Fail: if (!bRet) { // Add the entry to the free list and fail. If even this
// fails, we simply lose some slight efficiency, but this is
// not a memory leak.
//
DPA_InsertPtr(pcache->hdpaFree, cFree+1, IntToPtr(iItem)); } } } Cache_LeaveCS(pcache);
return bRet; }
/*----------------------------------------------------------
Purpose: Delete an item from the cache. If the reference count is 0, we do nothing. This also frees the actual value as well, using the pfnFreeValue function.
Returns: The reference count. If 0, then we deleted it from cache.
Cond: N.b. Decrements the reference count. */ int PUBLIC Cache_DeleteItem( CACHE * pcache, int atomKey, BOOL bNuke, // TRUE to ignore reference count
HWND hwndOwner, PFNFREEVALUE pfnFree) { int nRet = 0; CITEM item; CITEM * pitem; int idpa; int cFree;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { item.atomKey = atomKey; idpa = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED); if (idpa != -1) { VALIDATE_ATOM(atomKey);
pitem = MyGetPtr(pcache, idpa);
if (!bNuke && pitem->ucRef-- > 0) { nRet = pitem->ucRef+1; } else { int iItem;
DPA_DeletePtr(pcache->hdpa, idpa);
// Free old pointer
if (pfnFree != NULL) pfnFree(pitem->pvValue, hwndOwner);
Atom_Delete(pitem->atomKey);
DEBUG_CODE( pitem->atomKey = -1; ) DEBUG_CODE( pitem->pszKey = NULL; ) DEBUG_CODE( pitem->pvValue = NULL; ) DEBUG_CODE( pitem->ucRef = 0; )
// Reset the FindFirst/FindNext in case this gets
// called in the middle of an enumeration.
//
pcache->atomPrev = ATOM_ERR;
// Add ptr to the free list. If this fails, we simply lose
// some efficiency in reusing this portion of the cache.
// This is not a memory leak.
//
cFree = DPA_GetPtrCount(pcache->hdpaFree); iItem = DSA_GetPtrIndex(pcache->hdsa, pitem, sizeof(CITEM)); DPA_InsertPtr(pcache->hdpaFree, cFree+1, IntToPtr(iItem)); } } } } Cache_LeaveCS(pcache);
return nRet; }
/*----------------------------------------------------------
Purpose: Replace the contents of the value in the cache list. If a value does not exist for the given key, return FALSE. Returns: TRUE if success Cond: -- */ BOOL PUBLIC Cache_ReplaceItem( CACHE * pcache, int atomKey, LPVOID pvBuf, int cbBuf) { BOOL bRet = FALSE; CITEM item; CITEM * pitem; int idpa;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { // Search for an existing cache entry
//
item.atomKey = atomKey; idpa = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED);
if (idpa != -1) { // Found a value for this key. Replace the contents.
//
pitem = MyGetPtr(pcache, idpa); ASSERT(pitem);
BltByte(pvBuf, pitem->pvValue, cbBuf); bRet = TRUE;
// No need to sort because key hasn't changed.
} } } Cache_LeaveCS(pcache);
return bRet; }
/*----------------------------------------------------------
Purpose: Get the value of the given key and return a ptr to it Returns: Ptr to actual entry
Cond: Reference count is incremented */ LPVOID PUBLIC Cache_GetPtr( CACHE * pcache, int atomKey) { LPVOID pvRet = NULL; CITEM item; CITEM * pitem; int idpa;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { item.atomKey = atomKey; idpa = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED); if (idpa != -1) { pitem = MyGetPtr(pcache, idpa); ASSERT(pitem);
pitem->ucRef++; pvRet = pitem->pvValue; } } } Cache_LeaveCS(pcache);
return pvRet; }
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Get the current reference count Returns: Ptr to actual entry
Cond: Used for debugging */ UINT PUBLIC Cache_GetRefCount( CACHE * pcache, int atomKey) { UINT ucRef = (UINT)-1; CITEM item; CITEM * pitem; int idpa;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { item.atomKey = atomKey; idpa = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED); if (idpa != -1) { pitem = MyGetPtr(pcache, idpa); ASSERT(pitem);
ucRef = pitem->ucRef; } } } Cache_LeaveCS(pcache);
return ucRef; } #endif
/*----------------------------------------------------------
Purpose: Get the value of the given key and return a copy of it in the supplied buffer Returns: Copy of value in buffer TRUE if found, FALSE if not
Cond: -- */ BOOL PUBLIC Cache_GetItem( CACHE * pcache, int atomKey, LPVOID pvBuf, int cbBuf) { BOOL bRet = FALSE; CITEM item; CITEM * pitem; int idpa;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { item.atomKey = atomKey; idpa = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED); if (idpa != -1) { pitem = MyGetPtr(pcache, idpa); ASSERT(pitem);
BltByte(pvBuf, pitem->pvValue, cbBuf); bRet = TRUE; } } } Cache_LeaveCS(pcache);
return bRet; }
/*----------------------------------------------------------
Purpose: Get the first key in the cache. Returns: Atom ATOM_ERR if cache is empty Cond: -- */ int PUBLIC Cache_FindFirstKey( CACHE * pcache) { int atomRet = ATOM_ERR; CITEM * pitem;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { int i;
pcache->iPrev = 0; if (DPA_GetPtrCount(pcache->hdpa) > 0) { i = PtrToUlong(DPA_FastGetPtr(pcache->hdpa, 0));
pitem = DSA_GetItemPtr(pcache->hdsa, i);
pcache->atomPrev = pitem->atomKey; atomRet = pitem->atomKey;
VALIDATE_ATOM(atomRet); } } } Cache_LeaveCS(pcache);
return atomRet; }
/*----------------------------------------------------------
Purpose: Get the next key in the cache. Returns: Atom ATOM_ERR if we're at the end of the cache Cond: -- */ int PUBLIC Cache_FindNextKey( CACHE * pcache, int atomPrev) { int atomRet = ATOM_ERR; CITEM * pitem;
ASSERT(pcache); Cache_EnterCS(pcache); { if (!Cache_Bogus(pcache)) { if (atomPrev != ATOM_ERR) { int i;
if (atomPrev != pcache->atomPrev) { CITEM item;
// Search for atomPrev or next one nearest to it.
//
item.atomKey = atomPrev; pcache->iPrev = DPA_Search(pcache->hdpa, &item, 0, Cache_Compare, (LPARAM)pcache->hdsa, DPAS_SORTED | DPAS_INSERTBEFORE); } else pcache->iPrev++;
if (DPA_GetPtrCount(pcache->hdpa) > pcache->iPrev) { i = PtrToUlong(DPA_FastGetPtr(pcache->hdpa, pcache->iPrev)); pitem = DSA_GetItemPtr(pcache->hdsa, i);
pcache->atomPrev = pitem->atomKey; atomRet = pitem->atomKey;
VALIDATE_ATOM(atomRet); } } } } Cache_LeaveCS(pcache);
return atomRet; }
|