1 /* 2 ****************************************************************************** 3 * Copyright (C) 2014, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ****************************************************************************** 6 * 7 * File LRUCACHE.CPP 8 ****************************************************************************** 9 */ 10 11 #include "lrucache.h" 12 #include "uhash.h" 13 #include "cstring.h" 14 #include "uassert.h" 15 16 U_NAMESPACE_BEGIN 17 18 // TODO (Travis Keep): Consider building synchronization into this cache 19 // instead of leaving synchronization up to the clients. 20 21 LRUCache::CacheEntry::CacheEntry() 22 : moreRecent(NULL), lessRecent(NULL), localeId(NULL), cachedData(NULL), 23 status(U_ZERO_ERROR) { 24 } 25 26 LRUCache::CacheEntry::~CacheEntry() { 27 reset(); 28 } 29 30 void LRUCache::CacheEntry::unlink() { 31 if (moreRecent != NULL) { 32 moreRecent->lessRecent = lessRecent; 33 } 34 if (lessRecent != NULL) { 35 lessRecent->moreRecent = moreRecent; 36 } 37 moreRecent = NULL; 38 lessRecent = NULL; 39 } 40 41 void LRUCache::CacheEntry::reset() { 42 SharedObject::clearPtr(cachedData); 43 status = U_ZERO_ERROR; 44 uprv_free(localeId); 45 localeId = NULL; 46 } 47 48 void LRUCache::CacheEntry::init( 49 char *adoptedLocId, SharedObject *dataToAdopt, UErrorCode err) { 50 U_ASSERT(localeId == NULL); 51 localeId = adoptedLocId; 52 SharedObject::copyPtr(dataToAdopt, cachedData); 53 status = err; 54 } 55 56 void LRUCache::moveToMostRecent(CacheEntry *entry) { 57 if (entry->moreRecent == mostRecentlyUsedMarker) { 58 return; 59 } 60 entry->unlink(); 61 entry->moreRecent = mostRecentlyUsedMarker; 62 entry->lessRecent = mostRecentlyUsedMarker->lessRecent; 63 mostRecentlyUsedMarker->lessRecent->moreRecent = entry; 64 mostRecentlyUsedMarker->lessRecent = entry; 65 } 66 67 void LRUCache::init(char *adoptedLocId, CacheEntry *entry) { 68 UErrorCode status = U_ZERO_ERROR; 69 SharedObject *result = create(adoptedLocId, status); 70 entry->init(adoptedLocId, result, status); 71 } 72 73 UBool LRUCache::contains(const char *localeId) const { 74 return (uhash_get(localeIdToEntries, localeId) != NULL); 75 } 76 77 78 const SharedObject *LRUCache::_get(const char *localeId, UErrorCode &status) { 79 // TODO (Travis Keep): Consider stripping irrelevant locale keywords. 80 if (U_FAILURE(status)) { 81 return NULL; 82 } 83 CacheEntry *entry = static_cast<CacheEntry *>(uhash_get( 84 localeIdToEntries, localeId)); 85 if (entry == NULL) { 86 // Its a cache miss. 87 88 if (uhash_count(localeIdToEntries) < maxSize) { 89 // Cache not full. There is room for a new entry. 90 entry = new CacheEntry; 91 if (entry == NULL) { 92 status = U_MEMORY_ALLOCATION_ERROR; 93 return NULL; 94 } 95 } else { 96 // Cache full. Must evict an entry and re-use it. 97 entry = leastRecentlyUsedMarker->moreRecent; 98 uhash_remove(localeIdToEntries, entry->localeId); 99 entry->unlink(); 100 entry->reset(); 101 } 102 103 // entry is an uninitialized, unlinked cache entry 104 char *dupLocaleId = uprv_strdup(localeId); 105 if (dupLocaleId == NULL) { 106 delete entry; 107 status = U_MEMORY_ALLOCATION_ERROR; 108 return NULL; 109 } 110 init(dupLocaleId, entry); 111 112 // Entry is initialized, add to hashtable 113 uhash_put(localeIdToEntries, entry->localeId, entry, &status); 114 if (U_FAILURE(status)) { 115 delete entry; 116 return NULL; 117 } 118 } 119 120 // Re-link entry so that it is the most recent. 121 moveToMostRecent(entry); 122 123 if (U_FAILURE(entry->status)) { 124 status = entry->status; 125 return NULL; 126 } 127 return entry->cachedData; 128 } 129 130 LRUCache::LRUCache(int32_t size, UErrorCode &status) : 131 mostRecentlyUsedMarker(NULL), 132 leastRecentlyUsedMarker(NULL), 133 localeIdToEntries(NULL), 134 maxSize(size) { 135 if (U_FAILURE(status)) { 136 return; 137 } 138 mostRecentlyUsedMarker = new CacheEntry; 139 leastRecentlyUsedMarker = new CacheEntry; 140 if (mostRecentlyUsedMarker == NULL || leastRecentlyUsedMarker == NULL) { 141 delete mostRecentlyUsedMarker; 142 delete leastRecentlyUsedMarker; 143 mostRecentlyUsedMarker = leastRecentlyUsedMarker = NULL; 144 status = U_MEMORY_ALLOCATION_ERROR; 145 return; 146 } 147 mostRecentlyUsedMarker->moreRecent = NULL; 148 mostRecentlyUsedMarker->lessRecent = leastRecentlyUsedMarker; 149 leastRecentlyUsedMarker->moreRecent = mostRecentlyUsedMarker; 150 leastRecentlyUsedMarker->lessRecent = NULL; 151 localeIdToEntries = uhash_openSize( 152 uhash_hashChars, 153 uhash_compareChars, 154 NULL, 155 maxSize + maxSize / 5, 156 &status); 157 if (U_FAILURE(status)) { 158 return; 159 } 160 } 161 162 LRUCache::~LRUCache() { 163 uhash_close(localeIdToEntries); 164 for (CacheEntry *i = mostRecentlyUsedMarker; i != NULL;) { 165 CacheEntry *next = i->lessRecent; 166 delete i; 167 i = next; 168 } 169 } 170 171 SimpleLRUCache::~SimpleLRUCache() { 172 } 173 174 SharedObject *SimpleLRUCache::create(const char *localeId, UErrorCode &status) { 175 return createFunc(localeId, status); 176 } 177 178 U_NAMESPACE_END 179