mux2.0/game/
mux2.0/game/data/
mux2.0/src/tools/
// 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
}