]> granicus.if.org Git - icu/commitdiff
ICU-10461 Make NumberFormat::createInstance() and PluralRules::forLocale() 10X faster...
authorTravis Keep <keep94@gmail.com>
Mon, 10 Feb 2014 19:13:50 +0000 (19:13 +0000)
committerTravis Keep <keep94@gmail.com>
Mon, 10 Feb 2014 19:13:50 +0000 (19:13 +0000)
X-SVN-Rev: 35114

16 files changed:
.gitattributes
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/measfmt.cpp
icu4c/source/i18n/numfmt.cpp
icu4c/source/i18n/plurrule.cpp
icu4c/source/i18n/reldatefmt.cpp
icu4c/source/i18n/sharednumberformat.h [new file with mode: 0644]
icu4c/source/i18n/sharedpluralrules.h [new file with mode: 0644]
icu4c/source/i18n/tmutfmt.cpp
icu4c/source/i18n/unicode/measfmt.h
icu4c/source/i18n/unicode/numfmt.h
icu4c/source/i18n/unicode/plurrule.h
icu4c/source/i18n/unicode/reldatefmt.h
icu4c/source/test/intltest/measfmttest.cpp
icu4c/source/test/intltest/reldatefmttest.cpp

index 75b75b81c8888f9e84afdd95f801ad2511339736..03d5dd15d6bedc75514810f2746c8888c0fa5784 100644 (file)
@@ -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
index 09ec2fecf3138129fc63a4ffc1657afa26bfc56b..39ebd67eb72fc7dd26524990a73d860b777cee12 100644 (file)
     </CustomBuild>\r
     <ClInclude Include="plurrule_impl.h" />\r
     <ClInclude Include="quantityformatter.h" />\r
+    <ClInclude Include="sharednumberformat.h" />\r
+    <ClInclude Include="sharedpluralrules.h" />\r
     <CustomBuild Include="unicode\rbnf.h">\r
       <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
 </Command>\r
index 643603c84315c6bb6917b1bdfeb011ff360556eb..e6dd664b1bcbb970946e6b9b4549d8932bd70c7f 100644 (file)
     <ClInclude Include="reldtfmt.h">\r
       <Filter>formatting</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="sharednumberformat.h">\r
+      <Filter>formatting</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="sharedpluralrules.h">\r
+      <Filter>formatting</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="smpdtfst.h">\r
       <Filter>formatting</Filter>\r
     </ClInclude>\r
index 3fd41e4074d57c9c8f42d5489b0208d6f644b5d9..aed1ed66f76853d7ccf38f310dd9314e97f95e3a 100644 (file)
 #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> unitFormatters;
-    SharedPtr<PluralRules> pluralRules;
-    SharedPtr<NumberFormat> numberFormat;
-    SharedPtr<NumberFormat> currencyFormats[UMEASFMT_WIDTH_NARROW + 1];
-    SharedPtr<NumericDateFormatters> 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<MeasureFormatData> result(new MeasureFormatData());
-    LocalPointer<UnitFormatters> unitFormatters(new UnitFormatters());
-    if (result.getAlias() == NULL
-            || unitFormatters.getAlias() == NULL) {
+    LocalPointer<MeasureFormatCacheData> 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<NumericDateFormatters> 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<PluralRules> 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<NumberFormat> 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<NumberFormat> 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);
-}
-
-MeasureFormat::MeasureFormat(const MeasureFormat &other)
-        : Format(other), ptr(other.ptr), width(other.width) {
-    ptr->addRef();
+        : cache(NULL),
+          numberFormat(NULL),
+          pluralRules(NULL),
+          width(w),
+          listFormatter(NULL) {
+    initMeasureFormat(locale, w, nfToAdopt, status);
+}
+
+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 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;
+    // 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;
+        }
     }
-    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<ListFormatter> 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,
index 52220978041b429147bbed769934f3dae907d731..95ea05c41b0045e604efa6ea106299798567b201 100644 (file)
@@ -51,6 +51,8 @@
 #include "mutex.h"
 #include "digitlst.h"
 #include <float.h>
