mirror of
https://github.com/nodejs/node.git
synced 2025-04-28 13:40:37 +00:00
deps: update icu to 77.1
Some checks failed
Coverage Linux (without intl) / coverage-linux-without-intl (push) Waiting to run
Coverage Linux / coverage-linux (push) Waiting to run
Coverage Windows / coverage-windows (push) Waiting to run
Test and upload documentation to artifacts / build-docs (push) Waiting to run
Linters / lint-addon-docs (push) Waiting to run
Linters / lint-cpp (push) Waiting to run
Linters / format-cpp (push) Waiting to run
Linters / lint-js-and-md (push) Waiting to run
Linters / lint-py (push) Waiting to run
Linters / lint-yaml (push) Waiting to run
Linters / lint-sh (push) Waiting to run
Linters / lint-codeowners (push) Waiting to run
Linters / lint-pr-url (push) Waiting to run
Linters / lint-readme (push) Waiting to run
Notify on Push / Notify on Force Push on `main` (push) Waiting to run
Notify on Push / Notify on Push on `main` that lacks metadata (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
License update / update_license (push) Has been cancelled
Find inactive collaborators / find (push) Has been cancelled
Some checks failed
Coverage Linux (without intl) / coverage-linux-without-intl (push) Waiting to run
Coverage Linux / coverage-linux (push) Waiting to run
Coverage Windows / coverage-windows (push) Waiting to run
Test and upload documentation to artifacts / build-docs (push) Waiting to run
Linters / lint-addon-docs (push) Waiting to run
Linters / lint-cpp (push) Waiting to run
Linters / format-cpp (push) Waiting to run
Linters / lint-js-and-md (push) Waiting to run
Linters / lint-py (push) Waiting to run
Linters / lint-yaml (push) Waiting to run
Linters / lint-sh (push) Waiting to run
Linters / lint-codeowners (push) Waiting to run
Linters / lint-pr-url (push) Waiting to run
Linters / lint-readme (push) Waiting to run
Notify on Push / Notify on Force Push on `main` (push) Waiting to run
Notify on Push / Notify on Push on `main` that lacks metadata (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
License update / update_license (push) Has been cancelled
Find inactive collaborators / find (push) Has been cancelled
PR-URL: https://github.com/nodejs/node/pull/57455 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Steven R Loomis <srl295@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com>
This commit is contained in:
parent
8ccbfb656a
commit
ab9660b55a
2
deps/icu-small/LICENSE
vendored
2
deps/icu-small/LICENSE
vendored
@ -2,7 +2,7 @@ UNICODE LICENSE V3
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 2016-2024 Unicode, Inc.
|
||||
Copyright © 2016-2025 Unicode, Inc.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement. BY
|
||||
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
|
||||
|
4
deps/icu-small/README-FULL-ICU.txt
vendored
4
deps/icu-small/README-FULL-ICU.txt
vendored
@ -1,8 +1,8 @@
|
||||
ICU sources - auto generated by shrink-icu-src.py
|
||||
|
||||
This directory contains the ICU subset used by --with-intl=full-icu
|
||||
It is a strict subset of ICU 76 source files with the following exception(s):
|
||||
* deps/icu-small/source/data/in/icudt76l.dat.bz2 : compressed data file
|
||||
It is a strict subset of ICU 77 source files with the following exception(s):
|
||||
* deps/icu-small/source/data/in/icudt77l.dat.bz2 : compressed data file
|
||||
|
||||
|
||||
To rebuild this directory, see ../../tools/icu/README.md
|
||||
|
59
deps/icu-small/source/common/brkiter.cpp
vendored
59
deps/icu-small/source/common/brkiter.cpp
vendored
@ -59,7 +59,7 @@ BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &st
|
||||
{
|
||||
char fnbuff[256];
|
||||
char ext[4]={'\0'};
|
||||
CharString actualLocale;
|
||||
CharString actual;
|
||||
int32_t size;
|
||||
const char16_t* brkfname = nullptr;
|
||||
UResourceBundle brkRulesStack;
|
||||
@ -94,7 +94,7 @@ BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &st
|
||||
|
||||
// Use the string if we found it
|
||||
if (U_SUCCESS(status) && brkfname) {
|
||||
actualLocale.append(ures_getLocaleInternal(brkName, &status), -1, status);
|
||||
actual.append(ures_getLocaleInternal(brkName, &status), -1, status);
|
||||
|
||||
char16_t* extStart=u_strchr(brkfname, 0x002e);
|
||||
int len = 0;
|
||||
@ -123,10 +123,9 @@ BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &st
|
||||
if (U_SUCCESS(status) && result != nullptr) {
|
||||
U_LOCALE_BASED(locBased, *(BreakIterator*)result);
|
||||
|
||||
locBased.setLocaleIDs(ures_getLocaleByType(b, ULOC_VALID_LOCALE, &status),
|
||||
actualLocale.data());
|
||||
uprv_strncpy(result->requestLocale, loc.getName(), ULOC_FULLNAME_CAPACITY);
|
||||
result->requestLocale[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate
|
||||
locBased.setLocaleIDs(ures_getLocaleByType(b, ULOC_VALID_LOCALE, &status),
|
||||
actual.data(), status);
|
||||
LocaleBased::setLocaleID(loc.getName(), result->requestLocale, status);
|
||||
}
|
||||
|
||||
ures_close(b);
|
||||
@ -206,26 +205,32 @@ BreakIterator::getAvailableLocales(int32_t& count)
|
||||
|
||||
BreakIterator::BreakIterator()
|
||||
{
|
||||
*validLocale = *actualLocale = *requestLocale = 0;
|
||||
}
|
||||
|
||||
BreakIterator::BreakIterator(const BreakIterator &other) : UObject(other) {
|
||||
uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale));
|
||||
uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale));
|
||||
uprv_strncpy(requestLocale, other.requestLocale, sizeof(requestLocale));
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(other.validLocale, other.actualLocale, status);
|
||||
LocaleBased::setLocaleID(other.requestLocale, requestLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
|
||||
BreakIterator &BreakIterator::operator =(const BreakIterator &other) {
|
||||
if (this != &other) {
|
||||
uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale));
|
||||
uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale));
|
||||
uprv_strncpy(requestLocale, other.requestLocale, sizeof(requestLocale));
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(other.validLocale, other.actualLocale, status);
|
||||
LocaleBased::setLocaleID(other.requestLocale, requestLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BreakIterator::~BreakIterator()
|
||||
{
|
||||
delete validLocale;
|
||||
delete actualLocale;
|
||||
delete requestLocale;
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
@ -394,7 +399,7 @@ BreakIterator::createInstance(const Locale& loc, int32_t kind, UErrorCode& statu
|
||||
// revisit this in ICU 3.0 and clean it up/fix it/remove it.
|
||||
if (U_SUCCESS(status) && (result != nullptr) && *actualLoc.getName() != 0) {
|
||||
U_LOCALE_BASED(locBased, *result);
|
||||
locBased.setLocaleIDs(actualLoc.getName(), actualLoc.getName());
|
||||
locBased.setLocaleIDs(actualLoc.getName(), actualLoc.getName(), status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -488,6 +493,7 @@ BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status)
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
delete result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -496,20 +502,25 @@ BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status)
|
||||
|
||||
Locale
|
||||
BreakIterator::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
if (type == ULOC_REQUESTED_LOCALE) {
|
||||
return {requestLocale};
|
||||
if (U_FAILURE(status)) {
|
||||
return Locale::getRoot();
|
||||
}
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocale(type, status);
|
||||
if (type == ULOC_REQUESTED_LOCALE) {
|
||||
return requestLocale == nullptr ?
|
||||
Locale::getRoot() : Locale(requestLocale->data());
|
||||
}
|
||||
return LocaleBased::getLocale(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
const char *
|
||||
BreakIterator::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
if (type == ULOC_REQUESTED_LOCALE) {
|
||||
return requestLocale;
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocaleID(type, status);
|
||||
if (type == ULOC_REQUESTED_LOCALE) {
|
||||
return requestLocale == nullptr ? "" : requestLocale->data();
|
||||
}
|
||||
return LocaleBased::getLocaleID(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
|
||||
@ -536,8 +547,10 @@ int32_t BreakIterator::getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UE
|
||||
}
|
||||
|
||||
BreakIterator::BreakIterator (const Locale& valid, const Locale& actual) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, (*this));
|
||||
locBased.setLocaleIDs(valid, actual);
|
||||
locBased.setLocaleIDs(valid.getName(), actual.getName(), status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
11
deps/icu-small/source/common/charstr.cpp
vendored
11
deps/icu-small/source/common/charstr.cpp
vendored
@ -70,6 +70,15 @@ CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
CharString &CharString::copyFrom(StringPiece s, UErrorCode &errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return *this;
|
||||
}
|
||||
len = 0;
|
||||
append(s, errorCode);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int32_t CharString::lastIndexOf(char c) const {
|
||||
for(int32_t i=len; i>0;) {
|
||||
if(buffer[--i]==c) {
|
||||
@ -143,7 +152,7 @@ CharString &CharString::append(const char *s, int32_t sLength, UErrorCode &error
|
||||
return *this;
|
||||
}
|
||||
|
||||
CharString &CharString::appendNumber(int32_t number, UErrorCode &status) {
|
||||
CharString &CharString::appendNumber(int64_t number, UErrorCode &status) {
|
||||
if (number < 0) {
|
||||
this->append('-', status);
|
||||
if (U_FAILURE(status)) {
|
||||
|
3
deps/icu-small/source/common/charstr.h
vendored
3
deps/icu-small/source/common/charstr.h
vendored
@ -74,6 +74,7 @@ public:
|
||||
* use a UErrorCode where memory allocations might be needed.
|
||||
*/
|
||||
CharString ©From(const CharString &other, UErrorCode &errorCode);
|
||||
CharString ©From(StringPiece s, UErrorCode &errorCode);
|
||||
|
||||
UBool isEmpty() const { return len==0; }
|
||||
int32_t length() const { return len; }
|
||||
@ -135,7 +136,7 @@ public:
|
||||
}
|
||||
CharString &append(const char *s, int32_t sLength, UErrorCode &status);
|
||||
|
||||
CharString &appendNumber(int32_t number, UErrorCode &status);
|
||||
CharString &appendNumber(int64_t number, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Returns a writable buffer for appending and writes the buffer's capacity to
|
||||
|
2311
deps/icu-small/source/common/localefallback_data.h
vendored
2311
deps/icu-small/source/common/localefallback_data.h
vendored
File diff suppressed because it is too large
Load Diff
72
deps/icu-small/source/common/locbased.cpp
vendored
72
deps/icu-small/source/common/locbased.cpp
vendored
@ -12,44 +12,84 @@
|
||||
*/
|
||||
#include "locbased.h"
|
||||
#include "cstring.h"
|
||||
#include "charstr.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
Locale LocaleBased::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
const char* id = getLocaleID(type, status);
|
||||
Locale LocaleBased::getLocale(const CharString* valid, const CharString* actual,
|
||||
ULocDataLocaleType type, UErrorCode& status) {
|
||||
const char* id = getLocaleID(valid, actual, type, status);
|
||||
return Locale(id != nullptr ? id : "");
|
||||
}
|
||||
|
||||
const char* LocaleBased::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
const char* LocaleBased::getLocaleID(const CharString* valid, const CharString* actual,
|
||||
ULocDataLocaleType type, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case ULOC_VALID_LOCALE:
|
||||
return valid;
|
||||
return valid == nullptr ? "" : valid->data();
|
||||
case ULOC_ACTUAL_LOCALE:
|
||||
return actual;
|
||||
return actual == nullptr ? "" : actual->data();
|
||||
default:
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LocaleBased::setLocaleIDs(const char* validID, const char* actualID) {
|
||||
if (validID != nullptr) {
|
||||
uprv_strncpy(valid, validID, ULOC_FULLNAME_CAPACITY);
|
||||
valid[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate
|
||||
}
|
||||
if (actualID != nullptr) {
|
||||
uprv_strncpy(actual, actualID, ULOC_FULLNAME_CAPACITY);
|
||||
actual[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate
|
||||
void LocaleBased::setLocaleIDs(const CharString* validID, const CharString* actualID, UErrorCode& status) {
|
||||
setValidLocaleID(validID, status);
|
||||
setActualLocaleID(actualID,status);
|
||||
}
|
||||
void LocaleBased::setLocaleIDs(const char* validID, const char* actualID, UErrorCode& status) {
|
||||
setValidLocaleID(validID, status);
|
||||
setActualLocaleID(actualID,status);
|
||||
}
|
||||
|
||||
void LocaleBased::setLocaleID(const char* id, CharString*& dest, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (id == nullptr || *id == 0) {
|
||||
delete dest;
|
||||
dest = nullptr;
|
||||
} else {
|
||||
if (dest == nullptr) {
|
||||
dest = new CharString(id, status);
|
||||
if (dest == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dest->copyFrom(id, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocaleBased::setLocaleIDs(const Locale& validID, const Locale& actualID) {
|
||||
uprv_strcpy(valid, validID.getName());
|
||||
uprv_strcpy(actual, actualID.getName());
|
||||
void LocaleBased::setLocaleID(const CharString* id, CharString*& dest, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (id == nullptr || id->isEmpty()) {
|
||||
delete dest;
|
||||
dest = nullptr;
|
||||
} else {
|
||||
if (dest == nullptr) {
|
||||
dest = new CharString(*id, status);
|
||||
if (dest == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dest->copyFrom(*id, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LocaleBased::equalIDs(const CharString* left, const CharString* right) {
|
||||
// true if both are nullptr
|
||||
if (left == nullptr && right == nullptr) return true;
|
||||
// false if only one is nullptr
|
||||
if (left == nullptr || right == nullptr) return false;
|
||||
return *left == *right;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
64
deps/icu-small/source/common/locbased.h
vendored
64
deps/icu-small/source/common/locbased.h
vendored
@ -19,13 +19,14 @@
|
||||
/**
|
||||
* Macro to declare a locale LocaleBased wrapper object for the given
|
||||
* object, which must have two members named `validLocale' and
|
||||
* `actualLocale' of size ULOC_FULLNAME_CAPACITY
|
||||
* `actualLocale' of which are pointers to the internal icu::CharString.
|
||||
*/
|
||||
#define U_LOCALE_BASED(varname, objname) \
|
||||
LocaleBased varname((objname).validLocale, (objname).actualLocale)
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class CharString;
|
||||
/**
|
||||
* A utility class that unifies the implementation of getLocale() by
|
||||
* various ICU services. This class is likely to be removed in the
|
||||
@ -41,33 +42,35 @@ class U_COMMON_API LocaleBased : public UMemory {
|
||||
* Construct a LocaleBased wrapper around the two pointers. These
|
||||
* will be aliased for the lifetime of this object.
|
||||
*/
|
||||
inline LocaleBased(char* validAlias, char* actualAlias);
|
||||
|
||||
/**
|
||||
* Construct a LocaleBased wrapper around the two const pointers.
|
||||
* These will be aliased for the lifetime of this object.
|
||||
*/
|
||||
inline LocaleBased(const char* validAlias, const char* actualAlias);
|
||||
inline LocaleBased(CharString*& validAlias, CharString*& actualAlias);
|
||||
|
||||
/**
|
||||
* Return locale meta-data for the service object wrapped by this
|
||||
* object. Either the valid or the actual locale may be
|
||||
* retrieved.
|
||||
* @param valid The valid locale.
|
||||
* @param actual The actual locale.
|
||||
* @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE
|
||||
* @param status input-output error code
|
||||
* @return the indicated locale
|
||||
*/
|
||||
Locale getLocale(ULocDataLocaleType type, UErrorCode& status) const;
|
||||
static Locale getLocale(
|
||||
const CharString* valid, const CharString* actual,
|
||||
ULocDataLocaleType type, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Return the locale ID for the service object wrapped by this
|
||||
* object. Either the valid or the actual locale may be
|
||||
* retrieved.
|
||||
* @param valid The valid locale.
|
||||
* @param actual The actual locale.
|
||||
* @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE
|
||||
* @param status input-output error code
|
||||
* @return the indicated locale ID
|
||||
*/
|
||||
const char* getLocaleID(ULocDataLocaleType type, UErrorCode& status) const;
|
||||
static const char* getLocaleID(
|
||||
const CharString* valid, const CharString* actual,
|
||||
ULocDataLocaleType type, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Set the locale meta-data for the service object wrapped by this
|
||||
@ -75,31 +78,40 @@ class U_COMMON_API LocaleBased : public UMemory {
|
||||
* @param valid the ID of the valid locale
|
||||
* @param actual the ID of the actual locale
|
||||
*/
|
||||
void setLocaleIDs(const char* valid, const char* actual);
|
||||
void setLocaleIDs(const char* valid, const char* actual, UErrorCode& status);
|
||||
void setLocaleIDs(const CharString* valid, const CharString* actual, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Set the locale meta-data for the service object wrapped by this
|
||||
* object.
|
||||
* @param valid the ID of the valid locale
|
||||
* @param actual the ID of the actual locale
|
||||
*/
|
||||
void setLocaleIDs(const Locale& valid, const Locale& actual);
|
||||
static void setLocaleID(const char* id, CharString*& dest, UErrorCode& status);
|
||||
static void setLocaleID(const CharString* id, CharString*& dest, UErrorCode& status);
|
||||
|
||||
static bool equalIDs(const CharString* left, const CharString* right);
|
||||
|
||||
private:
|
||||
|
||||
char* valid;
|
||||
|
||||
char* actual;
|
||||
void setValidLocaleID(const CharString* id, UErrorCode& status);
|
||||
void setActualLocaleID(const CharString* id, UErrorCode& status);
|
||||
void setValidLocaleID(const char* id, UErrorCode& status);
|
||||
void setActualLocaleID(const char* id, UErrorCode& status);
|
||||
|
||||
CharString*& valid;
|
||||
CharString*& actual;
|
||||
};
|
||||
|
||||
inline LocaleBased::LocaleBased(char* validAlias, char* actualAlias) :
|
||||
inline LocaleBased::LocaleBased(CharString*& validAlias, CharString*& actualAlias) :
|
||||
valid(validAlias), actual(actualAlias) {
|
||||
}
|
||||
|
||||
inline LocaleBased::LocaleBased(const char* validAlias,
|
||||
const char* actualAlias) :
|
||||
// ugh: cast away const
|
||||
valid(const_cast<char*>(validAlias)), actual(const_cast<char*>(actualAlias)) {
|
||||
inline void LocaleBased::setValidLocaleID(const CharString* id, UErrorCode& status) {
|
||||
setLocaleID(id, valid, status);
|
||||
}
|
||||
inline void LocaleBased::setActualLocaleID(const CharString* id, UErrorCode& status) {
|
||||
setLocaleID(id, actual, status);
|
||||
}
|
||||
inline void LocaleBased::setValidLocaleID(const char* id, UErrorCode& status) {
|
||||
setLocaleID(id, valid, status);
|
||||
}
|
||||
inline void LocaleBased::setActualLocaleID(const char* id, UErrorCode& status) {
|
||||
setLocaleID(id, actual, status);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
@ -19,6 +19,8 @@
|
||||
* that then do not depend on resource bundle code and display name data.
|
||||
*/
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/brkiter.h"
|
||||
#include "unicode/locid.h"
|
||||
@ -359,7 +361,7 @@ _getStringOrCopyKey(const char *path, const char *locale,
|
||||
return u_terminateUChars(dest, destCapacity, length, &errorCode);
|
||||
}
|
||||
|
||||
using UDisplayNameGetter = icu::CharString(const char*, UErrorCode&);
|
||||
using UDisplayNameGetter = icu::CharString(std::string_view, UErrorCode&);
|
||||
|
||||
int32_t
|
||||
_getDisplayNameForComponent(const char *locale,
|
||||
@ -377,6 +379,10 @@ _getDisplayNameForComponent(const char *locale,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (locale == nullptr) {
|
||||
locale = uloc_getDefault();
|
||||
}
|
||||
|
||||
localStatus = U_ZERO_ERROR;
|
||||
icu::CharString localeBuffer = (*getter)(locale, localStatus);
|
||||
if (U_FAILURE(localStatus)) {
|
||||
|
43
deps/icu-small/source/common/locid.cpp
vendored
43
deps/icu-small/source/common/locid.cpp
vendored
@ -1828,8 +1828,13 @@ ulocimp_isCanonicalizedLocaleForTest(const char* localeName)
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/*This function initializes a Locale from a C locale ID*/
|
||||
Locale& Locale::init(const char* localeID, UBool canonicalize)
|
||||
{
|
||||
return localeID == nullptr ? *this = getDefault() : init(StringPiece{localeID}, canonicalize);
|
||||
}
|
||||
|
||||
/*This function initializes a Locale from a C locale ID*/
|
||||
Locale& Locale::init(StringPiece localeID, UBool canonicalize)
|
||||
{
|
||||
fIsBogus = false;
|
||||
/* Free our current storage */
|
||||
@ -1854,19 +1859,28 @@ Locale& Locale::init(const char* localeID, UBool canonicalize)
|
||||
int32_t length;
|
||||
UErrorCode err;
|
||||
|
||||
if(localeID == nullptr) {
|
||||
// not an error, just set the default locale
|
||||
return *this = getDefault();
|
||||
}
|
||||
|
||||
/* preset all fields to empty */
|
||||
language[0] = script[0] = country[0] = 0;
|
||||
|
||||
const auto parse = [canonicalize](std::string_view localeID,
|
||||
char* name,
|
||||
int32_t nameCapacity,
|
||||
UErrorCode& status) {
|
||||
return ByteSinkUtil::viaByteSinkToTerminatedChars(
|
||||
name, nameCapacity,
|
||||
[&](ByteSink& sink, UErrorCode& status) {
|
||||
if (canonicalize) {
|
||||
ulocimp_canonicalize(localeID, sink, status);
|
||||
} else {
|
||||
ulocimp_getName(localeID, sink, status);
|
||||
}
|
||||
},
|
||||
status);
|
||||
};
|
||||
|
||||
// "canonicalize" the locale ID to ICU/Java format
|
||||
err = U_ZERO_ERROR;
|
||||
length = canonicalize ?
|
||||
uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :
|
||||
uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);
|
||||
length = parse(localeID, fullName, sizeof fullNameBuffer, err);
|
||||
|
||||
if (err == U_BUFFER_OVERFLOW_ERROR || length >= static_cast<int32_t>(sizeof(fullNameBuffer))) {
|
||||
U_ASSERT(baseName == nullptr);
|
||||
@ -1877,9 +1891,7 @@ Locale& Locale::init(const char* localeID, UBool canonicalize)
|
||||
}
|
||||
fullName = newFullName;
|
||||
err = U_ZERO_ERROR;
|
||||
length = canonicalize ?
|
||||
uloc_canonicalize(localeID, fullName, length+1, &err) :
|
||||
uloc_getName(localeID, fullName, length+1, &err);
|
||||
length = parse(localeID, fullName, length + 1, err);
|
||||
}
|
||||
if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
|
||||
/* should never occur */
|
||||
@ -2200,6 +2212,13 @@ Locale::createFromName (const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
Locale U_EXPORT2
|
||||
Locale::createFromName(StringPiece name) {
|
||||
Locale loc("");
|
||||
loc.init(name, false);
|
||||
return loc;
|
||||
}
|
||||
|
||||
Locale U_EXPORT2
|
||||
Locale::createCanonical(const char* name) {
|
||||
Locale loc("");
|
||||
|
16
deps/icu-small/source/common/loclikely.cpp
vendored
16
deps/icu-small/source/common/loclikely.cpp
vendored
@ -300,6 +300,9 @@ ulocimp_addLikelySubtags(const char* localeID,
|
||||
icu::ByteSink& sink,
|
||||
UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (localeID == nullptr) {
|
||||
localeID = uloc_getDefault();
|
||||
}
|
||||
icu::CharString localeBuffer = ulocimp_canonicalize(localeID, status);
|
||||
_uloc_addLikelySubtags(localeBuffer.data(), sink, status);
|
||||
}
|
||||
@ -334,6 +337,9 @@ ulocimp_minimizeSubtags(const char* localeID,
|
||||
bool favorScript,
|
||||
UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (localeID == nullptr) {
|
||||
localeID = uloc_getDefault();
|
||||
}
|
||||
icu::CharString localeBuffer = ulocimp_canonicalize(localeID, status);
|
||||
_uloc_minimizeSubtags(localeBuffer.data(), sink, favorScript, status);
|
||||
}
|
||||
@ -349,7 +355,9 @@ uloc_isRightToLeft(const char *locale) {
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
icu::CharString lang;
|
||||
icu::CharString script;
|
||||
ulocimp_getSubtags(locale, &lang, &script, nullptr, nullptr, nullptr, errorCode);
|
||||
ulocimp_getSubtags(
|
||||
locale == nullptr ? uloc_getDefault() : locale,
|
||||
&lang, &script, nullptr, nullptr, nullptr, errorCode);
|
||||
if (U_FAILURE(errorCode) || script.isEmpty()) {
|
||||
// Fastpath: We know the likely scripts and their writing direction
|
||||
// for some common languages.
|
||||
@ -369,7 +377,7 @@ uloc_isRightToLeft(const char *locale) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return false;
|
||||
}
|
||||
ulocimp_getSubtags(likely.data(), nullptr, &script, nullptr, nullptr, nullptr, errorCode);
|
||||
ulocimp_getSubtags(likely.toStringPiece(), nullptr, &script, nullptr, nullptr, nullptr, errorCode);
|
||||
if (U_FAILURE(errorCode) || script.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
@ -430,7 +438,7 @@ ulocimp_getRegionForSupplementalData(const char *localeID, bool inferRegion,
|
||||
icu::CharString rgBuf = GetRegionFromKey(localeID, "rg", status);
|
||||
if (U_SUCCESS(status) && rgBuf.isEmpty()) {
|
||||
// No valid rg keyword value, try for unicode_region_subtag
|
||||
rgBuf = ulocimp_getRegion(localeID, status);
|
||||
rgBuf = ulocimp_getRegion(localeID == nullptr ? uloc_getDefault() : localeID, status);
|
||||
if (U_SUCCESS(status) && rgBuf.isEmpty() && inferRegion) {
|
||||
// Second check for sd keyword value
|
||||
rgBuf = GetRegionFromKey(localeID, "sd", status);
|
||||
@ -439,7 +447,7 @@ ulocimp_getRegionForSupplementalData(const char *localeID, bool inferRegion,
|
||||
UErrorCode rgStatus = U_ZERO_ERROR;
|
||||
icu::CharString locBuf = ulocimp_addLikelySubtags(localeID, rgStatus);
|
||||
if (U_SUCCESS(rgStatus)) {
|
||||
rgBuf = ulocimp_getRegion(locBuf.data(), status);
|
||||
rgBuf = ulocimp_getRegion(locBuf.toStringPiece(), status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -527,7 +527,7 @@ LSR LikelySubtags::makeMaximizedLsrFrom(const Locale &locale,
|
||||
return {};
|
||||
}
|
||||
const char *name = locale.getName();
|
||||
if (uprv_isAtSign(name[0]) && name[1] == 'x' && name[2] == '=') { // name.startsWith("@x=")
|
||||
if (!returnInputIfUnmatch && uprv_isAtSign(name[0]) && name[1] == 'x' && name[2] == '=') { // name.startsWith("@x=")
|
||||
// Private use language tag x-subtag-subtag... which CLDR changes to
|
||||
// und-x-subtag-subtag...
|
||||
return LSR(name, "", "", LSR::EXPLICIT_LSR);
|
||||
|
3
deps/icu-small/source/common/locresdata.cpp
vendored
3
deps/icu-small/source/common/locresdata.cpp
vendored
@ -161,6 +161,9 @@ _uloc_getOrientationHelper(const char* localeId,
|
||||
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
|
||||
if (localeId == nullptr) {
|
||||
localeId = uloc_getDefault();
|
||||
}
|
||||
icu::CharString localeBuffer = ulocimp_canonicalize(localeId, status);
|
||||
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
|
2
deps/icu-small/source/common/punycode.cpp
vendored
2
deps/icu-small/source/common/punycode.cpp
vendored
@ -193,7 +193,7 @@ u_strToPunycode(const char16_t *src, int32_t srcLength,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src==nullptr || srcLength<-1 || (dest==nullptr && destCapacity!=0)) {
|
||||
if(src==nullptr || srcLength<-1 || destCapacity<0 || (dest==nullptr && destCapacity!=0)) {
|
||||
*pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
2
deps/icu-small/source/common/putil.cpp
vendored
2
deps/icu-small/source/common/putil.cpp
vendored
@ -76,7 +76,7 @@
|
||||
#include <float.h>
|
||||
|
||||
#ifndef U_COMMON_IMPLEMENTATION
|
||||
#error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu
|
||||
#error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/icu/howtouseicu.html
|
||||
#endif
|
||||
|
||||
|
||||
|
118
deps/icu-small/source/common/rbbinode.cpp
vendored
118
deps/icu-small/source/common/rbbinode.cpp
vendored
@ -47,7 +47,10 @@ static int gLastSerial = 0;
|
||||
// Constructor. Just set the fields to reasonable default values.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
RBBINode::RBBINode(NodeType t) : UMemory() {
|
||||
RBBINode::RBBINode(NodeType t, UErrorCode& status) : UMemory() {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
#ifdef RBBI_DEBUG
|
||||
fSerialNum = ++gLastSerial;
|
||||
#endif
|
||||
@ -65,10 +68,13 @@ RBBINode::RBBINode(NodeType t) : UMemory() {
|
||||
fVal = 0;
|
||||
fPrecedence = precZero;
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere
|
||||
fFirstPosSet = new UVector(status);
|
||||
fLastPosSet = new UVector(status);
|
||||
fFollowPos = new UVector(status);
|
||||
if (U_SUCCESS(status) &&
|
||||
(fFirstPosSet == nullptr || fLastPosSet == nullptr || fFollowPos == nullptr)) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (t==opCat) {fPrecedence = precOpCat;}
|
||||
else if (t==opOr) {fPrecedence = precOpOr;}
|
||||
else if (t==opStart) {fPrecedence = precStart;}
|
||||
@ -77,7 +83,10 @@ RBBINode::RBBINode(NodeType t) : UMemory() {
|
||||
}
|
||||
|
||||
|
||||
RBBINode::RBBINode(const RBBINode &other) : UMemory(other) {
|
||||
RBBINode::RBBINode(const RBBINode &other, UErrorCode& status) : UMemory(other) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
#ifdef RBBI_DEBUG
|
||||
fSerialNum = ++gLastSerial;
|
||||
#endif
|
||||
@ -94,10 +103,13 @@ RBBINode::RBBINode(const RBBINode &other) : UMemory(other) {
|
||||
fVal = other.fVal;
|
||||
fRuleRoot = false;
|
||||
fChainIn = other.fChainIn;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere
|
||||
fLastPosSet = new UVector(status);
|
||||
fFollowPos = new UVector(status);
|
||||
if (U_SUCCESS(status) &&
|
||||
(fFirstPosSet == nullptr || fLastPosSet == nullptr || fFollowPos == nullptr)) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -193,27 +205,54 @@ void RBBINode::NRDeleteNode(RBBINode *node) {
|
||||
// references in preparation for generating the DFA tables.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
RBBINode *RBBINode::cloneTree() {
|
||||
constexpr int kRecursiveDepthLimit = 3500;
|
||||
RBBINode *RBBINode::cloneTree(UErrorCode &status, int depth) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
// If the depth of the stack is too deep, we return U_INPUT_TOO_LONG_ERROR
|
||||
// to avoid stack overflow crash.
|
||||
if (depth > kRecursiveDepthLimit) {
|
||||
status = U_INPUT_TOO_LONG_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
RBBINode *n;
|
||||
|
||||
if (fType == RBBINode::varRef) {
|
||||
// If the current node is a variable reference, skip over it
|
||||
// and clone the definition of the variable instead.
|
||||
n = fLeftChild->cloneTree();
|
||||
n = fLeftChild->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (fType == RBBINode::uset) {
|
||||
n = this;
|
||||
} else {
|
||||
n = new RBBINode(*this);
|
||||
n = new RBBINode(*this, status);
|
||||
if (U_FAILURE(status)) {
|
||||
delete n;
|
||||
return nullptr;
|
||||
}
|
||||
// Check for null pointer.
|
||||
if (n != nullptr) {
|
||||
if (fLeftChild != nullptr) {
|
||||
n->fLeftChild = fLeftChild->cloneTree();
|
||||
n->fLeftChild->fParent = n;
|
||||
if (n == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
if (fLeftChild != nullptr) {
|
||||
n->fLeftChild = fLeftChild->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
delete n;
|
||||
return nullptr;
|
||||
}
|
||||
if (fRightChild != nullptr) {
|
||||
n->fRightChild = fRightChild->cloneTree();
|
||||
n->fRightChild->fParent = n;
|
||||
n->fLeftChild->fParent = n;
|
||||
}
|
||||
if (fRightChild != nullptr) {
|
||||
n->fRightChild = fRightChild->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
delete n;
|
||||
return nullptr;
|
||||
}
|
||||
n->fRightChild->fParent = n;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
@ -239,7 +278,6 @@ RBBINode *RBBINode::cloneTree() {
|
||||
// nested references are handled by cloneTree(), not here.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
constexpr int kRecursiveDepthLimit = 3500;
|
||||
RBBINode *RBBINode::flattenVariables(UErrorCode& status, int depth) {
|
||||
if (U_FAILURE(status)) {
|
||||
return this;
|
||||
@ -251,21 +289,34 @@ RBBINode *RBBINode::flattenVariables(UErrorCode& status, int depth) {
|
||||
return this;
|
||||
}
|
||||
if (fType == varRef) {
|
||||
RBBINode *retNode = fLeftChild->cloneTree();
|
||||
if (retNode != nullptr) {
|
||||
retNode->fRuleRoot = this->fRuleRoot;
|
||||
retNode->fChainIn = this->fChainIn;
|
||||
RBBINode *retNode = fLeftChild->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
return this;
|
||||
}
|
||||
retNode->fRuleRoot = this->fRuleRoot;
|
||||
retNode->fChainIn = this->fChainIn;
|
||||
delete this; // TODO: undefined behavior. Fix.
|
||||
return retNode;
|
||||
}
|
||||
|
||||
if (fLeftChild != nullptr) {
|
||||
fLeftChild = fLeftChild->flattenVariables(status, depth+1);
|
||||
if (fLeftChild == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return this;
|
||||
}
|
||||
fLeftChild->fParent = this;
|
||||
}
|
||||
if (fRightChild != nullptr) {
|
||||
fRightChild = fRightChild->flattenVariables(status, depth+1);
|
||||
if (fRightChild == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return this;
|
||||
}
|
||||
fRightChild->fParent = this;
|
||||
}
|
||||
return this;
|
||||
@ -280,7 +331,16 @@ RBBINode *RBBINode::flattenVariables(UErrorCode& status, int depth) {
|
||||
// the left child of the uset node.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
void RBBINode::flattenSets() {
|
||||
void RBBINode::flattenSets(UErrorCode &status, int depth) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// If the depth of the stack is too deep, we return U_INPUT_TOO_LONG_ERROR
|
||||
// to avoid stack overflow crash.
|
||||
if (depth > kRecursiveDepthLimit) {
|
||||
status = U_INPUT_TOO_LONG_ERROR;
|
||||
return;
|
||||
}
|
||||
U_ASSERT(fType != setRef);
|
||||
|
||||
if (fLeftChild != nullptr) {
|
||||
@ -288,11 +348,15 @@ void RBBINode::flattenSets() {
|
||||
RBBINode *setRefNode = fLeftChild;
|
||||
RBBINode *usetNode = setRefNode->fLeftChild;
|
||||
RBBINode *replTree = usetNode->fLeftChild;
|
||||
fLeftChild = replTree->cloneTree();
|
||||
fLeftChild = replTree->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
delete setRefNode;
|
||||
return;
|
||||
}
|
||||
fLeftChild->fParent = this;
|
||||
delete setRefNode;
|
||||
} else {
|
||||
fLeftChild->flattenSets();
|
||||
fLeftChild->flattenSets(status, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,11 +365,15 @@ void RBBINode::flattenSets() {
|
||||
RBBINode *setRefNode = fRightChild;
|
||||
RBBINode *usetNode = setRefNode->fLeftChild;
|
||||
RBBINode *replTree = usetNode->fLeftChild;
|
||||
fRightChild = replTree->cloneTree();
|
||||
fRightChild = replTree->cloneTree(status, depth+1);
|
||||
if (U_FAILURE(status)) {
|
||||
delete setRefNode;
|
||||
return;
|
||||
}
|
||||
fRightChild->fParent = this;
|
||||
delete setRefNode;
|
||||
} else {
|
||||
fRightChild->flattenSets();
|
||||
fRightChild->flattenSets(status, depth+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
deps/icu-small/source/common/rbbinode.h
vendored
8
deps/icu-small/source/common/rbbinode.h
vendored
@ -91,14 +91,14 @@ class RBBINode : public UMemory {
|
||||
UVector *fFollowPos;
|
||||
|
||||
|
||||
RBBINode(NodeType t);
|
||||
RBBINode(const RBBINode &other);
|
||||
RBBINode(NodeType t, UErrorCode& status);
|
||||
RBBINode(const RBBINode &other, UErrorCode& status);
|
||||
~RBBINode();
|
||||
static void NRDeleteNode(RBBINode *node);
|
||||
|
||||
RBBINode *cloneTree();
|
||||
RBBINode *cloneTree(UErrorCode &status, int depth=0);
|
||||
RBBINode *flattenVariables(UErrorCode &status, int depth=0);
|
||||
void flattenSets();
|
||||
void flattenSets(UErrorCode &status, int depth=0);
|
||||
void findNodes(UVector *dest, RBBINode::NodeType kind, UErrorCode &status);
|
||||
|
||||
#ifdef RBBI_DEBUG
|
||||
|
15
deps/icu-small/source/common/rbbiscan.cpp
vendored
15
deps/icu-small/source/common/rbbiscan.cpp
vendored
@ -767,15 +767,24 @@ void RBBIRuleScanner::findSetFor(const UnicodeString &s, RBBINode *node, Unicode
|
||||
c = s.char32At(0);
|
||||
setToAdopt = new UnicodeSet(c, c);
|
||||
}
|
||||
if (setToAdopt == nullptr) {
|
||||
error(U_MEMORY_ALLOCATION_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Make a new uset node to refer to this UnicodeSet
|
||||
// This new uset node becomes the child of the caller's setReference node.
|
||||
//
|
||||
RBBINode *usetNode = new RBBINode(RBBINode::uset);
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
RBBINode *usetNode = new RBBINode(RBBINode::uset, localStatus);
|
||||
if (usetNode == nullptr) {
|
||||
error(U_MEMORY_ALLOCATION_ERROR);
|
||||
localStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(localStatus)) {
|
||||
delete usetNode;
|
||||
error(localStatus);
|
||||
delete setToAdopt;
|
||||
return;
|
||||
}
|
||||
@ -1191,7 +1200,7 @@ RBBINode *RBBIRuleScanner::pushNewNode(RBBINode::NodeType t) {
|
||||
return nullptr;
|
||||
}
|
||||
fNodeStackPtr++;
|
||||
fNodeStack[fNodeStackPtr] = new RBBINode(t);
|
||||
fNodeStack[fNodeStackPtr] = new RBBINode(t, *fRB->fStatus);
|
||||
if (fNodeStack[fNodeStackPtr] == nullptr) {
|
||||
*fRB->fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
|
12
deps/icu-small/source/common/rbbisetb.cpp
vendored
12
deps/icu-small/source/common/rbbisetb.cpp
vendored
@ -375,7 +375,11 @@ void RBBISetBuilder::addValToSets(UVector *sets, uint32_t val) {
|
||||
}
|
||||
|
||||
void RBBISetBuilder::addValToSet(RBBINode *usetNode, uint32_t val) {
|
||||
RBBINode *leafNode = new RBBINode(RBBINode::leafChar);
|
||||
RBBINode *leafNode = new RBBINode(RBBINode::leafChar, *fStatus);
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete leafNode;
|
||||
return;
|
||||
}
|
||||
if (leafNode == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
@ -388,9 +392,13 @@ void RBBISetBuilder::addValToSet(RBBINode *usetNode, uint32_t val) {
|
||||
// There are already input symbols present for this set.
|
||||
// Set up an OR node, with the previous stuff as the left child
|
||||
// and the new value as the right child.
|
||||
RBBINode *orNode = new RBBINode(RBBINode::opOr);
|
||||
RBBINode *orNode = new RBBINode(RBBINode::opOr, *fStatus);
|
||||
if (orNode == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete orNode;
|
||||
delete leafNode;
|
||||
return;
|
||||
}
|
||||
orNode->fLeftChild = usetNode->fLeftChild;
|
||||
|
28
deps/icu-small/source/common/rbbitblb.cpp
vendored
28
deps/icu-small/source/common/rbbitblb.cpp
vendored
@ -99,13 +99,22 @@ void RBBITableBuilder::buildForwardTable() {
|
||||
// {bof} fake character.
|
||||
//
|
||||
if (fRB->fSetBuilder->sawBOF()) {
|
||||
RBBINode *bofTop = new RBBINode(RBBINode::opCat);
|
||||
RBBINode *bofLeaf = new RBBINode(RBBINode::leafChar);
|
||||
// Delete and exit if memory allocation failed.
|
||||
if (bofTop == nullptr || bofLeaf == nullptr) {
|
||||
RBBINode *bofTop = new RBBINode(RBBINode::opCat, *fStatus);
|
||||
if (bofTop == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete bofTop;
|
||||
return;
|
||||
}
|
||||
RBBINode *bofLeaf = new RBBINode(RBBINode::leafChar, *fStatus);
|
||||
// Delete and exit if memory allocation failed.
|
||||
if (bofLeaf == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete bofLeaf;
|
||||
delete bofTop;
|
||||
return;
|
||||
}
|
||||
bofTop->fLeftChild = bofLeaf;
|
||||
@ -120,18 +129,23 @@ void RBBITableBuilder::buildForwardTable() {
|
||||
// Appears as a cat-node, left child being the original tree,
|
||||
// right child being the end marker.
|
||||
//
|
||||
RBBINode *cn = new RBBINode(RBBINode::opCat);
|
||||
RBBINode *cn = new RBBINode(RBBINode::opCat, *fStatus);
|
||||
// Exit if memory allocation failed.
|
||||
if (cn == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete cn;
|
||||
return;
|
||||
}
|
||||
cn->fLeftChild = fTree;
|
||||
fTree->fParent = cn;
|
||||
RBBINode *endMarkerNode = cn->fRightChild = new RBBINode(RBBINode::endMark);
|
||||
RBBINode *endMarkerNode = cn->fRightChild = new RBBINode(RBBINode::endMark, *fStatus);
|
||||
// Delete and exit if memory allocation failed.
|
||||
if (cn->fRightChild == nullptr) {
|
||||
*fStatus = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
if (U_FAILURE(*fStatus)) {
|
||||
delete cn;
|
||||
return;
|
||||
}
|
||||
@ -142,7 +156,7 @@ void RBBITableBuilder::buildForwardTable() {
|
||||
// Replace all references to UnicodeSets with the tree for the equivalent
|
||||
// expression.
|
||||
//
|
||||
fTree->flattenSets();
|
||||
fTree->flattenSets(*fStatus, 0);
|
||||
#ifdef RBBI_DEBUG
|
||||
if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "stree")) {
|
||||
RBBIDebugPuts("\nParse tree after flattening Unicode Set references.");
|
||||
|
2
deps/icu-small/source/common/resbund.cpp
vendored
2
deps/icu-small/source/common/resbund.cpp
vendored
@ -388,7 +388,7 @@ const Locale &ResourceBundle::getLocale() const {
|
||||
return ncThis->fLocale != nullptr ? *ncThis->fLocale : Locale::getDefault();
|
||||
}
|
||||
|
||||
const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const
|
||||
Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const
|
||||
{
|
||||
return ures_getLocaleByType(fResource, type, &status);
|
||||
}
|
||||
|
5
deps/icu-small/source/common/ucnvmbcs.cpp
vendored
5
deps/icu-small/source/common/ucnvmbcs.cpp
vendored
@ -3146,11 +3146,8 @@ ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs,
|
||||
if(c<0) {
|
||||
if(U_SUCCESS(*pErrorCode) && source==sourceLimit && lastSource<source) {
|
||||
/* incomplete character byte sequence */
|
||||
uint8_t *bytes=cnv->toUBytes;
|
||||
cnv->toULength = static_cast<int8_t>(source - lastSource);
|
||||
do {
|
||||
*bytes++=*lastSource++;
|
||||
} while(lastSource<source);
|
||||
uprv_memcpy(cnv->toUBytes, lastSource, cnv->toULength);
|
||||
*pErrorCode=U_TRUNCATED_CHAR_FOUND;
|
||||
} else if(U_FAILURE(*pErrorCode)) {
|
||||
/* callback(illegal) */
|
||||
|
17
deps/icu-small/source/common/ucurr.cpp
vendored
17
deps/icu-small/source/common/ucurr.cpp
vendored
@ -372,12 +372,8 @@ struct CReg : public icu::UMemory {
|
||||
CReg(const char16_t* _iso, const char* _id)
|
||||
: next(nullptr)
|
||||
{
|
||||
int32_t len = static_cast<int32_t>(uprv_strlen(_id));
|
||||
if (len > static_cast<int32_t>(sizeof(id) - 1)) {
|
||||
len = (sizeof(id)-1);
|
||||
}
|
||||
uprv_strncpy(id, _id, len);
|
||||
id[len] = 0;
|
||||
uprv_strncpy(id, _id, sizeof(id)-1);
|
||||
id[sizeof(id)-1] = 0;
|
||||
u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH);
|
||||
iso[ISO_CURRENCY_CODE_LENGTH] = 0;
|
||||
}
|
||||
@ -682,6 +678,9 @@ ucurr_getName(const char16_t* currency,
|
||||
// this function.
|
||||
UErrorCode ec2 = U_ZERO_ERROR;
|
||||
|
||||
if (locale == nullptr) {
|
||||
locale = uloc_getDefault();
|
||||
}
|
||||
CharString loc = ulocimp_getName(locale, ec2);
|
||||
if (U_FAILURE(ec2)) {
|
||||
*ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
@ -780,6 +779,9 @@ ucurr_getPluralName(const char16_t* currency,
|
||||
// this function.
|
||||
UErrorCode ec2 = U_ZERO_ERROR;
|
||||
|
||||
if (locale == nullptr) {
|
||||
locale = uloc_getDefault();
|
||||
}
|
||||
CharString loc = ulocimp_getName(locale, ec2);
|
||||
if (U_FAILURE(ec2)) {
|
||||
*ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
@ -973,6 +975,9 @@ collectCurrencyNames(const char* locale,
|
||||
// Look up the Currencies resource for the given locale.
|
||||
UErrorCode ec2 = U_ZERO_ERROR;
|
||||
|
||||
if (locale == nullptr) {
|
||||
locale = uloc_getDefault();
|
||||
}
|
||||
CharString loc = ulocimp_getName(locale, ec2);
|
||||
if (U_FAILURE(ec2)) {
|
||||
ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
551
deps/icu-small/source/common/uloc.cpp
vendored
551
deps/icu-small/source/common/uloc.cpp
vendored
File diff suppressed because it is too large
Load Diff
25
deps/icu-small/source/common/uloc_tag.cpp
vendored
25
deps/icu-small/source/common/uloc_tag.cpp
vendored
@ -1043,7 +1043,7 @@ _initializeULanguageTag(ULanguageTag* langtag) {
|
||||
}
|
||||
|
||||
void
|
||||
_appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
_appendLanguageToLanguageTag(std::string_view localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
UErrorCode tmpStatus = U_ZERO_ERROR;
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
@ -1088,7 +1088,7 @@ _appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, bool str
|
||||
}
|
||||
|
||||
void
|
||||
_appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
_appendScriptToLanguageTag(std::string_view localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
UErrorCode tmpStatus = U_ZERO_ERROR;
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
@ -1118,7 +1118,7 @@ _appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, bool stric
|
||||
}
|
||||
|
||||
void
|
||||
_appendRegionToLanguageTag(const char* localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
_appendRegionToLanguageTag(std::string_view localeID, icu::ByteSink& sink, bool strict, UErrorCode& status) {
|
||||
UErrorCode tmpStatus = U_ZERO_ERROR;
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
@ -1169,7 +1169,7 @@ void _sortVariants(VariantListEntry* first) {
|
||||
}
|
||||
|
||||
void
|
||||
_appendVariantsToLanguageTag(const char* localeID, icu::ByteSink& sink, bool strict, bool& hadPosix, UErrorCode& status) {
|
||||
_appendVariantsToLanguageTag(std::string_view localeID, icu::ByteSink& sink, bool strict, bool& hadPosix, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
UErrorCode tmpStatus = U_ZERO_ERROR;
|
||||
@ -1872,7 +1872,7 @@ _appendKeywords(ULanguageTag* langtag, icu::ByteSink& sink, UErrorCode& status)
|
||||
}
|
||||
|
||||
void
|
||||
_appendPrivateuseToLanguageTag(const char* localeID, icu::ByteSink& sink, bool strict, bool /*hadPosix*/, UErrorCode& status) {
|
||||
_appendPrivateuseToLanguageTag(std::string_view localeID, icu::ByteSink& sink, bool strict, bool /*hadPosix*/, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
UErrorCode tmpStatus = U_ZERO_ERROR;
|
||||
@ -2596,6 +2596,9 @@ ulocimp_toLanguageTag(const char* localeID,
|
||||
bool hadPosix = false;
|
||||
const char* pKeywordStart;
|
||||
|
||||
if (localeID == nullptr) {
|
||||
localeID = uloc_getDefault();
|
||||
}
|
||||
/* Note: uloc_canonicalize returns "en_US_POSIX" for input locale ID "". See #6835 */
|
||||
icu::CharString canonical = ulocimp_canonicalize(localeID, tmpStatus);
|
||||
if (U_FAILURE(tmpStatus)) {
|
||||
@ -2604,7 +2607,7 @@ ulocimp_toLanguageTag(const char* localeID,
|
||||
}
|
||||
|
||||
/* For handling special case - private use only tag */
|
||||
pKeywordStart = locale_getKeywordsStart(canonical.data());
|
||||
pKeywordStart = locale_getKeywordsStart(canonical.toStringPiece());
|
||||
if (pKeywordStart == canonical.data()) {
|
||||
int kwdCnt = 0;
|
||||
bool done = false;
|
||||
@ -2642,12 +2645,12 @@ ulocimp_toLanguageTag(const char* localeID,
|
||||
}
|
||||
}
|
||||
|
||||
_appendLanguageToLanguageTag(canonical.data(), sink, strict, status);
|
||||
_appendScriptToLanguageTag(canonical.data(), sink, strict, status);
|
||||
_appendRegionToLanguageTag(canonical.data(), sink, strict, status);
|
||||
_appendVariantsToLanguageTag(canonical.data(), sink, strict, hadPosix, status);
|
||||
_appendLanguageToLanguageTag(canonical.toStringPiece(), sink, strict, status);
|
||||
_appendScriptToLanguageTag(canonical.toStringPiece(), sink, strict, status);
|
||||
_appendRegionToLanguageTag(canonical.toStringPiece(), sink, strict, status);
|
||||
_appendVariantsToLanguageTag(canonical.toStringPiece(), sink, strict, hadPosix, status);
|
||||
_appendKeywordsToLanguageTag(canonical.data(), sink, strict, hadPosix, status);
|
||||
_appendPrivateuseToLanguageTag(canonical.data(), sink, strict, hadPosix, status);
|
||||
_appendPrivateuseToLanguageTag(canonical.toStringPiece(), sink, strict, hadPosix, status);
|
||||
}
|
||||
|
||||
|
||||
|
5
deps/icu-small/source/common/ulocale.cpp
vendored
5
deps/icu-small/source/common/ulocale.cpp
vendored
@ -10,7 +10,6 @@
|
||||
#include "unicode/locid.h"
|
||||
|
||||
#include "bytesinkutil.h"
|
||||
#include "charstr.h"
|
||||
#include "cmemory.h"
|
||||
|
||||
U_NAMESPACE_USE
|
||||
@ -24,9 +23,7 @@ ulocale_openForLocaleID(const char* localeID, int32_t length, UErrorCode* err) {
|
||||
if (length < 0) {
|
||||
return EXTERNAL(icu::Locale::createFromName(localeID).clone());
|
||||
}
|
||||
CharString str(localeID, length, *err); // Make a NUL terminated copy.
|
||||
if (U_FAILURE(*err)) { return nullptr; }
|
||||
return EXTERNAL(icu::Locale::createFromName(str.data()).clone());
|
||||
return EXTERNAL(icu::Locale::createFromName(StringPiece{localeID, length}).clone());
|
||||
}
|
||||
|
||||
ULocale*
|
||||
|
32
deps/icu-small/source/common/ulocimp.h
vendored
32
deps/icu-small/source/common/ulocimp.h
vendored
@ -68,42 +68,42 @@ U_EXPORT std::optional<std::string_view>
|
||||
ulocimp_toLegacyTypeWithFallback(std::string_view keyword, std::string_view value);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getKeywords(const char* localeID,
|
||||
ulocimp_getKeywords(std::string_view localeID,
|
||||
char prev,
|
||||
bool valuesToo,
|
||||
UErrorCode& status);
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_getKeywords(const char* localeID,
|
||||
ulocimp_getKeywords(std::string_view localeID,
|
||||
char prev,
|
||||
icu::ByteSink& sink,
|
||||
bool valuesToo,
|
||||
UErrorCode& status);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getName(const char* localeID,
|
||||
ulocimp_getName(std::string_view localeID,
|
||||
UErrorCode& err);
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_getName(const char* localeID,
|
||||
ulocimp_getName(std::string_view localeID,
|
||||
icu::ByteSink& sink,
|
||||
UErrorCode& err);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getBaseName(const char* localeID,
|
||||
ulocimp_getBaseName(std::string_view localeID,
|
||||
UErrorCode& err);
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_getBaseName(const char* localeID,
|
||||
ulocimp_getBaseName(std::string_view localeID,
|
||||
icu::ByteSink& sink,
|
||||
UErrorCode& err);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_canonicalize(const char* localeID,
|
||||
ulocimp_canonicalize(std::string_view localeID,
|
||||
UErrorCode& err);
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_canonicalize(const char* localeID,
|
||||
ulocimp_canonicalize(std::string_view localeID,
|
||||
icu::ByteSink& sink,
|
||||
UErrorCode& err);
|
||||
|
||||
@ -119,16 +119,16 @@ ulocimp_getKeywordValue(const char* localeID,
|
||||
UErrorCode& status);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getLanguage(const char* localeID, UErrorCode& status);
|
||||
ulocimp_getLanguage(std::string_view localeID, UErrorCode& status);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getScript(const char* localeID, UErrorCode& status);
|
||||
ulocimp_getScript(std::string_view localeID, UErrorCode& status);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getRegion(const char* localeID, UErrorCode& status);
|
||||
ulocimp_getRegion(std::string_view localeID, UErrorCode& status);
|
||||
|
||||
U_EXPORT icu::CharString
|
||||
ulocimp_getVariant(const char* localeID, UErrorCode& status);
|
||||
ulocimp_getVariant(std::string_view localeID, UErrorCode& status);
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_setKeywordValue(std::string_view keywordName,
|
||||
@ -145,7 +145,7 @@ ulocimp_setKeywordValue(std::string_view keywords,
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_getSubtags(
|
||||
const char* localeID,
|
||||
std::string_view localeID,
|
||||
icu::CharString* language,
|
||||
icu::CharString* script,
|
||||
icu::CharString* region,
|
||||
@ -155,7 +155,7 @@ ulocimp_getSubtags(
|
||||
|
||||
U_EXPORT void
|
||||
ulocimp_getSubtags(
|
||||
const char* localeID,
|
||||
std::string_view localeID,
|
||||
icu::ByteSink* language,
|
||||
icu::ByteSink* script,
|
||||
icu::ByteSink* region,
|
||||
@ -165,7 +165,7 @@ ulocimp_getSubtags(
|
||||
|
||||
inline void
|
||||
ulocimp_getSubtags(
|
||||
const char* localeID,
|
||||
std::string_view localeID,
|
||||
std::nullptr_t,
|
||||
std::nullptr_t,
|
||||
std::nullptr_t,
|
||||
@ -364,7 +364,7 @@ ulocimp_minimizeSubtags(const char* localeID,
|
||||
UErrorCode& err);
|
||||
|
||||
U_CAPI const char * U_EXPORT2
|
||||
locale_getKeywordsStart(const char *localeID);
|
||||
locale_getKeywordsStart(std::string_view localeID);
|
||||
|
||||
bool
|
||||
ultag_isExtensionSubtags(const char* s, int32_t len);
|
||||
|
7
deps/icu-small/source/common/umapfile.cpp
vendored
7
deps/icu-small/source/common/umapfile.cpp
vendored
@ -237,8 +237,13 @@ typedef HANDLE MemoryMap;
|
||||
pData->map = (char *)data + length;
|
||||
pData->pHeader=(const DataHeader *)data;
|
||||
pData->mapAddr = data;
|
||||
#if U_PLATFORM == U_PF_IPHONE
|
||||
#if U_PLATFORM == U_PF_IPHONE || U_PLATFORM == U_PF_ANDROID
|
||||
// Apparently supported from Android 23 and higher:
|
||||
// https://github.com/ggml-org/llama.cpp/pull/3631
|
||||
// Checking for the flag itself is safer than checking for __ANDROID_API__.
|
||||
# ifdef POSIX_MADV_RANDOM
|
||||
posix_madvise(data, length, POSIX_MADV_RANDOM);
|
||||
# endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ U_NAMESPACE_END
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class CharString;
|
||||
|
||||
/**
|
||||
* The BreakIterator class implements methods for finding the location
|
||||
* of boundaries in text. BreakIterator is an abstract base class.
|
||||
@ -646,9 +648,9 @@ protected:
|
||||
private:
|
||||
|
||||
/** @internal (private) */
|
||||
char actualLocale[ULOC_FULLNAME_CAPACITY];
|
||||
char validLocale[ULOC_FULLNAME_CAPACITY];
|
||||
char requestLocale[ULOC_FULLNAME_CAPACITY];
|
||||
CharString* actualLocale = nullptr;
|
||||
CharString* validLocale = nullptr;
|
||||
CharString* requestLocale = nullptr;
|
||||
};
|
||||
|
||||
#ifndef U_HIDE_DEPRECATED_API
|
||||
|
77
deps/icu-small/source/common/unicode/char16ptr.h
vendored
77
deps/icu-small/source/common/unicode/char16ptr.h
vendored
@ -9,10 +9,13 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
#if U_SHOW_CPLUSPLUS_API || U_SHOW_CPLUSPLUS_HEADER_API
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \file
|
||||
@ -21,8 +24,6 @@
|
||||
* Also conversion functions from char16_t * to UChar * and OldUChar *.
|
||||
*/
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \def U_ALIASING_BARRIER
|
||||
* Barrier for pointer anti-aliasing optimizations even across function boundaries.
|
||||
@ -36,6 +37,11 @@ U_NAMESPACE_BEGIN
|
||||
# define U_ALIASING_BARRIER(ptr)
|
||||
#endif
|
||||
|
||||
// ICU DLL-exported
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* char16_t * wrapper with implicit conversion from distinct but bit-compatible pointer types.
|
||||
* @stable ICU 59
|
||||
@ -251,6 +257,60 @@ const char16_t *ConstChar16Ptr::get() const { return u_.cp; }
|
||||
#endif
|
||||
/// \endcond
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // U_SHOW_CPLUSPLUS_API
|
||||
|
||||
// Usable in header-only definitions
|
||||
#if U_SHOW_CPLUSPLUS_API || U_SHOW_CPLUSPLUS_HEADER_API
|
||||
|
||||
namespace U_ICU_NAMESPACE_OR_INTERNAL {
|
||||
|
||||
#ifndef U_FORCE_HIDE_INTERNAL_API
|
||||
/** @internal */
|
||||
template<typename T, typename = std::enable_if_t<std::is_same_v<T, UChar>>>
|
||||
inline const char16_t *uprv_char16PtrFromUChar(const T *p) {
|
||||
if constexpr (std::is_same_v<UChar, char16_t>) {
|
||||
return p;
|
||||
} else {
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
return ConstChar16Ptr(p).get();
|
||||
#else
|
||||
#ifdef U_ALIASING_BARRIER
|
||||
U_ALIASING_BARRIER(p);
|
||||
#endif
|
||||
return reinterpret_cast<const char16_t *>(p);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if !U_CHAR16_IS_TYPEDEF && (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION < 180000)
|
||||
/** @internal */
|
||||
inline const char16_t *uprv_char16PtrFromUint16(const uint16_t *p) {
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
return ConstChar16Ptr(p).get();
|
||||
#else
|
||||
#ifdef U_ALIASING_BARRIER
|
||||
U_ALIASING_BARRIER(p);
|
||||
#endif
|
||||
return reinterpret_cast<const char16_t *>(p);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if U_SIZEOF_WCHAR_T==2
|
||||
/** @internal */
|
||||
inline const char16_t *uprv_char16PtrFromWchar(const wchar_t *p) {
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
return ConstChar16Ptr(p).get();
|
||||
#else
|
||||
#ifdef U_ALIASING_BARRIER
|
||||
U_ALIASING_BARRIER(p);
|
||||
#endif
|
||||
return reinterpret_cast<const char16_t *>(p);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts from const char16_t * to const UChar *.
|
||||
* Includes an aliasing barrier if available.
|
||||
@ -307,6 +367,15 @@ inline OldUChar *toOldUCharPtr(char16_t *p) {
|
||||
return reinterpret_cast<OldUChar *>(p);
|
||||
}
|
||||
|
||||
} // U_ICU_NAMESPACE_OR_INTERNAL
|
||||
|
||||
#endif // U_SHOW_CPLUSPLUS_API || U_SHOW_CPLUSPLUS_HEADER_API
|
||||
|
||||
// ICU DLL-exported
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
#ifndef U_FORCE_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Is T convertible to a std::u16string_view or some other 16-bit string view?
|
||||
@ -379,6 +448,6 @@ inline std::u16string_view toU16StringViewNullable(const T& text) {
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
#endif // U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#endif // __CHAR16PTR_H__
|
||||
|
9
deps/icu-small/source/common/unicode/locid.h
vendored
9
deps/icu-small/source/common/unicode/locid.h
vendored
@ -449,6 +449,11 @@ public:
|
||||
*/
|
||||
static Locale U_EXPORT2 createFromName(const char *name);
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/** @internal */
|
||||
static Locale U_EXPORT2 createFromName(StringPiece name);
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/**
|
||||
* Creates a locale from the given string after canonicalizing
|
||||
* the string according to CLDR by calling uloc_canonicalize().
|
||||
@ -1133,7 +1138,9 @@ private:
|
||||
* @param cLocaleID The new locale name.
|
||||
* @param canonicalize whether to call uloc_canonicalize on cLocaleID
|
||||
*/
|
||||
Locale& init(const char* cLocaleID, UBool canonicalize);
|
||||
Locale& init(const char* localeID, UBool canonicalize);
|
||||
/** @internal */
|
||||
Locale& init(StringPiece localeID, UBool canonicalize);
|
||||
|
||||
/*
|
||||
* Internal constructor to allow construction of a locale object with
|
||||
|
@ -450,7 +450,7 @@ public:
|
||||
* @return a Locale object
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
const Locale
|
||||
Locale
|
||||
getLocale(ULocDataLocaleType type, UErrorCode &status) const;
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
|
48
deps/icu-small/source/common/unicode/uchar.h
vendored
48
deps/icu-small/source/common/unicode/uchar.h
vendored
@ -675,14 +675,14 @@ typedef enum UProperty {
|
||||
* @stable ICU 63
|
||||
*/
|
||||
UCHAR_VERTICAL_ORIENTATION=0x1018,
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Enumerated property Identifier_Status.
|
||||
* Used for UTS #39 General Security Profile for Identifiers
|
||||
* (https://www.unicode.org/reports/tr39/#General_Security_Profile).
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
UCHAR_IDENTIFIER_STATUS=0x1019,
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Enumerated property Indic_Conjunct_Break.
|
||||
* Used in the grapheme cluster break algorithm in UAX #29.
|
||||
@ -796,7 +796,6 @@ typedef enum UProperty {
|
||||
UCHAR_SCRIPT_EXTENSIONS=0x7000,
|
||||
/** First constant for Unicode properties with unusual value types. @stable ICU 4.6 */
|
||||
UCHAR_OTHER_PROPERTY_START=UCHAR_SCRIPT_EXTENSIONS,
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Miscellaneous property Identifier_Type.
|
||||
* Used for UTS #39 General Security Profile for Identifiers
|
||||
@ -808,10 +807,9 @@ typedef enum UProperty {
|
||||
*
|
||||
* @see u_hasIDType
|
||||
* @see u_getIDTypes
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
UCHAR_IDENTIFIER_TYPE=0x7001,
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
#ifndef U_HIDE_DEPRECATED_API
|
||||
/**
|
||||
* One more than the last constant for Unicode properties with unusual value types.
|
||||
@ -2791,13 +2789,12 @@ typedef enum UVerticalOrientation {
|
||||
U_VO_UPRIGHT,
|
||||
} UVerticalOrientation;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Identifier Status constants.
|
||||
* See https://www.unicode.org/reports/tr39/#Identifier_Status_and_Type.
|
||||
*
|
||||
* @see UCHAR_IDENTIFIER_STATUS
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
typedef enum UIdentifierStatus {
|
||||
/*
|
||||
@ -2806,9 +2803,9 @@ typedef enum UIdentifierStatus {
|
||||
* U_ID_STATUS_<Unicode Identifier_Status value name>
|
||||
*/
|
||||
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_STATUS_RESTRICTED,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_STATUS_ALLOWED,
|
||||
} UIdentifierStatus;
|
||||
|
||||
@ -2817,7 +2814,7 @@ typedef enum UIdentifierStatus {
|
||||
* See https://www.unicode.org/reports/tr39/#Identifier_Status_and_Type.
|
||||
*
|
||||
* @see UCHAR_IDENTIFIER_TYPE
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
typedef enum UIdentifierType {
|
||||
/*
|
||||
@ -2826,32 +2823,31 @@ typedef enum UIdentifierType {
|
||||
* U_ID_TYPE_<Unicode Identifier_Type value name>
|
||||
*/
|
||||
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_NOT_CHARACTER,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_DEPRECATED,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_DEFAULT_IGNORABLE,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_NOT_NFKC,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_NOT_XID,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_EXCLUSION,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_OBSOLETE,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_TECHNICAL,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_UNCOMMON_USE,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_LIMITED_USE,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_INCLUSION,
|
||||
/** @draft ICU 75 */
|
||||
/** @stable ICU 75 */
|
||||
U_ID_TYPE_RECOMMENDED,
|
||||
} UIdentifierType;
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
/**
|
||||
* Check a binary Unicode property for a code point.
|
||||
@ -4057,7 +4053,6 @@ u_isIDStart(UChar32 c);
|
||||
U_CAPI UBool U_EXPORT2
|
||||
u_isIDPart(UChar32 c);
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Does the set of Identifier_Type values code point c contain the given type?
|
||||
*
|
||||
@ -4069,7 +4064,7 @@ u_isIDPart(UChar32 c);
|
||||
* @param c code point
|
||||
* @param type Identifier_Type to check
|
||||
* @return true if type is in Identifier_Type(c)
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
U_CAPI bool U_EXPORT2
|
||||
u_hasIDType(UChar32 c, UIdentifierType type);
|
||||
@ -4104,11 +4099,10 @@ u_hasIDType(UChar32 c, UIdentifierType type);
|
||||
* function chaining. (See User Guide for details.)
|
||||
* @return number of values in c's Identifier_Type,
|
||||
* written to types unless U_BUFFER_OVERFLOW_ERROR indicates insufficient capacity
|
||||
* @draft ICU 75
|
||||
* @stable ICU 75
|
||||
*/
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
u_getIDTypes(UChar32 c, UIdentifierType *types, int32_t capacity, UErrorCode *pErrorCode);
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
/**
|
||||
* Determines if the specified character should be regarded
|
||||
|
11
deps/icu-small/source/common/unicode/uniset.h
vendored
11
deps/icu-small/source/common/unicode/uniset.h
vendored
@ -1173,10 +1173,12 @@ public:
|
||||
inline U_HEADER_NESTED_NAMESPACE::USetStrings strings() const {
|
||||
return U_HEADER_NESTED_NAMESPACE::USetStrings(toUSet());
|
||||
}
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Returns a C++ iterator for iterating over all of the elements of this set.
|
||||
* Convenient all-in one iteration, but creates a UnicodeString for each
|
||||
* Convenient all-in one iteration, but creates a std::u16string for each
|
||||
* code point or string.
|
||||
* (Similar to how Java UnicodeSet *is an* Iterable<String>.)
|
||||
*
|
||||
@ -1185,13 +1187,14 @@ public:
|
||||
* \code
|
||||
* UnicodeSet set(u"[abcçカ🚴{}{abc}{de}]", errorCode);
|
||||
* for (auto el : set) {
|
||||
* UnicodeString us(el);
|
||||
* std::string u8;
|
||||
* printf("set.string length %ld \"%s\"\n", (long)el.length(), el.toUTF8String(u8).c_str());
|
||||
* printf("set.element length %ld \"%s\"\n", (long)us.length(), us.toUTF8String(u8).c_str());
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* @return an all-elements iterator.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
* @see end
|
||||
* @see codePoints
|
||||
* @see ranges
|
||||
@ -1203,7 +1206,7 @@ public:
|
||||
|
||||
/**
|
||||
* @return an exclusive-end sentinel for iterating over all of the elements of this set.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
* @see begin
|
||||
* @see codePoints
|
||||
* @see ranges
|
||||
|
92
deps/icu-small/source/common/unicode/uset.h
vendored
92
deps/icu-small/source/common/unicode/uset.h
vendored
@ -32,12 +32,13 @@
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/uchar.h"
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
#if U_SHOW_CPLUSPLUS_API || U_SHOW_CPLUSPLUS_HEADER_API
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "unicode/char16ptr.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/unistr.h"
|
||||
#endif // U_SHOW_CPLUSPLUS_API
|
||||
#include "unicode/utf16.h"
|
||||
#endif
|
||||
|
||||
#ifndef USET_DEFINED
|
||||
|
||||
@ -1392,8 +1393,8 @@ public:
|
||||
private:
|
||||
friend class USetCodePoints;
|
||||
|
||||
USetCodePointIterator(const USet *uset, int32_t rangeIndex, int32_t rangeCount)
|
||||
: uset(uset), rangeIndex(rangeIndex), rangeCount(rangeCount),
|
||||
USetCodePointIterator(const USet *pUset, int32_t nRangeIndex, int32_t nRangeCount)
|
||||
: uset(pUset), rangeIndex(nRangeIndex), rangeCount(nRangeCount),
|
||||
c(U_SENTINEL), end(U_SENTINEL) {
|
||||
// Fetch the first range.
|
||||
operator++();
|
||||
@ -1429,7 +1430,7 @@ public:
|
||||
* Constructs a C++ "range" object over the code points of the USet.
|
||||
* @draft ICU 76
|
||||
*/
|
||||
USetCodePoints(const USet *uset) : uset(uset), rangeCount(uset_getRangeCount(uset)) {}
|
||||
USetCodePoints(const USet *pUset) : uset(pUset), rangeCount(uset_getRangeCount(pUset)) {}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
USetCodePoints(const USetCodePoints &other) = default;
|
||||
@ -1460,7 +1461,7 @@ struct CodePointRange {
|
||||
/** @draft ICU 76 */
|
||||
struct iterator {
|
||||
/** @draft ICU 76 */
|
||||
iterator(UChar32 c) : c(c) {}
|
||||
iterator(UChar32 aC) : c(aC) {}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
bool operator==(const iterator &other) const { return c == other.c; }
|
||||
@ -1573,8 +1574,8 @@ public:
|
||||
private:
|
||||
friend class USetRanges;
|
||||
|
||||
USetRangeIterator(const USet *uset, int32_t rangeIndex, int32_t rangeCount)
|
||||
: uset(uset), rangeIndex(rangeIndex), rangeCount(rangeCount) {}
|
||||
USetRangeIterator(const USet *pUset, int32_t nRangeIndex, int32_t nRangeCount)
|
||||
: uset(pUset), rangeIndex(nRangeIndex), rangeCount(nRangeCount) {}
|
||||
|
||||
const USet *uset;
|
||||
int32_t rangeIndex;
|
||||
@ -1610,7 +1611,7 @@ public:
|
||||
* Constructs a C++ "range" object over the code point ranges of the USet.
|
||||
* @draft ICU 76
|
||||
*/
|
||||
USetRanges(const USet *uset) : uset(uset), rangeCount(uset_getRangeCount(uset)) {}
|
||||
USetRanges(const USet *pUset) : uset(pUset), rangeCount(uset_getRangeCount(pUset)) {}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
USetRanges(const USetRanges &other) = default;
|
||||
@ -1657,7 +1658,7 @@ public:
|
||||
int32_t length;
|
||||
const UChar *uchars = uset_getString(uset, index, &length);
|
||||
// assert uchars != nullptr;
|
||||
return {ConstChar16Ptr(uchars), static_cast<uint32_t>(length)};
|
||||
return {uprv_char16PtrFromUChar(uchars), static_cast<size_t>(length)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -1684,8 +1685,8 @@ public:
|
||||
private:
|
||||
friend class USetStrings;
|
||||
|
||||
USetStringIterator(const USet *uset, int32_t index, int32_t count)
|
||||
: uset(uset), index(index), count(count) {}
|
||||
USetStringIterator(const USet *pUset, int32_t nIndex, int32_t nCount)
|
||||
: uset(pUset), index(nIndex), count(nCount) {}
|
||||
|
||||
const USet *uset;
|
||||
int32_t index;
|
||||
@ -1699,9 +1700,11 @@ private:
|
||||
* using U_HEADER_NESTED_NAMESPACE::USetStrings;
|
||||
* LocalUSetPointer uset(uset_openPattern(u"[abcçカ🚴{}{abc}{de}]", -1, &errorCode));
|
||||
* for (auto s : USetStrings(uset.getAlias())) {
|
||||
* UnicodeString us(s);
|
||||
* std::string u8;
|
||||
* printf("uset.string length %ld \"%s\"\n", (long)s.length(), us.toUTF8String(u8).c_str());
|
||||
* int32_t len32 = s.length();
|
||||
* char utf8[200];
|
||||
* u_strToUTF8WithSub(utf8, int32_t{sizeof(utf8) - 1}, nullptr,
|
||||
* s.data(), len32, 0xFFFD, nullptr, errorCode);
|
||||
* printf("uset.string length %ld \"%s\"\n", long{len32}, utf8);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
@ -1718,7 +1721,7 @@ public:
|
||||
* Constructs a C++ "range" object over the strings of the USet.
|
||||
* @draft ICU 76
|
||||
*/
|
||||
USetStrings(const USet *uset) : uset(uset), count(uset_getStringCount(uset)) {}
|
||||
USetStrings(const USet *pUset) : uset(pUset), count(uset_getStringCount(pUset)) {}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
USetStrings(const USetStrings &other) = default;
|
||||
@ -1737,17 +1740,19 @@ private:
|
||||
const USet *uset;
|
||||
int32_t count;
|
||||
};
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Iterator returned by USetElements.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
*/
|
||||
class USetElementIterator {
|
||||
public:
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
USetElementIterator(const USetElementIterator &other) = default;
|
||||
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
bool operator==(const USetElementIterator &other) const {
|
||||
// No need to compare rangeCount & end given private constructor
|
||||
// and assuming we don't compare iterators across the set being modified.
|
||||
@ -1756,26 +1761,28 @@ public:
|
||||
return uset == other.uset && c == other.c && index == other.index;
|
||||
}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
bool operator!=(const USetElementIterator &other) const { return !operator==(other); }
|
||||
|
||||
/** @draft ICU 76 */
|
||||
UnicodeString operator*() const {
|
||||
/** @draft ICU 77 */
|
||||
std::u16string operator*() const {
|
||||
if (c >= 0) {
|
||||
return UnicodeString(c);
|
||||
return c <= 0xffff ?
|
||||
std::u16string({static_cast<char16_t>(c)}) :
|
||||
std::u16string({U16_LEAD(c), U16_TRAIL(c)});
|
||||
} else if (index < totalCount) {
|
||||
int32_t length;
|
||||
const UChar *uchars = uset_getString(uset, index - rangeCount, &length);
|
||||
// assert uchars != nullptr;
|
||||
return UnicodeString(uchars, length);
|
||||
return {uprv_char16PtrFromUChar(uchars), static_cast<size_t>(length)};
|
||||
} else {
|
||||
return UnicodeString();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-increment.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
*/
|
||||
USetElementIterator &operator++() {
|
||||
if (c < end) {
|
||||
@ -1800,7 +1807,7 @@ public:
|
||||
|
||||
/**
|
||||
* Post-increment.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
*/
|
||||
USetElementIterator operator++(int) {
|
||||
USetElementIterator result(*this);
|
||||
@ -1811,8 +1818,8 @@ public:
|
||||
private:
|
||||
friend class USetElements;
|
||||
|
||||
USetElementIterator(const USet *uset, int32_t index, int32_t rangeCount, int32_t totalCount)
|
||||
: uset(uset), index(index), rangeCount(rangeCount), totalCount(totalCount),
|
||||
USetElementIterator(const USet *pUset, int32_t nIndex, int32_t nRangeCount, int32_t nTotalCount)
|
||||
: uset(pUset), index(nIndex), rangeCount(nRangeCount), totalCount(nTotalCount),
|
||||
c(U_SENTINEL), end(U_SENTINEL) {
|
||||
if (index < rangeCount) {
|
||||
// Fetch the first range.
|
||||
@ -1840,7 +1847,7 @@ private:
|
||||
|
||||
/**
|
||||
* A C++ "range" for iterating over all of the elements of a USet.
|
||||
* Convenient all-in one iteration, but creates a UnicodeString for each
|
||||
* Convenient all-in one iteration, but creates a std::u16string for each
|
||||
* code point or string.
|
||||
*
|
||||
* Code points are returned first, then empty and multi-character strings.
|
||||
@ -1849,15 +1856,18 @@ private:
|
||||
* using U_HEADER_NESTED_NAMESPACE::USetElements;
|
||||
* LocalUSetPointer uset(uset_openPattern(u"[abcçカ🚴{}{abc}{de}]", -1, &errorCode));
|
||||
* for (auto el : USetElements(uset.getAlias())) {
|
||||
* std::string u8;
|
||||
* printf("uset.string length %ld \"%s\"\n", (long)el.length(), el.toUTF8String(u8).c_str());
|
||||
* int32_t len32 = el.length();
|
||||
* char utf8[200];
|
||||
* u_strToUTF8WithSub(utf8, int32_t{sizeof(utf8) - 1}, nullptr,
|
||||
* el.data(), len32, 0xFFFD, nullptr, errorCode);
|
||||
* printf("uset.element length %ld \"%s\"\n", long{len32}, utf8);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* C++ UnicodeSet has member functions for iteration, including begin() and end().
|
||||
*
|
||||
* @return an all-elements iterator.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
* @see USetCodePoints
|
||||
* @see USetRanges
|
||||
* @see USetStrings
|
||||
@ -1866,21 +1876,21 @@ class USetElements {
|
||||
public:
|
||||
/**
|
||||
* Constructs a C++ "range" object over all of the elements of the USet.
|
||||
* @draft ICU 76
|
||||
* @draft ICU 77
|
||||
*/
|
||||
USetElements(const USet *uset)
|
||||
: uset(uset), rangeCount(uset_getRangeCount(uset)),
|
||||
stringCount(uset_getStringCount(uset)) {}
|
||||
USetElements(const USet *pUset)
|
||||
: uset(pUset), rangeCount(uset_getRangeCount(pUset)),
|
||||
stringCount(uset_getStringCount(pUset)) {}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
USetElements(const USetElements &other) = default;
|
||||
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
USetElementIterator begin() const {
|
||||
return USetElementIterator(uset, 0, rangeCount, rangeCount + stringCount);
|
||||
}
|
||||
|
||||
/** @draft ICU 76 */
|
||||
/** @draft ICU 77 */
|
||||
USetElementIterator end() const {
|
||||
return USetElementIterator(uset, rangeCount + stringCount, rangeCount, rangeCount + stringCount);
|
||||
}
|
||||
|
4
deps/icu-small/source/common/unicode/utf8.h
vendored
4
deps/icu-small/source/common/unicode/utf8.h
vendored
@ -124,7 +124,7 @@
|
||||
* @internal
|
||||
*/
|
||||
U_CAPI UChar32 U_EXPORT2
|
||||
utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict);
|
||||
utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, int8_t strict);
|
||||
|
||||
/**
|
||||
* Function for handling "append code point" with error-checking.
|
||||
@ -148,7 +148,7 @@ utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool
|
||||
* @internal
|
||||
*/
|
||||
U_CAPI UChar32 U_EXPORT2
|
||||
utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict);
|
||||
utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, int8_t strict);
|
||||
|
||||
/**
|
||||
* Function for handling "skip backward one code point" with error-checking.
|
||||
|
@ -598,12 +598,13 @@ typedef enum UErrorCode {
|
||||
U_MF_DUPLICATE_DECLARATION_ERROR, /**< The same variable is declared in more than one .local or .input declaration. @internal ICU 75 technology preview @deprecated This API is for technology preview only. */
|
||||
U_MF_OPERAND_MISMATCH_ERROR, /**< An operand provided to a function does not have the required form for that function @internal ICU 75 technology preview @deprecated This API is for technology preview only. */
|
||||
U_MF_DUPLICATE_VARIANT_ERROR, /**< A message includes a variant with the same key list as another variant. @internal ICU 76 technology preview @deprecated This API is for technology preview only. */
|
||||
U_MF_BAD_OPTION, /**< An option value provided to a function does not have the required form for that option. @internal ICU 77 technology preview @deprecated This API is for technology preview only. */
|
||||
#ifndef U_HIDE_DEPRECATED_API
|
||||
/**
|
||||
* One more than the highest normal formatting API error code.
|
||||
* @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
|
||||
*/
|
||||
U_FMT_PARSE_ERROR_LIMIT = 0x10120,
|
||||
U_FMT_PARSE_ERROR_LIMIT = 0x10121,
|
||||
#endif // U_HIDE_DEPRECATED_API
|
||||
|
||||
/*
|
||||
|
10
deps/icu-small/source/common/unicode/uvernum.h
vendored
10
deps/icu-small/source/common/unicode/uvernum.h
vendored
@ -53,7 +53,7 @@
|
||||
* This value will change in the subsequent releases of ICU
|
||||
* @stable ICU 2.4
|
||||
*/
|
||||
#define U_ICU_VERSION_MAJOR_NUM 76
|
||||
#define U_ICU_VERSION_MAJOR_NUM 77
|
||||
|
||||
/** The current ICU minor version as an integer.
|
||||
* This value will change in the subsequent releases of ICU
|
||||
@ -79,7 +79,7 @@
|
||||
* This value will change in the subsequent releases of ICU
|
||||
* @stable ICU 2.6
|
||||
*/
|
||||
#define U_ICU_VERSION_SUFFIX _76
|
||||
#define U_ICU_VERSION_SUFFIX _77
|
||||
|
||||
/**
|
||||
* \def U_DEF2_ICU_ENTRY_POINT_RENAME
|
||||
@ -132,7 +132,7 @@
|
||||
* This value will change in the subsequent releases of ICU
|
||||
* @stable ICU 2.4
|
||||
*/
|
||||
#define U_ICU_VERSION "76.1"
|
||||
#define U_ICU_VERSION "77.1"
|
||||
|
||||
/**
|
||||
* The current ICU library major version number as a string, for library name suffixes.
|
||||
@ -145,13 +145,13 @@
|
||||
*
|
||||
* @stable ICU 2.6
|
||||
*/
|
||||
#define U_ICU_VERSION_SHORT "76"
|
||||
#define U_ICU_VERSION_SHORT "77"
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/** Data version in ICU4C.
|
||||
* @internal ICU 4.4 Internal Use Only
|
||||
**/
|
||||
#define U_ICU_DATA_VERSION "76.1"
|
||||
#define U_ICU_DATA_VERSION "77.1"
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/*===========================================================================
|
||||
|
19
deps/icu-small/source/common/unicode/uversion.h
vendored
19
deps/icu-small/source/common/unicode/uversion.h
vendored
@ -125,7 +125,7 @@ typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH];
|
||||
U_NAMESPACE_USE
|
||||
# endif
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
#ifndef U_FORCE_HIDE_DRAFT_API
|
||||
/**
|
||||
* \def U_HEADER_NESTED_NAMESPACE
|
||||
* Nested namespace used inside U_ICU_NAMESPACE for header-only APIs.
|
||||
@ -150,22 +150,37 @@ typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH];
|
||||
* @draft ICU 76
|
||||
*/
|
||||
|
||||
/**
|
||||
* \def U_ICU_NAMESPACE_OR_INTERNAL
|
||||
* Namespace used for header-only APIs that used to be regular C++ APIs.
|
||||
* Different when used inside ICU to prevent public use of internal instantiations.
|
||||
* Similar to U_HEADER_ONLY_NAMESPACE, but the public definition is the same as U_ICU_NAMESPACE.
|
||||
* "U_ICU_NAMESPACE" or "U_ICU_NAMESPACE::internal".
|
||||
*
|
||||
* @draft ICU 77
|
||||
*/
|
||||
|
||||
// The first test is the same as for defining U_EXPORT for Windows.
|
||||
#if defined(_MSC_VER) || (UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllexport__) && \
|
||||
UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllimport__))
|
||||
# define U_HEADER_NESTED_NAMESPACE header
|
||||
# define U_ICU_NAMESPACE_OR_INTERNAL U_ICU_NAMESPACE
|
||||
#elif defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \
|
||||
defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) || \
|
||||
defined(U_LAYOUTEX_IMPLEMENTATION) || defined(U_TOOLUTIL_IMPLEMENTATION)
|
||||
# define U_HEADER_NESTED_NAMESPACE internal
|
||||
# define U_ICU_NAMESPACE_OR_INTERNAL U_ICU_NAMESPACE::internal
|
||||
namespace U_ICU_NAMESPACE_OR_INTERNAL {}
|
||||
using namespace U_ICU_NAMESPACE_OR_INTERNAL;
|
||||
#else
|
||||
# define U_HEADER_NESTED_NAMESPACE header
|
||||
# define U_ICU_NAMESPACE_OR_INTERNAL U_ICU_NAMESPACE
|
||||
#endif
|
||||
|
||||
#define U_HEADER_ONLY_NAMESPACE U_ICU_NAMESPACE::U_HEADER_NESTED_NAMESPACE
|
||||
|
||||
namespace U_HEADER_ONLY_NAMESPACE {}
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
#endif // U_FORCE_HIDE_DRAFT_API
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
7
deps/icu-small/source/common/unistr.cpp
vendored
7
deps/icu-small/source/common/unistr.cpp
vendored
@ -1945,6 +1945,13 @@ UnicodeString::cloneArrayIfNeeded(int32_t newCapacity,
|
||||
growCapacity = newCapacity;
|
||||
} else if(newCapacity <= US_STACKBUF_SIZE && growCapacity > US_STACKBUF_SIZE) {
|
||||
growCapacity = US_STACKBUF_SIZE;
|
||||
} else if(newCapacity > growCapacity) {
|
||||
setToBogus();
|
||||
return false; // bad inputs
|
||||
}
|
||||
if(growCapacity > kMaxCapacity) {
|
||||
setToBogus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// save old values
|
||||
|
8
deps/icu-small/source/common/uresbund.cpp
vendored
8
deps/icu-small/source/common/uresbund.cpp
vendored
@ -2716,6 +2716,9 @@ ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
|
||||
|
||||
UResourceDataEntry *entry;
|
||||
if(openType != URES_OPEN_DIRECT) {
|
||||
if (localeID == nullptr) {
|
||||
localeID = uloc_getDefault();
|
||||
}
|
||||
/* first "canonicalize" the locale ID */
|
||||
CharString canonLocaleID = ulocimp_getBaseName(localeID, *status);
|
||||
if(U_FAILURE(*status)) {
|
||||
@ -3080,6 +3083,9 @@ ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
|
||||
kwVal.clear();
|
||||
}
|
||||
}
|
||||
if (locid == nullptr) {
|
||||
locid = uloc_getDefault();
|
||||
}
|
||||
CharString base = ulocimp_getBaseName(locid, subStatus);
|
||||
#if defined(URES_TREE_DEBUG)
|
||||
fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
|
||||
@ -3244,7 +3250,7 @@ ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
|
||||
const char *validLoc = ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus);
|
||||
if (U_SUCCESS(subStatus) && validLoc != nullptr && validLoc[0] != 0 && uprv_strcmp(validLoc, "root") != 0) {
|
||||
CharString validLang = ulocimp_getLanguage(validLoc, subStatus);
|
||||
CharString parentLang = ulocimp_getLanguage(parent.data(), subStatus);
|
||||
CharString parentLang = ulocimp_getLanguage(parent.toStringPiece(), subStatus);
|
||||
if (U_SUCCESS(subStatus) && validLang != parentLang) {
|
||||
// validLoc is not root and has a different language than parent, use it instead
|
||||
found.clear().append(validLoc, subStatus);
|
||||
|
3
deps/icu-small/source/common/uscript.cpp
vendored
3
deps/icu-small/source/common/uscript.cpp
vendored
@ -59,6 +59,9 @@ getCodesFromLocale(const char *locale,
|
||||
if (U_FAILURE(*err)) { return 0; }
|
||||
icu::CharString lang;
|
||||
icu::CharString script;
|
||||
if (locale == nullptr) {
|
||||
locale = uloc_getDefault();
|
||||
}
|
||||
ulocimp_getSubtags(locale, &lang, &script, nullptr, nullptr, nullptr, *err);
|
||||
if (U_FAILURE(*err)) { return 0; }
|
||||
// Multi-script languages, equivalent to the LocaleScript data
|
||||
|
5
deps/icu-small/source/common/ushape.cpp
vendored
5
deps/icu-small/source/common/ushape.cpp
vendored
@ -28,6 +28,7 @@
|
||||
#include "ubidi_props.h"
|
||||
#include "uassert.h"
|
||||
|
||||
#include <limits>
|
||||
/*
|
||||
* This implementation is designed for 16-bit Unicode strings.
|
||||
* The main assumption is that the Arabic characters and their
|
||||
@ -747,6 +748,10 @@ handleGeneratedSpaces(char16_t *dest, int32_t sourceLength,
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(sourceLength) + 1 > std::numeric_limits<size_t>::max() / U_SIZEOF_UCHAR) {
|
||||
*pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR;
|
||||
return 0;
|
||||
}
|
||||
tempbuffer = static_cast<char16_t*>(uprv_malloc((sourceLength + 1) * U_SIZEOF_UCHAR));
|
||||
/* Test for nullptr */
|
||||
if(tempbuffer == nullptr) {
|
||||
|
2
deps/icu-small/source/common/usprep.cpp
vendored
2
deps/icu-small/source/common/usprep.cpp
vendored
@ -126,7 +126,7 @@ compareEntries(const UHashTok p1, const UHashTok p2) {
|
||||
name2.pointer = b2->name;
|
||||
path1.pointer = b1->path;
|
||||
path2.pointer = b2->path;
|
||||
return uhash_compareChars(name1, name2) & uhash_compareChars(path1, path2);
|
||||
return uhash_compareChars(name1, name2) && uhash_compareChars(path1, path2);
|
||||
}
|
||||
|
||||
static void
|
||||
|
6
deps/icu-small/source/common/utf_impl.cpp
vendored
6
deps/icu-small/source/common/utf_impl.cpp
vendored
@ -124,11 +124,9 @@ errorValue(int32_t count, int8_t strict) {
|
||||
* >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., true):
|
||||
* Same as the obsolete "safe" behavior, but non-characters are also treated
|
||||
* like illegal sequences.
|
||||
*
|
||||
* Note that a UBool is the same as an int8_t.
|
||||
*/
|
||||
U_CAPI UChar32 U_EXPORT2
|
||||
utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) {
|
||||
utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, int8_t strict) {
|
||||
// *pi is one after byte c.
|
||||
int32_t i=*pi;
|
||||
// length can be negative for NUL-terminated strings: Read and validate one byte at a time.
|
||||
@ -233,7 +231,7 @@ utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool
|
||||
}
|
||||
|
||||
U_CAPI UChar32 U_EXPORT2
|
||||
utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict) {
|
||||
utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, int8_t strict) {
|
||||
// *pi is the index of byte c.
|
||||
int32_t i=*pi;
|
||||
if(U8_IS_TRAIL(c) && i>start) {
|
||||
|
3
deps/icu-small/source/common/utypes.cpp
vendored
3
deps/icu-small/source/common/utypes.cpp
vendored
@ -140,7 +140,8 @@ _uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = {
|
||||
"U_MF_MISSING_SELECTOR_ANNOTATION_ERROR",
|
||||
"U_MF_DUPLICATE_DECLARATION_ERROR",
|
||||
"U_MF_OPERAND_MISMATCH_ERROR",
|
||||
"U_MF_DUPLICATE_VARIANT_ERROR"
|
||||
"U_MF_DUPLICATE_VARIANT_ERROR",
|
||||
"U_MF_BAD_OPTION"
|
||||
};
|
||||
|
||||
static const char * const
|
||||
|
Binary file not shown.
12
deps/icu-small/source/i18n/basictz.cpp
vendored
12
deps/icu-small/source/i18n/basictz.cpp
vendored
@ -160,12 +160,13 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
|
||||
|| (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
|
||||
&& (date + MILLIS_PER_YEAR > nextTransitionTime)) {
|
||||
|
||||
int32_t year, month, dom, dow, doy, mid;
|
||||
int32_t year, mid;
|
||||
int8_t month, dom, dow;
|
||||
UDate d;
|
||||
|
||||
// Get local wall time for the next transition time
|
||||
Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
|
||||
year, month, dom, dow, doy, mid, status);
|
||||
year, month, dom, dow, mid, status);
|
||||
if (U_FAILURE(status)) return;
|
||||
int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
|
||||
// Create DOW rule
|
||||
@ -193,7 +194,7 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
|
||||
|
||||
// Get local wall time for the next transition time
|
||||
Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
|
||||
year, month, dom, dow, doy, mid, status);
|
||||
year, month, dom, dow, mid, status);
|
||||
if (U_FAILURE(status)) return;
|
||||
weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
|
||||
// Generate another DOW rule
|
||||
@ -225,7 +226,7 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
|
||||
|
||||
// Generate another DOW rule
|
||||
Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
|
||||
year, month, dom, dow, doy, mid, status);
|
||||
year, month, dom, dow, mid, status);
|
||||
if (U_FAILURE(status)) return;
|
||||
weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
|
||||
dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
|
||||
@ -486,8 +487,7 @@ BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
|
||||
}
|
||||
} else {
|
||||
// Calculate the transition year
|
||||
int32_t year, month, dom, dow, doy, mid;
|
||||
Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid, status);
|
||||
int32_t year = Grego::timeToYear(tzt.getTime(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
7
deps/icu-small/source/i18n/buddhcal.cpp
vendored
7
deps/icu-small/source/i18n/buddhcal.cpp
vendored
@ -36,7 +36,6 @@ static const int32_t kGregorianEpoch = 1970; // used as the default value of
|
||||
BuddhistCalendar::BuddhistCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: GregorianCalendar(aLocale, success)
|
||||
{
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
BuddhistCalendar::~BuddhistCalendar()
|
||||
@ -48,12 +47,6 @@ BuddhistCalendar::BuddhistCalendar(const BuddhistCalendar& source)
|
||||
{
|
||||
}
|
||||
|
||||
BuddhistCalendar& BuddhistCalendar::operator= ( const BuddhistCalendar& right)
|
||||
{
|
||||
GregorianCalendar::operator=(right);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuddhistCalendar* BuddhistCalendar::clone() const
|
||||
{
|
||||
return new BuddhistCalendar(*this);
|
||||
|
7
deps/icu-small/source/i18n/buddhcal.h
vendored
7
deps/icu-small/source/i18n/buddhcal.h
vendored
@ -82,13 +82,6 @@ public:
|
||||
*/
|
||||
BuddhistCalendar(const BuddhistCalendar& source);
|
||||
|
||||
/**
|
||||
* Default assignment operator
|
||||
* @param right the object to be copied.
|
||||
* @internal
|
||||
*/
|
||||
BuddhistCalendar& operator=(const BuddhistCalendar& right);
|
||||
|
||||
/**
|
||||
* Create and return a polymorphic copy of this calendar.
|
||||
* @return return a polymorphic copy of this calendar.
|
||||
|
162
deps/icu-small/source/i18n/calendar.cpp
vendored
162
deps/icu-small/source/i18n/calendar.cpp
vendored
@ -156,7 +156,7 @@ U_CFUNC void ucal_dump(UCalendar* cal) {
|
||||
#endif
|
||||
|
||||
/* Max value for stamp allowable before recalculation */
|
||||
#define STAMP_MAX 10000
|
||||
#define STAMP_MAX 127
|
||||
|
||||
static const char * const gCalTypes[] = {
|
||||
"gregorian",
|
||||
@ -700,15 +700,10 @@ fIsTimeSet(false),
|
||||
fAreFieldsSet(false),
|
||||
fAreAllFieldsSet(false),
|
||||
fAreFieldsVirtuallySet(false),
|
||||
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
|
||||
fTime(0),
|
||||
fLenient(true),
|
||||
fZone(nullptr),
|
||||
fRepeatedWallTime(UCAL_WALLTIME_LAST),
|
||||
fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
{
|
||||
validLocale[0] = 0;
|
||||
actualLocale[0] = 0;
|
||||
clear();
|
||||
if (U_FAILURE(success)) {
|
||||
return;
|
||||
@ -722,26 +717,21 @@ fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
|
||||
Calendar::Calendar(TimeZone* adoptZone, const Locale& aLocale, UErrorCode& success)
|
||||
: UObject(),
|
||||
fIsTimeSet(false),
|
||||
fAreFieldsSet(false),
|
||||
fAreAllFieldsSet(false),
|
||||
fAreFieldsVirtuallySet(false),
|
||||
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
|
||||
fTime(0),
|
||||
fLenient(true),
|
||||
fZone(nullptr),
|
||||
fRepeatedWallTime(UCAL_WALLTIME_LAST),
|
||||
fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
{
|
||||
validLocale[0] = 0;
|
||||
actualLocale[0] = 0;
|
||||
LocalPointer<TimeZone> zone(adoptZone, success);
|
||||
if (U_FAILURE(success)) {
|
||||
delete zone;
|
||||
return;
|
||||
}
|
||||
if (zone == nullptr) {
|
||||
if (zone.isNull()) {
|
||||
#if defined (U_DEBUG_CAL)
|
||||
fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
|
||||
__FILE__, __LINE__);
|
||||
@ -751,7 +741,7 @@ fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
}
|
||||
|
||||
clear();
|
||||
fZone = zone;
|
||||
fZone = zone.orphan();
|
||||
setWeekData(aLocale, nullptr, success);
|
||||
}
|
||||
|
||||
@ -763,15 +753,10 @@ fIsTimeSet(false),
|
||||
fAreFieldsSet(false),
|
||||
fAreAllFieldsSet(false),
|
||||
fAreFieldsVirtuallySet(false),
|
||||
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
|
||||
fTime(0),
|
||||
fLenient(true),
|
||||
fZone(nullptr),
|
||||
fRepeatedWallTime(UCAL_WALLTIME_LAST),
|
||||
fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
{
|
||||
validLocale[0] = 0;
|
||||
actualLocale[0] = 0;
|
||||
if (U_FAILURE(success)) {
|
||||
return;
|
||||
}
|
||||
@ -779,6 +764,7 @@ fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
fZone = zone.clone();
|
||||
if (fZone == nullptr) {
|
||||
success = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
setWeekData(aLocale, nullptr, success);
|
||||
}
|
||||
@ -788,6 +774,8 @@ fSkippedWallTime(UCAL_WALLTIME_LAST)
|
||||
Calendar::~Calendar()
|
||||
{
|
||||
delete fZone;
|
||||
delete actualLocale;
|
||||
delete validLocale;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -795,7 +783,6 @@ Calendar::~Calendar()
|
||||
Calendar::Calendar(const Calendar &source)
|
||||
: UObject(source)
|
||||
{
|
||||
fZone = nullptr;
|
||||
*this = source;
|
||||
}
|
||||
|
||||
@ -806,7 +793,6 @@ Calendar::operator=(const Calendar &right)
|
||||
{
|
||||
if (this != &right) {
|
||||
uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT);
|
||||
uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT);
|
||||
uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT);
|
||||
fTime = right.fTime;
|
||||
fIsTimeSet = right.fIsTimeSet;
|
||||
@ -828,10 +814,10 @@ Calendar::operator=(const Calendar &right)
|
||||
fWeekendCease = right.fWeekendCease;
|
||||
fWeekendCeaseMillis = right.fWeekendCeaseMillis;
|
||||
fNextStamp = right.fNextStamp;
|
||||
uprv_strncpy(validLocale, right.validLocale, sizeof(validLocale));
|
||||
uprv_strncpy(actualLocale, right.actualLocale, sizeof(actualLocale));
|
||||
validLocale[sizeof(validLocale)-1] = 0;
|
||||
actualLocale[sizeof(validLocale)-1] = 0;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(right.validLocale, right.actualLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -1167,13 +1153,9 @@ Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
|
||||
fAreFieldsSet = fAreAllFieldsSet = false;
|
||||
fIsTimeSet = fAreFieldsVirtuallySet = true;
|
||||
|
||||
for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
|
||||
fFields[i] = 0;
|
||||
fStamp[i] = kUnset;
|
||||
fIsSet[i] = false;
|
||||
}
|
||||
|
||||
|
||||
uprv_memset(fFields, 0, sizeof(fFields));
|
||||
uprv_memset(fStamp, kUnset, sizeof(fStamp));
|
||||
fNextStamp = kMinimumUserStamp;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -1208,12 +1190,11 @@ Calendar::set(UCalendarDateFields field, int32_t value)
|
||||
computeFields(ec);
|
||||
}
|
||||
fFields[field] = value;
|
||||
/* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
|
||||
/* Ensure that the fNextStamp value doesn't go pass max value for int8_t */
|
||||
if (fNextStamp == STAMP_MAX) {
|
||||
recalculateStamp();
|
||||
}
|
||||
fStamp[field] = fNextStamp++;
|
||||
fIsSet[field] = true; // Remove later
|
||||
fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = false;
|
||||
}
|
||||
|
||||
@ -1270,11 +1251,9 @@ void Calendar::setRelatedYear(int32_t year)
|
||||
void
|
||||
Calendar::clear()
|
||||
{
|
||||
for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
|
||||
fFields[i] = 0; // Must do this; other code depends on it
|
||||
fStamp[i] = kUnset;
|
||||
fIsSet[i] = false; // Remove later
|
||||
}
|
||||
uprv_memset(fFields, 0, sizeof(fFields));
|
||||
uprv_memset(fStamp, kUnset, sizeof(fStamp));
|
||||
fNextStamp = kMinimumUserStamp;
|
||||
fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
|
||||
// fTime is not 'cleared' - may be used if no fields are set.
|
||||
}
|
||||
@ -1296,12 +1275,10 @@ Calendar::clear(UCalendarDateFields field)
|
||||
if (field == UCAL_MONTH) {
|
||||
fFields[UCAL_ORDINAL_MONTH] = 0;
|
||||
fStamp[UCAL_ORDINAL_MONTH] = kUnset;
|
||||
fIsSet[UCAL_ORDINAL_MONTH] = false; // Remove later
|
||||
}
|
||||
if (field == UCAL_ORDINAL_MONTH) {
|
||||
fFields[UCAL_MONTH] = 0;
|
||||
fStamp[UCAL_MONTH] = kUnset;
|
||||
fIsSet[UCAL_MONTH] = false; // Remove later
|
||||
}
|
||||
fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
|
||||
}
|
||||
@ -1434,10 +1411,8 @@ void Calendar::computeFields(UErrorCode &ec)
|
||||
for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
|
||||
if ((mask & 1) == 0) {
|
||||
fStamp[i] = kInternallySet;
|
||||
fIsSet[i] = true; // Remove later
|
||||
} else {
|
||||
fStamp[i] = kUnset;
|
||||
fIsSet[i] = false; // Remove later
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
@ -1467,7 +1442,7 @@ void Calendar::computeFields(UErrorCode &ec)
|
||||
//__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
|
||||
#endif
|
||||
|
||||
computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
|
||||
computeGregorianFields(fFields[UCAL_JULIAN_DAY], ec);
|
||||
|
||||
// Call framework method to have subclass compute its fields.
|
||||
// These must include, at a minimum, MONTH, DAY_OF_MONTH,
|
||||
@ -1538,32 +1513,6 @@ uint8_t Calendar::julianDayToDayOfWeek(int32_t julian)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the Gregorian calendar year, month, and day of month from
|
||||
* the given Julian day. These values are not stored in fields, but in
|
||||
* member variables gregorianXxx. Also compute the DAY_OF_WEEK and
|
||||
* DOW_LOCAL fields.
|
||||
*/
|
||||
void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec)
|
||||
{
|
||||
computeGregorianFields(julianDay, ec);
|
||||
if (U_FAILURE(ec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute day of week: JD 0 = Monday
|
||||
int32_t dow = julianDayToDayOfWeek(julianDay);
|
||||
internalSet(UCAL_DAY_OF_WEEK,dow);
|
||||
|
||||
// Calculate 1-based localized day of week
|
||||
int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
|
||||
if (dowLocal < 1) {
|
||||
dowLocal += 7;
|
||||
}
|
||||
internalSet(UCAL_DOW_LOCAL,dowLocal);
|
||||
fFields[UCAL_DOW_LOCAL] = dowLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the Gregorian calendar year, month, and day of month from the
|
||||
* Julian day. These values are not stored in fields, but in member
|
||||
@ -1575,14 +1524,13 @@ void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) {
|
||||
if (U_FAILURE(ec)) {
|
||||
return;
|
||||
}
|
||||
int32_t gregorianDayOfWeekUnused;
|
||||
if (uprv_add32_overflow(
|
||||
julianDay, -kEpochStartAsJulianDay, &julianDay)) {
|
||||
ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
Grego::dayToFields(julianDay, fGregorianYear, fGregorianMonth,
|
||||
fGregorianDayOfMonth, gregorianDayOfWeekUnused,
|
||||
fGregorianDayOfMonth,
|
||||
fGregorianDayOfYear, ec);
|
||||
}
|
||||
|
||||
@ -1610,8 +1558,19 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
|
||||
if(U_FAILURE(ec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute day of week: JD 0 = Monday
|
||||
int32_t dayOfWeek = julianDayToDayOfWeek(fFields[UCAL_JULIAN_DAY]);
|
||||
internalSet(UCAL_DAY_OF_WEEK, dayOfWeek);
|
||||
int32_t firstDayOfWeek = getFirstDayOfWeek();
|
||||
// Calculate 1-based localized day of week
|
||||
int32_t dowLocal = dayOfWeek - firstDayOfWeek + 1;
|
||||
if (dowLocal < 1) {
|
||||
dowLocal += 7;
|
||||
}
|
||||
internalSet(UCAL_DOW_LOCAL,dowLocal);
|
||||
|
||||
int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
|
||||
int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
|
||||
int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
|
||||
|
||||
// WEEK_OF_YEAR start
|
||||
@ -1624,10 +1583,11 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
|
||||
// first week of the next year. ASSUME that the year length is less than
|
||||
// 7000 days.
|
||||
int32_t yearOfWeekOfYear = eyear;
|
||||
int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
|
||||
int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
|
||||
int32_t relDow = (dayOfWeek + 7 - firstDayOfWeek) % 7; // 0..6
|
||||
int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - firstDayOfWeek) % 7; // 0..6
|
||||
int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
|
||||
if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
|
||||
int32_t minimalDaysInFirstWeek = getMinimalDaysInFirstWeek();
|
||||
if ((7 - relDowJan1) >= minimalDaysInFirstWeek) {
|
||||
++woy;
|
||||
}
|
||||
|
||||
@ -1639,11 +1599,13 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
|
||||
// to handle the case in which we are the first week of the
|
||||
// next year.
|
||||
|
||||
int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
|
||||
int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1, ec);
|
||||
if(U_FAILURE(ec)) return;
|
||||
woy = weekNumber(prevDoy, dayOfWeek);
|
||||
yearOfWeekOfYear--;
|
||||
} else {
|
||||
int32_t lastDoy = handleGetYearLength(eyear);
|
||||
int32_t lastDoy = handleGetYearLength(eyear, ec);
|
||||
if(U_FAILURE(ec)) return;
|
||||
// Fast check: For it to be week 1 of the next year, the DOY
|
||||
// must be on or after L-5, where L is yearLength(), then it
|
||||
// cannot possibly be week 1 of the next year:
|
||||
@ -1655,7 +1617,7 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
|
||||
if (lastRelDow < 0) {
|
||||
lastRelDow += 7;
|
||||
}
|
||||
if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
|
||||
if (((6 - lastRelDow) >= minimalDaysInFirstWeek) &&
|
||||
((dayOfYear + 7 - relDow) > lastDoy)) {
|
||||
woy = 1;
|
||||
yearOfWeekOfYear++;
|
||||
@ -2946,7 +2908,7 @@ void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
validateField(field, 1, handleGetYearLength(y), status);
|
||||
validateField(field, 1, handleGetYearLength(y, status), status);
|
||||
break;
|
||||
case UCAL_DAY_OF_WEEK_IN_MONTH:
|
||||
if (internalGet(field) == 0) {
|
||||
@ -3607,9 +3569,19 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
|
||||
fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
|
||||
__FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay);
|
||||
#endif
|
||||
if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
|
||||
if (uprv_add32_overflow(julianDay, testDate, &testDate)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
|
||||
// Fire up the calculating engines.. retry YWOY = (year-1)
|
||||
julianDay = handleComputeMonthStart(year-1, 0, false, status); // jd before Jan 1 of previous year
|
||||
int32_t prevYear;
|
||||
if (uprv_add32_overflow(year, -1, &prevYear)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
julianDay = handleComputeMonthStart(prevYear, 0, false, status); // jd before Jan 1 of previous year
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
@ -3834,16 +3806,20 @@ int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t w
|
||||
|
||||
int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const
|
||||
{
|
||||
return handleComputeMonthStart(extendedYear, month+1, true, status) -
|
||||
int32_t nextMonth;
|
||||
if (uprv_add32_overflow(month, 1, &nextMonth)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return handleComputeMonthStart(extendedYear, nextMonth, true, status) -
|
||||
handleComputeMonthStart(extendedYear, month, true, status);
|
||||
}
|
||||
|
||||
int32_t Calendar::handleGetYearLength(int32_t eyear) const
|
||||
int32_t Calendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t result = handleComputeMonthStart(eyear+1, 0, false, status) -
|
||||
handleComputeMonthStart(eyear, 0, false, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
if (U_FAILURE(status)) return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3882,7 +3858,7 @@ Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
|
||||
}
|
||||
cal->setLenient(true);
|
||||
cal->prepareGetActual(field,false,status);
|
||||
result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status));
|
||||
result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status), status);
|
||||
delete cal;
|
||||
}
|
||||
break;
|
||||
@ -4141,7 +4117,7 @@ Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode&
|
||||
if (U_SUCCESS(status)) {
|
||||
U_LOCALE_BASED(locBased,*this);
|
||||
locBased.setLocaleIDs(ures_getLocaleByType(monthNames.getAlias(), ULOC_VALID_LOCALE, &status),
|
||||
ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status));
|
||||
ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status), status);
|
||||
} else {
|
||||
status = U_USING_FALLBACK_WARNING;
|
||||
return;
|
||||
@ -4229,14 +4205,12 @@ Calendar::updateTime(UErrorCode& status)
|
||||
|
||||
Locale
|
||||
Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocale(type, status);
|
||||
return LocaleBased::getLocale(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
const char *
|
||||
Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocaleID(type, status);
|
||||
return LocaleBased::getLocaleID(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4245,7 +4219,7 @@ Calendar::recalculateStamp() {
|
||||
int32_t currentValue;
|
||||
int32_t j, i;
|
||||
|
||||
fNextStamp = 1;
|
||||
fNextStamp = kInternallySet;
|
||||
|
||||
for (j = 0; j < UCAL_FIELD_COUNT; j++) {
|
||||
currentValue = STAMP_MAX;
|
||||
|
8
deps/icu-small/source/i18n/cecal.cpp
vendored
8
deps/icu-small/source/i18n/cecal.cpp
vendored
@ -53,7 +53,6 @@ static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
|
||||
CECalendar::CECalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
|
||||
{
|
||||
setTimeInMillis(getNow(), success);
|
||||
}
|
||||
|
||||
CECalendar::CECalendar (const CECalendar& other)
|
||||
@ -65,13 +64,6 @@ CECalendar::~CECalendar()
|
||||
{
|
||||
}
|
||||
|
||||
CECalendar&
|
||||
CECalendar::operator=(const CECalendar& right)
|
||||
{
|
||||
Calendar::operator=(right);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Calendar framework
|
||||
//-------------------------------------------------------------------------
|
||||
|
7
deps/icu-small/source/i18n/cecal.h
vendored
7
deps/icu-small/source/i18n/cecal.h
vendored
@ -82,13 +82,6 @@ protected:
|
||||
*/
|
||||
virtual ~CECalendar();
|
||||
|
||||
/**
|
||||
* Default assignment operator
|
||||
* @param right Calendar object to be copied
|
||||
* @internal
|
||||
*/
|
||||
CECalendar& operator=(const CECalendar& right);
|
||||
|
||||
protected:
|
||||
//-------------------------------------------------------------------------
|
||||
// Calendar framework
|
||||
|
69
deps/icu-small/source/i18n/chnsecal.cpp
vendored
69
deps/icu-small/source/i18n/chnsecal.cpp
vendored
@ -130,7 +130,6 @@ ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success),
|
||||
hasLeapMonthBetweenWinterSolstices(false)
|
||||
{
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
|
||||
@ -219,7 +218,9 @@ int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) {
|
||||
}
|
||||
|
||||
int32_t year;
|
||||
if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) {
|
||||
// if UCAL_EXTENDED_YEAR is not older than UCAL_ERA nor UCAL_YEAR
|
||||
if (newerField(UCAL_EXTENDED_YEAR, newerField(UCAL_ERA, UCAL_YEAR)) ==
|
||||
UCAL_EXTENDED_YEAR) {
|
||||
year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
|
||||
} else {
|
||||
// adjust to the instance specific epoch
|
||||
@ -252,11 +253,16 @@ int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) {
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
|
||||
bool isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) == 1;
|
||||
return handleGetMonthLengthWithLeap(extendedYear, month, isLeapMonth, status);
|
||||
}
|
||||
|
||||
int32_t ChineseCalendar::handleGetMonthLengthWithLeap(int32_t extendedYear, int32_t month, bool leap, UErrorCode& status) const {
|
||||
const Setting setting = getSetting(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
int32_t thisStart = handleComputeMonthStart(extendedYear, month, true, status);
|
||||
int32_t thisStart = handleComputeMonthStartWithLeap(extendedYear, month, leap, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
@ -332,18 +338,24 @@ struct MonthInfo computeMonthInfo(
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth, UErrorCode& status) const {
|
||||
bool isLeapMonth = false;
|
||||
if (useMonth) {
|
||||
isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
|
||||
}
|
||||
return handleComputeMonthStartWithLeap(eyear, month, isLeapMonth, status);
|
||||
}
|
||||
|
||||
int64_t ChineseCalendar::handleComputeMonthStartWithLeap(int32_t eyear, int32_t month, bool isLeapMonth, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
// If the month is out of range, adjust it into range, and
|
||||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
double m = month;
|
||||
if (uprv_add32_overflow(eyear, ClockMath::floorDivide(m, 12.0, &m), &eyear)) {
|
||||
if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
month = static_cast<int32_t>(m);
|
||||
}
|
||||
|
||||
const Setting setting = getSetting(status);
|
||||
@ -362,19 +374,9 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ignore IS_LEAP_MONTH field if useMonth is false
|
||||
bool isLeapMonth = false;
|
||||
if (useMonth) {
|
||||
isLeapMonth = internalGet(UCAL_IS_LEAP_MONTH) != 0;
|
||||
}
|
||||
int32_t newMonthYear = Grego::dayToYear(newMoon, status);
|
||||
|
||||
int32_t unusedMonth;
|
||||
int32_t unusedDayOfWeek;
|
||||
int32_t unusedDayOfMonth;
|
||||
int32_t unusedDayOfYear;
|
||||
Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear, status);
|
||||
|
||||
struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, newMoon, status);
|
||||
struct MonthInfo monthInfo = computeMonthInfo(setting, newMonthYear, newMoon, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
@ -794,6 +796,9 @@ struct MonthInfo computeMonthInfo(
|
||||
solsticeBefore = solsticeAfter;
|
||||
solsticeAfter = winterSolstice(setting, gyear + 1, status);
|
||||
}
|
||||
if (!(solsticeBefore <= days && days < solsticeAfter)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return output;
|
||||
}
|
||||
@ -1043,7 +1048,12 @@ void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dayOfMonth, int32_t d
|
||||
}
|
||||
|
||||
// Find the target dayOfMonth
|
||||
int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dayOfMonth;
|
||||
int32_t jd;
|
||||
if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay - 1, &jd) ||
|
||||
uprv_add32_overflow(jd, dayOfMonth, &jd)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Pin the dayOfMonth. In this calendar all months are 29 or 30 days
|
||||
// so pinning just means handling dayOfMonth 30.
|
||||
@ -1182,6 +1192,27 @@ ChineseCalendar::Setting ChineseCalendar::getSetting(UErrorCode&) const {
|
||||
};
|
||||
}
|
||||
|
||||
int32_t
|
||||
ChineseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
|
||||
{
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (field == UCAL_DATE) {
|
||||
LocalPointer<ChineseCalendar> cal(clone(), status);
|
||||
if(U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
cal->setLenient(true);
|
||||
cal->prepareGetActual(field,false,status);
|
||||
int32_t year = cal->get(UCAL_EXTENDED_YEAR, status);
|
||||
int32_t month = cal->get(UCAL_MONTH, status);
|
||||
bool leap = cal->get(UCAL_IS_LEAP_MONTH, status) != 0;
|
||||
return handleGetMonthLengthWithLeap(year, month, leap, status);
|
||||
}
|
||||
return Calendar::getActualMaximum(field, status);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
6
deps/icu-small/source/i18n/chnsecal.h
vendored
6
deps/icu-small/source/i18n/chnsecal.h
vendored
@ -194,6 +194,10 @@ class U_I18N_API ChineseCalendar : public Calendar {
|
||||
virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override;
|
||||
virtual const UFieldResolutionTable* getFieldResolutionTable() const override;
|
||||
|
||||
private:
|
||||
int32_t handleGetMonthLengthWithLeap(int32_t extendedYear, int32_t month, bool isLeap, UErrorCode& status) const;
|
||||
int64_t handleComputeMonthStartWithLeap(int32_t eyear, int32_t month, bool isLeap, UErrorCode& status) const;
|
||||
|
||||
public:
|
||||
virtual void add(UCalendarDateFields field, int32_t amount, UErrorCode &status) override;
|
||||
virtual void add(EDateFields field, int32_t amount, UErrorCode &status) override;
|
||||
@ -254,6 +258,8 @@ class U_I18N_API ChineseCalendar : public Calendar {
|
||||
*/
|
||||
virtual const char * getType() const override;
|
||||
|
||||
virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const override;
|
||||
|
||||
struct Setting {
|
||||
int32_t epochYear;
|
||||
const TimeZone* zoneAstroCalc;
|
||||
|
@ -613,18 +613,24 @@ CollationRuleParser::parseSetting(UErrorCode &errorCode) {
|
||||
return;
|
||||
}
|
||||
// localeID minus all keywords
|
||||
char baseID[ULOC_FULLNAME_CAPACITY];
|
||||
int32_t length = uloc_getBaseName(localeID.data(), baseID, ULOC_FULLNAME_CAPACITY, &errorCode);
|
||||
if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) {
|
||||
CharString baseID = ulocimp_getBaseName(localeID.toStringPiece(), errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
errorCode = U_ZERO_ERROR;
|
||||
setParseError("expected language tag in [import langTag]", errorCode);
|
||||
return;
|
||||
}
|
||||
if(length == 0) {
|
||||
uprv_strcpy(baseID, "root");
|
||||
} else if(*baseID == '_') {
|
||||
uprv_memmove(baseID + 3, baseID, length + 1);
|
||||
uprv_memcpy(baseID, "und", 3);
|
||||
if (baseID.isEmpty()) {
|
||||
baseID.copyFrom("root", errorCode);
|
||||
} else if (baseID[0] == '_') {
|
||||
// CharString doesn't have any insert() method, only append().
|
||||
constexpr char und[] = "und";
|
||||
constexpr int32_t length = sizeof und - 1;
|
||||
int32_t dummy;
|
||||
char* tail = baseID.getAppendBuffer(length, length, dummy, errorCode);
|
||||
char* head = baseID.data();
|
||||
uprv_memmove(head + length, head, baseID.length());
|
||||
uprv_memcpy(head, und, length);
|
||||
baseID.append(tail, length, errorCode);
|
||||
}
|
||||
// @collation=type, or length=0 if not specified
|
||||
CharString collationType = ulocimp_getKeywordValue(localeID.data(), "collation", errorCode);
|
||||
@ -637,7 +643,7 @@ CollationRuleParser::parseSetting(UErrorCode &errorCode) {
|
||||
setParseError("[import langTag] is not supported", errorCode);
|
||||
} else {
|
||||
UnicodeString importedRules;
|
||||
importer->getRules(baseID,
|
||||
importer->getRules(baseID.data(),
|
||||
!collationType.isEmpty() ? collationType.data() : "standard",
|
||||
importedRules, errorReason, errorCode);
|
||||
if(U_FAILURE(errorCode)) {
|
||||
|
7
deps/icu-small/source/i18n/datefmt.cpp
vendored
7
deps/icu-small/source/i18n/datefmt.cpp
vendored
@ -40,6 +40,7 @@
|
||||
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <typeinfo>
|
||||
|
||||
// *****************************************************************************
|
||||
// class DateFormat
|
||||
@ -279,9 +280,8 @@ UnicodeString&
|
||||
DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const {
|
||||
if (fCalendar != nullptr) {
|
||||
UErrorCode ec = U_ZERO_ERROR;
|
||||
const auto* calType = fCalendar->getType();
|
||||
// Avoid a heap allocation and corresponding free for the common case
|
||||
if (uprv_strcmp(calType, "gregorian") == 0) {
|
||||
if (typeid(*fCalendar) == typeid(GregorianCalendar)) {
|
||||
GregorianCalendar cal(*static_cast<GregorianCalendar*>(fCalendar));
|
||||
cal.setTime(date, ec);
|
||||
if (U_SUCCESS(ec)) {
|
||||
@ -309,9 +309,8 @@ DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* p
|
||||
UErrorCode& status) const {
|
||||
if (fCalendar != nullptr) {
|
||||
UErrorCode ec = U_ZERO_ERROR;
|
||||
const auto* calType = fCalendar->getType();
|
||||
// Avoid a heap allocation and corresponding free for the common case
|
||||
if (uprv_strcmp(calType, "gregorian") == 0) {
|
||||
if (typeid(*fCalendar) == typeid(GregorianCalendar)) {
|
||||
GregorianCalendar cal(*static_cast<GregorianCalendar*>(fCalendar));
|
||||
cal.setTime(date, ec);
|
||||
if (U_SUCCESS(ec)) {
|
||||
|
22
deps/icu-small/source/i18n/dcfmtsym.cpp
vendored
22
deps/icu-small/source/i18n/dcfmtsym.cpp
vendored
@ -118,7 +118,6 @@ DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSys
|
||||
|
||||
DecimalFormatSymbols::DecimalFormatSymbols()
|
||||
: UObject(), locale(Locale::getRoot()) {
|
||||
*validLocale = *actualLocale = 0;
|
||||
initialize();
|
||||
}
|
||||
|
||||
@ -136,6 +135,8 @@ DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) {
|
||||
|
||||
DecimalFormatSymbols::~DecimalFormatSymbols()
|
||||
{
|
||||
delete actualLocale;
|
||||
delete validLocale;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -163,8 +164,12 @@ DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
|
||||
currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]);
|
||||
}
|
||||
locale = rhs.locale;
|
||||
uprv_strcpy(validLocale, rhs.validLocale);
|
||||
uprv_strcpy(actualLocale, rhs.actualLocale);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(rhs.validLocale, rhs.actualLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
|
||||
fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol;
|
||||
fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol;
|
||||
fCodePointZero = rhs.fCodePointZero;
|
||||
@ -203,8 +208,8 @@ DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const
|
||||
}
|
||||
// No need to check fCodePointZero since it is based on fSymbols
|
||||
return locale == that.locale &&
|
||||
uprv_strcmp(validLocale, that.validLocale) == 0 &&
|
||||
uprv_strcmp(actualLocale, that.actualLocale) == 0;
|
||||
LocaleBased::equalIDs(actualLocale, that.actualLocale) &&
|
||||
LocaleBased::equalIDs(validLocale, that.validLocale);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -353,7 +358,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
|
||||
UBool useLastResortData, const NumberingSystem* ns)
|
||||
{
|
||||
if (U_FAILURE(status)) { return; }
|
||||
*validLocale = *actualLocale = 0;
|
||||
|
||||
// First initialize all the symbols to the fallbacks for anything we can't find
|
||||
initialize();
|
||||
@ -409,7 +413,8 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
|
||||
ULOC_VALID_LOCALE, &status),
|
||||
ures_getLocaleByType(
|
||||
numberElementsRes.getAlias(),
|
||||
ULOC_ACTUAL_LOCALE, &status));
|
||||
ULOC_ACTUAL_LOCALE, &status),
|
||||
status);
|
||||
|
||||
// Now load the rest of the data from the data sink.
|
||||
// Start with loading this nsName if it is not Latin.
|
||||
@ -568,8 +573,7 @@ void DecimalFormatSymbols::setCurrency(const char16_t* currency, UErrorCode& sta
|
||||
|
||||
Locale
|
||||
DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocale(type, status);
|
||||
return LocaleBased::getLocale(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
const UnicodeString&
|
||||
|
16
deps/icu-small/source/i18n/dtfmtsym.cpp
vendored
16
deps/icu-small/source/i18n/dtfmtsym.cpp
vendored
@ -402,9 +402,8 @@ void
|
||||
DateFormatSymbols::copyData(const DateFormatSymbols& other) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(
|
||||
other.getLocale(ULOC_VALID_LOCALE, status),
|
||||
other.getLocale(ULOC_ACTUAL_LOCALE, status));
|
||||
locBased.setLocaleIDs(other.validLocale, other.actualLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
assignArray(fEras, fErasCount, other.fEras, other.fErasCount);
|
||||
assignArray(fEraNames, fEraNamesCount, other.fEraNames, other.fEraNamesCount);
|
||||
assignArray(fNarrowEras, fNarrowErasCount, other.fNarrowEras, other.fNarrowErasCount);
|
||||
@ -497,6 +496,8 @@ DateFormatSymbols& DateFormatSymbols::operator=(const DateFormatSymbols& other)
|
||||
DateFormatSymbols::~DateFormatSymbols()
|
||||
{
|
||||
dispose();
|
||||
delete actualLocale;
|
||||
delete validLocale;
|
||||
}
|
||||
|
||||
void DateFormatSymbols::dispose()
|
||||
@ -536,6 +537,10 @@ void DateFormatSymbols::dispose()
|
||||
delete[] fStandaloneWideDayPeriods;
|
||||
delete[] fStandaloneNarrowDayPeriods;
|
||||
|
||||
delete actualLocale;
|
||||
actualLocale = nullptr;
|
||||
delete validLocale;
|
||||
validLocale = nullptr;
|
||||
disposeZoneStrings();
|
||||
}
|
||||
|
||||
@ -2302,7 +2307,7 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
|
||||
// of it that we need except for the time-zone and localized-pattern data, which
|
||||
// are stored in a separate file
|
||||
locBased.setLocaleIDs(ures_getLocaleByType(cb.getAlias(), ULOC_VALID_LOCALE, &status),
|
||||
ures_getLocaleByType(cb.getAlias(), ULOC_ACTUAL_LOCALE, &status));
|
||||
ures_getLocaleByType(cb.getAlias(), ULOC_ACTUAL_LOCALE, &status), status);
|
||||
|
||||
// Load eras
|
||||
initField(&fEras, fErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesAbbrTag, status), status);
|
||||
@ -2528,8 +2533,7 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError
|
||||
|
||||
Locale
|
||||
DateFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocale(type, status);
|
||||
return LocaleBased::getLocale(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
5
deps/icu-small/source/i18n/erarules.cpp
vendored
5
deps/icu-small/source/i18n/erarules.cpp
vendored
@ -305,8 +305,9 @@ void EraRules::initCurrentEra() {
|
||||
localMillis += (rawOffset + dstOffset);
|
||||
}
|
||||
|
||||
int year, month0, dom, dow, doy, mid;
|
||||
Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid, ec);
|
||||
int32_t year, mid;
|
||||
int8_t month0, dom;
|
||||
Grego::timeToFields(localMillis, year, month0, dom, mid, ec);
|
||||
if (U_FAILURE(ec)) return;
|
||||
int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom);
|
||||
int eraIdx = numEras - 1;
|
||||
|
20
deps/icu-small/source/i18n/format.cpp
vendored
20
deps/icu-small/source/i18n/format.cpp
vendored
@ -24,6 +24,7 @@
|
||||
#include "utypeinfo.h" // for 'typeid' to work
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "charstr.h"
|
||||
|
||||
#ifndef U_I18N_IMPLEMENTATION
|
||||
#error U_I18N_IMPLEMENTATION not set - must be set for all ICU source files in i18n/ - see https://unicode-org.github.io/icu/userguide/howtouseicu
|
||||
@ -72,13 +73,14 @@ FieldPosition::clone() const {
|
||||
Format::Format()
|
||||
: UObject()
|
||||
{
|
||||
*validLocale = *actualLocale = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
Format::~Format()
|
||||
{
|
||||
delete actualLocale;
|
||||
delete validLocale;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -97,8 +99,10 @@ Format&
|
||||
Format::operator=(const Format& that)
|
||||
{
|
||||
if (this != &that) {
|
||||
uprv_strcpy(validLocale, that.validLocale);
|
||||
uprv_strcpy(actualLocale, that.actualLocale);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(that.validLocale, that.actualLocale, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -196,20 +200,20 @@ void Format::syntaxError(const UnicodeString& pattern,
|
||||
|
||||
Locale
|
||||
Format::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocale(type, status);
|
||||
return LocaleBased::getLocale(validLocale, actualLocale, type, status);
|
||||
}
|
||||
|
||||
const char *
|
||||
Format::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
return locBased.getLocaleID(type, status);
|
||||
return LocaleBased::getLocaleID(validLocale,actualLocale, type, status);
|
||||
}
|
||||
|
||||
void
|
||||
Format::setLocaleIDs(const char* valid, const char* actual) {
|
||||
U_LOCALE_BASED(locBased, *this);
|
||||
locBased.setLocaleIDs(valid, actual);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
locBased.setLocaleIDs(valid, actual, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
@ -193,6 +193,11 @@ ucfpos_close(UConstrainedFieldPosition* ptr) {
|
||||
}
|
||||
|
||||
|
||||
// -Wreturn-local-addr first found in https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Warning-Options.html#Warning-Options
|
||||
#if U_GCC_MAJOR_MINOR >= 409
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
|
||||
#endif
|
||||
U_CAPI const char16_t* U_EXPORT2
|
||||
ufmtval_getString(
|
||||
const UFormattedValue* ufmtval,
|
||||
@ -213,6 +218,9 @@ ufmtval_getString(
|
||||
// defined to return memory owned by the ufmtval argument.
|
||||
return readOnlyAlias.getBuffer();
|
||||
}
|
||||
#if U_GCC_MAJOR_MINOR >= 409
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
U_CAPI UBool U_EXPORT2
|
||||
|
66
deps/icu-small/source/i18n/gregocal.cpp
vendored
66
deps/icu-small/source/i18n/gregocal.cpp
vendored
@ -147,6 +147,7 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar)
|
||||
// in Java, -12219292800000L
|
||||
//const UDate GregorianCalendar::kPapalCutover = -12219292800000L;
|
||||
static const uint32_t kCutoverJulianDay = 2299161;
|
||||
static const int32_t kDefaultCutoverYear = 1582;
|
||||
static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY;
|
||||
//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay);
|
||||
|
||||
@ -155,7 +156,7 @@ static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILL
|
||||
GregorianCalendar::GregorianCalendar(UErrorCode& status)
|
||||
: Calendar(status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(kDefaultCutoverYear),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
@ -164,34 +165,22 @@ fIsGregorian(true), fInvertGregorian(false)
|
||||
// -------------------------------------
|
||||
|
||||
GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status)
|
||||
: Calendar(zone, Locale::getDefault(), status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
: GregorianCalendar(zone, Locale::getDefault(), status)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status)
|
||||
: Calendar(zone, Locale::getDefault(), status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
: GregorianCalendar(zone, Locale::getDefault(), status)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
: GregorianCalendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, status)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
@ -200,7 +189,7 @@ GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale,
|
||||
UErrorCode& status)
|
||||
: Calendar(zone, aLocale, status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(kDefaultCutoverYear),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
@ -212,7 +201,7 @@ GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale
|
||||
UErrorCode& status)
|
||||
: Calendar(zone, aLocale, status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(kDefaultCutoverYear),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
{
|
||||
setTimeInMillis(getNow(), status);
|
||||
@ -224,7 +213,7 @@ GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
|
||||
UErrorCode& status)
|
||||
: Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(kDefaultCutoverYear),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
{
|
||||
set(UCAL_ERA, AD);
|
||||
@ -237,15 +226,8 @@ GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
|
||||
|
||||
GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
|
||||
int32_t hour, int32_t minute, UErrorCode& status)
|
||||
: Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
: GregorianCalendar(year, month, date, status)
|
||||
{
|
||||
set(UCAL_ERA, AD);
|
||||
set(UCAL_YEAR, year);
|
||||
set(UCAL_MONTH, month);
|
||||
set(UCAL_DATE, date);
|
||||
set(UCAL_HOUR_OF_DAY, hour);
|
||||
set(UCAL_MINUTE, minute);
|
||||
}
|
||||
@ -255,17 +237,8 @@ GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
|
||||
GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
|
||||
int32_t hour, int32_t minute, int32_t second,
|
||||
UErrorCode& status)
|
||||
: Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
|
||||
fGregorianCutover(kPapalCutover),
|
||||
fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
|
||||
fIsGregorian(true), fInvertGregorian(false)
|
||||
: GregorianCalendar(year, month, date, hour, minute, status)
|
||||
{
|
||||
set(UCAL_ERA, AD);
|
||||
set(UCAL_YEAR, year);
|
||||
set(UCAL_MONTH, month);
|
||||
set(UCAL_DATE, date);
|
||||
set(UCAL_HOUR_OF_DAY, hour);
|
||||
set(UCAL_MINUTE, minute);
|
||||
set(UCAL_SECOND, second);
|
||||
}
|
||||
|
||||
@ -601,7 +574,8 @@ int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mo
|
||||
return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month];
|
||||
}
|
||||
|
||||
int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const {
|
||||
int32_t GregorianCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) return 0;
|
||||
return isLeapYear(eyear) ? 366 : 365;
|
||||
}
|
||||
|
||||
@ -871,13 +845,14 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
|
||||
}
|
||||
if (month == UCAL_JANUARY) {
|
||||
if (woy >= 52) {
|
||||
isoDoy += handleGetYearLength(isoYear);
|
||||
isoDoy += handleGetYearLength(isoYear, status);
|
||||
}
|
||||
} else {
|
||||
if (woy == 1) {
|
||||
isoDoy -= handleGetYearLength(isoYear - 1);
|
||||
isoDoy -= handleGetYearLength(isoYear - 1, status);
|
||||
}
|
||||
}
|
||||
if (U_FAILURE(status)) return;
|
||||
if (uprv_add32_overflow(woy, amount, &woy)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
@ -890,7 +865,8 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
|
||||
// days at the end of the year are going to fall into
|
||||
// week 1 of the next year, we drop the last week by
|
||||
// subtracting 7 from the last day of the year.
|
||||
int32_t lastDoy = handleGetYearLength(isoYear);
|
||||
int32_t lastDoy = handleGetYearLength(isoYear, status);
|
||||
if (U_FAILURE(status)) return;
|
||||
int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) -
|
||||
getFirstDayOfWeek()) % 7;
|
||||
if (lastRelDow < 0) lastRelDow += 7;
|
||||
@ -1186,14 +1162,10 @@ int32_t GregorianCalendar::handleGetExtendedYear(UErrorCode& status) {
|
||||
int32_t year = kEpochYear;
|
||||
|
||||
// year field to use
|
||||
int32_t yearField = UCAL_EXTENDED_YEAR;
|
||||
|
||||
// There are three separate fields which could be used to
|
||||
// derive the proper year. Use the one most recently set.
|
||||
if (fStamp[yearField] < fStamp[UCAL_YEAR])
|
||||
yearField = UCAL_YEAR;
|
||||
if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY])
|
||||
yearField = UCAL_YEAR_WOY;
|
||||
UCalendarDateFields yearField = newerField(
|
||||
newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR), UCAL_YEAR_WOY);
|
||||
|
||||
// based on the "best" year field, get the year
|
||||
switch(yearField) {
|
||||
|
111
deps/icu-small/source/i18n/gregoimp.cpp
vendored
111
deps/icu-small/source/i18n/gregoimp.cpp
vendored
@ -117,57 +117,110 @@ int64_t Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) {
|
||||
return julian - JULIAN_1970_CE; // JD => epoch day
|
||||
}
|
||||
|
||||
void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status) {
|
||||
|
||||
void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int16_t& doy, UErrorCode& status) {
|
||||
year = dayToYear(day, doy, status); // one-based doy
|
||||
if (U_FAILURE(status)) return;
|
||||
|
||||
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
|
||||
if (uprv_add32_overflow(day, JULIAN_1970_CE - JULIAN_1_CE, &day)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert from the day number to the multiple radix
|
||||
// representation. We use 400-year, 100-year, and 4-year cycles.
|
||||
// For example, the 4-year cycle has 4 years + 1 leap day; giving
|
||||
// 1461 == 365*4 + 1 days.
|
||||
int32_t n400 = ClockMath::floorDivide(day, 146097, &doy); // 400-year cycle length
|
||||
int32_t n100 = ClockMath::floorDivide(doy, 36524, &doy); // 100-year cycle length
|
||||
int32_t n4 = ClockMath::floorDivide(doy, 1461, &doy); // 4-year cycle length
|
||||
int32_t n1 = ClockMath::floorDivide(doy, 365, &doy);
|
||||
year = 400*n400 + 100*n100 + 4*n4 + n1;
|
||||
if (n100 == 4 || n1 == 4) {
|
||||
doy = 365; // Dec 31 at end of 4- or 400-year cycle
|
||||
} else {
|
||||
++year;
|
||||
}
|
||||
|
||||
UBool isLeap = isLeapYear(year);
|
||||
|
||||
// Gregorian day zero is a Monday.
|
||||
dow = (day + 1) % 7;
|
||||
dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY;
|
||||
|
||||
// Common Julian/Gregorian calculation
|
||||
int32_t correction = 0;
|
||||
bool isLeap = isLeapYear(year);
|
||||
int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
|
||||
if (doy >= march1) {
|
||||
if (doy > march1) {
|
||||
correction = isLeap ? 1 : 2;
|
||||
}
|
||||
month = (12 * (doy + correction) + 6) / 367; // zero-based month
|
||||
dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM
|
||||
doy++; // one-based doy
|
||||
month = (12 * (doy - 1 + correction) + 6) / 367; // zero-based month
|
||||
dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)]; // one-based DOM
|
||||
}
|
||||
|
||||
void Grego::timeToFields(UDate time, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status) {
|
||||
int32_t Grego::dayToYear(int32_t day, UErrorCode& status) {
|
||||
int16_t unusedDOY;
|
||||
return dayToYear(day, unusedDOY, status);
|
||||
}
|
||||
|
||||
int32_t Grego::dayToYear(int32_t day, int16_t& doy, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) return 0;
|
||||
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
|
||||
if (uprv_add32_overflow(day, JULIAN_1970_CE - JULIAN_1_CE, &day)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert from the day number to the multiple radix
|
||||
// representation. We use 400-year, 100-year, and 4-year cycles.
|
||||
// For example, the 4-year cycle has 4 years + 1 leap day; giving
|
||||
// 1461 == 365*4 + 1 days.
|
||||
int32_t doy32;
|
||||
int32_t n400 = ClockMath::floorDivide(day, 146097, &doy32); // 400-year cycle length
|
||||
int32_t n100 = ClockMath::floorDivide(doy32, 36524, &doy32); // 100-year cycle length
|
||||
int32_t n4 = ClockMath::floorDivide(doy32, 1461, &doy32); // 4-year cycle length
|
||||
int32_t n1 = ClockMath::floorDivide(doy32, 365, &doy32);
|
||||
int32_t year = 400*n400 + 100*n100 + 4*n4 + n1;
|
||||
if (n100 == 4 || n1 == 4) {
|
||||
doy = 365; // Dec 31 at end of 4- or 400-year cycle
|
||||
} else {
|
||||
doy = doy32;
|
||||
++year;
|
||||
}
|
||||
doy++; // one-based doy
|
||||
return year;
|
||||
}
|
||||
|
||||
void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, UErrorCode& status) {
|
||||
int16_t unusedDOY;
|
||||
dayToFields(day, year, month, dom, dow, unusedDOY, status);
|
||||
}
|
||||
|
||||
void Grego::dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int16_t& doy, UErrorCode& status) {
|
||||
int8_t unusedDOW;
|
||||
dayToFields(day, year, month, dom, unusedDOW, doy, status);
|
||||
}
|
||||
|
||||
void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int32_t& mid, UErrorCode& status) {
|
||||
int8_t unusedDOW;
|
||||
timeToFields(time, year, month, dom, unusedDOW, mid, status);
|
||||
}
|
||||
|
||||
void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int32_t& mid, UErrorCode& status) {
|
||||
int16_t unusedDOY;
|
||||
timeToFields(time, year, month, dom, dow, unusedDOY, mid, status);
|
||||
}
|
||||
|
||||
void Grego::timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int16_t& doy, int32_t& mid, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) return;
|
||||
double millisInDay;
|
||||
double day = ClockMath::floorDivide(static_cast<double>(time), static_cast<double>(U_MILLIS_PER_DAY), &millisInDay);
|
||||
mid = static_cast<int32_t>(millisInDay);
|
||||
double day = ClockMath::floorDivide(time, U_MILLIS_PER_DAY, &mid);
|
||||
if (day > INT32_MAX || day < INT32_MIN) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
dayToFields(day, year, month, dom, dow, doy, status);
|
||||
}
|
||||
|
||||
int32_t Grego::timeToYear(UDate time, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) return 0;
|
||||
double day = ClockMath::floorDivide(time, double(U_MILLIS_PER_DAY));
|
||||
if (day > INT32_MAX || day < INT32_MIN) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return Grego::dayToYear(day, status);
|
||||
}
|
||||
|
||||
int32_t Grego::dayOfWeek(int32_t day) {
|
||||
int32_t dow;
|
||||
ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, &dow);
|
||||
|
82
deps/icu-small/source/i18n/gregoimp.h
vendored
82
deps/icu-small/source/i18n/gregoimp.h
vendored
@ -210,8 +210,21 @@ class Grego {
|
||||
* @param doy output parameter to receive day-of-year (1-based)
|
||||
* @param status error code.
|
||||
*/
|
||||
static void dayToFields(int32_t day, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status);
|
||||
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int16_t& doy, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch day number to proleptic Gregorian year,
|
||||
* month, day-of-month, and day-of-week.
|
||||
* @param day 1970-epoch day
|
||||
* @param year output parameter to receive year
|
||||
* @param month output parameter to receive month (0-based, 0==Jan)
|
||||
* @param dom output parameter to receive day-of-month (1-based)
|
||||
* @param doy output parameter to receive day-of-year (1-based)
|
||||
* @param status error code.
|
||||
*/
|
||||
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int16_t& doy, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch day number to proleptic Gregorian year,
|
||||
@ -223,8 +236,24 @@ class Grego {
|
||||
* @param dow output parameter to receive day-of-week (1-based, 1==Sun)
|
||||
* @param status error code.
|
||||
*/
|
||||
static inline void dayToFields(int32_t day, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, UErrorCode& status);
|
||||
static void dayToFields(int32_t day, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch day number to proleptic Gregorian year.
|
||||
* @param day 1970-epoch day
|
||||
* @param status error code.
|
||||
* @return year.
|
||||
*/
|
||||
static int32_t dayToYear(int32_t day, UErrorCode& status);
|
||||
/**
|
||||
* Convert a 1970-epoch day number to proleptic Gregorian year.
|
||||
* @param day 1970-epoch day
|
||||
* @param doy output parameter to receive day-of-year (1-based)
|
||||
* @param status error code.
|
||||
* @return year.
|
||||
*/
|
||||
static int32_t dayToYear(int32_t day, int16_t& doy, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
|
||||
@ -238,8 +267,43 @@ class Grego {
|
||||
* @param mid output parameter to receive millis-in-day
|
||||
* @param status error code.
|
||||
*/
|
||||
static void timeToFields(UDate time, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status);
|
||||
static void timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int16_t& doy, int32_t& mid, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
|
||||
* month, day-of-month, and day-of-week, day of year and millis-in-day.
|
||||
* @param time 1970-epoch milliseconds
|
||||
* @param year output parameter to receive year
|
||||
* @param month output parameter to receive month (0-based, 0==Jan)
|
||||
* @param dom output parameter to receive day-of-month (1-based)
|
||||
* @param dow output parameter to receive day-of-week (1-based, 1==Sun)
|
||||
* @param mid output parameter to receive millis-in-day
|
||||
* @param status error code.
|
||||
*/
|
||||
static void timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int8_t& dow, int32_t& mid, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch milliseconds to proleptic Gregorian year,
|
||||
* month, day-of-month, and day-of-week, day of year and millis-in-day.
|
||||
* @param time 1970-epoch milliseconds
|
||||
* @param year output parameter to receive year
|
||||
* @param month output parameter to receive month (0-based, 0==Jan)
|
||||
* @param dom output parameter to receive day-of-month (1-based)
|
||||
* @param mid output parameter to receive millis-in-day
|
||||
* @param status error code.
|
||||
*/
|
||||
static void timeToFields(UDate time, int32_t& year, int8_t& month,
|
||||
int8_t& dom, int32_t& mid, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Convert a 1970-epoch milliseconds to proleptic Gregorian year.
|
||||
* @param time 1970-epoch milliseconds
|
||||
* @param status error code.
|
||||
* @return year.
|
||||
*/
|
||||
static int32_t timeToYear(UDate time, UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Return the day of week on the 1970-epoch day
|
||||
@ -305,12 +369,6 @@ Grego::previousMonthLength(int y, int m) {
|
||||
return (m > 0) ? monthLength(y, m-1) : 31;
|
||||
}
|
||||
|
||||
inline void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, UErrorCode& status) {
|
||||
int32_t doy_unused;
|
||||
dayToFields(day,year,month,dom,dow,doy_unused, status);
|
||||
}
|
||||
|
||||
inline double Grego::julianDayToMillis(int32_t julian)
|
||||
{
|
||||
return (static_cast<double>(julian) - kEpochStartAsJulianDay) * kOneDay;
|
||||
|
10
deps/icu-small/source/i18n/hebrwcal.cpp
vendored
10
deps/icu-small/source/i18n/hebrwcal.cpp
vendored
@ -164,7 +164,6 @@ HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
|
||||
|
||||
{
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
|
||||
@ -591,13 +590,8 @@ int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month
|
||||
* Returns the number of days in the given Hebrew year
|
||||
* @internal
|
||||
*/
|
||||
int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t len = daysInYear(eyear, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 12;
|
||||
}
|
||||
return len;
|
||||
int32_t HebrewCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
|
||||
return daysInYear(eyear, status);
|
||||
}
|
||||
|
||||
void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) {
|
||||
|
4
deps/icu-small/source/i18n/hebrwcal.h
vendored
4
deps/icu-small/source/i18n/hebrwcal.h
vendored
@ -326,9 +326,9 @@ public:
|
||||
* calendar system. Subclasses should override this method if they can
|
||||
* provide a more correct or more efficient implementation than the
|
||||
* default implementation in Calendar.
|
||||
* @stable ICU 2.0
|
||||
* @internal
|
||||
*/
|
||||
virtual int32_t handleGetYearLength(int32_t eyear) const override;
|
||||
virtual int32_t handleGetYearLength(int32_t eyear, UErrorCode& status) const override;
|
||||
|
||||
/**
|
||||
* Subclasses may override this method to compute several fields
|
||||
|
21
deps/icu-small/source/i18n/indiancal.cpp
vendored
21
deps/icu-small/source/i18n/indiancal.cpp
vendored
@ -41,7 +41,6 @@ IndianCalendar* IndianCalendar::clone() const {
|
||||
IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
|
||||
{
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) {
|
||||
@ -129,7 +128,8 @@ int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month, UErro
|
||||
*
|
||||
* @param eyear The year in Saka Era.
|
||||
*/
|
||||
int32_t IndianCalendar::handleGetYearLength(int32_t eyear) const {
|
||||
int32_t IndianCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) return 0;
|
||||
return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365;
|
||||
}
|
||||
/*
|
||||
@ -143,18 +143,6 @@ static double gregorianToJD(int32_t year, int32_t month, int32_t date) {
|
||||
return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the Gregorian Date corresponding to a given Julian Day
|
||||
* Month is 0 based.
|
||||
* @param jd The Julian Day
|
||||
*/
|
||||
static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3], UErrorCode& status) {
|
||||
int32_t gdow;
|
||||
Grego::dayToFields(jd - kEpochStartAsJulianDay,
|
||||
gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow, status);
|
||||
return gregorianDate;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions for converting from field values to milliseconds....
|
||||
@ -266,10 +254,9 @@ int32_t IndianCalendar::handleGetExtendedYear(UErrorCode& status) {
|
||||
void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
|
||||
double jdAtStartOfGregYear;
|
||||
int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
|
||||
int32_t gregorianYear; // Stores gregorian date corresponding to Julian day;
|
||||
int32_t gd[3];
|
||||
// Stores gregorian date corresponding to Julian day;
|
||||
int32_t gregorianYear = Grego::dayToYear(julianDay - kEpochStartAsJulianDay, status);
|
||||
|
||||
gregorianYear = jdToGregorian(julianDay, gd, status)[0]; // Gregorian date for Julian day
|
||||
if (U_FAILURE(status)) return;
|
||||
IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era
|
||||
jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year
|
||||
|
2
deps/icu-small/source/i18n/indiancal.h
vendored
2
deps/icu-small/source/i18n/indiancal.h
vendored
@ -215,7 +215,7 @@ public:
|
||||
* Return the number of days in the given Indian year
|
||||
* @internal
|
||||
*/
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear) const override;
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear, UErrorCode& status) const override;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions for converting from field values to milliseconds....
|
||||
|
28
deps/icu-small/source/i18n/islamcal.cpp
vendored
28
deps/icu-small/source/i18n/islamcal.cpp
vendored
@ -202,7 +202,6 @@ IslamicCalendar* IslamicCalendar::clone() const {
|
||||
IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
|
||||
{
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
IslamicCalendar::~IslamicCalendar()
|
||||
@ -444,15 +443,8 @@ int32_t yearLength(int32_t extendedYear, UErrorCode& status) {
|
||||
* Return the number of days in the given Islamic year
|
||||
* @draft ICU 2.4
|
||||
*/
|
||||
int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t length = yearLength(extendedYear, status);
|
||||
if (U_FAILURE(status)) {
|
||||
// fallback to normal Islamic calendar length 355 day a year if we
|
||||
// encounter error and cannot propagate.
|
||||
return 355;
|
||||
}
|
||||
return length;
|
||||
int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
|
||||
return yearLength(extendedYear, status);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@ -706,7 +698,8 @@ int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t
|
||||
* Return the number of days in the given Islamic year
|
||||
* @draft ICU 2.4
|
||||
*/
|
||||
int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear) const {
|
||||
int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) return 0;
|
||||
return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
|
||||
}
|
||||
|
||||
@ -872,7 +865,7 @@ int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int3
|
||||
|
||||
int32_t IslamicUmalquraCalendar::yearLength(int32_t extendedYear, UErrorCode& status) const {
|
||||
if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
|
||||
return IslamicCivilCalendar::handleGetYearLength(extendedYear);
|
||||
return IslamicCivilCalendar::handleGetYearLength(extendedYear, status);
|
||||
}
|
||||
int length = 0;
|
||||
for(int i=0; i<12; i++) {
|
||||
@ -888,15 +881,8 @@ int32_t IslamicUmalquraCalendar::yearLength(int32_t extendedYear, UErrorCode& st
|
||||
* Return the number of days in the given Islamic year
|
||||
* @draft ICU 2.4
|
||||
*/
|
||||
int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t length = yearLength(extendedYear, status);
|
||||
if (U_FAILURE(status)) {
|
||||
// fallback to normal Islamic calendar length 355 day a year if we
|
||||
// encounter error and cannot propagate.
|
||||
return 355;
|
||||
}
|
||||
return length;
|
||||
int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
|
||||
return yearLength(extendedYear, status);
|
||||
}
|
||||
|
||||
/**
|
||||
|
6
deps/icu-small/source/i18n/islamcal.h
vendored
6
deps/icu-small/source/i18n/islamcal.h
vendored
@ -235,7 +235,7 @@ class U_I18N_API IslamicCalendar : public Calendar {
|
||||
* Return the number of days in the given Islamic year
|
||||
* @internal
|
||||
*/
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear) const override;
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear, UErrorCode& status) const override;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Functions for converting from field values to milliseconds....
|
||||
@ -438,7 +438,7 @@ class U_I18N_API IslamicCivilCalendar : public IslamicCalendar {
|
||||
* Return the number of days in the given Islamic year
|
||||
* @internal
|
||||
*/
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear) const override;
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear, UErrorCode& status) const override;
|
||||
|
||||
/**
|
||||
* Override Calendar to compute several fields specific to the Islamic
|
||||
@ -621,7 +621,7 @@ class U_I18N_API IslamicUmalquraCalendar : public IslamicCivilCalendar {
|
||||
* Return the number of days in the given Islamic year
|
||||
* @internal
|
||||
*/
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear) const override;
|
||||
virtual int32_t handleGetYearLength(int32_t extendedYear, UErrorCode& status) const override;
|
||||
|
||||
/**
|
||||
* Override Calendar to compute several fields specific to the Islamic
|
||||
|
7
deps/icu-small/source/i18n/japancal.cpp
vendored
7
deps/icu-small/source/i18n/japancal.cpp
vendored
@ -115,7 +115,6 @@ JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success)
|
||||
: GregorianCalendar(aLocale, success)
|
||||
{
|
||||
init(success);
|
||||
setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
|
||||
}
|
||||
|
||||
JapaneseCalendar::~JapaneseCalendar()
|
||||
@ -130,12 +129,6 @@ JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source)
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
|
||||
JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right)
|
||||
{
|
||||
GregorianCalendar::operator=(right);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JapaneseCalendar* JapaneseCalendar::clone() const
|
||||
{
|
||||
return new JapaneseCalendar(*this);
|
||||
|
7
deps/icu-small/source/i18n/japancal.h
vendored
7
deps/icu-small/source/i18n/japancal.h
vendored
@ -104,13 +104,6 @@ public:
|
||||
*/
|
||||
JapaneseCalendar(const JapaneseCalendar& source);
|
||||
|
||||
/**
|
||||
* Default assignment operator
|
||||
* @param right the object to be copied.
|
||||
* @internal
|
||||
*/
|
||||
JapaneseCalendar& operator=(const JapaneseCalendar& right);
|
||||
|
||||
/**
|
||||
* Create and return a polymorphic copy of this calendar.
|
||||
* @return return a polymorphic copy of this calendar.
|
||||
|
48
deps/icu-small/source/i18n/measunit.cpp
vendored
48
deps/icu-small/source/i18n/measunit.cpp
vendored
@ -41,26 +41,26 @@ static const int32_t gOffsets[] = {
|
||||
2,
|
||||
7,
|
||||
17,
|
||||
27,
|
||||
31,
|
||||
333,
|
||||
344,
|
||||
362,
|
||||
366,
|
||||
375,
|
||||
378,
|
||||
382,
|
||||
390,
|
||||
412,
|
||||
416,
|
||||
431,
|
||||
28,
|
||||
32,
|
||||
334,
|
||||
345,
|
||||
363,
|
||||
367,
|
||||
376,
|
||||
379,
|
||||
383,
|
||||
391,
|
||||
413,
|
||||
417,
|
||||
432,
|
||||
438,
|
||||
449,
|
||||
455,
|
||||
459,
|
||||
461,
|
||||
495
|
||||
433,
|
||||
439,
|
||||
450,
|
||||
456,
|
||||
460,
|
||||
462,
|
||||
496
|
||||
};
|
||||
|
||||
static const int32_t kCurrencyOffset = 5;
|
||||
@ -121,6 +121,7 @@ static const char * const gSubTypes[] = {
|
||||
"permille",
|
||||
"permillion",
|
||||
"permyriad",
|
||||
"portion-per-1e9",
|
||||
"liter-per-100-kilometer",
|
||||
"liter-per-kilometer",
|
||||
"mile-per-gallon",
|
||||
@ -811,6 +812,14 @@ MeasureUnit MeasureUnit::getPermyriad() {
|
||||
return MeasureUnit(3, 9);
|
||||
}
|
||||
|
||||
MeasureUnit *MeasureUnit::createPortionPer1E9(UErrorCode &status) {
|
||||
return MeasureUnit::create(3, 10, status);
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::getPortionPer1E9() {
|
||||
return MeasureUnit(3, 10);
|
||||
}
|
||||
|
||||
MeasureUnit *MeasureUnit::createLiterPer100Kilometers(UErrorCode &status) {
|
||||
return MeasureUnit::create(4, 0, status);
|
||||
}
|
||||
@ -2400,6 +2409,7 @@ MeasureUnitImpl MeasureUnitImpl::copy(UErrorCode &status) const {
|
||||
MeasureUnitImpl result;
|
||||
result.complexity = complexity;
|
||||
result.identifier.append(identifier, status);
|
||||
result.constantDenominator = constantDenominator;
|
||||
for (int32_t i = 0; i < singleUnits.length(); i++) {
|
||||
SingleUnitImpl *item = result.singleUnits.emplaceBack(*singleUnits[i]);
|
||||
if (!item) {
|
||||
|
446
deps/icu-small/source/i18n/measunit_extra.cpp
vendored
446
deps/icu-small/source/i18n/measunit_extra.cpp
vendored
@ -15,6 +15,7 @@
|
||||
#include "charstr.h"
|
||||
#include "cmemory.h"
|
||||
#include "cstring.h"
|
||||
#include "double-conversion-string-to-double.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "resource.h"
|
||||
#include "uarrsort.h"
|
||||
@ -30,13 +31,15 @@
|
||||
#include "unicode/ustringtrie.h"
|
||||
#include "uresimp.h"
|
||||
#include "util.h"
|
||||
#include <limits.h>
|
||||
#include <cstdlib>
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using icu::double_conversion::StringToDoubleConverter;
|
||||
|
||||
// TODO: Propose a new error code for this?
|
||||
constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
@ -467,37 +470,55 @@ void U_CALLCONV initUnitExtras(UErrorCode& status) {
|
||||
|
||||
class Token {
|
||||
public:
|
||||
Token(int32_t match) : fMatch(match) {}
|
||||
Token(int64_t match) : fMatch(match) {
|
||||
if (fMatch < kCompoundPartOffset) {
|
||||
this->fType = TYPE_PREFIX;
|
||||
} else if (fMatch < kInitialCompoundPartOffset) {
|
||||
this->fType = TYPE_COMPOUND_PART;
|
||||
} else if (fMatch < kPowerPartOffset) {
|
||||
this->fType = TYPE_INITIAL_COMPOUND_PART;
|
||||
} else if (fMatch < kSimpleUnitOffset) {
|
||||
this->fType = TYPE_POWER_PART;
|
||||
} else {
|
||||
this->fType = TYPE_SIMPLE_UNIT;
|
||||
}
|
||||
}
|
||||
|
||||
enum Type {
|
||||
TYPE_UNDEFINED,
|
||||
TYPE_PREFIX,
|
||||
// Token type for "-per-", "-", and "-and-".
|
||||
TYPE_COMPOUND_PART,
|
||||
// Token type for "per-".
|
||||
TYPE_INITIAL_COMPOUND_PART,
|
||||
TYPE_POWER_PART,
|
||||
TYPE_SIMPLE_UNIT,
|
||||
};
|
||||
static Token constantToken(StringPiece str, UErrorCode &status) {
|
||||
Token result;
|
||||
auto value = Token::parseStringToLong(str, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
}
|
||||
result.fMatch = value;
|
||||
result.fType = TYPE_CONSTANT_DENOMINATOR;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Calling getType() is invalid, resulting in an assertion failure, if Token
|
||||
// value isn't positive.
|
||||
Type getType() const {
|
||||
U_ASSERT(fMatch > 0);
|
||||
if (fMatch < kCompoundPartOffset) {
|
||||
return TYPE_PREFIX;
|
||||
}
|
||||
if (fMatch < kInitialCompoundPartOffset) {
|
||||
return TYPE_COMPOUND_PART;
|
||||
}
|
||||
if (fMatch < kPowerPartOffset) {
|
||||
return TYPE_INITIAL_COMPOUND_PART;
|
||||
}
|
||||
if (fMatch < kSimpleUnitOffset) {
|
||||
return TYPE_POWER_PART;
|
||||
}
|
||||
return TYPE_SIMPLE_UNIT;
|
||||
}
|
||||
enum Type {
|
||||
TYPE_UNDEFINED,
|
||||
TYPE_PREFIX,
|
||||
// Token type for "-per-", "-", and "-and-".
|
||||
TYPE_COMPOUND_PART,
|
||||
// Token type for "per-".
|
||||
TYPE_INITIAL_COMPOUND_PART,
|
||||
TYPE_POWER_PART,
|
||||
TYPE_SIMPLE_UNIT,
|
||||
TYPE_CONSTANT_DENOMINATOR,
|
||||
};
|
||||
|
||||
// Calling getType() is invalid, resulting in an assertion failure, if Token
|
||||
// value isn't positive.
|
||||
Type getType() const {
|
||||
U_ASSERT(fMatch >= 0);
|
||||
return this->fType;
|
||||
}
|
||||
|
||||
// Retrieve the value of the constant denominator if the token is of type TYPE_CONSTANT_DENOMINATOR.
|
||||
uint64_t getConstantDenominator() const {
|
||||
U_ASSERT(getType() == TYPE_CONSTANT_DENOMINATOR);
|
||||
return static_cast<uint64_t>(fMatch);
|
||||
}
|
||||
|
||||
UMeasurePrefix getUnitPrefix() const {
|
||||
U_ASSERT(getType() == TYPE_PREFIX);
|
||||
@ -530,8 +551,41 @@ public:
|
||||
return fMatch - kSimpleUnitOffset;
|
||||
}
|
||||
|
||||
// TODO: Consider moving this to a separate utility class.
|
||||
// Utility function to parse a string into an unsigned long value.
|
||||
// The value must be a positive integer within the range [1, INT64_MAX].
|
||||
// The input can be in integer or scientific notation.
|
||||
static uint64_t parseStringToLong(const StringPiece strNum, UErrorCode &status) {
|
||||
// We are processing well-formed input, so we don't need any special options to
|
||||
// StringToDoubleConverter.
|
||||
StringToDoubleConverter converter(0, 0, 0, "", "");
|
||||
int32_t count;
|
||||
double double_result = converter.StringToDouble(strNum.data(), strNum.length(), &count);
|
||||
if (count != strNum.length()) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (U_FAILURE(status) || double_result < 1.0 || double_result > static_cast<double>(INT64_MAX)) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if the value is integer.
|
||||
uint64_t int_result = static_cast<uint64_t>(double_result);
|
||||
const double kTolerance = 1e-9;
|
||||
if (abs(double_result - int_result) > kTolerance) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return int_result;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t fMatch;
|
||||
Token() = default;
|
||||
int64_t fMatch;
|
||||
Type fType = TYPE_UNDEFINED;
|
||||
};
|
||||
|
||||
class Parser {
|
||||
@ -555,6 +609,50 @@ public:
|
||||
return {source};
|
||||
}
|
||||
|
||||
/**
|
||||
* A single unit or a constant denominator.
|
||||
*/
|
||||
struct SingleUnitOrConstant {
|
||||
enum ValueType {
|
||||
kSingleUnit,
|
||||
kConstantDenominator,
|
||||
};
|
||||
|
||||
ValueType type = kSingleUnit;
|
||||
SingleUnitImpl singleUnit;
|
||||
uint64_t constantDenominator;
|
||||
|
||||
static SingleUnitOrConstant singleUnitValue(SingleUnitImpl singleUnit) {
|
||||
SingleUnitOrConstant result;
|
||||
result.type = kSingleUnit;
|
||||
result.singleUnit = singleUnit;
|
||||
result.constantDenominator = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static SingleUnitOrConstant constantDenominatorValue(uint64_t constant) {
|
||||
SingleUnitOrConstant result;
|
||||
result.type = kConstantDenominator;
|
||||
result.singleUnit = {};
|
||||
result.constantDenominator = constant;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t getConstantDenominator() const {
|
||||
U_ASSERT(type == kConstantDenominator);
|
||||
return constantDenominator;
|
||||
}
|
||||
|
||||
SingleUnitImpl getSingleUnit() const {
|
||||
U_ASSERT(type == kSingleUnit);
|
||||
return singleUnit;
|
||||
}
|
||||
|
||||
bool isSingleUnit() const { return type == kSingleUnit; }
|
||||
|
||||
bool isConstantDenominator() const { return type == kConstantDenominator; }
|
||||
};
|
||||
|
||||
MeasureUnitImpl parse(UErrorCode& status) {
|
||||
MeasureUnitImpl result;
|
||||
|
||||
@ -569,12 +667,19 @@ public:
|
||||
while (hasNext()) {
|
||||
bool sawAnd = false;
|
||||
|
||||
SingleUnitImpl singleUnit = nextSingleUnit(sawAnd, status);
|
||||
auto singleUnitOrConstant = nextSingleUnitOrConstant(sawAnd, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool added = result.appendSingleUnit(singleUnit, status);
|
||||
if (singleUnitOrConstant.isConstantDenominator()) {
|
||||
result.constantDenominator = singleUnitOrConstant.getConstantDenominator();
|
||||
result.complexity = UMEASURE_UNIT_COMPOUND;
|
||||
continue;
|
||||
}
|
||||
|
||||
U_ASSERT(singleUnitOrConstant.isSingleUnit());
|
||||
bool added = result.appendSingleUnit(singleUnitOrConstant.getSingleUnit(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
}
|
||||
@ -604,6 +709,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (result.singleUnits.length() == 0) {
|
||||
// The identifier was empty or only had a constant denominator.
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result; // add it for code consistency.
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -622,6 +733,10 @@ private:
|
||||
// identifier is invalid pending TODO(CLDR-13701).
|
||||
bool fAfterPer = false;
|
||||
|
||||
// Set to true when we've just seen a "per-". This is used to determine if
|
||||
// the next token can be a constant denominator token.
|
||||
bool fJustSawPer = false;
|
||||
|
||||
Parser() : fSource(""), fTrie(u"") {}
|
||||
|
||||
Parser(StringPiece source)
|
||||
@ -640,6 +755,10 @@ private:
|
||||
// Saves the position in the fSource string for the end of the most
|
||||
// recent matching token.
|
||||
int32_t previ = -1;
|
||||
|
||||
// Saves the position in the fSource string for later use in case of unit constant found.
|
||||
int32_t currentFIndex = fIndex;
|
||||
|
||||
// Find the longest token that matches a value in the trie:
|
||||
while (fIndex < fSource.length()) {
|
||||
auto result = fTrie.next(fSource.data()[fIndex++]);
|
||||
@ -658,12 +777,25 @@ private:
|
||||
// continue;
|
||||
}
|
||||
|
||||
if (match < 0) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
} else {
|
||||
if (match >= 0) {
|
||||
fIndex = previ;
|
||||
return {match};
|
||||
}
|
||||
return {match};
|
||||
|
||||
// If no match was found, we check if the token is a constant denominator.
|
||||
// 1. We find the index of the start of the next token or the end of the string.
|
||||
int32_t endOfConstantIndex = fSource.find("-", currentFIndex);
|
||||
endOfConstantIndex = (endOfConstantIndex == -1) ? fSource.length() : endOfConstantIndex;
|
||||
if (endOfConstantIndex <= currentFIndex) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return {match};
|
||||
}
|
||||
|
||||
// 2. We extract the substring from the start of the constant to the end of the constant.
|
||||
StringPiece constantDenominatorStr =
|
||||
fSource.substr(currentFIndex, endOfConstantIndex - currentFIndex);
|
||||
fIndex = endOfConstantIndex;
|
||||
return Token::constantToken(constantDenominatorStr, status);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -680,10 +812,10 @@ private:
|
||||
* unit", sawAnd is set to true. If not, it is left as is.
|
||||
* @param status ICU error code.
|
||||
*/
|
||||
SingleUnitImpl nextSingleUnit(bool &sawAnd, UErrorCode &status) {
|
||||
SingleUnitImpl result;
|
||||
SingleUnitOrConstant nextSingleUnitOrConstant(bool &sawAnd, UErrorCode &status) {
|
||||
SingleUnitImpl singleUnitResult;
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
// state:
|
||||
@ -695,19 +827,22 @@ private:
|
||||
bool atStart = fIndex == 0;
|
||||
Token token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
fJustSawPer = false;
|
||||
|
||||
if (atStart) {
|
||||
// Identifiers optionally start with "per-".
|
||||
if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
|
||||
U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER);
|
||||
fAfterPer = true;
|
||||
result.dimensionality = -1;
|
||||
fJustSawPer = true;
|
||||
singleUnitResult.dimensionality = -1;
|
||||
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -715,7 +850,7 @@ private:
|
||||
// via a compound part:
|
||||
if (token.getType() != Token::TYPE_COMPOUND_PART) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (token.getMatch()) {
|
||||
@ -724,15 +859,16 @@ private:
|
||||
// Mixed compound units not yet supported,
|
||||
// TODO(CLDR-13701).
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
fAfterPer = true;
|
||||
result.dimensionality = -1;
|
||||
fJustSawPer = true;
|
||||
singleUnitResult.dimensionality = -1;
|
||||
break;
|
||||
|
||||
case COMPOUND_PART_TIMES:
|
||||
if (fAfterPer) {
|
||||
result.dimensionality = -1;
|
||||
singleUnitResult.dimensionality = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -741,7 +877,7 @@ private:
|
||||
// Can't start with "-and-", and mixed compound units
|
||||
// not yet supported, TODO(CLDR-13701).
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
sawAnd = true;
|
||||
break;
|
||||
@ -749,52 +885,65 @@ private:
|
||||
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (token.getType() == Token::TYPE_CONSTANT_DENOMINATOR) {
|
||||
if (!fJustSawPer) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return {};
|
||||
}
|
||||
|
||||
return SingleUnitOrConstant::constantDenominatorValue(token.getConstantDenominator());
|
||||
}
|
||||
|
||||
// Read tokens until we have a complete SingleUnit or we reach the end.
|
||||
while (true) {
|
||||
switch (token.getType()) {
|
||||
case Token::TYPE_POWER_PART:
|
||||
if (state > 0) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
}
|
||||
result.dimensionality *= token.getPower();
|
||||
state = 1;
|
||||
break;
|
||||
|
||||
case Token::TYPE_PREFIX:
|
||||
if (state > 1) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
}
|
||||
result.unitPrefix = token.getUnitPrefix();
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case Token::TYPE_SIMPLE_UNIT:
|
||||
result.index = token.getSimpleUnitIndex();
|
||||
return result;
|
||||
|
||||
default:
|
||||
case Token::TYPE_POWER_PART:
|
||||
if (state > 0) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
singleUnitResult.dimensionality *= token.getPower();
|
||||
state = 1;
|
||||
break;
|
||||
|
||||
case Token::TYPE_PREFIX:
|
||||
if (state > 1) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return {};
|
||||
}
|
||||
singleUnitResult.unitPrefix = token.getUnitPrefix();
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case Token::TYPE_SIMPLE_UNIT:
|
||||
singleUnitResult.index = token.getSimpleUnitIndex();
|
||||
break;
|
||||
|
||||
default:
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (token.getType() == Token::TYPE_SIMPLE_UNIT) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasNext()) {
|
||||
// We ran out of tokens before finding a complete single unit.
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return SingleUnitOrConstant::singleUnitValue(singleUnitResult);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1120,6 +1269,51 @@ MeasureUnitImpl::extractIndividualUnitsWithIndices(UErrorCode &status) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t countCharacter(const CharString &str, char c) {
|
||||
int32_t count = 0;
|
||||
for (int32_t i = 0, n = str.length(); i < n; i++) {
|
||||
if (str[i] == c) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function that returns a string of the constants in the correct
|
||||
* format.
|
||||
*
|
||||
* Example:
|
||||
* 1000 --> "-per-1000"
|
||||
* 1000000 --> "-per-1e6"
|
||||
*
|
||||
* NOTE: this function is only used when the constant denominator is greater
|
||||
* than 0.
|
||||
*/
|
||||
CharString getConstantsString(uint64_t constantDenominator, UErrorCode &status) {
|
||||
U_ASSERT(constantDenominator > 0 && constantDenominator <= LLONG_MAX);
|
||||
|
||||
CharString result;
|
||||
result.appendNumber(constantDenominator, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (constantDenominator <= 1000) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if the constant is a power of 10.
|
||||
int32_t zeros = countCharacter(result, '0');
|
||||
if (zeros == result.length() - 1 && result[0] == '1') {
|
||||
result.clear();
|
||||
result.append(StringPiece("1e"), status);
|
||||
result.appendNumber(zeros, status);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a MeasureUnitImpl and generate the identifier string in place.
|
||||
*/
|
||||
@ -1128,7 +1322,7 @@ void MeasureUnitImpl::serialize(UErrorCode &status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->singleUnits.length() == 0) {
|
||||
if (this->singleUnits.length() == 0 && this->constantDenominator == 0) {
|
||||
// Dimensionless, constructed by the default constructor.
|
||||
return;
|
||||
}
|
||||
@ -1145,6 +1339,7 @@ void MeasureUnitImpl::serialize(UErrorCode &status) {
|
||||
CharString result;
|
||||
bool beforePer = true;
|
||||
bool firstTimeNegativeDimension = false;
|
||||
bool constantDenominatorAppended = false;
|
||||
for (int32_t i = 0; i < this->singleUnits.length(); i++) {
|
||||
if (beforePer && (*this->singleUnits[i]).dimensionality < 0) {
|
||||
beforePer = false;
|
||||
@ -1168,43 +1363,103 @@ void MeasureUnitImpl::serialize(UErrorCode &status) {
|
||||
} else {
|
||||
result.append(StringPiece("-per-"), status);
|
||||
}
|
||||
} else {
|
||||
if (result.length() != 0) {
|
||||
|
||||
if (this->constantDenominator > 0) {
|
||||
result.append(getConstantsString(this->constantDenominator, status), status);
|
||||
result.append(StringPiece("-"), status);
|
||||
constantDenominatorAppended = true;
|
||||
}
|
||||
|
||||
} else if (result.length() != 0) {
|
||||
result.append(StringPiece("-"), status);
|
||||
}
|
||||
}
|
||||
|
||||
this->singleUnits[i]->appendNeutralIdentifier(result, status);
|
||||
}
|
||||
|
||||
if (!constantDenominatorAppended && this->constantDenominator > 0) {
|
||||
result.append(StringPiece("-per-"), status);
|
||||
result.append(getConstantsString(this->constantDenominator, status), status);
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
this->identifier = CharString(result, status);
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnitImpl::build(UErrorCode& status) && {
|
||||
MeasureUnit MeasureUnitImpl::build(UErrorCode &status) && {
|
||||
this->serialize(status);
|
||||
return MeasureUnit(std::move(*this));
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode& status) {
|
||||
MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode &status) {
|
||||
return Parser::from(identifier, status).parse(status).build(status);
|
||||
}
|
||||
|
||||
UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode& status) const {
|
||||
UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode &status) const {
|
||||
MeasureUnitImpl temp;
|
||||
return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity;
|
||||
}
|
||||
|
||||
UMeasurePrefix MeasureUnit::getPrefix(UErrorCode& status) const {
|
||||
UMeasurePrefix MeasureUnit::getPrefix(UErrorCode &status) const {
|
||||
return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix;
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix, UErrorCode& status) const UPRV_NO_SANITIZE_UNDEFINED {
|
||||
MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix,
|
||||
UErrorCode &status) const UPRV_NO_SANITIZE_UNDEFINED {
|
||||
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
|
||||
singleUnit.unitPrefix = prefix;
|
||||
return singleUnit.build(status);
|
||||
}
|
||||
|
||||
uint64_t MeasureUnit::getConstantDenominator(UErrorCode &status) const {
|
||||
auto complexity = this->getComplexity(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (complexity != UMEASURE_UNIT_SINGLE && complexity != UMEASURE_UNIT_COMPOUND) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->fImpl == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->fImpl->constantDenominator;
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::withConstantDenominator(uint64_t denominator, UErrorCode &status) const {
|
||||
// To match the behavior of the Java API, we do not allow a constant denominator
|
||||
// bigger than LONG_MAX.
|
||||
if (denominator > LONG_MAX) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto complexity = this->getComplexity(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
if (complexity != UMEASURE_UNIT_SINGLE && complexity != UMEASURE_UNIT_COMPOUND) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
impl.constantDenominator = denominator;
|
||||
impl.complexity = (impl.singleUnits.length() < 2 && denominator == 0) ? UMEASURE_UNIT_SINGLE
|
||||
: UMEASURE_UNIT_COMPOUND;
|
||||
return std::move(impl).build(status);
|
||||
}
|
||||
|
||||
int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
|
||||
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
|
||||
if (U_FAILURE(status)) { return 0; }
|
||||
@ -1222,6 +1477,11 @@ MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode&
|
||||
|
||||
MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const {
|
||||
MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
|
||||
// The reciprocal of a unit that has a constant denominator is not allowed.
|
||||
if (impl.constantDenominator != 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
impl.takeReciprocal(status);
|
||||
return std::move(impl).build(status);
|
||||
}
|
||||
@ -1237,9 +1497,25 @@ MeasureUnit MeasureUnit::product(const MeasureUnit& other, UErrorCode& status) c
|
||||
for (int32_t i = 0; i < otherImpl.singleUnits.length(); i++) {
|
||||
impl.appendSingleUnit(*otherImpl.singleUnits[i], status);
|
||||
}
|
||||
if (impl.singleUnits.length() > 1) {
|
||||
|
||||
uint64_t currentConstatDenominator = this->getConstantDenominator(status);
|
||||
uint64_t otherConstantDenominator = other.getConstantDenominator(status);
|
||||
|
||||
// TODO: we can also multiply the constant denominators instead of returning an error.
|
||||
if (currentConstatDenominator != 0 && otherConstantDenominator != 0) {
|
||||
// There is only `one` constant denominator in a compound unit.
|
||||
// Therefore, we Cannot multiply units that both of them have a constant denominator
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Because either one of the constant denominators is zero, we can use the maximum of them.
|
||||
impl.constantDenominator = uprv_max(currentConstatDenominator, otherConstantDenominator);
|
||||
|
||||
if (impl.singleUnits.length() > 1 || impl.constantDenominator > 0) {
|
||||
impl.complexity = UMEASURE_UNIT_COMPOUND;
|
||||
}
|
||||
|
||||
return std::move(impl).build(status);
|
||||
}
|
||||
|
||||
|
8
deps/icu-small/source/i18n/measunit_impl.h
vendored
8
deps/icu-small/source/i18n/measunit_impl.h
vendored
@ -328,6 +328,14 @@ class U_I18N_API MeasureUnitImpl : public UMemory {
|
||||
*/
|
||||
CharString identifier;
|
||||
|
||||
/**
|
||||
* Represents the unit constant denominator.
|
||||
*
|
||||
* NOTE:
|
||||
* if set to 0, it means that the constant is not set.
|
||||
*/
|
||||
uint64_t constantDenominator = 0;
|
||||
|
||||
// For calling serialize
|
||||
// TODO(icu-units#147): revisit serialization
|
||||
friend class number::impl::LongNameHandler;
|
||||
|
434
deps/icu-small/source/i18n/messageformat2.cpp
vendored
434
deps/icu-small/source/i18n/messageformat2.cpp
vendored
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -11,8 +13,10 @@
|
||||
#include "unicode/messageformat2_data_model.h"
|
||||
#include "unicode/messageformat2_formattable.h"
|
||||
#include "unicode/messageformat2.h"
|
||||
#include "unicode/normalizer2.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "messageformat2_allocation.h"
|
||||
#include "messageformat2_checker.h"
|
||||
#include "messageformat2_evaluation.h"
|
||||
#include "messageformat2_macros.h"
|
||||
|
||||
@ -37,7 +41,7 @@ static Formattable evalLiteral(const Literal& lit) {
|
||||
// The fallback for a variable name is itself.
|
||||
UnicodeString str(DOLLAR);
|
||||
str += var;
|
||||
const Formattable* val = context.getGlobal(var, errorCode);
|
||||
const Formattable* val = context.getGlobal(*this, var, errorCode);
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
return (FormattedPlaceholder(*val, str));
|
||||
}
|
||||
@ -51,16 +55,16 @@ static Formattable evalLiteral(const Literal& lit) {
|
||||
return FormattedPlaceholder(evalLiteral(lit), lit.quoted());
|
||||
}
|
||||
|
||||
[[nodiscard]] FormattedPlaceholder MessageFormatter::formatOperand(const Environment& env,
|
||||
const Operand& rand,
|
||||
MessageContext& context,
|
||||
UErrorCode &status) const {
|
||||
[[nodiscard]] InternalValue* MessageFormatter::formatOperand(const Environment& env,
|
||||
const Operand& rand,
|
||||
MessageContext& context,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (rand.isNull()) {
|
||||
return FormattedPlaceholder();
|
||||
return create<InternalValue>(InternalValue(FormattedPlaceholder()), status);
|
||||
}
|
||||
if (rand.isVariable()) {
|
||||
// Check if it's local or global
|
||||
@ -71,15 +75,19 @@ static Formattable evalLiteral(const Literal& lit) {
|
||||
// Eager vs. lazy evaluation is an open issue:
|
||||
// see https://github.com/unicode-org/message-format-wg/issues/299
|
||||
|
||||
// NFC-normalize the variable name. See
|
||||
// https://github.com/unicode-org/message-format-wg/blob/main/spec/syntax.md#names-and-identifiers
|
||||
const VariableName normalized = normalizeNFC(var);
|
||||
|
||||
// Look up the variable in the environment
|
||||
if (env.has(var)) {
|
||||
if (env.has(normalized)) {
|
||||
// `var` is a local -- look it up
|
||||
const Closure& rhs = env.lookup(var);
|
||||
const Closure& rhs = env.lookup(normalized);
|
||||
// Format the expression using the environment from the closure
|
||||
return formatExpression(rhs.getEnv(), rhs.getExpr(), context, status);
|
||||
}
|
||||
// Variable wasn't found in locals -- check if it's global
|
||||
FormattedPlaceholder result = evalArgument(var, context, status);
|
||||
FormattedPlaceholder result = evalArgument(normalized, context, status);
|
||||
if (status == U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
// Unbound variable -- set a resolution error
|
||||
@ -88,12 +96,12 @@ static Formattable evalLiteral(const Literal& lit) {
|
||||
// https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#fallback-resolution
|
||||
UnicodeString str(DOLLAR);
|
||||
str += var;
|
||||
return FormattedPlaceholder(str);
|
||||
return create<InternalValue>(InternalValue(FormattedPlaceholder(str)), status);
|
||||
}
|
||||
return result;
|
||||
return create<InternalValue>(InternalValue(std::move(result)), status);
|
||||
} else {
|
||||
U_ASSERT(rand.isLiteral());
|
||||
return formatLiteral(rand.asLiteral());
|
||||
return create<InternalValue>(InternalValue(formatLiteral(rand.asLiteral())), status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,28 +122,32 @@ FunctionOptions MessageFormatter::resolveOptions(const Environment& env, const O
|
||||
|
||||
// Options are fully evaluated before calling the function
|
||||
// Format the operand
|
||||
FormattedPlaceholder rhsVal = formatOperand(env, v, context, status);
|
||||
LocalPointer<InternalValue> rhsVal(formatOperand(env, v, context, status));
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
if (!rhsVal.isFallback()) {
|
||||
resolvedOpt.adoptInstead(create<ResolvedFunctionOption>(ResolvedFunctionOption(k, rhsVal.asFormattable()), status));
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
optionsVector->adoptElement(resolvedOpt.orphan(), status);
|
||||
// Note: this means option values are "eagerly" evaluated.
|
||||
// Currently, options don't have options. This will be addressed by the
|
||||
// full FormattedPlaceholder redesign.
|
||||
FormattedPlaceholder optValue = rhsVal->forceFormatting(context.getErrors(), status);
|
||||
resolvedOpt.adoptInstead(create<ResolvedFunctionOption>
|
||||
(ResolvedFunctionOption(k,
|
||||
optValue.asFormattable()),
|
||||
status));
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
optionsVector->adoptElement(resolvedOpt.orphan(), status);
|
||||
}
|
||||
|
||||
return FunctionOptions(std::move(*optionsVector), status);
|
||||
}
|
||||
|
||||
// Overload that dispatches on argument type. Syntax doesn't provide for options in this case.
|
||||
[[nodiscard]] FormattedPlaceholder MessageFormatter::evalFormatterCall(FormattedPlaceholder&& argument,
|
||||
MessageContext& context,
|
||||
UErrorCode& status) const {
|
||||
[[nodiscard]] InternalValue* MessageFormatter::evalFunctionCall(FormattedPlaceholder&& argument,
|
||||
MessageContext& context,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// These cases should have been checked for already
|
||||
@ -153,11 +165,11 @@ FunctionOptions MessageFormatter::resolveOptions(const Environment& env, const O
|
||||
// No formatter for this type -- follow default behavior
|
||||
break;
|
||||
}
|
||||
return evalFormatterCall(functionName,
|
||||
std::move(argument),
|
||||
FunctionOptions(),
|
||||
context,
|
||||
status);
|
||||
return evalFunctionCall(functionName,
|
||||
create<InternalValue>(std::move(argument), status),
|
||||
FunctionOptions(),
|
||||
context,
|
||||
status);
|
||||
}
|
||||
default: {
|
||||
// TODO: The array case isn't handled yet; not sure whether it's desirable
|
||||
@ -167,104 +179,76 @@ FunctionOptions MessageFormatter::resolveOptions(const Environment& env, const O
|
||||
}
|
||||
// No formatter for this type, or it's a primitive type (which will be formatted later)
|
||||
// -- just return the argument itself
|
||||
return std::move(argument);
|
||||
return create<InternalValue>(std::move(argument), status);
|
||||
}
|
||||
|
||||
// Overload that dispatches on function name
|
||||
[[nodiscard]] FormattedPlaceholder MessageFormatter::evalFormatterCall(const FunctionName& functionName,
|
||||
FormattedPlaceholder&& argument,
|
||||
FunctionOptions&& options,
|
||||
MessageContext& context,
|
||||
UErrorCode& status) const {
|
||||
// Adopts `arg`
|
||||
[[nodiscard]] InternalValue* MessageFormatter::evalFunctionCall(const FunctionName& functionName,
|
||||
InternalValue* arg_,
|
||||
FunctionOptions&& options,
|
||||
MessageContext& context,
|
||||
UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
DynamicErrors& errs = context.getErrors();
|
||||
|
||||
UnicodeString fallback(COLON);
|
||||
fallback += functionName;
|
||||
if (!argument.isNullOperand()) {
|
||||
fallback = argument.fallback;
|
||||
}
|
||||
LocalPointer<InternalValue> arg(arg_);
|
||||
|
||||
// Look up the formatter or selector
|
||||
LocalPointer<Formatter> formatterImpl(nullptr);
|
||||
LocalPointer<Selector> selectorImpl(nullptr);
|
||||
if (isFormatter(functionName)) {
|
||||
LocalPointer<Formatter> formatterImpl(getFormatter(functionName, status));
|
||||
if (U_FAILURE(status)) {
|
||||
if (status == U_MF_FORMATTING_ERROR) {
|
||||
errs.setFormattingError(functionName, status);
|
||||
status = U_ZERO_ERROR;
|
||||
return {};
|
||||
}
|
||||
if (status == U_MF_UNKNOWN_FUNCTION_ERROR) {
|
||||
errs.setUnknownFunction(functionName, status);
|
||||
status = U_ZERO_ERROR;
|
||||
return {};
|
||||
}
|
||||
// Other errors are non-recoverable
|
||||
return {};
|
||||
}
|
||||
U_ASSERT(formatterImpl != nullptr);
|
||||
|
||||
UErrorCode savedStatus = status;
|
||||
FormattedPlaceholder result = formatterImpl->format(std::move(argument), std::move(options), status);
|
||||
// Update errors
|
||||
if (savedStatus != status) {
|
||||
if (U_FAILURE(status)) {
|
||||
if (status == U_MF_OPERAND_MISMATCH_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
errs.setOperandMismatchError(functionName, status);
|
||||
} else {
|
||||
status = U_ZERO_ERROR;
|
||||
// Convey any error generated by the formatter
|
||||
// as a formatting error, except for operand mismatch errors
|
||||
errs.setFormattingError(functionName, status);
|
||||
}
|
||||
return FormattedPlaceholder(fallback);
|
||||
} else {
|
||||
// Ignore warnings
|
||||
status = savedStatus;
|
||||
}
|
||||
}
|
||||
// Ignore the output if any errors occurred
|
||||
if (errs.hasFormattingError()) {
|
||||
return FormattedPlaceholder(fallback);
|
||||
}
|
||||
return result;
|
||||
formatterImpl.adoptInstead(getFormatter(functionName, status));
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
// No formatter with this name -- set error
|
||||
if (isSelector(functionName)) {
|
||||
errs.setFormattingError(functionName, status);
|
||||
} else {
|
||||
errs.setUnknownFunction(functionName, status);
|
||||
selectorImpl.adoptInstead(getSelector(context, functionName, status));
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
return FormattedPlaceholder(fallback);
|
||||
if (formatterImpl == nullptr && selectorImpl == nullptr) {
|
||||
// Unknown function error
|
||||
context.getErrors().setUnknownFunction(functionName, status);
|
||||
|
||||
if (arg->hasNullOperand()) {
|
||||
// Non-selector used as selector; an error would have been recorded earlier
|
||||
UnicodeString fallback(COLON);
|
||||
fallback += functionName;
|
||||
return new InternalValue(FormattedPlaceholder(fallback));
|
||||
} else {
|
||||
return new InternalValue(FormattedPlaceholder(arg->getFallback()));
|
||||
}
|
||||
}
|
||||
return new InternalValue(arg.orphan(),
|
||||
std::move(options),
|
||||
functionName,
|
||||
formatterImpl.isValid() ? formatterImpl.orphan() : nullptr,
|
||||
selectorImpl.isValid() ? selectorImpl.orphan() : nullptr);
|
||||
}
|
||||
|
||||
// Formats an expression using `globalEnv` for the values of variables
|
||||
[[nodiscard]] FormattedPlaceholder MessageFormatter::formatExpression(const Environment& globalEnv,
|
||||
const Expression& expr,
|
||||
MessageContext& context,
|
||||
UErrorCode &status) const {
|
||||
[[nodiscard]] InternalValue* MessageFormatter::formatExpression(const Environment& globalEnv,
|
||||
const Expression& expr,
|
||||
MessageContext& context,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const Operand& rand = expr.getOperand();
|
||||
// Format the operand (formatOperand handles the case of a null operand)
|
||||
FormattedPlaceholder randVal = formatOperand(globalEnv, rand, context, status);
|
||||
LocalPointer<InternalValue> randVal(formatOperand(globalEnv, rand, context, status));
|
||||
|
||||
// Don't call the function on error values
|
||||
if (randVal.isFallback()) {
|
||||
return randVal;
|
||||
}
|
||||
FormattedPlaceholder maybeRand = randVal->takeArgument(status);
|
||||
|
||||
if (!expr.isFunctionCall()) {
|
||||
if (!expr.isFunctionCall() && U_SUCCESS(status)) {
|
||||
// Dispatch based on type of `randVal`
|
||||
return evalFormatterCall(std::move(randVal),
|
||||
context,
|
||||
status);
|
||||
} else {
|
||||
if (maybeRand.isFallback()) {
|
||||
return randVal.orphan();
|
||||
}
|
||||
return evalFunctionCall(std::move(maybeRand), context, status);
|
||||
} else if (expr.isFunctionCall()) {
|
||||
status = U_ZERO_ERROR;
|
||||
const Operator* rator = expr.getOperator(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
const FunctionName& functionName = rator->getFunctionName();
|
||||
@ -273,19 +257,14 @@ FunctionOptions MessageFormatter::resolveOptions(const Environment& env, const O
|
||||
FunctionOptions resolvedOptions = resolveOptions(globalEnv, options, context, status);
|
||||
|
||||
// Call the formatter function
|
||||
// The fallback for a nullary function call is the function name
|
||||
UnicodeString fallback;
|
||||
if (rand.isNull()) {
|
||||
fallback = UnicodeString(COLON);
|
||||
fallback += functionName;
|
||||
} else {
|
||||
fallback = randVal.fallback;
|
||||
}
|
||||
return evalFormatterCall(functionName,
|
||||
std::move(randVal),
|
||||
std::move(resolvedOptions),
|
||||
context,
|
||||
status);
|
||||
return evalFunctionCall(functionName,
|
||||
randVal.orphan(),
|
||||
std::move(resolvedOptions),
|
||||
context,
|
||||
status);
|
||||
} else {
|
||||
status = U_ZERO_ERROR;
|
||||
return randVal.orphan();
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,11 +280,13 @@ void MessageFormatter::formatPattern(MessageContext& context, const Environment&
|
||||
// Markup is ignored
|
||||
} else {
|
||||
// Format the expression
|
||||
FormattedPlaceholder partVal = formatExpression(globalEnv, part.contents(), context, status);
|
||||
// Force full evaluation, e.g. applying default formatters to
|
||||
LocalPointer<InternalValue> partVal(
|
||||
formatExpression(globalEnv, part.contents(), context, status));
|
||||
FormattedPlaceholder partResult = partVal->forceFormatting(context.getErrors(),
|
||||
status);
|
||||
// Force full evaluation, e.g. applying default formatters to
|
||||
// unformatted input (or formatting numbers as strings)
|
||||
UnicodeString partResult = partVal.formatToString(locale, status);
|
||||
result += partResult;
|
||||
result += partResult.formatToString(locale, status);
|
||||
// Handle formatting errors. `formatToString()` can't take a context and thus can't
|
||||
// register an error directly
|
||||
if (status == U_MF_FORMATTING_ERROR) {
|
||||
@ -328,14 +309,14 @@ void MessageFormatter::resolveSelectors(MessageContext& context, const Environme
|
||||
CHECK_ERROR(status);
|
||||
U_ASSERT(!dataModel.hasPattern());
|
||||
|
||||
const Expression* selectors = dataModel.getSelectorsInternal();
|
||||
const VariableName* selectors = dataModel.getSelectorsInternal();
|
||||
// 1. Let res be a new empty list of resolved values that support selection.
|
||||
// (Implicit, since `res` is an out-parameter)
|
||||
// 2. For each expression exp of the message's selectors
|
||||
for (int32_t i = 0; i < dataModel.numSelectors(); i++) {
|
||||
// 2i. Let rv be the resolved value of exp.
|
||||
ResolvedSelector rv = formatSelectorExpression(env, selectors[i], context, status);
|
||||
if (rv.hasSelector()) {
|
||||
LocalPointer<InternalValue> rv(formatOperand(env, Operand(selectors[i]), context, status));
|
||||
if (rv->canSelect()) {
|
||||
// 2ii. If selection is supported for rv:
|
||||
// (True if this code has been reached)
|
||||
} else {
|
||||
@ -344,17 +325,17 @@ void MessageFormatter::resolveSelectors(MessageContext& context, const Environme
|
||||
// Append nomatch as the last element of the list res.
|
||||
// Emit a Selection Error.
|
||||
// (Note: in this case, rv, being a fallback, serves as `nomatch`)
|
||||
#if U_DEBUG
|
||||
const DynamicErrors& err = context.getErrors();
|
||||
U_ASSERT(err.hasError());
|
||||
U_ASSERT(rv.argument().isFallback());
|
||||
#endif
|
||||
DynamicErrors& err = context.getErrors();
|
||||
err.setSelectorError(rv->getFunctionName(), status);
|
||||
rv.adoptInstead(new InternalValue(FormattedPlaceholder(rv->getFallback())));
|
||||
if (!rv.isValid()) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 2ii(a). Append rv as the last element of the list res.
|
||||
// (Also fulfills 2iii)
|
||||
LocalPointer<ResolvedSelector> v(create<ResolvedSelector>(std::move(rv), status));
|
||||
CHECK_ERROR(status);
|
||||
res.adoptElement(v.orphan(), status);
|
||||
res.adoptElement(rv.orphan(), status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,18 +343,17 @@ void MessageFormatter::resolveSelectors(MessageContext& context, const Environme
|
||||
// `keys` and `matches` are vectors of strings
|
||||
void MessageFormatter::matchSelectorKeys(const UVector& keys,
|
||||
MessageContext& context,
|
||||
ResolvedSelector&& rv,
|
||||
InternalValue* rv, // Does not adopt `rv`
|
||||
UVector& keysOut,
|
||||
UErrorCode& status) const {
|
||||
CHECK_ERROR(status);
|
||||
|
||||
if (!rv.hasSelector()) {
|
||||
if (U_FAILURE(status)) {
|
||||
// Return an empty list of matches
|
||||
status = U_ZERO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
auto selectorImpl = rv.getSelector();
|
||||
U_ASSERT(selectorImpl != nullptr);
|
||||
UErrorCode savedStatus = status;
|
||||
|
||||
// Convert `keys` to an array
|
||||
@ -400,15 +380,17 @@ void MessageFormatter::matchSelectorKeys(const UVector& keys,
|
||||
int32_t prefsLen = 0;
|
||||
|
||||
// Call the selector
|
||||
selectorImpl->selectKey(rv.takeArgument(), rv.takeOptions(),
|
||||
adoptedKeys.getAlias(), keysLen, adoptedPrefs.getAlias(), prefsLen,
|
||||
status);
|
||||
FunctionName name = rv->getFunctionName();
|
||||
rv->forceSelection(context.getErrors(),
|
||||
adoptedKeys.getAlias(), keysLen,
|
||||
adoptedPrefs.getAlias(), prefsLen,
|
||||
status);
|
||||
|
||||
// Update errors
|
||||
if (savedStatus != status) {
|
||||
if (U_FAILURE(status)) {
|
||||
status = U_ZERO_ERROR;
|
||||
context.getErrors().setSelectorError(rv.getSelectorName(), status);
|
||||
context.getErrors().setSelectorError(name, status);
|
||||
} else {
|
||||
// Ignore warnings
|
||||
status = savedStatus;
|
||||
@ -461,8 +443,8 @@ void MessageFormatter::resolvePreferences(MessageContext& context, UVector& res,
|
||||
if (!key.isWildcard()) {
|
||||
// 2ii(b)(a) Assert that key is a literal.
|
||||
// (Not needed)
|
||||
// 2ii(b)(b) Let `ks` be the resolved value of `key`.
|
||||
ks = key.asLiteral().unquoted();
|
||||
// 2ii(b)(b) Let `ks` be the resolved value of `key` in Unicode Normalization Form C.
|
||||
ks = normalizeNFC(key.asLiteral().unquoted());
|
||||
// 2ii(b)(c) Append `ks` as the last element of the list `keys`.
|
||||
ksP.adoptInstead(create<UnicodeString>(std::move(ks), status));
|
||||
CHECK_ERROR(status);
|
||||
@ -471,7 +453,7 @@ void MessageFormatter::resolvePreferences(MessageContext& context, UVector& res,
|
||||
}
|
||||
// 2iii. Let `rv` be the resolved value at index `i` of `res`.
|
||||
U_ASSERT(i < res.size());
|
||||
ResolvedSelector rv = std::move(*(static_cast<ResolvedSelector*>(res[i])));
|
||||
InternalValue* rv = static_cast<InternalValue*>(res[i]);
|
||||
// 2iv. Let matches be the result of calling the method MatchSelectorKeys(rv, keys)
|
||||
LocalPointer<UVector> matches(createUVector(status));
|
||||
matchSelectorKeys(*keys, context, std::move(rv), *matches, status);
|
||||
@ -523,7 +505,7 @@ void MessageFormatter::filterVariants(const UVector& pref, UVector& vars, UError
|
||||
// 2i(c). Assert that `key` is a literal.
|
||||
// (Not needed)
|
||||
// 2i(d). Let `ks` be the resolved value of `key`.
|
||||
UnicodeString ks = key.asLiteral().unquoted();
|
||||
UnicodeString ks = normalizeNFC(key.asLiteral().unquoted());
|
||||
// 2i(e). Let `matches` be the list of strings at index `i` of `pref`.
|
||||
const UVector& matches = *(static_cast<UVector*>(pref[i])); // `matches` is a vector of strings
|
||||
// 2i(f). If `matches` includes `ks`
|
||||
@ -585,7 +567,7 @@ void MessageFormatter::sortVariants(const UVector& pref, UVector& vars, UErrorCo
|
||||
// 5iii(c)(a). Assert that `key` is a literal.
|
||||
// (Not needed)
|
||||
// 5iii(c)(b). Let `ks` be the resolved value of `key`.
|
||||
UnicodeString ks = key.asLiteral().unquoted();
|
||||
UnicodeString ks = normalizeNFC(key.asLiteral().unquoted());
|
||||
// 5iii(c)(c) Let matchpref be the integer position of ks in `matches`.
|
||||
matchpref = vectorFind(matches, ks);
|
||||
U_ASSERT(matchpref >= 0);
|
||||
@ -604,123 +586,13 @@ void MessageFormatter::sortVariants(const UVector& pref, UVector& vars, UErrorCo
|
||||
// 7. Select the pattern of `var`
|
||||
}
|
||||
|
||||
|
||||
// Evaluate the operand
|
||||
ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, const Operand& rand, MessageContext& context, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (rand.isNull()) {
|
||||
return ResolvedSelector(FormattedPlaceholder());
|
||||
}
|
||||
|
||||
if (rand.isLiteral()) {
|
||||
return ResolvedSelector(formatLiteral(rand.asLiteral()));
|
||||
}
|
||||
|
||||
// Must be variable
|
||||
const VariableName& var = rand.asVariable();
|
||||
// Resolve the variable
|
||||
if (env.has(var)) {
|
||||
const Closure& referent = env.lookup(var);
|
||||
// Resolve the referent
|
||||
return resolveVariables(referent.getEnv(), referent.getExpr(), context, status);
|
||||
}
|
||||
// Either this is a global var or an unbound var --
|
||||
// either way, it can't be bound to a function call.
|
||||
// Check globals
|
||||
FormattedPlaceholder val = evalArgument(var, context, status);
|
||||
if (status == U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
// Unresolved variable -- could be a previous warning. Nothing to resolve
|
||||
U_ASSERT(context.getErrors().hasUnresolvedVariableError());
|
||||
return ResolvedSelector(FormattedPlaceholder(var));
|
||||
}
|
||||
// Pass through other errors
|
||||
return ResolvedSelector(std::move(val));
|
||||
}
|
||||
|
||||
// Evaluate the expression except for not performing the top-level function call
|
||||
// (which is expected to be a selector, but may not be, in error cases)
|
||||
ResolvedSelector MessageFormatter::resolveVariables(const Environment& env,
|
||||
const Expression& expr,
|
||||
MessageContext& context,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Function call -- resolve the operand and options
|
||||
if (expr.isFunctionCall()) {
|
||||
const Operator* rator = expr.getOperator(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
// Already checked that rator is non-reserved
|
||||
const FunctionName& selectorName = rator->getFunctionName();
|
||||
if (isSelector(selectorName)) {
|
||||
auto selector = getSelector(context, selectorName, status);
|
||||
if (U_SUCCESS(status)) {
|
||||
FunctionOptions resolvedOptions = resolveOptions(env, rator->getOptionsInternal(), context, status);
|
||||
// Operand may be the null argument, but resolveVariables() handles that
|
||||
FormattedPlaceholder argument = formatOperand(env, expr.getOperand(), context, status);
|
||||
return ResolvedSelector(selectorName, selector, std::move(resolvedOptions), std::move(argument));
|
||||
}
|
||||
} else if (isFormatter(selectorName)) {
|
||||
context.getErrors().setSelectorError(selectorName, status);
|
||||
} else {
|
||||
context.getErrors().setUnknownFunction(selectorName, status);
|
||||
}
|
||||
// Non-selector used as selector; an error would have been recorded earlier
|
||||
UnicodeString fallback(COLON);
|
||||
fallback += selectorName;
|
||||
if (!expr.getOperand().isNull()) {
|
||||
fallback = formatOperand(env, expr.getOperand(), context, status).fallback;
|
||||
}
|
||||
return ResolvedSelector(FormattedPlaceholder(fallback));
|
||||
} else {
|
||||
// Might be a variable reference, so expand one more level of variable
|
||||
return resolveVariables(env, expr.getOperand(), context, status);
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedSelector MessageFormatter::formatSelectorExpression(const Environment& globalEnv, const Expression& expr, MessageContext& context, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Resolve expression to determine if it's a function call
|
||||
ResolvedSelector exprResult = resolveVariables(globalEnv, expr, context, status);
|
||||
|
||||
DynamicErrors& err = context.getErrors();
|
||||
|
||||
// If there is a selector, then `resolveVariables()` recorded it in the context
|
||||
if (exprResult.hasSelector()) {
|
||||
// Check if there was an error
|
||||
if (exprResult.argument().isFallback()) {
|
||||
// Use a null expression if it's a syntax or data model warning;
|
||||
// create a valid (non-fallback) formatted placeholder from the
|
||||
// fallback string otherwise
|
||||
if (err.hasSyntaxError() || err.hasDataModelError()) {
|
||||
return ResolvedSelector(FormattedPlaceholder()); // Null operand
|
||||
} else {
|
||||
return ResolvedSelector(exprResult.takeArgument());
|
||||
}
|
||||
}
|
||||
return exprResult;
|
||||
}
|
||||
|
||||
// No selector was found; error should already have been set
|
||||
U_ASSERT(err.hasMissingSelectorAnnotationError() || err.hasUnknownFunctionError() || err.hasSelectorError());
|
||||
return ResolvedSelector(FormattedPlaceholder(exprResult.argument().fallback));
|
||||
}
|
||||
|
||||
void MessageFormatter::formatSelectors(MessageContext& context, const Environment& env, UErrorCode &status, UnicodeString& result) const {
|
||||
CHECK_ERROR(status);
|
||||
|
||||
// See https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection
|
||||
|
||||
// Resolve Selectors
|
||||
// res is a vector of FormattedPlaceholders
|
||||
// res is a vector of InternalValues
|
||||
LocalPointer<UVector> res(createUVector(status));
|
||||
CHECK_ERROR(status);
|
||||
resolveSelectors(context, env, status, *res);
|
||||
@ -761,28 +633,35 @@ void MessageFormatter::formatSelectors(MessageContext& context, const Environmen
|
||||
UnicodeString MessageFormatter::formatToString(const MessageArguments& arguments, UErrorCode &status) {
|
||||
EMPTY_ON_ERROR(status);
|
||||
|
||||
// Create a new environment that will store closures for all local variables
|
||||
Environment* env = Environment::create(status);
|
||||
// Create a new context with the given arguments and the `errors` structure
|
||||
MessageContext context(arguments, *errors, status);
|
||||
|
||||
// Check for unresolved variable errors
|
||||
checkDeclarations(context, env, status);
|
||||
LocalPointer<Environment> globalEnv(env);
|
||||
|
||||
UnicodeString result;
|
||||
if (dataModel.hasPattern()) {
|
||||
formatPattern(context, *globalEnv, dataModel.getPattern(), status, result);
|
||||
} else {
|
||||
// Check for errors/warnings -- if so, then the result of pattern selection is the fallback value
|
||||
// See https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection
|
||||
const DynamicErrors& err = context.getErrors();
|
||||
if (err.hasSyntaxError() || err.hasDataModelError()) {
|
||||
result += REPLACEMENT;
|
||||
|
||||
if (!(errors->hasSyntaxError() || errors->hasDataModelError())) {
|
||||
// Create a new environment that will store closures for all local variables
|
||||
// Check for unresolved variable errors
|
||||
// checkDeclarations needs a reference to the pointer to the environment
|
||||
// since it uses its `env` argument as an out-parameter. So it needs to be
|
||||
// temporarily not a LocalPointer...
|
||||
Environment* env(Environment::create(status));
|
||||
checkDeclarations(context, env, status);
|
||||
// ...and then it's adopted to avoid leaks
|
||||
LocalPointer<Environment> globalEnv(env);
|
||||
|
||||
if (dataModel.hasPattern()) {
|
||||
formatPattern(context, *globalEnv, dataModel.getPattern(), status, result);
|
||||
} else {
|
||||
formatSelectors(context, *globalEnv, status, result);
|
||||
// Check for errors/warnings -- if so, then the result of pattern selection is the fallback value
|
||||
// See https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection
|
||||
const DynamicErrors& err = context.getErrors();
|
||||
if (err.hasSyntaxError() || err.hasDataModelError()) {
|
||||
result += REPLACEMENT;
|
||||
} else {
|
||||
formatSelectors(context, *globalEnv, status, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update status according to all errors seen while formatting
|
||||
if (signalErrors) {
|
||||
context.checkErrors(status);
|
||||
@ -813,12 +692,14 @@ void MessageFormatter::check(MessageContext& context, const Environment& localEn
|
||||
|
||||
// Check that variable is in scope
|
||||
const VariableName& var = rand.asVariable();
|
||||
UnicodeString normalized = normalizeNFC(var);
|
||||
|
||||
// Check local scope
|
||||
if (localEnv.has(var)) {
|
||||
if (localEnv.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
// Check global scope
|
||||
context.getGlobal(var, status);
|
||||
context.getGlobal(*this, normalized, status);
|
||||
if (status == U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
status = U_ZERO_ERROR;
|
||||
context.getErrors().setUnresolvedVariable(var, status);
|
||||
@ -855,7 +736,10 @@ void MessageFormatter::checkDeclarations(MessageContext& context, Environment*&
|
||||
// memoizing the value of localEnv up to this point
|
||||
|
||||
// Add the LHS to the environment for checking the next declaration
|
||||
env = Environment::create(decl.getVariable(), Closure(rhs, *env), env, status);
|
||||
env = Environment::create(normalizeNFC(decl.getVariable()),
|
||||
Closure(rhs, *env),
|
||||
env,
|
||||
status);
|
||||
CHECK_ERROR(status);
|
||||
}
|
||||
}
|
||||
@ -866,3 +750,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -139,6 +141,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT2_UTILS_H
|
||||
|
@ -3,12 +3,16 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
|
||||
#include "unicode/messageformat2.h"
|
||||
#include "unicode/messageformat2_arguments.h"
|
||||
#include "unicode/messageformat2_data_model_names.h"
|
||||
#include "messageformat2_evaluation.h"
|
||||
#include "uvector.h" // U_ASSERT
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
@ -22,11 +26,15 @@ namespace message2 {
|
||||
|
||||
using Arguments = MessageArguments;
|
||||
|
||||
const Formattable* Arguments::getArgument(const VariableName& arg, UErrorCode& errorCode) const {
|
||||
const Formattable* Arguments::getArgument(const MessageFormatter& context,
|
||||
const VariableName& arg,
|
||||
UErrorCode& errorCode) const {
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
U_ASSERT(argsLen == 0 || arguments.isValid());
|
||||
for (int32_t i = 0; i < argsLen; i++) {
|
||||
if (argumentNames[i] == arg) {
|
||||
UnicodeString normalized = context.normalizeNFC(argumentNames[i]);
|
||||
// arg already assumed to be normalized
|
||||
if (normalized == arg) {
|
||||
return &arguments[i];
|
||||
}
|
||||
}
|
||||
@ -57,3 +65,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -3,12 +3,16 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
|
||||
#include "unicode/messageformat2.h"
|
||||
#include "messageformat2_allocation.h"
|
||||
#include "messageformat2_checker.h"
|
||||
#include "messageformat2_evaluation.h"
|
||||
#include "messageformat2_macros.h"
|
||||
#include "uvector.h" // U_ASSERT
|
||||
|
||||
@ -104,6 +108,14 @@ TypeEnvironment::~TypeEnvironment() {}
|
||||
|
||||
// ---------------------
|
||||
|
||||
Key Checker::normalizeNFC(const Key& k) const {
|
||||
if (k.isWildcard()) {
|
||||
return k;
|
||||
}
|
||||
return Key(Literal(k.asLiteral().isQuoted(),
|
||||
context.normalizeNFC(k.asLiteral().unquoted())));
|
||||
}
|
||||
|
||||
static bool areDefaultKeys(const Key* keys, int32_t len) {
|
||||
U_ASSERT(len > 0);
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
@ -185,7 +197,7 @@ void Checker::checkVariants(UErrorCode& status) {
|
||||
// This variant was already checked,
|
||||
// so we know keys1.len == len
|
||||
for (int32_t kk = 0; kk < len; kk++) {
|
||||
if (!(keys[kk] == keys1[kk])) {
|
||||
if (!(normalizeNFC(keys[kk]) == normalizeNFC(keys1[kk]))) {
|
||||
allEqual = false;
|
||||
break;
|
||||
}
|
||||
@ -205,18 +217,14 @@ void Checker::checkVariants(UErrorCode& status) {
|
||||
}
|
||||
}
|
||||
|
||||
void Checker::requireAnnotated(const TypeEnvironment& t, const Expression& selectorExpr, UErrorCode& status) {
|
||||
void Checker::requireAnnotated(const TypeEnvironment& t,
|
||||
const VariableName& selectorVar,
|
||||
UErrorCode& status) {
|
||||
CHECK_ERROR(status);
|
||||
|
||||
if (selectorExpr.isFunctionCall()) {
|
||||
if (t.get(selectorVar) == TypeEnvironment::Type::Annotated) {
|
||||
return; // No error
|
||||
}
|
||||
const Operand& rand = selectorExpr.getOperand();
|
||||
if (rand.isVariable()) {
|
||||
if (t.get(rand.asVariable()) == TypeEnvironment::Type::Annotated) {
|
||||
return; // No error
|
||||
}
|
||||
}
|
||||
// If this code is reached, an error was detected
|
||||
errors.addError(StaticErrorType::MissingSelectorAnnotation, status);
|
||||
}
|
||||
@ -226,7 +234,7 @@ void Checker::checkSelectors(const TypeEnvironment& t, UErrorCode& status) {
|
||||
|
||||
// Check each selector; if it's not annotated, emit a
|
||||
// "missing selector annotation" error
|
||||
const Expression* selectors = dataModel.getSelectorsInternal();
|
||||
const VariableName* selectors = dataModel.getSelectorsInternal();
|
||||
for (int32_t i = 0; i < dataModel.numSelectors(); i++) {
|
||||
requireAnnotated(t, selectors[i], status);
|
||||
}
|
||||
@ -312,3 +320,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -56,15 +58,20 @@ namespace message2 {
|
||||
// an explicit declaration
|
||||
}; // class TypeEnvironment
|
||||
|
||||
class MessageFormatter;
|
||||
|
||||
// Checks a data model for semantic errors
|
||||
// (Errors are defined in https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md )
|
||||
class Checker {
|
||||
public:
|
||||
void check(UErrorCode&);
|
||||
Checker(const MFDataModel& m, StaticErrors& e) : dataModel(m), errors(e) {}
|
||||
Checker(const MFDataModel& d, StaticErrors& e, const MessageFormatter& mf)
|
||||
: dataModel(d), errors(e), context(mf) {}
|
||||
private:
|
||||
|
||||
void requireAnnotated(const TypeEnvironment&, const Expression&, UErrorCode&);
|
||||
Key normalizeNFC(const Key&) const;
|
||||
|
||||
void requireAnnotated(const TypeEnvironment&, const VariableName&, UErrorCode&);
|
||||
void addFreeVars(TypeEnvironment& t, const Operand&, UErrorCode&);
|
||||
void addFreeVars(TypeEnvironment& t, const Operator&, UErrorCode&);
|
||||
void addFreeVars(TypeEnvironment& t, const OptionMap&, UErrorCode&);
|
||||
@ -78,6 +85,9 @@ namespace message2 {
|
||||
void check(const Pattern&);
|
||||
const MFDataModel& dataModel;
|
||||
StaticErrors& errors;
|
||||
|
||||
// Used for NFC normalization
|
||||
const MessageFormatter& context;
|
||||
}; // class Checker
|
||||
|
||||
} // namespace message2
|
||||
@ -88,6 +98,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT_CHECKER_H
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -691,9 +693,9 @@ Matcher::Matcher(const Matcher& other) {
|
||||
numSelectors = other.numSelectors;
|
||||
numVariants = other.numVariants;
|
||||
UErrorCode localErrorCode = U_ZERO_ERROR;
|
||||
selectors.adoptInstead(copyArray<Expression>(other.selectors.getAlias(),
|
||||
numSelectors,
|
||||
localErrorCode));
|
||||
selectors.adoptInstead(copyArray<VariableName>(other.selectors.getAlias(),
|
||||
numSelectors,
|
||||
localErrorCode));
|
||||
variants.adoptInstead(copyArray<Variant>(other.variants.getAlias(),
|
||||
numVariants,
|
||||
localErrorCode));
|
||||
@ -702,7 +704,7 @@ Matcher::Matcher(const Matcher& other) {
|
||||
}
|
||||
}
|
||||
|
||||
Matcher::Matcher(Expression* ss, int32_t ns, Variant* vs, int32_t nv)
|
||||
Matcher::Matcher(VariableName* ss, int32_t ns, Variant* vs, int32_t nv)
|
||||
: selectors(ss), numSelectors(ns), variants(vs), numVariants(nv) {}
|
||||
|
||||
Matcher::~Matcher() {}
|
||||
@ -724,7 +726,7 @@ const Binding* MFDataModel::getLocalVariablesInternal() const {
|
||||
return bindings.getAlias();
|
||||
}
|
||||
|
||||
const Expression* MFDataModel::getSelectorsInternal() const {
|
||||
const VariableName* MFDataModel::getSelectorsInternal() const {
|
||||
U_ASSERT(!bogus);
|
||||
U_ASSERT(!hasPattern());
|
||||
return std::get_if<Matcher>(&body)->selectors.getAlias();
|
||||
@ -786,15 +788,13 @@ MFDataModel::Builder& MFDataModel::Builder::addBinding(Binding&& b, UErrorCode&
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
selector must be non-null
|
||||
*/
|
||||
MFDataModel::Builder& MFDataModel::Builder::addSelector(Expression&& selector, UErrorCode& status) noexcept {
|
||||
MFDataModel::Builder& MFDataModel::Builder::addSelector(VariableName&& selector,
|
||||
UErrorCode& status) {
|
||||
THIS_ON_ERROR(status);
|
||||
|
||||
buildSelectorsMessage(status);
|
||||
U_ASSERT(selectors != nullptr);
|
||||
selectors->adoptElement(create<Expression>(std::move(selector), status), status);
|
||||
selectors->adoptElement(create<VariableName>(std::move(selector), status), status);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -830,11 +830,11 @@ MFDataModel::MFDataModel(const MFDataModel& other) : body(Pattern()) {
|
||||
if (other.hasPattern()) {
|
||||
body = *std::get_if<Pattern>(&other.body);
|
||||
} else {
|
||||
const Expression* otherSelectors = other.getSelectorsInternal();
|
||||
const VariableName* otherSelectors = other.getSelectorsInternal();
|
||||
const Variant* otherVariants = other.getVariantsInternal();
|
||||
int32_t numSelectors = other.numSelectors();
|
||||
int32_t numVariants = other.numVariants();
|
||||
Expression* copiedSelectors = copyArray(otherSelectors, numSelectors, localErrorCode);
|
||||
VariableName* copiedSelectors = copyArray(otherSelectors, numSelectors, localErrorCode);
|
||||
Variant* copiedVariants = copyArray(otherVariants, numVariants, localErrorCode);
|
||||
if (U_FAILURE(localErrorCode)) {
|
||||
bogus = true;
|
||||
@ -863,7 +863,9 @@ MFDataModel::MFDataModel(const MFDataModel::Builder& builder, UErrorCode& errorC
|
||||
int32_t numVariants = builder.variants->size();
|
||||
int32_t numSelectors = builder.selectors->size();
|
||||
LocalArray<Variant> variants(copyVectorToArray<Variant>(*builder.variants, errorCode), errorCode);
|
||||
LocalArray<Expression> selectors(copyVectorToArray<Expression>(*builder.selectors, errorCode), errorCode);
|
||||
LocalArray<VariableName> selectors(copyVectorToArray<VariableName>(*builder.selectors,
|
||||
errorCode),
|
||||
errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
bogus = true;
|
||||
return;
|
||||
@ -918,3 +920,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -290,3 +292,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -15,6 +15,8 @@
|
||||
* \brief C++ API: Formats messages using the draft MessageFormat 2.0.
|
||||
*/
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -151,6 +153,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT2_ERRORS_H
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -89,35 +91,54 @@ FunctionOptions::FunctionOptions(FunctionOptions&& other) {
|
||||
FunctionOptions::~FunctionOptions() {
|
||||
if (options != nullptr) {
|
||||
delete[] options;
|
||||
options = nullptr;
|
||||
}
|
||||
}
|
||||
// ResolvedSelector
|
||||
// ----------------
|
||||
|
||||
ResolvedSelector::ResolvedSelector(const FunctionName& fn,
|
||||
Selector* sel,
|
||||
FunctionOptions&& opts,
|
||||
FormattedPlaceholder&& val)
|
||||
: selectorName(fn), selector(sel), options(std::move(opts)), value(std::move(val)) {
|
||||
U_ASSERT(sel != nullptr);
|
||||
static bool containsOption(const UVector& opts, const ResolvedFunctionOption& opt) {
|
||||
for (int32_t i = 0; i < opts.size(); i++) {
|
||||
if (static_cast<ResolvedFunctionOption*>(opts[i])->getName()
|
||||
== opt.getName()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ResolvedSelector::ResolvedSelector(FormattedPlaceholder&& val) : value(std::move(val)) {}
|
||||
// Options in `this` take precedence
|
||||
// `this` can't be used after mergeOptions is called
|
||||
FunctionOptions FunctionOptions::mergeOptions(FunctionOptions&& other,
|
||||
UErrorCode& status) {
|
||||
UVector mergedOptions(status);
|
||||
mergedOptions.setDeleter(uprv_deleteUObject);
|
||||
|
||||
ResolvedSelector& ResolvedSelector::operator=(ResolvedSelector&& other) noexcept {
|
||||
selectorName = std::move(other.selectorName);
|
||||
selector.adoptInstead(other.selector.orphan());
|
||||
options = std::move(other.options);
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a new vector consisting of the options from this `FunctionOptions`
|
||||
for (int32_t i = 0; i < functionOptionsLen; i++) {
|
||||
mergedOptions.adoptElement(create<ResolvedFunctionOption>(std::move(options[i]), status),
|
||||
status);
|
||||
}
|
||||
|
||||
// Add each option from `other` that doesn't appear in this `FunctionOptions`
|
||||
for (int i = 0; i < other.functionOptionsLen; i++) {
|
||||
// Note: this is quadratic in the length of `options`
|
||||
if (!containsOption(mergedOptions, other.options[i])) {
|
||||
mergedOptions.adoptElement(create<ResolvedFunctionOption>(std::move(other.options[i]),
|
||||
status),
|
||||
status);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] options;
|
||||
options = nullptr;
|
||||
functionOptionsLen = 0;
|
||||
|
||||
return FunctionOptions(std::move(mergedOptions), status);
|
||||
}
|
||||
|
||||
ResolvedSelector::ResolvedSelector(ResolvedSelector&& other) {
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
ResolvedSelector::~ResolvedSelector() {}
|
||||
|
||||
// PrioritizedVariant
|
||||
// ------------------
|
||||
|
||||
@ -190,18 +211,210 @@ PrioritizedVariant::~PrioritizedVariant() {}
|
||||
errors.checkErrors(status);
|
||||
}
|
||||
|
||||
const Formattable* MessageContext::getGlobal(const VariableName& v, UErrorCode& errorCode) const {
|
||||
return arguments.getArgument(v, errorCode);
|
||||
const Formattable* MessageContext::getGlobal(const MessageFormatter& context,
|
||||
const VariableName& v,
|
||||
UErrorCode& errorCode) const {
|
||||
return arguments.getArgument(context, v, errorCode);
|
||||
}
|
||||
|
||||
MessageContext::MessageContext(const MessageArguments& args,
|
||||
const StaticErrors& e,
|
||||
UErrorCode& status) : arguments(args), errors(e, status) {}
|
||||
|
||||
MessageContext::~MessageContext() {}
|
||||
|
||||
// InternalValue
|
||||
// -------------
|
||||
|
||||
bool InternalValue::isFallback() const {
|
||||
return std::holds_alternative<FormattedPlaceholder>(argument)
|
||||
&& std::get_if<FormattedPlaceholder>(&argument)->isFallback();
|
||||
}
|
||||
|
||||
bool InternalValue::hasNullOperand() const {
|
||||
return std::holds_alternative<FormattedPlaceholder>(argument)
|
||||
&& std::get_if<FormattedPlaceholder>(&argument)->isNullOperand();
|
||||
}
|
||||
|
||||
FormattedPlaceholder InternalValue::takeArgument(UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (std::holds_alternative<FormattedPlaceholder>(argument)) {
|
||||
return std::move(*std::get_if<FormattedPlaceholder>(&argument));
|
||||
}
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
const UnicodeString& InternalValue::getFallback() const {
|
||||
if (std::holds_alternative<FormattedPlaceholder>(argument)) {
|
||||
return std::get_if<FormattedPlaceholder>(&argument)->getFallback();
|
||||
}
|
||||
return (*std::get_if<InternalValue*>(&argument))->getFallback();
|
||||
}
|
||||
|
||||
const Selector* InternalValue::getSelector(UErrorCode& errorCode) const {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (selector == nullptr) {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
InternalValue::InternalValue(FormattedPlaceholder&& arg) {
|
||||
argument = std::move(arg);
|
||||
selector = nullptr;
|
||||
formatter = nullptr;
|
||||
}
|
||||
|
||||
InternalValue::InternalValue(InternalValue* operand,
|
||||
FunctionOptions&& opts,
|
||||
const FunctionName& functionName,
|
||||
const Formatter* f,
|
||||
const Selector* s) {
|
||||
argument = operand;
|
||||
options = std::move(opts);
|
||||
name = functionName;
|
||||
selector = s;
|
||||
formatter = f;
|
||||
U_ASSERT(selector != nullptr || formatter != nullptr);
|
||||
}
|
||||
|
||||
// `this` cannot be used after calling this method
|
||||
void InternalValue::forceSelection(DynamicErrors& errs,
|
||||
const UnicodeString* keys,
|
||||
int32_t keysLen,
|
||||
UnicodeString* prefs,
|
||||
int32_t& prefsLen,
|
||||
UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canSelect()) {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
// Find the argument and complete set of options by traversing `argument`
|
||||
FunctionOptions opts;
|
||||
InternalValue* p = this;
|
||||
FunctionName selectorName = name;
|
||||
while (std::holds_alternative<InternalValue*>(p->argument)) {
|
||||
if (p->name != selectorName) {
|
||||
// Can only compose calls to the same selector
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
// First argument to mergeOptions takes precedence
|
||||
opts = opts.mergeOptions(std::move(p->options), errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return;
|
||||
}
|
||||
InternalValue* next = *std::get_if<InternalValue*>(&p->argument);
|
||||
p = next;
|
||||
}
|
||||
FormattedPlaceholder arg = std::move(*std::get_if<FormattedPlaceholder>(&p->argument));
|
||||
|
||||
selector->selectKey(std::move(arg), std::move(opts),
|
||||
keys, keysLen,
|
||||
prefs, prefsLen, errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
errorCode = U_ZERO_ERROR;
|
||||
errs.setSelectorError(selectorName, errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
FormattedPlaceholder InternalValue::forceFormatting(DynamicErrors& errs, UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (formatter == nullptr && selector == nullptr) {
|
||||
U_ASSERT(std::holds_alternative<FormattedPlaceholder>(argument));
|
||||
return std::move(*std::get_if<FormattedPlaceholder>(&argument));
|
||||
}
|
||||
if (formatter == nullptr) {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return {};
|
||||
}
|
||||
|
||||
FormattedPlaceholder arg;
|
||||
|
||||
if (std::holds_alternative<FormattedPlaceholder>(argument)) {
|
||||
arg = std::move(*std::get_if<FormattedPlaceholder>(&argument));
|
||||
} else {
|
||||
arg = (*std::get_if<InternalValue*>(&argument))->forceFormatting(errs,
|
||||
errorCode);
|
||||
}
|
||||
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// The fallback for a nullary function call is the function name
|
||||
UnicodeString fallback;
|
||||
if (arg.isNullOperand()) {
|
||||
fallback = u":";
|
||||
fallback += name;
|
||||
} else {
|
||||
fallback = arg.getFallback();
|
||||
}
|
||||
|
||||
// Call the function with the argument
|
||||
FormattedPlaceholder result = formatter->format(std::move(arg), std::move(options), errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
if (errorCode == U_MF_OPERAND_MISMATCH_ERROR) {
|
||||
errorCode = U_ZERO_ERROR;
|
||||
errs.setOperandMismatchError(name, errorCode);
|
||||
} else {
|
||||
errorCode = U_ZERO_ERROR;
|
||||
// Convey any error generated by the formatter
|
||||
// as a formatting error, except for operand mismatch errors
|
||||
errs.setFormattingError(name, errorCode);
|
||||
}
|
||||
}
|
||||
// Ignore the output if any error occurred
|
||||
if (errs.hasFormattingError()) {
|
||||
return FormattedPlaceholder(fallback);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
InternalValue& InternalValue::operator=(InternalValue&& other) noexcept {
|
||||
argument = std::move(other.argument);
|
||||
other.argument = nullptr;
|
||||
options = std::move(other.options);
|
||||
name = other.name;
|
||||
selector = other.selector;
|
||||
formatter = other.formatter;
|
||||
other.selector = nullptr;
|
||||
other.formatter = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
InternalValue::~InternalValue() {
|
||||
delete selector;
|
||||
selector = nullptr;
|
||||
delete formatter;
|
||||
formatter = nullptr;
|
||||
if (std::holds_alternative<InternalValue*>(argument)) {
|
||||
delete *std::get_if<InternalValue*>(&argument);
|
||||
argument = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace message2
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -14,6 +14,7 @@
|
||||
* \file
|
||||
* \brief C++ API: Formats messages using the draft MessageFormat 2.0.
|
||||
*/
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
@ -63,38 +64,6 @@ namespace message2 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Encapsulates a value to be scrutinized by a `match` with its resolved
|
||||
// options and the name of the selector
|
||||
class ResolvedSelector : public UObject {
|
||||
public:
|
||||
ResolvedSelector() {}
|
||||
ResolvedSelector(const FunctionName& fn,
|
||||
Selector* selector,
|
||||
FunctionOptions&& options,
|
||||
FormattedPlaceholder&& value);
|
||||
// Used either for errors, or when selector isn't yet known
|
||||
explicit ResolvedSelector(FormattedPlaceholder&& value);
|
||||
bool hasSelector() const { return selector.isValid(); }
|
||||
const FormattedPlaceholder& argument() const { return value; }
|
||||
FormattedPlaceholder&& takeArgument() { return std::move(value); }
|
||||
const Selector* getSelector() {
|
||||
U_ASSERT(selector.isValid());
|
||||
return selector.getAlias();
|
||||
}
|
||||
FunctionOptions&& takeOptions() {
|
||||
return std::move(options);
|
||||
}
|
||||
const FunctionName& getSelectorName() const { return selectorName; }
|
||||
virtual ~ResolvedSelector();
|
||||
ResolvedSelector& operator=(ResolvedSelector&&) noexcept;
|
||||
ResolvedSelector(ResolvedSelector&&);
|
||||
private:
|
||||
FunctionName selectorName; // For error reporting
|
||||
LocalPointer<Selector> selector;
|
||||
FunctionOptions options;
|
||||
FormattedPlaceholder value;
|
||||
}; // class ResolvedSelector
|
||||
|
||||
// Closures and environments
|
||||
// -------------------------
|
||||
|
||||
@ -174,11 +143,15 @@ namespace message2 {
|
||||
// The context contains all the information needed to process
|
||||
// an entire message: arguments, formatter cache, and error list
|
||||
|
||||
class MessageFormatter;
|
||||
|
||||
class MessageContext : public UMemory {
|
||||
public:
|
||||
MessageContext(const MessageArguments&, const StaticErrors&, UErrorCode&);
|
||||
|
||||
const Formattable* getGlobal(const VariableName&, UErrorCode&) const;
|
||||
const Formattable* getGlobal(const MessageFormatter&,
|
||||
const VariableName&,
|
||||
UErrorCode&) const;
|
||||
|
||||
// If any errors were set, update `status` accordingly
|
||||
void checkErrors(UErrorCode& status) const;
|
||||
@ -191,8 +164,47 @@ namespace message2 {
|
||||
const MessageArguments& arguments; // External message arguments
|
||||
// Errors accumulated during parsing/formatting
|
||||
DynamicErrors errors;
|
||||
|
||||
}; // class MessageContext
|
||||
|
||||
// InternalValue
|
||||
// ----------------
|
||||
|
||||
class InternalValue : public UObject {
|
||||
public:
|
||||
const FunctionName& getFunctionName() const { return name; }
|
||||
bool canSelect() const { return selector != nullptr; }
|
||||
const Selector* getSelector(UErrorCode&) const;
|
||||
FormattedPlaceholder forceFormatting(DynamicErrors& errs,
|
||||
UErrorCode& errorCode);
|
||||
void forceSelection(DynamicErrors& errs,
|
||||
const UnicodeString* keys,
|
||||
int32_t keysLen,
|
||||
UnicodeString* prefs,
|
||||
int32_t& prefsLen,
|
||||
UErrorCode& errorCode);
|
||||
// Needs to be deep-copyable and movable
|
||||
virtual ~InternalValue();
|
||||
InternalValue(FormattedPlaceholder&&);
|
||||
// Formatter and selector may be null
|
||||
InternalValue(InternalValue*, FunctionOptions&&, const FunctionName&, const Formatter*,
|
||||
const Selector*);
|
||||
const UnicodeString& getFallback() const;
|
||||
bool isFallback() const;
|
||||
bool hasNullOperand() const;
|
||||
// Can't be used anymore after calling this
|
||||
FormattedPlaceholder takeArgument(UErrorCode& errorCode);
|
||||
InternalValue(InternalValue&& other) { *this = std::move(other); }
|
||||
InternalValue& operator=(InternalValue&& other) noexcept;
|
||||
private:
|
||||
// InternalValue is owned (if present)
|
||||
std::variant<InternalValue*, FormattedPlaceholder> argument;
|
||||
FunctionOptions options;
|
||||
FunctionName name;
|
||||
const Selector* selector; // May be null
|
||||
const Formatter* formatter; // May be null, but one or the other should be non-null unless argument is a FormattedPlaceholder
|
||||
}; // class InternalValue
|
||||
|
||||
} // namespace message2
|
||||
|
||||
U_NAMESPACE_END
|
||||
@ -201,6 +213,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT2_EVALUATION_H
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -336,3 +338,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -43,7 +45,8 @@ namespace message2 {
|
||||
|
||||
// Parse the pattern
|
||||
MFDataModel::Builder tree(errorCode);
|
||||
Parser(pat, tree, *errors, normalizedInput).parse(parseError, errorCode);
|
||||
Parser(pat, tree, *errors, normalizedInput, errorCode)
|
||||
.parse(parseError, errorCode);
|
||||
|
||||
// Fail on syntax errors
|
||||
if (errors->hasSyntaxError()) {
|
||||
@ -116,6 +119,24 @@ namespace message2 {
|
||||
|
||||
// MessageFormatter
|
||||
|
||||
// Returns the NFC-normalized version of s, returning s itself
|
||||
// if it's already normalized.
|
||||
UnicodeString MessageFormatter::normalizeNFC(const UnicodeString& s) const {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
// Check if string is already normalized
|
||||
UNormalizationCheckResult result = nfcNormalizer->quickCheck(s, status);
|
||||
// If so, return it
|
||||
if (U_SUCCESS(status) && result == UNORM_YES) {
|
||||
return s;
|
||||
}
|
||||
// Otherwise, normalize it
|
||||
UnicodeString normalized = nfcNormalizer->normalize(s, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return {};
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
MessageFormatter::MessageFormatter(const MessageFormatter::Builder& builder, UErrorCode &success) : locale(builder.locale), customMFFunctionRegistry(builder.customMFFunctionRegistry) {
|
||||
CHECK_ERROR(success);
|
||||
|
||||
@ -132,9 +153,13 @@ namespace message2 {
|
||||
.adoptFormatter(FunctionName(UnicodeString("time")), time, success)
|
||||
.adoptFormatter(FunctionName(UnicodeString("number")), number, success)
|
||||
.adoptFormatter(FunctionName(UnicodeString("integer")), integer, success)
|
||||
.adoptFormatter(FunctionName(UnicodeString("test:function")), new StandardFunctions::TestFormatFactory(), success)
|
||||
.adoptFormatter(FunctionName(UnicodeString("test:format")), new StandardFunctions::TestFormatFactory(), success)
|
||||
.adoptSelector(FunctionName(UnicodeString("number")), new StandardFunctions::PluralFactory(UPLURAL_TYPE_CARDINAL), success)
|
||||
.adoptSelector(FunctionName(UnicodeString("integer")), new StandardFunctions::PluralFactory(StandardFunctions::PluralFactory::integer()), success)
|
||||
.adoptSelector(FunctionName(UnicodeString("string")), new StandardFunctions::TextFactory(), success);
|
||||
.adoptSelector(FunctionName(UnicodeString("string")), new StandardFunctions::TextFactory(), success)
|
||||
.adoptSelector(FunctionName(UnicodeString("test:function")), new StandardFunctions::TestSelectFactory(), success)
|
||||
.adoptSelector(FunctionName(UnicodeString("test:select")), new StandardFunctions::TestSelectFactory(), success);
|
||||
CHECK_ERROR(success);
|
||||
standardMFFunctionRegistry = standardFunctionsBuilder.build();
|
||||
CHECK_ERROR(success);
|
||||
@ -163,6 +188,8 @@ namespace message2 {
|
||||
errors = errorsNew.orphan();
|
||||
}
|
||||
|
||||
nfcNormalizer = Normalizer2::getNFCInstance(success);
|
||||
|
||||
// Note: we currently evaluate variables lazily,
|
||||
// without memoization. This call is still necessary
|
||||
// to check out-of-scope uses of local variables in
|
||||
@ -170,7 +197,7 @@ namespace message2 {
|
||||
// only be checked when arguments are known)
|
||||
|
||||
// Check for resolution errors
|
||||
Checker(dataModel, *errors).check(success);
|
||||
Checker(dataModel, *errors, *this).check(success);
|
||||
}
|
||||
|
||||
void MessageFormatter::cleanup() noexcept {
|
||||
@ -191,6 +218,7 @@ namespace message2 {
|
||||
signalErrors = other.signalErrors;
|
||||
errors = other.errors;
|
||||
other.errors = nullptr;
|
||||
nfcNormalizer = other.nfcNormalizer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -256,8 +284,11 @@ namespace message2 {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
bool MessageFormatter::getDefaultFormatterNameByType(const UnicodeString& type, FunctionName& name) const {
|
||||
U_ASSERT(hasCustomMFFunctionRegistry());
|
||||
bool MessageFormatter::getDefaultFormatterNameByType(const UnicodeString& type,
|
||||
FunctionName& name) const {
|
||||
if (!hasCustomMFFunctionRegistry()) {
|
||||
return false;
|
||||
}
|
||||
const MFFunctionRegistry& reg = getCustomMFFunctionRegistry();
|
||||
return reg.getDefaultFormatterNameByType(type, name);
|
||||
}
|
||||
@ -352,3 +383,5 @@ U_NAMESPACE_END
|
||||
#endif /* #if !UCONFIG_NO_MF2 */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -85,10 +87,11 @@ MFFunctionRegistry::Builder::Builder(UErrorCode& errorCode) {
|
||||
formattersByType = new Hashtable();
|
||||
if (!(formatters != nullptr && selectors != nullptr && formattersByType != nullptr)) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
} else {
|
||||
formatters->setValueDeleter(uprv_deleteUObject);
|
||||
selectors->setValueDeleter(uprv_deleteUObject);
|
||||
formattersByType->setValueDeleter(uprv_deleteUObject);
|
||||
}
|
||||
formatters->setValueDeleter(uprv_deleteUObject);
|
||||
selectors->setValueDeleter(uprv_deleteUObject);
|
||||
formattersByType->setValueDeleter(uprv_deleteUObject);
|
||||
}
|
||||
|
||||
MFFunctionRegistry::Builder::~Builder() {
|
||||
@ -158,9 +161,13 @@ void MFFunctionRegistry::checkStandard() const {
|
||||
checkFormatter("time");
|
||||
checkFormatter("number");
|
||||
checkFormatter("integer");
|
||||
checkFormatter("test:function");
|
||||
checkFormatter("test:format");
|
||||
checkSelector("number");
|
||||
checkSelector("integer");
|
||||
checkSelector("string");
|
||||
checkSelector("test:function");
|
||||
checkSelector("test:select");
|
||||
}
|
||||
|
||||
// Formatter/selector helpers
|
||||
@ -424,14 +431,14 @@ static FormattedPlaceholder notANumber(const FormattedPlaceholder& input) {
|
||||
return FormattedPlaceholder(input, FormattedValue(UnicodeString("NaN")));
|
||||
}
|
||||
|
||||
static double parseNumberLiteral(const FormattedPlaceholder& input, UErrorCode& errorCode) {
|
||||
static double parseNumberLiteral(const Formattable& input, UErrorCode& errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Copying string to avoid GCC dangling-reference warning
|
||||
// (although the reference is safe)
|
||||
UnicodeString inputStr = input.asFormattable().getString(errorCode);
|
||||
UnicodeString inputStr = input.getString(errorCode);
|
||||
// Precondition: `input`'s source Formattable has type string
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
@ -463,8 +470,42 @@ static double parseNumberLiteral(const FormattedPlaceholder& input, UErrorCode&
|
||||
return result;
|
||||
}
|
||||
|
||||
static UChar32 digitToChar(int32_t val, UErrorCode errorCode) {
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return '0';
|
||||
}
|
||||
if (val < 0 || val > 9) {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
switch(val) {
|
||||
case 0:
|
||||
return '0';
|
||||
case 1:
|
||||
return '1';
|
||||
case 2:
|
||||
return '2';
|
||||
case 3:
|
||||
return '3';
|
||||
case 4:
|
||||
return '4';
|
||||
case 5:
|
||||
return '5';
|
||||
case 6:
|
||||
return '6';
|
||||
case 7:
|
||||
return '7';
|
||||
case 8:
|
||||
return '8';
|
||||
case 9:
|
||||
return '9';
|
||||
default:
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
||||
static FormattedPlaceholder tryParsingNumberLiteral(const number::LocalizedNumberFormatter& nf, const FormattedPlaceholder& input, UErrorCode& errorCode) {
|
||||
double numberValue = parseNumberLiteral(input, errorCode);
|
||||
double numberValue = parseNumberLiteral(input.asFormattable(), errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return notANumber(input);
|
||||
}
|
||||
@ -1235,6 +1276,273 @@ void StandardFunctions::TextSelector::selectKey(FormattedPlaceholder&& toFormat,
|
||||
StandardFunctions::TextFactory::~TextFactory() {}
|
||||
StandardFunctions::TextSelector::~TextSelector() {}
|
||||
|
||||
// ------------ TestFormatFactory
|
||||
|
||||
Formatter* StandardFunctions::TestFormatFactory::createFormatter(const Locale& locale, UErrorCode& errorCode) {
|
||||
NULL_ON_ERROR(errorCode);
|
||||
|
||||
// Results are not locale-dependent
|
||||
(void) locale;
|
||||
|
||||
Formatter* result = new TestFormat();
|
||||
if (result == nullptr) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StandardFunctions::TestFormatFactory::~TestFormatFactory() {}
|
||||
StandardFunctions::TestFormat::~TestFormat() {}
|
||||
|
||||
// Extract numeric value from a Formattable or, if it's a string,
|
||||
// parse it as a number according to the MF2 `number-literal` grammar production
|
||||
double formattableToNumber(const Formattable& arg, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double result = 0;
|
||||
|
||||
switch (arg.getType()) {
|
||||
case UFMT_DOUBLE: {
|
||||
result = arg.getDouble(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
break;
|
||||
}
|
||||
case UFMT_LONG: {
|
||||
result = (double) arg.getLong(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
break;
|
||||
}
|
||||
case UFMT_INT64: {
|
||||
result = (double) arg.getInt64(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
break;
|
||||
}
|
||||
case UFMT_STRING: {
|
||||
// Try to parse the string as a number
|
||||
result = parseNumberLiteral(arg, status);
|
||||
if (U_FAILURE(status)) {
|
||||
status = U_MF_OPERAND_MISMATCH_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Other types can't be parsed as a number
|
||||
status = U_MF_OPERAND_MISMATCH_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* static */ void StandardFunctions::TestFormat::testFunctionParameters(const FormattedPlaceholder& arg,
|
||||
const FunctionOptions& options,
|
||||
int32_t& decimalPlaces,
|
||||
bool& failsFormat,
|
||||
bool& failsSelect,
|
||||
double& input,
|
||||
UErrorCode& status) {
|
||||
CHECK_ERROR(status);
|
||||
|
||||
// 1. Let DecimalPlaces be 0.
|
||||
decimalPlaces = 0;
|
||||
|
||||
// 2. Let FailsFormat be false.
|
||||
failsFormat = false;
|
||||
|
||||
// 3. Let FailsSelect be false.
|
||||
failsSelect = false;
|
||||
|
||||
// 4. Let arg be the resolved value of the expression operand.
|
||||
// (already true)
|
||||
|
||||
// Step 5 omitted because composition isn't fully implemented yet
|
||||
// 6. Else if arg is a numerical value or a string matching the number-literal production, then
|
||||
input = formattableToNumber(arg.asFormattable(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
// 7. Else,
|
||||
// 7i. Emit "bad-input" Resolution Error.
|
||||
status = U_MF_OPERAND_MISMATCH_ERROR;
|
||||
// 7ii. Use a fallback value as the resolved value of the expression.
|
||||
// Further steps of this algorithm are not followed.
|
||||
}
|
||||
// 8. If the decimalPlaces option is set, then
|
||||
Formattable opt;
|
||||
if (options.getFunctionOption(UnicodeString("decimalPlaces"), opt)) {
|
||||
// 8i. If its value resolves to a numerical integer value 0 or 1
|
||||
// or their corresponding string representations '0' or '1', then
|
||||
double decimalPlacesInput = formattableToNumber(opt, status);
|
||||
if (U_SUCCESS(status)) {
|
||||
if (decimalPlacesInput == 0 || decimalPlacesInput == 1) {
|
||||
// 8ia. Set DecimalPlaces to be the numerical value of the option.
|
||||
decimalPlaces = decimalPlacesInput;
|
||||
}
|
||||
}
|
||||
// 8ii. Else if its value is not an unresolved value set by option resolution,
|
||||
else {
|
||||
// 8iia. Emit "bad-option" Resolution Error.
|
||||
status = U_MF_BAD_OPTION;
|
||||
// 8iib. Use a fallback value as the resolved value of the expression.
|
||||
}
|
||||
}
|
||||
// 9. If the fails option is set, then
|
||||
Formattable failsOpt;
|
||||
if (options.getFunctionOption(UnicodeString("fails"), failsOpt)) {
|
||||
UnicodeString failsString = failsOpt.getString(status);
|
||||
if (U_SUCCESS(status)) {
|
||||
// 9i. If its value resolves to the string 'always', then
|
||||
if (failsString == u"always") {
|
||||
// 9ia. Set FailsFormat to be true
|
||||
failsFormat = true;
|
||||
// 9ib. Set FailsSelect to be true.
|
||||
failsSelect = true;
|
||||
}
|
||||
// 9ii. Else if its value resolves to the string "format", then
|
||||
else if (failsString == u"format") {
|
||||
// 9ia. Set FailsFormat to be true
|
||||
failsFormat = true;
|
||||
}
|
||||
// 9iii. Else if its value resolves to the string "select", then
|
||||
else if (failsString == u"select") {
|
||||
// 9iiia. Set FailsSelect to be true.
|
||||
failsSelect = true;
|
||||
}
|
||||
// 9iv. Else if its value does not resolve to the string "never", then
|
||||
else if (failsString != u"never") {
|
||||
// 9iv(a). Emit "bad-option" Resolution Error.
|
||||
status = U_MF_BAD_OPTION;
|
||||
}
|
||||
} else {
|
||||
// 9iv. again
|
||||
status = U_MF_BAD_OPTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormattedPlaceholder StandardFunctions::TestFormat::format(FormattedPlaceholder&& arg,
|
||||
FunctionOptions&& options,
|
||||
UErrorCode& status) const{
|
||||
|
||||
int32_t decimalPlaces;
|
||||
bool failsFormat;
|
||||
bool failsSelect;
|
||||
double input;
|
||||
|
||||
testFunctionParameters(arg, options, decimalPlaces,
|
||||
failsFormat, failsSelect, input, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return FormattedPlaceholder(arg.getFallback());
|
||||
}
|
||||
|
||||
// If FailsFormat is true, attempting to format the placeholder to any
|
||||
// formatting target will fail.
|
||||
if (failsFormat) {
|
||||
status = U_MF_FORMATTING_ERROR;
|
||||
return FormattedPlaceholder(arg.getFallback());
|
||||
}
|
||||
UnicodeString result;
|
||||
// When :test:function is used as a formatter, a placeholder resolving to a value
|
||||
// with a :test:function expression is formatted as a concatenation of the following parts:
|
||||
// 1. If Input is less than 0, the character - U+002D Hyphen-Minus.
|
||||
if (input < 0) {
|
||||
result += HYPHEN;
|
||||
}
|
||||
// 2. The truncated absolute integer value of Input, i.e. floor(abs(Input)), formatted as a
|
||||
// sequence of decimal digit characters (U+0030...U+0039).
|
||||
char buffer[256];
|
||||
bool ignore;
|
||||
int ignoreLen;
|
||||
int ignorePoint;
|
||||
double_conversion::DoubleToStringConverter::DoubleToAscii(floor(abs(input)),
|
||||
double_conversion::DoubleToStringConverter::DtoaMode::SHORTEST,
|
||||
0,
|
||||
buffer,
|
||||
256,
|
||||
&ignore,
|
||||
&ignoreLen,
|
||||
&ignorePoint);
|
||||
result += UnicodeString(buffer);
|
||||
// 3. If DecimalPlaces is 1, then
|
||||
if (decimalPlaces == 1) {
|
||||
// 3i. The character . U+002E Full Stop.
|
||||
result += u".";
|
||||
// 3ii. The single decimal digit character representing the value
|
||||
// floor((abs(Input) - floor(abs(Input))) * 10)
|
||||
int32_t val = floor((abs(input) - floor(abs(input)) * 10));
|
||||
result += digitToChar(val, status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
}
|
||||
return FormattedPlaceholder(result);
|
||||
}
|
||||
|
||||
// ------------ TestSelectFactory
|
||||
|
||||
StandardFunctions::TestSelectFactory::~TestSelectFactory() {}
|
||||
StandardFunctions::TestSelect::~TestSelect() {}
|
||||
|
||||
Selector* StandardFunctions::TestSelectFactory::createSelector(const Locale& locale,
|
||||
UErrorCode& errorCode) const {
|
||||
NULL_ON_ERROR(errorCode);
|
||||
|
||||
// Results are not locale-dependent
|
||||
(void) locale;
|
||||
|
||||
Selector* result = new TestSelect();
|
||||
if (result == nullptr) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void StandardFunctions::TestSelect::selectKey(FormattedPlaceholder&& val,
|
||||
FunctionOptions&& options,
|
||||
const UnicodeString* keys,
|
||||
int32_t keysLen,
|
||||
UnicodeString* prefs,
|
||||
int32_t& prefsLen,
|
||||
UErrorCode& status) const {
|
||||
int32_t decimalPlaces;
|
||||
bool failsFormat;
|
||||
bool failsSelect;
|
||||
double input;
|
||||
|
||||
TestFormat::testFunctionParameters(val, options, decimalPlaces,
|
||||
failsFormat, failsSelect, input, status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (failsSelect) {
|
||||
status = U_MF_SELECTOR_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the Input is 1 and DecimalPlaces is 1, the method will return some slice
|
||||
// of the list « '1.0', '1' », depending on whether those values are included in keys.
|
||||
bool include1point0 = false;
|
||||
bool include1 = false;
|
||||
if (input == 1 && decimalPlaces == 1) {
|
||||
include1point0 = true;
|
||||
include1 = true;
|
||||
} else if (input == 1 && decimalPlaces == 0) {
|
||||
include1 = true;
|
||||
}
|
||||
|
||||
// If the Input is 1 and DecimalPlaces is 0, the method will return the list « '1' » if
|
||||
// keys includes '1', or an empty list otherwise.
|
||||
// If the Input is any other value, the method will return an empty list.
|
||||
for (int32_t i = 0; i < keysLen; i++) {
|
||||
if ((keys[i] == u"1" && include1)
|
||||
|| (keys[i] == u"1.0" && include1point0)) {
|
||||
prefs[prefsLen] = keys[i];
|
||||
prefsLen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace message2
|
||||
U_NAMESPACE_END
|
||||
|
||||
@ -1242,3 +1550,4 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -209,6 +211,60 @@ namespace message2 {
|
||||
|
||||
TextSelector(const Locale& l) : locale(l) {}
|
||||
};
|
||||
|
||||
// See https://github.com/unicode-org/message-format-wg/blob/main/test/README.md
|
||||
class TestFormatFactory : public FormatterFactory {
|
||||
public:
|
||||
Formatter* createFormatter(const Locale& locale, UErrorCode& status) override;
|
||||
TestFormatFactory() {}
|
||||
virtual ~TestFormatFactory();
|
||||
};
|
||||
|
||||
class TestSelect;
|
||||
|
||||
class TestFormat : public Formatter {
|
||||
public:
|
||||
FormattedPlaceholder format(FormattedPlaceholder&& toFormat, FunctionOptions&& options, UErrorCode& status) const override;
|
||||
virtual ~TestFormat();
|
||||
|
||||
private:
|
||||
friend class TestFormatFactory;
|
||||
friend class TestSelect;
|
||||
TestFormat() {}
|
||||
static void testFunctionParameters(const FormattedPlaceholder& arg,
|
||||
const FunctionOptions& options,
|
||||
int32_t& decimalPlaces,
|
||||
bool& failsFormat,
|
||||
bool& failsSelect,
|
||||
double& input,
|
||||
UErrorCode& status);
|
||||
|
||||
};
|
||||
|
||||
// See https://github.com/unicode-org/message-format-wg/blob/main/test/README.md
|
||||
class TestSelectFactory : public SelectorFactory {
|
||||
public:
|
||||
Selector* createSelector(const Locale& locale, UErrorCode& status) const override;
|
||||
TestSelectFactory() {}
|
||||
virtual ~TestSelectFactory();
|
||||
};
|
||||
|
||||
class TestSelect : public Selector {
|
||||
public:
|
||||
void selectKey(FormattedPlaceholder&& val,
|
||||
FunctionOptions&& options,
|
||||
const UnicodeString* keys,
|
||||
int32_t keysLen,
|
||||
UnicodeString* prefs,
|
||||
int32_t& prefsLen,
|
||||
UErrorCode& status) const override;
|
||||
virtual ~TestSelect();
|
||||
|
||||
private:
|
||||
friend class TestSelectFactory;
|
||||
TestSelect() {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
extern void formatDateWithDefaults(const Locale& locale, UDate date, UnicodeString&, UErrorCode& errorCode);
|
||||
@ -226,6 +282,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT2_FUNCTION_REGISTRY_INTERNAL_H
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -97,6 +99,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT2_MACROS_H
|
||||
|
574
deps/icu-small/source/i18n/messageformat2_parser.cpp
vendored
574
deps/icu-small/source/i18n/messageformat2_parser.cpp
vendored
@ -3,13 +3,18 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
|
||||
#include "unicode/uniset.h"
|
||||
#include "messageformat2_errors.h"
|
||||
#include "messageformat2_macros.h"
|
||||
#include "messageformat2_parser.h"
|
||||
#include "ucln_in.h"
|
||||
#include "umutex.h"
|
||||
#include "uvector.h" // U_ASSERT
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
@ -91,14 +96,282 @@ static void copyContext(const UChar in[U_PARSE_CONTEXT_LEN], UChar out[U_PARSE_C
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// Predicates
|
||||
// Initialization of UnicodeSets
|
||||
|
||||
// Returns true if `c` is in the interval [`first`, `last`]
|
||||
static bool inRange(UChar32 c, UChar32 first, UChar32 last) {
|
||||
U_ASSERT(first < last);
|
||||
return c >= first && c <= last;
|
||||
namespace unisets {
|
||||
|
||||
UnicodeSet* gUnicodeSets[unisets::UNISETS_KEY_COUNT] = {};
|
||||
|
||||
inline UnicodeSet* getImpl(Key key) {
|
||||
return gUnicodeSets[key];
|
||||
}
|
||||
|
||||
icu::UInitOnce gMF2ParseUniSetsInitOnce {};
|
||||
}
|
||||
|
||||
UnicodeSet* initContentChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* result = new UnicodeSet(0x0001, 0x0008); // Omit NULL, HTAB and LF
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
result->add(0x000B, 0x000C); // Omit CR
|
||||
result->add(0x000E, 0x001F); // Omit SP
|
||||
result->add(0x0021, 0x002D); // Omit '.'
|
||||
result->add(0x002F, 0x003F); // Omit '@'
|
||||
result->add(0x0041, 0x005B); // Omit '\'
|
||||
result->add(0x005D, 0x007A); // Omit { | }
|
||||
result->add(0x007E, 0x2FFF); // Omit IDEOGRAPHIC_SPACE
|
||||
result->add(0x3001, 0x10FFFF); // Allowing surrogates is intentional
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initWhitespace(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* result = new UnicodeSet();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
result->add(SPACE);
|
||||
result->add(HTAB);
|
||||
result->add(CR);
|
||||
result->add(LF);
|
||||
result->add(IDEOGRAPHIC_SPACE);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initBidiControls(UErrorCode& status) {
|
||||
UnicodeSet* result = new UnicodeSet(UnicodeString("[\\u061C]"), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
result->add(0x200E, 0x200F);
|
||||
result->add(0x2066, 0x2069);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initAlpha(UErrorCode& status) {
|
||||
UnicodeSet* result = new UnicodeSet(UnicodeString("[:letter:]"), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initDigits(UErrorCode& status) {
|
||||
UnicodeSet* result = new UnicodeSet(UnicodeString("[:number:]"), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initNameStartChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* isAlpha = unisets::gUnicodeSets[unisets::ALPHA] = initAlpha(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeSet* result = new UnicodeSet(*isAlpha);
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
};
|
||||
result->add(UNDERSCORE);
|
||||
result->add(0x00C0, 0x00D6);
|
||||
result->add(0x00D8, 0x00F6);
|
||||
result->add(0x00F8, 0x02FF);
|
||||
result->add(0x0370, 0x037D);
|
||||
result->add(0x037F, 0x061B);
|
||||
result->add(0x061D, 0x1FFF);
|
||||
result->add(0x200C, 0x200D);
|
||||
result->add(0x2070, 0x218F);
|
||||
result->add(0x2C00, 0x2FEF);
|
||||
result->add(0x3001, 0xD7FF);
|
||||
result->add(0xF900, 0xFDCF);
|
||||
result->add(0xFDF0, 0xFFFD);
|
||||
result->add(0x100000, 0xEFFFF);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initNameChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* nameStart = unisets::gUnicodeSets[unisets::NAME_START] = initNameStartChars(status);
|
||||
UnicodeSet* digit = unisets::gUnicodeSets[unisets::DIGIT] = initDigits(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeSet* result = new UnicodeSet();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
};
|
||||
result->addAll(*nameStart);
|
||||
result->addAll(*digit);
|
||||
result->add(HYPHEN);
|
||||
result->add(PERIOD);
|
||||
result->add(0x00B7);
|
||||
result->add(0x0300, 0x036F);
|
||||
result->add(0x203F, 0x2040);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initTextChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* content = unisets::gUnicodeSets[unisets::CONTENT] = initContentChars(status);
|
||||
UnicodeSet* whitespace = unisets::gUnicodeSets[unisets::WHITESPACE] = initWhitespace(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeSet* result = new UnicodeSet();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
};
|
||||
result->addAll(*content);
|
||||
result->addAll(*whitespace);
|
||||
result->add(PERIOD);
|
||||
result->add(AT);
|
||||
result->add(PIPE);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initQuotedChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unisets::gUnicodeSets[unisets::TEXT] = initTextChars(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeSet* result = new UnicodeSet();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
};
|
||||
// content and whitespace were initialized by `initTextChars()`
|
||||
UnicodeSet* content = unisets::getImpl(unisets::CONTENT);
|
||||
if (content == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
result->addAll(*content);
|
||||
UnicodeSet* whitespace = unisets::getImpl(unisets::WHITESPACE);
|
||||
if (whitespace == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
result->addAll(*whitespace);
|
||||
result->add(PERIOD);
|
||||
result->add(AT);
|
||||
result->add(LEFT_CURLY_BRACE);
|
||||
result->add(RIGHT_CURLY_BRACE);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
UnicodeSet* initEscapableChars(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnicodeSet* result = new UnicodeSet();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
result->add(PIPE);
|
||||
result->add(BACKSLASH);
|
||||
result->add(LEFT_CURLY_BRACE);
|
||||
result->add(RIGHT_CURLY_BRACE);
|
||||
result->freeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace unisets {
|
||||
|
||||
UBool U_CALLCONV cleanupMF2ParseUniSets() {
|
||||
for (int32_t i = 0; i < UNISETS_KEY_COUNT; i++) {
|
||||
delete gUnicodeSets[i];
|
||||
gUnicodeSets[i] = nullptr;
|
||||
}
|
||||
gMF2ParseUniSetsInitOnce.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void U_CALLCONV initMF2ParseUniSets(UErrorCode& status) {
|
||||
ucln_i18n_registerCleanup(UCLN_I18N_MF2_UNISETS, cleanupMF2ParseUniSets);
|
||||
/*
|
||||
Each of the init functions initializes the UnicodeSets
|
||||
that it depends on.
|
||||
|
||||
initBidiControls (no dependencies)
|
||||
|
||||
initEscapableChars (no dependencies)
|
||||
|
||||
initNameChars depends on
|
||||
initDigits
|
||||
initNameStartChars depends on
|
||||
initAlpha
|
||||
|
||||
initQuotedChars depends on
|
||||
initTextChars depends on
|
||||
initContentChars
|
||||
initWhitespace
|
||||
*/
|
||||
gUnicodeSets[unisets::BIDI] = initBidiControls(status);
|
||||
gUnicodeSets[unisets::NAME_CHAR] = initNameChars(status);
|
||||
gUnicodeSets[unisets::QUOTED] = initQuotedChars(status);
|
||||
gUnicodeSets[unisets::ESCAPABLE] = initEscapableChars(status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
cleanupMF2ParseUniSets();
|
||||
}
|
||||
}
|
||||
|
||||
const UnicodeSet* get(Key key, UErrorCode& status) {
|
||||
umtx_initOnce(gMF2ParseUniSetsInitOnce, &initMF2ParseUniSets, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeSet* result = getImpl(key);
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// Predicates
|
||||
|
||||
/*
|
||||
The following helper predicates should exactly match nonterminals in the MessageFormat 2 grammar:
|
||||
|
||||
@ -113,76 +386,50 @@ static bool inRange(UChar32 c, UChar32 first, UChar32 last) {
|
||||
`isWhitespace()` : `s`
|
||||
*/
|
||||
|
||||
static bool isContentChar(UChar32 c) {
|
||||
return inRange(c, 0x0001, 0x0008) // Omit NULL, HTAB and LF
|
||||
|| inRange(c, 0x000B, 0x000C) // Omit CR
|
||||
|| inRange(c, 0x000E, 0x001F) // Omit SP
|
||||
|| inRange(c, 0x0021, 0x002D) // Omit '.'
|
||||
|| inRange(c, 0x002F, 0x003F) // Omit '@'
|
||||
|| inRange(c, 0x0041, 0x005B) // Omit '\'
|
||||
|| inRange(c, 0x005D, 0x007A) // Omit { | }
|
||||
|| inRange(c, 0x007E, 0xD7FF) // Omit surrogates
|
||||
|| inRange(c, 0xE000, 0x10FFFF);
|
||||
bool Parser::isContentChar(UChar32 c) const {
|
||||
return contentChars->contains(c);
|
||||
}
|
||||
|
||||
// See `s` in the MessageFormat 2 grammar
|
||||
inline bool isWhitespace(UChar32 c) {
|
||||
switch (c) {
|
||||
case SPACE:
|
||||
case HTAB:
|
||||
case CR:
|
||||
case LF:
|
||||
case IDEOGRAPHIC_SPACE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// See `bidi` in the MF2 grammar
|
||||
bool Parser::isBidiControl(UChar32 c) const {
|
||||
return bidiControlChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isTextChar(UChar32 c) {
|
||||
return isContentChar(c)
|
||||
|| isWhitespace(c)
|
||||
|| c == PERIOD
|
||||
|| c == AT
|
||||
|| c == PIPE;
|
||||
// See `ws` in the MessageFormat 2 grammar
|
||||
bool Parser::isWhitespace(UChar32 c) const {
|
||||
return whitespaceChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isAlpha(UChar32 c) { return inRange(c, 0x0041, 0x005A) || inRange(c, 0x0061, 0x007A); }
|
||||
|
||||
static bool isDigit(UChar32 c) { return inRange(c, 0x0030, 0x0039); }
|
||||
|
||||
static bool isNameStart(UChar32 c) {
|
||||
return isAlpha(c) || c == UNDERSCORE || inRange(c, 0x00C0, 0x00D6) || inRange(c, 0x00D8, 0x00F6) ||
|
||||
inRange(c, 0x00F8, 0x02FF) || inRange(c, 0x0370, 0x037D) || inRange(c, 0x037F, 0x1FFF) ||
|
||||
inRange(c, 0x200C, 0x200D) || inRange(c, 0x2070, 0x218F) || inRange(c, 0x2C00, 0x2FEF) ||
|
||||
inRange(c, 0x3001, 0xD7FF) || inRange(c, 0xF900, 0xFDCF) || inRange(c, 0xFDF0, 0xFFFD) ||
|
||||
inRange(c, 0x10000, 0xEFFFF);
|
||||
bool Parser::isTextChar(UChar32 c) const {
|
||||
return textChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isNameChar(UChar32 c) {
|
||||
return isNameStart(c) || isDigit(c) || c == HYPHEN || c == PERIOD || c == 0x00B7 ||
|
||||
inRange(c, 0x0300, 0x036F) || inRange(c, 0x203F, 0x2040);
|
||||
bool Parser::isAlpha(UChar32 c) const {
|
||||
return alphaChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isUnquotedStart(UChar32 c) {
|
||||
return isNameStart(c) || isDigit(c) || c == HYPHEN || c == PERIOD || c == 0x00B7 ||
|
||||
inRange(c, 0x0300, 0x036F) || inRange(c, 0x203F, 0x2040);
|
||||
bool Parser::isDigit(UChar32 c) const {
|
||||
return digitChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isQuotedChar(UChar32 c) {
|
||||
return isContentChar(c)
|
||||
|| isWhitespace(c)
|
||||
|| c == PERIOD
|
||||
|| c == AT
|
||||
|| c == LEFT_CURLY_BRACE
|
||||
|| c == RIGHT_CURLY_BRACE;
|
||||
bool Parser::isNameStart(UChar32 c) const {
|
||||
return nameStartChars->contains(c);
|
||||
}
|
||||
|
||||
static bool isEscapableChar(UChar32 c) {
|
||||
return c == PIPE
|
||||
|| c == BACKSLASH
|
||||
|| c == LEFT_CURLY_BRACE
|
||||
|| c == RIGHT_CURLY_BRACE;
|
||||
bool Parser::isNameChar(UChar32 c) const {
|
||||
return nameChars->contains(c);
|
||||
}
|
||||
|
||||
bool Parser::isUnquotedStart(UChar32 c) const {
|
||||
return isNameChar(c);
|
||||
}
|
||||
|
||||
bool Parser::isQuotedChar(UChar32 c) const {
|
||||
return quotedChars->contains(c);
|
||||
}
|
||||
|
||||
bool Parser::isEscapableChar(UChar32 c) const {
|
||||
return escapableChars->contains(c);
|
||||
}
|
||||
|
||||
// Returns true iff `c` can begin a `function` nonterminal
|
||||
@ -203,12 +450,12 @@ static bool isAnnotationStart(UChar32 c) {
|
||||
}
|
||||
|
||||
// Returns true iff `c` can begin a `literal` nonterminal
|
||||
static bool isLiteralStart(UChar32 c) {
|
||||
bool Parser::isLiteralStart(UChar32 c) const {
|
||||
return (c == PIPE || isNameStart(c) || c == HYPHEN || isDigit(c));
|
||||
}
|
||||
|
||||
// Returns true iff `c` can begin a `key` nonterminal
|
||||
static bool isKeyStart(UChar32 c) {
|
||||
bool Parser::isKeyStart(UChar32 c) const {
|
||||
return (c == ASTERISK || isLiteralStart(c));
|
||||
}
|
||||
|
||||
@ -347,7 +594,7 @@ option, or the optional space before an attribute.
|
||||
No pre, no post.
|
||||
A message may end with whitespace, so `index` may equal `len()` on exit.
|
||||
*/
|
||||
void Parser::parseWhitespaceMaybeRequired(bool required, UErrorCode& errorCode) {
|
||||
void Parser::parseRequiredWS(UErrorCode& errorCode) {
|
||||
bool sawWhitespace = false;
|
||||
|
||||
// The loop exits either when we consume all the input,
|
||||
@ -358,7 +605,7 @@ void Parser::parseWhitespaceMaybeRequired(bool required, UErrorCode& errorCode)
|
||||
// If whitespace isn't required -- or if we saw it already --
|
||||
// then the caller is responsible for checking this case and
|
||||
// setting an error if necessary.
|
||||
if (!required || sawWhitespace) {
|
||||
if (sawWhitespace) {
|
||||
// Not an error.
|
||||
return;
|
||||
}
|
||||
@ -380,24 +627,51 @@ void Parser::parseWhitespaceMaybeRequired(bool required, UErrorCode& errorCode)
|
||||
}
|
||||
}
|
||||
|
||||
if (!sawWhitespace && required) {
|
||||
if (!sawWhitespace) {
|
||||
ERROR(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parseOptionalBidi() {
|
||||
while (true) {
|
||||
if (!inBounds()) {
|
||||
return;
|
||||
}
|
||||
if (isBidiControl(peek())) {
|
||||
next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
No pre, no post, for the same reason as `parseWhitespaceMaybeRequired()`.
|
||||
No pre, no post, because a message may end with whitespace
|
||||
Matches `s` in the MF2 grammar
|
||||
*/
|
||||
void Parser::parseRequiredWhitespace(UErrorCode& errorCode) {
|
||||
parseWhitespaceMaybeRequired(true, errorCode);
|
||||
parseOptionalBidi();
|
||||
parseRequiredWS(errorCode);
|
||||
parseOptionalWhitespace();
|
||||
normalizedInput += SPACE;
|
||||
}
|
||||
|
||||
/*
|
||||
No pre, no post, for the same reason as `parseWhitespaceMaybeRequired()`.
|
||||
*/
|
||||
void Parser::parseOptionalWhitespace(UErrorCode& errorCode) {
|
||||
parseWhitespaceMaybeRequired(false, errorCode);
|
||||
void Parser::parseOptionalWhitespace() {
|
||||
while (true) {
|
||||
if (!inBounds()) {
|
||||
return;
|
||||
}
|
||||
auto cp = peek();
|
||||
if (isWhitespace(cp) || isBidiControl(cp)) {
|
||||
maybeAdvanceLine();
|
||||
next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consumes a single character, signaling an error if `peek()` != `c`
|
||||
@ -442,11 +716,11 @@ void Parser::parseToken(const std::u16string_view& token, UErrorCode& errorCode)
|
||||
*/
|
||||
void Parser::parseTokenWithWhitespace(const std::u16string_view& token, UErrorCode& errorCode) {
|
||||
// No need for error check or bounds check before parseOptionalWhitespace
|
||||
parseOptionalWhitespace(errorCode);
|
||||
parseOptionalWhitespace();
|
||||
// Establish precondition
|
||||
CHECK_BOUNDS(errorCode);
|
||||
parseToken(token, errorCode);
|
||||
parseOptionalWhitespace(errorCode);
|
||||
parseOptionalWhitespace();
|
||||
// Guarantee postcondition
|
||||
CHECK_BOUNDS(errorCode);
|
||||
}
|
||||
@ -458,12 +732,12 @@ void Parser::parseTokenWithWhitespace(const std::u16string_view& token, UErrorCo
|
||||
then consumes optional whitespace again
|
||||
*/
|
||||
void Parser::parseTokenWithWhitespace(UChar32 c, UErrorCode& errorCode) {
|
||||
// No need for error check or bounds check before parseOptionalWhitespace(errorCode)
|
||||
parseOptionalWhitespace(errorCode);
|
||||
// No need for error check or bounds check before parseOptionalWhitespace()
|
||||
parseOptionalWhitespace();
|
||||
// Establish precondition
|
||||
CHECK_BOUNDS(errorCode);
|
||||
parseToken(c, errorCode);
|
||||
parseOptionalWhitespace(errorCode);
|
||||
parseOptionalWhitespace();
|
||||
// Guarantee postcondition
|
||||
CHECK_BOUNDS(errorCode);
|
||||
}
|
||||
@ -482,11 +756,17 @@ UnicodeString Parser::parseName(UErrorCode& errorCode) {
|
||||
|
||||
U_ASSERT(inBounds());
|
||||
|
||||
if (!isNameStart(peek())) {
|
||||
if (!(isNameStart(peek()) || isBidiControl(peek()))) {
|
||||
ERROR(errorCode);
|
||||
return name;
|
||||
}
|
||||
|
||||
// name = [bidi] name-start *name-char [bidi]
|
||||
|
||||
// [bidi]
|
||||
parseOptionalBidi();
|
||||
|
||||
// name-start *name-char
|
||||
while (isNameChar(peek())) {
|
||||
UChar32 c = peek();
|
||||
name += c;
|
||||
@ -497,6 +777,10 @@ UnicodeString Parser::parseName(UErrorCode& errorCode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// [bidi]
|
||||
parseOptionalBidi();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -510,21 +794,13 @@ VariableName Parser::parseVariableName(UErrorCode& errorCode) {
|
||||
VariableName result;
|
||||
|
||||
U_ASSERT(inBounds());
|
||||
// If the '$' is missing, we don't want a binding
|
||||
// for this variable to be created.
|
||||
bool valid = peek() == DOLLAR;
|
||||
|
||||
parseToken(DOLLAR, errorCode);
|
||||
if (!inBounds()) {
|
||||
ERROR(errorCode);
|
||||
return result;
|
||||
}
|
||||
UnicodeString varName = parseName(errorCode);
|
||||
// Set the name to "" if the variable wasn't
|
||||
// declared correctly
|
||||
if (!valid) {
|
||||
varName.remove();
|
||||
}
|
||||
return VariableName(varName);
|
||||
return VariableName(parseName(errorCode));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -853,7 +1129,7 @@ void Parser::parseAttribute(AttributeAdder<T>& attrAdder, UErrorCode& errorCode)
|
||||
// about whether whitespace precedes another
|
||||
// attribute, or the '=' sign
|
||||
int32_t savedIndex = index;
|
||||
parseOptionalWhitespace(errorCode);
|
||||
parseOptionalWhitespace();
|
||||
|
||||
Operand rand;
|
||||
if (peek() == EQUALS) {
|
||||
@ -861,19 +1137,9 @@ void Parser::parseAttribute(AttributeAdder<T>& attrAdder, UErrorCode& errorCode)
|
||||
parseTokenWithWhitespace(EQUALS, errorCode);
|
||||
|
||||
UnicodeString rhsStr;
|
||||
// Parse RHS, which is either a literal or variable
|
||||
switch (peek()) {
|
||||
case DOLLAR: {
|
||||
rand = Operand(parseVariableName(errorCode));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Must be a literal
|
||||
rand = Operand(parseLiteral(errorCode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
U_ASSERT(!rand.isNull());
|
||||
// Parse RHS, which must be a literal
|
||||
// attribute = "@" identifier [o "=" o literal]
|
||||
rand = Operand(parseLiteral(errorCode));
|
||||
} else {
|
||||
// attribute -> "@" identifier [[s] "=" [s]]
|
||||
// Use null operand, which `rand` is already set to
|
||||
@ -881,7 +1147,7 @@ void Parser::parseAttribute(AttributeAdder<T>& attrAdder, UErrorCode& errorCode)
|
||||
index = savedIndex;
|
||||
}
|
||||
|
||||
attrAdder.addAttribute(lhs, std::move(rand), errorCode);
|
||||
attrAdder.addAttribute(lhs, std::move(Operand(rand)), errorCode);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1149,7 +1415,7 @@ the comment in `parseOptions()` for details.
|
||||
// (the character is either the required space before an annotation, or optional
|
||||
// trailing space after the literal or variable). It's still ambiguous which
|
||||
// one does apply.
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
// Restore precondition
|
||||
CHECK_BOUNDS(status);
|
||||
|
||||
@ -1220,7 +1486,7 @@ Expression Parser::parseExpression(UErrorCode& status) {
|
||||
// Parse opening brace
|
||||
parseToken(LEFT_CURLY_BRACE, status);
|
||||
// Optional whitespace after opening brace
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
|
||||
Expression::Builder exprBuilder(status);
|
||||
// Restore precondition
|
||||
@ -1263,7 +1529,7 @@ Expression Parser::parseExpression(UErrorCode& status) {
|
||||
|
||||
// Parse optional space
|
||||
// (the last [s] in e.g. "{" [s] literal [s annotation] *(s attribute) [s] "}")
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
|
||||
// Either an operand or operator (or both) must have been set already,
|
||||
// so there can't be an error
|
||||
@ -1339,7 +1605,7 @@ void Parser::parseInputDeclaration(UErrorCode& status) {
|
||||
CHECK_BOUNDS(status);
|
||||
|
||||
parseToken(ID_INPUT, status);
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
|
||||
// Restore precondition before calling parseExpression()
|
||||
CHECK_BOUNDS(status);
|
||||
@ -1400,7 +1666,7 @@ void Parser::parseDeclarations(UErrorCode& status) {
|
||||
// Avoid looping infinitely
|
||||
CHECK_ERROR(status);
|
||||
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
// Restore precondition
|
||||
CHECK_BOUNDS(status);
|
||||
}
|
||||
@ -1510,8 +1776,8 @@ This is addressed using "backtracking" (similarly to `parseOptions()`).
|
||||
|
||||
// We've seen at least one whitespace-key pair, so now we can parse
|
||||
// *(s key) [s]
|
||||
while (peek() != LEFT_CURLY_BRACE || isWhitespace(peek())) { // Try to recover from errors
|
||||
bool wasWhitespace = isWhitespace(peek());
|
||||
while (peek() != LEFT_CURLY_BRACE || isWhitespace(peek()) || isBidiControl(peek())) {
|
||||
bool wasWhitespace = isWhitespace(peek()) || isBidiControl(peek());
|
||||
parseRequiredWhitespace(status);
|
||||
if (!wasWhitespace) {
|
||||
// Avoid infinite loop when parsing something like:
|
||||
@ -1569,7 +1835,7 @@ Markup Parser::parseMarkup(UErrorCode& status) {
|
||||
// Consume the '{'
|
||||
next();
|
||||
normalizedInput += LEFT_CURLY_BRACE;
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
bool closing = false;
|
||||
switch (peek()) {
|
||||
case NUMBER_SIGN: {
|
||||
@ -1596,19 +1862,19 @@ Markup Parser::parseMarkup(UErrorCode& status) {
|
||||
|
||||
// Parse the options, which must begin with a ' '
|
||||
// if present
|
||||
if (inBounds() && isWhitespace(peek())) {
|
||||
if (inBounds() && (isWhitespace(peek()) || isBidiControl(peek()))) {
|
||||
OptionAdder<Markup::Builder> optionAdder(builder);
|
||||
parseOptions(optionAdder, status);
|
||||
}
|
||||
|
||||
// Parse the attributes, which also must begin
|
||||
// with a ' '
|
||||
if (inBounds() && isWhitespace(peek())) {
|
||||
if (inBounds() && (isWhitespace(peek()) || isBidiControl(peek()))) {
|
||||
AttributeAdder<Markup::Builder> attrAdder(builder);
|
||||
parseAttributes(attrAdder, status);
|
||||
}
|
||||
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
|
||||
bool standalone = false;
|
||||
// Check if this is a standalone or not
|
||||
@ -1656,7 +1922,7 @@ std::variant<Expression, Markup> Parser::parsePlaceholder(UErrorCode& status) {
|
||||
isMarkup = true;
|
||||
break;
|
||||
}
|
||||
if (!isWhitespace(c)){
|
||||
if (!(isWhitespace(c) || isBidiControl(c))) {
|
||||
break;
|
||||
}
|
||||
tempIndex++;
|
||||
@ -1712,7 +1978,7 @@ Pattern Parser::parseSimpleMessage(UErrorCode& status) {
|
||||
break;
|
||||
}
|
||||
// Don't loop infinitely
|
||||
if (errors.hasSyntaxError()) {
|
||||
if (errors.hasSyntaxError() || U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1720,6 +1986,22 @@ Pattern Parser::parseSimpleMessage(UErrorCode& status) {
|
||||
return result.build(status);
|
||||
}
|
||||
|
||||
void Parser::parseVariant(UErrorCode& status) {
|
||||
CHECK_ERROR(status);
|
||||
|
||||
// At least one key is required
|
||||
SelectorKeys keyList(parseNonEmptyKeys(status));
|
||||
|
||||
// parseNonEmptyKeys() consumes any trailing whitespace,
|
||||
// so the pattern can be consumed next.
|
||||
|
||||
// Restore precondition before calling parsePattern()
|
||||
// (which must return a non-null value)
|
||||
CHECK_BOUNDS(status);
|
||||
Pattern rhs = parseQuotedPattern(status);
|
||||
|
||||
dataModel.addVariant(std::move(keyList), std::move(rhs), status);
|
||||
}
|
||||
|
||||
/*
|
||||
Consume a `selectors` (matching the nonterminal in the grammar),
|
||||
@ -1739,22 +2021,25 @@ void Parser::parseSelectors(UErrorCode& status) {
|
||||
// Parse selectors
|
||||
// "Backtracking" is required here. It's not clear if whitespace is
|
||||
// (`[s]` selector) or (`[s]` variant)
|
||||
while (isWhitespace(peek()) || peek() == LEFT_CURLY_BRACE) {
|
||||
parseOptionalWhitespace(status);
|
||||
while (isWhitespace(peek()) || peek() == DOLLAR) {
|
||||
int32_t whitespaceStart = index;
|
||||
parseRequiredWhitespace(status);
|
||||
// Restore precondition
|
||||
CHECK_BOUNDS(status);
|
||||
if (peek() != LEFT_CURLY_BRACE) {
|
||||
if (peek() != DOLLAR) {
|
||||
// This is not necessarily an error, but rather,
|
||||
// means the whitespace we parsed was the optional
|
||||
// whitespace preceding the first variant, not the
|
||||
// optional whitespace preceding a subsequent expression.
|
||||
// required whitespace preceding a subsequent variable.
|
||||
// In that case, "push back" the whitespace.
|
||||
normalizedInput.truncate(normalizedInput.length() - 1);
|
||||
index = whitespaceStart;
|
||||
break;
|
||||
}
|
||||
Expression expression;
|
||||
expression = parseExpression(status);
|
||||
VariableName var = parseVariableName(status);
|
||||
empty = false;
|
||||
|
||||
dataModel.addSelector(std::move(expression), status);
|
||||
dataModel.addSelector(std::move(var), status);
|
||||
CHECK_ERROR(status);
|
||||
}
|
||||
|
||||
@ -1770,27 +2055,29 @@ void Parser::parseSelectors(UErrorCode& status) {
|
||||
} \
|
||||
|
||||
// Parse variants
|
||||
while (isWhitespace(peek()) || isKeyStart(peek())) {
|
||||
// Trailing whitespace is allowed
|
||||
parseOptionalWhitespace(status);
|
||||
// matcher = match-statement s variant *(o variant)
|
||||
|
||||
// Parse first variant
|
||||
parseRequiredWhitespace(status);
|
||||
if (!inBounds()) {
|
||||
ERROR(status);
|
||||
return;
|
||||
}
|
||||
parseVariant(status);
|
||||
if (!inBounds()) {
|
||||
// Not an error; there might be only one variant
|
||||
return;
|
||||
}
|
||||
|
||||
while (isWhitespace(peek()) || isBidiControl(peek()) || isKeyStart(peek())) {
|
||||
parseOptionalWhitespace();
|
||||
// Restore the precondition.
|
||||
// Trailing whitespace is allowed.
|
||||
if (!inBounds()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one key is required
|
||||
SelectorKeys keyList(parseNonEmptyKeys(status));
|
||||
|
||||
CHECK_ERROR(status);
|
||||
|
||||
// parseNonEmptyKeys() consumes any trailing whitespace,
|
||||
// so the pattern can be consumed next.
|
||||
|
||||
// Restore precondition before calling parsePattern()
|
||||
// (which must return a non-null value)
|
||||
CHECK_BOUNDS(status);
|
||||
Pattern rhs = parseQuotedPattern(status);
|
||||
|
||||
dataModel.addVariant(std::move(keyList), std::move(rhs), status);
|
||||
parseVariant(status);
|
||||
|
||||
// Restore the precondition, *without* erroring out if we've
|
||||
// reached the end of input. That's because it's valid for the
|
||||
@ -1799,6 +2086,10 @@ void Parser::parseSelectors(UErrorCode& status) {
|
||||
// Because if we don't check it here, the `isWhitespace()` call in
|
||||
// the loop head will read off the end of the input string.
|
||||
CHECK_END_OF_INPUT
|
||||
|
||||
if (errors.hasSyntaxError() || U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1871,7 +2162,7 @@ void Parser::parse(UParseError &parseErrorResult, UErrorCode& status) {
|
||||
bool complex = false;
|
||||
// First, "look ahead" to determine if this is a simple or complex
|
||||
// message. To do that, check the first non-whitespace character.
|
||||
while (inBounds(index) && isWhitespace(peek())) {
|
||||
while (inBounds(index) && (isWhitespace(peek()) || isBidiControl(peek()))) {
|
||||
next();
|
||||
}
|
||||
|
||||
@ -1891,10 +2182,10 @@ void Parser::parse(UParseError &parseErrorResult, UErrorCode& status) {
|
||||
// Message can be empty, so we need to only look ahead
|
||||
// if we know it's non-empty
|
||||
if (complex) {
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
parseDeclarations(status);
|
||||
parseBody(status);
|
||||
parseOptionalWhitespace(status);
|
||||
parseOptionalWhitespace();
|
||||
} else {
|
||||
// Simple message
|
||||
// For normalization, quote the pattern
|
||||
@ -1926,3 +2217,4 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -10,12 +10,15 @@
|
||||
|
||||
#include "unicode/messageformat2_data_model.h"
|
||||
#include "unicode/parseerr.h"
|
||||
#include "unicode/uniset.h"
|
||||
|
||||
#include "messageformat2_allocation.h"
|
||||
#include "messageformat2_errors.h"
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -54,6 +57,26 @@ namespace message2 {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Initialization of UnicodeSets
|
||||
namespace unisets {
|
||||
enum Key {
|
||||
CONTENT,
|
||||
WHITESPACE,
|
||||
BIDI,
|
||||
ALPHA,
|
||||
DIGIT,
|
||||
NAME_START,
|
||||
NAME_CHAR,
|
||||
TEXT,
|
||||
QUOTED,
|
||||
ESCAPABLE,
|
||||
UNISETS_KEY_COUNT
|
||||
};
|
||||
|
||||
U_I18N_API const UnicodeSet* get(Key key, UErrorCode& status);
|
||||
}
|
||||
|
||||
// Parser class (private)
|
||||
class Parser : public UMemory {
|
||||
public:
|
||||
@ -82,8 +105,23 @@ namespace message2 {
|
||||
UChar postContext[U_PARSE_CONTEXT_LEN];
|
||||
} MessageParseError;
|
||||
|
||||
Parser(const UnicodeString &input, MFDataModel::Builder& dataModelBuilder, StaticErrors& e, UnicodeString& normalizedInputRef)
|
||||
: source(input), index(0), errors(e), normalizedInput(normalizedInputRef), dataModel(dataModelBuilder) {
|
||||
Parser(const UnicodeString &input,
|
||||
MFDataModel::Builder& dataModelBuilder,
|
||||
StaticErrors& e,
|
||||
UnicodeString& normalizedInputRef,
|
||||
UErrorCode& status)
|
||||
: contentChars(unisets::get(unisets::CONTENT, status)),
|
||||
whitespaceChars(unisets::get(unisets::WHITESPACE, status)),
|
||||
bidiControlChars(unisets::get(unisets::BIDI, status)),
|
||||
alphaChars(unisets::get(unisets::ALPHA, status)),
|
||||
digitChars(unisets::get(unisets::DIGIT, status)),
|
||||
nameStartChars(unisets::get(unisets::NAME_START, status)),
|
||||
nameChars(unisets::get(unisets::NAME_CHAR, status)),
|
||||
textChars(unisets::get(unisets::TEXT, status)),
|
||||
quotedChars(unisets::get(unisets::QUOTED, status)),
|
||||
escapableChars(unisets::get(unisets::ESCAPABLE, status)),
|
||||
source(input), index(0), errors(e), normalizedInput(normalizedInputRef), dataModel(dataModelBuilder) {
|
||||
(void) status;
|
||||
parseError.line = 0;
|
||||
parseError.offset = 0;
|
||||
parseError.lengthBeforeCurrentLine = 0;
|
||||
@ -91,6 +129,20 @@ namespace message2 {
|
||||
parseError.postContext[0] = '\0';
|
||||
}
|
||||
|
||||
bool isContentChar(UChar32) const;
|
||||
bool isBidiControl(UChar32) const;
|
||||
bool isWhitespace(UChar32) const;
|
||||
bool isTextChar(UChar32) const;
|
||||
bool isQuotedChar(UChar32) const;
|
||||
bool isEscapableChar(UChar32) const;
|
||||
bool isAlpha(UChar32) const;
|
||||
bool isDigit(UChar32) const;
|
||||
bool isNameStart(UChar32) const;
|
||||
bool isNameChar(UChar32) const;
|
||||
bool isUnquotedStart(UChar32) const;
|
||||
bool isLiteralStart(UChar32) const;
|
||||
bool isKeyStart(UChar32) const;
|
||||
|
||||
static void translateParseError(const MessageParseError&, UParseError&);
|
||||
static void setParseError(MessageParseError&, uint32_t);
|
||||
void maybeAdvanceLine();
|
||||
@ -100,11 +152,13 @@ namespace message2 {
|
||||
void parseUnsupportedStatement(UErrorCode&);
|
||||
void parseLocalDeclaration(UErrorCode&);
|
||||
void parseInputDeclaration(UErrorCode&);
|
||||
void parseSelectors(UErrorCode&);
|
||||
void parseSelectors(UErrorCode&);
|
||||
void parseVariant(UErrorCode&);
|
||||
|
||||
void parseWhitespaceMaybeRequired(bool, UErrorCode&);
|
||||
void parseRequiredWS(UErrorCode&);
|
||||
void parseRequiredWhitespace(UErrorCode&);
|
||||
void parseOptionalWhitespace(UErrorCode&);
|
||||
void parseOptionalBidi();
|
||||
void parseOptionalWhitespace();
|
||||
void parseToken(UChar32, UErrorCode&);
|
||||
void parseTokenWithWhitespace(UChar32, UErrorCode&);
|
||||
void parseToken(const std::u16string_view&, UErrorCode&);
|
||||
@ -149,6 +203,18 @@ namespace message2 {
|
||||
bool inBounds(uint32_t i) const { return source.moveIndex32(index, i) < source.length(); }
|
||||
bool allConsumed() const { return (int32_t) index == source.length(); }
|
||||
|
||||
// UnicodeSets for checking character ranges
|
||||
const UnicodeSet* contentChars;
|
||||
const UnicodeSet* whitespaceChars;
|
||||
const UnicodeSet* bidiControlChars;
|
||||
const UnicodeSet* alphaChars;
|
||||
const UnicodeSet* digitChars;
|
||||
const UnicodeSet* nameStartChars;
|
||||
const UnicodeSet* nameChars;
|
||||
const UnicodeSet* textChars;
|
||||
const UnicodeSet* quotedChars;
|
||||
const UnicodeSet* escapableChars;
|
||||
|
||||
// The input string
|
||||
const UnicodeString &source;
|
||||
// The current position within the input string -- counting in UChar32
|
||||
@ -165,8 +231,8 @@ namespace message2 {
|
||||
|
||||
// The parent builder
|
||||
MFDataModel::Builder &dataModel;
|
||||
}; // class Parser
|
||||
|
||||
}; // class Parser
|
||||
} // namespace message2
|
||||
|
||||
U_NAMESPACE_END
|
||||
@ -175,6 +241,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT_PARSER_H
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -244,11 +246,12 @@ void Serializer::serializeDeclarations() {
|
||||
|
||||
void Serializer::serializeSelectors() {
|
||||
U_ASSERT(!dataModel.hasPattern());
|
||||
const Expression* selectors = dataModel.getSelectorsInternal();
|
||||
const VariableName* selectors = dataModel.getSelectorsInternal();
|
||||
|
||||
emit(ID_MATCH);
|
||||
for (int32_t i = 0; i < dataModel.numSelectors(); i++) {
|
||||
// No whitespace needed here -- see `selectors` in the grammar
|
||||
whitespace();
|
||||
emit(DOLLAR);
|
||||
emit(selectors[i]);
|
||||
}
|
||||
}
|
||||
@ -256,6 +259,7 @@ void Serializer::serializeSelectors() {
|
||||
void Serializer::serializeVariants() {
|
||||
U_ASSERT(!dataModel.hasPattern());
|
||||
const Variant* variants = dataModel.getVariantsInternal();
|
||||
whitespace();
|
||||
for (int32_t i = 0; i < dataModel.numVariants(); i++) {
|
||||
const Variant& v = variants[i];
|
||||
emit(v.getKeys());
|
||||
@ -285,3 +289,4 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
#if !UCONFIG_NO_NORMALIZATION
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#if !UCONFIG_NO_MF2
|
||||
@ -63,6 +65,8 @@ U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_NORMALIZATION */
|
||||
|
||||
#endif /* U_SHOW_CPLUSPLUS_API */
|
||||
|
||||
#endif // MESSAGEFORMAT_SERIALIZER_H
|
||||
|
8
deps/icu-small/source/i18n/nfrs.cpp
vendored
8
deps/icu-small/source/i18n/nfrs.cpp
vendored
@ -152,7 +152,7 @@ NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions,
|
||||
|
||||
UnicodeString& description = descriptions[index]; // !!! make sure index is valid
|
||||
|
||||
if (description.length() == 0) {
|
||||
if (description.isEmpty()) {
|
||||
// throw new IllegalArgumentException("Empty rule set description");
|
||||
status = U_PARSE_ERROR;
|
||||
return;
|
||||
@ -177,16 +177,16 @@ NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions,
|
||||
name.setTo(UNICODE_STRING_SIMPLE("%default"));
|
||||
}
|
||||
|
||||
if (description.length() == 0) {
|
||||
if (description.isEmpty()) {
|
||||
// throw new IllegalArgumentException("Empty rule set description");
|
||||
status = U_PARSE_ERROR;
|
||||
}
|
||||
|
||||
fIsPublic = name.indexOf(gPercentPercent, 2, 0) != 0;
|
||||
|
||||
if ( name.endsWith(gNoparse,8) ) {
|
||||
if (name.endsWith(gNoparse, 8)) {
|
||||
fIsParseable = false;
|
||||
name.truncate(name.length()-8); // remove the @noparse from the name
|
||||
name.truncate(name.length() - 8); // remove the @noparse from the name
|
||||
}
|
||||
|
||||
// all of the other members of NFRuleSet are initialized
|
||||
|
39
deps/icu-small/source/i18n/nfrule.cpp
vendored
39
deps/icu-small/source/i18n/nfrule.cpp
vendored
@ -19,7 +19,6 @@
|
||||
|
||||
#if U_HAVE_RBNF
|
||||
|
||||
#include <limits>
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/rbnf.h"
|
||||
#include "unicode/tblcoll.h"
|
||||
@ -65,6 +64,7 @@ NFRule::~NFRule()
|
||||
|
||||
static const char16_t gLeftBracket = 0x005b;
|
||||
static const char16_t gRightBracket = 0x005d;
|
||||
static const char16_t gVerticalLine = 0x007C;
|
||||
static const char16_t gColon = 0x003a;
|
||||
static const char16_t gZero = 0x0030;
|
||||
static const char16_t gNine = 0x0039;
|
||||
@ -147,6 +147,7 @@ NFRule::makeRules(UnicodeString& description,
|
||||
// then it's really shorthand for two rules (with one exception)
|
||||
LocalPointer<NFRule> rule2;
|
||||
UnicodeString sbuf;
|
||||
int32_t orElseOp = description.indexOf(gVerticalLine);
|
||||
|
||||
// we'll actually only split the rule into two rules if its
|
||||
// base value is an even multiple of its divisor (or it's one
|
||||
@ -194,9 +195,13 @@ NFRule::makeRules(UnicodeString& description,
|
||||
rule2->radix = rule1->radix;
|
||||
rule2->exponent = rule1->exponent;
|
||||
|
||||
// rule2's rule text omits the stuff in brackets: initialize
|
||||
// its rule text and substitutions accordingly
|
||||
// By default, rule2's rule text omits the stuff in brackets,
|
||||
// unless it contains a | between the brackets.
|
||||
// Initialize its rule text and substitutions accordingly.
|
||||
sbuf.append(description, 0, brack1);
|
||||
if (orElseOp >= 0) {
|
||||
sbuf.append(description, orElseOp + 1, brack2 - orElseOp - 1);
|
||||
}
|
||||
if (brack2 + 1 < description.length()) {
|
||||
sbuf.append(description, brack2 + 1, description.length() - brack2 - 1);
|
||||
}
|
||||
@ -207,7 +212,12 @@ NFRule::makeRules(UnicodeString& description,
|
||||
// the brackets themselves: initialize _its_ rule text and
|
||||
// substitutions accordingly
|
||||
sbuf.setTo(description, 0, brack1);
|
||||
sbuf.append(description, brack1 + 1, brack2 - brack1 - 1);
|
||||
if (orElseOp >= 0) {
|
||||
sbuf.append(description, brack1 + 1, orElseOp - brack1 - 1);
|
||||
}
|
||||
else {
|
||||
sbuf.append(description, brack1 + 1, brack2 - brack1 - 1);
|
||||
}
|
||||
if (brack2 + 1 < description.length()) {
|
||||
sbuf.append(description, brack2 + 1, description.length() - brack2 - 1);
|
||||
}
|
||||
@ -286,18 +296,17 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
|
||||
// into "tempValue", skip periods, commas, and spaces,
|
||||
// stop on a slash or > sign (or at the end of the string),
|
||||
// and throw an exception on any other character
|
||||
int64_t ll_10 = 10;
|
||||
while (p < descriptorLength) {
|
||||
c = descriptor.charAt(p);
|
||||
if (c >= gZero && c <= gNine) {
|
||||
int32_t single_digit = static_cast<int32_t>(c - gZero);
|
||||
if ((val > 0 && val > (std::numeric_limits<int64_t>::max() - single_digit) / 10) ||
|
||||
(val < 0 && val < (std::numeric_limits<int64_t>::min() - single_digit) / 10)) {
|
||||
int64_t digit = static_cast<int64_t>(c - gZero);
|
||||
if ((val > 0 && val > (INT64_MAX - digit) / 10) ||
|
||||
(val < 0 && val < (INT64_MIN - digit) / 10)) {
|
||||
// out of int64_t range
|
||||
status = U_PARSE_ERROR;
|
||||
return;
|
||||
}
|
||||
val = val * ll_10 + single_digit;
|
||||
val = val * 10 + digit;
|
||||
}
|
||||
else if (c == gSlash || c == gGreaterThan) {
|
||||
break;
|
||||
@ -322,11 +331,17 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
|
||||
if (c == gSlash) {
|
||||
val = 0;
|
||||
++p;
|
||||
ll_10 = 10;
|
||||
while (p < descriptorLength) {
|
||||
c = descriptor.charAt(p);
|
||||
if (c >= gZero && c <= gNine) {
|
||||
val = val * ll_10 + static_cast<int32_t>(c - gZero);
|
||||
int64_t digit = static_cast<int64_t>(c - gZero);
|
||||
if ((val > 0 && val > (INT64_MAX - digit) / 10) ||
|
||||
(val < 0 && val < (INT64_MIN - digit) / 10)) {
|
||||
// out of int64_t range
|
||||
status = U_PARSE_ERROR;
|
||||
return;
|
||||
}
|
||||
val = val * 10 + digit;
|
||||
}
|
||||
else if (c == gGreaterThan) {
|
||||
break;
|
||||
@ -400,7 +415,7 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
|
||||
// finally, if the rule body begins with an apostrophe, strip it off
|
||||
// (this is generally used to put whitespace at the beginning of
|
||||
// a rule's rule text)
|
||||
if (description.length() > 0 && description.charAt(0) == gTick) {
|
||||
if (!description.isEmpty() && description.charAt(0) == gTick) {
|
||||
description.removeBetween(0, 1);
|
||||
}
|
||||
|
||||
|
@ -1133,7 +1133,7 @@ void DecimalQuantity::setDigitPos(int32_t position, int8_t value) {
|
||||
}
|
||||
|
||||
void DecimalQuantity::shiftLeft(int32_t numDigits) {
|
||||
if (!usingBytes && precision + numDigits > 16) {
|
||||
if (!usingBytes && precision + numDigits >= 16) {
|
||||
switchStorage();
|
||||
}
|
||||
if (usingBytes) {
|
||||
|
50
deps/icu-small/source/i18n/number_longnames.cpp
vendored
50
deps/icu-small/source/i18n/number_longnames.cpp
vendored
@ -48,8 +48,12 @@ constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1;
|
||||
* Gender of the word, in languages with grammatical gender.
|
||||
*/
|
||||
constexpr int32_t GENDER_INDEX = StandardPlural::Form::COUNT + 2;
|
||||
/**
|
||||
* Denominator constant of the unit.
|
||||
*/
|
||||
constexpr int32_t CONSTANT_DENOMINATOR_INDEX = StandardPlural::Form::COUNT + 3;
|
||||
// Number of keys in the array populated by PluralTableSink.
|
||||
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 3;
|
||||
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 4;
|
||||
|
||||
// TODO(icu-units#28): load this list from resources, after creating a "&set"
|
||||
// function for use in ldml2icu rules.
|
||||
@ -1010,6 +1014,11 @@ void LongNameHandler::forArbitraryUnit(const Locale &loc,
|
||||
// denominator (the part after the "-per-). If both are empty, fail
|
||||
MeasureUnitImpl unit;
|
||||
MeasureUnitImpl perUnit;
|
||||
|
||||
if (unitRef.getConstantDenominator(status) != 0) {
|
||||
perUnit.constantDenominator = unitRef.getConstantDenominator(status);
|
||||
}
|
||||
|
||||
{
|
||||
MeasureUnitImpl fullUnit = MeasureUnitImpl::forMeasureUnitMaybeCopy(unitRef, status);
|
||||
if (U_FAILURE(status)) {
|
||||
@ -1196,6 +1205,12 @@ void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit,
|
||||
DerivedComponents derivedTimesCases(loc, "case", "times");
|
||||
DerivedComponents derivedPowerCases(loc, "case", "power");
|
||||
|
||||
if (productUnit.constantDenominator != 0) {
|
||||
CharString constantString;
|
||||
constantString.appendNumber(productUnit.constantDenominator, status);
|
||||
outArray[CONSTANT_DENOMINATOR_INDEX] = UnicodeString::fromUTF8(constantString.toStringPiece());
|
||||
}
|
||||
|
||||
// 4. For each single_unit in product_unit
|
||||
for (int32_t singleUnitIndex = 0; singleUnitIndex < productUnit.singleUnits.length();
|
||||
singleUnitIndex++) {
|
||||
@ -1454,6 +1469,39 @@ void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Handling constant denominator if it exists.
|
||||
if (productUnit.constantDenominator != 0) {
|
||||
int32_t pluralIndex = -1;
|
||||
for (int32_t index = 0; index < StandardPlural::Form::COUNT; index++) {
|
||||
if (!outArray[index].isBogus()) {
|
||||
pluralIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
U_ASSERT(pluralIndex >= 0); // "No plural form found for constant denominator"
|
||||
|
||||
// TODO(ICU-23039):
|
||||
// Improve the handling of constant_denominator representation.
|
||||
// For instance, a constant_denominator of 1000000 should be adaptable to
|
||||
// formats like
|
||||
// 1,000,000, 1e6, or 1 million.
|
||||
// Furthermore, ensure consistent pluralization rules for units. For example,
|
||||
// "meter per 100 seconds" should be evaluated for correct singular/plural
|
||||
// usage: "second" or "seconds"?
|
||||
// Similarly, "kilogram per 1000 meters" should be checked for "meter" or
|
||||
// "meters"?
|
||||
if (outArray[pluralIndex].length() == 0) {
|
||||
outArray[pluralIndex] = outArray[CONSTANT_DENOMINATOR_INDEX];
|
||||
} else {
|
||||
UnicodeString tmp;
|
||||
timesPatternFormatter.format(outArray[CONSTANT_DENOMINATOR_INDEX], outArray[pluralIndex],
|
||||
tmp, status);
|
||||
outArray[pluralIndex] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) {
|
||||
if (globalPlaceholder[pluralIndex] == PH_BEGINNING) {
|
||||
UnicodeString tmp;
|
||||
|
9
deps/icu-small/source/i18n/number_mapper.cpp
vendored
9
deps/icu-small/source/i18n/number_mapper.cpp
vendored
@ -74,9 +74,11 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
||||
!properties.currencyPluralInfo.fPtr.isNull() ||
|
||||
!properties.currencyUsage.isNull() ||
|
||||
warehouse.affixProvider.get().hasCurrencySign());
|
||||
CurrencyUnit currency = resolveCurrency(properties, locale, status);
|
||||
UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
|
||||
CurrencyUnit currency;
|
||||
UCurrencyUsage currencyUsage;
|
||||
if (useCurrency) {
|
||||
currency = resolveCurrency(properties, locale, status);
|
||||
currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
|
||||
// NOTE: Slicing is OK.
|
||||
macros.unit = currency; // NOLINT
|
||||
}
|
||||
@ -129,6 +131,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
||||
}
|
||||
Precision precision;
|
||||
if (!properties.currencyUsage.isNull()) {
|
||||
U_ASSERT(useCurrency);
|
||||
precision = Precision::constructCurrency(currencyUsage).withCurrency(currency);
|
||||
} else if (roundingIncrement != 0.0) {
|
||||
if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) {
|
||||
@ -276,7 +279,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
||||
exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt;
|
||||
|
||||
Precision rounding_;
|
||||
if (precision.fType == Precision::PrecisionType::RND_CURRENCY) {
|
||||
if (useCurrency && precision.fType == Precision::PrecisionType::RND_CURRENCY) {
|
||||
rounding_ = precision.withCurrency(currency, status);
|
||||
} else {
|
||||
rounding_ = precision;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user