C語言實現LRU緩存(二)


 

/*
* file name: LRUCache.h
* desp: LRU緩存接口
*/

#ifndef __LRUCACHE_H__
#define __LRUCACHE_H__

int LRUCacheCreate(int capacity, void **lruCache);

int LRUCacheDestroy(void *lruCache);

int LRUCacheSet(void *lruCache, char key, char data);

char LRUCacheGet(void *lruCache, char key);

void LRUCachePrint(void *lruCache);

#endif

 

頭文件描述

/*
LRUCacheImpl.h
定義LRU緩存內部數據結構
*/
#ifndef __LRUCACHEIMPL_H__
#define __LRUCACHEIMPL_H__

/*定義LRU緩存的緩存單元*/
typedef struct cacheEntryS
{
    char key;
    char data;

    struct cacheEntryS *hashListPrev;   /* 緩存哈希表指針,指向哈希鏈表的前一個元素 */
    struct cacheEntryS *hashListNext;   /* 緩存哈希表指針,指向哈希鏈表的后一個元素 */

    struct cacheEntryS *lruListPrev;    /* 緩存雙向鏈表,指向雙向鏈表的前一個元素 */
    struct cacheEntryS *lruListNext;    /* 緩存雙向鏈表,指向雙向鏈表的后一個元素 */
}cacheEntryS;

typedef struct LRUCacheS
{
    int cacheCapacity;
    cacheEntryS **hashMap;  //緩存的hash表

    cacheEntryS *lruListHead;   //緩存雙向鏈表的表頭
    cacheEntryS *lruListTail;   //緩存雙向鏈表表位
    int lruListSize;            //緩存雙向鏈表節點個數
}LRUCacheS;

#endif

 

相關函數介紹

/*
LRUCacheImpl.c
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"LRUCache.h"
#include"LRUCacheImpl.h"

static void freeList(LRUCacheS *cache);

/****************************************************
*LRU緩存及緩存單位相關接口及實現
*****************************************************/

//創建一個緩存單位
static cacheEntryS *newCacheEntry(char key, char data)
{
    cacheEntryS* entry = NULL;
    if (NULL == (entry = malloc(sizeof(*entry))))
    {
        perror("malloc");
        return NULL;
    }

    memset(entry, 0, sizeof(*entry));
    entry->key = key;
    entry->data = data;
    return entry;
}

//釋放一個緩存單元
static void freeCacheEntry(cacheEntryS* entry)
{
    if (NULL == entry)
    {
        return ;
    }
    free(entry);
}

//創建一個LRU緩存
int LRUCacheCreate(int capacity, void **lruCache)
{
    LRUCacheS* cache = NULL;
    if (NULL == (cache = malloc(sizeof(*cache))))
    {
        perror("malloc");
        return -1;
    }

    memset(cache, 0, sizeof(*cache));
    cache->cacheCapacity = capacity;
    cache->hashMap = (cacheEntryS**)malloc(sizeof(cacheEntryS)*capacity);
    if (NULL == cache->hashMap)
    {
        free(cache);
        perror("malloc");
        return -1;
    }

    memset(cache->hashMap, 0, sizeof(cacheEntryS)*capacity);
    *lruCache = cache;
    return 0;
}

//釋放一個LRU緩存
int LRUCacheDestroy(void *lruCache)
{
    LRUCacheS* cache = (LRUCacheS*)lruCache;
    if (NULL == cache)
    {
        return 0;
    }

    if (cache->hashMap)
    {
        free(cache->hashMap);
    }

    freeList(cache);
    free(cache);
    return 0;
}

/****************************************************
* 雙向鏈表相關接口及實現
*****************************************************/

//從雙向鏈表中刪除指定節點
static void removeFromList(LRUCacheS *cache, cacheEntryS* entry)
{
    //鏈表為空
    if (cache->lruListSize == 0)
    {
        return;
    }

    if (entry == cache->lruListHead && entry == cache->lruListTail)
    {
        //當鏈表僅剩當前一個節點
        cache->lruListTail = cache->lruListHead = NULL;
    }
    else if (entry == cache->lruListHead)
    {
        //刪除節點位於表頭
        cache->lruListHead = entry->lruListNext;
        cache->lruListHead->lruListPrev = NULL;
    }
    else if (entry == cache->lruListTail)
    {
        //刪除節點位於表尾
        cache->lruListTail = entry->lruListPrev;
        cache->lruListTail->lruListNext = NULL;
    }
    else
    {
        //非頭非為情況,直接摘抄節點
        entry->lruListPrev->lruListNext = entry->lruListNext;
        entry->lruListNext->lruListPrev = entry->lruListPrev;
    }

    // 
    cache->lruListSize--;
}

