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 '';
    }
    
}

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

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

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

");
}

测试函数

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

#define HANDLE_ERROR(msg)   
        do { fprintf(stderr, "%s fail.
", 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.
", data, data);   
    else    
        fprintf(stdout, "put (%c,%c) to cache success.
", data, data); 
}while(0)

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

void  testcase1()
{
    fprintf(stdout, "=========================
");
    fprintf(stdout, "In testcase1....
");
    fprintf(stdout, "=========================
");
    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, "

testcase1 finished
");
    fprintf(stdout, "=========================

");   
}
 
void testcase2(void)
{
    fprintf(stdout, "=========================
");
    fprintf(stdout, "In testcase2....
");
    fprintf(stdout, "=========================
");
    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, "

testcase2 finished
");
    fprintf(stdout, "=========================

");
}

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

    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, "

testcase3 finished
");
    fprintf(stdout, "=========================

");
}


int main()
{
    testcase1();
    testcase2();
    testcase3();
    return 0;
}
原文地址:https://www.cnblogs.com/wanghao-boke/p/12162157.html