ICU-11075 Remove dead code including LRUCache and tests and sharedptr.h

X-SVN-Rev: 36226
This commit is contained in:
Travis Keep 2014-08-21 21:02:57 +00:00
parent 6f9dd8c75a
commit 9c4a5db353
11 changed files with 3 additions and 858 deletions

View File

@ -104,7 +104,7 @@ rbbi.o rbbidata.o rbbinode.o rbbirb.o rbbiscan.o rbbisetb.o rbbistbl.o rbbitblb.
serv.o servnotf.o servls.o servlk.o servlkf.o servrbf.o servslkf.o \
uidna.o usprep.o uts46.o punycode.o \
util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o propsvec.o \
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o lrucache.o \
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o \
sharedobject.o simplepatternformatter.o unifiedcache.o
## Header files to install

View File

@ -368,9 +368,6 @@
<ClCompile Include="locresdata.cpp" />
<ClCompile Include="locutil.cpp">
</ClCompile>
<ClCompile Include="lrucache.cpp">
<DisableLanguageExtensions>false</DisableLanguageExtensions>
</ClCompile>
<ClCompile Include="resbund.cpp">
</ClCompile>
<ClCompile Include="resbund_cnv.cpp" />
@ -1069,7 +1066,6 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
</CustomBuild>
<ClInclude Include="locutil.h" />
<ClInclude Include="lrucache.h" />
<CustomBuild Include="unicode\resbund.h">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode
</Command>
@ -1086,7 +1082,6 @@
</CustomBuild>
<ClInclude Include="sharedobject.h" />
<ClCompile Include="sharedobject.cpp" />
<ClInclude Include="sharedptr.h" />
<ClInclude Include="simplepatternformatter.h" />
<CustomBuild Include="unicode\ucat.h">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode

View File

@ -103,9 +103,6 @@
<ClCompile Include="ucol_swp.cpp">
<Filter>collation</Filter>
</ClCompile>
<ClCompile Include="lrucache.cpp">
<Filter>collections</Filter>
</ClCompile>
<ClCompile Include="propsvec.c">
<Filter>collections</Filter>
</ClCompile>
@ -618,9 +615,6 @@
<ClInclude Include="hash.h">
<Filter>collections</Filter>
</ClInclude>
<ClInclude Include="lrucache.h">
<Filter>collections</Filter>
</ClInclude>
<ClInclude Include="propsvec.h">
<Filter>collections</Filter>
</ClInclude>
@ -714,9 +708,6 @@
<ClInclude Include="sharedobject.h">
<Filter>data &amp; memory</Filter>
</ClInclude>
<ClInclude Include="sharedptr.h">
<Filter>data &amp; memory</Filter>
</ClInclude>
<ClInclude Include="ucln.h">
<Filter>data &amp; memory</Filter>
</ClInclude>

View File

