Merge pull request #44507 from bruvzg/icu_update_68_2

ICU: Update to upstream release 68.2
This commit is contained in:
Rémi Verschelde 2020-12-21 12:36:47 +01:00 committed by GitHub
commit 0923494c78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 275 additions and 51 deletions

View file

@ -206,7 +206,7 @@ Files extracted from upstream source:
## International Components for Unicode
- Upstream: https://github.com/unicode-org/icu
- Version: 68.1
- Version: 68.2
- License: Unicode
Files extracted from upstream source:

View file

@ -23,8 +23,10 @@ Removed from ICU 67
| File | API | ICU 67 | ICU 68 |
|---|---|---|---|
| fmtable.h | const UFormattable* icu::Formattable::toUFormattable() | StableICU 52 | (missing)
| measunit.h | LocalArray<MeasureUnit> icu::MeasureUnit::splitToSingleUnits(int32_t&, UErrorCode&) const | InternalICU 67 | (missing)
| measunit.h | int32_t icu::MeasureUnit::getIndex() const | Internal | (missing)
| measunit.h | <tt>static</tt> MeasureUnit icu::MeasureUnit::resolveUnitPerUnit(const MeasureUnit&amp;, const MeasureUnit&amp;, bool*) | Internal | (missing)
| measunit.h | <tt>static</tt> int32_t icu::MeasureUnit::getIndexCount() | Internal | (missing)
| measunit.h | <tt>static</tt> int32_t icu::MeasureUnit::internalGetIndexForTypeAndSubtype(const char*, const char*) | Internal | (missing)
| nounit.h | UClassID icu::NoUnit::getDynamicClassID() const | DraftICU 60 | (missing)
@ -35,6 +37,7 @@ Removed from ICU 67
| nounit.h | <tt>static</tt> NoUnit icu::NoUnit::permille() | DraftICU 60 | (missing)
| nounit.h | <tt>static</tt> UClassID icu::NoUnit::getStaticClassID() | DraftICU 60 | (missing)
| nounit.h | void* icu::NoUnit::clone() const | DraftICU 60 | (missing)
| uniset.h | const USet* icu::UnicodeSet::toUSet() | StableICU 4.2 | (missing)
## Deprecated
@ -57,6 +60,7 @@ Changed in ICU 68 (old, new)
|---|---|---|---|
| bytestrie.h | BytesTrie&amp; icu::BytesTrie::resetToState64(uint64_t) | Draft→StableICU 65
| bytestrie.h | uint64_t icu::BytesTrie::getState64() const | Draft→StableICU 65
| listformatter.h | <tt>static</tt> ListFormatter* icu::ListFormatter::createInstance(const Locale&amp;, UListFormatterType, UListFormatterWidth, UErrorCode&amp;) | Draft→StableICU 67
| localebuilder.h | UBool icu::LocaleBuilder::copyErrorTo(UErrorCode&amp;) const | Draft→StableICU 65
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::addSupportedLocale(const Locale&amp;) | Draft→StableICU 65
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::operator=(Builder&amp;&amp;) | Draft→StableICU 65
@ -132,6 +136,13 @@ Changed in ICU 68 (old, new)
| ucal.h | int32_t ucal_getHostTimeZone(UChar*, int32_t, UErrorCode*) | Draft→StableICU 65
| ucharstrie.h | UCharsTrie&amp; icu::UCharsTrie::resetToState64(uint64_t) | Draft→StableICU 65
| ucharstrie.h | uint64_t icu::UCharsTrie::getState64() const | Draft→StableICU 65
| ulistformatter.h | UListFormatter* ulistfmt_openForType(const char*, UListFormatterType, UListFormatterWidth, UErrorCode*) | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_AND | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_OR | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_UNITS | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_NARROW | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_SHORT | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_WIDE | Draft→StableICU 67
| uloc.h | UEnumeration* uloc_openAvailableByType(ULocAvailableType, UErrorCode*) | Draft→StableICU 65
| uloc.h | <tt>enum</tt> ULocAvailableType::ULOC_AVAILABLE_DEFAULT | Draft→StableICU 65
| uloc.h | <tt>enum</tt> ULocAvailableType::ULOC_AVAILABLE_ONLY_LEGACY_ALIASES | Draft→StableICU 65
@ -151,6 +162,8 @@ Promoted to stable in ICU 68
|---|---|---|---|
| bytestrie.h | BytesTrie&amp; icu::BytesTrie::resetToState64(uint64_t) | Draft→StableICU 65
| bytestrie.h | uint64_t icu::BytesTrie::getState64() const | Draft→StableICU 65
| fmtable.h | UFormattable* icu::Formattable::toUFormattable() | (missing) | StableICU 52
| listformatter.h | <tt>static</tt> ListFormatter* icu::ListFormatter::createInstance(const Locale&amp;, UListFormatterType, UListFormatterWidth, UErrorCode&amp;) | Draft→StableICU 67
| localebuilder.h | UBool icu::LocaleBuilder::copyErrorTo(UErrorCode&amp;) const | Draft→StableICU 65
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::addSupportedLocale(const Locale&amp;) | Draft→StableICU 65
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::operator=(Builder&amp;&amp;) | Draft→StableICU 65
@ -224,10 +237,18 @@ Promoted to stable in ICU 68
| ucal.h | int32_t ucal_getHostTimeZone(UChar*, int32_t, UErrorCode*) | Draft→StableICU 65
| ucharstrie.h | UCharsTrie&amp; icu::UCharsTrie::resetToState64(uint64_t) | Draft→StableICU 65
| ucharstrie.h | uint64_t icu::UCharsTrie::getState64() const | Draft→StableICU 65
| ulistformatter.h | UListFormatter* ulistfmt_openForType(const char*, UListFormatterType, UListFormatterWidth, UErrorCode*) | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_AND | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_OR | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_UNITS | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_NARROW | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_SHORT | Draft→StableICU 67
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_WIDE | Draft→StableICU 67
| uloc.h | UEnumeration* uloc_openAvailableByType(ULocAvailableType, UErrorCode*) | Draft→StableICU 65
| uloc.h | <tt>enum</tt> ULocAvailableType::ULOC_AVAILABLE_DEFAULT | Draft→StableICU 65
| uloc.h | <tt>enum</tt> ULocAvailableType::ULOC_AVAILABLE_ONLY_LEGACY_ALIASES | Draft→StableICU 65
| uloc.h | <tt>enum</tt> ULocAvailableType::ULOC_AVAILABLE_WITH_LEGACY_ALIASES | Draft→StableICU 65
| uniset.h | USet* icu::UnicodeSet::toUSet() | (missing) | StableICU 4.2
| utrace.h | <tt>enum</tt> UTraceFunctionNumber::UTRACE_UDATA_BUNDLE | Draft→StableICU 65
| utrace.h | <tt>enum</tt> UTraceFunctionNumber::UTRACE_UDATA_DATA_FILE | Draft→StableICU 65
| utrace.h | <tt>enum</tt> UTraceFunctionNumber::UTRACE_UDATA_RES_FILE | Draft→StableICU 65
@ -242,6 +263,7 @@ Added in ICU 68
| dtitvfmt.h | UDisplayContext icu::DateIntervalFormat::getContext(UDisplayContextType, UErrorCode&amp;) const | (missing) | DraftICU 68
| dtitvfmt.h | void icu::DateIntervalFormat::setContext(UDisplayContext, UErrorCode&amp;) | (missing) | DraftICU 68
| dtptngen.h | <tt>static</tt> DateTimePatternGenerator* icu::DateTimePatternGenerator::createInstanceNoStdPat(const Locale&amp;, UErrorCode&amp;) | (missing) | Internal
| fmtable.h | UFormattable* icu::Formattable::toUFormattable() | (missing) | StableICU 52
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::setMaxDistance(const Locale&amp;, const Locale&amp;) | (missing) | DraftICU 68
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::setNoDefaultLocale() | (missing) | DraftICU 68
| localematcher.h | UBool icu::LocaleMatcher::isMatch(const Locale&amp;, const Locale&amp;, UErrorCode&amp;) const | (missing) | DraftICU 68
@ -285,12 +307,14 @@ Added in ICU 68
| numberrangeformatter.h | std::pair&lt; StringClass, StringClass &gt; icu::number::FormattedNumberRange::getDecimalNumbers(UErrorCode&amp;) const | (missing) | DraftICU 68
| plurrule.h | UnicodeString icu::PluralRules::select(const number::FormattedNumberRange&amp;, UErrorCode&amp;) const | (missing) | DraftICU 68
| plurrule.h | UnicodeString icu::PluralRules::select(const number::impl::UFormattedNumberRangeData*, UErrorCode&amp;) const | (missing) | Internal
| plurrule.h | int32_t icu::PluralRules::getSamples(const UnicodeString&amp;, FixedDecimal*, int32_t, UErrorCode&amp;) | (missing) | Internal
| timezone.h | <tt>static</tt> TimeZone* icu::TimeZone::forLocaleOrDefault(const Locale&amp;) | (missing) | Internal
| ucurr.h | <tt>enum</tt> UCurrNameStyle::UCURR_FORMAL_SYMBOL_NAME | (missing) | DraftICU 68
| ucurr.h | <tt>enum</tt> UCurrNameStyle::UCURR_VARIANT_SYMBOL_NAME | (missing) | DraftICU 68
| udateintervalformat.h | UDisplayContext udtitvfmt_getContext(const UDateIntervalFormat*, UDisplayContextType, UErrorCode*) | (missing) | DraftICU 68
| udateintervalformat.h | void udtitvfmt_setContext(UDateIntervalFormat*, UDisplayContext, UErrorCode*) | (missing) | DraftICU 68
| umachine.h | <tt>#define</tt> U_DEFINE_FALSE_AND_TRUE | (missing) | InternalICU 68
| uniset.h | USet* icu::UnicodeSet::toUSet() | (missing) | StableICU 4.2
| unum.h | <tt>enum</tt> UNumberFormatMinimumGroupingDigits::UNUM_MINIMUM_GROUPING_DIGITS_AUTO | (missing) | DraftICU 68
| unum.h | <tt>enum</tt> UNumberFormatMinimumGroupingDigits::UNUM_MINIMUM_GROUPING_DIGITS_MIN2 | (missing) | DraftICU 68
| unumberformatter.h | <tt>enum</tt> UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL | (missing) | DraftICU 68
@ -317,7 +341,6 @@ Other existing drafts in ICU 68
| bytestream.h | void icu::ByteSink::AppendU8(const char*, int32_t) | DraftICU 67 |
| bytestream.h | void icu::ByteSink::AppendU8(const char8_t*, int32_t) | DraftICU 67 |
| dtptngen.h | UDateFormatHourCycle icu::DateTimePatternGenerator::getDefaultHourCycle(UErrorCode&amp;) const | DraftICU 67 |
| listformatter.h | <tt>static</tt> ListFormatter* icu::ListFormatter::createInstance(const Locale&amp;, UListFormatterType, UListFormatterWidth, UErrorCode&amp;) | DraftICU 67 |
| localematcher.h | Builder&amp; icu::LocaleMatcher::Builder::setDirection(ULocMatchDirection) | DraftICU 67 |
| localematcher.h | <tt>enum</tt> ULocMatchDirection::ULOCMATCH_DIRECTION_ONLY_TWO_WAY | DraftICU 67 |
| localematcher.h | <tt>enum</tt> ULocMatchDirection::ULOCMATCH_DIRECTION_WITH_ONE_WAY | DraftICU 67 |
@ -349,13 +372,6 @@ Other existing drafts in ICU 68
| udateintervalformat.h | void udtitvfmt_formatCalendarToResult(const UDateIntervalFormat*, UCalendar*, UCalendar*, UFormattedDateInterval*, UErrorCode*) | DraftICU 67 |
| udateintervalformat.h | void udtitvfmt_formatToResult(const UDateIntervalFormat*, UDate, UDate, UFormattedDateInterval*, UErrorCode*) | DraftICU 67 |
| udatpg.h | UDateFormatHourCycle udatpg_getDefaultHourCycle(const UDateTimePatternGenerator*, UErrorCode*) | DraftICU 67 |
| ulistformatter.h | UListFormatter* ulistfmt_openForType(const char*, UListFormatterType, UListFormatterWidth, UErrorCode*) | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_AND | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_OR | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterType::ULISTFMT_TYPE_UNITS | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_NARROW | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_SHORT | DraftICU 67 |
| ulistformatter.h | <tt>enum</tt> UListFormatterWidth::ULISTFMT_WIDTH_WIDE | DraftICU 67 |
| uregex.h | <tt>enum</tt> URegexpFlag::UREGEX_CANON_EQ | DraftICU 2.4 |
| utrace.h | <tt>enum</tt> UTraceFunctionNumber::UTRACE_UBRK_CREATE_BREAK_ENGINE | DraftICU 67 |
| utrace.h | <tt>enum</tt> UTraceFunctionNumber::UTRACE_UBRK_CREATE_CHARACTER | DraftICU 67 |
@ -373,7 +389,7 @@ This section shows cases where the signature was "simplified" for the sake of co
## Colophon
Contents generated by StableAPI tool on Wed Sep 30 17:44:26 PDT 2020
Contents generated by StableAPI tool on Fri Oct 23 11:32:42 PDT 2020
Copyright © 2019 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html