+#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;
index 3ef05972f964b73b1022fe12acda19365a5c900f..2984bc19f189c73d13959f6801ccd9c2bda0df53 100644 (file)
 #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;
     }
index 1b26cd91b6de690bbcd4aef4238e224d487ade00..0e09b0dcfd1e8fa6c44bc8e6cd1759c9fffec8a9 100644 (file)
@@ -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];
-private:
-    QualitativeUnits(const QualitativeUnits &other);
-    QualitativeUnits &operator=(const QualitativeUnits& other);
-};
+    RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { }
+    virtual ~RelativeDateTimeCacheData();
 
-class QuantitativeUnits : public UMemory {
-public:
-    QuantitativeUnits() { }
-    QuantityFormatter data[UDAT_RELATIVE_UNIT_COUNT][2];
-private:
-    QuantitativeUnits(const QuantitativeUnits &other);
-    QuantitativeUnits &operator=(const QuantitativeUnits& other);
-};
+    // no numbers: e.g Next Tuesday; Yesterday; etc.
+    UnicodeString absoluteUnits[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
 
-class RelativeDateTimeData : public SharedObject {
-public:
-    SharedPtr<QualitativeUnits> qualitativeUnits;
-    SharedPtr<QuantitativeUnits> quantitativeUnits;
-    SharedPtr<MessageFormat> combinedDateAndTime;
-    SharedPtr<PluralRules> pluralRules;
-    SharedPtr<NumberFormat> numberFormat;
-    virtual ~RelativeDateTimeData();
+    // 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:
-    RelativeDateTimeData &operator=(const RelativeDateTimeData& other);
+    MessageFormat *combinedDateAndTime;
+    RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
+    RelativeDateTimeCacheData& operator=(
+            const RelativeDateTimeCacheData &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<RelativeDateTimeData> result(new RelativeDateTimeData());
-    LocalPointer<QualitativeUnits> qualitativeUnits(new QualitativeUnits());
-    LocalPointer<QuantitativeUnits> quantitativeUnits(new QuantitativeUnits());
-    if (result.getAlias() == NULL
-            || qualitativeUnits.getAlias() == NULL
-            || quantitativeUnits.getAlias() == NULL) {
+    LocalPointer<RelativeDateTimeCacheData> 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<MessageFormat> 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<PluralRules> 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<NumberFormat> 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 (file)
index 0000000..4fd53a8
--- /dev/null
@@ -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 (file)
index 0000000..ab01021
--- /dev/null
@@ -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
index f3bf3dc8a2fb3eeb267121b3bb0971a0a17ed152..e7719379f61df429c4d416575ecf6b411b00e99e 100644 (file)
@@ -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);
index b59dfd9e582d2905460f24f025c50cb73d5e941c..6f64809a9cfdfbeda2b6c35566ad1d855fb01bfd 100644 (file)
@@ -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;
 
index 73bf45f1956a12b6c8706cdfdea40b523a5b1b27..b356fdcbf7886603068d73365ae8dc0e2bfd42a8 100644 (file)
@@ -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
index c36b1a829d13e06efd09fea81f3cd805a888bce4..74189c57a2629162c5df320b42a93837da9ff0c1 100644 (file)
@@ -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 */
 
     /**
index 7c4bfcb3d1a1a5aefec1ae9c5ece669adf848a5c..b72ec6593ba96ef320deafc5dec1447acd193ff8 100644 (file)
@@ -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
index 015e2be6c01999fce86eb700ea1a09258d4cd508..cf316c3ddc2cea9bf63ccc83a66e8cc5f2c89394 100644 (file)
@@ -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,
index 09bf0bb565fb91609b63d584a74e53e51fcb6d4a..ce2ffc0aea6cfe329e0d878ce09e35ced87dfa4d 100644 (file)
@@ -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() {