• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CacheEntry()21 LRUCache::CacheEntry::CacheEntry()
22     : moreRecent(NULL), lessRecent(NULL), localeId(NULL), cachedData(NULL),
23       status(U_ZERO_ERROR) {
24 }
25 
~CacheEntry()26 LRUCache::CacheEntry::~CacheEntry() {
27     reset();
28 }
29 
unlink()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 
reset()41 void LRUCache::CacheEntry::reset() {
42     SharedObject::clearPtr(cachedData);
43     status = U_ZERO_ERROR;
44     uprv_free(localeId);
45     localeId = NULL;
46 }
47 
init(char * adoptedLocId,SharedObject * dataToAdopt,UErrorCode err)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 
moveToMostRecent(CacheEntry * entry)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 
init(char * adoptedLocId,CacheEntry * entry)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 
contains(const char * localeId) const73 UBool LRUCache::contains(const char *localeId) const {
74     return (uhash_get(localeIdToEntries, localeId) != NULL);
75 }
76 
77 
_get(const char * localeId,UErrorCode & status)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 
LRUCache(int32_t size,UErrorCode & status)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 
~LRUCache()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 
~SimpleLRUCache()171 SimpleLRUCache::~SimpleLRUCache() {
172 }
173 
create(const char * localeId,UErrorCode & status)174 SharedObject *SimpleLRUCache::create(const char *localeId, UErrorCode &status) {
175     return createFunc(localeId, status);
176 }
177 
178 U_NAMESPACE_END
179