Skip to content

Commit

Permalink
Bug 1404666 - patch 2 - Accelerate DateTimeFormat::FormatUDateTime by…
Browse files Browse the repository at this point in the history
… caching ICU UDateFormat objects instead of creating them afresh every time. r=gandalf
  • Loading branch information
jfkthame committed Dec 18, 2018
1 parent 572203b commit 4e5cb48
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 126 deletions.
288 changes: 162 additions & 126 deletions intl/locale/DateTimeFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace mozilla {
using namespace mozilla::intl;

nsCString* DateTimeFormat::mLocale = nullptr;
nsDataHashtable<nsCStringHashKey, UDateFormat*>* DateTimeFormat::mFormatCache;

/*static*/ nsresult DateTimeFormat::Initialize() {
if (mLocale) {
Expand Down Expand Up @@ -74,140 +75,168 @@ nsCString* DateTimeFormat::mLocale = nullptr;
return rv;
}

// Get the date style for the formatter.
nsAutoString skeletonDate;
nsAutoString patternDate;
bool haveSkeleton = true;
switch (aDateFormatSelector) {
case kDateFormatLong:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleLong,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(mLocale->get()), patternDate);
NS_ENSURE_SUCCESS(rv, rv);
haveSkeleton = false;
break;
case kDateFormatShort:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleShort,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(mLocale->get()), patternDate);
NS_ENSURE_SUCCESS(rv, rv);
haveSkeleton = false;
break;
case kDateFormatYearMonth:
skeletonDate.AssignLiteral("yyyyMM");
break;
case kDateFormatYearMonthLong:
skeletonDate.AssignLiteral("yyyyMMMM");
break;
case kDateFormatMonthLong:
skeletonDate.AssignLiteral("MMMM");
break;
case kDateFormatWeekday:
skeletonDate.AssignLiteral("EEE");
break;
case kDateFormatNone:
haveSkeleton = false;
break;
default:
NS_ERROR("Unknown nsDateFormatSelector");
return NS_ERROR_ILLEGAL_VALUE;
UErrorCode status = U_ZERO_ERROR;

nsAutoCString key;
key.AppendInt((int)aDateFormatSelector);
key.Append(':');
key.AppendInt((int)aTimeFormatSelector);
if (aTimeParameters) {
key.Append(':');
key.AppendInt(aTimeParameters->tp_gmt_offset);
key.Append(':');
key.AppendInt(aTimeParameters->tp_dst_offset);
}

UErrorCode status = U_ZERO_ERROR;
if (haveSkeleton) {
// Get pattern for skeleton.
UDateTimePatternGenerator* patternGenerator =
udatpg_open(mLocale->get(), &status);
if (U_SUCCESS(status)) {
int32_t patternLength;
patternDate.SetLength(DATETIME_FORMAT_INITIAL_LEN);
patternLength = udatpg_getBestPattern(
patternGenerator,
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
skeletonDate.Length(),
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
DATETIME_FORMAT_INITIAL_LEN, &status);
patternDate.SetLength(patternLength);

if (status == U_BUFFER_OVERFLOW_ERROR) {
status = U_ZERO_ERROR;
udatpg_getBestPattern(
if (mFormatCache && mFormatCache->Count() == kMaxCachedFormats) {
// Don't allow a pathological page to extend the cache unreasonably.
NS_WARNING("flushing UDateFormat cache");
DeleteCache();
}
if (!mFormatCache) {
mFormatCache =
new nsDataHashtable<nsCStringHashKey, UDateFormat*>(kMaxCachedFormats);
}

UDateFormat*& dateTimeFormat = mFormatCache->GetOrInsert(key);

if (!dateTimeFormat) {
// We didn't have a cached formatter for this key, so create one.

// Get the date style for the formatter.
nsAutoString skeletonDate;
nsAutoString patternDate;
bool haveSkeleton = true;
switch (aDateFormatSelector) {
case kDateFormatLong:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleLong,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(mLocale->get()), patternDate);
NS_ENSURE_SUCCESS(rv, rv);
haveSkeleton = false;
break;
case kDateFormatShort:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleShort,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(mLocale->get()), patternDate);
NS_ENSURE_SUCCESS(rv, rv);
haveSkeleton = false;
break;
case kDateFormatYearMonth:
skeletonDate.AssignLiteral("yyyyMM");
break;
case kDateFormatYearMonthLong:
skeletonDate.AssignLiteral("yyyyMMMM");
break;
case kDateFormatMonthLong:
skeletonDate.AssignLiteral("MMMM");
break;
case kDateFormatWeekday:
skeletonDate.AssignLiteral("EEE");
break;
case kDateFormatNone:
haveSkeleton = false;
break;
default:
NS_ERROR("Unknown nsDateFormatSelector");
return NS_ERROR_ILLEGAL_VALUE;
}

if (haveSkeleton) {
// Get pattern for skeleton.
UDateTimePatternGenerator* patternGenerator =
udatpg_open(mLocale->get(), &status);
if (U_SUCCESS(status)) {
int32_t patternLength;
patternDate.SetLength(DATETIME_FORMAT_INITIAL_LEN);
patternLength = udatpg_getBestPattern(
patternGenerator,
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
skeletonDate.Length(),
reinterpret_cast<UChar*>(patternDate.BeginWriting()), patternLength,
&status);
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
DATETIME_FORMAT_INITIAL_LEN, &status);
patternDate.SetLength(patternLength);

if (status == U_BUFFER_OVERFLOW_ERROR) {
status = U_ZERO_ERROR;
udatpg_getBestPattern(
patternGenerator,
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
skeletonDate.Length(),
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
patternLength, &status);
}
}
udatpg_close(patternGenerator);
}
udatpg_close(patternGenerator);
}

// Get the time style for the formatter.
nsAutoString patternTime;
switch (aTimeFormatSelector) {
case kTimeFormatSeconds:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleNone,
mozIOSPreferences::dateTimeFormatStyleLong,
nsDependentCString(mLocale->get()), patternTime);
NS_ENSURE_SUCCESS(rv, rv);
break;
case kTimeFormatNoSeconds:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleNone,
mozIOSPreferences::dateTimeFormatStyleShort,
nsDependentCString(mLocale->get()), patternTime);
NS_ENSURE_SUCCESS(rv, rv);
break;
case kTimeFormatNone:
break;
default:
NS_ERROR("Unknown nsTimeFormatSelector");
return NS_ERROR_ILLEGAL_VALUE;
}
// Get the time style for the formatter.
nsAutoString patternTime;
switch (aTimeFormatSelector) {
case kTimeFormatSeconds:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleNone,
mozIOSPreferences::dateTimeFormatStyleLong,
nsDependentCString(mLocale->get()), patternTime);
NS_ENSURE_SUCCESS(rv, rv);
break;
case kTimeFormatNoSeconds:
rv = OSPreferences::GetInstance()->GetDateTimePattern(
mozIOSPreferences::dateTimeFormatStyleNone,
mozIOSPreferences::dateTimeFormatStyleShort,
nsDependentCString(mLocale->get()), patternTime);
NS_ENSURE_SUCCESS(rv, rv);
break;
case kTimeFormatNone:
break;
default:
NS_ERROR("Unknown nsTimeFormatSelector");
return NS_ERROR_ILLEGAL_VALUE;
}

