// svdocache.cpp -- Attribute caching module
//
// $Id: attrcache.cpp,v 1.5 2000/06/03 04:18:24 sdennis Exp $
//
// MUX 2.0
// Copyright (C) 1998 through 2000 Solid Vertical Domains, Ltd. All
// rights not explicitly given are reserved. Permission is given to
// use this code for building and hosting text-based game servers.
// Permission is given to use this code for other non-commercial
// purposes. To use this code for commercial purposes other than
// building/hosting text-based game servers, contact the author at
// Stephen Dennis <sdennis@svdltd.com> for another license.
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include "mudconf.h"
CHashFile hfAttributeFile;
static int cache_initted = FALSE;
#ifdef STANDALONE
static int cache_redirected = FALSE;
#define N_TEMP_FILES 4
FILE *TempFiles[N_TEMP_FILES];
#endif
CLinearTimeAbsolute cs_ltime;
#pragma pack(1)
typedef struct tagAttrRecord
{
Aname attrKey;
char attrText[LBUF_SIZE];
} ATTR_RECORD, *PATTR_RECORD;
#pragma pack()
static ATTR_RECORD TempRecord;
#ifndef STANDALONE
#define DO_CACHEING
#endif
#ifdef DO_CACHEING
typedef struct tagCacheEntryHeader
{
struct tagCacheEntryHeader *pPrevEntry;
struct tagCacheEntryHeader *pNextEntry;
Aname attrKey;
unsigned int nSize;
} CENT_HDR, *PCENT_HDR;
PCENT_HDR pCacheHead = 0;
PCENT_HDR pCacheTail = 0;
unsigned int CacheSize = 0;
#endif // DO_CACHEING
int cache_init(const char *game_dir_file, const char *game_pag_file)
{
if (cache_initted)
{
return HF_OPEN_STATUS_ERROR;
}
int cc = hfAttributeFile.Open(game_dir_file, game_pag_file);
if (cc != HF_OPEN_STATUS_ERROR)
{
// Mark caching system live
//
cache_initted = TRUE;
cs_ltime.GetUTC();
}
return cc;
}
#ifdef STANDALONE
void cache_redirect(void)
{
for (int i = 0; i < N_TEMP_FILES; i++)
{
char TempFileName[20];
sprintf(TempFileName, "$convtemp.%d", i);
TempFiles[i] = fopen(TempFileName, "wb+");
Tiny_Assert(TempFiles[i]);
setvbuf(TempFiles[i], NULL, _IOFBF, 16384);
}
cache_redirected = TRUE;
}
void cache_pass2(void)
{
cache_redirected = FALSE;
fprintf(stderr, "2nd Pass:\n");
for (int i = 0; i < N_TEMP_FILES; i++)
{
fprintf(stderr, "File %d: ", i);
fseek(TempFiles[i], 0, SEEK_SET);
int cnt = 1000;
int nSize;
for (;;)
{
int cc = fread(&nSize, 1, sizeof(nSize), TempFiles[i]);
if (cc != sizeof(nSize))
{
break;
}
ATTR_RECORD Record;
fread(&Record, 1, nSize, TempFiles[i]);
cache_put(&Record.attrKey, Record.attrText, nSize - sizeof(Aname));
if (cnt-- == 0)
{
fputc('.', stderr);
fflush(stderr);
cnt = 1000;
}
}
fclose(TempFiles[i]);
char TempFileName[20];
sprintf(TempFileName, "$convtemp.%d", i);
RemoveFile(TempFileName);
fprintf(stderr, "\n");
}
}
#endif
void cache_close(void)
{
hfAttributeFile.CloseAll();
cache_initted = FALSE;
}
void cache_tick(void)
{
hfAttributeFile.Tick();
}
#ifdef DO_CACHEING
void REMOVE_ENTRY(PCENT_HDR pEntry)
{
// How is X positioned?
//
if (pEntry == pCacheHead)
{
if (pEntry == pCacheTail)
{
// HEAD --> X --> 0
// 0 <-- <-- TAIL
//
// ASSERT: pEntry->pNextEntry == 0;
// ASSERT: pEntry->pPrevEntry == 0;
//
pCacheHead = pCacheTail = 0;
}
else
{
// HEAD --> X --> Y --> 0
// 0 <-- <-- <-- TAIL
//
// ASSERT: pEntry->pNextEntry != 0;
// ASSERT: pEntry->pPrevEntry == 0;
//
pCacheHead = pEntry->pNextEntry;
pCacheHead->pPrevEntry = 0;
pEntry->pNextEntry = 0;
}
}
else if (pEntry == pCacheTail)
{
// HEAD --> Y --> X --> 0
// 0 <-- <-- <-- TAIL
//
// ASSERT: pEntry->pNextEntry == 0;
// ASSERT: pEntry->pPrevEntry != 0;
//
pCacheTail = pEntry->pPrevEntry;
pCacheTail->pNextEntry = 0;
pEntry->pPrevEntry = 0;
}
else
{
// HEAD --> Y --> X --> Z --> 0
// 0 <-- <-- <-- <-- TAIL
//
// ASSERT: pEntry->pNextEntry != 0;
// ASSERT: pEntry->pNextEntry != 0;
//
pEntry->pNextEntry->pPrevEntry = pEntry->pPrevEntry;
pEntry->pPrevEntry->pNextEntry = pEntry->pNextEntry;
pEntry->pNextEntry = 0;
pEntry->pPrevEntry = 0;
}
}
void ADD_ENTRY(PCENT_HDR pEntry)
{
if (pCacheHead)
{
pCacheHead->pPrevEntry = pEntry;
}
pEntry->pNextEntry = pCacheHead;
pEntry->pPrevEntry = 0;
pCacheHead = pEntry;
if (!pCacheTail)
{
pCacheTail = pCacheHead;
}
}
#endif // DO_CACHEING
char *cache_get(Aname *nam, int *pLen)
{
if (nam == (Aname *) 0 || !cache_initted)
{
*pLen = 0;
return 0;
}
#ifdef DO_CACHEING
// Check the cache, first.
//
PCENT_HDR pCacheEntry = (PCENT_HDR)hashfindLEN((char *)nam, sizeof(Aname), &mudstate.acache_htab);
if (pCacheEntry)
{
// It was in the cache, so move this entry to the head of the queue.
// and return a pointer to it.
//
REMOVE_ENTRY(pCacheEntry);
ADD_ENTRY(pCacheEntry);
*pLen = pCacheEntry->nSize - sizeof(CENT_HDR);
return (char *)(pCacheEntry+1);
}
#endif // DO_CACHEING
unsigned long nHash = CRC32_ProcessInteger2(nam->object, nam->attrnum);
HP_DIRINDEX iDir;
iDir = hfAttributeFile.FindFirstKey(nHash);
while (iDir != HF_FIND_END)
{
HP_HEAPLENGTH nRecord;
hfAttributeFile.Copy(iDir, &nRecord, &TempRecord);
if ((TempRecord.attrKey.attrnum == nam->attrnum) && (TempRecord.attrKey.object == nam->object))
{
int nLength = nRecord - sizeof(Aname);
*pLen = nLength;
#ifdef DO_CACHEING
// Add this information to the cache.
//
pCacheEntry = (PCENT_HDR)MEMALLOC(sizeof(CENT_HDR)+nLength);
if (pCacheEntry)
{
pCacheEntry->attrKey = *nam;
pCacheEntry->nSize = nLength + sizeof(CENT_HDR);
CacheSize += pCacheEntry->nSize;
memcpy((char *)(pCacheEntry+1), TempRecord.attrText, nLength);
ADD_ENTRY(pCacheEntry);
hashaddLEN((char *)nam, sizeof(Aname), (int *)pCacheEntry, &mudstate.acache_htab);
// Check to see if the cache needs to be trimmed.
//
while (CacheSize > mudconf.max_cache_size)
{
// Blow something away.
//
pCacheEntry = pCacheTail;
if (!pCacheEntry)
{
CacheSize = 0;
break;
}
REMOVE_ENTRY(pCacheEntry);
CacheSize -= pCacheEntry->nSize;
hashdeleteLEN((char *)&(pCacheEntry->attrKey), sizeof(Aname), &mudstate.acache_htab);
MEMFREE(pCacheEntry);
}
}
#endif // DO_CACHEING
return TempRecord.attrText;
}
iDir = hfAttributeFile.FindNextKey(iDir, nHash);
}
// We didn't find that one.
//
*pLen = 0;
return 0;
}
// cache_put not longer frees the pointer.
//
BOOL cache_put(Aname *nam, char *value, int len)
{
if (!value || !nam || !cache_initted)
{
return FALSE;
}
if (len > (int)sizeof(TempRecord.attrText))
{
len = sizeof(TempRecord.attrText);
}
// Removal from DB.
//
unsigned long nHash = CRC32_ProcessInteger2(nam->object, nam->attrnum);
#ifdef STANDALONE
if (cache_redirected)
{
TempRecord.attrKey = *nam;
memcpy(TempRecord.attrText, value, len);
TempRecord.attrText[len-1] = '\0';
int iFile = (N_TEMP_FILES-1) & (nHash >> 30);
int nSize = len+sizeof(Aname);
fwrite(&nSize, 1, sizeof(nSize), TempFiles[iFile]);
fwrite(&TempRecord, 1, nSize, TempFiles[iFile]);
return TRUE;
}
#endif
HP_DIRINDEX iDir = hfAttributeFile.FindFirstKey(nHash);
while (iDir != HF_FIND_END)
{
HP_HEAPLENGTH nRecord;
hfAttributeFile.Copy(iDir, &nRecord, &TempRecord);
if ((TempRecord.attrKey.attrnum == nam->attrnum) && (TempRecord.attrKey.object == nam->object))
{
hfAttributeFile.Remove(iDir);
}
iDir = hfAttributeFile.FindNextKey(iDir, nHash);
}
TempRecord.attrKey = *nam;
memcpy(TempRecord.attrText, value, len);
TempRecord.attrText[len-1] = '\0';
// Insertion into DB.
//
hfAttributeFile.Insert(len+sizeof(Aname), nHash, &TempRecord);
#ifdef DO_CACHEING
// Update cache.
//
PCENT_HDR pCacheEntry = (PCENT_HDR)hashfindLEN((char *)nam, sizeof(Aname), &mudstate.acache_htab);
if (pCacheEntry)
{
// It was in the cache, so delete it.
//
REMOVE_ENTRY(pCacheEntry);
CacheSize -= pCacheEntry->nSize;
hashdeleteLEN((char *)nam, sizeof(Aname), &mudstate.acache_htab);
MEMFREE(pCacheEntry);
}
// Add information about the new entry back into the cache.
//
pCacheEntry = (PCENT_HDR)MEMALLOC(sizeof(CENT_HDR)+len);
if (pCacheEntry)
{
pCacheEntry->attrKey = *nam;
pCacheEntry->nSize = len + sizeof(CENT_HDR);
CacheSize += pCacheEntry->nSize;
memcpy((char *)(pCacheEntry+1), TempRecord.attrText, len);
ADD_ENTRY(pCacheEntry);
hashaddLEN((char *)nam, sizeof(Aname), (int *)pCacheEntry, &mudstate.acache_htab);
// Check to see if the cache needs to be trimmed.
//
while (CacheSize > mudconf.max_cache_size)
{
// Blow something away.
//
pCacheEntry = pCacheTail;
if (!pCacheEntry)
{
CacheSize = 0;
break;
}
REMOVE_ENTRY(pCacheEntry);
CacheSize -= pCacheEntry->nSize;
hashdeleteLEN((char *)&(pCacheEntry->attrKey), sizeof(Aname), &mudstate.acache_htab);
MEMFREE(pCacheEntry);
}
}
#endif // DO_CACHEING
return TRUE;
}
BOOL cache_sync(void)
{
hfAttributeFile.Sync();
return TRUE;
}
/*
* Delete this attribute from the database.
*/
void cache_del(Aname *nam)
{
if (!nam || !cache_initted)
return;
unsigned long nHash = CRC32_ProcessInteger2(nam->object, nam->attrnum);
HP_DIRINDEX iDir = hfAttributeFile.FindFirstKey(nHash);
while (iDir != HF_FIND_END)
{
HP_HEAPLENGTH nRecord;
hfAttributeFile.Copy(iDir, &nRecord, &TempRecord);
if ((TempRecord.attrKey.attrnum == nam->attrnum) && (TempRecord.attrKey.object == nam->object))
{
hfAttributeFile.Remove(iDir);
}
iDir = hfAttributeFile.FindNextKey(iDir, nHash);
}
#ifdef DO_CACHEING
// Update cache.
//
PCENT_HDR pCacheEntry = (PCENT_HDR)hashfindLEN((char *)nam, sizeof(Aname), &mudstate.acache_htab);
if (pCacheEntry)
{
// It was in the cache, so delete it.
//
REMOVE_ENTRY(pCacheEntry);
CacheSize -= pCacheEntry->nSize;;
hashdeleteLEN((char *)nam, sizeof(Aname), &mudstate.acache_htab);
MEMFREE(pCacheEntry);
}
#endif // DO_CACHEING
}