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