nsAutoString pattern;
if (patternTime.Length() == 0) {
pattern.Assign(patternDate);
} else if (patternDate.Length() == 0) {
pattern.Assign(patternTime);
} else {
OSPreferences::GetDateTimeConnectorPattern(
nsDependentCString(mLocale->get()), pattern);
int32_t index = pattern.Find("{1}");
if (index != kNotFound) pattern.Replace(index, 3, patternDate);
index = pattern.Find("{0}");
if (index != kNotFound) pattern.Replace(index, 3, patternTime);
}
nsAutoString pattern;
if (patternTime.Length() == 0) {
pattern.Assign(patternDate);
} else if (patternDate.Length() == 0) {
pattern.Assign(patternTime);
} else {
OSPreferences::GetDateTimeConnectorPattern(
nsDependentCString(mLocale->get()), pattern);
int32_t index = pattern.Find("{1}");
if (index != kNotFound) pattern.Replace(index, 3, patternDate);
index = pattern.Find("{0}");
if (index != kNotFound) pattern.Replace(index, 3, patternTime);
}

// Generate date/time string.
nsAutoString timeZoneID(u"GMT");
if (aTimeParameters) {
int32_t totalOffsetMinutes =
(aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60;
if (totalOffsetMinutes != 0) {
char sign = totalOffsetMinutes < 0 ? '-' : '+';
int32_t hours = abs(totalOffsetMinutes) / 60;
int32_t minutes = abs(totalOffsetMinutes) % 60;
timeZoneID.AppendPrintf("%c%02d:%02d", sign, hours, minutes);
// Generate date/time string.
nsAutoString timeZoneID(u"GMT");
if (aTimeParameters) {
int32_t totalOffsetMinutes =
(aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) /
60;
if (totalOffsetMinutes != 0) {
char sign = totalOffsetMinutes < 0 ? '-' : '+';
int32_t hours = abs(totalOffsetMinutes) / 60;
int32_t minutes = abs(totalOffsetMinutes) % 60;
timeZoneID.AppendPrintf("%c%02d:%02d", sign, hours, minutes);
}
}
}

UDateFormat* dateTimeFormat;
if (aTimeParameters) {
dateTimeFormat =
udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(),
reinterpret_cast<const UChar*>(timeZoneID.BeginReading()),
timeZoneID.Length(),
reinterpret_cast<const UChar*>(pattern.BeginReading()),
pattern.Length(), &status);
} else {
dateTimeFormat =
udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), nullptr, -1,
reinterpret_cast<const UChar*>(pattern.BeginReading()),
pattern.Length(), &status);
if (aTimeParameters) {
dateTimeFormat =
udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(),
reinterpret_cast<const UChar*>(timeZoneID.BeginReading()),
timeZoneID.Length(),
reinterpret_cast<const UChar*>(pattern.BeginReading()),
pattern.Length(), &status);
} else {
dateTimeFormat =
udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(), nullptr, -1,
reinterpret_cast<const UChar*>(pattern.BeginReading()),
pattern.Length(), &status);
}
}