View file

@ -725,9 +725,14 @@ public:
}
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 @@ protected:
template<typename T, int32_t stackCapacity = 8>
class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
public:
using MemoryPool<T, stackCapacity>::MemoryPool;
using MemoryPool<T, stackCapacity>::operator=;
template<typename... Args>
T* emplaceBack(Args&&... args) {
return this->create(args...);

View file

@ -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 @@ public:
// 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<CharString> 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);
}

View file

@ -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; col<catCount; col++) {
U_ASSERT (sd->fDtran->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; col<catCount; col++) {
U_ASSERT(rowString->charAt(col) <= kMaxStateFor8BitsTable);
row->r8.fNextState[col] = static_cast<uint8_t>(rowString->charAt(col));
r8->fNextState[col] = static_cast<uint8_t>(rowString->charAt(col));
}
} else {
row->r16.fAccepting = 0;

View file

@ -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 **************************************************/

View file

@ -143,6 +143,11 @@
* <td>icu::MessageFormat</td>
* </tr>
* <tr>
* <td>List Formatting</td>
* <td>ulistformatter.h</td>
* <td>icu::ListFormatter</td>
* </tr>
* <tr>
* <td>Number Formatting<br/>(includes currency and unit formatting)</td>
* <td>unumberformatter.h, unum.h</td>
* <td>icu::number::NumberFormatter (ICU 60+) or icu::NumberFormat (older versions)</td>

View file

@ -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)

View file

@ -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 */
/*===========================================================================

View file

@ -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.
*
* 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).
* 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: 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-<offset> and a timezone
// behind UTC is Etc/GMT+<offset>.
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<PHKEY>(&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<PHKEY>(&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, &registryValueType,
reinterpret_cast<LPBYTE>(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<WCHAR *>(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(&registryTziValue, 0, sizeof(registryTziValue));
// Note: RegQueryValueExW wants the size of the buffer in bytes.
DWORD timezoneTziValueSize = sizeof(registryTziValue);
ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"TZI", nullptr, &registryValueType,
reinterpret_cast<LPBYTE>(&registryTziValue), &timezoneTziValueSize);
if (ret == ERROR_SUCCESS) {
if ((dynamicTZI.Bias == registryTziValue.Bias) &&
(memcmp((const void *)&dynamicTZI.StandardDate, (const void *)&registryTziValue.StandardDate, sizeof(SYSTEMTIME)) == 0) &&
(memcmp((const void *)&dynamicTZI.DaylightDate, (const void *)&registryTziValue.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<char>(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

Binary file not shown.