diff --git a/deps/icu-small/source/common/cmemory.h b/deps/icu-small/source/common/cmemory.h index b5e0c2b7a17296..c9156f253cf1c7 100644 --- a/deps/icu-small/source/common/cmemory.h +++ b/deps/icu-small/source/common/cmemory.h @@ -725,9 +725,14 @@ class MemoryPool : public UMemory { } MemoryPool& operator=(MemoryPool&& other) U_NOEXCEPT { - fCount = other.fCount; - fPool = std::move(other.fPool); - other.fCount = 0; + // Since `this` may contain instances that need to be deleted, we can't + // just throw them away and replace them with `other`. The normal way of + // dealing with this in C++ is to swap `this` and `other`, rather than + // simply overwrite: the destruction of `other` can then take care of + // running MemoryPool::~MemoryPool() over the still-to-be-deallocated + // instances. + std::swap(fCount, other.fCount); + std::swap(fPool, other.fPool); return *this; } @@ -796,9 +801,6 @@ class MemoryPool : public UMemory { template class MaybeStackVector : protected MemoryPool { public: - using MemoryPool::MemoryPool; - using MemoryPool::operator=; - template T* emplaceBack(Args&&... args) { return this->create(args...); diff --git a/deps/icu-small/source/common/locid.cpp b/deps/icu-small/source/common/locid.cpp index 2804e36bf62cb1..874e4a70556f31 100644 --- a/deps/icu-small/source/common/locid.cpp +++ b/deps/icu-small/source/common/locid.cpp @@ -35,6 +35,7 @@ #include "unicode/bytestream.h" #include "unicode/locid.h" +#include "unicode/localebuilder.h" #include "unicode/strenum.h" #include "unicode/stringpiece.h" #include "unicode/uloc.h" @@ -1028,7 +1029,7 @@ class AliasReplacer { // place the the replaced locale ID in out and return true. // Otherwise return false for no replacement or error. bool replace( - const Locale& locale, CharString& out, UErrorCode status); + const Locale& locale, CharString& out, UErrorCode& status); private: const char* language; @@ -1336,10 +1337,13 @@ AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status) // Cannot use nullptr for language because that will construct // the default locale, in that case, use "und" to get the correct // locale. - Locale l(language == nullptr ? "und" : language, nullptr, script); + Locale l = LocaleBuilder() + .setLanguage(language == nullptr ? "und" : language) + .setScript(script) + .build(status); l.addLikelySubtags(status); const char* likelyRegion = l.getCountry(); - CharString* item = nullptr; + LocalPointer item; if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) { size_t len = uprv_strlen(likelyRegion); const char* foundInReplacement = uprv_strstr(replacement, @@ -1351,20 +1355,22 @@ AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status) *(foundInReplacement-1) == ' '); U_ASSERT(foundInReplacement[len] == ' ' || foundInReplacement[len] == '\0'); - item = new CharString(foundInReplacement, (int32_t)len, status); + item.adoptInsteadAndCheckErrorCode( + new CharString(foundInReplacement, (int32_t)len, status), status); } } - if (item == nullptr) { - item = new CharString(replacement, - (int32_t)(firstSpace - replacement), status); + if (item.isNull() && U_SUCCESS(status)) { + item.adoptInsteadAndCheckErrorCode( + new CharString(replacement, + (int32_t)(firstSpace - replacement), status), status); } if (U_FAILURE(status)) { return false; } - if (item == nullptr) { + if (item.isNull()) { status = U_MEMORY_ALLOCATION_ERROR; return false; } replacedRegion = item->data(); - toBeFreed.addElement(item, status); + toBeFreed.addElement(item.orphan(), status); } U_ASSERT(!same(region, replacedRegion)); region = replacedRegion; @@ -1453,7 +1459,7 @@ AliasReplacer::outputToString( int32_t variantsStart = out.length(); for (int32_t i = 0; i < variants.size(); i++) { out.append(SEP_CHAR, status) - .append((const char*)((UVector*)variants.elementAt(i)), + .append((const char*)(variants.elementAt(i)), status); } T_CString_toUpperCase(out.data() + variantsStart); @@ -1470,7 +1476,7 @@ AliasReplacer::outputToString( } bool -AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode status) +AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status) { data = AliasData::singleton(status); if (U_FAILURE(status)) { @@ -2453,9 +2459,13 @@ Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErro if (U_FAILURE(status)) { return; } + if (status == U_STRING_NOT_TERMINATED_WARNING) { + status = U_ZERO_ERROR; + } int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY); int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName, bufferLength, &status) + 1; + U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); /* Handle the case the current buffer is not enough to hold the new id */ if (status == U_BUFFER_OVERFLOW_ERROR) { U_ASSERT(newLength > bufferLength); @@ -2472,6 +2482,7 @@ Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErro fullName = newFullName; status = U_ZERO_ERROR; uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status); + U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); } else { U_ASSERT(newLength <= bufferLength); } diff --git a/deps/icu-small/source/common/rbbitblb.cpp b/deps/icu-small/source/common/rbbitblb.cpp index 65b597c4a936b0..cbd8f315c252d8 100644 --- a/deps/icu-small/source/common/rbbitblb.cpp +++ b/deps/icu-small/source/common/rbbitblb.cpp @@ -1402,12 +1402,13 @@ void RBBITableBuilder::exportTable(void *where) { U_ASSERT (sd->fAccepting <= 255); U_ASSERT (sd->fLookAhead <= 255); U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 255); - row->r8.fAccepting = sd->fAccepting; - row->r8.fLookAhead = sd->fLookAhead; - row->r8.fTagsIdx = sd->fTagsIdx; + RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; + r8->fAccepting = sd->fAccepting; + r8->fLookAhead = sd->fLookAhead; + r8->fTagsIdx = sd->fTagsIdx; for (col=0; colfDtran->elementAti(col) <= kMaxStateFor8BitsTable); - row->r8.fNextState[col] = sd->fDtran->elementAti(col); + r8->fNextState[col] = sd->fDtran->elementAti(col); } } else { U_ASSERT (sd->fAccepting <= 0xffff); @@ -1603,12 +1604,13 @@ void RBBITableBuilder::exportSafeTable(void *where) { UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(state); RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); if (use8BitsForSafeTable()) { - row->r8.fAccepting = 0; - row->r8.fLookAhead = 0; - row->r8.fTagsIdx = 0; + RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; + r8->fAccepting = 0; + r8->fLookAhead = 0; + r8->fTagsIdx = 0; for (col=0; colcharAt(col) <= kMaxStateFor8BitsTable); - row->r8.fNextState[col] = static_cast(rowString->charAt(col)); + r8->fNextState[col] = static_cast(rowString->charAt(col)); } } else { row->r16.fAccepting = 0; diff --git a/deps/icu-small/source/common/uloc.cpp b/deps/icu-small/source/common/uloc.cpp index 522f33dbe243a9..ebfbb506508c1b 100644 --- a/deps/icu-small/source/common/uloc.cpp +++ b/deps/icu-small/source/common/uloc.cpp @@ -877,6 +877,9 @@ uloc_setKeywordValue(const char* keywordName, if(U_FAILURE(*status)) { return -1; } + if (*status == U_STRING_NOT_TERMINATED_WARNING) { + *status = U_ZERO_ERROR; + } if (keywordName == NULL || keywordName[0] == 0 || bufferCapacity <= 1) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; @@ -914,6 +917,7 @@ uloc_setKeywordValue(const char* keywordName, startSearchHere = (char*)locale_getKeywordsStart(buffer); if(startSearchHere == NULL || (startSearchHere[1]==0)) { if(keywordValueLen == 0) { /* no keywords = nothing to remove */ + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); return bufLen; } @@ -933,6 +937,7 @@ uloc_setKeywordValue(const char* keywordName, startSearchHere += keywordNameLen; *startSearchHere++ = '='; uprv_strcpy(startSearchHere, keywordValueBuffer); + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); return needLen; } /* end shortcut - no @ */ @@ -1047,13 +1052,27 @@ uloc_setKeywordValue(const char* keywordName, if (!handledInputKeyAndValue || U_FAILURE(*status)) { /* if input key/value specified removal of a keyword not present in locale, or * there was an error in CharString.append, leave original locale alone. */ + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); return bufLen; } // needLen = length of the part before '@' needLen = (int32_t)(startSearchHere - buffer); - return needLen + updatedKeysAndValues.extract( + // Check to see can we fit the startSearchHere, if not, return + // U_BUFFER_OVERFLOW_ERROR without copy updatedKeysAndValues into it. + // We do this because this API function does not behave like most others: + // It promises never to set a U_STRING_NOT_TERMINATED_WARNING. + // When the contents fits but without the terminating NUL, in this case we need to not change + // the buffer contents and return with a buffer overflow error. + int32_t appendLength = updatedKeysAndValues.length(); + if (appendLength >= bufferCapacity - needLen) { + *status = U_BUFFER_OVERFLOW_ERROR; + return needLen + appendLength; + } + needLen += updatedKeysAndValues.extract( startSearchHere, bufferCapacity - needLen, *status); + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); + return needLen; } /* ### ID parsing implementation **************************************************/ diff --git a/deps/icu-small/source/common/unicode/docmain.h b/deps/icu-small/source/common/unicode/docmain.h index f09d7e1dc29cec..14491494c5ca7a 100644 --- a/deps/icu-small/source/common/unicode/docmain.h +++ b/deps/icu-small/source/common/unicode/docmain.h @@ -143,6 +143,11 @@ * icu::MessageFormat * * + * List Formatting + * ulistformatter.h + * icu::ListFormatter + * + * * Number Formatting
(includes currency and unit formatting) * unumberformatter.h, unum.h * icu::number::NumberFormatter (ICU 60+) or icu::NumberFormat (older versions) diff --git a/deps/icu-small/source/common/unicode/urename.h b/deps/icu-small/source/common/unicode/urename.h index 20232cd209c2d6..fe59fdd893d940 100644 --- a/deps/icu-small/source/common/unicode/urename.h +++ b/deps/icu-small/source/common/unicode/urename.h @@ -1137,6 +1137,7 @@ #define ulocimp_toLanguageTag U_ICU_ENTRY_POINT_RENAME(ulocimp_toLanguageTag) #define ulocimp_toLegacyKey U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyKey) #define ulocimp_toLegacyType U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyType) +#define ultag_getTKeyStart U_ICU_ENTRY_POINT_RENAME(ultag_getTKeyStart) #define ultag_isExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isExtensionSubtags) #define ultag_isLanguageSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isLanguageSubtag) #define ultag_isPrivateuseValueSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isPrivateuseValueSubtags) diff --git a/deps/icu-small/source/common/unicode/uvernum.h b/deps/icu-small/source/common/unicode/uvernum.h index a4cbb9e0fe8661..a46481a3fe610c 100644 --- a/deps/icu-small/source/common/unicode/uvernum.h +++ b/deps/icu-small/source/common/unicode/uvernum.h @@ -66,7 +66,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.6 */ -#define U_ICU_VERSION_MINOR_NUM 1 +#define U_ICU_VERSION_MINOR_NUM 2 /** The current ICU patchlevel version as an integer. * This value will change in the subsequent releases of ICU @@ -139,7 +139,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.4 */ -#define U_ICU_VERSION "68.1" +#define U_ICU_VERSION "68.2" /** * The current ICU library major version number as a string, for library name suffixes. @@ -158,7 +158,7 @@ /** Data version in ICU4C. * @internal ICU 4.4 Internal Use Only **/ -#define U_ICU_DATA_VERSION "68.1" +#define U_ICU_DATA_VERSION "68.2" #endif /* U_HIDE_INTERNAL_API */ /*=========================================================================== diff --git a/deps/icu-small/source/common/wintz.cpp b/deps/icu-small/source/common/wintz.cpp index 047f4290f10d0e..6805297a38a161 100644 --- a/deps/icu-small/source/common/wintz.cpp +++ b/deps/icu-small/source/common/wintz.cpp @@ -36,17 +36,58 @@ U_NAMESPACE_BEGIN +// Note these constants and the struct are only used when dealing with the fallback path for RDP sesssions. + +// This is the location of the time zones in the registry on Vista+ systems. +// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information +#define WINDOWS_TIMEZONES_REG_KEY_PATH L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" + +// Max length for a registry key is 255. +1 for null. +// See: https://docs.microsoft.com/windows/win32/sysinfo/registry-element-size-limits +#define WINDOWS_MAX_REG_KEY_LENGTH 256 + +#if U_PLATFORM_HAS_WINUWP_API == 0 + +// This is the layout of the TZI binary value in the registry. +// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information +typedef struct _REG_TZI_FORMAT { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +} REG_TZI_FORMAT; + +#endif // U_PLATFORM_HAS_WINUWP_API + /** -* Main Windows time zone detection function. -* Returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure. +* This is main Windows time zone detection function. +* +* It returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure. +* +* We use the Win32 API GetDynamicTimeZoneInformation (which is available since Vista) to get the current time zone info, +* as this API returns a non-localized time zone name which can be then mapped to an ICU time zone. +* +* However, in some RDP/terminal services situations, this struct isn't always fully complete, and the TimeZoneKeyName +* field of the struct might be NULL. This can happen with some 3rd party RDP clients, and also when using older versions +* of the RDP protocol, which don't send the newer TimeZoneKeyNamei information and only send the StandardName and DaylightName. +* +* Since these 3rd party clients and older RDP clients only send the pre-Vista time zone information to the server, this means that we +* need to fallback on using the pre-Vista methods to determine the time zone. This unfortunately requires examining the registry directly +* in order to try and determine the current time zone. +* +* Note that this can however still fail in some cases though if the client and server are using different languages, as the StandardName +* that is sent by client is localized in the client's language. However, we must compare this to the names that are on the server, which +* are localized in registry using the server's language. Despite that, this is the best we can do. * -* Note: We use the Win32 API GetDynamicTimeZoneInformation (available since Vista+) to get the current time zone info. -* This API returns a non-localized time zone name, which is mapped to an ICU time zone ID (~ Olsen ID). +* Note: This fallback method won't work for the UWP version though, as we can't use the registry APIs in UWP. +* +* Once we have the current Windows time zone, then we can then map it to an ICU time zone ID (~ Olsen ID). */ U_CAPI const char* U_EXPORT2 uprv_detectWindowsTimeZone() { - // Obtain the DYNAMIC_TIME_ZONE_INFORMATION info to get the non-localized time zone name. + // We first try to obtain the time zone directly by using the TimeZoneKeyName field of the DYNAMIC_TIME_ZONE_INFORMATION struct. DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI; uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI)); SYSTEMTIME systemTimeAllZero; @@ -86,22 +127,138 @@ uprv_detectWindowsTimeZone() // Note '-' before 'utcOffsetMin'. The timezone ID's sign convention // is that a timezone ahead of UTC is Etc/GMT- and a timezone // behind UTC is Etc/GMT+. - int ret = snprintf(gmtOffsetTz, UPRV_LENGTHOF(gmtOffsetTz), "Etc/GMT%+d", -utcOffsetMins / 60); + int ret = snprintf(gmtOffsetTz, UPRV_LENGTHOF(gmtOffsetTz), "Etc/GMT%+ld", -utcOffsetMins / 60); if (ret > 0 && ret < UPRV_LENGTHOF(gmtOffsetTz)) { return uprv_strdup(gmtOffsetTz); } } } - // If DST is NOT disabled, but we have an empty TimeZoneKeyName, then it is unclear - // what we should do as this should not happen. + // If DST is NOT disabled, but the TimeZoneKeyName field of the struct is NULL, then we may be dealing with a + // RDP/terminal services session where the 'Time Zone Redirection' feature is enabled. However, either the RDP + // client sent the server incomplete info (some 3rd party RDP clients only send the StandardName and DaylightName, + // but do not send the important TimeZoneKeyName), or if the RDP server has not appropriately populated the struct correctly. + // + // In this case we unfortunately have no choice but to fallback to using the pre-Vista method of determining the + // time zone, which requires examining the registry directly. + // + // Note that this can however still fail though if the client and server are using different languages, as the StandardName + // that is sent by client is *localized* in the client's language. However, we must compare this to the names that are + // on the server, which are *localized* in registry using the server's language. + // + // One other note is that this fallback method doesn't work for the UWP version, as we can't use the registry APIs. + + // windowsTimeZoneName will point at timezoneSubKeyName if we had to fallback to using the registry, and we found a match. + WCHAR timezoneSubKeyName[WINDOWS_MAX_REG_KEY_LENGTH]; + WCHAR *windowsTimeZoneName = dynamicTZI.TimeZoneKeyName; + if (dynamicTZI.TimeZoneKeyName[0] == 0) { + +// We can't use the registry APIs in the UWP version. +#if U_PLATFORM_HAS_WINUWP_API == 1 + (void)timezoneSubKeyName; // suppress unused variable warnings. return nullptr; +#else + // Open the path to the time zones in the Windows registry. + LONG ret; + HKEY hKeyAllTimeZones = nullptr; + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, WINDOWS_TIMEZONES_REG_KEY_PATH, 0, KEY_READ, + reinterpret_cast(&hKeyAllTimeZones)); + + if (ret != ERROR_SUCCESS) { + // If we can't open the key, then we can't do much, so fail. + return nullptr; + } + + // Read the number of subkeys under the time zone registry path. + DWORD numTimeZoneSubKeys; + ret = RegQueryInfoKeyW(hKeyAllTimeZones, nullptr, nullptr, nullptr, &numTimeZoneSubKeys, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Examine each of the subkeys to try and find a match for the localized standard name ("Std"). + // + // Note: The name of the time zone subkey itself is not localized, but the "Std" name is localized. This means + // that we could fail to find a match if the RDP client and RDP server are using different languages, but unfortunately + // there isn't much we can do about it. + HKEY hKeyTimeZoneSubKey = nullptr; + ULONG registryValueType; + WCHAR registryStandardName[WINDOWS_MAX_REG_KEY_LENGTH]; + + for (DWORD i = 0; i < numTimeZoneSubKeys; i++) { + // Note: RegEnumKeyExW wants the size of the buffer in characters. + DWORD size = UPRV_LENGTHOF(timezoneSubKeyName); + ret = RegEnumKeyExW(hKeyAllTimeZones, i, timezoneSubKeyName, &size, nullptr, nullptr, nullptr, nullptr); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + ret = RegOpenKeyExW(hKeyAllTimeZones, timezoneSubKeyName, 0, KEY_READ, + reinterpret_cast(&hKeyTimeZoneSubKey)); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Note: RegQueryValueExW wants the size of the buffer in bytes. + size = sizeof(registryStandardName); + ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"Std", nullptr, ®istryValueType, + reinterpret_cast(registryStandardName), &size); + + if (ret != ERROR_SUCCESS || registryValueType != REG_SZ) { + RegCloseKey(hKeyTimeZoneSubKey); + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Note: wcscmp does an ordinal (byte) comparison. + if (wcscmp(reinterpret_cast(registryStandardName), dynamicTZI.StandardName) == 0) { + // Since we are comparing the *localized* time zone name, it's possible that some languages might use + // the same string for more than one time zone. Thus we need to examine the TZI data in the registry to + // compare the GMT offset (the bias), and the DST transition dates, to ensure it's the same time zone + // as the currently reported one. + REG_TZI_FORMAT registryTziValue; + uprv_memset(®istryTziValue, 0, sizeof(registryTziValue)); + + // Note: RegQueryValueExW wants the size of the buffer in bytes. + DWORD timezoneTziValueSize = sizeof(registryTziValue); + ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"TZI", nullptr, ®istryValueType, + reinterpret_cast(®istryTziValue), &timezoneTziValueSize); + + if (ret == ERROR_SUCCESS) { + if ((dynamicTZI.Bias == registryTziValue.Bias) && + (memcmp((const void *)&dynamicTZI.StandardDate, (const void *)®istryTziValue.StandardDate, sizeof(SYSTEMTIME)) == 0) && + (memcmp((const void *)&dynamicTZI.DaylightDate, (const void *)®istryTziValue.DaylightDate, sizeof(SYSTEMTIME)) == 0)) + { + // We found a matching time zone. + windowsTimeZoneName = timezoneSubKeyName; + break; + } + } + } + RegCloseKey(hKeyTimeZoneSubKey); + hKeyTimeZoneSubKey = nullptr; + } + + if (hKeyTimeZoneSubKey != nullptr) { + RegCloseKey(hKeyTimeZoneSubKey); + } + if (hKeyAllTimeZones != nullptr) { + RegCloseKey(hKeyAllTimeZones); + } +#endif // U_PLATFORM_HAS_WINUWP_API } CharString winTZ; UErrorCode status = U_ZERO_ERROR; - winTZ.appendInvariantChars(UnicodeString(TRUE, dynamicTZI.TimeZoneKeyName, -1), status); + winTZ.appendInvariantChars(UnicodeString(TRUE, windowsTimeZoneName, -1), status); // Map Windows Timezone name (non-localized) to ICU timezone ID (~ Olson timezone id). StackUResourceBundle winTZBundle; @@ -123,18 +280,29 @@ uprv_detectWindowsTimeZone() int regionCodeLen = GetGeoInfoW(geoId, GEO_ISO2, regionCodeW, UPRV_LENGTHOF(regionCodeW), 0); const UChar *icuTZ16 = nullptr; - int32_t tzLen; + int32_t tzListLen = 0; if (regionCodeLen != 0) { for (int i = 0; i < UPRV_LENGTHOF(regionCodeW); i++) { regionCode[i] = static_cast(regionCodeW[i]); } - icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), regionCode, &tzLen, &status); + icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), regionCode, &tzListLen, &status); } if (regionCodeLen == 0 || U_FAILURE(status)) { // fallback to default "001" (world) status = U_ZERO_ERROR; - icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), "001", &tzLen, &status); + icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), "001", &tzListLen, &status); + } + + // Note: We want the first entry in the string returned by ures_getStringByKey. + // However this string can be a space delimited list of timezones: + // Ex: "America/New_York America/Detroit America/Indiana/Petersburg ..." + // We need to stop at the first space, so we pass tzLen (instead of tzListLen) to appendInvariantChars below. + int32_t tzLen = 0; + if (tzListLen > 0) { + while (!(icuTZ16[tzLen] == u'\0' || icuTZ16[tzLen] == u' ')) { + tzLen++; + } } // Note: cloneData returns nullptr if the status is a failure, so this diff --git a/deps/icu-small/source/data/in/icudt68l.dat.bz2 b/deps/icu-small/source/data/in/icudt68l.dat.bz2 index fcee4ecfa17efe..8fd32b7471d648 100644 Binary files a/deps/icu-small/source/data/in/icudt68l.dat.bz2 and b/deps/icu-small/source/data/in/icudt68l.dat.bz2 differ diff --git a/deps/icu-small/source/i18n/dtitvfmt.cpp b/deps/icu-small/source/i18n/dtitvfmt.cpp index a913dc43c808d6..d6ec501af88701 100644 --- a/deps/icu-small/source/i18n/dtitvfmt.cpp +++ b/deps/icu-small/source/i18n/dtitvfmt.cpp @@ -1422,7 +1422,11 @@ DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, if ( field == UCAL_AM_PM ) { fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); if ( !pattern.isEmpty() ) { - setIntervalPattern(field, pattern); + UBool suppressDayPeriodField = fSkeleton.indexOf(CAP_J) != -1; + UnicodeString adjustIntervalPattern; + adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, + suppressDayPeriodField, adjustIntervalPattern); + setIntervalPattern(field, adjustIntervalPattern); } return false; } @@ -1694,27 +1698,23 @@ DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); if (suppressDayPeriodField) { - adjustedPtn.findAndReplace(UnicodeString(LOW_A), UnicodeString()); - adjustedPtn.findAndReplace(UnicodeString(" "), UnicodeString(" ")); + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString()); + findReplaceInPattern(adjustedPtn, UnicodeString(" "), UnicodeString(" ")); adjustedPtn.trim(); } if ( differenceInfo == 2 ) { if (inputSkeleton.indexOf(LOW_Z) != -1) { - adjustedPtn.findAndReplace(UnicodeString(LOW_V), - UnicodeString(LOW_Z)); - } - if (inputSkeleton.indexOf(CAP_K) != -1) { - adjustedPtn.findAndReplace(UnicodeString(LOW_H), - UnicodeString(CAP_K)); - } - if (inputSkeleton.indexOf(LOW_K) != -1) { - adjustedPtn.findAndReplace(UnicodeString(CAP_H), - UnicodeString(LOW_K)); - } - if (inputSkeleton.indexOf(LOW_B) != -1) { - adjustedPtn.findAndReplace(UnicodeString(LOW_A), - UnicodeString(LOW_B)); - } + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), UnicodeString(LOW_Z)); + } + if (inputSkeleton.indexOf(CAP_K) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_H), UnicodeString(CAP_K)); + } + if (inputSkeleton.indexOf(LOW_K) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(CAP_H), UnicodeString(LOW_K)); + } + if (inputSkeleton.indexOf(LOW_B) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString(LOW_B)); + } } if (adjustedPtn.indexOf(LOW_A) != -1 && bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] == 0) { bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] = 1; @@ -1792,6 +1792,39 @@ DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, } } +void +DateIntervalFormat::findReplaceInPattern(UnicodeString& targetString, + const UnicodeString& strToReplace, + const UnicodeString& strToReplaceWith) { + int32_t firstQuoteIndex = targetString.indexOf(u'\''); + if (firstQuoteIndex == -1) { + targetString.findAndReplace(strToReplace, strToReplaceWith); + } else { + UnicodeString result; + UnicodeString source = targetString; + + while (firstQuoteIndex >= 0) { + int32_t secondQuoteIndex = source.indexOf(u'\'', firstQuoteIndex + 1); + if (secondQuoteIndex == -1) { + secondQuoteIndex = source.length() - 1; + } + + UnicodeString unquotedText(source, 0, firstQuoteIndex); + UnicodeString quotedText(source, firstQuoteIndex, secondQuoteIndex - firstQuoteIndex + 1); + + unquotedText.findAndReplace(strToReplace, strToReplaceWith); + result += unquotedText; + result += quotedText; + + source.remove(0, secondQuoteIndex + 1); + firstQuoteIndex = source.indexOf(u'\''); + } + source.findAndReplace(strToReplace, strToReplaceWith); + result += source; + targetString = result; + } +} + void diff --git a/deps/icu-small/source/i18n/formatted_string_builder.cpp b/deps/icu-small/source/i18n/formatted_string_builder.cpp index 5aabc31cc4391b..b370f14f2ac4ff 100644 --- a/deps/icu-small/source/i18n/formatted_string_builder.cpp +++ b/deps/icu-small/source/i18n/formatted_string_builder.cpp @@ -276,6 +276,11 @@ int32_t FormattedStringBuilder::prepareForInsertHelper(int32_t index, int32_t co char16_t *oldChars = getCharPtr(); Field *oldFields = getFieldPtr(); if (fLength + count > oldCapacity) { + if ((fLength + count) > INT32_MAX / 2) { + // If we continue, then newCapacity will overlow int32_t in the next line. + status = U_INPUT_TOO_LONG_ERROR; + return -1; + } int32_t newCapacity = (fLength + count) * 2; int32_t newZero = newCapacity / 2 - (fLength + count) / 2; @@ -330,12 +335,14 @@ int32_t FormattedStringBuilder::prepareForInsertHelper(int32_t index, int32_t co fZero = newZero; fLength += count; } + U_ASSERT((fZero + index) >= 0); return fZero + index; } int32_t FormattedStringBuilder::remove(int32_t index, int32_t count) { // TODO: Reset the heap here? (If the string after removal can fit on stack?) int32_t position = index + fZero; + U_ASSERT(position >= 0); uprv_memmove2(getCharPtr() + position, getCharPtr() + position + count, sizeof(char16_t) * (fLength - index - count)); diff --git a/deps/icu-small/source/i18n/formattedval_impl.h b/deps/icu-small/source/i18n/formattedval_impl.h index 8005b0abb4d848..1e6eb1e639f809 100644 --- a/deps/icu-small/source/i18n/formattedval_impl.h +++ b/deps/icu-small/source/i18n/formattedval_impl.h @@ -117,6 +117,12 @@ class FormattedValueFieldPositionIteratorImpl : public UMemory, public Formatted }; +// Internal struct that must be exported for MSVC +struct U_I18N_API SpanInfo { + int32_t spanValue; + int32_t length; +}; + // Export an explicit template instantiation of the MaybeStackArray that // is used as a data member of CEBuffer. // @@ -126,7 +132,7 @@ class FormattedValueFieldPositionIteratorImpl : public UMemory, public Formatted // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. // #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -template class U_I18N_API MaybeStackArray; +template class U_I18N_API MaybeStackArray; #endif /** @@ -162,13 +168,19 @@ class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public Format return fString; } - void appendSpanIndex(int32_t index); - void prependSpanIndex(int32_t index); + /** + * Adds additional metadata used for span fields. + * + * spanValue: the index of the list item, for example. + * length: the length of the span, used to split adjacent fields. + */ + void appendSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status); + void prependSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status); private: FormattedStringBuilder fString; FormattedStringBuilder::Field fNumericField; - MaybeStackArray spanIndices; + MaybeStackArray spanIndices; bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; static bool isIntOrGroup(FormattedStringBuilder::Field field); diff --git a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp index b2ae4c34c0a66d..84c2d00666c2be 100644 --- a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp +++ b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp @@ -46,19 +46,19 @@ Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UE UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool - return nextPositionImpl(cfpos, fNumericField, status) ? TRUE : FALSE; + return nextPositionImpl(cfpos, fNumericField, status) ? true : false; } UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const { int32_t rawField = fp.getField(); if (rawField == FieldPosition::DONT_CARE) { - return FALSE; + return false; } if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) { status = U_ILLEGAL_ARGUMENT_ERROR; - return FALSE; + return false; } ConstrainedFieldPosition cfpos; @@ -67,7 +67,7 @@ UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErr if (nextPositionImpl(cfpos, kUndefinedField, status)) { fp.setBeginIndex(cfpos.getStart()); fp.setEndIndex(cfpos.getLimit()); - return TRUE; + return true; } // Special case: fraction should start after integer if fraction is not present @@ -85,7 +85,7 @@ UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErr fp.setEndIndex(i - fString.fZero); } - return FALSE; + return false; } void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih, @@ -103,23 +103,12 @@ static constexpr Field kEndField = Field(0xf, 0xf); bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const { int32_t fieldStart = -1; Field currField = kUndefinedField; - UFieldCategory spanCategory = UFIELD_CATEGORY_UNDEFINED; - int32_t spanValue; for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) { Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField; // Case 1: currently scanning a field. if (currField != kUndefinedField) { if (currField != _field) { int32_t end = i - fString.fZero; - // Handle span fields; don't trim them - if (spanCategory != UFIELD_CATEGORY_UNDEFINED) { - cfpos.setState( - spanCategory, - spanValue, - fieldStart, - end); - return true; - } // Grouping separators can be whitespace; don't throw them out! if (isTrimmable(currField)) { end = trimBack(i - fString.fZero); @@ -182,13 +171,11 @@ bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& if (elementField == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) && cfpos.matchesField(elementField.getCategory(), elementField.getField()) && (cfpos.getLimit() < i - fString.fZero || cfpos.getCategory() != elementField.getCategory())) { - // Re-wind to the beginning of the field and then emit it - int32_t j = i - 1; - for (; j >= fString.fZero && fString.getFieldPtr()[j] == fString.getFieldPtr()[i-1]; j--) {} + int64_t si = cfpos.getInt64IterationContext() - 1; cfpos.setState( elementField.getCategory(), elementField.getField(), - j - fString.fZero + 1, + i - fString.fZero - spanIndices[si].length, i - fString.fZero); return true; } @@ -203,22 +190,28 @@ bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& } // Case 3: check for field starting at this position // Case 3a: Need to add a SpanField - if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) - // don't return the same field twice in a row: - && (i == fString.fZero - || fString.getFieldPtr()[i-1].getCategory() != UFIELD_CATEGORY_LIST - || fString.getFieldPtr()[i-1].getField() != ULISTFMT_ELEMENT_FIELD)) { + if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { int64_t si = cfpos.getInt64IterationContext(); - spanValue = spanIndices[si]; + int32_t spanValue = spanIndices[si].spanValue; + int32_t length = spanIndices[si].length; cfpos.setInt64IterationContext(si + 1); if (cfpos.matchesField(UFIELD_CATEGORY_LIST_SPAN, spanValue)) { - spanCategory = UFIELD_CATEGORY_LIST_SPAN; + UFieldCategory spanCategory = UFIELD_CATEGORY_LIST_SPAN; fieldStart = i - fString.fZero; - currField = _field; + int32_t end = fieldStart + length; + cfpos.setState( + spanCategory, + spanValue, + fieldStart, + end); + return true; + } else { + // Failed to match; jump ahead + i += length - 1; continue; } } - // Case 3b: No SpanField or SpanField did not match + // Case 3b: No SpanField if (cfpos.matchesField(_field.getCategory(), _field.getField())) { fieldStart = i - fString.fZero; currField = _field; @@ -226,24 +219,40 @@ bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& } U_ASSERT(currField == kUndefinedField); + // Always set the position to the end so that we don't revisit previous sections + cfpos.setState( + cfpos.getCategory(), + cfpos.getField(), + fString.fLength, + fString.fLength); return false; } -void FormattedValueStringBuilderImpl::appendSpanIndex(int32_t position) { - if (spanIndices.getCapacity() <= position) { - spanIndices.resize(position * 2); +void FormattedValueStringBuilderImpl::appendSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + U_ASSERT(spanIndices.getCapacity() >= spanValue); + if (spanIndices.getCapacity() == spanValue) { + if (!spanIndices.resize(spanValue * 2, spanValue)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } } - spanIndices[position] = position; + spanIndices[spanValue] = {spanValue, length}; } -void FormattedValueStringBuilderImpl::prependSpanIndex(int32_t position) { - if (spanIndices.getCapacity() <= position) { - spanIndices.resize(position * 2); +void FormattedValueStringBuilderImpl::prependSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + U_ASSERT(spanIndices.getCapacity() >= spanValue); + if (spanIndices.getCapacity() == spanValue) { + if (!spanIndices.resize(spanValue * 2, spanValue)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } } - for (int32_t i = 0; i < position; i++) { + for (int32_t i = spanValue - 1; i >= 0; i--) { spanIndices[i+1] = spanIndices[i]; } - spanIndices[0] = position; + spanIndices[0] = {spanValue, length}; } bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) { diff --git a/deps/icu-small/source/i18n/listformatter.cpp b/deps/icu-small/source/i18n/listformatter.cpp index ab04ac9080a5ab..be0d16bc7f52b3 100644 --- a/deps/icu-small/source/i18n/listformatter.cpp +++ b/deps/icu-small/source/i18n/listformatter.cpp @@ -16,6 +16,10 @@ * created by: Umesh P. Nair */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + #include "cmemory.h" #include "unicode/fpositer.h" // FieldPositionIterator #include "unicode/listformatter.h" @@ -171,21 +175,21 @@ PatternHandler* createPatternHandler( UErrorCode& status) { if (uprv_strcmp(lang, "es") == 0) { // Spanish - UnicodeString spanishYStr(TRUE, spanishY, -1); + UnicodeString spanishYStr(true, spanishY, -1); bool twoIsY = two == spanishYStr; bool endIsY = end == spanishYStr; if (twoIsY || endIsY) { - UnicodeString replacement(TRUE, spanishE, -1); + UnicodeString replacement(true, spanishE, -1); return new ContextualHandler( shouldChangeToE, twoIsY ? replacement : two, two, endIsY ? replacement : end, end, status); } - UnicodeString spanishOStr(TRUE, spanishO, -1); + UnicodeString spanishOStr(true, spanishO, -1); bool twoIsO = two == spanishOStr; bool endIsO = end == spanishOStr; if (twoIsO || endIsO) { - UnicodeString replacement(TRUE, spanishU, -1); + UnicodeString replacement(true, spanishU, -1); return new ContextualHandler( shouldChangeToU, twoIsO ? replacement : two, two, @@ -193,11 +197,11 @@ PatternHandler* createPatternHandler( } } else if (uprv_strcmp(lang, "he") == 0 || uprv_strcmp(lang, "iw") == 0) { // Hebrew - UnicodeString hebrewVavStr(TRUE, hebrewVav, -1); + UnicodeString hebrewVavStr(true, hebrewVav, -1); bool twoIsVav = two == hebrewVavStr; bool endIsVav = end == hebrewVavStr; if (twoIsVav || endIsVav) { - UnicodeString replacement(TRUE, hebrewVavDash, -1); + UnicodeString replacement(true, hebrewVavDash, -1); return new ContextualHandler( shouldChangeToVavDash, twoIsVav ? replacement : two, two, @@ -238,7 +242,6 @@ ListFormatInternal(const ListFormatInternal &other) : }; -#if !UCONFIG_NO_FORMATTING class FormattedListData : public FormattedValueStringBuilderImpl { public: FormattedListData(UErrorCode&) : FormattedValueStringBuilderImpl(kUndefinedField) {} @@ -248,7 +251,6 @@ class FormattedListData : public FormattedValueStringBuilderImpl { FormattedListData::~FormattedListData() = default; UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedList) -#endif static Hashtable* listPatternHash = nullptr; @@ -257,7 +259,7 @@ U_CDECL_BEGIN static UBool U_CALLCONV uprv_listformatter_cleanup() { delete listPatternHash; listPatternHash = nullptr; - return TRUE; + return true; } static void U_CALLCONV @@ -350,7 +352,6 @@ const ListFormatInternal* ListFormatter::getListFormatInternal( return result; } -#if !UCONFIG_NO_FORMATTING static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) { switch (type) { case ULISTFMT_TYPE_AND: @@ -394,7 +395,6 @@ static const char* typeWidthToStyleString(UListFormatterType type, UListFormatte return nullptr; } -#endif static const UChar solidus = 0x2F; static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/" @@ -515,14 +515,9 @@ ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { } ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { -#if !UCONFIG_NO_FORMATTING return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode); -#else - return createInstance(locale, "standard", errorCode); -#endif } -#if !UCONFIG_NO_FORMATTING ListFormatter* ListFormatter::createInstance( const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) { const char* style = typeWidthToStyleString(type, width); @@ -532,7 +527,6 @@ ListFormatter* ListFormatter::createInstance( } return createInstance(locale, style, errorCode); } -#endif ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode); @@ -573,7 +567,7 @@ class FormattedListBuilder { start, {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, status); - data->appendSpanIndex(0); + data->appendSpanInfo(0, start.length(), status); } } @@ -609,7 +603,7 @@ class FormattedListBuilder { next, {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, status); - data->appendSpanIndex(position); + data->appendSpanInfo(position, next.length(), status); data->getStringRef().append( temp.tempSubString(offsets[1]), {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, @@ -628,7 +622,7 @@ class FormattedListBuilder { next, {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, status); - data->prependSpanIndex(position); + data->prependSpanInfo(position, next.length(), status); data->getStringRef().insert( 0, temp.tempSubStringBetween(0, offsets[1]), @@ -660,7 +654,6 @@ UnicodeString& ListFormatter::format( int32_t index, int32_t &offset, UErrorCode& errorCode) const { -#if !UCONFIG_NO_FORMATTING int32_t initialOffset = appendTo.length(); auto result = formatStringsToValue(items, nItems, errorCode); UnicodeStringAppendable appendable(appendTo); @@ -671,11 +664,9 @@ UnicodeString& ListFormatter::format( result.nextPosition(cfpos, errorCode); offset = initialOffset + cfpos.getStart(); } -#endif return appendTo; } -#if !UCONFIG_NO_FORMATTING FormattedList ListFormatter::formatStringsToValue( const UnicodeString items[], int32_t nItems, @@ -741,7 +732,8 @@ FormattedList ListFormatter::formatStringsToValue( return FormattedList(result.data.orphan()); } } -#endif U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/measunit.cpp b/deps/icu-small/source/i18n/measunit.cpp index dab3abb5e21ff6..ece83177625513 100644 --- a/deps/icu-small/source/i18n/measunit.cpp +++ b/deps/icu-small/source/i18n/measunit.cpp @@ -33,7 +33,8 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureUnit) // update this code, refer to: // http://site.icu-project.org/design/formatting/measureformat/updating-measure-unit // -// Start generated code for measunit.cpp +// Start generated code +// TODO(ICU-21076): improve how this generated code is produced. // Maps from Type ID to offset in gSubTypes. static const int32_t gOffsets[] = { @@ -54,15 +55,44 @@ static const int32_t gOffsets[] = { 404, 408, 423, - 424, - 430, - 440, - 444, - 448, + 426, + 432, + 442, + 446, 450, - 484 + 452, + 486 }; +// TODO: FIX CODE GENERATION - leaving this here but commented-out to make it +// clear that we no longer want this array. We needed it for only one thing: efficient checking of "currency". +// +// static const int32_t gIndexes[] = { +// 0, +// 2, +// 7, +// 17, +// 25, +// 29, +// 29, +// 40, +// 56, +// 60, +// 69, +// 71, +// 75, +// 83, +// 105, +// 109, +// 124, +// 127, +// 133, +// 143, +// 147, +// 151, +// 153, +// 187 +// }; static const int32_t kCurrencyOffset = 5; // Must be sorted alphabetically. @@ -517,7 +547,9 @@ static const char * const gSubTypes[] = { "solar-mass", "stone", "ton", - "", + "", // TODO(ICU-21076): manual edit of what should have been generated by Java. + "percent", // TODO(ICU-21076): regenerate, deal with duplication. + "permille", // TODO(ICU-21076): regenerate, deal with duplication. "gigawatt", "horsepower", "kilowatt", @@ -580,6 +612,8 @@ static const char * const gSubTypes[] = { "teaspoon" }; +// unitPerUnitToSingleUnit no longer in use! TODO: remove from code-generation code. + // Shortcuts to the base unit in order to make the default constructor fast static const int32_t kBaseTypeIdx = 16; static const int32_t kBaseSubTypeIdx = 0; @@ -2056,7 +2090,7 @@ MeasureUnit MeasureUnit::getTeaspoon() { return MeasureUnit(22, 33); } -// End generated code for measunit.cpp +// End generated code static int32_t binarySearch( const char * const * array, int32_t start, int32_t end, StringPiece key) { @@ -2105,7 +2139,9 @@ MeasureUnit &MeasureUnit::operator=(const MeasureUnit &other) { if (this == &other) { return *this; } - delete fImpl; + if (fImpl != nullptr) { + delete fImpl; + } if (other.fImpl) { ErrorCode localStatus; fImpl = new MeasureUnitImpl(other.fImpl->copy(localStatus)); @@ -2126,7 +2162,9 @@ MeasureUnit &MeasureUnit::operator=(MeasureUnit &&other) noexcept { if (this == &other) { return *this; } - delete fImpl; + if (fImpl != nullptr) { + delete fImpl; + } fImpl = other.fImpl; other.fImpl = nullptr; fTypeId = other.fTypeId; @@ -2139,8 +2177,10 @@ MeasureUnit *MeasureUnit::clone() const { } MeasureUnit::~MeasureUnit() { - delete fImpl; - fImpl = nullptr; + if (fImpl != nullptr) { + delete fImpl; + fImpl = nullptr; + } } const char *MeasureUnit::getType() const { @@ -2237,11 +2277,9 @@ StringEnumeration* MeasureUnit::getAvailableTypes(UErrorCode &errorCode) { } bool MeasureUnit::findBySubType(StringPiece subType, MeasureUnit* output) { - // Sanity checking kCurrencyOffset and final entry in gOffsets - U_ASSERT(uprv_strcmp(gTypes[kCurrencyOffset], "currency") == 0); - U_ASSERT(gOffsets[UPRV_LENGTHOF(gOffsets) - 1] == UPRV_LENGTHOF(gSubTypes)); - for (int32_t t = 0; t < UPRV_LENGTHOF(gOffsets) - 1; t++) { + // Ensure kCurrencyOffset is set correctly + U_ASSERT(uprv_strcmp(gTypes[kCurrencyOffset], "currency") == 0); // Skip currency units if (t == kCurrencyOffset) { continue; @@ -2298,8 +2336,10 @@ void MeasureUnit::initCurrency(StringPiece isoCurrency) { void MeasureUnit::setTo(int32_t typeId, int32_t subTypeId) { fTypeId = typeId; fSubTypeId = subTypeId; - delete fImpl; - fImpl = nullptr; + if (fImpl != nullptr) { + delete fImpl; + fImpl = nullptr; + } } int32_t MeasureUnit::getOffset() const { diff --git a/deps/icu-small/source/i18n/number_skeletons.cpp b/deps/icu-small/source/i18n/number_skeletons.cpp index e6d94d27b2ba5b..028525a589db91 100644 --- a/deps/icu-small/source/i18n/number_skeletons.cpp +++ b/deps/icu-small/source/i18n/number_skeletons.cpp @@ -732,6 +732,7 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se case STEM_CURRENCY: CHECK_NULL(seen, unit, status); + CHECK_NULL(seen, perUnit, status); return STATE_CURRENCY_UNIT; case STEM_INTEGER_WIDTH: @@ -1500,32 +1501,33 @@ bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UEr } bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { - if (utils::unitIsCurrency(macros.unit)) { + MeasureUnit unit = macros.unit; + if (!utils::unitIsBaseUnit(macros.perUnit)) { + if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) { + status = U_UNSUPPORTED_ERROR; + return false; + } + unit = unit.product(macros.perUnit.reciprocal(status), status); + } + + if (utils::unitIsCurrency(unit)) { sb.append(u"currency/", -1); - CurrencyUnit currency(macros.unit, status); + CurrencyUnit currency(unit, status); if (U_FAILURE(status)) { return false; } blueprint_helpers::generateCurrencyOption(currency, sb, status); return true; - } else if (utils::unitIsBaseUnit(macros.unit)) { + } else if (utils::unitIsBaseUnit(unit)) { // Default value is not shown in normalized form return false; - } else if (utils::unitIsPercent(macros.unit)) { + } else if (utils::unitIsPercent(unit)) { sb.append(u"percent", -1); return true; - } else if (utils::unitIsPermille(macros.unit)) { + } else if (utils::unitIsPermille(unit)) { sb.append(u"permille", -1); return true; } else { - MeasureUnit unit = macros.unit; - if (utils::unitIsCurrency(macros.perUnit)) { - status = U_UNSUPPORTED_ERROR; - return false; - } - if (!utils::unitIsBaseUnit(macros.perUnit)) { - unit = unit.product(macros.perUnit.reciprocal(status), status); - } sb.append(u"unit/", -1); sb.append(unit.getIdentifier()); return true; diff --git a/deps/icu-small/source/i18n/unicode/dtitvfmt.h b/deps/icu-small/source/i18n/unicode/dtitvfmt.h index 3d20d8e9c44990..4a1ab801a04c9d 100644 --- a/deps/icu-small/source/i18n/unicode/dtitvfmt.h +++ b/deps/icu-small/source/i18n/unicode/dtitvfmt.h @@ -1037,6 +1037,17 @@ class U_I18N_API DateIntervalFormat : public Format { UBool suppressDayPeriodField, UnicodeString& adjustedIntervalPattern); + /** + * Does the same thing as UnicodeString::findAndReplace(), except that it won't perform + * the substitution inside quoted literal text. + * @param targetString The string to perform the find-replace operation on. + * @param strToReplace The string to search for and replace in the target string. + * @param strToReplaceWith The string to substitute in wherever `stringToReplace` was found. + */ + static void U_EXPORT2 findReplaceInPattern(UnicodeString& targetString, + const UnicodeString& strToReplace, + const UnicodeString& strToReplaceWith); + /** * Concat a single date pattern with a time interval pattern, * set it into the intervalPatterns, while field is time field. diff --git a/deps/icu-small/source/i18n/unicode/listformatter.h b/deps/icu-small/source/i18n/unicode/listformatter.h index a969a8744dcf58..eddb5dab6701b3 100644 --- a/deps/icu-small/source/i18n/unicode/listformatter.h +++ b/deps/icu-small/source/i18n/unicode/listformatter.h @@ -23,6 +23,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_FORMATTING + #include "unicode/unistr.h" #include "unicode/locid.h" #include "unicode/formattedvalue.h" @@ -65,7 +67,6 @@ struct ListFormatData : public UMemory { */ -#if !UCONFIG_NO_FORMATTING /** * An immutable class containing the result of a list formatting operation. * @@ -135,7 +136,6 @@ class U_I18N_API FormattedList : public UMemory, public FormattedValue { : fData(nullptr), fErrorCode(errorCode) {} friend class ListFormatter; }; -#endif // !UCONFIG_NO_FORMATTING /** @@ -185,8 +185,6 @@ class U_I18N_API ListFormatter : public UObject{ */ static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode); -#ifndef U_HIDE_DRAFT_API -#if !UCONFIG_NO_FORMATTING /** * Creates a ListFormatter for the given locale, list type, and style. * @@ -195,12 +193,10 @@ class U_I18N_API ListFormatter : public UObject{ * @param width The width of formatting to use. * @param errorCode ICU error code, set if no data available for the given locale. * @return A ListFormatter object created from internal data derived from CLDR data. - * @draft ICU 67 + * @stable ICU 67 */ static ListFormatter* createInstance( const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode); -#endif /* !UCONFIG_NO_FORMATTING */ -#endif /* U_HIDE_DRAFT_API */ #ifndef U_HIDE_INTERNAL_API /** @@ -239,7 +235,6 @@ class U_I18N_API ListFormatter : public UObject{ UnicodeString& format(const UnicodeString items[], int32_t n_items, UnicodeString& appendTo, UErrorCode& errorCode) const; -#if !UCONFIG_NO_FORMATTING /** * Formats a list of strings to a FormattedList, which exposes field * position information. The FormattedList contains more information than @@ -255,7 +250,6 @@ class U_I18N_API ListFormatter : public UObject{ const UnicodeString items[], int32_t n_items, UErrorCode& errorCode) const; -#endif // !UCONFIG_NO_FORMATTING #ifndef U_HIDE_INTERNAL_API /** @@ -296,6 +290,8 @@ class U_I18N_API ListFormatter : public UObject{ U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // __LISTFORMATTER_H__ diff --git a/deps/icu-small/source/i18n/unicode/measunit.h b/deps/icu-small/source/i18n/unicode/measunit.h index b9f732ae99011d..ed8773c7710f3e 100644 --- a/deps/icu-small/source/i18n/unicode/measunit.h +++ b/deps/icu-small/source/i18n/unicode/measunit.h @@ -3519,6 +3519,7 @@ class U_I18N_API MeasureUnit: public UObject { */ static MeasureUnit getTeaspoon(); + // End generated createXXX methods protected: diff --git a/deps/icu-small/source/i18n/unicode/ulistformatter.h b/deps/icu-small/source/i18n/unicode/ulistformatter.h index 3dfa9f2d56171f..28a1e580370006 100644 --- a/deps/icu-small/source/i18n/unicode/ulistformatter.h +++ b/deps/icu-small/source/i18n/unicode/ulistformatter.h @@ -62,17 +62,16 @@ typedef enum UListFormatterField { ULISTFMT_ELEMENT_FIELD } UListFormatterField; -#ifndef U_HIDE_DRAFT_API /** * Type of meaning expressed by the list. * - * @draft ICU 67 + * @stable ICU 67 */ typedef enum UListFormatterType { /** * Conjunction formatting, e.g. "Alice, Bob, Charlie, and Delta". * - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_TYPE_AND, @@ -80,14 +79,14 @@ typedef enum UListFormatterType { * Disjunction (or alternative, or simply one of) formatting, e.g. * "Alice, Bob, Charlie, or Delta". * - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_TYPE_OR, /** * Formatting of a list of values with units, e.g. "5 pounds, 12 ounces". * - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_TYPE_UNITS } UListFormatterType; @@ -95,29 +94,28 @@ typedef enum UListFormatterType { /** * Verbosity level of the list patterns. * - * @draft ICU 67 + * @stable ICU 67 */ typedef enum UListFormatterWidth { /** * Use list formatting with full words (no abbreviations) when possible. * - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_WIDTH_WIDE, /** * Use list formatting of typical length. - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_WIDTH_SHORT, /** * Use list formatting of the shortest possible length. - * @draft ICU 67 + * @stable ICU 67 */ ULISTFMT_WIDTH_NARROW, } UListFormatterWidth; -#endif /* U_HIDE_DRAFT_API */ /** * Open a new UListFormatter object using the rules for a given locale. @@ -141,7 +139,6 @@ U_CAPI UListFormatter* U_EXPORT2 ulistfmt_open(const char* locale, UErrorCode* status); -#ifndef U_HIDE_DRAFT_API /** * Open a new UListFormatter object appropriate for the given locale, list type, * and style. @@ -162,12 +159,11 @@ ulistfmt_open(const char* locale, * @return * A pointer to a UListFormatter object for the specified locale, * or NULL if an error occurred. - * @draft ICU 67 + * @stable ICU 67 */ U_CAPI UListFormatter* U_EXPORT2 ulistfmt_openForType(const char* locale, UListFormatterType type, UListFormatterWidth width, UErrorCode* status); -#endif /* U_HIDE_DRAFT_API */ /** * Close a UListFormatter object. Once closed it may no longer be used. diff --git a/tools/icu/current_ver.dep b/tools/icu/current_ver.dep index b4caab129071e1..1c7f45879621c0 100644 --- a/tools/icu/current_ver.dep +++ b/tools/icu/current_ver.dep @@ -1,6 +1,6 @@ [ { - "url": "https://github.com/unicode-org/icu/releases/download/release-68-1/icu4c-68_1-src.tgz", - "md5": "6a99b541ea01f271257b121a4433c7c0" + "url": "https://github.com/unicode-org/icu/releases/download/release-68-2/icu4c-68_2-src.tgz", + "md5": "c21cbdfe31a1e325afe765a16f907d20" } ]