@ -1,178 +0,0 @@
/*
******************************************************************************
* 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 = static_cast<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

View File

@ -1,147 +0,0 @@
/*
******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation and
* others. All Rights Reserved.
******************************************************************************
*
* File LRUCACHE.H
******************************************************************************
*/
#ifndef __LRU_CACHE_H__
#define __LRU_CACHE_H__
#include "unicode/uobject.h"
#include "sharedobject.h"
struct UHashtable;
U_NAMESPACE_BEGIN
/**
* A cache of SharedObjects keyed by locale ID.
*
* LRUCache has one main method, get(), which fetches a SharedObject by
* locale ID. If no such SharedObject is cached, get() creates the new
* SharedObject and caches it behind the scenes.
*
* Each LRUCache has a maximum size. Whenever adding a new item to the cache
* would exceed this maximum size, LRUCache evicts the SharedObject that was
* least recently fetched via the get() method.
*
* LRUCache is designed to be subclassed. Subclasses must override the create()
* method to create a new SharedObject by localeId. If only locale ID is
* needed to create the SharedObject, a client can use SimpleLRUCache.
*/
class U_COMMON_API LRUCache : public UObject {
public:
/**
* Fetches a SharedObject by locale ID. On success, get() makes ptr point
* to the fetched SharedObject while automatically updating reference
* counts; on failure, get() leaves ptr unchanged and sets status.
* When get() is called, ptr must either be NULL or be included in the
* reference count of what it points to. After get() returns successfully,
* caller must eventually call removeRef() on ptr to avoid memory leaks.
*
* T must be a subclass of SharedObject.
*/
template<typename T>
void get(const char *localeId, const T *&ptr, UErrorCode &status) {
const T *value = (const T *) _get(localeId, status);
if (U_FAILURE(status)) {
return;
}
SharedObject::copyPtr(value, ptr);
}
/**
* Returns TRUE if a SharedObject for given ID is cached. Used
* primarily for testing purposes.
*/
UBool contains(const char *localeId) const;
virtual ~LRUCache();
protected:
/**
* Subclasses override to create a new SharedObject for given localeID.
* get() calls this to resolve cache misses. create() must either return
* a SharedObject with 0 reference count and no error in status or return
* NULL and set an error in status.
*/
virtual SharedObject *create(const char *localeId, UErrorCode &status)=0;
/**
* Constructor.
* @param maxSize the maximum size of the LRUCache
* @param status any error is set here.
*/
LRUCache(int32_t maxSize, UErrorCode &status);
private:
class CacheEntry : public UMemory {
public:
CacheEntry *moreRecent;
CacheEntry *lessRecent;
char *localeId;
const SharedObject *cachedData;
UErrorCode status; // This is the error if any from creating
// cachedData.
CacheEntry();
~CacheEntry();
void unlink();
void reset();
void init(
char *adoptedLocId, SharedObject *dataToAdopt, UErrorCode err);
private:
CacheEntry(const CacheEntry& other);
CacheEntry &operator=(const CacheEntry& other);
};
LRUCache();
LRUCache(const LRUCache &other);
LRUCache &operator=(const LRUCache &other);
// TODO (Travis Keep): Consider replacing both of these end nodes with a
// single sentinel.
CacheEntry *mostRecentlyUsedMarker;
CacheEntry *leastRecentlyUsedMarker;
UHashtable *localeIdToEntries;
int32_t maxSize;
void moveToMostRecent(CacheEntry *cacheEntry);
void init(char *localeId, CacheEntry *cacheEntry);
const SharedObject *_get(const char *localeId, UErrorCode &status);
};
/**
* A function type that creates a SharedObject from a locale ID. Functions of
* this type must return a SharedObject with 0 reference count and no error in
* status or return NULL and set an error in status.
*/
typedef SharedObject *CreateFunc(const char *localeId, UErrorCode &status);
/**
* A concrete subclass of LRUCache that creates SharedObjects using a
* function of type CreateFunc.
*/
class U_COMMON_API SimpleLRUCache : public LRUCache {
public:
/**
* Constructor.
* @param maxSize the maximum cache size.
* @param cf creates SharedObject on cache miss.
* @param status error reported here.
*/
SimpleLRUCache(
int32_t maxSize,
CreateFunc cf,
UErrorCode &status) :
LRUCache(maxSize, status), createFunc(cf) {
}
virtual ~SimpleLRUCache();
protected:
virtual SharedObject *create(const char *localeId, UErrorCode &status);
private:
CreateFunc *createFunc;
};
U_NAMESPACE_END
#endif

View File