//見節點插入鏈表表頭
static cacheEntryS *insertToListHead(LRUCacheS *cache, cacheEntryS* entry)
{
    cacheEntryS *removeEntry = NULL;
    if (++cache->lruListSize > cache->cacheCapacity)
    {
        /* 如果緩存滿了,即鏈表當前節點數已等於緩存容量,那么需要先刪除鏈表表尾節點,即淘汰最久沒有被訪問到的緩存數據單元*/
        removeEntry = cache->lruListTail;
        removeFromList(cache, cache->lruListTail);
    }

    if (cache->lruListHead == NULL && cache->lruListTail == NULL)
    {
        //如果當前倆目標為空鏈表
        cache->lruListHead = cache->lruListTail = entry;
    }
    else
    {
        //當前鏈表非空,插入表頭
        entry->lruListNext = cache->lruListHead;
        entry->lruListPrev = NULL;
        cache->lruListHead->lruListPrev = entry;
        cache->lruListHead = entry;
    }
    return removeEntry;    
}

//釋放整個鏈表
static void freeList(LRUCacheS *cache)
{
    //鏈表為空
    if (0 == cache->lruListSize)
    {
        return;
    }
    cacheEntryS* entry = cache->lruListHead;
    while(entry)
    {
        cacheEntryS *temp = entry->lruListNext;
        freeCacheEntry(entry);
        entry = temp;
    }

    cache->lruListSize = 0;
}


//輔助性接口,用於保證最近訪問的節點總是位於鏈表表頭
static void updateLRUList(LRUCacheS *cache, cacheEntryS *entry)
{
    //摘抄節點
    removeFromList(cache, entry);
    //將節點插入到鏈表表頭
    insertToListHead(cache, entry);
}

/****************************************************
* hash表相關接口及實現
*****************************************************/

//哈希函數
static int hashKey(LRUCacheS *cache, char key)
{
    return (int)key%cache->cacheCapacity;
}

//從哈希表中獲取緩存單元
static cacheEntryS *getValueFromHashMap(LRUCacheS *cache, char key)
{
    //使用函數定位數據存放在哪個槽中
    cacheEntryS *entry = cache->hashMap[hashKey(cache,key)];

    //遍歷槽內鏈表,找到准確的數據項
    while(entry)
    {
        if (entry->key == key)
        {
            break;
        }
        entry = entry->hashListNext;
    }

    return entry;
}


//向hash表中插入緩存單元
static void insertentryToHashMap(LRUCacheS *cache, cacheEntryS *entry)
{
    //使用函數定位數據存放在哪個槽中
    cacheEntryS *n = cache->hashMap[hashKey(cache, entry->key)];
    if (n != NULL)
    {
        //如果槽內已經有其他數據項,將巢內數據項與當前預加入數據項組成鏈表
        //當前預加入數據項為表頭
        entry->hashListNext = n;
        n->hashListPrev = entry;
    }

    //將數據項放到到哈希槽內
    cache->hashMap[hashKey(cache, entry->key)] = entry;
}


//從哈希表中刪除緩存單元
static void removeEntryFromHashMap(LRUCacheS *cache, cacheEntryS *entry)
{
    //無需做任何刪除操作的情況
    if (NULL == entry || NULL == cache || NULL == cache->hashMap)
    {
        return ;
    }

    //定位數據位於哪個槽內
    cacheEntryS *n = cache->hashMap[hashKey(cache, entry->key)];
    //遍歷槽內鏈表,找到與刪除節點,將節點從哈希表摘除
    while(n)
    {
        //找到預刪除節點,將節點從hash表摘除
        if (n->key == entry->key)
        {
            if (n->hashListPrev)
            {
                n->hashListPrev->hashListNext = n->hashListNext;
            }
            else
            {
                cache->hashMap[hashKey(cache, entry->key)] = n->hashListNext;
            }
            
            if (n->hashListNext)
            {
                n->hashListNext->hashListPrev = n->hashListPrev;
            }
        }

        n = n->hashListNext;
    }
}


/****************************************************
* hash表相關接口及實現
*****************************************************/

