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