@ -1,225 +0,0 @@
/*
*******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File SHAREDPTR.H
*******************************************************************************
*/
#ifndef __SHARED_PTR_H__
#define __SHARED_PTR_H__
#include "unicode/uobject.h"
#include "umutex.h"
#include "uassert.h"
U_NAMESPACE_BEGIN
// Wrap u_atomic_int32_t in a UMemory so that we allocate them in the same
// way we allocate all other ICU objects.
struct AtomicInt : public UMemory {
u_atomic_int32_t value;
};
/**
* SharedPtr are shared pointers that support copy-on-write sematics.
* SharedPtr makes the act of copying large objects cheap by deferring the
* cost of the copy to the first write operation after the copy.
*
* A SharedPtr<T> instance can refer to no object or an object of type T.
* T must have a clone() method that copies
* the object and returns a pointer to the copy. Copy and assignment of
* SharedPtr instances are cheap because they only involve copying or
* assigning the SharedPtr instance, not the T object which could be large.
* Although many SharedPtr<T> instances may refer to the same T object,
* clients can still assume that each SharedPtr<T> instance has its own
* private instance of T because each SharedPtr<T> instance offers only a
* const view of its T object through normal pointer operations. If a caller
* must change a T object through its SharedPtr<T>, it can do so by calling
* readWrite() on the SharedPtr instance. readWrite() ensures that the
* SharedPtr<T> really does have its own private T object by cloning it if
* it is shared by using its clone() method. SharedPtr<T> instances handle
* management by reference counting their T objects. T objects that are
* referenced by no SharedPtr<T> instances get deleted automatically.
*/
// TODO (Travis Keep): Leave interface the same, but find a more efficient
// implementation that is easier to understand.
template<typename T>
class SharedPtr {
public:
/**
* Constructor. If there is a memory allocation error creating
* reference counter then this object will contain NULL, and adopted
* pointer will be freed. Note that when passing NULL or no argument to
* constructor, no memory allocation error can happen as NULL pointers
* are never reference counted.
*/
explicit SharedPtr(T *adopted=NULL) : ptr(adopted), refPtr(NULL) {
if (ptr != NULL) {
refPtr = new AtomicInt();
if (refPtr == NULL) {
delete ptr;
ptr = NULL;
} else {
refPtr->value = 1;
}
}
}
/**
* Copy constructor.
*/
SharedPtr(const SharedPtr<T> &other) :
ptr(other.ptr), refPtr(other.refPtr) {
if (refPtr != NULL) {
umtx_atomic_inc(&refPtr->value);
}
}
/**
* assignment operator.
*/
SharedPtr<T> &operator=(const SharedPtr<T> &other) {
if (ptr != other.ptr) {
SharedPtr<T> newValue(other);
swap(newValue);
}
return *this;
}
/**
* Destructor.
*/
~SharedPtr() {
if (refPtr != NULL) {
if (umtx_atomic_dec(&refPtr->value) == 0) {
delete ptr;
delete refPtr;
}
}
}
/**
* reset adopts a new pointer. On success, returns TRUE.
* On memory allocation error creating reference counter for adopted
* pointer, returns FALSE while leaving this instance unchanged.
*/
bool reset(T *adopted) {
SharedPtr<T> newValue(adopted);
if (adopted != NULL && newValue.ptr == NULL) {
// We couldn't allocate ref counter.
return FALSE;
}
swap(newValue);
return TRUE;
}
/**
* reset makes this instance refer to no object.
*/
void reset() {
reset(NULL);
}
/**
* count returns how many SharedPtr instances, including this one,
* refer to the T object. Used for testing. Clients need not use in
* practice.
*/
int32_t count() const {
if (refPtr == NULL) {
return 0;
}
return umtx_loadAcquire(refPtr->value);
}
/**
* Swaps this instance with other.
*/
void swap(SharedPtr<T> &other) {
T *tempPtr = other.ptr;
AtomicInt *tempRefPtr = other.refPtr;
other.ptr = ptr;
other.refPtr = refPtr;
ptr = tempPtr;
refPtr = tempRefPtr;
}
const T *operator->() const {
return ptr;
}
const T &operator*() const {
return *ptr;
}
bool operator==(const T *other) const {
return ptr == other;
}
bool operator!=(const T *other) const {
return ptr != other;
}
/**
* readOnly gives const access to this instance's T object. If this
* instance refers to no object, returns NULL.
*/
const T *readOnly() const {
return ptr;
}
/**
* readWrite returns a writable pointer to its T object copying it first
* using its clone() method if it is shared.
* On memory allocation error or if this instance refers to no object,
* this method returns NULL leaving this instance unchanged.
* <p>
* If readWrite() returns a non NULL pointer, it guarantees that this
* object holds the only reference to its T object enabling the caller to
* perform mutations using the returned pointer without affecting other
* SharedPtr objects. However, the non-constness of readWrite continues as
* long as the returned pointer is in scope. Therefore it is an API
* violation to call readWrite() on A; perform B = A; and then proceed to
* mutate A via its writeable pointer as that would be the same as setting
* B = A while A is changing. The returned pointer is guaranteed to be
* valid only while this object is in scope because this object maintains
* ownership of its T object. Therefore, callers must never attempt to
* delete the returned writeable pointer. The best practice with readWrite
* is this: callers should use the returned pointer from readWrite() only
* within the same scope as that call to readWrite, and that scope should
* be made as small as possible avoiding overlap with other operatios on
* this object.
*/
T *readWrite() {
int32_t refCount = count();
if (refCount <= 1) {
return ptr;
}
T *result = (T *) ptr->clone();
if (result == NULL) {
// Memory allocation error
return NULL;
}
if (!reset(result)) {
return NULL;
}
return ptr;
}
private:
T *ptr;
AtomicInt *refPtr;
// No heap allocation. Use only stack.
static void * U_EXPORT2 operator new(size_t size);
static void * U_EXPORT2 operator new[](size_t size);
#if U_HAVE_PLACEMENT_NEW
static void * U_EXPORT2 operator new(size_t, void *ptr);
#endif
};
U_NAMESPACE_END
#endif

