Home | History | Annotate | Download | only in common
      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