NT4/private/windows/rover/filesync/syncui/cache.c

662 lines
17 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
//---------------------------------------------------------------------------
//
// 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, (int)DPA_FastGetPtr((this)->hdpa, idpa))
#define DSA_GetPtrIndex(hdsa, ptr, cbItem) \
((int)( (DWORD)(ptr) - (DWORD)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 = (int)lpv1;
int i2 = (int)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 = (int)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);
InitializeCriticalSection(&pcache->cs);
}
/*----------------------------------------------------------
Purpose: Destroy the cache
Returns: --
Cond: --
*/
void PUBLIC Cache_Term(
CACHE * pcache,
HWND hwndOwner,
PFNFREEVALUE pfnFree)
{
ASSERT(pcache);
ASSERT(!IsBadCodePtr((PROC)pfnFree));
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 (!IsBadCodePtr((PROC)pfnFree))
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 = (int)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, (LPVOID)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, (LPVOID)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);
ASSERT(!IsBadCodePtr((PROC)pfnFree));
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 (!IsBadCodePtr((PROC)pfnFree))
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, (LPVOID)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 = (int)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 = (int)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;
}