View File

@ -57,7 +57,7 @@ uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o uts46te
incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
reldatefmttest.o lrucachetest.o simplepatternformattertest.o measfmttest.o scientificformathelpertest.o numfmtspectest.o unifiedcachetest.o
reldatefmttest.o simplepatternformattertest.o measfmttest.o scientificformathelpertest.o numfmtspectest.o unifiedcachetest.o
DEPS = $(OBJECTS:.o=.d)

View File

@ -309,9 +309,6 @@
<ClCompile Include="itrbnfp.cpp" />
<ClCompile Include="itrbnfrt.cpp" />
<ClCompile Include="locnmtst.cpp" />
<ClCompile Include="lrucachetest.cpp">
<DisableLanguageExtensions>false</DisableLanguageExtensions>
</ClCompile>
<ClCompile Include="measfmttest.cpp" />
<ClCompile Include="miscdtfm.cpp" />
<ClCompile Include="msfmrgts.cpp" />

View File

@ -136,9 +136,6 @@
<ClCompile Include="ucaconf.cpp">
<Filter>collation</Filter>
</ClCompile>
<ClCompile Include="lrucachetest.cpp">
<Filter>collections</Filter>
</ClCompile>
<ClCompile Include="unifiedcachetest.cpp">
<Filter>collections</Filter>
</ClCompile>

View File