if (U_SUCCESS(status) && dateTimeFormat) {
Expand All @@ -230,14 +259,21 @@ nsCString* DateTimeFormat::mLocale = nullptr;
rv = NS_ERROR_FAILURE;
}

if (dateTimeFormat) {
udat_close(dateTimeFormat);
}

return rv;
}

/*static*/ void DateTimeFormat::DeleteCache() {
if (mFormatCache) {
for (auto i = mFormatCache->Iter(); !i.Done(); i.Next()) {
udat_close(i.Data());
}
delete mFormatCache;
mFormatCache = nullptr;
}
}

/*static*/ void DateTimeFormat::Shutdown() {
DeleteCache();
if (mLocale) {
delete mLocale;
}
Expand Down
4 changes: 4 additions & 0 deletions intl/locale/DateTimeFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <time.h>
#include "gtest/MozGtestFriend.h"
#include "nsDataHashtable.h"
#include "nsString.h"
#include "prtime.h"
#include "unicode/udat.h"
Expand Down Expand Up @@ -55,6 +56,8 @@ class DateTimeFormat {
DateTimeFormat() = delete;

static nsresult Initialize();
static void DeleteCache();
static const size_t kMaxCachedFormats = 15;

FRIEND_TEST(DateTimeFormat, FormatPRExplodedTime);
FRIEND_TEST(DateTimeFormat, DateFormatSelectors);
Expand All @@ -69,6 +72,7 @@ class DateTimeFormat {
const PRTimeParameters* aTimeParameters, nsAString& aStringOut);

static nsCString* mLocale;
static nsDataHashtable<nsCStringHashKey, UDateFormat*>* mFormatCache;
};

} // namespace mozilla
Expand Down

0 comments on commit 4e5cb48

Please sign in to comment.