From 53ababf5a7a7be66b7716c46aabdcb871c6b604b Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Mon, 10 Feb 2014 19:13:50 +0000 Subject: [PATCH] ICU-10461 Make NumberFormat::createInstance() and PluralRules::forLocale() 10X faster than before for most common types. Provide internal createSharedInstance() for NumberFormat and SharedFormat for internal use that is 2 orders of magnitude faster than before. X-SVN-Rev: 35114 --- .gitattributes | 2 + icu4c/source/i18n/i18n.vcxproj | 2 + icu4c/source/i18n/i18n.vcxproj.filters | 6 + icu4c/source/i18n/measfmt.cpp | 323 +++++++++------- icu4c/source/i18n/numfmt.cpp | 92 ++++- icu4c/source/i18n/plurrule.cpp | 104 ++++++ icu4c/source/i18n/reldatefmt.cpp | 344 ++++++++---------- icu4c/source/i18n/sharednumberformat.h | 34 ++ icu4c/source/i18n/sharedpluralrules.h | 34 ++ icu4c/source/i18n/tmutfmt.cpp | 10 +- icu4c/source/i18n/unicode/measfmt.h | 27 +- icu4c/source/i18n/unicode/numfmt.h | 26 ++ icu4c/source/i18n/unicode/plurrule.h | 22 +- icu4c/source/i18n/unicode/reldatefmt.h | 10 +- icu4c/source/test/intltest/measfmttest.cpp | 25 ++ icu4c/source/test/intltest/reldatefmttest.cpp | 12 +- 16 files changed, 735 insertions(+), 338 deletions(-) create mode 100644 icu4c/source/i18n/sharednumberformat.h create mode 100644 icu4c/source/i18n/sharedpluralrules.h diff --git a/.gitattributes b/.gitattributes index 75b75b81c88..03d5dd15d6b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -80,6 +80,8 @@ icu4c/source/i18n/i18n.vcxproj.filters -text icu4c/source/i18n/measunit.cpp -text icu4c/source/i18n/quantityformatter.cpp -text icu4c/source/i18n/quantityformatter.h -text +icu4c/source/i18n/sharednumberformat.h -text +icu4c/source/i18n/sharedpluralrules.h -text icu4c/source/io/io.vcxproj -text icu4c/source/io/io.vcxproj.filters -text icu4c/source/layout/layout.vcxproj -text diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index 09ec2fecf31..39ebd67eb72 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -1115,6 +1115,8 @@ + + copy "%(FullPath)" ..\..\include\unicode diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 643603c8431..e6dd664b1bc 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -644,6 +644,12 @@ formatting + + formatting + + + formatting + formatting diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp index 3fd41e4074d..aed1ed66f76 100644 --- a/icu4c/source/i18n/measfmt.cpp +++ b/icu4c/source/i18n/measfmt.cpp @@ -30,10 +30,12 @@ #include "unicode/putil.h" #include "unicode/smpdtfmt.h" -#include "sharedptr.h" +#include "sharednumberformat.h" +#include "sharedpluralrules.h" #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) #define MEAS_UNIT_COUNT 46 +#define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1) static icu::LRUCache *gCache = NULL; static UMutex gCacheMutex = U_MUTEX_INITIALIZER; @@ -52,20 +54,20 @@ U_CDECL_END U_NAMESPACE_BEGIN -class UnitFormatters : public UMemory { -public: - UnitFormatters() { } - QuantityFormatter formatters[MEAS_UNIT_COUNT][UMEASFMT_WIDTH_NARROW + 1]; -private: - UnitFormatters(const UnitFormatters &other); - UnitFormatters &operator=(const UnitFormatters &other); -}; - +// Used to format durations like 5:47 or 21:35:42. class NumericDateFormatters : public UMemory { public: + // Formats like H:mm SimpleDateFormat hourMinute; + + // formats like M:ss SimpleDateFormat minuteSecond; + + // formats like H:mm:ss SimpleDateFormat hourMinuteSecond; + + // Constructor that takes the actual patterns for hour-minute, + // minute-second, and hour-minute-second respectively. NumericDateFormatters( const UnicodeString &hm, const UnicodeString &ms, @@ -84,24 +86,51 @@ private: NumericDateFormatters &operator=(const NumericDateFormatters &other); }; -class MeasureFormatData : public SharedObject { +// Instances contain all MeasureFormat specific data for a particular locale. +// This data is cached. It is never copied, but is shared via shared pointers. +class MeasureFormatCacheData : public SharedObject { public: - SharedPtr unitFormatters; - SharedPtr pluralRules; - SharedPtr numberFormat; - SharedPtr currencyFormats[UMEASFMT_WIDTH_NARROW + 1]; - SharedPtr numericDateFormatters; - virtual ~MeasureFormatData(); + QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; + MeasureFormatCacheData(); + void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { + delete currencyFormats[widthIndex]; + currencyFormats[widthIndex] = nfToAdopt; + } + const NumberFormat *getCurrencyFormat(int32_t widthIndex) const { + return currencyFormats[widthIndex]; + } + void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { + delete numericDateFormatters; + numericDateFormatters = formattersToAdopt; + } + const NumericDateFormatters *getNumericDateFormatters() const { + return numericDateFormatters; + } + virtual ~MeasureFormatCacheData(); private: - MeasureFormatData &operator=(const MeasureFormatData& other); + NumberFormat *currencyFormats[WIDTH_INDEX_COUNT]; + NumericDateFormatters *numericDateFormatters; + MeasureFormatCacheData(const MeasureFormatCacheData &other); + MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); }; -MeasureFormatData::~MeasureFormatData() { +MeasureFormatCacheData::MeasureFormatCacheData() { + for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) { + currencyFormats[i] = NULL; + } + numericDateFormatters = NULL; +} + +MeasureFormatCacheData::~MeasureFormatCacheData() { + for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) { + delete currencyFormats[i]; + } + delete numericDateFormatters; } static int32_t widthToIndex(UMeasureFormatWidth width) { - if (width > UMEASFMT_WIDTH_NARROW) { - return UMEASFMT_WIDTH_NARROW; + if (width >= WIDTH_INDEX_COUNT) { + return WIDTH_INDEX_COUNT - 1; } return width; } @@ -124,9 +153,9 @@ static UBool getString( } -static UBool load( +static UBool loadMeasureUnitData( const UResourceBundle *resource, - UnitFormatters &unitFormatters, + MeasureFormatCacheData &cacheData, UErrorCode &status) { if (U_FAILURE(status)) { return FALSE; @@ -144,7 +173,7 @@ static UBool load( } unitCount = MeasureUnit::getAvailable(units, unitCount, status); } - for (int32_t currentWidth = 0; currentWidth <= UMEASFMT_WIDTH_NARROW; ++currentWidth) { + for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentWidth) { // Be sure status is clear since next resource bundle lookup may fail. if (U_FAILURE(status)) { delete [] units; @@ -198,7 +227,7 @@ static UBool load( } UnicodeString rawPattern; getString(pluralBundle.getAlias(), rawPattern, status); - unitFormatters.formatters[units[currentUnit].getIndex()][currentWidth].add( + cacheData.formatters[units[currentUnit].getIndex()][currentWidth].add( ures_getKey(pluralBundle.getAlias()), rawPattern, status); @@ -260,6 +289,7 @@ static NumericDateFormatters *loadNumericDateFormatters( return result; } +// Creates the MeasureFormatCacheData for a particular locale static SharedObject *U_CALLCONV createData( const char *localeId, UErrorCode &status) { LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status)); @@ -268,66 +298,30 @@ static SharedObject *U_CALLCONV createData( if (U_FAILURE(status)) { return NULL; } - LocalPointer result(new MeasureFormatData()); - LocalPointer unitFormatters(new UnitFormatters()); - if (result.getAlias() == NULL - || unitFormatters.getAlias() == NULL) { + LocalPointer result(new MeasureFormatCacheData()); + if (result.getAlias() == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - if (!load( + if (!loadMeasureUnitData( topLevel.getAlias(), - *unitFormatters, + *result, status)) { return NULL; } - if (!result->unitFormatters.reset(unitFormatters.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - LocalPointer ndf( - loadNumericDateFormatters(topLevel.getAlias(), status)); + result->adoptNumericDateFormatters(loadNumericDateFormatters( + topLevel.getAlias(), status)); if (U_FAILURE(status)) { return NULL; } - if (!result->numericDateFormatters.reset(ndf.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - LocalPointer pr(PluralRules::forLocale(localeId, status)); - if (U_FAILURE(status)) { - return NULL; - } - if (!result->pluralRules.reset(pr.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - LocalPointer nf( - NumberFormat::createInstance(localeId, status)); - if (U_FAILURE(status)) { - return NULL; - } - if (!result->numberFormat.reset(nf.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - for (int32_t i = 0; i <= UMEASFMT_WIDTH_NARROW; ++i) { - LocalPointer cf( - NumberFormat::createInstance( - localeId, currencyStyles[i], status)); + for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { + result->adoptCurrencyFormat(i, NumberFormat::createInstance( + localeId, currencyStyles[i], status)); if (U_FAILURE(status)) { return NULL; } - if (!result->currencyFormats[i].reset(cf.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } } - return result.orphan(); } @@ -342,16 +336,17 @@ static void U_CALLCONV cacheInit(UErrorCode &status) { } } -static void getFromCache( +static UBool getFromCache( const char *locale, - const MeasureFormatData *&ptr, + const MeasureFormatCacheData *&ptr, UErrorCode &status) { umtx_initOnce(gCacheInitOnce, &cacheInit, status); if (U_FAILURE(status)) { - return; + return FALSE; } Mutex lock(&gCacheMutex); gCache->get(locale, ptr, status); + return U_SUCCESS(status); } static int32_t toHMS( @@ -405,8 +400,12 @@ static int32_t toHMS( MeasureFormat::MeasureFormat( const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) - : ptr(NULL), width(w) { - initMeasureFormat(locale, w, status); + : cache(NULL), + numberFormat(NULL), + pluralRules(NULL), + width(w), + listFormatter(NULL) { + initMeasureFormat(locale, w, NULL, status); } MeasureFormat::MeasureFormat( @@ -414,14 +413,25 @@ MeasureFormat::MeasureFormat( UMeasureFormatWidth w, NumberFormat *nfToAdopt, UErrorCode &status) - : ptr(NULL), width(w) { - initMeasureFormat(locale, w, status); - adoptNumberFormat(nfToAdopt, status); + : cache(NULL), + numberFormat(NULL), + pluralRules(NULL), + width(w), + listFormatter(NULL) { + initMeasureFormat(locale, w, nfToAdopt, status); } -MeasureFormat::MeasureFormat(const MeasureFormat &other) - : Format(other), ptr(other.ptr), width(other.width) { - ptr->addRef(); +MeasureFormat::MeasureFormat(const MeasureFormat &other) : + Format(other), + cache(other.cache), + numberFormat(other.numberFormat), + pluralRules(other.pluralRules), + width(other.width), + listFormatter(NULL) { + cache->addRef(); + numberFormat->addRef(); + pluralRules->addRef(); + listFormatter = new ListFormatter(*other.listFormatter); } MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { @@ -429,18 +439,34 @@ MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { return *this; } Format::operator=(other); - SharedObject::copyPtr(other.ptr, ptr); + SharedObject::copyPtr(other.cache, cache); + SharedObject::copyPtr(other.numberFormat, numberFormat); + SharedObject::copyPtr(other.pluralRules, pluralRules); width = other.width; + delete listFormatter; + listFormatter = new ListFormatter(*other.listFormatter); return *this; } -MeasureFormat::MeasureFormat() : ptr(NULL), width(UMEASFMT_WIDTH_WIDE) { +MeasureFormat::MeasureFormat() : + cache(NULL), + numberFormat(NULL), + pluralRules(NULL), + width(UMEASFMT_WIDTH_WIDE), + listFormatter(NULL) { } MeasureFormat::~MeasureFormat() { - if (ptr != NULL) { - ptr->removeRef(); + if (cache != NULL) { + cache->removeRef(); } + if (numberFormat != NULL) { + numberFormat->removeRef(); + } + if (pluralRules != NULL) { + pluralRules->removeRef(); + } + delete listFormatter; } UBool MeasureFormat::operator==(const Format &other) const { @@ -448,6 +474,10 @@ UBool MeasureFormat::operator==(const Format &other) const { if (rhs == NULL) { return FALSE; } + + // Note: Since the ListFormatter depends only on Locale and width, we + // don't have to check it here. + // Same objects are equivalent if (this == rhs) { return TRUE; @@ -456,21 +486,24 @@ UBool MeasureFormat::operator==(const Format &other) const { if (width != rhs->width) { return FALSE; } - // Width the same, same shared data -> equivlanet - if (ptr == rhs->ptr) { - return TRUE; + // Width the same check locales. + // We don't need to check locales if both objects have same cache. + if (cache != rhs->cache) { + UErrorCode status = U_ZERO_ERROR; + const char *localeId = getLocaleID(status); + const char *rhsLocaleId = rhs->getLocaleID(status); + if (U_FAILURE(status)) { + // On failure, assume not equal + return FALSE; + } + if (uprv_strcmp(localeId, rhsLocaleId) != 0) { + return FALSE; + } } - // Width same, but differing shred data. Depends on locale - // and number format objects being the same. - UErrorCode status = U_ZERO_ERROR; - const char *localeId = getLocaleID(status); - const char *rhsLocaleId = rhs->getLocaleID(status); - if (U_FAILURE(status)) { - // On failure, assume not equal - return FALSE; - } - return (uprv_strcmp(localeId, rhsLocaleId) == 0 - && *ptr->numberFormat == *rhs->ptr->numberFormat); + // Locales same, check NumberFormat if shared data differs. + return ( + numberFormat == rhs->numberFormat || + **numberFormat == **rhs->numberFormat); } Format *MeasureFormat::clone() const { @@ -507,7 +540,6 @@ UnicodeString &MeasureFormat::formatMeasures( UnicodeString &appendTo, FieldPosition &pos, UErrorCode &status) const { - static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"}; if (U_FAILURE(status)) { return appendTo; } @@ -524,17 +556,9 @@ UnicodeString &MeasureFormat::formatMeasures( return formatNumeric(hms, bitMap, appendTo, status); } } - LocalPointer lf( - ListFormatter::createInstance( - getLocale(status), - listStyles[widthToIndex(width)], - status)); - if (U_FAILURE(status)) { - return appendTo; - } if (pos.getField() != FieldPosition::DONT_CARE) { return formatMeasuresSlowTrack( - measures, measureCount, *lf, appendTo, pos, status); + measures, measureCount, appendTo, pos, status); } UnicodeString *results = new UnicodeString[measureCount]; if (results == NULL) { @@ -544,51 +568,86 @@ UnicodeString &MeasureFormat::formatMeasures( for (int32_t i = 0; i < measureCount; ++i) { formatMeasure(measures[i], results[i], pos, status); } - lf->format(results, measureCount, appendTo, status); + listFormatter->format(results, measureCount, appendTo, status); delete [] results; return appendTo; } void MeasureFormat::initMeasureFormat( - const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) { + const Locale &locale, + UMeasureFormatWidth w, + NumberFormat *nfToAdopt, + UErrorCode &status) { + static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"}; if (U_FAILURE(status)) { return; } const char *name = locale.getName(); setLocaleIDs(name, name); - width = w; - getFromCache(name, ptr, status); -} -void MeasureFormat::adoptNumberFormat(NumberFormat *nfToAdopt, UErrorCode &status) { + if (!getFromCache(name, cache, status)) { + return; + } + + SharedObject::copyPtr( + PluralRules::createSharedInstance( + locale, UPLURAL_TYPE_CARDINAL, status), + pluralRules); if (U_FAILURE(status)) { return; } - MeasureFormatData* wptr = SharedObject::copyOnWrite(ptr); - if (wptr == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + pluralRules->removeRef(); + if (nfToAdopt == NULL) { + SharedObject::copyPtr( + NumberFormat::createSharedInstance( + locale, UNUM_DECIMAL, status), + numberFormat); + if (U_FAILURE(status)) { + return; + } + numberFormat->removeRef(); + } else { + adoptNumberFormat(nfToAdopt, status); + if (U_FAILURE(status)) { + return; + } + } + width = w; + delete listFormatter; + listFormatter = ListFormatter::createInstance( + locale, + listStyles[widthToIndex(width)], + status); +} + +void MeasureFormat::adoptNumberFormat( + NumberFormat *nfToAdopt, UErrorCode &status) { + if (U_FAILURE(status)) { return; } - if (!wptr->numberFormat.reset(nfToAdopt)) { + SharedNumberFormat *shared = new SharedNumberFormat(nfToAdopt); + if (shared == NULL) { status = U_MEMORY_ALLOCATION_ERROR; + delete nfToAdopt; return; } + SharedObject::copyPtr(shared, numberFormat); } UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { if (U_FAILURE(status) || locale == getLocale(status)) { return FALSE; } - initMeasureFormat(locale, width, status); + initMeasureFormat(locale, width, NULL, status); return U_SUCCESS(status); } const NumberFormat &MeasureFormat::getNumberFormat() const { - return *ptr->numberFormat; + return **numberFormat; } const PluralRules &MeasureFormat::getPluralRules() const { - return *ptr->pluralRules; + return **pluralRules; } Locale MeasureFormat::getLocale(UErrorCode &status) const { @@ -612,7 +671,7 @@ UnicodeString &MeasureFormat::formatMeasure( if (isCurrency(amtUnit)) { UChar isoCode[4]; u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); - return ptr->currencyFormats[widthToIndex(width)]->format( + return cache->getCurrencyFormat(widthToIndex(width))->format( new CurrencyAmount(amtNumber, isoCode, status), appendTo, pos, @@ -625,8 +684,9 @@ UnicodeString &MeasureFormat::formatMeasure( } return quantityFormatter->format( amtNumber, - *ptr->numberFormat, - *ptr->pluralRules, appendTo, + **numberFormat, + **pluralRules, + appendTo, pos, status); } @@ -648,7 +708,7 @@ UnicodeString &MeasureFormat::formatNumeric( case 7: // hms return formatNumeric( millis, - ptr->numericDateFormatters->hourMinuteSecond, + cache->getNumericDateFormatters()->hourMinuteSecond, UDAT_SECOND_FIELD, hms[2], appendTo, @@ -657,7 +717,7 @@ UnicodeString &MeasureFormat::formatNumeric( case 6: // ms return formatNumeric( millis, - ptr->numericDateFormatters->minuteSecond, + cache->getNumericDateFormatters()->minuteSecond, UDAT_SECOND_FIELD, hms[2], appendTo, @@ -666,7 +726,7 @@ UnicodeString &MeasureFormat::formatNumeric( case 3: // hm return formatNumeric( millis, - ptr->numericDateFormatters->hourMinute, + cache->getNumericDateFormatters()->hourMinute, UDAT_MINUTE_FIELD, hms[1], appendTo, @@ -691,7 +751,7 @@ UnicodeString &MeasureFormat::formatNumeric( return appendTo; } UnicodeString smallestAmountFormatted; - ptr->numberFormat->format( + (*numberFormat)->format( smallestAmount, smallestAmountFormatted, status); FieldPosition smallestFieldPosition(smallestField); UnicodeString draft; @@ -718,7 +778,7 @@ const QuantityFormatter *MeasureFormat::getQuantityFormatter( return NULL; } const QuantityFormatter *formatters = - ptr->unitFormatters->formatters[index]; + cache->formatters[index]; if (formatters[widthIndex].isValid()) { return &formatters[widthIndex]; } @@ -735,7 +795,6 @@ const QuantityFormatter *MeasureFormat::getQuantityFormatter( UnicodeString &MeasureFormat::formatMeasuresSlowTrack( const Measure *measures, int32_t measureCount, - const ListFormatter& lf, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const { @@ -761,7 +820,7 @@ UnicodeString &MeasureFormat::formatMeasuresSlowTrack( } } int32_t offset; - lf.format( + listFormatter->format( results, measureCount, appendTo, diff --git a/icu4c/source/i18n/numfmt.cpp b/icu4c/source/i18n/numfmt.cpp index 52220978041..95ea05c41b0 100644 --- a/icu4c/source/i18n/numfmt.cpp +++ b/icu4c/source/i18n/numfmt.cpp @@ -51,6 +51,8 @@ #include "mutex.h" #include "digitlst.h" #include +#include "sharednumberformat.h" +#include "lrucache.h" //#define FMT_DEBUG @@ -139,6 +141,10 @@ static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = { "currencyFormat" // UNUM_CURRENCY_PLURAL }; +static icu::LRUCache *gNumberFormatCache = NULL; +static UMutex gNumberFormatCacheMutex = U_MUTEX_INITIALIZER; +static icu::UInitOnce gNumberFormatCacheInitOnce = U_INITONCE_INITIALIZER; + // Static hashtable cache of NumberingSystem objects used by NumberFormat static UHashtable * NumberingSystem_cache = NULL; static UMutex nscacheMutex = U_MUTEX_INITIALIZER; @@ -172,7 +178,11 @@ static UBool U_CALLCONV numfmt_cleanup(void) { uhash_close(NumberingSystem_cache); NumberingSystem_cache = NULL; } - + gNumberFormatCacheInitOnce.reset(); + if (gNumberFormatCache) { + delete gNumberFormatCache; + gNumberFormatCache = NULL; + } return TRUE; } U_CDECL_END @@ -234,6 +244,10 @@ NumberFormat::~NumberFormat() { } +SharedNumberFormat::~SharedNumberFormat() { + delete ptr; +} + // ------------------------------------- // copy constructor @@ -1008,8 +1022,8 @@ NumberFormat::getAvailableLocales(void) #endif /* UCONFIG_NO_SERVICE */ // ------------------------------------- -NumberFormat* U_EXPORT2 -NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { +NumberFormat* +NumberFormat::internalCreateInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { #if !UCONFIG_NO_SERVICE if (haveService()) { return (NumberFormat*)gService->get(loc, kind, status); @@ -1018,6 +1032,23 @@ NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorC return makeInstance(loc, kind, status); } +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (kind != UNUM_DECIMAL) { + return internalCreateInstance(loc, kind, status); + } + const SharedNumberFormat *shared = createSharedInstance(loc, kind, status); + if (U_FAILURE(status)) { + return NULL; + } + NumberFormat *result = (NumberFormat *) (*shared)->clone(); + shared->removeRef(); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + // ------------------------------------- // Checks if the thousand/10 thousand grouping is used in the @@ -1205,6 +1236,61 @@ static void U_CALLCONV nscacheInit() { uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem); } +static SharedObject *U_CALLCONV createSharedNumberFormat( + const char *localeId, UErrorCode &status) { + if (U_FAILURE(status)) { + return NULL; + } + NumberFormat *nf = NumberFormat::internalCreateInstance( + localeId, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return NULL; + } + SharedObject *result = new SharedNumberFormat(nf); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete nf; + return NULL; + } + return result; +} + +static void U_CALLCONV numberFormatCacheInit(UErrorCode &status) { + U_ASSERT(gNumberFormatCache == NULL); + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + gNumberFormatCache = new SimpleLRUCache(100, &createSharedNumberFormat, status); + if (U_FAILURE(status)) { + delete gNumberFormatCache; + gNumberFormatCache = NULL; + } +} + +static void getSharedNumberFormatFromCache( + const char *locale, + const SharedNumberFormat *&ptr, + UErrorCode &status) { + umtx_initOnce(gNumberFormatCacheInitOnce, &numberFormatCacheInit, status); + if (U_FAILURE(status)) { + return; + } + Mutex lock(&gNumberFormatCacheMutex); + gNumberFormatCache->get(locale, ptr, status); +} + +const SharedNumberFormat* U_EXPORT2 +NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (U_FAILURE(status)) { + return NULL; + } + if (kind != UNUM_DECIMAL) { + status = U_UNSUPPORTED_ERROR; + return NULL; + } + const SharedNumberFormat *result = NULL; + getSharedNumberFormatFromCache(loc.getName(), result, status); + return result; +} + UBool NumberFormat::isStyleSupported(UNumberFormatStyle style) { return gLastResortNumberPatterns[style] != NULL; diff --git a/icu4c/source/i18n/plurrule.cpp b/icu4c/source/i18n/plurrule.cpp index 3ef05972f96..2984bc19f18 100644 --- a/icu4c/source/i18n/plurrule.cpp +++ b/icu4c/source/i18n/plurrule.cpp @@ -29,9 +29,26 @@ #include "ustrfmt.h" #include "uassert.h" #include "uvectr32.h" +#include "sharedpluralrules.h" +#include "lrucache.h" #if !UCONFIG_NO_FORMATTING +static icu::LRUCache *gPluralRulesCache = NULL; +static UMutex gPluralRulesCacheMutex = U_MUTEX_INITIALIZER; +static icu::UInitOnce gPluralRulesCacheInitOnce = U_INITONCE_INITIALIZER; + +U_CDECL_BEGIN +static UBool U_CALLCONV plurrules_cleanup(void) { + gPluralRulesCacheInitOnce.reset(); + if (gPluralRulesCache) { + delete gPluralRulesCache; + gPluralRulesCache = NULL; + } + return TRUE; +} +U_CDECL_END + U_NAMESPACE_BEGIN #define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0]) @@ -73,6 +90,10 @@ PluralRules::~PluralRules() { delete mRules; } +SharedPluralRules::~SharedPluralRules() { + delete ptr; +} + PluralRules* PluralRules::clone() const { return new PluralRules(*this); @@ -131,6 +152,71 @@ PluralRules::createDefaultRules(UErrorCode& status) { return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status); } +/******************************************************************************/ +/* Create PluralRules cache */ + +static SharedObject *U_CALLCONV createSharedPluralRules( + const char *localeId, UErrorCode &status) { + if (U_FAILURE(status)) { + return NULL; + } + PluralRules *pr = PluralRules::internalForLocale( + localeId, UPLURAL_TYPE_CARDINAL, status); + if (U_FAILURE(status)) { + return NULL; + } + SharedObject *result = new SharedPluralRules(pr); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete pr; + return NULL; + } + return result; +} + +static void U_CALLCONV pluralRulesCacheInit(UErrorCode &status) { + U_ASSERT(gPluralRulesCache == NULL); + ucln_i18n_registerCleanup(UCLN_I18N_PLURAL_RULE, plurrules_cleanup); + gPluralRulesCache = new SimpleLRUCache(100, &createSharedPluralRules, status); + if (U_FAILURE(status)) { + delete gPluralRulesCache; + gPluralRulesCache = NULL; + } +} + +static void getSharedPluralRulesFromCache( + const char *locale, + const SharedPluralRules *&ptr, + UErrorCode &status) { + umtx_initOnce(gPluralRulesCacheInitOnce, &pluralRulesCacheInit, status); + if (U_FAILURE(status)) { + return; + } + Mutex lock(&gPluralRulesCacheMutex); + gPluralRulesCache->get(locale, ptr, status); +} + + + + +/* end plural rules cache */ +/******************************************************************************/ + +const SharedPluralRules* U_EXPORT2 +PluralRules::createSharedInstance( + const Locale& locale, UPluralType type, UErrorCode& status) { + if (U_FAILURE(status)) { + return NULL; + } + if (type != UPLURAL_TYPE_CARDINAL) { + status = U_UNSUPPORTED_ERROR; + return NULL; + } + const SharedPluralRules *result = NULL; + getSharedPluralRulesFromCache(locale.getName(), result, status); + return result; +} + PluralRules* U_EXPORT2 PluralRules::forLocale(const Locale& locale, UErrorCode& status) { return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); @@ -138,6 +224,24 @@ PluralRules::forLocale(const Locale& locale, UErrorCode& status) { PluralRules* U_EXPORT2 PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { + if (type != UPLURAL_TYPE_CARDINAL) { + return internalForLocale(locale, type, status); + } + const SharedPluralRules *shared = createSharedInstance( + locale, type, status); + if (U_FAILURE(status)) { + return NULL; + } + PluralRules *result = (*shared)->clone(); + shared->removeRef(); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + +PluralRules* U_EXPORT2 +PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } diff --git a/icu4c/source/i18n/reldatefmt.cpp b/icu4c/source/i18n/reldatefmt.cpp index 1b26cd91b6d..0e09b0dcfd1 100644 --- a/icu4c/source/i18n/reldatefmt.cpp +++ b/icu4c/source/i18n/reldatefmt.cpp @@ -27,6 +27,8 @@ #include "charstr.h" #include "sharedptr.h" +#include "sharedpluralrules.h" +#include "sharednumberformat.h" // Copied from uscript_props.cpp #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) @@ -48,37 +50,35 @@ U_CDECL_END U_NAMESPACE_BEGIN -class QualitativeUnits : public UMemory { +// RelativeDateTimeFormatter specific data for a single locale +class RelativeDateTimeCacheData: public SharedObject { public: - QualitativeUnits() { } - UnicodeString data[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; + RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { } + virtual ~RelativeDateTimeCacheData(); + + // no numbers: e.g Next Tuesday; Yesterday; etc. + UnicodeString absoluteUnits[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; + + // has numbers: e.g Next Tuesday; Yesterday; etc. For second index, 0 + // means past e.g 5 days ago; 1 means future e.g in 5 days. + QuantityFormatter relativeUnits[UDAT_RELATIVE_UNIT_COUNT][2]; + + void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) { + delete combinedDateAndTime; + combinedDateAndTime = mfToAdopt; + } + const MessageFormat *getCombinedDateAndTime() const { + return combinedDateAndTime; + } private: - QualitativeUnits(const QualitativeUnits &other); - QualitativeUnits &operator=(const QualitativeUnits& other); + MessageFormat *combinedDateAndTime; + RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other); + RelativeDateTimeCacheData& operator=( + const RelativeDateTimeCacheData &other); }; -class QuantitativeUnits : public UMemory { -public: - QuantitativeUnits() { } - QuantityFormatter data[UDAT_RELATIVE_UNIT_COUNT][2]; -private: - QuantitativeUnits(const QuantitativeUnits &other); - QuantitativeUnits &operator=(const QuantitativeUnits& other); -}; - -class RelativeDateTimeData : public SharedObject { -public: - SharedPtr qualitativeUnits; - SharedPtr quantitativeUnits; - SharedPtr combinedDateAndTime; - SharedPtr pluralRules; - SharedPtr numberFormat; - virtual ~RelativeDateTimeData(); -private: - RelativeDateTimeData &operator=(const RelativeDateTimeData& other); -}; - -RelativeDateTimeData::~RelativeDateTimeData() { +RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { + delete combinedDateAndTime; } static UBool getStringWithFallback( @@ -147,45 +147,42 @@ static UBool getStringByIndex( return TRUE; } -static void addQualitativeUnit( +static void initAbsoluteUnit( const UResourceBundle *resource, - UDateAbsoluteUnit absoluteUnit, const UnicodeString &unitName, - QualitativeUnits &qualitativeUnits, + UnicodeString *absoluteUnit, UErrorCode &status) { getStringWithFallback( resource, "-1", - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST], + absoluteUnit[UDAT_DIRECTION_LAST], status); getStringWithFallback( resource, "0", - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_THIS], + absoluteUnit[UDAT_DIRECTION_THIS], status); getStringWithFallback( resource, "1", - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT], + absoluteUnit[UDAT_DIRECTION_NEXT], status); getOptionalStringWithFallback( resource, "-2", - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST_2], + absoluteUnit[UDAT_DIRECTION_LAST_2], status); getOptionalStringWithFallback( resource, "2", - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT_2], + absoluteUnit[UDAT_DIRECTION_NEXT_2], status); - qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_PLAIN] = unitName; + absoluteUnit[UDAT_DIRECTION_PLAIN] = unitName; } -static void addTimeUnit( +static void initQuantityFormatter( const UResourceBundle *resource, - UDateRelativeUnit relativeUnit, - int32_t pastOrFuture, - QuantitativeUnits &quantitativeUnits, + QuantityFormatter &formatter, UErrorCode &status) { if (U_FAILURE(status)) { return; @@ -201,20 +198,18 @@ static void addTimeUnit( if (!getString(pluralBundle.getAlias(), rawPattern, status)) { return; } - if (!quantitativeUnits.data[relativeUnit][pastOrFuture] - .add( - ures_getKey(pluralBundle.getAlias()), - rawPattern, - status)) { + if (!formatter.add( + ures_getKey(pluralBundle.getAlias()), + rawPattern, + status)) { return; } } } -static void addTimeUnit( +static void initRelativeUnit( const UResourceBundle *resource, - UDateRelativeUnit relativeUnit, - QuantitativeUnits &quantitativeUnits, + QuantityFormatter *relativeUnit, UErrorCode &status) { LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback( @@ -227,53 +222,46 @@ static void addTimeUnit( if (U_FAILURE(status)) { return; } - addTimeUnit( + initQuantityFormatter( futureBundle.getAlias(), - relativeUnit, - 1, - quantitativeUnits, + relativeUnit[1], status); LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback( topLevel.getAlias(), "past", NULL, &status)); if (U_FAILURE(status)) { return; } - addTimeUnit( + initQuantityFormatter( pastBundle.getAlias(), - relativeUnit, - 0, - quantitativeUnits, + relativeUnit[0], status); } -static void addTimeUnit( +static void initRelativeUnit( const UResourceBundle *resource, const char *path, - UDateRelativeUnit relativeUnit, - QuantitativeUnits &quantitativeUnits, + QuantityFormatter *relativeUnit, UErrorCode &status) { LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback(resource, path, NULL, &status)); if (U_FAILURE(status)) { return; } - addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status); + initRelativeUnit(topLevel.getAlias(), relativeUnit, status); } static void addTimeUnit( const UResourceBundle *resource, const char *path, - UDateRelativeUnit relativeUnit, - UDateAbsoluteUnit absoluteUnit, - QuantitativeUnits &quantitativeUnits, - QualitativeUnits &qualitativeUnits, + QuantityFormatter *relativeUnit, + UnicodeString *absoluteUnit, UErrorCode &status) { LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback(resource, path, NULL, &status)); if (U_FAILURE(status)) { return; } - addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status); + initRelativeUnit(topLevel.getAlias(), relativeUnit, status); UnicodeString unitName; if (!getStringWithFallback(topLevel.getAlias(), "dn", unitName, status)) { return; @@ -294,11 +282,10 @@ static void addTimeUnit( if (U_FAILURE(status)) { return; } - addQualitativeUnit( + initAbsoluteUnit( topLevel.getAlias(), - absoluteUnit, unitName, - qualitativeUnits, + absoluteUnit, status); } @@ -329,80 +316,67 @@ static void addWeekDay( const char *path, const UnicodeString *daysOfWeek, UDateAbsoluteUnit absoluteUnit, - QualitativeUnits &qualitativeUnits, + UnicodeString absoluteUnits[][UDAT_DIRECTION_COUNT], UErrorCode &status) { LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback(resource, path, NULL, &status)); if (U_FAILURE(status)) { return; } - addQualitativeUnit( + initAbsoluteUnit( topLevel.getAlias(), - absoluteUnit, daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY], - qualitativeUnits, + absoluteUnits[absoluteUnit], status); } -static UBool load( +static UBool loadUnitData( const UResourceBundle *resource, - QualitativeUnits &qualitativeUnits, - QuantitativeUnits &quantitativeUnits, + RelativeDateTimeCacheData &cacheData, UErrorCode &status) { addTimeUnit( resource, "fields/day", - UDAT_RELATIVE_DAYS, - UDAT_ABSOLUTE_DAY, - quantitativeUnits, - qualitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_DAYS], + cacheData.absoluteUnits[UDAT_ABSOLUTE_DAY], status); addTimeUnit( resource, "fields/week", - UDAT_RELATIVE_WEEKS, - UDAT_ABSOLUTE_WEEK, - quantitativeUnits, - qualitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_WEEKS], + cacheData.absoluteUnits[UDAT_ABSOLUTE_WEEK], status); addTimeUnit( resource, "fields/month", - UDAT_RELATIVE_MONTHS, - UDAT_ABSOLUTE_MONTH, - quantitativeUnits, - qualitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_MONTHS], + cacheData.absoluteUnits[UDAT_ABSOLUTE_MONTH], status); addTimeUnit( resource, "fields/year", - UDAT_RELATIVE_YEARS, - UDAT_ABSOLUTE_YEAR, - quantitativeUnits, - qualitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_YEARS], + cacheData.absoluteUnits[UDAT_ABSOLUTE_YEAR], status); - addTimeUnit( + initRelativeUnit( resource, "fields/second", - UDAT_RELATIVE_SECONDS, - quantitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_SECONDS], status); - addTimeUnit( + initRelativeUnit( resource, "fields/minute", - UDAT_RELATIVE_MINUTES, - quantitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_MINUTES], status); - addTimeUnit( + initRelativeUnit( resource, "fields/hour", - UDAT_RELATIVE_HOURS, - quantitativeUnits, + cacheData.relativeUnits[UDAT_RELATIVE_HOURS], status); getStringWithFallback( resource, "fields/second/relative/0", - qualitativeUnits.data[UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN], + cacheData.absoluteUnits[UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN], status); UnicodeString daysOfWeek[7]; readDaysOfWeek( @@ -415,49 +389,49 @@ static UBool load( "fields/mon/relative", daysOfWeek, UDAT_ABSOLUTE_MONDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/tue/relative", daysOfWeek, UDAT_ABSOLUTE_TUESDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/wed/relative", daysOfWeek, UDAT_ABSOLUTE_WEDNESDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/thu/relative", daysOfWeek, UDAT_ABSOLUTE_THURSDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/fri/relative", daysOfWeek, UDAT_ABSOLUTE_FRIDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/sat/relative", daysOfWeek, UDAT_ABSOLUTE_SATURDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); addWeekDay( resource, "fields/sun/relative", daysOfWeek, UDAT_ABSOLUTE_SUNDAY, - qualitativeUnits, + cacheData.absoluteUnits, status); return U_SUCCESS(status); } @@ -494,72 +468,34 @@ static UBool getDateTimePattern( return getStringByIndex(topLevel.getAlias(), 8, result, status); } +// Creates RelativeDateTimeFormatter specific data for a given locale static SharedObject *U_CALLCONV createData( const char *localeId, UErrorCode &status) { LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status)); if (U_FAILURE(status)) { return NULL; } - LocalPointer result(new RelativeDateTimeData()); - LocalPointer qualitativeUnits(new QualitativeUnits()); - LocalPointer quantitativeUnits(new QuantitativeUnits()); - if (result.getAlias() == NULL - || qualitativeUnits.getAlias() == NULL - || quantitativeUnits.getAlias() == NULL) { + LocalPointer result( + new RelativeDateTimeCacheData()); + if (result.getAlias() == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - if (!load( + if (!loadUnitData( topLevel.getAlias(), - *qualitativeUnits, - *quantitativeUnits, + *result, status)) { return NULL; } - if (!result->qualitativeUnits.reset(qualitativeUnits.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (!result->quantitativeUnits.reset(quantitativeUnits.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - UnicodeString dateTimePattern; if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) { return NULL; } - LocalPointer mf( + result->adoptCombinedDateAndTime( new MessageFormat(dateTimePattern, localeId, status)); if (U_FAILURE(status)) { return NULL; } - if (mf.getAlias() == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (!result->combinedDateAndTime.reset(mf.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - LocalPointer pr(PluralRules::forLocale(localeId, status)); - if (U_FAILURE(status)) { - return NULL; - } - if (!result->pluralRules.reset(pr.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - LocalPointer nf( - NumberFormat::createInstance(localeId, status)); - if (U_FAILURE(status)) { - return NULL; - } - if (!result->numberFormat.reset(nf.orphan())) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } return result.orphan(); } @@ -573,67 +509,70 @@ static void U_CALLCONV cacheInit(UErrorCode &status) { } } -static void getFromCache( +static UBool getFromCache( const char *locale, - const RelativeDateTimeData *&ptr, + const RelativeDateTimeCacheData *&ptr, UErrorCode &status) { umtx_initOnce(gCacheInitOnce, &cacheInit, status); if (U_FAILURE(status)) { - return; + return FALSE; } Mutex lock(&gCacheMutex); gCache->get(locale, ptr, status); + return U_SUCCESS(status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) - : ptr(NULL) { - getFromCache(Locale::getDefault().getName(), ptr, status); + : cache(NULL), numberFormat(NULL), pluralRules(NULL) { + init(Locale::getDefault(), NULL, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const Locale& locale, UErrorCode& status) : ptr(NULL) { - getFromCache(locale.getName(), ptr, status); + const Locale& locale, UErrorCode& status) + : cache(NULL), numberFormat(NULL), pluralRules(NULL) { + init(locale, NULL, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) - : ptr(NULL) { - getFromCache(locale.getName(), ptr, status); - if (U_FAILURE(status)) { - return; - } - RelativeDateTimeData* wptr = SharedObject::copyOnWrite(ptr); - if (wptr == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (!wptr->numberFormat.reset(nfToAdopt)) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } + : cache(NULL), numberFormat(NULL), pluralRules(NULL) { + init(locale, nfToAdopt, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const RelativeDateTimeFormatter& other) : ptr(other.ptr) { - ptr->addRef(); + const RelativeDateTimeFormatter& other) + : cache(other.cache), + numberFormat(other.numberFormat), + pluralRules(other.pluralRules) { + cache->addRef(); + numberFormat->addRef(); + pluralRules->addRef(); } RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( const RelativeDateTimeFormatter& other) { if (this != &other) { - SharedObject::copyPtr(other.ptr, ptr); + SharedObject::copyPtr(other.cache, cache); + SharedObject::copyPtr(other.numberFormat, numberFormat); + SharedObject::copyPtr(other.pluralRules, pluralRules); } return *this; } RelativeDateTimeFormatter::~RelativeDateTimeFormatter() { - if (ptr != NULL) { - ptr->removeRef(); + if (cache != NULL) { + cache->removeRef(); + } + if (numberFormat != NULL) { + numberFormat->removeRef(); + } + if (pluralRules != NULL) { + pluralRules->removeRef(); } } const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { - return *ptr->numberFormat; + return **numberFormat; } UnicodeString& RelativeDateTimeFormatter::format( @@ -648,10 +587,10 @@ UnicodeString& RelativeDateTimeFormatter::format( } int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; FieldPosition pos(FieldPosition::DONT_CARE); - return ptr->quantitativeUnits->data[unit][bFuture].format( + return cache->relativeUnits[unit][bFuture].format( quantity, - *ptr->numberFormat, - *ptr->pluralRules, + **numberFormat, + **pluralRules, appendTo, pos, status); @@ -667,7 +606,7 @@ UnicodeString& RelativeDateTimeFormatter::format( status = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } - return appendTo.append(ptr->qualitativeUnits->data[unit][direction]); + return appendTo.append(cache->absoluteUnits[unit][direction]); } UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( @@ -675,9 +614,44 @@ UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( UnicodeString& appendTo, UErrorCode& status) const { Formattable args[2] = {timeString, relativeDateString}; FieldPosition fpos(0); - return ptr->combinedDateAndTime->format(args, 2, appendTo, fpos, status); + return cache->getCombinedDateAndTime()->format( + args, 2, appendTo, fpos, status); } +void RelativeDateTimeFormatter::init( + const Locale &locale, NumberFormat *nfToAdopt, UErrorCode &status) { + if (!getFromCache(locale.getName(), cache, status)) { + return; + } + SharedObject::copyPtr( + PluralRules::createSharedInstance( + locale, UPLURAL_TYPE_CARDINAL, status), + pluralRules); + if (U_FAILURE(status)) { + return; + } + pluralRules->removeRef(); + if (nfToAdopt == NULL) { + SharedObject::copyPtr( + NumberFormat::createSharedInstance( + locale, UNUM_DECIMAL, status), + numberFormat); + if (U_FAILURE(status)) { + return; + } + numberFormat->removeRef(); + } else { + SharedNumberFormat *shared = new SharedNumberFormat(nfToAdopt); + if (shared == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete nfToAdopt; + return; + } + SharedObject::copyPtr(shared, numberFormat); + } +} + + U_NAMESPACE_END #endif /* !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/sharednumberformat.h b/icu4c/source/i18n/sharednumberformat.h new file mode 100644 index 00000000000..4fd53a8ec6e --- /dev/null +++ b/icu4c/source/i18n/sharednumberformat.h @@ -0,0 +1,34 @@ +/* +****************************************************************************** +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* sharednumberformat.h +*/ + +#ifndef __SHARED_NUMBERFORMAT_H__ +#define __SHARED_NUMBERFORMAT_H__ + +#include "unicode/utypes.h" +#include "sharedobject.h" +#include "sharedptr.h" + +U_NAMESPACE_BEGIN + +class NumberFormat; + +class U_I18N_API SharedNumberFormat : public SharedObject { +public: +SharedNumberFormat(NumberFormat *nfToAdopt) : ptr(nfToAdopt) { } +virtual ~SharedNumberFormat(); +const NumberFormat *operator->() const { return ptr; } +const NumberFormat &operator*() const { return *ptr; } +private: +NumberFormat *ptr; +SharedNumberFormat(const SharedNumberFormat &); +SharedNumberFormat &operator=(const SharedNumberFormat &); +}; + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/sharedpluralrules.h b/icu4c/source/i18n/sharedpluralrules.h new file mode 100644 index 00000000000..ab0102145b8 --- /dev/null +++ b/icu4c/source/i18n/sharedpluralrules.h @@ -0,0 +1,34 @@ +/* +****************************************************************************** +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* sharedpluralrules.h +*/ + +#ifndef __SHARED_PLURALRULES_H__ +#define __SHARED_PLURALRULES_H__ + +#include "unicode/utypes.h" +#include "sharedobject.h" +#include "sharedptr.h" + +U_NAMESPACE_BEGIN + +class PluralRules; + +class U_I18N_API SharedPluralRules : public SharedObject { +public: +SharedPluralRules(PluralRules *prToAdopt) : ptr(prToAdopt) { } +virtual ~SharedPluralRules(); +const PluralRules *operator->() const { return ptr; } +const PluralRules &operator*() const { return *ptr; } +private: +PluralRules *ptr; +SharedPluralRules(const SharedPluralRules &); +SharedPluralRules &operator=(const SharedPluralRules &); +}; + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/tmutfmt.cpp b/icu4c/source/i18n/tmutfmt.cpp index f3bf3dc8a2f..e7719379f61 100644 --- a/icu4c/source/i18n/tmutfmt.cpp +++ b/icu4c/source/i18n/tmutfmt.cpp @@ -79,13 +79,13 @@ static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0}; static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0}; TimeUnitFormat::TimeUnitFormat(UErrorCode& status) { - initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, status); + initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status); create(UTMUTFMT_FULL_STYLE, status); } TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) { - initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status); + initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status); create(UTMUTFMT_FULL_STYLE, status); } @@ -93,13 +93,13 @@ TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) { TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) { switch (style) { case UTMUTFMT_FULL_STYLE: - initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status); + initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status); break; case UTMUTFMT_ABBREVIATED_STYLE: - initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, status); + initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status); break; default: - initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status); + initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status); break; } create(style, status); diff --git a/icu4c/source/i18n/unicode/measfmt.h b/icu4c/source/i18n/unicode/measfmt.h index b59dfd9e582..6f64809a9cf 100644 --- a/icu4c/source/i18n/unicode/measfmt.h +++ b/icu4c/source/i18n/unicode/measfmt.h @@ -72,7 +72,9 @@ U_NAMESPACE_BEGIN class NumberFormat; class PluralRules; -class MeasureFormatData; +class MeasureFormatCacheData; +class SharedNumberFormat; +class SharedPluralRules; class QuantityFormatter; class ListFormatter; class DateFormat; @@ -208,10 +210,14 @@ class U_I18N_API MeasureFormat : public Format { /** * ICU use only. - * Initialize MeasureFormat class from base class. + * Initialize or change MeasureFormat class from subclass. * @internal. */ - void initMeasureFormat(const Locale &locale, UMeasureFormatWidth width, UErrorCode &status); + void initMeasureFormat( + const Locale &locale, + UMeasureFormatWidth width, + NumberFormat *nfToAdopt, + UErrorCode &status); /** * ICU use only. @@ -256,9 +262,16 @@ class U_I18N_API MeasureFormat : public Format { #endif /* U_HIDE_INTERNAL_API */ private: - const MeasureFormatData *ptr; + const MeasureFormatCacheData *cache; + const SharedNumberFormat *numberFormat; + const SharedPluralRules *pluralRules; UMeasureFormatWidth width; + // Declared outside of MeasureFormatSharedData because ListFormatter + // objects are relatively cheap to copy; therefore, they don't need to be + // shared across instances. + ListFormatter *listFormatter; + const QuantityFormatter *getQuantityFormatter( int32_t index, int32_t widthIndex, @@ -273,14 +286,14 @@ class U_I18N_API MeasureFormat : public Format { UnicodeString &formatMeasuresSlowTrack( const Measure *measures, int32_t measureCount, - const ListFormatter& lf, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const; UnicodeString &formatNumeric( - const Formattable *hms, // always length 3 - int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset + const Formattable *hms, // always length 3: [0] is hour; [1] is + // minute; [2] is second. + int32_t bitMap, // 1=hour set, 2=minute set, 4=second set UnicodeString &appendTo, UErrorCode &status) const; diff --git a/icu4c/source/i18n/unicode/numfmt.h b/icu4c/source/i18n/unicode/numfmt.h index 73bf45f1956..b356fdcbf78 100644 --- a/icu4c/source/i18n/unicode/numfmt.h +++ b/icu4c/source/i18n/unicode/numfmt.h @@ -43,6 +43,8 @@ class NumberFormatTest; U_NAMESPACE_BEGIN +class SharedNumberFormat; + #if !UCONFIG_NO_SERVICE class NumberFormatFactory; class StringEnumeration; @@ -706,6 +708,30 @@ public: UNumberFormatStyle style, UErrorCode& errorCode); +#ifndef U_HIDE_INTERNAL_API + + /** + * ICU use only. + * Creates NumberFormat instance without using the cache. + * @internal + */ + static NumberFormat* internalCreateInstance( + const Locale& desiredLocale, + UNumberFormatStyle style, + UErrorCode& errorCode); + + /** + * ICU use only. + * Returns handle to the shared, cached NumberFormat instance for given + * locale. On success, caller must call removeRef() on returned value + * once it is done with the shared instance. + * @internal + */ + static const SharedNumberFormat* U_EXPORT2 createSharedInstance( + const Locale& inLocale, UNumberFormatStyle style, UErrorCode& status); + +#endif + /** * Returns a currency format for the current default locale. * @stable ICU 2.0 diff --git a/icu4c/source/i18n/unicode/plurrule.h b/icu4c/source/i18n/unicode/plurrule.h index c36b1a829d1..74189c57a26 100644 --- a/icu4c/source/i18n/unicode/plurrule.h +++ b/icu4c/source/i18n/unicode/plurrule.h @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2008-2013, International Business Machines Corporation and +* Copyright (C) 2008-2014, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * @@ -43,6 +43,7 @@ class RuleChain; class PluralRuleParser; class PluralKeywordEnumeration; class AndConstraint; +class SharedPluralRules; /** * Defines rules for mapping non-negative numeric values onto a small set of @@ -297,6 +298,25 @@ public: * @internal */ static UBool hasOverride(const Locale &locale); + + /** + * For ICU use only. + * creates a SharedPluralRules object + * @internal + */ + static PluralRules* U_EXPORT2 internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status); + + /** + * For ICU use only. + * Returns handle to the shared, cached PluralRules isntance. + * Caller must call removeRef() on returned value once it is done with + * the shared instance. + * @internal + */ + static const SharedPluralRules* U_EXPORT2 createSharedInstance( + const Locale& locale, UPluralType type, UErrorCode& status); + + #endif /* U_HIDE_INTERNAL_API */ /** diff --git a/icu4c/source/i18n/unicode/reldatefmt.h b/icu4c/source/i18n/unicode/reldatefmt.h index 7c4bfcb3d1a..b72ec6593ba 100644 --- a/icu4c/source/i18n/unicode/reldatefmt.h +++ b/icu4c/source/i18n/unicode/reldatefmt.h @@ -221,7 +221,9 @@ typedef enum UDateDirection { U_NAMESPACE_BEGIN -class RelativeDateTimeData; +class RelativeDateTimeCacheData; +class SharedNumberFormat; +class SharedPluralRules; class NumberFormat; /** @@ -407,8 +409,10 @@ public: const NumberFormat& getNumberFormat() const; private: - RelativeDateTimeFormatter(); - const RelativeDateTimeData* ptr; + const RelativeDateTimeCacheData* cache; + const SharedNumberFormat *numberFormat; + const SharedPluralRules *pluralRules; + void init(const Locale &, NumberFormat *nfToAdopt, UErrorCode &status); }; U_NAMESPACE_END diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp index 015e2be6c01..cf316c3ddc2 100644 --- a/icu4c/source/test/intltest/measfmttest.cpp +++ b/icu4c/source/test/intltest/measfmttest.cpp @@ -52,6 +52,7 @@ private: void TestFieldPositionMultiple(); void TestBadArg(); void TestEquality(); + void TestBenchmark(); void verifyFormat( const char *description, const MeasureFormat &fmt, @@ -106,6 +107,7 @@ void MeasureFormatTest::runIndexedTest( TESTCASE_AUTO(TestFieldPositionMultiple); TESTCASE_AUTO(TestBadArg); TESTCASE_AUTO(TestEquality); + TESTCASE_AUTO(TestBenchmark); TESTCASE_AUTO_END; } @@ -823,6 +825,29 @@ void MeasureFormatTest::TestEquality() { assertTrue("Not Equal 3", fmt != fmtne3); } +void MeasureFormatTest::TestBenchmark() { +/* + clock_t t; + UErrorCode status = U_ZERO_ERROR; + Locale en("en"); + MeasureFormat fmt(en, UMEASFMT_WIDTH_SHORT, status); + MeasureFormat fmt2 = fmt; + Measure ms[] = { + Measure(70, MeasureUnit::createYear(status), status), + Measure(5, MeasureUnit::createMonth(status), status), + Measure(23, MeasureUnit::createDay(status), status), + Measure(15, MeasureUnit::createHour(status), status), + Measure(58, MeasureUnit::createMinute(status), status)}; + FieldPosition pos(FieldPosition::DONT_CARE); + t = clock(); + for (int32_t i = 0; i < 1000000; ++i) { + fmt2 = fmt; + } + t = clock() - t; + errln("It took %f seconds.", ((float)t)/CLOCKS_PER_SEC); +*/ +} + void MeasureFormatTest::verifyFieldPosition( const char *description, const MeasureFormat &fmt, diff --git a/icu4c/source/test/intltest/reldatefmttest.cpp b/icu4c/source/test/intltest/reldatefmttest.cpp index 09bf0bb565f..ce2ffc0aea6 100644 --- a/icu4c/source/test/intltest/reldatefmttest.cpp +++ b/icu4c/source/test/intltest/reldatefmttest.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2013, International Business Machines Corporation and * +* Copyright (C) 2013-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -294,7 +294,15 @@ void RelativeDateTimeFormatterTest::TestCustomNumberFormat() { nf->setMinimumFractionDigits(1); nf->setMaximumFractionDigits(1); RelativeDateTimeFormatter fmt("en", nf, status); - RunTest(fmt, kEnglishDecimal, LENGTHOF(kEnglishDecimal), "en decimal digits"); + + // Test copy constructor. + RelativeDateTimeFormatter fmt2(fmt); + RunTest(fmt2, kEnglishDecimal, LENGTHOF(kEnglishDecimal), "en decimal digits"); + + // Test assignment + fmt = RelativeDateTimeFormatter("es", status); + RunTest(fmt, kSpanishNoQuantity, LENGTHOF(kSpanishNoQuantity), "assignment operator"); + } void RelativeDateTimeFormatterTest::TestCombineDateAndTime() {