@ -33,7 +33,6 @@ extern IntlTest *createBytesTrieTest();
static IntlTest *createLocalPointerTest();
extern IntlTest *createUCharsTrieTest();
static IntlTest *createEnumSetTest();
extern IntlTest *createLRUCacheTest();
extern IntlTest *createSimplePatternFormatterTest();
extern IntlTest *createUnifiedCacheTest();
@ -99,14 +98,6 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
}
break;
case 20:
name = "LRUCacheTest";
if (exec) {
logln("TestSuite LRUCacheTest---"); logln();
LocalPointer<IntlTest> test(createLRUCacheTest());
callTest(*test, par);
}
break;
case 21:
name = "SimplePatternFormatterTest";
if (exec) {
logln("TestSuite SimplePatternFormatterTest---"); logln();
@ -114,7 +105,7 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
callTest(*test, par);
}
break;
case 22:
case 21:
name = "UnifiedCacheTest";
if (exec) {
logln("TestSuite UnifiedCacheTest---"); logln();

View File

@ -1,276 +0,0 @@
/*
*******************************************************************************
* Copyright (C) 2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* File LRUCACHETEST.CPP
*
********************************************************************************
*/
#include "cstring.h"
#include "intltest.h"
#include "lrucache.h"
#include "sharedptr.h"
class CopyOnWriteForTesting : public SharedObject {
public:
CopyOnWriteForTesting() : SharedObject(), localeNamePtr(), formatStrPtr(), length(0) {
}
CopyOnWriteForTesting(const CopyOnWriteForTesting &other) :
SharedObject(other),
localeNamePtr(other.localeNamePtr),
formatStrPtr(other.formatStrPtr),
length(other.length) {
}
virtual ~CopyOnWriteForTesting() {
}
SharedPtr<UnicodeString> localeNamePtr;
SharedPtr<UnicodeString> formatStrPtr;
int32_t length;
private:
CopyOnWriteForTesting &operator=(const CopyOnWriteForTesting &rhs);
};
class LRUCacheForTesting : public LRUCache {
public:
LRUCacheForTesting(
int32_t maxSize,
const UnicodeString &dfs, UErrorCode &status);
virtual ~LRUCacheForTesting() {
}
protected:
virtual SharedObject *create(const char *localeId, UErrorCode &status);
private:
SharedPtr<UnicodeString> defaultFormatStr;
};
LRUCacheForTesting::LRUCacheForTesting(
int32_t maxSize,
const UnicodeString &dfs, UErrorCode &status) :
LRUCache(maxSize, status), defaultFormatStr() {
if (U_FAILURE(status)) {
return;
}
defaultFormatStr.reset(new UnicodeString(dfs));
}
SharedObject *LRUCacheForTesting::create(const char *localeId, UErrorCode &status) {
if (uprv_strcmp(localeId, "error") == 0) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return NULL;
}
CopyOnWriteForTesting *result = new CopyOnWriteForTesting;
result->localeNamePtr.reset(new UnicodeString(localeId));
result->formatStrPtr = defaultFormatStr;
result->length = 5;
return result;
}
class LRUCacheTest : public IntlTest {
public:
LRUCacheTest() {
}
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
private:
void TestSharedPointer();
void TestErrorCallingConstructor();
void TestLRUCache();
void TestLRUCacheError();
void verifySharedPointer(
const CopyOnWriteForTesting* ptr,
const UnicodeString& name,
const UnicodeString& format);
void verifyString(
const UnicodeString &expected, const UnicodeString &actual);
void verifyReferences(
const CopyOnWriteForTesting* ptr,
int32_t count, int32_t nameCount, int32_t formatCount);
};
void LRUCacheTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(TestSharedPointer);
TESTCASE_AUTO(TestErrorCallingConstructor);
TESTCASE_AUTO(TestLRUCache);
TESTCASE_AUTO(TestLRUCacheError);
TESTCASE_AUTO_END;
}
void LRUCacheTest::TestSharedPointer() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting* ptr = NULL;
cache.get("boo", ptr, status);
verifySharedPointer(ptr, "boo", "little");
const CopyOnWriteForTesting* ptrCopy = ptr;
ptrCopy->addRef();
{
const CopyOnWriteForTesting* ptrCopy2(ptrCopy);
ptrCopy2->addRef();
verifyReferences(ptr, 4, 1, 2);
ptrCopy2->removeRef();
}
verifyReferences(ptr, 3, 1, 2);
CopyOnWriteForTesting *wPtrCopy = SharedObject::copyOnWrite(ptrCopy);
*wPtrCopy->localeNamePtr.readWrite() = UnicodeString("hi there");
*wPtrCopy->formatStrPtr.readWrite() = UnicodeString("see you");
verifyReferences(ptr, 2, 1, 2);
verifyReferences(ptrCopy, 1, 1, 1);
verifySharedPointer(ptr, "boo", "little");
verifySharedPointer(ptrCopy, "hi there", "see you");
ptrCopy->removeRef();
ptr->removeRef();
}
void LRUCacheTest::TestErrorCallingConstructor() {
UErrorCode status = U_MEMORY_ALLOCATION_ERROR;
LRUCacheForTesting cache(3, "little", status);
}
void LRUCacheTest::TestLRUCache() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting* ptr1 = NULL;
const CopyOnWriteForTesting* ptr2 = NULL;
const CopyOnWriteForTesting* ptr3 = NULL;
const CopyOnWriteForTesting* ptr4 = NULL;
const CopyOnWriteForTesting* ptr5 = NULL;
cache.get("foo", ptr1, status);
cache.get("bar", ptr2, status);
cache.get("baz", ptr3, status);
verifySharedPointer(ptr1, "foo", "little");
verifySharedPointer(ptr2, "bar", "little");
verifySharedPointer(ptr3, "baz", "little");
// Cache holds a reference to returned data which explains the 2s
// Note the '4'. each cached data has a reference to "little" and the
// cache itself also has a reference to "little"
verifyReferences(ptr1, 2, 1, 4);
verifyReferences(ptr2, 2, 1, 4);
verifyReferences(ptr3, 2, 1, 4);
// (Most recent) "baz", "bar", "foo" (Least Recent)
// Cache is now full but thanks to shared pointers we can still evict.
cache.get("full", ptr4, status);
verifySharedPointer(ptr4, "full", "little");
verifyReferences(ptr4, 2, 1, 5);
// (Most Recent) "full" "baz", "bar" (Least Recent)
cache.get("baz", ptr5, status);
verifySharedPointer(ptr5, "baz", "little");
// ptr5, ptr3, and cache have baz data
verifyReferences(ptr5, 3, 1, 5);
// This should delete foo data since it got evicted from cache.
ptr1->removeRef();
ptr1 = NULL;
// Reference count for little drops to 4 because foo data was deleted.
verifyReferences(ptr5, 3, 1, 4);
// (Most Recent) "baz" "full" "bar" (Least Recent)
cache.get("baz", ptr5, status);
verifySharedPointer(ptr5, "baz", "little");
verifyReferences(ptr5, 3, 1, 4);
// (Most Recent) "baz", "full", "bar" (Least Recent)
// ptr3, ptr5 -> "baz" ptr4 -> "full" ptr2 -> "bar"
if (!cache.contains("baz") || !cache.contains("full") || !cache.contains("bar") || cache.contains("foo")) {
errln("Unexpected keys in cache.");
}
cache.get("new1", ptr5, status);
verifySharedPointer(ptr5, "new1", "little");
verifyReferences(ptr5, 2, 1, 5);
// Since bar was evicted, clearing its pointer should delete its data.
// Notice that the reference count to 'little' dropped from 5 to 4.
ptr2->removeRef();
ptr2 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("bar") || !cache.contains("full")) {
errln("Unexpected 'bar' in cache.");
}
// (Most Recent) "new1", "baz", "full" (Least Recent)
// ptr3 -> "baz" ptr4 -> "full" ptr5 -> "new1"
cache.get("new2", ptr5, status);
verifySharedPointer(ptr5, "new2", "little");
verifyReferences(ptr5, 2, 1, 5);
// since "full" was evicted, clearing its pointer should delete its data.
ptr4->removeRef();
ptr4 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("full") || !cache.contains("baz")) {
errln("Unexpected 'full' in cache.");
}
// (Most Recent) "new2", "new1", "baz" (Least Recent)
// ptr3 -> "baz" ptr5 -> "new2"
cache.get("new3", ptr5, status);
verifySharedPointer(ptr5, "new3", "little");
verifyReferences(ptr5, 2, 1, 5);
// since "baz" was evicted, clearing its pointer should delete its data.
ptr3->removeRef();
ptr3 = NULL;
verifyReferences(ptr5, 2, 1, 4);
if (cache.contains("baz") || !cache.contains("new3")) {
errln("Unexpected 'baz' in cache.");
}
SharedObject::clearPtr(ptr1);
SharedObject::clearPtr(ptr2);
SharedObject::clearPtr(ptr3);
SharedObject::clearPtr(ptr4);
SharedObject::clearPtr(ptr5);
}
void LRUCacheTest::TestLRUCacheError() {
UErrorCode status = U_ZERO_ERROR;
LRUCacheForTesting cache(3, "little", status);
const CopyOnWriteForTesting *ptr1;
cache.get("error", ptr1, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected an error.");
}
}
void LRUCacheTest::verifySharedPointer(
const CopyOnWriteForTesting* ptr,
const UnicodeString& name,
const UnicodeString& format) {
const UnicodeString *strPtr = ptr->localeNamePtr.readOnly();
verifyString(name, *strPtr);
strPtr = ptr->formatStrPtr.readOnly();
verifyString(format, *strPtr);
}
void LRUCacheTest::verifyString(const UnicodeString &expected, const UnicodeString &actual) {
if (expected != actual) {
errln(UnicodeString("Expected '") + expected + "', got '"+ actual+"'");
}
}
void LRUCacheTest::verifyReferences(const CopyOnWriteForTesting* ptr, int32_t count, int32_t nameCount, int32_t formatCount) {
int32_t actual = ptr->getRefCount();
if (count != actual) {
errln("Main reference count wrong: Expected %d, got %d", count, actual);
}
actual = ptr->localeNamePtr.count();
if (nameCount != actual) {
errln("name reference count wrong: Expected %d, got %d", nameCount, actual);
}
actual = ptr->formatStrPtr.count();
if (formatCount != actual) {
errln("format reference count wrong: Expected %d, got %d", formatCount, actual);
}
}
extern IntlTest *createLRUCacheTest() {
return new LRUCacheTest();
}