zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    loaded some nib but the view outlet was not set
    指标评比
    IOS DEVELOP FOR DUMMIES
    软件测试题二
    javascript select
    DOM节点类型详解
    mysql操作
    UVA 10055
    solutions for 'No Suitable Driver Found For Jdbc'
    解决git中文乱码问题
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/12162157.html
Copyright © 2011-2022 走看看