//將數據放入LRU緩存中
int LRUCacheSet(void *lrucache, char key, char data)
{
    LRUCacheS *cache = (LRUCacheS *)lrucache;
    //從hash表查找數據是否已經在緩存中
    cacheEntryS* entry = getValueFromHashMap(cache, key);
    if (NULL != entry)
    {
        //更新數據,將數據項更新至鏈表表頭
        entry->data = data;
        updateLRUList(cache, entry);
    }
    else    //數據沒在緩存中,新建緩存插入鏈表表頭
    {       
        entry = newCacheEntry(key, data);
        cacheEntryS *removeEntry = insertToListHead(cache, entry);
        if (NULL != removeEntry)
        {
            //緩存滿,淘汰最近最久沒有被訪問到的數據單元
            removeEntryFromHashMap(cache, removeEntry);
            freeCacheEntry(removeEntry);
        }
        insertentryToHashMap(cache, entry);
    }
    return 0;
}

char LRUCacheGet(void *lruCache, char key)
{
    LRUCacheS *cache = (LRUCacheS *)lruCache;
    cacheEntryS *entry = getValueFromHashMap(cache, key);
    //檢查hash緩存是否已經存在數據
    if (NULL != entry)
    {
        //緩存中存在數據,更新至表頭
        updateLRUList(cache, entry);
        return entry->data;
    }
    else
    {
        return '\0';
    }
    
}

void LRUCachePrint(void *lruCache)
{
    LRUCacheS *cache = (LRUCacheS *)lruCache;
    if (NULL == cache || 0 == cache->lruListSize)
    {
        return;
    }

    fprintf(stdout, "\n>>>>>>>>>>>>>>\n");
    fprintf(stdout, "cache (key data):\n");
    cacheEntryS *entry = cache->lruListHead;

    while(entry)
    {
        fprintf(stdout, "(%c, %c) ", entry->key, entry->data);
        entry = entry->lruListNext;
    }
    fprintf(stdout, "\n<<<<<<<<<<<<<<<<<<\n\n");
}

 

測試函數

/*main.c*/
#include<stdlib.h>
#include<stdio.h>
#include"LRUCache.h"

#define HANDLE_ERROR(msg)   \
        do { fprintf(stderr, "%s fail.\n", msg) ;exit(-1);}while(0)
    
#define LRUCACHE_PUTDATA(cache, data)   \
do {\
    if (0 != LRUCacheSet(cache, data, data)) \
        fprintf(stderr, "put (%c,%c) to cache fail.\n", data, data);   \
    else    \
        fprintf(stdout, "put (%c,%c) to cache success.\n", data, data); \
}while(0)

#define LRUCACHE_GETDATA(cache, key)    \
do  \
{   \
    char data = LRUCacheGet(cache, key);    \
    if ('\0' == data)   \
    {   \
        fprintf(stderr, "get  data (Key:%c) from cache fail.\n", key); \
    }   \
    else if (key == data)   \
    {\
        fprintf(stdout, "got (%c,%c) from cache \n", key, data);    \
    }   \
} while (0);

void  testcase1()
{
    fprintf(stdout, "=========================\n");
    fprintf(stdout, "In testcase1....\n");
    fprintf(stdout, "=========================\n");
    void *lruCache;
    if (0 != LRUCacheCreate(5, &lruCache)) 
        HANDLE_ERROR("LRUCacheCreate");
    /*ABC!*/
    LRUCACHE_PUTDATA(lruCache, 'A');
    LRUCACHE_GETDATA(lruCache, 'A');
    LRUCACHE_PUTDATA(lruCache, 'B');
    LRUCACHE_GETDATA(lruCache, 'B');
    LRUCACHE_PUTDATA(lruCache, 'C');
    LRUCACHE_GETDATA(lruCache, 'C');
    LRUCachePrint(lruCache);/*CBA*/

    /*DEAF!*/
    LRUCACHE_PUTDATA(lruCache, 'D');
    LRUCACHE_GETDATA(lruCache, 'D');
    LRUCACHE_PUTDATA(lruCache, 'E');
    LRUCACHE_GETDATA(lruCache, 'E');
    LRUCACHE_PUTDATA(lruCache, 'A');
    LRUCACHE_GETDATA(lruCache, 'A');
    LRUCACHE_PUTDATA(lruCache, 'F');
    LRUCACHE_GETDATA(lruCache, 'F');
    LRUCachePrint(lruCache); /*FAEDC*/

    /*B!*/
    LRUCACHE_PUTDATA(lruCache, 'F');
    LRUCACHE_GETDATA(lruCache, 'F');
    LRUCachePrint(lruCache); /*FAEDC*/

    if (0 != LRUCacheDestroy(lruCache))
        HANDLE_ERROR("LRUCacheDestroy");
    fprintf(stdout, "\n\ntestcase1 finished\n");
    fprintf(stdout, "=========================\n\n");   
}
 
