• 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;
    }
  • 相关阅读:
    C++文件读写详解(ofstream,ifstream,fstream)
    C++ char*,const char*,string,int 的相互转换
    Properties --- C++读配置信息的类
    值得推荐的C/C++框架和库
    leetcode 264: Ugly Number II
    几种Tab的实现方法
    HBase数据存储格式
    粗略。。Java项目设计模式之笔记----studying
    开放的平台、向上的文化——揭秘万达电商(4)
    RecyclerView
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/12162157.html
Copyright © 2020-2023  润新知