// --TopCache.C----------------------------------------------------------------
//
// This module contains functions for maintaining and using a topic cache for
// SMBAGENT.
//
// See TOPCACHE.H for details on how the cache works and the relationship
// between the STFolderArray, the STopicCache, and the STopic objects.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include "edk.h"
// Cause protected functions to be available.
#define FRIEND_OF_STOPIC
#define FRIEND_OF_STOPICCACHE
#include "smbagent.h"
#include "TopCache.CHK"
// -----------------------------------------------------------------------------
// This object contains a cache of opened topics. The functions whose
// names that begin with "STopicCache_" work on this object.
// -----------------------------------------------------------------------------
static STopicCache TopicCache;
//$--STopicCache_Search---------------------------------------------------------
// Search the cache for a folder with the given index. Note that items in the
// cache are always changing slots. What we are looking for is a topic object
// whose folder index matches the given index.
//
// RETURNS: The index in the cache of the topic or NOT_FOUND.
// -----------------------------------------------------------------------------
static ULONG STopicCache_Search(
IN ULONG iTFolderArray) // Index into TFolderArray of topic we want.
{
ULONG ii = 0;
// Search for the folder in the cache.
for( ii = 0; ii < TopicCache.cTotItems; ii ++)
{
if( STopic_GetFolderIndex( TopicCache.lpTopicArray + ii) == iTFolderArray)
return( ii);
}
return( NOT_FOUND);
}
//$--STopicCache_MoveSlotToTop--------------------------------------------------
// Move the item in the topic iSlot to the top of the cache and all other items
// down.
// -----------------------------------------------------------------------------
static void STopicCache_MoveSlotToTop(
IN ULONG iSlot) // Index of the slot to move to the top.
{
STopic TempTopic;
// Do we really need to move something?
if( iSlot)
{ // YES, so move slot from its present location to the top.
// Copy the item in the iSlot into a temporary buffer.
memmove( &TempTopic, TopicCache.lpTopicArray + iSlot, sizeof( STopic));
// Move the first items down until iSlot is filled.
memmove( TopicCache.lpTopicArray + 1, TopicCache.lpTopicArray, sizeof( STopic) * iSlot);
// Copy the temporary buffer back into the top of the cache.
memmove( TopicCache.lpTopicArray, &TempTopic, sizeof( STopic));
}
// Increment the usage count of the first item in the cache.
STopic_IncUsageCnt( TopicCache.lpTopicArray);
}
//$--STopicCache_FreeFirstTopicSlot---------------------------------------------
// Frees the first slot in the cache to be used for a new topic.
// RETURNS: a pointer to the first slot in the cache.
// -----------------------------------------------------------------------------
PROTECTED STopic* STopicCache_FreeFirstTopicSlot()
{
ULONG ii = 0;
ULONG iSlot = NOT_FOUND;
ULONG cLowestCnt= MAX_ULONG;
ULONG cUsageCnt = 0;
// First check to see if the cache is full.
if( TopicCache.cTotItems < TopicCache.cMaxItems)
{ // Cache is not full so move the first empty slot to the top and return it.
STopicCache_MoveSlotToTop( TopicCache.cTotItems);
TopicCache.cTotItems ++;
return( TopicCache.lpTopicArray);
}
// Start at the drop zone of the cache and search to the end for the slot
// whose item has had the least use. If more than one item has the same
// low usage count then the one with the highest index will be choosen.
for( ii = TopicCache.iDropZone; ii < TopicCache.cTotItems; ii++)
{
cUsageCnt = STopic_GetUsageCnt( TopicCache.lpTopicArray + ii);
if( cUsageCnt <= cLowestCnt)
{ // Found the lowest usage and highest index up to this point.
iSlot = ii;
cLowestCnt = cUsageCnt;
}
}
// Release and free everything associated with the topic.
STopic_Release( TopicCache.lpTopicArray + iSlot);
// Move the now empty item from iSlot to the top of the cache
// and everything else down until it fills iSlot.
STopicCache_MoveSlotToTop( iSlot);
// Decrement the count of the last item in the cache. This prevents an item
// that got a burst of activity, accumulating a high usage count, and then was
// never used again from remaining in the cache forever.
STopic_DecUsageCnt( TopicCache.lpTopicArray + TopicCache.cTotItems - 1);
// Return the pointer to the first slot.
return( TopicCache.lpTopicArray);
}
//$--STopicCache_AdjustIndex----------------------------------------------------
// Adjust the index of all topic objects that contain an index that is greater
// than or equal to iTFolderArray. This is used when a new folder in inserted
// into or deleted from the middle of the folder array.
// -----------------------------------------------------------------------------
PROTECTED void STopicCache_AdjustIndex(
IN ULONG iTFolderArray, // Index into TFolderArray of topic we want.
IN int nAddSub) // +1 indicates insert and -1 delete folder.
{
ULONG ii = 0;
ULONG Index = 0;
for( ii = 0; ii < TopicCache.cTotItems; ii++)
{
Index = STopic_GetFolderIndex( TopicCache.lpTopicArray + ii);
if( Index >= iTFolderArray)
STopic_SetFolderIndex( TopicCache.lpTopicArray + ii, Index + nAddSub);
}
}
//$--STopicCache_DeleteTopic----------------------------------------------------
// Remove the item from the cache and adjust the index in the topic cache that
// references any index >= iTFolderArray.
// -----------------------------------------------------------------------------
PROTECTED void STopicCache_DeleteTopic(
IN ULONG iTFolderArray) // Index into TFolderArray of topic we want to delete.
{
STopic* lpTopic = NULL;
ULONG iSlot = 0;
iSlot = STopicCache_Search( iTFolderArray);
if( iSlot != NOT_FOUND)
{ // The folder was found in the cache.
lpTopic = TopicCache.lpTopicArray + iSlot;
// Release all interfaces and free all buffers associated with this topic.
STopic_Release( lpTopic);
// There is now one less item in the cache.
TopicCache.cTotItems --;
// Make sure we really need to move something.
if( iSlot < TopicCache.cTotItems)
{ // YEP, we need to move cached items up to fill this now empty space.
memmove( lpTopic, lpTopic + 1, sizeof( STopic) * (TopicCache.cTotItems - iSlot));
// Initialize the last item to an empty state.
STopic_Init( TopicCache.lpTopicArray + TopicCache.cTotItems);
}
}
// Adjust all other folder indexes in the cache to reflect the deleted folder.
STopicCache_AdjustIndex( iTFolderArray, -1);
}
//$--STopicCache_HrInit---------------------------------------------------------
// Initialize the topic cache to an empty state.
// -----------------------------------------------------------------------------
HRESULT STopicCache_HrInit(
IN ULONG cMaxItems, // The maximum number of topics the cache will hold.
IN ULONG iDropZone) // The starting index at which items become available
{ // for removal from the cache.
HRESULT hr = NOERROR;
ULONG cBufSize = 0;
ULONG ii = 0;
DEBUGPUBLIC( "STopicCache_HrInit()");
hr = CHK_STopicCache_HrInit( cMaxItems, iDropZone);
if( FAILED( hr))
RETURN( hr);
TopicCache.cMaxItems = cMaxItems;
TopicCache.cTotItems = 0;
TopicCache.iDropZone = iDropZone;
// Allocate memory for the entire topic array.
cBufSize = sizeof( STopic) * cMaxItems;
hr = MAPIAllocateBuffer( cBufSize, &TopicCache.lpTopicArray);
if( FAILED(hr) || !TEST_WRITE_PTR( TopicCache.lpTopicArray, cBufSize))
{
hr = HR_LOG( E_OUTOFMEMORY);
goto cleanup;
}
// Initialize each item of array.
for( ii = 0; ii < cMaxItems; ii ++)
STopic_Init( TopicCache.lpTopicArray + ii);
cleanup:
RETURN( hr);
}
//$--STopicCache_ReleaseAll-----------------------------------------------------
// Release all topic items.
// -----------------------------------------------------------------------------
void STopicCache_ReleaseAll()
{
ULONG ii = 0;
for( ii = 0; ii < TopicCache.cTotItems; ii ++)
STopic_Release( TopicCache.lpTopicArray + ii);
TopicCache.cTotItems = 0;
}
//$--STopicCache_GetTopic-------------------------------------------------------
// This function gets the topic associated with the folder specified by the
// iTFolderArray index. If the topic is in the cache it moves it to the top and
// then returns it, otherwise it performs all the operations necessary to open
// the topic.
//
// NOTE: The data pointed to by the returned pointer will be different once this
// function is called again for a new pointer. In this implementation
// it will always return a pointer to the first slot in the cache, but
// don't depend upon it since the implementation could change.
//
// RETURNS: a pointer to the STopic object.
// -----------------------------------------------------------------------------
HRESULT STopicCache_HrGetTopic(
IN ULONG iTFolderArray, // Index into TFolderArray of topic we want.
OUT STopic** lppTopic) // We return the topic ptr in this var.
{
HRESULT hr = NOERROR;
ULONG iSlot = 0;
DEBUGPUBLIC( "STopicCache_HrGetTopic()");
hr = CHK_STopicCache_HrGetTopic( iTFolderArray, lppTopic);
if( FAILED( hr))
RETURN( hr);
// Search for the topic folder in the cache.
iSlot = STopicCache_Search( iTFolderArray);
if( iSlot == NOT_FOUND)
{ // Not found so we need to create an item in the cache for this topic.
STopicCache_FreeFirstTopicSlot();
}
else
{ // Found the topic folder in the cache so move it to the top.
STopicCache_MoveSlotToTop( iSlot);
}
// Load only the topic member objects that are not currently loaded.
hr = STopic_HrOpen( TopicCache.lpTopicArray, iTFolderArray);
if( FAILED( hr))
*lppTopic = NULL;
else
*lppTopic = TopicCache.lpTopicArray;
return( hr);
}
// -----------------------------------------------------------------------------