void testcase2(void)
{
    fprintf(stdout, "=========================\n");
    fprintf(stdout, "In testcase2....\n");
    fprintf(stdout, "=========================\n");
    void *lruCache;
    if (0 != LRUCacheCreate(3, &lruCache)) 
        HANDLE_ERROR("LRUCacheCreate");

    /*WXWYZ!*/
    LRUCACHE_PUTDATA(lruCache, 'W');
    LRUCACHE_PUTDATA(lruCache, 'X');
    LRUCACHE_PUTDATA(lruCache, 'W');
    LRUCACHE_PUTDATA(lruCache, 'Y');
    LRUCACHE_PUTDATA(lruCache, 'Z');
    LRUCachePrint(lruCache);/*ZYW*/

    LRUCACHE_GETDATA(lruCache, 'Z');
    LRUCACHE_GETDATA(lruCache, 'Y');
    LRUCACHE_GETDATA(lruCache, 'W');
    LRUCACHE_GETDATA(lruCache, 'X');
    LRUCACHE_GETDATA(lruCache, 'W');
    LRUCachePrint(lruCache);/*WYZ*/

    /*YZWYX!*/
    LRUCACHE_PUTDATA(lruCache, 'Y');
    LRUCACHE_PUTDATA(lruCache, 'Z');
    LRUCACHE_PUTDATA(lruCache, 'W');
    LRUCACHE_PUTDATA(lruCache, 'Y');
    LRUCACHE_PUTDATA(lruCache, 'X');
    LRUCachePrint(lruCache); /*XYW*/


    LRUCACHE_GETDATA(lruCache, 'X');
    LRUCACHE_GETDATA(lruCache, 'Y');
    LRUCACHE_GETDATA(lruCache, 'W');
    LRUCACHE_GETDATA(lruCache, 'Z');
    LRUCACHE_GETDATA(lruCache, 'Y');
    LRUCachePrint(lruCache); /*WYX*/

    /*XYXY!*/
    LRUCACHE_PUTDATA(lruCache, 'X');
    LRUCACHE_PUTDATA(lruCache, 'Y');
    LRUCACHE_PUTDATA(lruCache, 'X');
    LRUCACHE_PUTDATA(lruCache, 'Y');
    LRUCachePrint(lruCache);/*YX*/

    LRUCACHE_GETDATA(lruCache, 'Y');
    LRUCACHE_GETDATA(lruCache, 'X');
    LRUCACHE_GETDATA(lruCache, 'Y');
    LRUCACHE_GETDATA(lruCache, 'X');
    LRUCachePrint(lruCache); /*XY*/

    if (0 != LRUCacheDestroy(lruCache))
        HANDLE_ERROR("LRUCacheDestroy");
    fprintf(stdout, "\n\ntestcase2 finished\n");
    fprintf(stdout, "=========================\n\n");
}

  
void testcase3(void)
{
    fprintf(stdout, "=========================\n");
    fprintf(stdout, "In testcase3....\n");
    fprintf(stdout, "=========================\n");

    void *lruCache;
    if (0 != LRUCacheCreate(5, &lruCache)) 
        HANDLE_ERROR("LRUCacheCreate");

    /*EIEIO!*/
    LRUCACHE_PUTDATA(lruCache, 'E');
    LRUCACHE_PUTDATA(lruCache, 'I');
    LRUCACHE_PUTDATA(lruCache, 'E');
    LRUCACHE_PUTDATA(lruCache, 'I');
    LRUCACHE_PUTDATA(lruCache, 'O');
    LRUCachePrint(lruCache);/*OIE*/


    LRUCACHE_GETDATA(lruCache, 'A');
    LRUCACHE_GETDATA(lruCache, 'I');
    LRUCACHE_GETDATA(lruCache, 'B');
    LRUCACHE_GETDATA(lruCache, 'O');
    LRUCACHE_GETDATA(lruCache, 'C');
    LRUCACHE_PUTDATA(lruCache, 'E');
    LRUCachePrint(lruCache); /*EOI*/

    if (0 != LRUCacheDestroy(lruCache))
        HANDLE_ERROR("LRUCacheDestroy");
    fprintf(stdout, "\n\ntestcase3 finished\n");
    fprintf(stdout, "=========================\n\n");
}


int main()
{
    testcase1();
    testcase2();
    testcase3();
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM