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