281cbf5106
X-SVN-Rev: 34929
179 lines
5.3 KiB
C++
179 lines
5.3 KiB
C++
/*
|
|
******************************************************************************
|
|
* Copyright (C) 2014, International Business Machines Corporation and
|
|
* others. All Rights Reserved.
|
|
******************************************************************************
|
|
*
|
|
* File LRUCACHE.CPP
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "lrucache.h"
|
|
#include "uhash.h"
|
|
#include "cstring.h"
|
|
#include "uassert.h"
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
// TODO (Travis Keep): Consider building synchronization into this cache
|
|
// instead of leaving synchronization up to the clients.
|
|
|
|
LRUCache::CacheEntry::CacheEntry()
|
|
: moreRecent(NULL), lessRecent(NULL), localeId(NULL), cachedData(NULL),
|
|
status(U_ZERO_ERROR) {
|
|
}
|
|
|
|
LRUCache::CacheEntry::~CacheEntry() {
|
|
reset();
|
|
}
|
|
|
|
void LRUCache::CacheEntry::unlink() {
|
|
if (moreRecent != NULL) {
|
|
moreRecent->lessRecent = lessRecent;
|
|
}
|
|
if (lessRecent != NULL) {
|
|
lessRecent->moreRecent = moreRecent;
|
|
}
|
|
moreRecent = NULL;
|
|
lessRecent = NULL;
|
|
}
|
|
|
|
void LRUCache::CacheEntry::reset() {
|
|
SharedObject::clearPtr(cachedData);
|
|
status = U_ZERO_ERROR;
|
|
uprv_free(localeId);
|
|
localeId = NULL;
|
|
}
|
|
|
|
void LRUCache::CacheEntry::init(
|
|
char *adoptedLocId, SharedObject *dataToAdopt, UErrorCode err) {
|
|
U_ASSERT(localeId == NULL);
|
|
localeId = adoptedLocId;
|
|
SharedObject::copyPtr(dataToAdopt, cachedData);
|
|
status = err;
|
|
}
|
|
|
|
void LRUCache::moveToMostRecent(CacheEntry *entry) {
|
|
if (entry->moreRecent == mostRecentlyUsedMarker) {
|
|
return;
|
|
}
|
|
entry->unlink();
|
|
entry->moreRecent = mostRecentlyUsedMarker;
|
|
entry->lessRecent = mostRecentlyUsedMarker->lessRecent;
|
|
mostRecentlyUsedMarker->lessRecent->moreRecent = entry;
|
|
mostRecentlyUsedMarker->lessRecent = entry;
|
|
}
|
|
|
|
void LRUCache::init(char *adoptedLocId, CacheEntry *entry) {
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
SharedObject *result = create(adoptedLocId, status);
|
|
entry->init(adoptedLocId, result, status);
|
|
}
|
|
|
|
UBool LRUCache::contains(const char *localeId) const {
|
|
return (uhash_get(localeIdToEntries, localeId) != NULL);
|
|
}
|
|
|
|
|
|
const SharedObject *LRUCache::_get(const char *localeId, UErrorCode &status) {
|
|
// TODO (Travis Keep): Consider stripping irrelevant locale keywords.
|
|
if (U_FAILURE(status)) {
|
|
return NULL;
|
|
}
|
|
CacheEntry *entry = (CacheEntry *) uhash_get(
|
|
localeIdToEntries, localeId);
|
|
if (entry == NULL) {
|
|
// Its a cache miss.
|
|
|
|
if (uhash_count(localeIdToEntries) < maxSize) {
|
|
// Cache not full. There is room for a new entry.
|
|
entry = new CacheEntry;
|
|
if (entry == NULL) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return NULL;
|
|
}
|
|
} else {
|
|
// Cache full. Must evict an entry and re-use it.
|
|
entry = leastRecentlyUsedMarker->moreRecent;
|
|
uhash_remove(localeIdToEntries, entry->localeId);
|
|
entry->unlink();
|
|
entry->reset();
|
|
}
|
|
|
|
// entry is an uninitialized, unlinked cache entry
|
|
char *dupLocaleId = uprv_strdup(localeId);
|
|
if (dupLocaleId == NULL) {
|
|
delete entry;
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return NULL;
|
|
}
|
|
init(dupLocaleId, entry);
|
|
|
|
// Entry is initialized, add to hashtable
|
|
uhash_put(localeIdToEntries, entry->localeId, entry, &status);
|
|
if (U_FAILURE(status)) {
|
|
delete entry;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Re-link entry so that it is the most recent.
|
|
moveToMostRecent(entry);
|
|
|
|
if (U_FAILURE(entry->status)) {
|
|
status = entry->status;
|
|
return NULL;
|
|
}
|
|
return entry->cachedData;
|
|
}
|
|
|
|
LRUCache::LRUCache(int32_t size, UErrorCode &status) :
|
|
mostRecentlyUsedMarker(NULL),
|
|
leastRecentlyUsedMarker(NULL),
|
|
localeIdToEntries(NULL),
|
|
maxSize(size) {
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
mostRecentlyUsedMarker = new CacheEntry;
|
|
leastRecentlyUsedMarker = new CacheEntry;
|
|
if (mostRecentlyUsedMarker == NULL || leastRecentlyUsedMarker == NULL) {
|
|
delete mostRecentlyUsedMarker;
|
|
delete leastRecentlyUsedMarker;
|
|
mostRecentlyUsedMarker = leastRecentlyUsedMarker = NULL;
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
mostRecentlyUsedMarker->moreRecent = NULL;
|
|
mostRecentlyUsedMarker->lessRecent = leastRecentlyUsedMarker;
|
|
leastRecentlyUsedMarker->moreRecent = mostRecentlyUsedMarker;
|
|
leastRecentlyUsedMarker->lessRecent = NULL;
|
|
localeIdToEntries = uhash_openSize(
|
|
uhash_hashChars,
|
|
uhash_compareChars,
|
|
NULL,
|
|
maxSize + maxSize / 5,
|
|
&status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
LRUCache::~LRUCache() {
|
|
uhash_close(localeIdToEntries);
|
|
for (CacheEntry *i = mostRecentlyUsedMarker; i != NULL;) {
|
|
CacheEntry *next = i->lessRecent;
|
|
delete i;
|
|
i = next;
|
|
}
|
|
}
|
|
|
|
SimpleLRUCache::~SimpleLRUCache() {
|
|
}
|
|
|
|
SharedObject *SimpleLRUCache::create(const char *localeId, UErrorCode &status) {
|
|
return createFunc(localeId, status);
|
|
}
|
|
|
|
U_NAMESPACE_END
|