diff --git a/deps/icu-small/LICENSE b/deps/icu-small/LICENSE index 180db98fcc66ca..0b9efcd9092f97 100644 --- a/deps/icu-small/LICENSE +++ b/deps/icu-small/LICENSE @@ -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 diff --git a/deps/icu-small/README-FULL-ICU.txt b/deps/icu-small/README-FULL-ICU.txt index 46a34687d51b3e..ee3fdf50b5e59e 100644 --- a/deps/icu-small/README-FULL-ICU.txt +++ b/deps/icu-small/README-FULL-ICU.txt @@ -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 diff --git a/deps/icu-small/source/common/brkiter.cpp b/deps/icu-small/source/common/brkiter.cpp index 4d945cc17e2bb6..44a13ee6a2acd1 100644 --- a/deps/icu-small/source/common/brkiter.cpp +++ b/deps/icu-small/source/common/brkiter.cpp @@ -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 (U_FAILURE(status)) { + return Locale::getRoot(); + } if (type == ULOC_REQUESTED_LOCALE) { - return {requestLocale}; + return requestLocale == nullptr ? + Locale::getRoot() : Locale(requestLocale->data()); } - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); + return LocaleBased::getLocale(validLocale, actualLocale, type, status); } const char * BreakIterator::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } if (type == ULOC_REQUESTED_LOCALE) { - return requestLocale; + return requestLocale == nullptr ? "" : requestLocale->data(); } - U_LOCALE_BASED(locBased, *this); - return locBased.getLocaleID(type, status); + 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 diff --git a/deps/icu-small/source/common/charstr.cpp b/deps/icu-small/source/common/charstr.cpp index f76cc8a4dc90f3..dadc829b0b5605 100644 --- a/deps/icu-small/source/common/charstr.cpp +++ b/deps/icu-small/source/common/charstr.cpp @@ -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)) { diff --git a/deps/icu-small/source/common/charstr.h b/deps/icu-small/source/common/charstr.h index 08283ca452ce2b..ea54ede735cd3d 100644 --- a/deps/icu-small/source/common/charstr.h +++ b/deps/icu-small/source/common/charstr.h @@ -74,6 +74,7 @@ class U_COMMON_API CharString : public UMemory { * 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 @@ class U_COMMON_API CharString : public UMemory { } 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 diff --git a/deps/icu-small/source/common/localefallback_data.h b/deps/icu-small/source/common/localefallback_data.h index 0accf0324d7eb2..3b8ad8a3f398b8 100644 --- a/deps/icu-small/source/common/localefallback_data.h +++ b/deps/icu-small/source/common/localefallback_data.h @@ -11,11 +11,11 @@ //====================================================================== // Default script table const char scriptCodeChars[] = - "Aghb\0Ahom\0Arab\0Armi\0Armn\0Avst\0Bamu\0Bass\0Batk\0Beng\0Bopo\0" - "Brah\0Cakm\0Cans\0Cari\0Cham\0Cher\0Chrs\0Copt\0Cprt\0Cyrl\0Deva\0" - "Egyp\0Elym\0Ethi\0Geor\0Gong\0Gonm\0Goth\0Gran\0Grek\0Gujr\0Guru\0" - "Hang\0Hani\0Hans\0Hant\0Hebr\0Hluw\0Hmnp\0Ital\0Java\0Jpan\0Kali\0" - "Kana\0Kawi\0Khar\0Khmr\0Kits\0Knda\0Kore\0Lana\0Laoo\0Latf\0Latg\0" + "Aghb\0Ahom\0Arab\0Armi\0Armn\0Avst\0Bali\0Bamu\0Bass\0Batk\0Beng\0" + "Bopo\0Brah\0Cakm\0Cans\0Cari\0Cham\0Cher\0Chrs\0Copt\0Cprt\0Cyrl\0" + "Deva\0Egyp\0Elym\0Ethi\0Geor\0Gong\0Gonm\0Goth\0Gran\0Grek\0Gujr\0" + "Guru\0Hang\0Hani\0Hans\0Hant\0Hebr\0Hluw\0Hmnp\0Ital\0Java\0Jpan\0" + "Kali\0Kana\0Khar\0Khmr\0Kits\0Knda\0Kore\0Lana\0Laoo\0Latf\0Latg\0" "Lepc\0Lina\0Linb\0Lisu\0Lyci\0Lydi\0Mand\0Mani\0Marc\0Medf\0Merc\0" "Mlym\0Modi\0Mong\0Mroo\0Mtei\0Mymr\0Narb\0Newa\0Nkoo\0Nshu\0Ogam\0" "Olck\0Orkh\0Orya\0Osge\0Ougr\0Pauc\0Phli\0Phnx\0Plrd\0Prti\0Rjng\0" @@ -48,70 +48,71 @@ const char dsLocaleIDChars[] = "gbz\0gdb\0gdo\0gdx\0gez\0ggg\0gha\0ghe\0gho\0ghr\0ght\0gig\0gin\0" "gjk\0gju\0gld\0glh\0glk\0gml\0gmv\0gmy\0goe\0gof\0goj\0gok\0gon\0" "got\0gra\0grc\0grt\0gru\0gu\0gvr\0gwc\0gwf\0gwt\0gyo\0gzi\0ha_CM\0" - "ha_SD\0hac\0hak\0har\0haz\0hbo\0hdy\0he\0hi\0hif\0hii\0hit\0hkh\0" - "hlb\0hlu\0hmd\0hmj\0hmq\0hnd\0hne\0hnj\0hno\0hoc\0hoh\0hoj\0how\0" - "hoy\0hpo\0hrt\0hrz\0hsn\0hss\0htx\0hut\0huy\0huz\0hy\0hyw\0ii\0" - "imy\0inh\0int\0ior\0iru\0isk\0itk\0itl\0iu\0iw\0ja\0jad\0jat\0" - "jbe\0jbn\0jct\0jda\0jdg\0jdt\0jee\0jge\0ji\0jje\0jkm\0jml\0jna\0" - "jnd\0jnl\0jns\0jog\0jpa\0jpr\0jrb\0jul\0jun\0juy\0jya\0jye\0ka\0" - "kaa\0kap\0kaw\0kbd\0kbg\0kbu\0kby\0kca\0kcy\0kdq\0kdt\0ket\0kev\0" - "kex\0key\0kfa\0kfb\0kfc\0kfd\0kfe\0kfg\0kfh\0kfi\0kfk\0kfm\0kfp\0" - "kfq\0kfr\0kfs\0kfu\0kfx\0kfy\0kgj\0kgy\0khb\0khf\0khg\0khn\0kho\0" - "kht\0khv\0khw\0kif\0kim\0kip\0kjg\0kjh\0kjl\0kjo\0kjp\0kjt\0kjz\0" - "kk\0kk_AF\0kk_CN\0kk_IR\0kk_MN\0kkf\0kkh\0kkt\0kle\0klj\0klr\0" - "km\0kmj\0kmz\0kn\0knn\0ko\0koi\0kok\0kpt\0kpy\0kqd\0kqy\0kra\0" - "krc\0krk\0krr\0kru\0krv\0ks\0ksu\0ksw\0ksz\0ktb\0kte\0ktl\0ktp\0" - "ku_LB\0kuf\0kum\0kv\0kva\0kvq\0kvt\0kvx\0kvy\0kxf\0kxk\0kxm\0" - "kxp\0ky\0ky_CN\0kyu\0kyv\0kyw\0lab\0lad\0lae\0lah\0lbe\0lbf\0" - "lbj\0lbm\0lbo\0lbr\0lcp\0lep\0lez\0lhm\0lhs\0lif\0lis\0lkh\0lki\0" - "lmh\0lmn\0lo\0loy\0lpo\0lrc\0lrk\0lrl\0lsa\0lsd\0lss\0ltc\0luk\0" - "luu\0luv\0luz\0lwl\0lwm\0lya\0lzh\0mag\0mai\0man_GN\0mby\0mde\0" - "mdf\0mdx\0mdy\0mfa\0mfi\0mga\0mgp\0mhj\0mid\0mjl\0mjq\0mjr\0mjt\0" - "mju\0mjv\0mjz\0mk\0mkb\0mke\0mki\0mkm\0ml\0mlf\0mn\0mn_CN\0mnc\0" - "mni\0mnj\0mns\0mnw\0mpz\0mr\0mra\0mrd\0mrj\0mro\0mrr\0ms_CC\0" - "mtm\0mtr\0mud\0muk\0mut\0muv\0muz\0mve\0mvf\0mvy\0mvz\0mwr\0mwt\0" - "mww\0my\0mym\0myv\0myz\0mzn\0nan\0nao\0ncd\0ncq\0ndf\0ne\0neg\0" - "neh\0nei\0new\0ngt\0nio\0nit\0niv\0nli\0nlm\0nlx\0nmm\0nnp\0nod\0" - "noe\0nog\0noi\0non\0nos\0npb\0nqo\0nrn\0nsd\0nsf\0nsk\0nst\0nsv\0" - "nty\0ntz\0nwc\0nwx\0nyl\0nyq\0nyw\0oaa\0oac\0oar\0oav\0obm\0obr\0" - "odk\0oht\0oj\0ojs\0okm\0oko\0okz\0ola\0ole\0omk\0omp\0omr\0omx\0" - "oon\0or\0ort\0oru\0orv\0os\0osa\0osc\0osi\0ota\0otb\0otk\0oty\0" - "oui\0pa\0pa_PK\0pal\0paq\0pbt\0pcb\0pce\0pcf\0pcg\0pch\0pci\0" - "pcj\0peg\0peo\0pgd\0pgg\0pgl\0pgn\0phd\0phk\0phl\0phn\0pho\0phr\0" - "pht\0phu\0phv\0phw\0pi\0pka\0pkr\0plk\0pll\0pmh\0pnt\0pra\0prc\0" - "prd\0prt\0prx\0ps\0psh\0psi\0pst\0psu\0pum\0pwo\0pwr\0pww\0pyx\0" - "qxq\0raa\0rab\0raf\0rah\0raj\0rav\0rbb\0rdb\0rei\0rhg\0rji\0rjs\0" - "rka\0rki\0rkt\0rmi\0rmt\0rmz\0rsk\0rtw\0ru\0rue\0rut\0rwr\0ryu\0" - "sa\0sah\0sam\0sat\0saz\0sbn\0sbu\0sck\0scl\0scp\0sct\0scu\0scx\0" - "sd\0sd_IN\0sdb\0sdf\0sdg\0sdh\0sdr\0sds\0sel\0sfm\0sga\0sgh\0" - "sgj\0sgr\0sgt\0sgw\0sgy\0shd\0shi\0shm\0shn\0shu\0shv\0si\0sia\0" - "sip\0siy\0siz\0sjd\0sjp\0sjt\0skb\0skj\0skr\0smh\0smp\0smu\0smy\0" - "soa\0sog\0soi\0sou\0spt\0spv\0sqo\0sqq\0sqt\0sr\0srb\0srh\0srx\0" - "srz\0ssh\0sss\0sts\0stv\0sty\0suz\0sva\0swb\0swi\0swv\0sxu\0syc\0" - "syl\0syn\0syr\0syw\0ta\0tab\0taj\0tbk\0tcn\0tco\0tcx\0tcy\0tda\0" - "tdb\0tdd\0tdg\0tdh\0te\0tes\0tg\0tg_PK\0tge\0tgf\0th\0the\0thf\0" - "thi\0thl\0thm\0thq\0thr\0ths\0ti\0tig\0tij\0tin\0tjl\0tjo\0tkb\0" - "tks\0tkt\0tmr\0tnv\0tov\0tpu\0tra\0trg\0trm\0trw\0tsd\0tsj\0tt\0" - "tth\0tto\0tts\0ttz\0tvn\0twm\0txg\0txo\0tyr\0tyv\0ude\0udg\0udi\0" - "udm\0ug\0ug_KZ\0ug_MN\0uga\0ugh\0ugo\0uk\0uki\0ulc\0unr\0unr_NP\0" - "unx\0ur\0urk\0ush\0uum\0uz_AF\0uz_CN\0uzs\0vaa\0vaf\0vah\0vai\0" - "vas\0vav\0vay\0vgr\0vjk\0vmd\0vmh\0wal\0wbk\0wbq\0wbr\0wle\0wlo\0" - "wme\0wne\0wni\0wsg\0wsv\0wtm\0wuu\0xag\0xal\0xan\0xas\0xco\0xcr\0" - "xdq\0xhe\0xhm\0xis\0xka\0xkc\0xkf\0xkj\0xkp\0xlc\0xld\0xly\0xmf\0" - "xmn\0xmr\0xna\0xnr\0xpg\0xpi\0xpm\0xpr\0xrm\0xrn\0xsa\0xsr\0xtq\0" - "xub\0xuj\0xve\0xvi\0xwo\0xzh\0yai\0ybh\0ybi\0ydg\0yea\0yej\0yeu\0" - "ygp\0yhd\0yi\0yig\0yih\0yiv\0ykg\0ykh\0yna\0ynk\0yoi\0yoy\0yrk\0" - "ysd\0ysn\0ysp\0ysr\0ysy\0yud\0yue\0yue_CN\0yug\0yux\0ywq\0ywu\0" - "zau\0zba\0zch\0zdj\0zeh\0zen\0zgb\0zgh\0zgm\0zgn\0zh\0zh_AU\0" - "zh_BN\0zh_GB\0zh_GF\0zh_HK\0zh_ID\0zh_MO\0zh_PA\0zh_PF\0zh_PH\0" - "zh_SR\0zh_TH\0zh_TW\0zh_US\0zh_VN\0zhd\0zhx\0zko\0zkt\0zkz\0zlj\0" - "zln\0zlq\0zqe\0zrg\0zrp\0zum\0zwa\0zyg\0zyn\0zzj\0"; + "ha_SD\0hac\0hak\0hak_TW\0har\0haz\0hbo\0hdy\0he\0hi\0hif\0hii\0" + "hit\0hkh\0hlb\0hlu\0hmd\0hmj\0hmq\0hnd\0hne\0hnj\0hno\0hoc\0hoh\0" + "hoj\0how\0hoy\0hpo\0hrt\0hrz\0hsn\0hss\0htx\0hut\0huy\0huz\0hy\0" + "hyw\0ii\0imy\0inh\0int\0ior\0iru\0isk\0itk\0itl\0iu\0iw\0ja\0" + "jad\0jat\0jbe\0jbn\0jct\0jda\0jdg\0jdt\0jee\0jge\0ji\0jje\0jkm\0" + "jml\0jna\0jnd\0jnl\0jns\0jog\0jpa\0jpr\0jrb\0jul\0jun\0juy\0jya\0" + "jye\0ka\0kaa\0kap\0kaw\0kbd\0kbg\0kbu\0kby\0kca\0kcy\0kdq\0kdt\0" + "ket\0kev\0kex\0key\0kfa\0kfb\0kfc\0kfd\0kfe\0kfg\0kfh\0kfi\0kfk\0" + "kfm\0kfp\0kfq\0kfr\0kfs\0kfu\0kfx\0kfy\0kgj\0kgy\0khb\0khf\0khg\0" + "khn\0kho\0kht\0khv\0khw\0kif\0kim\0kip\0kjg\0kjh\0kjl\0kjo\0kjp\0" + "kjt\0kjz\0kk\0kk_AF\0kk_CN\0kk_IR\0kk_MN\0kkf\0kkh\0kkt\0kle\0" + "klj\0klr\0km\0kmj\0kmz\0kn\0knn\0ko\0koi\0kok\0kpt\0kpy\0kqd\0" + "kqy\0kra\0krc\0krk\0krr\0kru\0krv\0ks\0ksu\0ksw\0ksz\0ktb\0kte\0" + "ktl\0ktp\0ku_LB\0kuf\0kum\0kv\0kva\0kvq\0kvt\0kvx\0kvy\0kxf\0" + "kxk\0kxm\0kxp\0ky\0ky_CN\0kyu\0kyv\0kyw\0lab\0lad\0lae\0lah\0" + "lbe\0lbf\0lbj\0lbm\0lbo\0lbr\0lcp\0lep\0lez\0lhm\0lhs\0lif\0lis\0" + "lkh\0lki\0lmh\0lmn\0lo\0loy\0lpo\0lrc\0lrk\0lrl\0lsa\0lsd\0lss\0" + "ltc\0luk\0luu\0luv\0luz\0lwl\0lwm\0lya\0lzh\0lzz_GE\0mag\0mai\0" + "mby\0mde\0mdf\0mdx\0mdy\0mfa\0mfi\0mga\0mgp\0mhj\0mid\0mjl\0mjq\0" + "mjr\0mjt\0mju\0mjv\0mjz\0mk\0mkb\0mke\0mki\0mkm\0ml\0mlf\0mn\0" + "mn_CN\0mnc\0mni\0mnj\0mns\0mnw\0mpz\0mr\0mra\0mrd\0mrj\0mro\0" + "mrr\0ms_CC\0mtm\0mtr\0mud\0muk\0mut\0muv\0muz\0mve\0mvf\0mvy\0" + "mvz\0mwr\0mwt\0mww\0my\0mym\0myv\0myz\0mzn\0nan\0nan_TW\0nao\0" + "ncd\0ncq\0ndf\0ne\0neg\0neh\0nei\0new\0ngt\0nio\0nit\0niv\0nli\0" + "nlm\0nlx\0nmm\0nnp\0nod\0noe\0nog\0noi\0non\0nos\0npb\0nqo\0nrn\0" + "nsd\0nsf\0nsk\0nst\0nsv\0nty\0ntz\0nwc\0nwx\0nyl\0nyq\0nyw\0oaa\0" + "oac\0oar\0oav\0obm\0obr\0odk\0oht\0oj\0ojs\0okm\0oko\0okz\0ola\0" + "ole\0omk\0omp\0omr\0omx\0oon\0or\0ort\0oru\0orv\0os\0osa\0osc\0" + "osi\0ota\0otb\0otk\0oty\0oui\0pa\0pa_PK\0pal\0paq\0pbt\0pcb\0" + "pce\0pcf\0pcg\0pch\0pci\0pcj\0peg\0peo\0pgd\0pgg\0pgl\0pgn\0phd\0" + "phk\0phl\0phn\0pho\0phr\0pht\0phu\0phv\0phw\0pi\0pka\0pkr\0plk\0" + "pll\0pmh\0pnt\0pnt_RU\0pra\0prc\0prd\0prt\0prx\0ps\0psh\0psi\0" + "pst\0psu\0pum\0pwo\0pwr\0pww\0pyx\0qxq\0raa\0rab\0raf\0rah\0raj\0" + "rav\0rbb\0rdb\0rei\0rhg\0rji\0rjs\0rka\0rki\0rkt\0rmi\0rmt\0rmz\0" + "rsk\0rtw\0ru\0rue\0rut\0rwr\0ryu\0sa\0sah\0sam\0sat\0saz\0sbn\0" + "sbu\0sck\0scl\0scp\0sct\0scu\0scx\0sd\0sd_IN\0sdb\0sdf\0sdg\0" + "sdh\0sdr\0sds\0sel\0sfm\0sgh\0sgj\0sgr\0sgt\0sgw\0sgy\0shd\0shi\0" + "shm\0shn\0shu\0shv\0si\0sia\0sip\0siy\0siz\0sjd\0sjp\0sjt\0skb\0" + "skj\0skr\0smh\0smp\0smu\0smy\0soa\0sog\0soi\0sou\0spt\0spv\0sqo\0" + "sqq\0sqt\0sr\0srb\0srh\0srx\0srz\0ssh\0sss\0sts\0stv\0sty\0suz\0" + "sva\0swb\0swi\0swv\0sxu\0syc\0syl\0syn\0syr\0syw\0ta\0tab\0taj\0" + "tbk\0tcn\0tco\0tcx\0tcy\0tda\0tdb\0tdd\0tdg\0tdh\0te\0tes\0tg\0" + "tg_PK\0tge\0tgf\0th\0the\0thf\0thi\0thl\0thm\0thq\0thr\0ths\0" + "ti\0tig\0tij\0tin\0tjl\0tjo\0tkb\0tks\0tkt\0tmr\0tnv\0tov\0tpu\0" + "tra\0trg\0trm\0trw\0tsd\0tsj\0tt\0tth\0tto\0tts\0ttz\0tvn\0twm\0" + "txg\0txo\0tyr\0tyv\0ude\0udg\0udi\0udm\0ug\0ug_KZ\0ug_MN\0uga\0" + "ugh\0ugo\0uk\0uki\0ulc\0unr\0unr_NP\0unx\0ur\0urk\0ush\0uum\0" + "uz_AF\0uz_CN\0uzs\0vaa\0vaf\0vah\0vai\0vas\0vav\0vay\0vgr\0vjk\0" + "vmd\0vmh\0wal\0wbk\0wbq\0wbr\0wle\0wlo\0wme\0wne\0wni\0wsg\0wsv\0" + "wtm\0wuu\0xag\0xal\0xan\0xas\0xco\0xcr\0xdq\0xhe\0xhm\0xis\0xka\0" + "xkc\0xkf\0xkj\0xkp\0xlc\0xld\0xly\0xmf\0xmn\0xmr\0xna\0xnr\0xpg\0" + "xpi\0xpm\0xpr\0xrm\0xrn\0xsa\0xsr\0xtq\0xub\0xuj\0xve\0xvi\0xwo\0" + "xzh\0yai\0ybh\0ybi\0ydg\0yea\0yej\0yeu\0ygp\0yhd\0yi\0yig\0yih\0" + "yiv\0ykg\0ykh\0yna\0ynk\0yoi\0yoy\0yrk\0ysd\0ysn\0ysp\0ysr\0ysy\0" + "yud\0yue\0yue_CN\0yug\0yux\0ywq\0ywu\0zau\0zba\0zch\0zdj\0zeh\0" + "zen\0zgb\0zgh\0zgm\0zgn\0zh\0zh_AU\0zh_BN\0zh_GB\0zh_GF\0zh_HK\0" + "zh_ID\0zh_MO\0zh_PA\0zh_PF\0zh_PH\0zh_SR\0zh_TH\0zh_TW\0zh_US\0" + "zh_VN\0zhd\0zhx\0zko\0zkt\0zkz\0zlj\0zln\0zlq\0zqe\0zrg\0zrp\0" + "zum\0zwa\0zyg\0zyn\0zzj\0"; const int32_t defaultScriptTable[] = { 0, 330, // aaf -> Mlym 4, 10, // aao -> Arab - 8, 150, // aat -> Grek - 12, 100, // ab -> Cyrl + 8, 155, // aat -> Grek + 12, 105, // ab -> Cyrl 15, 10, // abh -> Arab 19, 435, // abl -> Rjng 23, 10, // abv -> Arab @@ -121,64 +122,64 @@ const int32_t defaultScriptTable[] = { 39, 10, // acx -> Arab 43, 10, // adf -> Arab 47, 555, // adx -> Tibt - 51, 100, // ady -> Cyrl + 51, 105, // ady -> Cyrl 55, 25, // ae -> Avst 58, 10, // aeb -> Arab 62, 10, // aec -> Arab 66, 10, // aee -> Arab 70, 10, // aeq -> Arab 74, 10, // afb -> Arab - 78, 105, // agi -> Deva - 82, 120, // agj -> Ethi - 86, 100, // agx -> Cyrl - 90, 120, // ahg -> Ethi + 78, 110, // agi -> Deva + 82, 125, // agj -> Ethi + 86, 105, // agx -> Cyrl + 90, 125, // ahg -> Ethi 94, 5, // aho -> Ahom - 98, 105, // ahr -> Deva + 98, 110, // ahr -> Deva 102, 10, // aib -> Arab 106, 495, // aii -> Syrc - 110, 185, // aij -> Hebr - 114, 220, // ain -> Kana + 110, 190, // aij -> Hebr + 114, 225, // ain -> Kana 118, 355, // aio -> Mymr 122, 10, // aiq -> Arab 126, 590, // akk -> Xsux - 130, 100, // akv -> Cyrl + 130, 105, // akv -> Cyrl 134, 260, // alk -> Laoo 138, 330, // all -> Mlym - 142, 100, // alr -> Cyrl - 146, 100, // alt -> Cyrl - 150, 120, // alw -> Ethi - 154, 120, // am -> Ethi - 157, 210, // ams -> Jpan + 142, 105, // alr -> Cyrl + 146, 105, // alt -> Cyrl + 150, 125, // alw -> Ethi + 154, 125, // am -> Ethi + 157, 215, // ams -> Jpan 161, 495, // amw -> Syrc - 165, 100, // ani -> Cyrl - 169, 105, // anp -> Deva - 173, 105, // anq -> Deva - 177, 105, // anr -> Deva - 181, 120, // anu -> Ethi - 185, 45, // aot -> Beng + 165, 105, // ani -> Cyrl + 169, 110, // anp -> Deva + 173, 110, // anq -> Deva + 177, 110, // anr -> Deva + 181, 125, // anu -> Ethi + 185, 50, // aot -> Beng 189, 10, // apc -> Arab 193, 10, // apd -> Arab - 197, 105, // aph -> Deva - 201, 100, // aqc -> Cyrl + 197, 110, // aph -> Deva + 201, 105, // aqc -> Cyrl 205, 10, // ar -> Arab 208, 15, // arc -> Armi 212, 10, // arq -> Arab 216, 10, // ars -> Arab 220, 10, // ary -> Arab 224, 10, // arz -> Arab - 228, 45, // as -> Beng + 228, 50, // as -> Beng 231, 465, // ase -> Sgnw 235, 10, // ask -> Arab - 239, 105, // asr -> Deva + 239, 110, // asr -> Deva 243, 10, // atn -> Arab - 247, 100, // atv -> Cyrl + 247, 105, // atv -> Cyrl 251, 10, // auj -> Arab 255, 10, // auz -> Arab - 259, 100, // av -> Cyrl + 259, 105, // av -> Cyrl 262, 10, // avd -> Arab 266, 10, // avl -> Arab - 270, 105, // awa -> Deva - 274, 120, // awn -> Ethi + 270, 110, // awa -> Deva + 274, 125, // awn -> Ethi 278, 20, // axm -> Armn 282, 10, // ayh -> Arab 286, 10, // ayl -> Arab @@ -186,971 +187,973 @@ const int32_t defaultScriptTable[] = { 294, 10, // ayp -> Arab 298, 10, // az_IQ -> Arab 304, 10, // az_IR -> Arab - 310, 100, // az_RU -> Cyrl + 310, 105, // az_RU -> Cyrl 316, 10, // azb -> Arab - 320, 100, // ba -> Cyrl + 320, 105, // ba -> Cyrl 323, 10, // bal -> Arab - 327, 105, // bap -> Deva - 331, 30, // bax -> Bamu - 335, 125, // bbl -> Geor - 339, 120, // bcq -> Ethi + 327, 110, // bap -> Deva + 331, 35, // bax -> Bamu + 335, 130, // bbl -> Geor + 339, 125, // bcq -> Ethi 343, 395, // bdv -> Orya 347, 10, // bdz -> Arab - 351, 100, // be -> Cyrl - 354, 105, // bee -> Deva + 351, 105, // be -> Cyrl + 354, 110, // bee -> Deva 358, 10, // bej -> Arab - 362, 105, // bfb -> Deva + 362, 110, // bfb -> Deva 366, 520, // bfq -> Taml 370, 10, // bft -> Arab 374, 555, // bfu -> Tibt 378, 395, // bfw -> Orya - 382, 105, // bfy -> Deva - 386, 105, // bfz -> Deva - 390, 100, // bg -> Cyrl - 393, 105, // bgc -> Deva - 397, 105, // bgd -> Deva + 382, 110, // bfy -> Deva + 386, 110, // bfz -> Deva + 390, 105, // bg -> Cyrl + 393, 110, // bgc -> Deva + 397, 110, // bgd -> Deva 401, 10, // bgn -> Arab 405, 10, // bgp -> Arab - 409, 105, // bgq -> Deva - 413, 105, // bgw -> Deva - 417, 150, // bgx -> Grek - 421, 105, // bha -> Deva - 425, 105, // bhb -> Deva - 429, 105, // bhd -> Deva + 409, 110, // bgq -> Deva + 413, 110, // bgw -> Deva + 417, 155, // bgx -> Grek + 421, 110, // bha -> Deva + 425, 110, // bhb -> Deva + 429, 110, // bhd -> Deva 433, 10, // bhe -> Arab - 437, 100, // bhh -> Cyrl - 441, 105, // bhi -> Deva - 445, 105, // bhj -> Deva + 437, 105, // bhh -> Cyrl + 441, 110, // bhi -> Deva + 445, 110, // bhj -> Deva 449, 10, // bhm -> Arab 453, 495, // bhn -> Syrc - 457, 105, // bho -> Deva - 461, 105, // bht -> Deva - 465, 105, // bhu -> Deva - 469, 105, // biy -> Deva + 457, 110, // bho -> Deva + 461, 110, // bht -> Deva + 465, 110, // bhu -> Deva + 469, 110, // biy -> Deva 473, 495, // bjf -> Syrc - 477, 105, // bjj -> Deva + 477, 110, // bjj -> Deva 481, 10, // bjm -> Arab 485, 555, // bkk -> Tibt 489, 355, // blk -> Mymr 493, 530, // blt -> Tavt - 497, 105, // bmj -> Deva - 501, 45, // bn -> Beng - 504, 105, // bns -> Deva + 497, 110, // bmj -> Deva + 501, 50, // bn -> Beng + 504, 110, // bns -> Deva 508, 555, // bo -> Tibt - 511, 100, // bph -> Cyrl - 515, 105, // bpx -> Deva - 519, 45, // bpy -> Beng + 511, 105, // bph -> Cyrl + 515, 110, // bpx -> Deva + 519, 50, // bpy -> Beng 523, 10, // bqi -> Arab - 527, 105, // bra -> Deva + 527, 110, // bra -> Deva 531, 235, // brb -> Khmr - 535, 105, // brd -> Deva + 535, 110, // brd -> Deva 539, 10, // brh -> Arab 543, 10, // brk -> Arab 547, 555, // bro -> Tibt 551, 260, // brv -> Laoo 555, 245, // brw -> Knda - 559, 105, // brx -> Deva + 559, 110, // brx -> Deva 563, 10, // bsh -> Arab 567, 10, // bsk -> Arab - 571, 35, // bsq -> Bass - 575, 120, // bst -> Ethi - 579, 40, // btd -> Batk - 583, 40, // btm -> Batk - 587, 105, // btv -> Deva - 591, 100, // bua -> Cyrl + 571, 40, // bsq -> Bass + 575, 125, // bst -> Ethi + 579, 45, // btd -> Batk + 583, 45, // btm -> Batk + 587, 110, // btv -> Deva + 591, 105, // bua -> Cyrl 595, 355, // bwe -> Mymr - 599, 100, // bxm -> Cyrl + 599, 105, // bxm -> Cyrl 603, 340, // bxu -> Mong - 607, 105, // byh -> Deva - 611, 120, // byn -> Ethi - 615, 105, // byw -> Deva + 607, 110, // byh -> Deva + 611, 125, // byn -> Ethi + 615, 110, // byw -> Deva 619, 550, // bzi -> Thai 623, 550, // cbn -> Thai - 627, 60, // ccp -> Cakm + 627, 65, // ccp -> Cakm 631, 535, // cde -> Telu - 635, 105, // cdh -> Deva - 639, 155, // cdi -> Gujr - 643, 105, // cdj -> Deva - 647, 105, // cdm -> Deva - 651, 175, // cdo -> Hans - 655, 45, // cdz -> Beng - 659, 100, // ce -> Cyrl + 635, 110, // cdh -> Deva + 639, 160, // cdi -> Gujr + 643, 110, // cdj -> Deva + 647, 110, // cdm -> Deva + 651, 180, // cdo -> Hans + 655, 50, // cdz -> Beng + 659, 105, // ce -> Cyrl 662, 555, // cgk -> Tibt 666, 10, // chg -> Arab - 670, 100, // chm -> Cyrl - 674, 80, // chr -> Cher - 678, 105, // chx -> Deva - 682, 105, // cih -> Deva + 670, 105, // chm -> Cyrl + 674, 85, // chr -> Cher + 678, 110, // chx -> Deva + 682, 110, // cih -> Deva 686, 10, // cja -> Arab - 690, 100, // cji -> Cyrl - 694, 75, // cjm -> Cham - 698, 175, // cjy -> Hans + 690, 105, // cji -> Cyrl + 694, 80, // cjm -> Cham + 698, 180, // cjy -> Hans 702, 10, // ckb -> Arab - 706, 100, // ckt -> Cyrl + 706, 105, // ckt -> Cyrl 710, 10, // clh -> Arab - 714, 100, // clw -> Cyrl + 714, 105, // clw -> Cyrl 718, 485, // cmg -> Soyo 722, 555, // cna -> Tibt - 726, 175, // cnp -> Hans + 726, 180, // cnp -> Hans 730, 550, // cog -> Thai - 734, 90, // cop -> Copt - 738, 150, // cpg -> Grek - 742, 65, // cr -> Cans - 745, 100, // crh -> Cyrl - 749, 65, // crj -> Cans - 753, 65, // crk -> Cans - 757, 65, // crl -> Cans - 761, 65, // crm -> Cans + 734, 95, // cop -> Copt + 738, 155, // cpg -> Grek + 742, 70, // cr -> Cans + 745, 105, // crh -> Cyrl + 749, 70, // crj -> Cans + 753, 70, // crk -> Cans + 757, 70, // crl -> Cans + 761, 70, // crm -> Cans 765, 355, // csh -> Mymr - 769, 175, // csp -> Hans - 773, 65, // csw -> Cans + 769, 180, // csp -> Hans + 773, 70, // csw -> Cans 777, 410, // ctd -> Pauc - 781, 45, // ctg -> Beng - 785, 105, // ctn -> Deva + 781, 50, // ctg -> Beng + 785, 110, // ctn -> Deva 789, 520, // ctt -> Taml 793, 520, // cty -> Taml - 797, 100, // cu -> Cyrl + 797, 105, // cu -> Cyrl 800, 255, // cuu -> Lana - 804, 100, // cv -> Cyrl - 807, 175, // czh -> Hans - 811, 185, // czk -> Hebr - 815, 105, // daq -> Deva - 819, 100, // dar -> Cyrl + 804, 105, // cv -> Cyrl + 807, 180, // czh -> Hans + 811, 190, // czk -> Hebr + 815, 110, // daq -> Deva + 819, 105, // dar -> Cyrl 823, 10, // dcc -> Arab - 827, 100, // ddo -> Cyrl + 827, 105, // ddo -> Cyrl 831, 10, // def -> Arab 835, 10, // deh -> Arab - 839, 45, // der -> Beng + 839, 50, // der -> Beng 843, 10, // dgl -> Arab - 847, 105, // dhi -> Deva - 851, 155, // dhn -> Gujr - 855, 105, // dho -> Deva - 859, 105, // dhw -> Deva + 847, 110, // dhi -> Deva + 851, 160, // dhn -> Gujr + 855, 110, // dho -> Deva + 859, 110, // dhw -> Deva 863, 555, // dka -> Tibt - 867, 100, // dlg -> Cyrl + 867, 105, // dlg -> Cyrl 871, 320, // dmf -> Medf 875, 10, // dmk -> Arab 879, 10, // dml -> Arab - 883, 100, // dng -> Cyrl + 883, 105, // dng -> Cyrl 887, 355, // dnu -> Mymr 891, 355, // dnv -> Mymr - 895, 105, // doi -> Deva - 899, 120, // dox -> Ethi + 895, 110, // doi -> Deva + 899, 125, // dox -> Ethi 903, 555, // dre -> Tibt - 907, 105, // drq -> Deva - 911, 120, // drs -> Ethi - 915, 105, // dry -> Deva + 907, 110, // drq -> Deva + 911, 125, // drs -> Ethi + 915, 110, // dry -> Deva 919, 395, // dso -> Orya - 923, 105, // dty -> Deva - 927, 155, // dub -> Gujr - 931, 105, // duh -> Deva - 935, 105, // dus -> Deva + 923, 110, // dty -> Deva + 927, 160, // dub -> Gujr + 931, 110, // duh -> Deva + 935, 110, // dus -> Deva 939, 545, // dv -> Thaa 942, 395, // dwk -> Orya - 946, 105, // dwz -> Deva + 946, 110, // dwz -> Deva 950, 555, // dz -> Tibt 953, 555, // dzl -> Tibt - 957, 150, // ecr -> Grek - 961, 95, // ecy -> Cprt - 965, 110, // egy -> Egyp - 969, 215, // eky -> Kali - 973, 150, // el -> Grek - 976, 105, // emg -> Deva - 980, 105, // emu -> Deva - 984, 100, // enf -> Cyrl - 988, 100, // enh -> Cyrl + 957, 155, // ecr -> Grek + 961, 100, // ecy -> Cprt + 965, 115, // egy -> Egyp + 969, 220, // eky -> Kali + 973, 155, // el -> Grek + 976, 110, // emg -> Deva + 980, 110, // emu -> Deva + 984, 105, // enf -> Cyrl + 988, 105, // enh -> Cyrl 992, 520, // era -> Taml - 996, 135, // esg -> Gonm + 996, 140, // esg -> Gonm 1000, 10, // esh -> Arab - 1004, 200, // ett -> Ital - 1008, 100, // eve -> Cyrl - 1012, 100, // evn -> Cyrl + 1004, 205, // ett -> Ital + 1008, 105, // eve -> Cyrl + 1012, 105, // evn -> Cyrl 1016, 10, // fa -> Arab 1019, 10, // fay -> Arab 1023, 10, // faz -> Arab 1027, 10, // fia -> Arab - 1031, 105, // fmu -> Deva + 1031, 110, // fmu -> Deva 1035, 10, // fub -> Arab - 1039, 175, // gan -> Hans + 1039, 180, // gan -> Hans 1043, 395, // gaq -> Orya - 1047, 155, // gas -> Gujr + 1047, 160, // gas -> Gujr 1051, 535, // gau -> Telu 1055, 395, // gbj -> Orya - 1059, 105, // gbk -> Deva - 1063, 155, // gbl -> Gujr - 1067, 105, // gbm -> Deva + 1059, 110, // gbk -> Deva + 1063, 160, // gbl -> Gujr + 1067, 110, // gbm -> Deva 1071, 10, // gbz -> Arab 1075, 395, // gdb -> Orya - 1079, 100, // gdo -> Cyrl - 1083, 105, // gdx -> Deva - 1087, 120, // gez -> Ethi + 1079, 105, // gdo -> Cyrl + 1083, 110, // gdx -> Deva + 1087, 125, // gez -> Ethi 1091, 10, // ggg -> Arab 1095, 10, // gha -> Arab - 1099, 105, // ghe -> Deva + 1099, 110, // ghe -> Deva 1103, 540, // gho -> Tfng 1107, 10, // ghr -> Arab 1111, 555, // ght -> Tibt 1115, 10, // gig -> Arab - 1119, 100, // gin -> Cyrl + 1119, 105, // gin -> Cyrl 1123, 10, // gjk -> Arab 1127, 10, // gju -> Arab - 1131, 100, // gld -> Cyrl + 1131, 105, // gld -> Cyrl 1135, 10, // glh -> Arab 1139, 10, // glk -> Arab 1143, 265, // gml -> Latf - 1147, 120, // gmv -> Ethi + 1147, 125, // gmv -> Ethi 1151, 285, // gmy -> Linb 1155, 555, // goe -> Tibt - 1159, 120, // gof -> Ethi - 1163, 105, // goj -> Deva - 1167, 105, // gok -> Deva - 1171, 105, // gon -> Deva - 1175, 140, // got -> Goth - 1179, 105, // gra -> Deva - 1183, 95, // grc -> Cprt - 1187, 45, // grt -> Beng - 1191, 120, // gru -> Ethi - 1195, 155, // gu -> Gujr - 1198, 105, // gvr -> Deva + 1159, 125, // gof -> Ethi + 1163, 110, // goj -> Deva + 1167, 110, // gok -> Deva + 1171, 110, // gon -> Deva + 1175, 145, // got -> Goth + 1179, 110, // gra -> Deva + 1183, 155, // grc -> Grek + 1187, 50, // grt -> Beng + 1191, 125, // gru -> Ethi + 1195, 160, // gu -> Gujr + 1198, 110, // gvr -> Deva 1202, 10, // gwc -> Arab 1206, 10, // gwf -> Arab 1210, 10, // gwt -> Arab - 1214, 105, // gyo -> Deva + 1214, 110, // gyo -> Deva 1218, 10, // gzi -> Arab 1222, 10, // ha_CM -> Arab 1228, 10, // ha_SD -> Arab 1234, 10, // hac -> Arab - 1238, 175, // hak -> Hans - 1242, 120, // har -> Ethi - 1246, 10, // haz -> Arab - 1250, 185, // hbo -> Hebr - 1254, 120, // hdy -> Ethi - 1258, 185, // he -> Hebr - 1261, 105, // hi -> Deva - 1264, 105, // hif -> Deva - 1268, 505, // hii -> Takr - 1272, 590, // hit -> Xsux - 1276, 10, // hkh -> Arab - 1280, 105, // hlb -> Deva - 1284, 190, // hlu -> Hluw - 1288, 425, // hmd -> Plrd - 1292, 50, // hmj -> Bopo - 1296, 50, // hmq -> Bopo - 1300, 10, // hnd -> Arab - 1304, 105, // hne -> Deva - 1308, 195, // hnj -> Hmnp - 1312, 10, // hno -> Arab - 1316, 105, // hoc -> Deva - 1320, 10, // hoh -> Arab - 1324, 105, // hoj -> Deva - 1328, 170, // how -> Hani - 1332, 105, // hoy -> Deva - 1336, 355, // hpo -> Mymr - 1340, 495, // hrt -> Syrc - 1344, 10, // hrz -> Arab - 1348, 175, // hsn -> Hans - 1352, 10, // hss -> Arab - 1356, 590, // htx -> Xsux - 1360, 105, // hut -> Deva - 1364, 185, // huy -> Hebr - 1368, 100, // huz -> Cyrl - 1372, 20, // hy -> Armn - 1375, 20, // hyw -> Armn - 1379, 595, // ii -> Yiii - 1382, 295, // imy -> Lyci - 1386, 100, // inh -> Cyrl - 1390, 355, // int -> Mymr - 1394, 120, // ior -> Ethi - 1398, 520, // iru -> Taml - 1402, 10, // isk -> Arab - 1406, 185, // itk -> Hebr - 1410, 100, // itl -> Cyrl - 1414, 65, // iu -> Cans - 1417, 185, // iw -> Hebr - 1420, 210, // ja -> Jpan - 1423, 10, // jad -> Arab - 1427, 10, // jat -> Arab - 1431, 185, // jbe -> Hebr - 1435, 10, // jbn -> Arab - 1439, 100, // jct -> Cyrl - 1443, 555, // jda -> Tibt - 1447, 10, // jdg -> Arab - 1451, 100, // jdt -> Cyrl - 1455, 105, // jee -> Deva - 1459, 125, // jge -> Geor - 1463, 185, // ji -> Hebr - 1466, 165, // jje -> Hang - 1470, 355, // jkm -> Mymr - 1474, 105, // jml -> Deva - 1478, 505, // jna -> Takr - 1482, 10, // jnd -> Arab - 1486, 105, // jnl -> Deva - 1490, 105, // jns -> Deva - 1494, 10, // jog -> Arab - 1498, 185, // jpa -> Hebr - 1502, 185, // jpr -> Hebr - 1506, 185, // jrb -> Hebr - 1510, 105, // jul -> Deva - 1514, 395, // jun -> Orya - 1518, 395, // juy -> Orya - 1522, 555, // jya -> Tibt - 1526, 185, // jye -> Hebr - 1530, 125, // ka -> Geor - 1533, 100, // kaa -> Cyrl - 1537, 100, // kap -> Cyrl - 1541, 225, // kaw -> Kawi - 1545, 100, // kbd -> Cyrl - 1549, 555, // kbg -> Tibt - 1553, 10, // kbu -> Arab - 1557, 10, // kby -> Arab - 1561, 100, // kca -> Cyrl - 1565, 10, // kcy -> Arab - 1569, 45, // kdq -> Beng - 1573, 550, // kdt -> Thai - 1577, 100, // ket -> Cyrl - 1581, 330, // kev -> Mlym - 1585, 105, // kex -> Deva - 1589, 535, // key -> Telu - 1593, 245, // kfa -> Knda - 1597, 105, // kfb -> Deva - 1601, 535, // kfc -> Telu - 1605, 245, // kfd -> Knda - 1609, 520, // kfe -> Taml - 1613, 245, // kfg -> Knda - 1617, 330, // kfh -> Mlym - 1621, 520, // kfi -> Taml - 1625, 105, // kfk -> Deva - 1629, 10, // kfm -> Arab - 1633, 105, // kfp -> Deva - 1637, 105, // kfq -> Deva - 1641, 105, // kfr -> Deva - 1645, 105, // kfs -> Deva - 1649, 105, // kfu -> Deva - 1653, 105, // kfx -> Deva - 1657, 105, // kfy -> Deva - 1661, 105, // kgj -> Deva - 1665, 105, // kgy -> Deva - 1669, 515, // khb -> Talu - 1673, 550, // khf -> Thai - 1677, 555, // khg -> Tibt - 1681, 105, // khn -> Deva - 1685, 55, // kho -> Brah - 1689, 355, // kht -> Mymr - 1693, 100, // khv -> Cyrl - 1697, 10, // khw -> Arab - 1701, 105, // kif -> Deva - 1705, 100, // kim -> Cyrl - 1709, 105, // kip -> Deva - 1713, 260, // kjg -> Laoo - 1717, 100, // kjh -> Cyrl - 1721, 105, // kjl -> Deva - 1725, 105, // kjo -> Deva - 1729, 355, // kjp -> Mymr - 1733, 550, // kjt -> Thai - 1737, 555, // kjz -> Tibt - 1741, 100, // kk -> Cyrl - 1744, 10, // kk_AF -> Arab - 1750, 10, // kk_CN -> Arab - 1756, 10, // kk_IR -> Arab - 1762, 10, // kk_MN -> Arab - 1768, 555, // kkf -> Tibt - 1772, 255, // kkh -> Lana - 1776, 105, // kkt -> Deva - 1780, 105, // kle -> Deva - 1784, 10, // klj -> Arab - 1788, 105, // klr -> Deva - 1792, 235, // km -> Khmr - 1795, 105, // kmj -> Deva - 1799, 10, // kmz -> Arab - 1803, 245, // kn -> Knda - 1806, 105, // knn -> Deva - 1810, 250, // ko -> Kore - 1813, 100, // koi -> Cyrl - 1817, 105, // kok -> Deva - 1821, 100, // kpt -> Cyrl - 1825, 100, // kpy -> Cyrl - 1829, 495, // kqd -> Syrc - 1833, 120, // kqy -> Ethi - 1837, 105, // kra -> Deva - 1841, 100, // krc -> Cyrl - 1845, 100, // krk -> Cyrl - 1849, 235, // krr -> Khmr - 1853, 105, // kru -> Deva - 1857, 235, // krv -> Khmr - 1861, 10, // ks -> Arab - 1864, 355, // ksu -> Mymr - 1868, 355, // ksw -> Mymr - 1872, 105, // ksz -> Deva - 1876, 120, // ktb -> Ethi - 1880, 105, // kte -> Deva - 1884, 10, // ktl -> Arab - 1888, 425, // ktp -> Plrd - 1892, 10, // ku_LB -> Arab - 1898, 260, // kuf -> Laoo - 1902, 100, // kum -> Cyrl - 1906, 100, // kv -> Cyrl - 1909, 100, // kva -> Cyrl - 1913, 355, // kvq -> Mymr - 1917, 355, // kvt -> Mymr - 1921, 10, // kvx -> Arab - 1925, 215, // kvy -> Kali - 1929, 355, // kxf -> Mymr - 1933, 355, // kxk -> Mymr - 1937, 550, // kxm -> Thai - 1941, 10, // kxp -> Arab - 1945, 100, // ky -> Cyrl - 1948, 10, // ky_CN -> Arab - 1954, 215, // kyu -> Kali - 1958, 105, // kyv -> Deva - 1962, 105, // kyw -> Deva - 1966, 280, // lab -> Lina - 1970, 185, // lad -> Hebr - 1974, 105, // lae -> Deva - 1978, 10, // lah -> Arab - 1982, 100, // lbe -> Cyrl - 1986, 105, // lbf -> Deva - 1990, 555, // lbj -> Tibt - 1994, 105, // lbm -> Deva - 1998, 260, // lbo -> Laoo - 2002, 105, // lbr -> Deva - 2006, 550, // lcp -> Thai - 2010, 275, // lep -> Lepc - 2014, 100, // lez -> Cyrl - 2018, 105, // lhm -> Deva - 2022, 495, // lhs -> Syrc - 2026, 105, // lif -> Deva - 2030, 290, // lis -> Lisu - 2034, 555, // lkh -> Tibt - 2038, 10, // lki -> Arab - 2042, 105, // lmh -> Deva - 2046, 535, // lmn -> Telu - 2050, 260, // lo -> Laoo - 2053, 105, // loy -> Deva - 2057, 425, // lpo -> Plrd - 2061, 10, // lrc -> Arab - 2065, 10, // lrk -> Arab - 2069, 10, // lrl -> Arab - 2073, 10, // lsa -> Arab - 2077, 185, // lsd -> Hebr - 2081, 10, // lss -> Arab - 2085, 180, // ltc -> Hant - 2089, 555, // luk -> Tibt - 2093, 105, // luu -> Deva - 2097, 10, // luv -> Arab - 2101, 10, // luz -> Arab - 2105, 550, // lwl -> Thai - 2109, 550, // lwm -> Thai - 2113, 555, // lya -> Tibt - 2117, 175, // lzh -> Hans - 2121, 105, // mag -> Deva - 2125, 105, // mai -> Deva - 2129, 370, // man_GN -> Nkoo - 2136, 10, // mby -> Arab - 2140, 10, // mde -> Arab - 2144, 100, // mdf -> Cyrl - 2148, 120, // mdx -> Ethi - 2152, 120, // mdy -> Ethi - 2156, 10, // mfa -> Arab - 2160, 10, // mfi -> Arab - 2164, 270, // mga -> Latg - 2168, 105, // mgp -> Deva - 2172, 10, // mhj -> Arab - 2176, 305, // mid -> Mand - 2180, 105, // mjl -> Deva - 2184, 330, // mjq -> Mlym - 2188, 330, // mjr -> Mlym - 2192, 105, // mjt -> Deva - 2196, 535, // mju -> Telu - 2200, 330, // mjv -> Mlym - 2204, 105, // mjz -> Deva - 2208, 100, // mk -> Cyrl - 2211, 105, // mkb -> Deva - 2215, 105, // mke -> Deva - 2219, 10, // mki -> Arab - 2223, 550, // mkm -> Thai - 2227, 330, // ml -> Mlym - 2230, 550, // mlf -> Thai - 2234, 100, // mn -> Cyrl - 2237, 340, // mn_CN -> Mong - 2243, 340, // mnc -> Mong - 2247, 45, // mni -> Beng - 2251, 10, // mnj -> Arab - 2255, 100, // mns -> Cyrl - 2259, 355, // mnw -> Mymr - 2263, 550, // mpz -> Thai - 2267, 105, // mr -> Deva - 2270, 550, // mra -> Thai - 2274, 105, // mrd -> Deva - 2278, 100, // mrj -> Cyrl - 2282, 345, // mro -> Mroo - 2286, 105, // mrr -> Deva - 2290, 10, // ms_CC -> Arab - 2296, 100, // mtm -> Cyrl - 2300, 105, // mtr -> Deva - 2304, 100, // mud -> Cyrl - 2308, 555, // muk -> Tibt - 2312, 105, // mut -> Deva - 2316, 520, // muv -> Taml - 2320, 120, // muz -> Ethi - 2324, 10, // mve -> Arab - 2328, 340, // mvf -> Mong - 2332, 10, // mvy -> Arab - 2336, 120, // mvz -> Ethi - 2340, 105, // mwr -> Deva - 2344, 355, // mwt -> Mymr - 2348, 195, // mww -> Hmnp - 2352, 355, // my -> Mymr - 2355, 120, // mym -> Ethi - 2359, 100, // myv -> Cyrl - 2363, 305, // myz -> Mand - 2367, 10, // mzn -> Arab - 2371, 175, // nan -> Hans - 2375, 105, // nao -> Deva - 2379, 105, // ncd -> Deva - 2383, 260, // ncq -> Laoo - 2387, 100, // ndf -> Cyrl - 2391, 105, // ne -> Deva - 2394, 100, // neg -> Cyrl - 2398, 555, // neh -> Tibt - 2402, 590, // nei -> Xsux - 2406, 105, // new -> Deva - 2410, 260, // ngt -> Laoo - 2414, 100, // nio -> Cyrl - 2418, 535, // nit -> Telu - 2422, 100, // niv -> Cyrl - 2426, 10, // nli -> Arab - 2430, 10, // nlm -> Arab - 2434, 105, // nlx -> Deva - 2438, 105, // nmm -> Deva - 2442, 580, // nnp -> Wcho - 2446, 255, // nod -> Lana - 2450, 105, // noe -> Deva - 2454, 100, // nog -> Cyrl - 2458, 105, // noi -> Deva - 2462, 445, // non -> Runr - 2466, 595, // nos -> Yiii - 2470, 555, // npb -> Tibt - 2474, 370, // nqo -> Nkoo - 2478, 445, // nrn -> Runr - 2482, 595, // nsd -> Yiii - 2486, 595, // nsf -> Yiii - 2490, 65, // nsk -> Cans - 2494, 560, // nst -> Tnsa - 2498, 595, // nsv -> Yiii - 2502, 595, // nty -> Yiii - 2506, 10, // ntz -> Arab - 2510, 365, // nwc -> Newa - 2514, 105, // nwx -> Deva - 2518, 550, // nyl -> Thai - 2522, 10, // nyq -> Arab - 2526, 550, // nyw -> Thai - 2530, 100, // oaa -> Cyrl - 2534, 100, // oac -> Cyrl - 2538, 495, // oar -> Syrc - 2542, 125, // oav -> Geor - 2546, 420, // obm -> Phnx - 2550, 355, // obr -> Mymr - 2554, 10, // odk -> Arab - 2558, 590, // oht -> Xsux - 2562, 65, // oj -> Cans - 2565, 65, // ojs -> Cans - 2569, 165, // okm -> Hang - 2573, 170, // oko -> Hani - 2577, 235, // okz -> Khmr - 2581, 105, // ola -> Deva - 2585, 555, // ole -> Tibt - 2589, 100, // omk -> Cyrl - 2593, 350, // omp -> Mtei - 2597, 335, // omr -> Modi - 2601, 355, // omx -> Mymr - 2605, 105, // oon -> Deva - 2609, 395, // or -> Orya - 2612, 535, // ort -> Telu - 2616, 10, // oru -> Arab - 2620, 100, // orv -> Cyrl - 2624, 100, // os -> Cyrl - 2627, 400, // osa -> Osge - 2631, 200, // osc -> Ital - 2635, 205, // osi -> Java - 2639, 10, // ota -> Arab - 2643, 555, // otb -> Tibt - 2647, 390, // otk -> Orkh - 2651, 145, // oty -> Gran - 2655, 405, // oui -> Ougr - 2659, 160, // pa -> Guru - 2662, 10, // pa_PK -> Arab - 2668, 415, // pal -> Phli - 2672, 100, // paq -> Cyrl - 2676, 10, // pbt -> Arab - 2680, 235, // pcb -> Khmr - 2684, 355, // pce -> Mymr - 2688, 330, // pcf -> Mlym - 2692, 330, // pcg -> Mlym - 2696, 105, // pch -> Deva - 2700, 105, // pci -> Deva - 2704, 535, // pcj -> Telu - 2708, 395, // peg -> Orya - 2712, 585, // peo -> Xpeo - 2716, 230, // pgd -> Khar - 2720, 105, // pgg -> Deva - 2724, 380, // pgl -> Ogam - 2728, 200, // pgn -> Ital - 2732, 105, // phd -> Deva - 2736, 355, // phk -> Mymr - 2740, 10, // phl -> Arab - 2744, 420, // phn -> Phnx - 2748, 260, // pho -> Laoo - 2752, 10, // phr -> Arab - 2756, 550, // pht -> Thai - 2760, 550, // phu -> Thai - 2764, 10, // phv -> Arab - 2768, 105, // phw -> Deva - 2772, 470, // pi -> Sinh - 2775, 55, // pka -> Brah - 2779, 330, // pkr -> Mlym - 2783, 10, // plk -> Arab - 2787, 355, // pll -> Mymr - 2791, 55, // pmh -> Brah - 2795, 150, // pnt -> Grek - 2799, 230, // pra -> Khar - 2803, 10, // prc -> Arab - 2807, 10, // prd -> Arab - 2811, 550, // prt -> Thai - 2815, 10, // prx -> Arab - 2819, 10, // ps -> Arab - 2822, 10, // psh -> Arab - 2826, 10, // psi -> Arab - 2830, 10, // pst -> Arab - 2834, 55, // psu -> Brah - 2838, 105, // pum -> Deva - 2842, 355, // pwo -> Mymr - 2846, 105, // pwr -> Deva - 2850, 550, // pww -> Thai - 2854, 355, // pyx -> Mymr - 2858, 10, // qxq -> Arab - 2862, 105, // raa -> Deva - 2866, 105, // rab -> Deva - 2870, 105, // raf -> Deva - 2874, 45, // rah -> Beng - 2878, 105, // raj -> Deva - 2882, 105, // rav -> Deva - 2886, 355, // rbb -> Mymr - 2890, 10, // rdb -> Arab - 2894, 395, // rei -> Orya - 2898, 440, // rhg -> Rohg - 2902, 105, // rji -> Deva - 2906, 105, // rjs -> Deva - 2910, 235, // rka -> Khmr - 2914, 355, // rki -> Mymr - 2918, 45, // rkt -> Beng - 2922, 20, // rmi -> Armn - 2926, 10, // rmt -> Arab - 2930, 355, // rmz -> Mymr - 2934, 100, // rsk -> Cyrl - 2938, 105, // rtw -> Deva - 2942, 100, // ru -> Cyrl - 2945, 100, // rue -> Cyrl - 2949, 100, // rut -> Cyrl - 2953, 105, // rwr -> Deva - 2957, 220, // ryu -> Kana - 2961, 105, // sa -> Deva - 2964, 100, // sah -> Cyrl - 2968, 450, // sam -> Samr - 2972, 385, // sat -> Olck - 2976, 460, // saz -> Saur - 2980, 10, // sbn -> Arab - 2984, 555, // sbu -> Tibt - 2988, 105, // sck -> Deva - 2992, 10, // scl -> Arab - 2996, 105, // scp -> Deva - 3000, 260, // sct -> Laoo - 3004, 505, // scu -> Takr - 3008, 150, // scx -> Grek - 3012, 10, // sd -> Arab - 3015, 105, // sd_IN -> Deva - 3021, 10, // sdb -> Arab - 3025, 10, // sdf -> Arab - 3029, 10, // sdg -> Arab - 3033, 10, // sdh -> Arab - 3037, 45, // sdr -> Beng - 3041, 10, // sds -> Arab - 3045, 100, // sel -> Cyrl - 3049, 425, // sfm -> Plrd - 3053, 380, // sga -> Ogam - 3057, 100, // sgh -> Cyrl - 3061, 105, // sgj -> Deva - 3065, 10, // sgr -> Arab - 3069, 555, // sgt -> Tibt - 3073, 120, // sgw -> Ethi - 3077, 10, // sgy -> Arab - 3081, 10, // shd -> Arab - 3085, 540, // shi -> Tfng - 3089, 10, // shm -> Arab - 3093, 355, // shn -> Mymr - 3097, 10, // shu -> Arab - 3101, 10, // shv -> Arab - 3105, 470, // si -> Sinh - 3108, 100, // sia -> Cyrl - 3112, 555, // sip -> Tibt - 3116, 10, // siy -> Arab - 3120, 10, // siz -> Arab - 3124, 100, // sjd -> Cyrl - 3128, 105, // sjp -> Deva - 3132, 100, // sjt -> Cyrl - 3136, 550, // skb -> Thai - 3140, 105, // skj -> Deva - 3144, 10, // skr -> Arab - 3148, 595, // smh -> Yiii - 3152, 450, // smp -> Samr - 3156, 235, // smu -> Khmr - 3160, 10, // smy -> Arab - 3164, 530, // soa -> Tavt - 3168, 475, // sog -> Sogd - 3172, 105, // soi -> Deva - 3176, 550, // sou -> Thai - 3180, 555, // spt -> Tibt - 3184, 395, // spv -> Orya - 3188, 10, // sqo -> Arab - 3192, 260, // sqq -> Laoo - 3196, 10, // sqt -> Arab - 3200, 100, // sr -> Cyrl - 3203, 480, // srb -> Sora - 3207, 10, // srh -> Arab - 3211, 105, // srx -> Deva - 3215, 10, // srz -> Arab - 3219, 10, // ssh -> Arab - 3223, 260, // sss -> Laoo - 3227, 10, // sts -> Arab - 3231, 120, // stv -> Ethi - 3235, 100, // sty -> Cyrl - 3239, 490, // suz -> Sunu - 3243, 125, // sva -> Geor - 3247, 10, // swb -> Arab - 3251, 170, // swi -> Hani - 3255, 105, // swv -> Deva - 3259, 445, // sxu -> Runr - 3263, 495, // syc -> Syrc - 3267, 45, // syl -> Beng - 3271, 495, // syn -> Syrc - 3275, 495, // syr -> Syrc - 3279, 105, // syw -> Deva - 3283, 520, // ta -> Taml - 3286, 100, // tab -> Cyrl - 3290, 105, // taj -> Deva - 3294, 500, // tbk -> Tagb - 3298, 555, // tcn -> Tibt - 3302, 355, // tco -> Mymr - 3306, 520, // tcx -> Taml - 3310, 245, // tcy -> Knda - 3314, 540, // tda -> Tfng - 3318, 105, // tdb -> Deva - 3322, 510, // tdd -> Tale - 3326, 105, // tdg -> Deva - 3330, 105, // tdh -> Deva - 3334, 535, // te -> Telu - 3337, 205, // tes -> Java - 3341, 100, // tg -> Cyrl - 3344, 10, // tg_PK -> Arab - 3350, 105, // tge -> Deva - 3354, 555, // tgf -> Tibt - 3358, 550, // th -> Thai - 3361, 105, // the -> Deva - 3365, 105, // thf -> Deva - 3369, 510, // thi -> Tale - 3373, 105, // thl -> Deva - 3377, 550, // thm -> Thai - 3381, 105, // thq -> Deva - 3385, 105, // thr -> Deva - 3389, 105, // ths -> Deva - 3393, 120, // ti -> Ethi - 3396, 120, // tig -> Ethi - 3400, 105, // tij -> Deva - 3404, 100, // tin -> Cyrl - 3408, 355, // tjl -> Mymr - 3412, 10, // tjo -> Arab - 3416, 105, // tkb -> Deva - 3420, 10, // tks -> Arab - 3424, 105, // tkt -> Deva - 3428, 495, // tmr -> Syrc - 3432, 60, // tnv -> Cakm - 3436, 10, // tov -> Arab - 3440, 235, // tpu -> Khmr - 3444, 10, // tra -> Arab - 3448, 185, // trg -> Hebr - 3452, 10, // trm -> Arab - 3456, 10, // trw -> Arab - 3460, 150, // tsd -> Grek - 3464, 555, // tsj -> Tibt - 3468, 100, // tt -> Cyrl - 3471, 260, // tth -> Laoo - 3475, 260, // tto -> Laoo - 3479, 550, // tts -> Thai - 3483, 105, // ttz -> Deva - 3487, 355, // tvn -> Mymr - 3491, 105, // twm -> Deva - 3495, 525, // txg -> Tang - 3499, 565, // txo -> Toto - 3503, 530, // tyr -> Tavt - 3507, 100, // tyv -> Cyrl - 3511, 100, // ude -> Cyrl - 3515, 330, // udg -> Mlym - 3519, 100, // udi -> Cyrl - 3523, 100, // udm -> Cyrl - 3527, 10, // ug -> Arab - 3530, 100, // ug_KZ -> Cyrl - 3536, 100, // ug_MN -> Cyrl - 3542, 570, // uga -> Ugar - 3546, 100, // ugh -> Cyrl - 3550, 550, // ugo -> Thai - 3554, 100, // uk -> Cyrl - 3557, 395, // uki -> Orya - 3561, 100, // ulc -> Cyrl - 3565, 45, // unr -> Beng - 3569, 105, // unr_NP -> Deva - 3576, 45, // unx -> Beng - 3580, 10, // ur -> Arab - 3583, 550, // urk -> Thai - 3587, 10, // ush -> Arab - 3591, 150, // uum -> Grek - 3595, 10, // uz_AF -> Arab - 3601, 100, // uz_CN -> Cyrl - 3607, 10, // uzs -> Arab - 3611, 520, // vaa -> Taml - 3615, 10, // vaf -> Arab - 3619, 105, // vah -> Deva - 3623, 575, // vai -> Vaii - 3627, 105, // vas -> Deva - 3631, 105, // vav -> Deva - 3635, 105, // vay -> Deva - 3639, 10, // vgr -> Arab - 3643, 105, // vjk -> Deva - 3647, 245, // vmd -> Knda - 3651, 10, // vmh -> Arab - 3655, 120, // wal -> Ethi - 3659, 10, // wbk -> Arab - 3663, 535, // wbq -> Telu - 3667, 105, // wbr -> Deva - 3671, 120, // wle -> Ethi - 3675, 10, // wlo -> Arab - 3679, 105, // wme -> Deva - 3683, 10, // wne -> Arab - 3687, 10, // wni -> Arab - 3691, 130, // wsg -> Gong - 3695, 10, // wsv -> Arab - 3699, 105, // wtm -> Deva - 3703, 175, // wuu -> Hans - 3707, 0, // xag -> Aghb - 3711, 100, // xal -> Cyrl - 3715, 120, // xan -> Ethi - 3719, 100, // xas -> Cyrl - 3723, 85, // xco -> Chrs - 3727, 70, // xcr -> Cari - 3731, 100, // xdq -> Cyrl - 3735, 10, // xhe -> Arab - 3739, 235, // xhm -> Khmr - 3743, 395, // xis -> Orya - 3747, 10, // xka -> Arab - 3751, 10, // xkc -> Arab - 3755, 555, // xkf -> Tibt - 3759, 10, // xkj -> Arab - 3763, 10, // xkp -> Arab - 3767, 295, // xlc -> Lyci - 3771, 300, // xld -> Lydi - 3775, 115, // xly -> Elym - 3779, 125, // xmf -> Geor - 3783, 310, // xmn -> Mani - 3787, 325, // xmr -> Merc - 3791, 360, // xna -> Narb - 3795, 105, // xnr -> Deva - 3799, 150, // xpg -> Grek - 3803, 380, // xpi -> Ogam - 3807, 100, // xpm -> Cyrl - 3811, 430, // xpr -> Prti - 3815, 100, // xrm -> Cyrl - 3819, 100, // xrn -> Cyrl - 3823, 455, // xsa -> Sarb - 3827, 105, // xsr -> Deva - 3831, 55, // xtq -> Brah - 3835, 520, // xub -> Taml - 3839, 520, // xuj -> Taml - 3843, 200, // xve -> Ital - 3847, 10, // xvi -> Arab - 3851, 100, // xwo -> Cyrl - 3855, 315, // xzh -> Marc - 3859, 100, // yai -> Cyrl - 3863, 105, // ybh -> Deva - 3867, 105, // ybi -> Deva - 3871, 10, // ydg -> Arab - 3875, 330, // yea -> Mlym - 3879, 150, // yej -> Grek - 3883, 535, // yeu -> Telu - 3887, 425, // ygp -> Plrd - 3891, 185, // yhd -> Hebr - 3895, 185, // yi -> Hebr - 3898, 595, // yig -> Yiii - 3902, 185, // yih -> Hebr - 3906, 595, // yiv -> Yiii - 3910, 100, // ykg -> Cyrl - 3914, 100, // ykh -> Cyrl - 3918, 425, // yna -> Plrd - 3922, 100, // ynk -> Cyrl - 3926, 210, // yoi -> Jpan - 3930, 550, // yoy -> Thai - 3934, 100, // yrk -> Cyrl - 3938, 595, // ysd -> Yiii - 3942, 595, // ysn -> Yiii - 3946, 595, // ysp -> Yiii - 3950, 100, // ysr -> Cyrl - 3954, 425, // ysy -> Plrd - 3958, 185, // yud -> Hebr - 3962, 180, // yue -> Hant - 3966, 175, // yue_CN -> Hans - 3973, 100, // yug -> Cyrl - 3977, 100, // yux -> Cyrl - 3981, 425, // ywq -> Plrd - 3985, 425, // ywu -> Plrd - 3989, 555, // zau -> Tibt - 3993, 10, // zba -> Arab - 3997, 170, // zch -> Hani - 4001, 10, // zdj -> Arab - 4005, 170, // zeh -> Hani - 4009, 540, // zen -> Tfng - 4013, 170, // zgb -> Hani - 4017, 540, // zgh -> Tfng - 4021, 170, // zgm -> Hani - 4025, 170, // zgn -> Hani - 4029, 175, // zh -> Hans - 4032, 180, // zh_AU -> Hant - 4038, 180, // zh_BN -> Hant - 4044, 180, // zh_GB -> Hant - 4050, 180, // zh_GF -> Hant - 4056, 180, // zh_HK -> Hant - 4062, 180, // zh_ID -> Hant - 4068, 180, // zh_MO -> Hant - 4074, 180, // zh_PA -> Hant - 4080, 180, // zh_PF -> Hant - 4086, 180, // zh_PH -> Hant - 4092, 180, // zh_SR -> Hant - 4098, 180, // zh_TH -> Hant - 4104, 180, // zh_TW -> Hant - 4110, 180, // zh_US -> Hant - 4116, 180, // zh_VN -> Hant - 4122, 170, // zhd -> Hani - 4126, 375, // zhx -> Nshu - 4130, 100, // zko -> Cyrl - 4134, 240, // zkt -> Kits - 4138, 100, // zkz -> Cyrl - 4142, 170, // zlj -> Hani - 4146, 170, // zln -> Hani - 4150, 170, // zlq -> Hani - 4154, 170, // zqe -> Hani - 4158, 395, // zrg -> Orya - 4162, 185, // zrp -> Hebr - 4166, 10, // zum -> Arab - 4170, 120, // zwa -> Ethi - 4174, 170, // zyg -> Hani - 4178, 170, // zyn -> Hani - 4182, 170, // zzj -> Hani + 1238, 180, // hak -> Hans + 1242, 185, // hak_TW -> Hant + 1249, 125, // har -> Ethi + 1253, 10, // haz -> Arab + 1257, 190, // hbo -> Hebr + 1261, 125, // hdy -> Ethi + 1265, 190, // he -> Hebr + 1268, 110, // hi -> Deva + 1271, 110, // hif -> Deva + 1275, 505, // hii -> Takr + 1279, 590, // hit -> Xsux + 1283, 10, // hkh -> Arab + 1287, 110, // hlb -> Deva + 1291, 195, // hlu -> Hluw + 1295, 425, // hmd -> Plrd + 1299, 55, // hmj -> Bopo + 1303, 55, // hmq -> Bopo + 1307, 10, // hnd -> Arab + 1311, 110, // hne -> Deva + 1315, 200, // hnj -> Hmnp + 1319, 10, // hno -> Arab + 1323, 110, // hoc -> Deva + 1327, 10, // hoh -> Arab + 1331, 110, // hoj -> Deva + 1335, 175, // how -> Hani + 1339, 110, // hoy -> Deva + 1343, 355, // hpo -> Mymr + 1347, 495, // hrt -> Syrc + 1351, 10, // hrz -> Arab + 1355, 180, // hsn -> Hans + 1359, 10, // hss -> Arab + 1363, 590, // htx -> Xsux + 1367, 110, // hut -> Deva + 1371, 190, // huy -> Hebr + 1375, 105, // huz -> Cyrl + 1379, 20, // hy -> Armn + 1382, 20, // hyw -> Armn + 1386, 595, // ii -> Yiii + 1389, 295, // imy -> Lyci + 1393, 105, // inh -> Cyrl + 1397, 355, // int -> Mymr + 1401, 125, // ior -> Ethi + 1405, 520, // iru -> Taml + 1409, 10, // isk -> Arab + 1413, 190, // itk -> Hebr + 1417, 105, // itl -> Cyrl + 1421, 70, // iu -> Cans + 1424, 190, // iw -> Hebr + 1427, 215, // ja -> Jpan + 1430, 10, // jad -> Arab + 1434, 10, // jat -> Arab + 1438, 190, // jbe -> Hebr + 1442, 10, // jbn -> Arab + 1446, 105, // jct -> Cyrl + 1450, 555, // jda -> Tibt + 1454, 10, // jdg -> Arab + 1458, 105, // jdt -> Cyrl + 1462, 110, // jee -> Deva + 1466, 130, // jge -> Geor + 1470, 190, // ji -> Hebr + 1473, 170, // jje -> Hang + 1477, 355, // jkm -> Mymr + 1481, 110, // jml -> Deva + 1485, 505, // jna -> Takr + 1489, 10, // jnd -> Arab + 1493, 110, // jnl -> Deva + 1497, 110, // jns -> Deva + 1501, 10, // jog -> Arab + 1505, 190, // jpa -> Hebr + 1509, 190, // jpr -> Hebr + 1513, 190, // jrb -> Hebr + 1517, 110, // jul -> Deva + 1521, 395, // jun -> Orya + 1525, 395, // juy -> Orya + 1529, 555, // jya -> Tibt + 1533, 190, // jye -> Hebr + 1537, 130, // ka -> Geor + 1540, 105, // kaa -> Cyrl + 1544, 105, // kap -> Cyrl + 1548, 30, // kaw -> Bali + 1552, 105, // kbd -> Cyrl + 1556, 555, // kbg -> Tibt + 1560, 10, // kbu -> Arab + 1564, 10, // kby -> Arab + 1568, 105, // kca -> Cyrl + 1572, 10, // kcy -> Arab + 1576, 50, // kdq -> Beng + 1580, 550, // kdt -> Thai + 1584, 105, // ket -> Cyrl + 1588, 330, // kev -> Mlym + 1592, 110, // kex -> Deva + 1596, 535, // key -> Telu + 1600, 245, // kfa -> Knda + 1604, 110, // kfb -> Deva + 1608, 535, // kfc -> Telu + 1612, 245, // kfd -> Knda + 1616, 520, // kfe -> Taml + 1620, 245, // kfg -> Knda + 1624, 330, // kfh -> Mlym + 1628, 520, // kfi -> Taml + 1632, 110, // kfk -> Deva + 1636, 10, // kfm -> Arab + 1640, 110, // kfp -> Deva + 1644, 110, // kfq -> Deva + 1648, 110, // kfr -> Deva + 1652, 110, // kfs -> Deva + 1656, 110, // kfu -> Deva + 1660, 110, // kfx -> Deva + 1664, 110, // kfy -> Deva + 1668, 110, // kgj -> Deva + 1672, 110, // kgy -> Deva + 1676, 515, // khb -> Talu + 1680, 550, // khf -> Thai + 1684, 555, // khg -> Tibt + 1688, 110, // khn -> Deva + 1692, 60, // kho -> Brah + 1696, 355, // kht -> Mymr + 1700, 105, // khv -> Cyrl + 1704, 10, // khw -> Arab + 1708, 110, // kif -> Deva + 1712, 105, // kim -> Cyrl + 1716, 110, // kip -> Deva + 1720, 260, // kjg -> Laoo + 1724, 105, // kjh -> Cyrl + 1728, 110, // kjl -> Deva + 1732, 110, // kjo -> Deva + 1736, 355, // kjp -> Mymr + 1740, 550, // kjt -> Thai + 1744, 555, // kjz -> Tibt + 1748, 105, // kk -> Cyrl + 1751, 10, // kk_AF -> Arab + 1757, 10, // kk_CN -> Arab + 1763, 10, // kk_IR -> Arab + 1769, 10, // kk_MN -> Arab + 1775, 555, // kkf -> Tibt + 1779, 255, // kkh -> Lana + 1783, 110, // kkt -> Deva + 1787, 110, // kle -> Deva + 1791, 10, // klj -> Arab + 1795, 110, // klr -> Deva + 1799, 235, // km -> Khmr + 1802, 110, // kmj -> Deva + 1806, 10, // kmz -> Arab + 1810, 245, // kn -> Knda + 1813, 110, // knn -> Deva + 1817, 250, // ko -> Kore + 1820, 105, // koi -> Cyrl + 1824, 110, // kok -> Deva + 1828, 105, // kpt -> Cyrl + 1832, 105, // kpy -> Cyrl + 1836, 495, // kqd -> Syrc + 1840, 125, // kqy -> Ethi + 1844, 110, // kra -> Deva + 1848, 105, // krc -> Cyrl + 1852, 105, // krk -> Cyrl + 1856, 235, // krr -> Khmr + 1860, 110, // kru -> Deva + 1864, 235, // krv -> Khmr + 1868, 10, // ks -> Arab + 1871, 355, // ksu -> Mymr + 1875, 355, // ksw -> Mymr + 1879, 110, // ksz -> Deva + 1883, 125, // ktb -> Ethi + 1887, 110, // kte -> Deva + 1891, 10, // ktl -> Arab + 1895, 425, // ktp -> Plrd + 1899, 10, // ku_LB -> Arab + 1905, 260, // kuf -> Laoo + 1909, 105, // kum -> Cyrl + 1913, 105, // kv -> Cyrl + 1916, 105, // kva -> Cyrl + 1920, 355, // kvq -> Mymr + 1924, 355, // kvt -> Mymr + 1928, 10, // kvx -> Arab + 1932, 220, // kvy -> Kali + 1936, 355, // kxf -> Mymr + 1940, 355, // kxk -> Mymr + 1944, 550, // kxm -> Thai + 1948, 10, // kxp -> Arab + 1952, 105, // ky -> Cyrl + 1955, 10, // ky_CN -> Arab + 1961, 220, // kyu -> Kali + 1965, 110, // kyv -> Deva + 1969, 110, // kyw -> Deva + 1973, 280, // lab -> Lina + 1977, 190, // lad -> Hebr + 1981, 110, // lae -> Deva + 1985, 10, // lah -> Arab + 1989, 105, // lbe -> Cyrl + 1993, 110, // lbf -> Deva + 1997, 555, // lbj -> Tibt + 2001, 110, // lbm -> Deva + 2005, 260, // lbo -> Laoo + 2009, 110, // lbr -> Deva + 2013, 550, // lcp -> Thai + 2017, 275, // lep -> Lepc + 2021, 105, // lez -> Cyrl + 2025, 110, // lhm -> Deva + 2029, 495, // lhs -> Syrc + 2033, 110, // lif -> Deva + 2037, 290, // lis -> Lisu + 2041, 555, // lkh -> Tibt + 2045, 10, // lki -> Arab + 2049, 110, // lmh -> Deva + 2053, 535, // lmn -> Telu + 2057, 260, // lo -> Laoo + 2060, 110, // loy -> Deva + 2064, 425, // lpo -> Plrd + 2068, 10, // lrc -> Arab + 2072, 10, // lrk -> Arab + 2076, 10, // lrl -> Arab + 2080, 10, // lsa -> Arab + 2084, 190, // lsd -> Hebr + 2088, 10, // lss -> Arab + 2092, 185, // ltc -> Hant + 2096, 555, // luk -> Tibt + 2100, 110, // luu -> Deva + 2104, 10, // luv -> Arab + 2108, 10, // luz -> Arab + 2112, 550, // lwl -> Thai + 2116, 550, // lwm -> Thai + 2120, 555, // lya -> Tibt + 2124, 180, // lzh -> Hans + 2128, 130, // lzz_GE -> Geor + 2135, 110, // mag -> Deva + 2139, 110, // mai -> Deva + 2143, 10, // mby -> Arab + 2147, 10, // mde -> Arab + 2151, 105, // mdf -> Cyrl + 2155, 125, // mdx -> Ethi + 2159, 125, // mdy -> Ethi + 2163, 10, // mfa -> Arab + 2167, 10, // mfi -> Arab + 2171, 270, // mga -> Latg + 2175, 110, // mgp -> Deva + 2179, 10, // mhj -> Arab + 2183, 305, // mid -> Mand + 2187, 110, // mjl -> Deva + 2191, 330, // mjq -> Mlym + 2195, 330, // mjr -> Mlym + 2199, 110, // mjt -> Deva + 2203, 535, // mju -> Telu + 2207, 330, // mjv -> Mlym + 2211, 110, // mjz -> Deva + 2215, 105, // mk -> Cyrl + 2218, 110, // mkb -> Deva + 2222, 110, // mke -> Deva + 2226, 10, // mki -> Arab + 2230, 550, // mkm -> Thai + 2234, 330, // ml -> Mlym + 2237, 550, // mlf -> Thai + 2241, 105, // mn -> Cyrl + 2244, 340, // mn_CN -> Mong + 2250, 340, // mnc -> Mong + 2254, 50, // mni -> Beng + 2258, 10, // mnj -> Arab + 2262, 105, // mns -> Cyrl + 2266, 355, // mnw -> Mymr + 2270, 550, // mpz -> Thai + 2274, 110, // mr -> Deva + 2277, 550, // mra -> Thai + 2281, 110, // mrd -> Deva + 2285, 105, // mrj -> Cyrl + 2289, 345, // mro -> Mroo + 2293, 110, // mrr -> Deva + 2297, 10, // ms_CC -> Arab + 2303, 105, // mtm -> Cyrl + 2307, 110, // mtr -> Deva + 2311, 105, // mud -> Cyrl + 2315, 555, // muk -> Tibt + 2319, 110, // mut -> Deva + 2323, 520, // muv -> Taml + 2327, 125, // muz -> Ethi + 2331, 10, // mve -> Arab + 2335, 340, // mvf -> Mong + 2339, 10, // mvy -> Arab + 2343, 125, // mvz -> Ethi + 2347, 110, // mwr -> Deva + 2351, 355, // mwt -> Mymr + 2355, 200, // mww -> Hmnp + 2359, 355, // my -> Mymr + 2362, 125, // mym -> Ethi + 2366, 105, // myv -> Cyrl + 2370, 305, // myz -> Mand + 2374, 10, // mzn -> Arab + 2378, 180, // nan -> Hans + 2382, 185, // nan_TW -> Hant + 2389, 110, // nao -> Deva + 2393, 110, // ncd -> Deva + 2397, 260, // ncq -> Laoo + 2401, 105, // ndf -> Cyrl + 2405, 110, // ne -> Deva + 2408, 105, // neg -> Cyrl + 2412, 555, // neh -> Tibt + 2416, 590, // nei -> Xsux + 2420, 110, // new -> Deva + 2424, 260, // ngt -> Laoo + 2428, 105, // nio -> Cyrl + 2432, 535, // nit -> Telu + 2436, 105, // niv -> Cyrl + 2440, 10, // nli -> Arab + 2444, 10, // nlm -> Arab + 2448, 110, // nlx -> Deva + 2452, 110, // nmm -> Deva + 2456, 580, // nnp -> Wcho + 2460, 255, // nod -> Lana + 2464, 110, // noe -> Deva + 2468, 105, // nog -> Cyrl + 2472, 110, // noi -> Deva + 2476, 445, // non -> Runr + 2480, 595, // nos -> Yiii + 2484, 555, // npb -> Tibt + 2488, 370, // nqo -> Nkoo + 2492, 445, // nrn -> Runr + 2496, 595, // nsd -> Yiii + 2500, 595, // nsf -> Yiii + 2504, 70, // nsk -> Cans + 2508, 560, // nst -> Tnsa + 2512, 595, // nsv -> Yiii + 2516, 595, // nty -> Yiii + 2520, 10, // ntz -> Arab + 2524, 365, // nwc -> Newa + 2528, 110, // nwx -> Deva + 2532, 550, // nyl -> Thai + 2536, 10, // nyq -> Arab + 2540, 550, // nyw -> Thai + 2544, 105, // oaa -> Cyrl + 2548, 105, // oac -> Cyrl + 2552, 495, // oar -> Syrc + 2556, 130, // oav -> Geor + 2560, 420, // obm -> Phnx + 2564, 355, // obr -> Mymr + 2568, 10, // odk -> Arab + 2572, 590, // oht -> Xsux + 2576, 70, // oj -> Cans + 2579, 70, // ojs -> Cans + 2583, 170, // okm -> Hang + 2587, 175, // oko -> Hani + 2591, 235, // okz -> Khmr + 2595, 110, // ola -> Deva + 2599, 555, // ole -> Tibt + 2603, 105, // omk -> Cyrl + 2607, 350, // omp -> Mtei + 2611, 335, // omr -> Modi + 2615, 355, // omx -> Mymr + 2619, 110, // oon -> Deva + 2623, 395, // or -> Orya + 2626, 535, // ort -> Telu + 2630, 10, // oru -> Arab + 2634, 105, // orv -> Cyrl + 2638, 105, // os -> Cyrl + 2641, 400, // osa -> Osge + 2645, 205, // osc -> Ital + 2649, 210, // osi -> Java + 2653, 10, // ota -> Arab + 2657, 555, // otb -> Tibt + 2661, 390, // otk -> Orkh + 2665, 150, // oty -> Gran + 2669, 405, // oui -> Ougr + 2673, 165, // pa -> Guru + 2676, 10, // pa_PK -> Arab + 2682, 415, // pal -> Phli + 2686, 105, // paq -> Cyrl + 2690, 10, // pbt -> Arab + 2694, 235, // pcb -> Khmr + 2698, 355, // pce -> Mymr + 2702, 330, // pcf -> Mlym + 2706, 330, // pcg -> Mlym + 2710, 110, // pch -> Deva + 2714, 110, // pci -> Deva + 2718, 535, // pcj -> Telu + 2722, 395, // peg -> Orya + 2726, 585, // peo -> Xpeo + 2730, 230, // pgd -> Khar + 2734, 110, // pgg -> Deva + 2738, 380, // pgl -> Ogam + 2742, 205, // pgn -> Ital + 2746, 110, // phd -> Deva + 2750, 355, // phk -> Mymr + 2754, 10, // phl -> Arab + 2758, 420, // phn -> Phnx + 2762, 260, // pho -> Laoo + 2766, 10, // phr -> Arab + 2770, 550, // pht -> Thai + 2774, 550, // phu -> Thai + 2778, 10, // phv -> Arab + 2782, 110, // phw -> Deva + 2786, 470, // pi -> Sinh + 2789, 60, // pka -> Brah + 2793, 330, // pkr -> Mlym + 2797, 10, // plk -> Arab + 2801, 355, // pll -> Mymr + 2805, 60, // pmh -> Brah + 2809, 155, // pnt -> Grek + 2813, 105, // pnt_RU -> Cyrl + 2820, 230, // pra -> Khar + 2824, 10, // prc -> Arab + 2828, 10, // prd -> Arab + 2832, 550, // prt -> Thai + 2836, 10, // prx -> Arab + 2840, 10, // ps -> Arab + 2843, 10, // psh -> Arab + 2847, 10, // psi -> Arab + 2851, 10, // pst -> Arab + 2855, 60, // psu -> Brah + 2859, 110, // pum -> Deva + 2863, 355, // pwo -> Mymr + 2867, 110, // pwr -> Deva + 2871, 550, // pww -> Thai + 2875, 355, // pyx -> Mymr + 2879, 10, // qxq -> Arab + 2883, 110, // raa -> Deva + 2887, 110, // rab -> Deva + 2891, 110, // raf -> Deva + 2895, 50, // rah -> Beng + 2899, 110, // raj -> Deva + 2903, 110, // rav -> Deva + 2907, 355, // rbb -> Mymr + 2911, 10, // rdb -> Arab + 2915, 395, // rei -> Orya + 2919, 440, // rhg -> Rohg + 2923, 110, // rji -> Deva + 2927, 110, // rjs -> Deva + 2931, 235, // rka -> Khmr + 2935, 355, // rki -> Mymr + 2939, 50, // rkt -> Beng + 2943, 20, // rmi -> Armn + 2947, 10, // rmt -> Arab + 2951, 355, // rmz -> Mymr + 2955, 105, // rsk -> Cyrl + 2959, 110, // rtw -> Deva + 2963, 105, // ru -> Cyrl + 2966, 105, // rue -> Cyrl + 2970, 105, // rut -> Cyrl + 2974, 110, // rwr -> Deva + 2978, 225, // ryu -> Kana + 2982, 110, // sa -> Deva + 2985, 105, // sah -> Cyrl + 2989, 450, // sam -> Samr + 2993, 385, // sat -> Olck + 2997, 460, // saz -> Saur + 3001, 10, // sbn -> Arab + 3005, 555, // sbu -> Tibt + 3009, 110, // sck -> Deva + 3013, 10, // scl -> Arab + 3017, 110, // scp -> Deva + 3021, 260, // sct -> Laoo + 3025, 505, // scu -> Takr + 3029, 155, // scx -> Grek + 3033, 10, // sd -> Arab + 3036, 110, // sd_IN -> Deva + 3042, 10, // sdb -> Arab + 3046, 10, // sdf -> Arab + 3050, 10, // sdg -> Arab + 3054, 10, // sdh -> Arab + 3058, 50, // sdr -> Beng + 3062, 10, // sds -> Arab + 3066, 105, // sel -> Cyrl + 3070, 425, // sfm -> Plrd + 3074, 105, // sgh -> Cyrl + 3078, 110, // sgj -> Deva + 3082, 10, // sgr -> Arab + 3086, 555, // sgt -> Tibt + 3090, 125, // sgw -> Ethi + 3094, 10, // sgy -> Arab + 3098, 10, // shd -> Arab + 3102, 540, // shi -> Tfng + 3106, 10, // shm -> Arab + 3110, 355, // shn -> Mymr + 3114, 10, // shu -> Arab + 3118, 10, // shv -> Arab + 3122, 470, // si -> Sinh + 3125, 105, // sia -> Cyrl + 3129, 555, // sip -> Tibt + 3133, 10, // siy -> Arab + 3137, 10, // siz -> Arab + 3141, 105, // sjd -> Cyrl + 3145, 110, // sjp -> Deva + 3149, 105, // sjt -> Cyrl + 3153, 550, // skb -> Thai + 3157, 110, // skj -> Deva + 3161, 10, // skr -> Arab + 3165, 595, // smh -> Yiii + 3169, 450, // smp -> Samr + 3173, 235, // smu -> Khmr + 3177, 10, // smy -> Arab + 3181, 530, // soa -> Tavt + 3185, 475, // sog -> Sogd + 3189, 110, // soi -> Deva + 3193, 550, // sou -> Thai + 3197, 555, // spt -> Tibt + 3201, 395, // spv -> Orya + 3205, 10, // sqo -> Arab + 3209, 260, // sqq -> Laoo + 3213, 10, // sqt -> Arab + 3217, 105, // sr -> Cyrl + 3220, 480, // srb -> Sora + 3224, 10, // srh -> Arab + 3228, 110, // srx -> Deva + 3232, 10, // srz -> Arab + 3236, 10, // ssh -> Arab + 3240, 260, // sss -> Laoo + 3244, 10, // sts -> Arab + 3248, 125, // stv -> Ethi + 3252, 105, // sty -> Cyrl + 3256, 490, // suz -> Sunu + 3260, 130, // sva -> Geor + 3264, 10, // swb -> Arab + 3268, 175, // swi -> Hani + 3272, 110, // swv -> Deva + 3276, 445, // sxu -> Runr + 3280, 495, // syc -> Syrc + 3284, 50, // syl -> Beng + 3288, 495, // syn -> Syrc + 3292, 495, // syr -> Syrc + 3296, 110, // syw -> Deva + 3300, 520, // ta -> Taml + 3303, 105, // tab -> Cyrl + 3307, 110, // taj -> Deva + 3311, 500, // tbk -> Tagb + 3315, 555, // tcn -> Tibt + 3319, 355, // tco -> Mymr + 3323, 520, // tcx -> Taml + 3327, 245, // tcy -> Knda + 3331, 540, // tda -> Tfng + 3335, 110, // tdb -> Deva + 3339, 510, // tdd -> Tale + 3343, 110, // tdg -> Deva + 3347, 110, // tdh -> Deva + 3351, 535, // te -> Telu + 3354, 210, // tes -> Java + 3358, 105, // tg -> Cyrl + 3361, 10, // tg_PK -> Arab + 3367, 110, // tge -> Deva + 3371, 555, // tgf -> Tibt + 3375, 550, // th -> Thai + 3378, 110, // the -> Deva + 3382, 110, // thf -> Deva + 3386, 510, // thi -> Tale + 3390, 110, // thl -> Deva + 3394, 550, // thm -> Thai + 3398, 110, // thq -> Deva + 3402, 110, // thr -> Deva + 3406, 110, // ths -> Deva + 3410, 125, // ti -> Ethi + 3413, 125, // tig -> Ethi + 3417, 110, // tij -> Deva + 3421, 105, // tin -> Cyrl + 3425, 355, // tjl -> Mymr + 3429, 10, // tjo -> Arab + 3433, 110, // tkb -> Deva + 3437, 10, // tks -> Arab + 3441, 110, // tkt -> Deva + 3445, 495, // tmr -> Syrc + 3449, 65, // tnv -> Cakm + 3453, 10, // tov -> Arab + 3457, 235, // tpu -> Khmr + 3461, 10, // tra -> Arab + 3465, 190, // trg -> Hebr + 3469, 10, // trm -> Arab + 3473, 10, // trw -> Arab + 3477, 155, // tsd -> Grek + 3481, 555, // tsj -> Tibt + 3485, 105, // tt -> Cyrl + 3488, 260, // tth -> Laoo + 3492, 260, // tto -> Laoo + 3496, 550, // tts -> Thai + 3500, 110, // ttz -> Deva + 3504, 355, // tvn -> Mymr + 3508, 110, // twm -> Deva + 3512, 525, // txg -> Tang + 3516, 565, // txo -> Toto + 3520, 530, // tyr -> Tavt + 3524, 105, // tyv -> Cyrl + 3528, 105, // ude -> Cyrl + 3532, 330, // udg -> Mlym + 3536, 105, // udi -> Cyrl + 3540, 105, // udm -> Cyrl + 3544, 10, // ug -> Arab + 3547, 105, // ug_KZ -> Cyrl + 3553, 105, // ug_MN -> Cyrl + 3559, 570, // uga -> Ugar + 3563, 105, // ugh -> Cyrl + 3567, 550, // ugo -> Thai + 3571, 105, // uk -> Cyrl + 3574, 395, // uki -> Orya + 3578, 105, // ulc -> Cyrl + 3582, 50, // unr -> Beng + 3586, 110, // unr_NP -> Deva + 3593, 50, // unx -> Beng + 3597, 10, // ur -> Arab + 3600, 550, // urk -> Thai + 3604, 10, // ush -> Arab + 3608, 155, // uum -> Grek + 3612, 10, // uz_AF -> Arab + 3618, 105, // uz_CN -> Cyrl + 3624, 10, // uzs -> Arab + 3628, 520, // vaa -> Taml + 3632, 10, // vaf -> Arab + 3636, 110, // vah -> Deva + 3640, 575, // vai -> Vaii + 3644, 110, // vas -> Deva + 3648, 110, // vav -> Deva + 3652, 110, // vay -> Deva + 3656, 10, // vgr -> Arab + 3660, 110, // vjk -> Deva + 3664, 245, // vmd -> Knda + 3668, 10, // vmh -> Arab + 3672, 125, // wal -> Ethi + 3676, 10, // wbk -> Arab + 3680, 535, // wbq -> Telu + 3684, 110, // wbr -> Deva + 3688, 125, // wle -> Ethi + 3692, 10, // wlo -> Arab + 3696, 110, // wme -> Deva + 3700, 10, // wne -> Arab + 3704, 10, // wni -> Arab + 3708, 135, // wsg -> Gong + 3712, 10, // wsv -> Arab + 3716, 110, // wtm -> Deva + 3720, 180, // wuu -> Hans + 3724, 0, // xag -> Aghb + 3728, 105, // xal -> Cyrl + 3732, 125, // xan -> Ethi + 3736, 105, // xas -> Cyrl + 3740, 90, // xco -> Chrs + 3744, 75, // xcr -> Cari + 3748, 105, // xdq -> Cyrl + 3752, 10, // xhe -> Arab + 3756, 235, // xhm -> Khmr + 3760, 395, // xis -> Orya + 3764, 10, // xka -> Arab + 3768, 10, // xkc -> Arab + 3772, 555, // xkf -> Tibt + 3776, 10, // xkj -> Arab + 3780, 10, // xkp -> Arab + 3784, 295, // xlc -> Lyci + 3788, 300, // xld -> Lydi + 3792, 120, // xly -> Elym + 3796, 130, // xmf -> Geor + 3800, 310, // xmn -> Mani + 3804, 325, // xmr -> Merc + 3808, 360, // xna -> Narb + 3812, 110, // xnr -> Deva + 3816, 155, // xpg -> Grek + 3820, 380, // xpi -> Ogam + 3824, 105, // xpm -> Cyrl + 3828, 430, // xpr -> Prti + 3832, 105, // xrm -> Cyrl + 3836, 105, // xrn -> Cyrl + 3840, 455, // xsa -> Sarb + 3844, 110, // xsr -> Deva + 3848, 60, // xtq -> Brah + 3852, 520, // xub -> Taml + 3856, 520, // xuj -> Taml + 3860, 205, // xve -> Ital + 3864, 10, // xvi -> Arab + 3868, 105, // xwo -> Cyrl + 3872, 315, // xzh -> Marc + 3876, 105, // yai -> Cyrl + 3880, 110, // ybh -> Deva + 3884, 110, // ybi -> Deva + 3888, 10, // ydg -> Arab + 3892, 330, // yea -> Mlym + 3896, 155, // yej -> Grek + 3900, 535, // yeu -> Telu + 3904, 425, // ygp -> Plrd + 3908, 190, // yhd -> Hebr + 3912, 190, // yi -> Hebr + 3915, 595, // yig -> Yiii + 3919, 190, // yih -> Hebr + 3923, 595, // yiv -> Yiii + 3927, 105, // ykg -> Cyrl + 3931, 105, // ykh -> Cyrl + 3935, 425, // yna -> Plrd + 3939, 105, // ynk -> Cyrl + 3943, 215, // yoi -> Jpan + 3947, 550, // yoy -> Thai + 3951, 105, // yrk -> Cyrl + 3955, 595, // ysd -> Yiii + 3959, 595, // ysn -> Yiii + 3963, 595, // ysp -> Yiii + 3967, 105, // ysr -> Cyrl + 3971, 425, // ysy -> Plrd + 3975, 190, // yud -> Hebr + 3979, 185, // yue -> Hant + 3983, 180, // yue_CN -> Hans + 3990, 105, // yug -> Cyrl + 3994, 105, // yux -> Cyrl + 3998, 425, // ywq -> Plrd + 4002, 425, // ywu -> Plrd + 4006, 555, // zau -> Tibt + 4010, 10, // zba -> Arab + 4014, 175, // zch -> Hani + 4018, 10, // zdj -> Arab + 4022, 175, // zeh -> Hani + 4026, 540, // zen -> Tfng + 4030, 175, // zgb -> Hani + 4034, 540, // zgh -> Tfng + 4038, 175, // zgm -> Hani + 4042, 175, // zgn -> Hani + 4046, 180, // zh -> Hans + 4049, 185, // zh_AU -> Hant + 4055, 185, // zh_BN -> Hant + 4061, 185, // zh_GB -> Hant + 4067, 185, // zh_GF -> Hant + 4073, 185, // zh_HK -> Hant + 4079, 185, // zh_ID -> Hant + 4085, 185, // zh_MO -> Hant + 4091, 185, // zh_PA -> Hant + 4097, 185, // zh_PF -> Hant + 4103, 185, // zh_PH -> Hant + 4109, 185, // zh_SR -> Hant + 4115, 185, // zh_TH -> Hant + 4121, 185, // zh_TW -> Hant + 4127, 185, // zh_US -> Hant + 4133, 185, // zh_VN -> Hant + 4139, 175, // zhd -> Hani + 4143, 375, // zhx -> Nshu + 4147, 105, // zko -> Cyrl + 4151, 240, // zkt -> Kits + 4155, 105, // zkz -> Cyrl + 4159, 175, // zlj -> Hani + 4163, 175, // zln -> Hani + 4167, 175, // zlq -> Hani + 4171, 175, // zqe -> Hani + 4175, 395, // zrg -> Orya + 4179, 190, // zrp -> Hebr + 4183, 10, // zum -> Arab + 4187, 125, // zwa -> Ethi + 4191, 175, // zyg -> Hani + 4195, 175, // zyn -> Hani + 4199, 175, // zzj -> Hani }; //====================================================================== @@ -1159,38 +1162,39 @@ const char parentLocaleChars[] = "az_Arab\0az_Cyrl\0bal_Latn\0blt_Latn\0bm_Nkoo\0bs_Cyrl\0byn_Latn\0" "cu_Glag\0dje_Arab\0dyo_Arab\0en_001\0en_150\0en_AG\0en_AI\0en_AT\0" "en_AU\0en_BB\0en_BE\0en_BM\0en_BS\0en_BW\0en_BZ\0en_CC\0en_CH\0" - "en_CK\0en_CM\0en_CX\0en_CY\0en_DE\0en_DG\0en_DK\0en_DM\0en_Dsrt\0" - "en_ER\0en_FI\0en_FJ\0en_FK\0en_FM\0en_GB\0en_GD\0en_GG\0en_GH\0" - "en_GI\0en_GM\0en_GY\0en_HK\0en_ID\0en_IE\0en_IL\0en_IM\0en_IN\0" - "en_IO\0en_JE\0en_JM\0en_KE\0en_KI\0en_KN\0en_KY\0en_LC\0en_LR\0" - "en_LS\0en_MG\0en_MO\0en_MS\0en_MT\0en_MU\0en_MV\0en_MW\0en_MY\0" - "en_NA\0en_NF\0en_NG\0en_NL\0en_NR\0en_NU\0en_NZ\0en_PG\0en_PK\0" - "en_PN\0en_PW\0en_RW\0en_SB\0en_SC\0en_SD\0en_SE\0en_SG\0en_SH\0" - "en_SI\0en_SL\0en_SS\0en_SX\0en_SZ\0en_Shaw\0en_TC\0en_TK\0en_TO\0" - "en_TT\0en_TV\0en_TZ\0en_UG\0en_VC\0en_VG\0en_VU\0en_WS\0en_ZA\0" - "en_ZM\0en_ZW\0es_419\0es_AR\0es_BO\0es_BR\0es_BZ\0es_CL\0es_CO\0" - "es_CR\0es_CU\0es_DO\0es_EC\0es_GT\0es_HN\0es_JP\0es_MX\0es_NI\0" - "es_PA\0es_PE\0es_PR\0es_PY\0es_SV\0es_US\0es_UY\0es_VE\0ff_Adlm\0" - "ff_Arab\0fr_HT\0ha_Arab\0hi_Latn\0ht\0iu_Latn\0kaa_Latn\0kk_Arab\0" - "kok_Latn\0ks_Deva\0ku_Arab\0kxv_Deva\0kxv_Orya\0kxv_Telu\0ky_Arab\0" - "ky_Latn\0ml_Arab\0mn_Mong\0mni_Mtei\0ms_Arab\0nb\0nn\0no\0no_NO\0" - "pa_Arab\0pt_AO\0pt_CH\0pt_CV\0pt_FR\0pt_GQ\0pt_GW\0pt_LU\0pt_MO\0" - "pt_MZ\0pt_PT\0pt_ST\0pt_TL\0root\0sat_Deva\0sd_Deva\0sd_Khoj\0" - "sd_Sind\0shi_Latn\0so_Arab\0sr_Latn\0sw_Arab\0tg_Arab\0ug_Cyrl\0" - "uz_Arab\0uz_Cyrl\0vai_Latn\0wo_Arab\0yo_Arab\0yue_Hans\0zh_Hant\0" - "zh_Hant_HK\0zh_Hant_MO\0"; + "en_CK\0en_CM\0en_CX\0en_CY\0en_CZ\0en_DE\0en_DG\0en_DK\0en_DM\0" + "en_Dsrt\0en_ER\0en_ES\0en_FI\0en_FJ\0en_FK\0en_FM\0en_FR\0en_GB\0" + "en_GD\0en_GG\0en_GH\0en_GI\0en_GM\0en_GS\0en_GY\0en_HK\0en_HU\0" + "en_ID\0en_IE\0en_IL\0en_IM\0en_IN\0en_IO\0en_IT\0en_JE\0en_JM\0" + "en_KE\0en_KI\0en_KN\0en_KY\0en_LC\0en_LR\0en_LS\0en_MG\0en_MO\0" + "en_MS\0en_MT\0en_MU\0en_MV\0en_MW\0en_MY\0en_NA\0en_NF\0en_NG\0" + "en_NL\0en_NO\0en_NR\0en_NU\0en_NZ\0en_PG\0en_PK\0en_PL\0en_PN\0" + "en_PT\0en_PW\0en_RO\0en_RW\0en_SB\0en_SC\0en_SD\0en_SE\0en_SG\0" + "en_SH\0en_SI\0en_SK\0en_SL\0en_SS\0en_SX\0en_SZ\0en_Shaw\0en_TC\0" + "en_TK\0en_TO\0en_TT\0en_TV\0en_TZ\0en_UG\0en_VC\0en_VG\0en_VU\0" + "en_WS\0en_ZA\0en_ZM\0en_ZW\0es_419\0es_AR\0es_BO\0es_BR\0es_BZ\0" + "es_CL\0es_CO\0es_CR\0es_CU\0es_DO\0es_EC\0es_GT\0es_HN\0es_JP\0" + "es_MX\0es_NI\0es_PA\0es_PE\0es_PR\0es_PY\0es_SV\0es_US\0es_UY\0" + "es_VE\0ff_Adlm\0ff_Arab\0fr_HT\0ha_Arab\0hi_Latn\0ht\0iu_Latn\0" + "kaa_Latn\0kk_Arab\0kok_Latn\0ks_Deva\0ku_Arab\0kxv_Deva\0kxv_Orya\0" + "kxv_Telu\0ky_Arab\0ky_Latn\0ml_Arab\0mn_Mong\0mni_Mtei\0ms_Arab\0" + "nb\0nn\0no\0no_NO\0pa_Arab\0pt_AO\0pt_CH\0pt_CV\0pt_FR\0pt_GQ\0" + "pt_GW\0pt_LU\0pt_MO\0pt_MZ\0pt_PT\0pt_ST\0pt_TL\0root\0sat_Deva\0" + "sd_Deva\0sd_Khoj\0sd_Sind\0shi_Latn\0so_Arab\0sr_Latn\0sw_Arab\0" + "tg_Arab\0ug_Cyrl\0uz_Arab\0uz_Cyrl\0vai_Latn\0wo_Arab\0yo_Arab\0" + "yue_Hans\0zh_Hant\0zh_Hant_HK\0zh_Hant_MO\0"; const int32_t parentLocaleTable[] = { - 0, 1080, // az_Arab -> root - 8, 1080, // az_Cyrl -> root - 16, 1080, // bal_Latn -> root - 25, 1080, // blt_Latn -> root - 34, 1080, // bm_Nkoo -> root - 42, 1080, // bs_Cyrl -> root - 50, 1080, // byn_Latn -> root - 59, 1080, // cu_Glag -> root - 67, 1080, // dje_Arab -> root - 76, 1080, // dyo_Arab -> root + 0, 1146, // az_Arab -> root + 8, 1146, // az_Cyrl -> root + 16, 1146, // bal_Latn -> root + 25, 1146, // blt_Latn -> root + 34, 1146, // bm_Nkoo -> root + 42, 1146, // bs_Cyrl -> root + 50, 1146, // byn_Latn -> root + 59, 1146, // cu_Glag -> root + 67, 1146, // dje_Arab -> root + 76, 1146, // dyo_Arab -> root 92, 85, // en_150 -> en_001 99, 85, // en_AG -> en_001 105, 85, // en_AI -> en_001 @@ -1208,161 +1212,172 @@ const int32_t parentLocaleTable[] = { 177, 85, // en_CM -> en_001 183, 85, // en_CX -> en_001 189, 85, // en_CY -> en_001 - 195, 92, // en_DE -> en_150 - 201, 85, // en_DG -> en_001 - 207, 92, // en_DK -> en_150 - 213, 85, // en_DM -> en_001 - 219, 1080, // en_Dsrt -> root - 227, 85, // en_ER -> en_001 - 233, 92, // en_FI -> en_150 - 239, 85, // en_FJ -> en_001 - 245, 85, // en_FK -> en_001 - 251, 85, // en_FM -> en_001 - 257, 85, // en_GB -> en_001 - 263, 85, // en_GD -> en_001 - 269, 85, // en_GG -> en_001 - 275, 85, // en_GH -> en_001 - 281, 85, // en_GI -> en_001 - 287, 85, // en_GM -> en_001 - 293, 85, // en_GY -> en_001 - 299, 85, // en_HK -> en_001 - 305, 85, // en_ID -> en_001 - 311, 85, // en_IE -> en_001 - 317, 85, // en_IL -> en_001 - 323, 85, // en_IM -> en_001 - 329, 85, // en_IN -> en_001 - 335, 85, // en_IO -> en_001 - 341, 85, // en_JE -> en_001 - 347, 85, // en_JM -> en_001 - 353, 85, // en_KE -> en_001 - 359, 85, // en_KI -> en_001 - 365, 85, // en_KN -> en_001 - 371, 85, // en_KY -> en_001 - 377, 85, // en_LC -> en_001 - 383, 85, // en_LR -> en_001 - 389, 85, // en_LS -> en_001 - 395, 85, // en_MG -> en_001 - 401, 85, // en_MO -> en_001 - 407, 85, // en_MS -> en_001 - 413, 85, // en_MT -> en_001 - 419, 85, // en_MU -> en_001 - 425, 85, // en_MV -> en_001 - 431, 85, // en_MW -> en_001 - 437, 85, // en_MY -> en_001 - 443, 85, // en_NA -> en_001 - 449, 85, // en_NF -> en_001 - 455, 85, // en_NG -> en_001 - 461, 92, // en_NL -> en_150 - 467, 85, // en_NR -> en_001 - 473, 85, // en_NU -> en_001 - 479, 85, // en_NZ -> en_001 - 485, 85, // en_PG -> en_001 - 491, 85, // en_PK -> en_001 - 497, 85, // en_PN -> en_001 - 503, 85, // en_PW -> en_001 - 509, 85, // en_RW -> en_001 - 515, 85, // en_SB -> en_001 - 521, 85, // en_SC -> en_001 - 527, 85, // en_SD -> en_001 - 533, 92, // en_SE -> en_150 - 539, 85, // en_SG -> en_001 - 545, 85, // en_SH -> en_001 - 551, 92, // en_SI -> en_150 - 557, 85, // en_SL -> en_001 - 563, 85, // en_SS -> en_001 - 569, 85, // en_SX -> en_001 - 575, 85, // en_SZ -> en_001 - 581, 1080, // en_Shaw -> root - 589, 85, // en_TC -> en_001 - 595, 85, // en_TK -> en_001 - 601, 85, // en_TO -> en_001 - 607, 85, // en_TT -> en_001 - 613, 85, // en_TV -> en_001 - 619, 85, // en_TZ -> en_001 - 625, 85, // en_UG -> en_001 - 631, 85, // en_VC -> en_001 - 637, 85, // en_VG -> en_001 - 643, 85, // en_VU -> en_001 - 649, 85, // en_WS -> en_001 - 655, 85, // en_ZA -> en_001 - 661, 85, // en_ZM -> en_001 - 667, 85, // en_ZW -> en_001 - 680, 673, // es_AR -> es_419 - 686, 673, // es_BO -> es_419 - 692, 673, // es_BR -> es_419 - 698, 673, // es_BZ -> es_419 - 704, 673, // es_CL -> es_419 - 710, 673, // es_CO -> es_419 - 716, 673, // es_CR -> es_419 - 722, 673, // es_CU -> es_419 - 728, 673, // es_DO -> es_419 - 734, 673, // es_EC -> es_419 - 740, 673, // es_GT -> es_419 - 746, 673, // es_HN -> es_419 - 752, 673, // es_JP -> es_419 - 758, 673, // es_MX -> es_419 - 764, 673, // es_NI -> es_419 - 770, 673, // es_PA -> es_419 - 776, 673, // es_PE -> es_419 - 782, 673, // es_PR -> es_419 - 788, 673, // es_PY -> es_419 - 794, 673, // es_SV -> es_419 - 800, 673, // es_US -> es_419 - 806, 673, // es_UY -> es_419 - 812, 673, // es_VE -> es_419 - 818, 1080, // ff_Adlm -> root - 826, 1080, // ff_Arab -> root - 840, 1080, // ha_Arab -> root - 848, 329, // hi_Latn -> en_IN - 856, 834, // ht -> fr_HT - 859, 1080, // iu_Latn -> root - 867, 1080, // kaa_Latn -> root - 876, 1080, // kk_Arab -> root - 884, 1080, // kok_Latn -> root - 893, 1080, // ks_Deva -> root - 901, 1080, // ku_Arab -> root - 909, 1080, // kxv_Deva -> root - 918, 1080, // kxv_Orya -> root - 927, 1080, // kxv_Telu -> root - 936, 1080, // ky_Arab -> root - 944, 1080, // ky_Latn -> root - 952, 1080, // ml_Arab -> root - 960, 1080, // mn_Mong -> root - 968, 1080, // mni_Mtei -> root - 977, 1080, // ms_Arab -> root - 985, 991, // nb -> no - 988, 991, // nn -> no - 994, 991, // no_NO -> no - 1000, 1080, // pa_Arab -> root - 1008, 1062, // pt_AO -> pt_PT - 1014, 1062, // pt_CH -> pt_PT - 1020, 1062, // pt_CV -> pt_PT - 1026, 1062, // pt_FR -> pt_PT - 1032, 1062, // pt_GQ -> pt_PT - 1038, 1062, // pt_GW -> pt_PT - 1044, 1062, // pt_LU -> pt_PT - 1050, 1062, // pt_MO -> pt_PT - 1056, 1062, // pt_MZ -> pt_PT - 1068, 1062, // pt_ST -> pt_PT - 1074, 1062, // pt_TL -> pt_PT - 1085, 1080, // sat_Deva -> root - 1094, 1080, // sd_Deva -> root - 1102, 1080, // sd_Khoj -> root - 1110, 1080, // sd_Sind -> root - 1118, 1080, // shi_Latn -> root - 1127, 1080, // so_Arab -> root - 1135, 1080, // sr_Latn -> root - 1143, 1080, // sw_Arab -> root - 1151, 1080, // tg_Arab -> root - 1159, 1080, // ug_Cyrl -> root - 1167, 1080, // uz_Arab -> root - 1175, 1080, // uz_Cyrl -> root - 1183, 1080, // vai_Latn -> root - 1192, 1080, // wo_Arab -> root - 1200, 1080, // yo_Arab -> root - 1208, 1080, // yue_Hans -> root - 1217, 1080, // zh_Hant -> root - 1236, 1225, // zh_Hant_MO -> zh_Hant_HK + 195, 92, // en_CZ -> en_150 + 201, 92, // en_DE -> en_150 + 207, 85, // en_DG -> en_001 + 213, 92, // en_DK -> en_150 + 219, 85, // en_DM -> en_001 + 225, 1146, // en_Dsrt -> root + 233, 85, // en_ER -> en_001 + 239, 92, // en_ES -> en_150 + 245, 92, // en_FI -> en_150 + 251, 85, // en_FJ -> en_001 + 257, 85, // en_FK -> en_001 + 263, 85, // en_FM -> en_001 + 269, 92, // en_FR -> en_150 + 275, 85, // en_GB -> en_001 + 281, 85, // en_GD -> en_001 + 287, 85, // en_GG -> en_001 + 293, 85, // en_GH -> en_001 + 299, 85, // en_GI -> en_001 + 305, 85, // en_GM -> en_001 + 311, 85, // en_GS -> en_001 + 317, 85, // en_GY -> en_001 + 323, 85, // en_HK -> en_001 + 329, 92, // en_HU -> en_150 + 335, 85, // en_ID -> en_001 + 341, 85, // en_IE -> en_001 + 347, 85, // en_IL -> en_001 + 353, 85, // en_IM -> en_001 + 359, 85, // en_IN -> en_001 + 365, 85, // en_IO -> en_001 + 371, 92, // en_IT -> en_150 + 377, 85, // en_JE -> en_001 + 383, 85, // en_JM -> en_001 + 389, 85, // en_KE -> en_001 + 395, 85, // en_KI -> en_001 + 401, 85, // en_KN -> en_001 + 407, 85, // en_KY -> en_001 + 413, 85, // en_LC -> en_001 + 419, 85, // en_LR -> en_001 + 425, 85, // en_LS -> en_001 + 431, 85, // en_MG -> en_001 + 437, 85, // en_MO -> en_001 + 443, 85, // en_MS -> en_001 + 449, 85, // en_MT -> en_001 + 455, 85, // en_MU -> en_001 + 461, 85, // en_MV -> en_001 + 467, 85, // en_MW -> en_001 + 473, 85, // en_MY -> en_001 + 479, 85, // en_NA -> en_001 + 485, 85, // en_NF -> en_001 + 491, 85, // en_NG -> en_001 + 497, 92, // en_NL -> en_150 + 503, 92, // en_NO -> en_150 + 509, 85, // en_NR -> en_001 + 515, 85, // en_NU -> en_001 + 521, 85, // en_NZ -> en_001 + 527, 85, // en_PG -> en_001 + 533, 85, // en_PK -> en_001 + 539, 92, // en_PL -> en_150 + 545, 85, // en_PN -> en_001 + 551, 92, // en_PT -> en_150 + 557, 85, // en_PW -> en_001 + 563, 92, // en_RO -> en_150 + 569, 85, // en_RW -> en_001 + 575, 85, // en_SB -> en_001 + 581, 85, // en_SC -> en_001 + 587, 85, // en_SD -> en_001 + 593, 92, // en_SE -> en_150 + 599, 85, // en_SG -> en_001 + 605, 85, // en_SH -> en_001 + 611, 92, // en_SI -> en_150 + 617, 92, // en_SK -> en_150 + 623, 85, // en_SL -> en_001 + 629, 85, // en_SS -> en_001 + 635, 85, // en_SX -> en_001 + 641, 85, // en_SZ -> en_001 + 647, 1146, // en_Shaw -> root + 655, 85, // en_TC -> en_001 + 661, 85, // en_TK -> en_001 + 667, 85, // en_TO -> en_001 + 673, 85, // en_TT -> en_001 + 679, 85, // en_TV -> en_001 + 685, 85, // en_TZ -> en_001 + 691, 85, // en_UG -> en_001 + 697, 85, // en_VC -> en_001 + 703, 85, // en_VG -> en_001 + 709, 85, // en_VU -> en_001 + 715, 85, // en_WS -> en_001 + 721, 85, // en_ZA -> en_001 + 727, 85, // en_ZM -> en_001 + 733, 85, // en_ZW -> en_001 + 746, 739, // es_AR -> es_419 + 752, 739, // es_BO -> es_419 + 758, 739, // es_BR -> es_419 + 764, 739, // es_BZ -> es_419 + 770, 739, // es_CL -> es_419 + 776, 739, // es_CO -> es_419 + 782, 739, // es_CR -> es_419 + 788, 739, // es_CU -> es_419 + 794, 739, // es_DO -> es_419 + 800, 739, // es_EC -> es_419 + 806, 739, // es_GT -> es_419 + 812, 739, // es_HN -> es_419 + 818, 739, // es_JP -> es_419 + 824, 739, // es_MX -> es_419 + 830, 739, // es_NI -> es_419 + 836, 739, // es_PA -> es_419 + 842, 739, // es_PE -> es_419 + 848, 739, // es_PR -> es_419 + 854, 739, // es_PY -> es_419 + 860, 739, // es_SV -> es_419 + 866, 739, // es_US -> es_419 + 872, 739, // es_UY -> es_419 + 878, 739, // es_VE -> es_419 + 884, 1146, // ff_Adlm -> root + 892, 1146, // ff_Arab -> root + 906, 1146, // ha_Arab -> root + 914, 359, // hi_Latn -> en_IN + 922, 900, // ht -> fr_HT + 925, 1146, // iu_Latn -> root + 933, 1146, // kaa_Latn -> root + 942, 1146, // kk_Arab -> root + 950, 1146, // kok_Latn -> root + 959, 1146, // ks_Deva -> root + 967, 1146, // ku_Arab -> root + 975, 1146, // kxv_Deva -> root + 984, 1146, // kxv_Orya -> root + 993, 1146, // kxv_Telu -> root + 1002, 1146, // ky_Arab -> root + 1010, 1146, // ky_Latn -> root + 1018, 1146, // ml_Arab -> root + 1026, 1146, // mn_Mong -> root + 1034, 1146, // mni_Mtei -> root + 1043, 1146, // ms_Arab -> root + 1051, 1057, // nb -> no + 1054, 1057, // nn -> no + 1060, 1057, // no_NO -> no + 1066, 1146, // pa_Arab -> root + 1074, 1128, // pt_AO -> pt_PT + 1080, 1128, // pt_CH -> pt_PT + 1086, 1128, // pt_CV -> pt_PT + 1092, 1128, // pt_FR -> pt_PT + 1098, 1128, // pt_GQ -> pt_PT + 1104, 1128, // pt_GW -> pt_PT + 1110, 1128, // pt_LU -> pt_PT + 1116, 1128, // pt_MO -> pt_PT + 1122, 1128, // pt_MZ -> pt_PT + 1134, 1128, // pt_ST -> pt_PT + 1140, 1128, // pt_TL -> pt_PT + 1151, 1146, // sat_Deva -> root + 1160, 1146, // sd_Deva -> root + 1168, 1146, // sd_Khoj -> root + 1176, 1146, // sd_Sind -> root + 1184, 1146, // shi_Latn -> root + 1193, 1146, // so_Arab -> root + 1201, 1146, // sr_Latn -> root + 1209, 1146, // sw_Arab -> root + 1217, 1146, // tg_Arab -> root + 1225, 1146, // ug_Cyrl -> root + 1233, 1146, // uz_Arab -> root + 1241, 1146, // uz_Cyrl -> root + 1249, 1146, // vai_Latn -> root + 1258, 1146, // wo_Arab -> root + 1266, 1146, // yo_Arab -> root + 1274, 1146, // yue_Hans -> root + 1283, 1146, // zh_Hant -> root + 1302, 1291, // zh_Hant_MO -> zh_Hant_HK }; diff --git a/deps/icu-small/source/common/locbased.cpp b/deps/icu-small/source/common/locbased.cpp index 832bc3e88b142c..6f35e72210fef6 100644 --- a/deps/icu-small/source/common/locbased.cpp +++ b/deps/icu-small/source/common/locbased.cpp @@ -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 +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); + } } - if (actualID != nullptr) { - uprv_strncpy(actual, actualID, ULOC_FULLNAME_CAPACITY); - actual[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate +} + +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); + } } } -void LocaleBased::setLocaleIDs(const Locale& validID, const Locale& actualID) { - uprv_strcpy(valid, validID.getName()); - uprv_strcpy(actual, actualID.getName()); +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 diff --git a/deps/icu-small/source/common/locbased.h b/deps/icu-small/source/common/locbased.h index 2d260b527873d3..9441eb823107e3 100644 --- a/deps/icu-small/source/common/locbased.h +++ b/deps/icu-small/source/common/locbased.h @@ -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(validAlias)), actual(const_cast(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 diff --git a/deps/icu-small/source/common/locdispnames.cpp b/deps/icu-small/source/common/locdispnames.cpp index ddf7687a2bf07b..d3521e879b60c8 100644 --- a/deps/icu-small/source/common/locdispnames.cpp +++ b/deps/icu-small/source/common/locdispnames.cpp @@ -19,6 +19,8 @@ * that then do not depend on resource bundle code and display name data. */ +#include + #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)) { diff --git a/deps/icu-small/source/common/locid.cpp b/deps/icu-small/source/common/locid.cpp index 4a73f559205232..e7e86079ae9169 100644 --- a/deps/icu-small/source/common/locid.cpp +++ b/deps/icu-small/source/common/locid.cpp @@ -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(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(""); diff --git a/deps/icu-small/source/common/loclikely.cpp b/deps/icu-small/source/common/loclikely.cpp index ccbcbfa7a5d7a1..f87fd8dd61ca13 100644 --- a/deps/icu-small/source/common/loclikely.cpp +++ b/deps/icu-small/source/common/loclikely.cpp @@ -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); } } } diff --git a/deps/icu-small/source/common/loclikelysubtags.cpp b/deps/icu-small/source/common/loclikelysubtags.cpp index 7c6131197d894b..7245a779816ce7 100644 --- a/deps/icu-small/source/common/loclikelysubtags.cpp +++ b/deps/icu-small/source/common/loclikelysubtags.cpp @@ -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); diff --git a/deps/icu-small/source/common/locresdata.cpp b/deps/icu-small/source/common/locresdata.cpp index 725e6609159896..ba7163fa2dbac7 100644 --- a/deps/icu-small/source/common/locresdata.cpp +++ b/deps/icu-small/source/common/locresdata.cpp @@ -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; } diff --git a/deps/icu-small/source/common/punycode.cpp b/deps/icu-small/source/common/punycode.cpp index aa02298c5e6d07..1868a07a856a78 100644 --- a/deps/icu-small/source/common/punycode.cpp +++ b/deps/icu-small/source/common/punycode.cpp @@ -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; } diff --git a/deps/icu-small/source/common/putil.cpp b/deps/icu-small/source/common/putil.cpp index 4cf07797ba350e..ea15fdff0b0c67 100644 --- a/deps/icu-small/source/common/putil.cpp +++ b/deps/icu-small/source/common/putil.cpp @@ -76,7 +76,7 @@ #include #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 diff --git a/deps/icu-small/source/common/rbbinode.cpp b/deps/icu-small/source/common/rbbinode.cpp index 71407b9e684eea..849ee7180a22f1 100644 --- a/deps/icu-small/source/common/rbbinode.cpp +++ b/deps/icu-small/source/common/rbbinode.cpp @@ -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); } } } diff --git a/deps/icu-small/source/common/rbbinode.h b/deps/icu-small/source/common/rbbinode.h index 497a31b8d098b3..8fbc9d1b588e05 100644 --- a/deps/icu-small/source/common/rbbinode.h +++ b/deps/icu-small/source/common/rbbinode.h @@ -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 diff --git a/deps/icu-small/source/common/rbbiscan.cpp b/deps/icu-small/source/common/rbbiscan.cpp index cf2d63cd807b0f..77fc3bcd9b7e29 100644 --- a/deps/icu-small/source/common/rbbiscan.cpp +++ b/deps/icu-small/source/common/rbbiscan.cpp @@ -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; } diff --git a/deps/icu-small/source/common/rbbisetb.cpp b/deps/icu-small/source/common/rbbisetb.cpp index 6c22cf470f8b2f..df94fc8bc4fb86 100644 --- a/deps/icu-small/source/common/rbbisetb.cpp +++ b/deps/icu-small/source/common/rbbisetb.cpp @@ -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; diff --git a/deps/icu-small/source/common/rbbitblb.cpp b/deps/icu-small/source/common/rbbitblb.cpp index 4d95137601efb1..b89909807c2023 100644 --- a/deps/icu-small/source/common/rbbitblb.cpp +++ b/deps/icu-small/source/common/rbbitblb.cpp @@ -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."); diff --git a/deps/icu-small/source/common/resbund.cpp b/deps/icu-small/source/common/resbund.cpp index 41337cdc155daf..4c14dae133eefc 100644 --- a/deps/icu-small/source/common/resbund.cpp +++ b/deps/icu-small/source/common/resbund.cpp @@ -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); } diff --git a/deps/icu-small/source/common/ucnvmbcs.cpp b/deps/icu-small/source/common/ucnvmbcs.cpp index f5507043bf35be..d65c284746163b 100644 --- a/deps/icu-small/source/common/ucnvmbcs.cpp +++ b/deps/icu-small/source/common/ucnvmbcs.cpp @@ -3146,11 +3146,8 @@ ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, if(c<0) { if(U_SUCCESS(*pErrorCode) && source==sourceLimit && lastSourcetoUBytes; cnv->toULength = static_cast(source - lastSource); - do { - *bytes++=*lastSource++; - } while(lastSourcetoUBytes, lastSource, cnv->toULength); *pErrorCode=U_TRUNCATED_CHAR_FOUND; } else if(U_FAILURE(*pErrorCode)) { /* callback(illegal) */ diff --git a/deps/icu-small/source/common/ucurr.cpp b/deps/icu-small/source/common/ucurr.cpp index b74a80a676afc5..cccf1130ae8e63 100644 --- a/deps/icu-small/source/common/ucurr.cpp +++ b/deps/icu-small/source/common/ucurr.cpp @@ -372,12 +372,8 @@ struct CReg : public icu::UMemory { CReg(const char16_t* _iso, const char* _id) : next(nullptr) { - int32_t len = static_cast(uprv_strlen(_id)); - if (len > static_cast(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; diff --git a/deps/icu-small/source/common/uloc.cpp b/deps/icu-small/source/common/uloc.cpp index 51887c97c3e1e3..bea4827a04984c 100644 --- a/deps/icu-small/source/common/uloc.cpp +++ b/deps/icu-small/source/common/uloc.cpp @@ -482,8 +482,8 @@ constexpr CanonicalizationMap CANONICALIZE_MAP[] = { /* ### BCP47 Conversion *******************************************/ /* Gets the size of the shortest subtag in the given localeID. */ -int32_t getShortestSubtagLength(const char *localeID) { - int32_t localeIDLength = static_cast(uprv_strlen(localeID)); +int32_t getShortestSubtagLength(std::string_view localeID) { + int32_t localeIDLength = static_cast(localeID.length()); int32_t length = localeIDLength; int32_t tmpLength = 0; int32_t i; @@ -507,8 +507,8 @@ int32_t getShortestSubtagLength(const char *localeID) { return length; } /* Test if the locale id has BCP47 u extension and does not have '@' */ -inline bool _hasBCP47Extension(const char *id) { - return id != nullptr && uprv_strstr(id, "@") == nullptr && getShortestSubtagLength(id) == 1; +inline bool _hasBCP47Extension(std::string_view id) { + return id.find('@') == std::string_view::npos && getShortestSubtagLength(id) == 1; } /* ### Keywords **************************************************/ @@ -523,10 +523,9 @@ inline bool UPRV_OK_VALUE_PUNCTUATION(char c) { return c == '_' || c == '-' || c #define ULOC_MAX_NO_KEYWORDS 25 U_CAPI const char * U_EXPORT2 -locale_getKeywordsStart(const char *localeID) { - const char *result = nullptr; - if((result = uprv_strchr(localeID, '@')) != nullptr) { - return result; +locale_getKeywordsStart(std::string_view localeID) { + if (size_t pos = localeID.find('@'); pos != std::string_view::npos) { + return localeID.data() + pos; } #if (U_CHARSET_FAMILY == U_EBCDIC_FAMILY) else { @@ -536,8 +535,8 @@ locale_getKeywordsStart(const char *localeID) { static const uint8_t ebcdicSigns[] = { 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; const uint8_t *charToFind = ebcdicSigns; while(*charToFind) { - if((result = uprv_strchr(localeID, *charToFind)) != nullptr) { - return result; + if (size_t pos = localeID.find(*charToFind); pos != std::string_view::npos) { + return localeID.data() + pos; } charToFind++; } @@ -590,7 +589,7 @@ compareKeywordStructs(const void * /*context*/, const void *left, const void *ri } // namespace U_EXPORT CharString -ulocimp_getKeywords(const char* localeID, +ulocimp_getKeywords(std::string_view localeID, char prev, bool valuesToo, UErrorCode& status) @@ -607,7 +606,7 @@ ulocimp_getKeywords(const char* localeID, } U_EXPORT void -ulocimp_getKeywords(const char* localeID, +ulocimp_getKeywords(std::string_view localeID, char prev, ByteSink& sink, bool valuesToo, @@ -619,9 +618,8 @@ ulocimp_getKeywords(const char* localeID, int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS; int32_t numKeywords = 0; - const char* pos = localeID; - const char* equalSign = nullptr; - const char* semicolon = nullptr; + size_t equalSign = std::string_view::npos; + size_t semicolon = std::string_view::npos; int32_t i = 0, j, n; if(prev == '@') { /* start of keyword definition */ @@ -629,74 +627,72 @@ ulocimp_getKeywords(const char* localeID, do { bool duplicate = false; /* skip leading spaces */ - while(*pos == ' ') { - pos++; + while (localeID.front() == ' ') { + localeID.remove_prefix(1); } - if (!*pos) { /* handle trailing "; " */ + if (localeID.empty()) { /* handle trailing "; " */ break; } if(numKeywords == maxKeywords) { status = U_INTERNAL_PROGRAM_ERROR; return; } - equalSign = uprv_strchr(pos, '='); - semicolon = uprv_strchr(pos, ';'); + equalSign = localeID.find('='); + semicolon = localeID.find(';'); /* lack of '=' [foo@currency] is illegal */ /* ';' before '=' [foo@currency;collation=pinyin] is illegal */ - if(!equalSign || (semicolon && semicolon= ULOC_KEYWORD_BUFFER_LEN) { + if (equalSign >= ULOC_KEYWORD_BUFFER_LEN) { /* keyword name too long for internal buffer */ status = U_INTERNAL_PROGRAM_ERROR; return; } - for(i = 0, n = 0; i < equalSign - pos; ++i) { - if (pos[i] != ' ') { - keywordList[numKeywords].keyword[n++] = uprv_tolower(pos[i]); + for (i = 0, n = 0; static_cast(i) < equalSign; ++i) { + if (localeID[i] != ' ') { + keywordList[numKeywords].keyword[n++] = uprv_tolower(localeID[i]); } } - /* zero-length keyword is an error. */ - if (n == 0) { - status = U_INVALID_FORMAT_ERROR; - return; - } - keywordList[numKeywords].keyword[n] = 0; keywordList[numKeywords].keywordLen = n; /* now grab the value part. First we skip the '=' */ equalSign++; /* then we leading spaces */ - while(*equalSign == ' ') { + while (equalSign < localeID.length() && localeID[equalSign] == ' ') { equalSign++; } /* Premature end or zero-length value */ - if (!*equalSign || equalSign == semicolon) { + if (equalSign == localeID.length() || equalSign == semicolon) { status = U_INVALID_FORMAT_ERROR; return; } - keywordList[numKeywords].valueStart = equalSign; + keywordList[numKeywords].valueStart = localeID.data() + equalSign; - pos = semicolon; - i = 0; - if(pos) { - while(*(pos - i - 1) == ' ') { - i++; - } - keywordList[numKeywords].valueLen = static_cast(pos - equalSign - i); - pos++; + std::string_view value = localeID; + if (semicolon != std::string_view::npos) { + value.remove_suffix(value.length() - semicolon); + localeID.remove_prefix(semicolon + 1); } else { - i = static_cast(uprv_strlen(equalSign)); - while(i && equalSign[i-1] == ' ') { - i--; - } - keywordList[numKeywords].valueLen = i; + localeID = {}; } + value.remove_prefix(equalSign); + if (size_t last = value.find_last_not_of(' '); last != std::string_view::npos) { + value.remove_suffix(value.length() - last - 1); + } + keywordList[numKeywords].valueLen = static_cast(value.length()); + /* If this is a duplicate keyword, then ignore it */ for (j=0; j(locale_getKeywordsStart(buffer)); + char* keywords = const_cast( + locale_getKeywordsStart({buffer, static_cast(bufLen)})); int32_t baseLen = keywords == nullptr ? bufLen : keywords - buffer; // Remove -1 from the capacity so that this function can guarantee NUL termination. CheckedArrayByteSink sink(keywords == nullptr ? buffer + bufLen : keywords, @@ -921,7 +918,7 @@ ulocimp_setKeywordValue(std::string_view keywordName, { if (U_FAILURE(status)) { return; } std::string_view keywords; - if (const char* start = locale_getKeywordsStart(localeID.data()); start != nullptr) { + if (const char* start = locale_getKeywordsStart(localeID.toStringPiece()); start != nullptr) { // This is safe because CharString::truncate() doesn't actually erase any // data, but simply sets the position for where new data will be written. int32_t size = start - localeID.data(); @@ -1138,15 +1135,18 @@ inline bool _isPrefixLetter(char a) { return a == 'x' || a == 'X' || a == 'i' || /*returns true if one of the special prefixes is here (s=string) 'x-' or 'i-' */ -inline bool _isIDPrefix(const char *s) { return _isPrefixLetter(s[0]) && _isIDSeparator(s[1]); } +inline bool _isIDPrefix(std::string_view s) { + return s.size() >= 2 && _isPrefixLetter(s[0]) && _isIDSeparator(s[1]); +} /* Dot terminates it because of POSIX form where dot precedes the codepage * except for variant */ -inline bool _isTerminator(char a) { return a == 0 || a == '.' || a == '@'; } +inline bool _isTerminator(char a) { return a == '.' || a == '@'; } -inline bool _isBCP47Extension(const char* p) { - return p[0] == '-' && +inline bool _isBCP47Extension(std::string_view p) { + return p.size() >= 3 && + p[0] == '-' && (p[1] == 't' || p[1] == 'T' || p[1] == 'u' || p[1] == 'U' || p[1] == 'x' || p[1] == 'X') && @@ -1202,49 +1202,44 @@ namespace { * TODO try to use this in Locale */ -void -_getLanguage(const char* localeID, - ByteSink* sink, - const char** pEnd, - UErrorCode& status) { - U_ASSERT(pEnd != nullptr); - *pEnd = localeID; - - if (uprv_stricmp(localeID, "root") == 0) { - localeID += 4; - } else if (uprv_strnicmp(localeID, "und", 3) == 0 && - (localeID[3] == '\0' || +size_t _getLanguage(std::string_view localeID, ByteSink* sink, UErrorCode& status) { + size_t skip = 0; + if (localeID.size() == 4 && uprv_strnicmp(localeID.data(), "root", 4) == 0) { + skip = 4; + localeID.remove_prefix(skip); + } else if (localeID.size() >= 3 && uprv_strnicmp(localeID.data(), "und", 3) == 0 && + (localeID.size() == 3 || localeID[3] == '-' || localeID[3] == '_' || localeID[3] == '@')) { - localeID += 3; + skip = 3; + localeID.remove_prefix(skip); } constexpr int32_t MAXLEN = ULOC_LANG_CAPACITY - 1; // Minus NUL. /* if it starts with i- or x- then copy that prefix */ - int32_t len = _isIDPrefix(localeID) ? 2 : 0; - while (!_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len])) { + size_t len = _isIDPrefix(localeID) ? 2 : 0; + while (len < localeID.size() && !_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len])) { if (len == MAXLEN) { status = U_ILLEGAL_ARGUMENT_ERROR; - return; + return 0; } len++; } - *pEnd = localeID + len; - if (sink == nullptr || len == 0) { return; } + if (sink == nullptr || len == 0) { return skip + len; } - int32_t minCapacity = uprv_max(len, 4); // Minimum 3 letters plus NUL. + int32_t minCapacity = uprv_max(static_cast(len), 4); // Minimum 3 letters plus NUL. char scratch[MAXLEN]; int32_t capacity = 0; char* buffer = sink->GetAppendBuffer( minCapacity, minCapacity, scratch, UPRV_LENGTHOF(scratch), &capacity); - for (int32_t i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { buffer[i] = uprv_tolower(localeID[i]); } - if (_isIDSeparator(localeID[1])) { + if (localeID.size() >= 2 && _isIDSeparator(localeID[1])) { buffer[1] = '-'; } @@ -1256,32 +1251,26 @@ _getLanguage(const char* localeID, if (offset.has_value()) { const char* const alias = LANGUAGES[*offset]; sink->Append(alias, static_cast(uprv_strlen(alias))); - return; + return skip + len; } } - sink->Append(buffer, len); + sink->Append(buffer, static_cast(len)); + return skip + len; } -void -_getScript(const char* localeID, - ByteSink* sink, - const char** pEnd) { - U_ASSERT(pEnd != nullptr); - *pEnd = localeID; - +size_t _getScript(std::string_view localeID, ByteSink* sink) { constexpr int32_t LENGTH = 4; - int32_t len = 0; - while (!_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len]) && + size_t len = 0; + while (len < localeID.size() && !_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len]) && uprv_isASCIILetter(localeID[len])) { - if (len == LENGTH) { return; } + if (len == LENGTH) { return 0; } len++; } - if (len != LENGTH) { return; } + if (len != LENGTH) { return 0; } - *pEnd = localeID + LENGTH; - if (sink == nullptr) { return; } + if (sink == nullptr) { return len; } char scratch[LENGTH]; int32_t capacity = 0; @@ -1294,27 +1283,21 @@ _getScript(const char* localeID, } sink->Append(buffer, LENGTH); + return len; } -void -_getRegion(const char* localeID, - ByteSink* sink, - const char** pEnd) { - U_ASSERT(pEnd != nullptr); - *pEnd = localeID; - +size_t _getRegion(std::string_view localeID, ByteSink* sink) { constexpr int32_t MINLEN = 2; constexpr int32_t MAXLEN = ULOC_COUNTRY_CAPACITY - 1; // Minus NUL. - int32_t len = 0; - while (!_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len])) { - if (len == MAXLEN) { return; } + size_t len = 0; + while (len < localeID.size() && !_isTerminator(localeID[len]) && !_isIDSeparator(localeID[len])) { + if (len == MAXLEN) { return 0; } len++; } - if (len < MINLEN) { return; } + if (len < MINLEN) { return 0; } - *pEnd = localeID + len; - if (sink == nullptr) { return; } + if (sink == nullptr) { return len; } char scratch[ULOC_COUNTRY_CAPACITY]; int32_t capacity = 0; @@ -1325,7 +1308,7 @@ _getRegion(const char* localeID, UPRV_LENGTHOF(scratch), &capacity); - for (int32_t i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { buffer[i] = uprv_toupper(localeID[i]); } @@ -1337,26 +1320,25 @@ _getRegion(const char* localeID, if (offset.has_value()) { const char* const alias = COUNTRIES[*offset]; sink->Append(alias, static_cast(uprv_strlen(alias))); - return; + return len; } } - sink->Append(buffer, len); + sink->Append(buffer, static_cast(len)); + return len; } /** * @param needSeparator if true, then add leading '_' if any variants * are added to 'variant' */ -void -_getVariant(const char* localeID, +size_t +_getVariant(std::string_view localeID, char prev, ByteSink* sink, - const char** pEnd, bool needSeparator, UErrorCode& status) { - if (U_FAILURE(status)) return; - if (pEnd != nullptr) { *pEnd = localeID; } + if (U_FAILURE(status) || localeID.empty()) return 0; // Reasonable upper limit for variants // There are no strict limitation of the syntax of variant in the legacy @@ -1369,63 +1351,82 @@ _getVariant(const char* localeID, constexpr int32_t MAX_VARIANTS_LENGTH = 179; /* get one or more variant tags and separate them with '_' */ - int32_t index = 0; + size_t index = 0; if (_isIDSeparator(prev)) { /* get a variant string after a '-' or '_' */ - for (index=0; !_isTerminator(localeID[index]); index++) { - if (index >= MAX_VARIANTS_LENGTH) { // same as length > MAX_VARIANTS_LENGTH + for (std::string_view sub = localeID;;) { + size_t next = sub.find_first_of(".@_-"); + // For historical reasons, a trailing separator is included in the variant. + bool finished = next == std::string_view::npos || next + 1 == sub.length(); + size_t limit = finished ? sub.length() : next; + index += limit; + if (index > MAX_VARIANTS_LENGTH) { status = U_ILLEGAL_ARGUMENT_ERROR; - return; + return 0; } - if (needSeparator) { - if (sink != nullptr) { + + if (sink != nullptr) { + if (needSeparator) { sink->Append("_", 1); + } else { + needSeparator = true; } - needSeparator = false; - } - if (sink != nullptr) { - char c = uprv_toupper(localeID[index]); - if (c == '-') c = '_'; - sink->Append(&c, 1); + + int32_t length = static_cast(limit); + int32_t minCapacity = uprv_min(length, MAX_VARIANTS_LENGTH); + char scratch[MAX_VARIANTS_LENGTH]; + int32_t capacity = 0; + char* buffer = sink->GetAppendBuffer( + minCapacity, minCapacity, scratch, UPRV_LENGTHOF(scratch), &capacity); + + for (size_t i = 0; i < limit; ++i) { + buffer[i] = uprv_toupper(sub[i]); + } + sink->Append(buffer, length); } + + if (finished) { return index; } + sub.remove_prefix(next); + if (_isTerminator(sub.front()) || _isBCP47Extension(sub)) { return index; } + sub.remove_prefix(1); + index++; } - if (pEnd != nullptr) { *pEnd = localeID+index; } } + size_t skip = 0; /* if there is no variant tag after a '-' or '_' then look for '@' */ - if (index == 0) { - if (prev=='@') { - /* keep localeID */ - } else if((localeID=locale_getKeywordsStart(localeID))!=nullptr) { - ++localeID; /* point after the '@' */ - } else { - return; + if (prev == '@') { + /* keep localeID */ + } else if (const char* p = locale_getKeywordsStart(localeID); p != nullptr) { + skip = 1 + p - localeID.data(); /* point after the '@' */ + localeID.remove_prefix(skip); + } else { + return 0; + } + for (; index < localeID.size() && !_isTerminator(localeID[index]); index++) { + if (index >= MAX_VARIANTS_LENGTH) { // same as length > MAX_VARIANTS_LENGTH + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; } - for(; !_isTerminator(localeID[index]); index++) { - if (index >= MAX_VARIANTS_LENGTH) { // same as length > MAX_VARIANTS_LENGTH - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (needSeparator) { - if (sink != nullptr) { - sink->Append("_", 1); - } - needSeparator = false; - } + if (needSeparator) { if (sink != nullptr) { - char c = uprv_toupper(localeID[index]); - if (c == '-' || c == ',') c = '_'; - sink->Append(&c, 1); + sink->Append("_", 1); } + needSeparator = false; + } + if (sink != nullptr) { + char c = uprv_toupper(localeID[index]); + if (c == '-' || c == ',') c = '_'; + sink->Append(&c, 1); } - if (pEnd != nullptr) { *pEnd = localeID + index; } } + return skip + index; } } // namespace U_EXPORT CharString -ulocimp_getLanguage(const char* localeID, UErrorCode& status) { +ulocimp_getLanguage(std::string_view localeID, UErrorCode& status) { return ByteSinkUtil::viaByteSinkToCharString( [&](ByteSink& sink, UErrorCode& status) { ulocimp_getSubtags( @@ -1441,7 +1442,7 @@ ulocimp_getLanguage(const char* localeID, UErrorCode& status) { } U_EXPORT CharString -ulocimp_getScript(const char* localeID, UErrorCode& status) { +ulocimp_getScript(std::string_view localeID, UErrorCode& status) { return ByteSinkUtil::viaByteSinkToCharString( [&](ByteSink& sink, UErrorCode& status) { ulocimp_getSubtags( @@ -1457,7 +1458,7 @@ ulocimp_getScript(const char* localeID, UErrorCode& status) { } U_EXPORT CharString -ulocimp_getRegion(const char* localeID, UErrorCode& status) { +ulocimp_getRegion(std::string_view localeID, UErrorCode& status) { return ByteSinkUtil::viaByteSinkToCharString( [&](ByteSink& sink, UErrorCode& status) { ulocimp_getSubtags( @@ -1473,7 +1474,7 @@ ulocimp_getRegion(const char* localeID, UErrorCode& status) { } U_EXPORT CharString -ulocimp_getVariant(const char* localeID, UErrorCode& status) { +ulocimp_getVariant(std::string_view localeID, UErrorCode& status) { return ByteSinkUtil::viaByteSinkToCharString( [&](ByteSink& sink, UErrorCode& status) { ulocimp_getSubtags( @@ -1490,7 +1491,7 @@ ulocimp_getVariant(const char* localeID, UErrorCode& status) { U_EXPORT void ulocimp_getSubtags( - const char* localeID, + std::string_view localeID, CharString* language, CharString* script, CharString* region, @@ -1521,7 +1522,7 @@ ulocimp_getSubtags( U_EXPORT void ulocimp_getSubtags( - const char* localeID, + std::string_view localeID, ByteSink* language, ByteSink* script, ByteSink* region, @@ -1531,7 +1532,7 @@ ulocimp_getSubtags( if (U_FAILURE(status)) { return; } if (pEnd != nullptr) { - *pEnd = localeID; + *pEnd = localeID.data(); } else if (language == nullptr && script == nullptr && region == nullptr && @@ -1539,62 +1540,94 @@ ulocimp_getSubtags( return; } + if (localeID.empty()) { return; } + bool hasRegion = false; - if (localeID == nullptr) { - localeID = uloc_getDefault(); + { + size_t len = _getLanguage(localeID, language, status); + if (U_FAILURE(status)) { return; } + if (len > 0) { + localeID.remove_prefix(len); + } } - _getLanguage(localeID, language, &localeID, status); - if (U_FAILURE(status)) { return; } - U_ASSERT(localeID != nullptr); - if (pEnd != nullptr) { - *pEnd = localeID; + *pEnd = localeID.data(); } else if (script == nullptr && region == nullptr && variant == nullptr) { return; } - if (_isIDSeparator(*localeID)) { - const char* begin = localeID + 1; - const char* end = nullptr; - _getScript(begin, script, &end); - U_ASSERT(end != nullptr); - if (end != begin) { - localeID = end; - if (pEnd != nullptr) { *pEnd = localeID; } + if (localeID.empty()) { return; } + + if (_isIDSeparator(localeID.front())) { + std::string_view sub = localeID; + sub.remove_prefix(1); + size_t len = _getScript(sub, script); + if (len > 0) { + localeID.remove_prefix(len + 1); + if (pEnd != nullptr) { *pEnd = localeID.data(); } } } - if (region == nullptr && variant == nullptr && pEnd == nullptr) { return; } + if ((region == nullptr && variant == nullptr && pEnd == nullptr) || localeID.empty()) { return; } - if (_isIDSeparator(*localeID)) { - const char* begin = localeID + 1; - const char* end = nullptr; - _getRegion(begin, region, &end); - U_ASSERT(end != nullptr); - if (end != begin) { + if (_isIDSeparator(localeID.front())) { + std::string_view sub = localeID; + sub.remove_prefix(1); + size_t len = _getRegion(sub, region); + if (len > 0) { hasRegion = true; - localeID = end; - if (pEnd != nullptr) { *pEnd = localeID; } + localeID.remove_prefix(len + 1); + if (pEnd != nullptr) { *pEnd = localeID.data(); } } } - if (variant == nullptr && pEnd == nullptr) { return; } + if ((variant == nullptr && pEnd == nullptr) || localeID.empty()) { return; } - if (_isIDSeparator(*localeID) && !_isBCP47Extension(localeID)) { + bool hasVariant = false; + + if (_isIDSeparator(localeID.front()) && !_isBCP47Extension(localeID)) { + std::string_view sub = localeID; /* If there was no country ID, skip a possible extra IDSeparator */ - if (!hasRegion && _isIDSeparator(localeID[1])) { - localeID++; - } - const char* begin = localeID + 1; - const char* end = nullptr; - _getVariant(begin, *localeID, variant, &end, false, status); + size_t skip = !hasRegion && localeID.size() > 1 && _isIDSeparator(localeID[1]) ? 2 : 1; + sub.remove_prefix(skip); + size_t len = _getVariant(sub, localeID[0], variant, false, status); if (U_FAILURE(status)) { return; } - U_ASSERT(end != nullptr); - if (end != begin && pEnd != nullptr) { *pEnd = end; } + if (len > 0) { + hasVariant = true; + localeID.remove_prefix(skip + len); + if (pEnd != nullptr) { *pEnd = localeID.data(); } + } + } + + if ((variant == nullptr && pEnd == nullptr) || localeID.empty()) { return; } + + if (_isBCP47Extension(localeID)) { + localeID.remove_prefix(2); + constexpr char vaposix[] = "-va-posix"; + constexpr size_t length = sizeof vaposix - 1; + for (size_t next;; localeID.remove_prefix(next)) { + next = localeID.find('-', 1); + if (next == std::string_view::npos) { break; } + next = localeID.find('-', next + 1); + bool finished = next == std::string_view::npos; + std::string_view sub = localeID; + if (!finished) { sub.remove_suffix(sub.length() - next); } + + if (sub.length() == length && uprv_strnicmp(sub.data(), vaposix, length) == 0) { + if (variant != nullptr) { + if (hasVariant) { variant->Append("_", 1); } + constexpr char posix[] = "POSIX"; + variant->Append(posix, sizeof posix - 1); + } + if (pEnd != nullptr) { *pEnd = localeID.data() + length; } + } + + if (finished) { break; } + } } } @@ -1700,7 +1733,7 @@ uloc_openKeywords(const char* localeID, CharString tempBuffer; const char* tmpLocaleID; - if (_hasBCP47Extension(localeID)) { + if (localeID != nullptr && _hasBCP47Extension(localeID)) { tempBuffer = ulocimp_forLanguageTag(localeID, -1, nullptr, *status); tmpLocaleID = U_SUCCESS(*status) && !tempBuffer.isEmpty() ? tempBuffer.data() : localeID; } else { @@ -1753,7 +1786,7 @@ constexpr int32_t I_DEFAULT_LENGTH = UPRV_LENGTHOF(i_default); * This is the code underlying uloc_getName and uloc_canonicalize. */ void -_canonicalize(const char* localeID, +_canonicalize(std::string_view localeID, ByteSink& sink, uint32_t options, UErrorCode& err) { @@ -1764,33 +1797,30 @@ _canonicalize(const char* localeID, int32_t j, fieldCount=0; CharString tempBuffer; // if localeID has a BCP47 extension, tmpLocaleID points to this CharString localeIDWithHyphens; // if localeID has a BPC47 extension and have _, tmpLocaleID points to this - const char* origLocaleID; - const char* tmpLocaleID; - const char* keywordAssign = nullptr; - const char* separatorIndicator = nullptr; + std::string_view origLocaleID; + std::string_view tmpLocaleID; + size_t keywordAssign = std::string_view::npos; + size_t separatorIndicator = std::string_view::npos; if (_hasBCP47Extension(localeID)) { - const char* localeIDPtr = localeID; + std::string_view localeIDPtr = localeID; // convert all underbars to hyphens, unless the "BCP47 extension" comes at the beginning of the string - if (uprv_strchr(localeID, '_') != nullptr && localeID[1] != '-' && localeID[1] != '_') { - localeIDWithHyphens.append(localeID, -1, err); + if (localeID.size() >= 2 && localeID.find('_') != std::string_view::npos && localeID[1] != '-' && localeID[1] != '_') { + localeIDWithHyphens.append(localeID, err); if (U_SUCCESS(err)) { for (char* p = localeIDWithHyphens.data(); *p != '\0'; ++p) { if (*p == '_') { *p = '-'; } } - localeIDPtr = localeIDWithHyphens.data(); + localeIDPtr = localeIDWithHyphens.toStringPiece(); } } - tempBuffer = ulocimp_forLanguageTag(localeIDPtr, -1, nullptr, err); - tmpLocaleID = U_SUCCESS(err) && !tempBuffer.isEmpty() ? tempBuffer.data() : localeIDPtr; + tempBuffer = ulocimp_forLanguageTag(localeIDPtr.data(), static_cast(localeIDPtr.size()), nullptr, err); + tmpLocaleID = U_SUCCESS(err) && !tempBuffer.isEmpty() ? static_cast(tempBuffer.toStringPiece()) : localeIDPtr; } else { - if (localeID==nullptr) { - localeID=uloc_getDefault(); - } tmpLocaleID=localeID; } @@ -1801,20 +1831,25 @@ _canonicalize(const char* localeID, CharString script; CharString country; CharString variant; + const char* end = nullptr; ulocimp_getSubtags( tmpLocaleID, &tag, &script, &country, &variant, - &tmpLocaleID, + &end, err); if (U_FAILURE(err)) { return; } + U_ASSERT(end != nullptr); + if (end > tmpLocaleID.data()) { + tmpLocaleID.remove_prefix(end - tmpLocaleID.data()); + } - if (tag.length() == I_DEFAULT_LENGTH && - uprv_strncmp(origLocaleID, i_default, I_DEFAULT_LENGTH) == 0) { + if (tag.length() == I_DEFAULT_LENGTH && origLocaleID.length() >= I_DEFAULT_LENGTH && + uprv_strncmp(origLocaleID.data(), i_default, I_DEFAULT_LENGTH) == 0) { tag.clear(); tag.append(uloc_getDefault(), err); } else { @@ -1839,15 +1874,14 @@ _canonicalize(const char* localeID, } /* Copy POSIX-style charset specifier, if any [mr.utf8] */ - if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') { + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && !tmpLocaleID.empty() && tmpLocaleID.front() == '.') { tag.append('.', err); - ++tmpLocaleID; - const char *atPos = nullptr; + tmpLocaleID.remove_prefix(1); size_t length; - if((atPos = uprv_strchr(tmpLocaleID, '@')) != nullptr) { - length = atPos - tmpLocaleID; + if (size_t atPos = tmpLocaleID.find('@'); atPos != std::string_view::npos) { + length = atPos; } else { - length = uprv_strlen(tmpLocaleID); + length = tmpLocaleID.length(); } // The longest charset name we found in IANA charset registry // https://www.iana.org/assignments/character-sets/ is @@ -1859,33 +1893,34 @@ _canonicalize(const char* localeID, err = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ return; } - tag.append(tmpLocaleID, static_cast(length), err); - tmpLocaleID += length; + if (length > 0) { + tag.append(tmpLocaleID.data(), static_cast(length), err); + tmpLocaleID.remove_prefix(length); + } } /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';' - After this, tmpLocaleID either points to '@' or is nullptr */ - if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=nullptr) { - keywordAssign = uprv_strchr(tmpLocaleID, '='); - separatorIndicator = uprv_strchr(tmpLocaleID, ';'); + After this, tmpLocaleID either starts at '@' or is empty. */ + if (const char* start = locale_getKeywordsStart(tmpLocaleID); start != nullptr) { + if (start > tmpLocaleID.data()) { + tmpLocaleID.remove_prefix(start - tmpLocaleID.data()); + } + keywordAssign = tmpLocaleID.find('='); + separatorIndicator = tmpLocaleID.find(';'); + } else { + tmpLocaleID = {}; } /* Copy POSIX-style variant, if any [mr@FOO] */ if (!OPTION_SET(options, _ULOC_CANONICALIZE) && - tmpLocaleID != nullptr && keywordAssign == nullptr) { - for (;;) { - char c = *tmpLocaleID; - if (c == 0) { - break; - } - tag.append(c, err); - ++tmpLocaleID; - } + !tmpLocaleID.empty() && keywordAssign == std::string_view::npos) { + tag.append(tmpLocaleID, err); + tmpLocaleID = {}; } if (OPTION_SET(options, _ULOC_CANONICALIZE)) { /* Handle @FOO variant if @ is present and not followed by = */ - if (tmpLocaleID!=nullptr && keywordAssign==nullptr) { + if (!tmpLocaleID.empty() && keywordAssign == std::string_view::npos) { /* Add missing '_' if needed */ if (fieldCount < 2 || (fieldCount < 3 && !script.isEmpty())) { do { @@ -1895,7 +1930,9 @@ _canonicalize(const char* localeID, } CharStringByteSink s(&tag); - _getVariant(tmpLocaleID+1, '@', &s, nullptr, !variant.isEmpty(), err); + std::string_view sub = tmpLocaleID; + sub.remove_prefix(1); + _getVariant(sub, '@', &s, !variant.isEmpty(), err); if (U_FAILURE(err)) { return; } } @@ -1903,7 +1940,7 @@ _canonicalize(const char* localeID, for (j=0; j keywordAssign)) { + if (!tmpLocaleID.empty() && keywordAssign != std::string_view::npos && + (separatorIndicator == std::string_view::npos || separatorIndicator > keywordAssign)) { sink.Append("@", 1); ++fieldCount; - ulocimp_getKeywords(tmpLocaleID+1, '@', sink, true, err); + tmpLocaleID.remove_prefix(1); + ulocimp_getKeywords(tmpLocaleID, '@', sink, true, err); } } } @@ -1989,6 +2027,10 @@ uloc_getLanguage(const char* localeID, int32_t languageCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } + /* uloc_getLanguage will return a 2 character iso-639 code if one exists. *CWB*/ return ByteSinkUtil::viaByteSinkToTerminatedChars( language, languageCapacity, @@ -2011,6 +2053,10 @@ uloc_getScript(const char* localeID, int32_t scriptCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } + return ByteSinkUtil::viaByteSinkToTerminatedChars( script, scriptCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2032,6 +2078,10 @@ uloc_getCountry(const char* localeID, int32_t countryCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } + return ByteSinkUtil::viaByteSinkToTerminatedChars( country, countryCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2053,6 +2103,10 @@ uloc_getVariant(const char* localeID, int32_t variantCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } + return ByteSinkUtil::viaByteSinkToTerminatedChars( variant, variantCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2074,6 +2128,9 @@ uloc_getName(const char* localeID, int32_t nameCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } return ByteSinkUtil::viaByteSinkToTerminatedChars( name, nameCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2083,7 +2140,7 @@ uloc_getName(const char* localeID, } U_EXPORT CharString -ulocimp_getName(const char* localeID, +ulocimp_getName(std::string_view localeID, UErrorCode& err) { return ByteSinkUtil::viaByteSinkToCharString( @@ -2094,7 +2151,7 @@ ulocimp_getName(const char* localeID, } U_EXPORT void -ulocimp_getName(const char* localeID, +ulocimp_getName(std::string_view localeID, ByteSink& sink, UErrorCode& err) { @@ -2107,6 +2164,9 @@ uloc_getBaseName(const char* localeID, int32_t nameCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } return ByteSinkUtil::viaByteSinkToTerminatedChars( name, nameCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2116,7 +2176,7 @@ uloc_getBaseName(const char* localeID, } U_EXPORT CharString -ulocimp_getBaseName(const char* localeID, +ulocimp_getBaseName(std::string_view localeID, UErrorCode& err) { return ByteSinkUtil::viaByteSinkToCharString( @@ -2127,7 +2187,7 @@ ulocimp_getBaseName(const char* localeID, } U_EXPORT void -ulocimp_getBaseName(const char* localeID, +ulocimp_getBaseName(std::string_view localeID, ByteSink& sink, UErrorCode& err) { @@ -2140,6 +2200,9 @@ uloc_canonicalize(const char* localeID, int32_t nameCapacity, UErrorCode* err) { + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } return ByteSinkUtil::viaByteSinkToTerminatedChars( name, nameCapacity, [&](ByteSink& sink, UErrorCode& status) { @@ -2149,7 +2212,7 @@ uloc_canonicalize(const char* localeID, } U_EXPORT CharString -ulocimp_canonicalize(const char* localeID, +ulocimp_canonicalize(std::string_view localeID, UErrorCode& err) { return ByteSinkUtil::viaByteSinkToCharString( @@ -2160,7 +2223,7 @@ ulocimp_canonicalize(const char* localeID, } U_EXPORT void -ulocimp_canonicalize(const char* localeID, +ulocimp_canonicalize(std::string_view localeID, ByteSink& sink, UErrorCode& err) { diff --git a/deps/icu-small/source/common/uloc_tag.cpp b/deps/icu-small/source/common/uloc_tag.cpp index 7b3b1e73a37cf4..b2e9946f48a7e5 100644 --- a/deps/icu-small/source/common/uloc_tag.cpp +++ b/deps/icu-small/source/common/uloc_tag.cpp @@ -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); } diff --git a/deps/icu-small/source/common/ulocale.cpp b/deps/icu-small/source/common/ulocale.cpp index f2f81bc97109bd..33814713dc1bfd 100644 --- a/deps/icu-small/source/common/ulocale.cpp +++ b/deps/icu-small/source/common/ulocale.cpp @@ -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* diff --git a/deps/icu-small/source/common/ulocimp.h b/deps/icu-small/source/common/ulocimp.h index 1887e2a849ab0e..7f09748c8ac1c8 100644 --- a/deps/icu-small/source/common/ulocimp.h +++ b/deps/icu-small/source/common/ulocimp.h @@ -68,42 +68,42 @@ U_EXPORT std::optional 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); diff --git a/deps/icu-small/source/common/umapfile.cpp b/deps/icu-small/source/common/umapfile.cpp index b58ac37f4d4593..3ba0251df9c52a 100644 --- a/deps/icu-small/source/common/umapfile.cpp +++ b/deps/icu-small/source/common/umapfile.cpp @@ -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; } diff --git a/deps/icu-small/source/common/unicode/brkiter.h b/deps/icu-small/source/common/unicode/brkiter.h index 30c59c4a94ace1..d953925bd72a0c 100644 --- a/deps/icu-small/source/common/unicode/brkiter.h +++ b/deps/icu-small/source/common/unicode/brkiter.h @@ -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 @@ class U_COMMON_API BreakIterator : public UObject { 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 diff --git a/deps/icu-small/source/common/unicode/char16ptr.h b/deps/icu-small/source/common/unicode/char16ptr.h index daf35cd43ba2d6..049de9efee810f 100644 --- a/deps/icu-small/source/common/unicode/char16ptr.h +++ b/deps/icu-small/source/common/unicode/char16ptr.h @@ -9,10 +9,13 @@ #include "unicode/utypes.h" -#if U_SHOW_CPLUSPLUS_API +#if U_SHOW_CPLUSPLUS_API || U_SHOW_CPLUSPLUS_HEADER_API #include #include +#include + +#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>> +inline const char16_t *uprv_char16PtrFromUChar(const T *p) { + if constexpr (std::is_same_v) { + 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(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(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(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(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__ diff --git a/deps/icu-small/source/common/unicode/locid.h b/deps/icu-small/source/common/unicode/locid.h index e1afd598cf9bc9..a394cd9347dbde 100644 --- a/deps/icu-small/source/common/unicode/locid.h +++ b/deps/icu-small/source/common/unicode/locid.h @@ -449,6 +449,11 @@ class U_COMMON_API Locale : public UObject { */ 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 @@ class U_COMMON_API Locale : public UObject { * @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 diff --git a/deps/icu-small/source/common/unicode/resbund.h b/deps/icu-small/source/common/unicode/resbund.h index 3965371729d814..03ff6faee239a7 100644 --- a/deps/icu-small/source/common/unicode/resbund.h +++ b/deps/icu-small/source/common/unicode/resbund.h @@ -450,7 +450,7 @@ class U_COMMON_API ResourceBundle : public UObject { * @return a Locale object * @stable ICU 2.8 */ - const Locale + Locale getLocale(ULocDataLocaleType type, UErrorCode &status) const; #ifndef U_HIDE_INTERNAL_API /** diff --git a/deps/icu-small/source/common/unicode/uchar.h b/deps/icu-small/source/common/unicode/uchar.h index 0daa7dd2141cd0..82ec63ab524e05 100644 --- a/deps/icu-small/source/common/unicode/uchar.h +++ b/deps/icu-small/source/common/unicode/uchar.h @@ -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_ */ - /** @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_ */ - /** @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 diff --git a/deps/icu-small/source/common/unicode/uniset.h b/deps/icu-small/source/common/unicode/uniset.h index d070fd631a22d9..6b1ac9ba26202d 100644 --- a/deps/icu-small/source/common/unicode/uniset.h +++ b/deps/icu-small/source/common/unicode/uniset.h @@ -1173,10 +1173,12 @@ class U_COMMON_API UnicodeSet final : public UnicodeFilter { 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 @@ class U_COMMON_API UnicodeSet final : public UnicodeFilter { * \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 @@ class U_COMMON_API UnicodeSet final : public UnicodeFilter { /** * @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 diff --git a/deps/icu-small/source/common/unicode/uset.h b/deps/icu-small/source/common/unicode/uset.h index c8f9b5592df2ea..c5e7f23901b6b7 100644 --- a/deps/icu-small/source/common/unicode/uset.h +++ b/deps/icu-small/source/common/unicode/uset.h @@ -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 #include #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 @@ class USetCodePointIterator { 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 @@ class USetCodePoints { * 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 @@ class USetRangeIterator { 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 @@ class USetRanges { * 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 @@ class USetStringIterator { int32_t length; const UChar *uchars = uset_getString(uset, index, &length); // assert uchars != nullptr; - return {ConstChar16Ptr(uchars), static_cast(length)}; + return {uprv_char16PtrFromUChar(uchars), static_cast(length)}; } return {}; } @@ -1684,8 +1685,8 @@ class USetStringIterator { 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 @@ class USetStringIterator { * 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 @@ class USetStrings { * 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 @@ class USetStrings { 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 @@ class USetElementIterator { 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(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(length)}; } else { - return UnicodeString(); + return {}; } } /** * Pre-increment. - * @draft ICU 76 + * @draft ICU 77 */ USetElementIterator &operator++() { if (c < end) { @@ -1800,7 +1807,7 @@ class USetElementIterator { /** * Post-increment. - * @draft ICU 76 + * @draft ICU 77 */ USetElementIterator operator++(int) { USetElementIterator result(*this); @@ -1811,8 +1818,8 @@ class USetElementIterator { 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 @@ class USetElementIterator { /** * 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 @@ class USetElementIterator { * 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); } diff --git a/deps/icu-small/source/common/unicode/utf8.h b/deps/icu-small/source/common/unicode/utf8.h index 5a07435fcf6096..96ad46161aa1e8 100644 --- a/deps/icu-small/source/common/unicode/utf8.h +++ b/deps/icu-small/source/common/unicode/utf8.h @@ -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. diff --git a/deps/icu-small/source/common/unicode/utypes.h b/deps/icu-small/source/common/unicode/utypes.h index 0151ebd4701576..ecdee51643166e 100644 --- a/deps/icu-small/source/common/unicode/utypes.h +++ b/deps/icu-small/source/common/unicode/utypes.h @@ -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 /* diff --git a/deps/icu-small/source/common/unicode/uvernum.h b/deps/icu-small/source/common/unicode/uvernum.h index a3cb882623be86..847c49f4cfb8cd 100644 --- a/deps/icu-small/source/common/unicode/uvernum.h +++ b/deps/icu-small/source/common/unicode/uvernum.h @@ -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 */ /*=========================================================================== diff --git a/deps/icu-small/source/common/unicode/uversion.h b/deps/icu-small/source/common/unicode/uversion.h index 25d73a3aeb5449..a29bf21efda597 100644 --- a/deps/icu-small/source/common/unicode/uversion.h +++ b/deps/icu-small/source/common/unicode/uversion.h @@ -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 */ diff --git a/deps/icu-small/source/common/unistr.cpp b/deps/icu-small/source/common/unistr.cpp index a720245772e637..4e29bad1d3b971 100644 --- a/deps/icu-small/source/common/unistr.cpp +++ b/deps/icu-small/source/common/unistr.cpp @@ -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 diff --git a/deps/icu-small/source/common/uresbund.cpp b/deps/icu-small/source/common/uresbund.cpp index 3a09cbf3bcaac6..afda2770fd3fc2 100644 --- a/deps/icu-small/source/common/uresbund.cpp +++ b/deps/icu-small/source/common/uresbund.cpp @@ -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); diff --git a/deps/icu-small/source/common/uscript.cpp b/deps/icu-small/source/common/uscript.cpp index c48a28fd14345a..ce40d354958fdd 100644 --- a/deps/icu-small/source/common/uscript.cpp +++ b/deps/icu-small/source/common/uscript.cpp @@ -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 diff --git a/deps/icu-small/source/common/ushape.cpp b/deps/icu-small/source/common/ushape.cpp index 00125635cb2672..b7946dc3ce03f2 100644 --- a/deps/icu-small/source/common/ushape.cpp +++ b/deps/icu-small/source/common/ushape.cpp @@ -28,6 +28,7 @@ #include "ubidi_props.h" #include "uassert.h" +#include /* * 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(sourceLength) + 1 > std::numeric_limits::max() / U_SIZEOF_UCHAR) { + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } tempbuffer = static_cast(uprv_malloc((sourceLength + 1) * U_SIZEOF_UCHAR)); /* Test for nullptr */ if(tempbuffer == nullptr) { diff --git a/deps/icu-small/source/common/usprep.cpp b/deps/icu-small/source/common/usprep.cpp index 477b8f2309db53..048b423645f825 100644 --- a/deps/icu-small/source/common/usprep.cpp +++ b/deps/icu-small/source/common/usprep.cpp @@ -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 diff --git a/deps/icu-small/source/common/utf_impl.cpp b/deps/icu-small/source/common/utf_impl.cpp index 827a82daf403a3..7da10c9b2d36d5 100644 --- a/deps/icu-small/source/common/utf_impl.cpp +++ b/deps/icu-small/source/common/utf_impl.cpp @@ -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) { diff --git a/deps/icu-small/source/common/utypes.cpp b/deps/icu-small/source/common/utypes.cpp index 4602314147f19e..4d4c1f81b5e6b8 100644 --- a/deps/icu-small/source/common/utypes.cpp +++ b/deps/icu-small/source/common/utypes.cpp @@ -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 diff --git a/deps/icu-small/source/data/in/icudt76l.dat.bz2 b/deps/icu-small/source/data/in/icudt77l.dat.bz2 similarity index 57% rename from deps/icu-small/source/data/in/icudt76l.dat.bz2 rename to deps/icu-small/source/data/in/icudt77l.dat.bz2 index 8843d4561126d3..336984fcd0f1ad 100644 Binary files a/deps/icu-small/source/data/in/icudt76l.dat.bz2 and b/deps/icu-small/source/data/in/icudt77l.dat.bz2 differ diff --git a/deps/icu-small/source/i18n/basictz.cpp b/deps/icu-small/source/i18n/basictz.cpp index a2c1ec7fb9142d..610a31ad5dc559 100644 --- a/deps/icu-small/source/i18n/basictz.cpp +++ b/deps/icu-small/source/i18n/basictz.cpp @@ -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; } diff --git a/deps/icu-small/source/i18n/buddhcal.cpp b/deps/icu-small/source/i18n/buddhcal.cpp index 7723ade105d2c5..c99b97e26713c8 100644 --- a/deps/icu-small/source/i18n/buddhcal.cpp +++ b/deps/icu-small/source/i18n/buddhcal.cpp @@ -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); diff --git a/deps/icu-small/source/i18n/buddhcal.h b/deps/icu-small/source/i18n/buddhcal.h index 1fa8395b35b216..196b21311fdaa9 100644 --- a/deps/icu-small/source/i18n/buddhcal.h +++ b/deps/icu-small/source/i18n/buddhcal.h @@ -82,13 +82,6 @@ class BuddhistCalendar : public GregorianCalendar { */ 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. diff --git a/deps/icu-small/source/i18n/calendar.cpp b/deps/icu-small/source/i18n/calendar.cpp index 96247174f70d6b..cac237d2b67d8f 100644 --- a/deps/icu-small/source/i18n/calendar.cpp +++ b/deps/icu-small/source/i18n/calendar.cpp @@ -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(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(kMinimumUserStamp)), -fTime(0), fLenient(true), -fZone(nullptr), fRepeatedWallTime(UCAL_WALLTIME_LAST), fSkippedWallTime(UCAL_WALLTIME_LAST) { - validLocale[0] = 0; - actualLocale[0] = 0; + LocalPointer 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(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>= 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; diff --git a/deps/icu-small/source/i18n/cecal.cpp b/deps/icu-small/source/i18n/cecal.cpp index 7771c32efb30c8..33d32adab731f5 100644 --- a/deps/icu-small/source/i18n/cecal.cpp +++ b/deps/icu-small/source/i18n/cecal.cpp @@ -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 //------------------------------------------------------------------------- diff --git a/deps/icu-small/source/i18n/cecal.h b/deps/icu-small/source/i18n/cecal.h index 9c3332f3b84a82..e6514e18f857cc 100644 --- a/deps/icu-small/source/i18n/cecal.h +++ b/deps/icu-small/source/i18n/cecal.h @@ -82,13 +82,6 @@ class U_I18N_API CECalendar : public Calendar { */ virtual ~CECalendar(); - /** - * Default assignment operator - * @param right Calendar object to be copied - * @internal - */ - CECalendar& operator=(const CECalendar& right); - protected: //------------------------------------------------------------------------- // Calendar framework diff --git a/deps/icu-small/source/i18n/chnsecal.cpp b/deps/icu-small/source/i18n/chnsecal.cpp index 050994fcbaf67c..afb16d3e25a83b 100644 --- a/deps/icu-small/source/i18n/chnsecal.cpp +++ b/deps/icu-small/source/i18n/chnsecal.cpp @@ -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(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 unusedMonth; - int32_t unusedDayOfWeek; - int32_t unusedDayOfMonth; - int32_t unusedDayOfYear; - Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear, status); + int32_t newMonthYear = Grego::dayToYear(newMoon, 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 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 diff --git a/deps/icu-small/source/i18n/chnsecal.h b/deps/icu-small/source/i18n/chnsecal.h index 41bd3557fcbff1..410a5a0222cf60 100644 --- a/deps/icu-small/source/i18n/chnsecal.h +++ b/deps/icu-small/source/i18n/chnsecal.h @@ -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; diff --git a/deps/icu-small/source/i18n/collationruleparser.cpp b/deps/icu-small/source/i18n/collationruleparser.cpp index b20d2c9428c48b..f5608cde7dc643 100644 --- a/deps/icu-small/source/i18n/collationruleparser.cpp +++ b/deps/icu-small/source/i18n/collationruleparser.cpp @@ -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)) { diff --git a/deps/icu-small/source/i18n/datefmt.cpp b/deps/icu-small/source/i18n/datefmt.cpp index 655cfbd1239daa..de9efc7ca8f971 100644 --- a/deps/icu-small/source/i18n/datefmt.cpp +++ b/deps/icu-small/source/i18n/datefmt.cpp @@ -40,6 +40,7 @@ #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) #include #endif +#include // ***************************************************************************** // 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(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(fCalendar)); cal.setTime(date, ec); if (U_SUCCESS(ec)) { diff --git a/deps/icu-small/source/i18n/dcfmtsym.cpp b/deps/icu-small/source/i18n/dcfmtsym.cpp index b4c90e6765a7df..b85f3ad134a289 100644 --- a/deps/icu-small/source/i18n/dcfmtsym.cpp +++ b/deps/icu-small/source/i18n/dcfmtsym.cpp @@ -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& diff --git a/deps/icu-small/source/i18n/dtfmtsym.cpp b/deps/icu-small/source/i18n/dtfmtsym.cpp index 23cea3eba20ae4..339db48dba8565 100644 --- a/deps/icu-small/source/i18n/dtfmtsym.cpp +++ b/deps/icu-small/source/i18n/dtfmtsym.cpp @@ -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 diff --git a/deps/icu-small/source/i18n/erarules.cpp b/deps/icu-small/source/i18n/erarules.cpp index 8ab6f00ae04b68..d23d7d516c7202 100644 --- a/deps/icu-small/source/i18n/erarules.cpp +++ b/deps/icu-small/source/i18n/erarules.cpp @@ -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; diff --git a/deps/icu-small/source/i18n/format.cpp b/deps/icu-small/source/i18n/format.cpp index 10856a4acba286..b4aec1d9811547 100644 --- a/deps/icu-small/source/i18n/format.cpp +++ b/deps/icu-small/source/i18n/format.cpp @@ -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 diff --git a/deps/icu-small/source/i18n/formattedvalue.cpp b/deps/icu-small/source/i18n/formattedvalue.cpp index aacd6ac70e090f..f2bfdda6e465f2 100644 --- a/deps/icu-small/source/i18n/formattedvalue.cpp +++ b/deps/icu-small/source/i18n/formattedvalue.cpp @@ -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 diff --git a/deps/icu-small/source/i18n/gregocal.cpp b/deps/icu-small/source/i18n/gregocal.cpp index 23366c7ab7a333..8a4bb15c16d60c 100644 --- a/deps/icu-small/source/i18n/gregocal.cpp +++ b/deps/icu-small/source/i18n/gregocal.cpp @@ -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) { diff --git a/deps/icu-small/source/i18n/gregoimp.cpp b/deps/icu-small/source/i18n/gregoimp.cpp index d5c8437a9b80f0..03bf9d2c9fdfec 100644 --- a/deps/icu-small/source/i18n/gregoimp.cpp +++ b/deps/icu-small/source/i18n/gregoimp.cpp @@ -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 + month = (12 * (doy - 1 + correction) + 6) / 367; // zero-based month + dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)]; // one-based DOM +} + +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, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& 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(time), static_cast(U_MILLIS_PER_DAY), &millisInDay); - mid = static_cast(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); diff --git a/deps/icu-small/source/i18n/gregoimp.h b/deps/icu-small/source/i18n/gregoimp.h index e069fb60de7909..39881c0eefbe6c 100644 --- a/deps/icu-small/source/i18n/gregoimp.h +++ b/deps/icu-small/source/i18n/gregoimp.h @@ -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(julian) - kEpochStartAsJulianDay) * kOneDay; diff --git a/deps/icu-small/source/i18n/hebrwcal.cpp b/deps/icu-small/source/i18n/hebrwcal.cpp index ef70a48f2353e3..70dfe9b7c735e5 100644 --- a/deps/icu-small/source/i18n/hebrwcal.cpp +++ b/deps/icu-small/source/i18n/hebrwcal.cpp @@ -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) { diff --git a/deps/icu-small/source/i18n/hebrwcal.h b/deps/icu-small/source/i18n/hebrwcal.h index 5fb10993d307b6..33db1b7860ab48 100644 --- a/deps/icu-small/source/i18n/hebrwcal.h +++ b/deps/icu-small/source/i18n/hebrwcal.h @@ -326,9 +326,9 @@ class U_I18N_API HebrewCalendar : public Calendar { * 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 diff --git a/deps/icu-small/source/i18n/indiancal.cpp b/deps/icu-small/source/i18n/indiancal.cpp index b1fd39b9927e5d..bb4b6b9939c029 100644 --- a/deps/icu-small/source/i18n/indiancal.cpp +++ b/deps/icu-small/source/i18n/indiancal.cpp @@ -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 diff --git a/deps/icu-small/source/i18n/indiancal.h b/deps/icu-small/source/i18n/indiancal.h index 2062bcec9103a4..ff067d0b3c3d92 100644 --- a/deps/icu-small/source/i18n/indiancal.h +++ b/deps/icu-small/source/i18n/indiancal.h @@ -215,7 +215,7 @@ class U_I18N_API IndianCalendar : public Calendar { * 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.... diff --git a/deps/icu-small/source/i18n/islamcal.cpp b/deps/icu-small/source/i18n/islamcal.cpp index dfeac36a66516d..e847ac28c894cc 100644 --- a/deps/icu-small/source/i18n/islamcal.cpp +++ b/deps/icu-small/source/i18n/islamcal.cpp @@ -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 (extendedYearUMALQURA_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); } /** diff --git a/deps/icu-small/source/i18n/islamcal.h b/deps/icu-small/source/i18n/islamcal.h index e42e681328b4d2..db90255b5b5a4f 100644 --- a/deps/icu-small/source/i18n/islamcal.h +++ b/deps/icu-small/source/i18n/islamcal.h @@ -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 diff --git a/deps/icu-small/source/i18n/japancal.cpp b/deps/icu-small/source/i18n/japancal.cpp index c0dd9fad0dc09b..b389b4530b290a 100644 --- a/deps/icu-small/source/i18n/japancal.cpp +++ b/deps/icu-small/source/i18n/japancal.cpp @@ -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); diff --git a/deps/icu-small/source/i18n/japancal.h b/deps/icu-small/source/i18n/japancal.h index 627b12750b98a3..3271cbfb794a9e 100644 --- a/deps/icu-small/source/i18n/japancal.h +++ b/deps/icu-small/source/i18n/japancal.h @@ -104,13 +104,6 @@ class JapaneseCalendar : public GregorianCalendar { */ 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. diff --git a/deps/icu-small/source/i18n/measunit.cpp b/deps/icu-small/source/i18n/measunit.cpp index 2741b84aabf0c4..8dda799d0165db 100644 --- a/deps/icu-small/source/i18n/measunit.cpp +++ b/deps/icu-small/source/i18n/measunit.cpp @@ -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) { diff --git a/deps/icu-small/source/i18n/measunit_extra.cpp b/deps/icu-small/source/i18n/measunit_extra.cpp index a6348422738bfa..8eb9fe55167077 100644 --- a/deps/icu-small/source/i18n/measunit_extra.cpp +++ b/deps/icu-small/source/i18n/measunit_extra.cpp @@ -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 #include - 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) {} - - 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, - }; - - // 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; - } + 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; + } + } + + 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; + } + + 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(fMatch); + } UMeasurePrefix getUnitPrefix() const { U_ASSERT(getType() == TYPE_PREFIX); @@ -530,8 +551,41 @@ class Token { 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(INT64_MAX)) { + status = kUnitIdentifierSyntaxError; + return 0; + } + + // Check if the value is integer. + uint64_t int_result = static_cast(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 @@ class Parser { 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 @@ class Parser { 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 @@ class Parser { } } + 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 @@ class Parser { // 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 @@ class Parser { // 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 @@ class Parser { // continue; } - if (match < 0) { - status = kUnitIdentifierSyntaxError; - } else { + if (match >= 0) { fIndex = previ; + 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}; } - 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 @@ class Parser { * 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 @@ class Parser { 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 @@ class Parser { // via a compound part: if (token.getType() != Token::TYPE_COMPOUND_PART) { status = kUnitIdentifierSyntaxError; - return result; + return {}; } switch (token.getMatch()) { @@ -724,15 +859,16 @@ class Parser { // 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 @@ class Parser { // 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 @@ class Parser { 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; + case Token::TYPE_POWER_PART: + if (state > 0) { + status = kUnitIdentifierSyntaxError; + return {}; + } + singleUnitResult.dimensionality *= token.getPower(); + state = 1; + break; - default: + case Token::TYPE_PREFIX: + if (state > 1) { status = kUnitIdentifierSyntaxError; - return result; + 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); } diff --git a/deps/icu-small/source/i18n/measunit_impl.h b/deps/icu-small/source/i18n/measunit_impl.h index f6a8f90dc94f0c..db31435944c2ec 100644 --- a/deps/icu-small/source/i18n/measunit_impl.h +++ b/deps/icu-small/source/i18n/measunit_impl.h @@ -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; diff --git a/deps/icu-small/source/i18n/messageformat2.cpp b/deps/icu-small/source/i18n/messageformat2.cpp index 73f7fa45e69f81..50d87422b64c57 100644 --- a/deps/icu-small/source/i18n/messageformat2.cpp +++ b/deps/icu-small/source/i18n/messageformat2.cpp @@ -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(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(FormattedPlaceholder(str)), status); } - return result; + return create(InternalValue(std::move(result)), status); } else { U_ASSERT(rand.isLiteral()); - return formatLiteral(rand.asLiteral()); + return create(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 rhsVal(formatOperand(env, v, context, status)); if (U_FAILURE(status)) { return {}; } - if (!rhsVal.isFallback()) { - resolvedOpt.adoptInstead(create(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(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(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(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 arg(arg_); + // Look up the formatter or selector + LocalPointer formatterImpl(nullptr); + LocalPointer selectorImpl(nullptr); if (isFormatter(functionName)) { - LocalPointer 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)); + } + 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 FormattedPlaceholder(fallback); + 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 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 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 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 v(create(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(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(res[i]))); + InternalValue* rv = static_cast(res[i]); // 2iv. Let matches be the result of calling the method MatchSelectorKeys(rv, keys) LocalPointer 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(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 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 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 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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_allocation.h b/deps/icu-small/source/i18n/messageformat2_allocation.h index 7be27e222520d6..5b06d0851296a1 100644 --- a/deps/icu-small/source/i18n/messageformat2_allocation.h +++ b/deps/icu-small/source/i18n/messageformat2_allocation.h @@ -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 diff --git a/deps/icu-small/source/i18n/messageformat2_arguments.cpp b/deps/icu-small/source/i18n/messageformat2_arguments.cpp index ded3f4dda160c3..c43c600a2f402c 100644 --- a/deps/icu-small/source/i18n/messageformat2_arguments.cpp +++ b/deps/icu-small/source/i18n/messageformat2_arguments.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_checker.cpp b/deps/icu-small/source/i18n/messageformat2_checker.cpp index bdc5c383b6e89f..46b25ff389b5dc 100644 --- a/deps/icu-small/source/i18n/messageformat2_checker.cpp +++ b/deps/icu-small/source/i18n/messageformat2_checker.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_checker.h b/deps/icu-small/source/i18n/messageformat2_checker.h index 4bb0498efb9940..122f668f4b7e9b 100644 --- a/deps/icu-small/source/i18n/messageformat2_checker.h +++ b/deps/icu-small/source/i18n/messageformat2_checker.h @@ -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 diff --git a/deps/icu-small/source/i18n/messageformat2_data_model.cpp b/deps/icu-small/source/i18n/messageformat2_data_model.cpp index 3fe5f65b532364..3406080695c6f9 100644 --- a/deps/icu-small/source/i18n/messageformat2_data_model.cpp +++ b/deps/icu-small/source/i18n/messageformat2_data_model.cpp @@ -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(other.selectors.getAlias(), - numSelectors, - localErrorCode)); + selectors.adoptInstead(copyArray(other.selectors.getAlias(), + numSelectors, + localErrorCode)); variants.adoptInstead(copyArray(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(&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(std::move(selector), status), status); + selectors->adoptElement(create(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(&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 variants(copyVectorToArray(*builder.variants, errorCode), errorCode); - LocalArray selectors(copyVectorToArray(*builder.selectors, errorCode), errorCode); + LocalArray selectors(copyVectorToArray(*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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_errors.cpp b/deps/icu-small/source/i18n/messageformat2_errors.cpp index 9d1d6bab81a1f7..5d3d938f02030e 100644 --- a/deps/icu-small/source/i18n/messageformat2_errors.cpp +++ b/deps/icu-small/source/i18n/messageformat2_errors.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_errors.h b/deps/icu-small/source/i18n/messageformat2_errors.h index f84aa736283786..085263e88b068d 100644 --- a/deps/icu-small/source/i18n/messageformat2_errors.h +++ b/deps/icu-small/source/i18n/messageformat2_errors.h @@ -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 diff --git a/deps/icu-small/source/i18n/messageformat2_evaluation.cpp b/deps/icu-small/source/i18n/messageformat2_evaluation.cpp index 41e4c9a8020a04..fcccbf5ae5e781 100644 --- a/deps/icu-small/source/i18n/messageformat2_evaluation.cpp +++ b/deps/icu-small/source/i18n/messageformat2_evaluation.cpp @@ -3,6 +3,8 @@ #include "unicode/utypes.h" +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -89,34 +91,53 @@ 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(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 {}; + } -ResolvedSelector::ResolvedSelector(ResolvedSelector&& other) { - *this = std::move(other); -} + // Create a new vector consisting of the options from this `FunctionOptions` + for (int32_t i = 0; i < functionOptionsLen; i++) { + mergedOptions.adoptElement(create(std::move(options[i]), status), + status); + } -ResolvedSelector::~ResolvedSelector() {} + // 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(std::move(other.options[i]), + status), + status); + } + } + + delete[] options; + options = nullptr; + functionOptionsLen = 0; + + return FunctionOptions(std::move(mergedOptions), status); +} // 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(argument) + && std::get_if(&argument)->isFallback(); + } + + bool InternalValue::hasNullOperand() const { + return std::holds_alternative(argument) + && std::get_if(&argument)->isNullOperand(); + } + + FormattedPlaceholder InternalValue::takeArgument(UErrorCode& errorCode) { + if (U_FAILURE(errorCode)) { + return {}; + } + + if (std::holds_alternative(argument)) { + return std::move(*std::get_if(&argument)); + } + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return {}; + } + + const UnicodeString& InternalValue::getFallback() const { + if (std::holds_alternative(argument)) { + return std::get_if(&argument)->getFallback(); + } + return (*std::get_if(&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(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(&p->argument); + p = next; + } + FormattedPlaceholder arg = std::move(*std::get_if(&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(argument)); + return std::move(*std::get_if(&argument)); + } + if (formatter == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return {}; + } + + FormattedPlaceholder arg; + + if (std::holds_alternative(argument)) { + arg = std::move(*std::get_if(&argument)); + } else { + arg = (*std::get_if(&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(argument)) { + delete *std::get_if(&argument); + argument = nullptr; + } + } + } // namespace message2 U_NAMESPACE_END #endif /* #if !UCONFIG_NO_MF2 */ #endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/deps/icu-small/source/i18n/messageformat2_evaluation.h b/deps/icu-small/source/i18n/messageformat2_evaluation.h index b8ae0242367df6..fcb30bc3e6388c 100644 --- a/deps/icu-small/source/i18n/messageformat2_evaluation.h +++ b/deps/icu-small/source/i18n/messageformat2_evaluation.h @@ -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; - 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 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 diff --git a/deps/icu-small/source/i18n/messageformat2_formattable.cpp b/deps/icu-small/source/i18n/messageformat2_formattable.cpp index 3152ccb44fd8b7..4e2df49aeccf01 100644 --- a/deps/icu-small/source/i18n/messageformat2_formattable.cpp +++ b/deps/icu-small/source/i18n/messageformat2_formattable.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_formatter.cpp b/deps/icu-small/source/i18n/messageformat2_formatter.cpp index 8d17ae49b99a9a..ead6f62a7899e7 100644 --- a/deps/icu-small/source/i18n/messageformat2_formatter.cpp +++ b/deps/icu-small/source/i18n/messageformat2_formatter.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_function_registry.cpp b/deps/icu-small/source/i18n/messageformat2_function_registry.cpp index 17955760ecfb44..e45fb3544ec524 100644 --- a/deps/icu-small/source/i18n/messageformat2_function_registry.cpp +++ b/deps/icu-small/source/i18n/messageformat2_function_registry.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_function_registry_internal.h b/deps/icu-small/source/i18n/messageformat2_function_registry_internal.h index 733fc5e945d5c8..9599b67bb2ba14 100644 --- a/deps/icu-small/source/i18n/messageformat2_function_registry_internal.h +++ b/deps/icu-small/source/i18n/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 @@ -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 diff --git a/deps/icu-small/source/i18n/messageformat2_macros.h b/deps/icu-small/source/i18n/messageformat2_macros.h index f06ed1a5a97746..20e81377d4d5cf 100644 --- a/deps/icu-small/source/i18n/messageformat2_macros.h +++ b/deps/icu-small/source/i18n/messageformat2_macros.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 diff --git a/deps/icu-small/source/i18n/messageformat2_parser.cpp b/deps/icu-small/source/i18n/messageformat2_parser.cpp index b4768756c5ead2..9a9f8e78df03bd 100644 --- a/deps/icu-small/source/i18n/messageformat2_parser.cpp +++ b/deps/icu-small/source/i18n/messageformat2_parser.cpp @@ -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 + +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; + } -// 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; + 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); } +bool Parser::isTextChar(UChar32 c) const { + return textChars->contains(c); +} + +bool Parser::isAlpha(UChar32 c) const { + return alphaChars->contains(c); +} -static bool isDigit(UChar32 c) { return inRange(c, 0x0030, 0x0039); } +bool Parser::isDigit(UChar32 c) const { + return digitChars->contains(c); +} -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::isNameStart(UChar32 c) const { + return nameStartChars->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::isNameChar(UChar32 c) const { + return nameChars->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::isUnquotedStart(UChar32 c) const { + return isNameChar(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::isQuotedChar(UChar32 c) const { + return quotedChars->contains(c); } -static bool isEscapableChar(UChar32 c) { - return c == PIPE - || c == BACKSLASH - || c == LEFT_CURLY_BRACE - || c == RIGHT_CURLY_BRACE; +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& 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& 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& 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 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 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 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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_parser.h b/deps/icu-small/source/i18n/messageformat2_parser.h index b62cbe9200b94a..62a52d8f680a1e 100644 --- a/deps/icu-small/source/i18n/messageformat2_parser.h +++ b/deps/icu-small/source/i18n/messageformat2_parser.h @@ -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 diff --git a/deps/icu-small/source/i18n/messageformat2_serializer.cpp b/deps/icu-small/source/i18n/messageformat2_serializer.cpp index b2765f5acf434f..dfae0083392bf1 100644 --- a/deps/icu-small/source/i18n/messageformat2_serializer.cpp +++ b/deps/icu-small/source/i18n/messageformat2_serializer.cpp @@ -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 */ diff --git a/deps/icu-small/source/i18n/messageformat2_serializer.h b/deps/icu-small/source/i18n/messageformat2_serializer.h index 1b97b3b930087d..f190b255f0d3e5 100644 --- a/deps/icu-small/source/i18n/messageformat2_serializer.h +++ b/deps/icu-small/source/i18n/messageformat2_serializer.h @@ -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 diff --git a/deps/icu-small/source/i18n/nfrs.cpp b/deps/icu-small/source/i18n/nfrs.cpp index be2ab2932e7a5c..b7ffb561461b98 100644 --- a/deps/icu-small/source/i18n/nfrs.cpp +++ b/deps/icu-small/source/i18n/nfrs.cpp @@ -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 diff --git a/deps/icu-small/source/i18n/nfrule.cpp b/deps/icu-small/source/i18n/nfrule.cpp index 264e8d79e2d968..ef7f5924c4eb55 100644 --- a/deps/icu-small/source/i18n/nfrule.cpp +++ b/deps/icu-small/source/i18n/nfrule.cpp @@ -19,7 +19,6 @@ #if U_HAVE_RBNF -#include #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 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(c - gZero); - if ((val > 0 && val > (std::numeric_limits::max() - single_digit) / 10) || - (val < 0 && val < (std::numeric_limits::min() - single_digit) / 10)) { + int64_t digit = static_cast(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(c - gZero); + int64_t digit = static_cast(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); } diff --git a/deps/icu-small/source/i18n/number_decimalquantity.cpp b/deps/icu-small/source/i18n/number_decimalquantity.cpp index f9350d5d5cc8a2..ca1dacd3579d85 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.cpp +++ b/deps/icu-small/source/i18n/number_decimalquantity.cpp @@ -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) { diff --git a/deps/icu-small/source/i18n/number_longnames.cpp b/deps/icu-small/source/i18n/number_longnames.cpp index de6aad7c3e8af7..bf2617e773d06f 100644 --- a/deps/icu-small/source/i18n/number_longnames.cpp +++ b/deps/icu-small/source/i18n/number_longnames.cpp @@ -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; diff --git a/deps/icu-small/source/i18n/number_mapper.cpp b/deps/icu-small/source/i18n/number_mapper.cpp index 2f398d4a9392fb..457fbc0d0712a3 100644 --- a/deps/icu-small/source/i18n/number_mapper.cpp +++ b/deps/icu-small/source/i18n/number_mapper.cpp @@ -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; diff --git a/deps/icu-small/source/i18n/number_rounding.cpp b/deps/icu-small/source/i18n/number_rounding.cpp index 8f1aa453ada44c..0f3975393c581b 100644 --- a/deps/icu-small/source/i18n/number_rounding.cpp +++ b/deps/icu-small/source/i18n/number_rounding.cpp @@ -278,23 +278,23 @@ Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { } FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) { - FractionSignificantSettings settings; + FractionSignificantSettings settings{}; settings.fMinFrac = static_cast(minFrac); settings.fMaxFrac = static_cast(maxFrac); settings.fMinSig = -1; settings.fMaxSig = -1; - PrecisionUnion union_; + PrecisionUnion union_{}; union_.fracSig = settings; return {RND_FRACTION, union_}; } Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) { - FractionSignificantSettings settings; + FractionSignificantSettings settings{}; settings.fMinFrac = -1; settings.fMaxFrac = -1; settings.fMinSig = static_cast(minSig); settings.fMaxSig = static_cast(maxSig); - PrecisionUnion union_; + PrecisionUnion union_{}; union_.fracSig = settings; return {RND_SIGNIFICANT, union_}; } @@ -311,20 +311,20 @@ Precision::constructFractionSignificant( settings.fMaxSig = static_cast(maxSig); settings.fPriority = priority; settings.fRetain = retain; - PrecisionUnion union_; + PrecisionUnion union_{}; union_.fracSig = settings; return {RND_FRACTION_SIGNIFICANT, union_}; } IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) { - IncrementSettings settings; + IncrementSettings settings{}; // Note: For number formatting, fIncrement is used for RND_INCREMENT but not // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all // three when constructing a skeleton. settings.fIncrement = increment; settings.fIncrementMagnitude = magnitude; settings.fMinFrac = magnitude > 0 ? 0 : -magnitude; - PrecisionUnion union_; + PrecisionUnion union_{}; union_.increment = settings; if (increment == 1) { // NOTE: In C++, we must return the correct value type with the correct union. @@ -339,7 +339,7 @@ IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t ma } CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { - PrecisionUnion union_; + PrecisionUnion union_{}; union_.currencyUsage = usage; return {RND_CURRENCY, union_}; } diff --git a/deps/icu-small/source/i18n/number_skeletons.cpp b/deps/icu-small/source/i18n/number_skeletons.cpp index 562a8663d05769..67a38dad073766 100644 --- a/deps/icu-small/source/i18n/number_skeletons.cpp +++ b/deps/icu-small/source/i18n/number_skeletons.cpp @@ -1015,6 +1015,12 @@ blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroPr return true; } +// The function is called by skeleton::parseOption which called by skeleton::parseSkeleton +// the data pointed in the return macros.unit is stack allocated in the parseSkeleton function. +#if U_GCC_MAJOR_MINOR >= 1204 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdangling-pointer" +#endif void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us @@ -1034,6 +1040,9 @@ void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroP // Slicing is OK macros.unit = currency; // NOLINT } +#if U_GCC_MAJOR_MINOR >= 1204 +#pragma GCC diagnostic pop +#endif void blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode&) { diff --git a/deps/icu-small/source/i18n/olsontz.cpp b/deps/icu-small/source/i18n/olsontz.cpp index 9d9770dd4224e4..7826d47a7d06f5 100644 --- a/deps/icu-small/source/i18n/olsontz.cpp +++ b/deps/icu-small/source/i18n/olsontz.cpp @@ -436,11 +436,11 @@ int32_t OlsonTimeZone::getRawOffset() const { #if defined U_DEBUG_TZ void printTime(double ms) { - int32_t year, month, dom, dow; - double millis=0; - double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis); - - Grego::dayToFields(days, year, month, dom, dow); + int32_t year; + int8_t month, dom, dow; + int32_t millis=0; + UErrorCode status = U_ZERO_ERROR; + Grego::timeToFields(ms, year, month, dom, dow, millis, status); U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms, year, month+1, dom, (millis/kOneHour))); } @@ -568,9 +568,8 @@ UBool OlsonTimeZone::useDaylightTime() const { return finalZone->useDaylightTime(); } - int32_t year, month, dom, dow, doy, mid; UErrorCode status = U_ZERO_ERROR; - Grego::timeToFields(current, year, month, dom, dow, doy, mid, status); + int32_t year = Grego::timeToYear(current, status); U_ASSERT(U_SUCCESS(status)); if (U_FAILURE(status)) return false; // If error, just return false. diff --git a/deps/icu-small/source/i18n/persncal.cpp b/deps/icu-small/source/i18n/persncal.cpp index 31f7ae252b5e8a..792a88807956b8 100644 --- a/deps/icu-small/source/i18n/persncal.cpp +++ b/deps/icu-small/source/i18n/persncal.cpp @@ -25,6 +25,9 @@ #include "umutex.h" #include "gregoimp.h" // Math #include +#include "cmemory.h" +#include "ucln_in.h" +#include "unicode/uniset.h" static const int16_t kPersianNumDays[] = {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year @@ -62,6 +65,45 @@ static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { { 0, 0, 11, 11}, // ORDINAL_MONTH }; +namespace { // anonymous + +static const icu::UnicodeSet *gLeapCorrection = nullptr; +static icu::UInitOnce gCorrectionInitOnce {}; +static int32_t gMinCorrection; +} // namespace +U_CDECL_BEGIN +static UBool calendar_persian_cleanup() { + if (gLeapCorrection) { + delete gLeapCorrection; + gLeapCorrection = nullptr; + } + gCorrectionInitOnce.reset(); + return true; +} +U_CDECL_END + +namespace { // anonymous +static void U_CALLCONV initLeapCorrection() { + static int16_t nonLeapYears[] = { + 1502, 1601, 1634, 1667, 1700, 1733, 1766, 1799, 1832, 1865, 1898, 1931, 1964, 1997, 2030, 2059, + 2063, 2096, 2129, 2158, 2162, 2191, 2195, 2224, 2228, 2257, 2261, 2290, 2294, 2323, 2327, 2356, + 2360, 2389, 2393, 2422, 2426, 2455, 2459, 2488, 2492, 2521, 2525, 2554, 2558, 2587, 2591, 2620, + 2624, 2653, 2657, 2686, 2690, 2719, 2723, 2748, 2752, 2756, 2781, 2785, 2789, 2818, 2822, 2847, + 2851, 2855, 2880, 2884, 2888, 2913, 2917, 2921, 2946, 2950, 2954, 2979, 2983, 2987, + }; + gMinCorrection = nonLeapYears[0]; + icu::UnicodeSet prefab; + for (auto year : nonLeapYears) { + prefab.add(year); + } + gLeapCorrection = prefab.cloneAsThawed()->freeze(); + ucln_i18n_registerCleanup(UCLN_I18N_PERSIAN_CALENDAR, calendar_persian_cleanup); +} +const icu::UnicodeSet* getLeapCorrection() { + umtx_initOnce(gCorrectionInitOnce, &initLeapCorrection); + return gLeapCorrection; +} +} // namespace anonymous U_NAMESPACE_BEGIN static const int32_t PERSIAN_EPOCH = 1948320; @@ -83,7 +125,6 @@ PersianCalendar* PersianCalendar::clone() const { PersianCalendar::PersianCalendar(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. } PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { @@ -111,8 +152,15 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li */ UBool PersianCalendar::isLeapYear(int32_t year) { + if (year >= gMinCorrection && getLeapCorrection()->contains(year)) { + return false; + } + if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + return true; + } int64_t y = static_cast(year) * 25LL + 11LL; - return (y % 33L < 8); + bool res = (y % 33L < 8); + return res; } /** @@ -157,7 +205,8 @@ int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mont /** * Return the number of days in the given Persian year */ -int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { +int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const { + if (U_FAILURE(status)) return 0; return isLeapYear(extendedYear) ? 366 : 365; } @@ -165,6 +214,15 @@ int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { // Functions for converting from field values to milliseconds.... //------------------------------------------------------------------------- +static int64_t firstJulianOfYear(int64_t year) { + int64_t julianDay = 365LL * (year - 1LL) + ClockMath::floorDivide(8LL * year + 21, 33); + if (year > gMinCorrection && getLeapCorrection()->contains(year-1)) { + julianDay--; + } + return julianDay; +} + + // Return JD of start of given month/year int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const { if (U_FAILURE(status)) { @@ -179,7 +237,7 @@ int64_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U } } - int64_t julianDay = PERSIAN_EPOCH - 1LL + 365LL * (eyear - 1LL) + ClockMath::floorDivide(8LL * eyear + 21, 33); + int64_t julianDay = PERSIAN_EPOCH - 1LL + firstJulianOfYear(eyear); if (month != 0) { julianDay += kPersianNumDays[month]; @@ -219,6 +277,7 @@ int32_t PersianCalendar::handleGetExtendedYear(UErrorCode& status) { void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { int64_t daysSinceEpoch = julianDay; daysSinceEpoch -= PERSIAN_EPOCH; + int64_t year = ClockMath::floorDivideInt64( 33LL * daysSinceEpoch + 3LL, 12053LL) + 1LL; if (year > INT32_MAX || year < INT32_MIN) { @@ -226,11 +285,16 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) return; } - int64_t farvardin1 = 365LL * (year - 1) + ClockMath::floorDivide(8LL * year + 21, 33); + int64_t farvardin1 = firstJulianOfYear(year); + int32_t dayOfYear = daysSinceEpoch - farvardin1; // 0-based U_ASSERT(dayOfYear >= 0); U_ASSERT(dayOfYear < 366); - // + + if (dayOfYear == 365 && year >= gMinCorrection && getLeapCorrection()->contains(year)) { + year++; + dayOfYear = 0; + } int32_t month; if (dayOfYear < 216) { // Compute 0-based month month = dayOfYear / 31; @@ -240,11 +304,11 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) U_ASSERT(month >= 0); U_ASSERT(month < 12); - int32_t dayOfMonth = dayOfYear - kPersianNumDays[month] + 1; + ++dayOfYear; // Make it 1-based now + int32_t dayOfMonth = dayOfYear - kPersianNumDays[month]; U_ASSERT(dayOfMonth > 0); U_ASSERT(dayOfMonth <= 31); - ++dayOfYear; // Make it 1-based now internalSet(UCAL_ERA, 0); internalSet(UCAL_YEAR, year); diff --git a/deps/icu-small/source/i18n/persncal.h b/deps/icu-small/source/i18n/persncal.h index daf7508b702ae4..d5bff7b232740b 100644 --- a/deps/icu-small/source/i18n/persncal.h +++ b/deps/icu-small/source/i18n/persncal.h @@ -209,7 +209,7 @@ class PersianCalendar : public Calendar { * Return the number of days in the given Persian 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.... diff --git a/deps/icu-small/source/i18n/rbnf.cpp b/deps/icu-small/source/i18n/rbnf.cpp index c4e8ff73a7cc0c..5b6b5e2c189282 100644 --- a/deps/icu-small/source/i18n/rbnf.cpp +++ b/deps/icu-small/source/i18n/rbnf.cpp @@ -1568,12 +1568,12 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali // divide up the descriptions into individual rule-set descriptions // and store them in a temporary array. At each step, we also - // new up a rule set, but all this does is initialize its name + // create a rule set, but all this does is initialize its name // and remove it from its description. We can't actually parse // the rest of the descriptions and finish initializing everything // because we have to know the names and locations of all the rule // sets before we can actually set everything up - if(!numRuleSets) { + if (!numRuleSets) { status = U_ILLEGAL_ARGUMENT_ERROR; return; } @@ -1616,9 +1616,9 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali // last public rule set, no matter what the localization data says. initDefaultRuleSet(); - // finally, we can go back through the temporary descriptions - // list and finish setting up the substructure (and we throw - // away the temporary descriptions as we go) + // Now that we know all the rule names, we can go back through + // the temporary descriptions list and finish setting up the substructure + // (and we throw away the temporary descriptions as we go) { for (int i = 0; i < numRuleSets; i++) { fRuleSets[i]->parseRules(ruleSetDescriptions[i], status); @@ -1706,10 +1706,13 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description) UnicodeString result; int start = 0; - while (start != -1 && start < description.length()) { - // seek to the first non-whitespace character... + UChar ch; + while (start < description.length()) { + // Seek to the first non-whitespace character... + // If the first non-whitespace character is semicolon, skip it and continue while (start < description.length() - && PatternProps::isWhiteSpace(description.charAt(start))) { + && (PatternProps::isWhiteSpace(ch = description.charAt(start)) || ch == gSemiColon)) + { ++start; } @@ -1720,20 +1723,16 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description) // or if we don't find a semicolon, just copy the rest of // the string into the result result.append(description, start, description.length() - start); - start = -1; + break; } else if (p < description.length()) { result.append(description, start, p + 1 - start); start = p + 1; } - - // when we get here, we've seeked off the end of the string, and + // when we get here from the else, we've seeked off the end of the string, and // we terminate the loop (we continue until *start* is -1 rather // than until *p* is -1, because otherwise we'd miss the last // rule in the description) - else { - start = -1; - } } description.setTo(result); diff --git a/deps/icu-small/source/i18n/scriptset.cpp b/deps/icu-small/source/i18n/scriptset.cpp index eec1eeb37dafbf..576917e81c4196 100644 --- a/deps/icu-small/source/i18n/scriptset.cpp +++ b/deps/icu-small/source/i18n/scriptset.cpp @@ -285,19 +285,19 @@ uhash_equalsScriptSet(const UElement key1, const UElement key2) { return (*s1 == *s2); } -U_CAPI int8_t U_EXPORT2 +U_CAPI int32_t U_EXPORT2 uhash_compareScriptSet(UElement key0, UElement key1) { icu::ScriptSet *s0 = static_cast(key0.pointer); icu::ScriptSet *s1 = static_cast(key1.pointer); int32_t diff = s0->countMembers() - s1->countMembers(); - if (diff != 0) return static_cast(diff); + if (diff != 0) return diff; int32_t i0 = s0->nextSetBit(0); int32_t i1 = s1->nextSetBit(0); while ((diff = i0-i1) == 0 && i0 > 0) { i0 = s0->nextSetBit(i0+1); i1 = s1->nextSetBit(i1+1); } - return (int8_t)diff; + return diff; } U_CAPI int32_t U_EXPORT2 diff --git a/deps/icu-small/source/i18n/scriptset.h b/deps/icu-small/source/i18n/scriptset.h index df5cfdc7486890..d21d0db8a0144b 100644 --- a/deps/icu-small/source/i18n/scriptset.h +++ b/deps/icu-small/source/i18n/scriptset.h @@ -74,7 +74,7 @@ class U_I18N_API ScriptSet: public UMemory { U_NAMESPACE_END -U_CAPI UBool U_EXPORT2 +U_CAPI int32_t U_EXPORT2 uhash_compareScriptSet(const UElement key1, const UElement key2); U_CAPI int32_t U_EXPORT2 diff --git a/deps/icu-small/source/i18n/simpletz.cpp b/deps/icu-small/source/i18n/simpletz.cpp index cbefc29830ffd9..3f3b236ea45ca9 100644 --- a/deps/icu-small/source/i18n/simpletz.cpp +++ b/deps/icu-small/source/i18n/simpletz.cpp @@ -518,15 +518,10 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT } rawOffsetGMT = getRawOffset(); - int32_t year, month, dom, dow, millis; - double dday = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis); - if (dday > INT32_MAX || dday < INT32_MIN) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t day = dday; + int32_t year, millis; + int8_t month, dom, dow; - Grego::dayToFields(day, year, month, dom, dow, status); + Grego::timeToFields(date, year, month, dom, dow, millis, status); if (U_FAILURE(status)) return; savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, @@ -554,8 +549,7 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT } } if (recalc) { - day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis); - Grego::dayToFields(day, year, month, dom, dow, status); + Grego::timeToFields(date, year, month, dom, dow, millis, status); if (U_FAILURE(status)) return; savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, static_cast(dow), millis, diff --git a/deps/icu-small/source/i18n/smpdtfmt.cpp b/deps/icu-small/source/i18n/smpdtfmt.cpp index f79d4ae49532b0..3c13d5a413fa54 100644 --- a/deps/icu-small/source/i18n/smpdtfmt.cpp +++ b/deps/icu-small/source/i18n/smpdtfmt.cpp @@ -77,6 +77,10 @@ #include "dayperiodrules.h" #include "tznames_impl.h" // ZONE_NAME_U16_MAX #include "number_utypes.h" +#include "chnsecal.h" +#include "dangical.h" +#include "japancal.h" +#include #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) #include @@ -945,7 +949,8 @@ SimpleDateFormat::initialize(const Locale& locale, // if format is non-numeric (includes 年) and fDateOverride is not already specified. // Now this does get updated if applyPattern subsequently changes the pattern type. if (fDateOverride.isBogus() && fHasHanYearChar && - fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 && + fCalendar != nullptr && + typeid(*fCalendar) == typeid(JapaneseCalendar) && uprv_strcmp(fLocale.getLanguage(),"ja") == 0) { fDateOverride.setTo(u"y=jpanyear", -1); } @@ -1050,7 +1055,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, } Calendar* workCal = &cal; Calendar* calClone = nullptr; - if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { + if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) { // Different calendar type // We use the time and time zone from the input calendar, but // do not use the input calendar for field calculation. @@ -1523,8 +1528,8 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name case UDAT_ERA_FIELD: { - const auto* calType = cal.getType(); - if (uprv_strcmp(calType,"chinese") == 0 || uprv_strcmp(calType,"dangi") == 0) { + if (typeid(cal) == typeid(ChineseCalendar) || + typeid(cal) == typeid(DangiCalendar)) { zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J } else { if (count == 5) { @@ -1575,7 +1580,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // for "MMMMM"/"LLLLL", use the narrow form case UDAT_MONTH_FIELD: case UDAT_STANDALONE_MONTH_FIELD: - if (uprv_strcmp(cal.getType(),"hebrew") == 0) { + if (typeid(cal) == typeid(HebrewCalendar)) { if (HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value == 6 && count >= 3 ) value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar. if (!HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value >= 6 && count < 3 ) @@ -2272,7 +2277,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& Calendar* calClone = nullptr; Calendar *workCal = &cal; - if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) { + if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) { // Different calendar type // We use the time/zone from the input calendar, but // do not use the input calendar for field calculation. @@ -2903,7 +2908,7 @@ int32_t SimpleDateFormat::matchAlphaMonthStrings(const UnicodeString& text, if (bestMatch >= 0) { // Adjustment for Hebrew Calendar month Adar II - if (!strcmp(cal.getType(),"hebrew") && bestMatch==13) { + if (typeid(cal) == typeid(HebrewCalendar) && bestMatch==13) { cal.set(UCAL_MONTH,6); } else { cal.set(UCAL_MONTH, bestMatch); @@ -2963,7 +2968,7 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, if (bestMatch >= 0) { if (field < UCAL_FIELD_COUNT) { // Adjustment for Hebrew Calendar month Adar II - if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) { + if (typeid(cal) == typeid(HebrewCalendar) && field==UCAL_MONTH && bestMatch==13) { cal.set(field,6); } else { if (field == UCAL_YEAR) { @@ -3052,7 +3057,6 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, ch if (numericLeapMonthFormatter != nullptr) { numericLeapMonthFormatter->setFormats(reinterpret_cast(¤tNumberFormat), 1); } - UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0); // If there are any spaces here, skip over them. If we hit the end // of the string, then fail. @@ -3068,6 +3072,8 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, ch } pos.setIndex(start); + UBool isChineseCalendar = typeid(cal) == typeid(ChineseCalendar) || + typeid(cal) == typeid(DangiCalendar); // We handle a few special cases here where we need to parse // a number value. We handle further, more generic cases below. We need // to handle some of them here because some fields require extra processing on @@ -3289,7 +3295,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, ch // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether // or not it was a leap year. We may or may not yet know what year it is, so might have to delay checking until // the year is parsed. - if (!strcmp(cal.getType(),"hebrew")) { + if (typeid(cal) == typeid(HebrewCalendar)) { HebrewCalendar *hc = (HebrewCalendar*)&cal; if (cal.isSet(UCAL_YEAR)) { UErrorCode monthStatus = U_ZERO_ERROR; @@ -3852,7 +3858,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, ch switch (patternCharIndex) { case UDAT_MONTH_FIELD: // See notes under UDAT_MONTH_FIELD case above - if (!strcmp(cal.getType(),"hebrew")) { + if (typeid(cal) == typeid(HebrewCalendar)) { HebrewCalendar *hc = (HebrewCalendar*)&cal; if (cal.isSet(UCAL_YEAR)) { UErrorCode monthStatus = U_ZERO_ERROR; @@ -4034,7 +4040,7 @@ SimpleDateFormat::applyPattern(const UnicodeString& pattern) // Hack to update use of Gannen year numbering for ja@calendar=japanese - // use only if format is non-numeric (includes 年) and no other fDateOverride. - if (fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 && + if (fCalendar != nullptr && typeid(*fCalendar) == typeid(JapaneseCalendar) && uprv_strcmp(fLocale.getLanguage(),"ja") == 0) { if (fDateOverride==UnicodeString(u"y=jpanyear") && !fHasHanYearChar) { // Gannen numbering is set but new pattern should not use it, unset; diff --git a/deps/icu-small/source/i18n/taiwncal.cpp b/deps/icu-small/source/i18n/taiwncal.cpp index e6ffd71ba3089a..1f948031002205 100644 --- a/deps/icu-small/source/i18n/taiwncal.cpp +++ b/deps/icu-small/source/i18n/taiwncal.cpp @@ -36,7 +36,6 @@ static const int32_t kGregorianEpoch = 1970; TaiwanCalendar::TaiwanCalendar(const Locale& aLocale, UErrorCode& success) : GregorianCalendar(aLocale, success) { - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. } TaiwanCalendar::~TaiwanCalendar() @@ -48,12 +47,6 @@ TaiwanCalendar::TaiwanCalendar(const TaiwanCalendar& source) { } -TaiwanCalendar& TaiwanCalendar::operator= ( const TaiwanCalendar& right) -{ - GregorianCalendar::operator=(right); - return *this; -} - TaiwanCalendar* TaiwanCalendar::clone() const { return new TaiwanCalendar(*this); diff --git a/deps/icu-small/source/i18n/taiwncal.h b/deps/icu-small/source/i18n/taiwncal.h index a8fa3d1829ed2e..d12ab1c4a2ee6d 100644 --- a/deps/icu-small/source/i18n/taiwncal.h +++ b/deps/icu-small/source/i18n/taiwncal.h @@ -79,13 +79,6 @@ class TaiwanCalendar : public GregorianCalendar { */ TaiwanCalendar(const TaiwanCalendar& source); - /** - * Default assignment operator - * @param right the object to be copied. - * @internal - */ - TaiwanCalendar& operator=(const TaiwanCalendar& right); - /** * Create and return a polymorphic copy of this calendar. * @return return a polymorphic copy of this calendar. diff --git a/deps/icu-small/source/i18n/timezone.cpp b/deps/icu-small/source/i18n/timezone.cpp index 118dfe2f2af629..8028c4ecf5cb96 100644 --- a/deps/icu-small/source/i18n/timezone.cpp +++ b/deps/icu-small/source/i18n/timezone.cpp @@ -730,15 +730,9 @@ void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, // (with 7 args) twice when local == true and DST is // detected in the initial call. for (int32_t pass=0; ; ++pass) { - int32_t year, month, dom, dow, millis; - double day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis); - - // out of the range - if (day < INT32_MIN || day > INT32_MAX) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - Grego::dayToFields(day, year, month, dom, dow, ec); + int32_t year, millis; + int8_t month, dom, dow; + Grego::timeToFields(date, year, month, dom, dow, millis, ec); if (U_FAILURE(ec)) return; dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, @@ -1057,7 +1051,7 @@ TimeZone::countEquivalentIDs(const UnicodeString& id) { // --------------------------------------- -const UnicodeString U_EXPORT2 +UnicodeString U_EXPORT2 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) { U_DEBUG_TZ_MSG(("gEI(%d)\n", index)); UnicodeString result; diff --git a/deps/icu-small/source/i18n/tzgnames.cpp b/deps/icu-small/source/i18n/tzgnames.cpp index 57ee984ee9ba35..e7b09efd01a76e 100644 --- a/deps/icu-small/source/i18n/tzgnames.cpp +++ b/deps/icu-small/source/i18n/tzgnames.cpp @@ -406,7 +406,7 @@ TZGNCore::initialize(const Locale& locale, UErrorCode& status) { int32_t regionLen = static_cast(uprv_strlen(region)); if (regionLen == 0) { CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), status); - ulocimp_getSubtags(loc.data(), nullptr, nullptr, &fTargetRegion, nullptr, nullptr, status); + ulocimp_getSubtags(loc.toStringPiece(), nullptr, nullptr, &fTargetRegion, nullptr, nullptr, status); if (U_FAILURE(status)) { cleanup(); return; diff --git a/deps/icu-small/source/i18n/tznames_impl.cpp b/deps/icu-small/source/i18n/tznames_impl.cpp index 9b7ade7f0bb12f..769c9a6526c587 100644 --- a/deps/icu-small/source/i18n/tznames_impl.cpp +++ b/deps/icu-small/source/i18n/tznames_impl.cpp @@ -2149,7 +2149,7 @@ TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale) if (regionLen == 0) { UErrorCode status = U_ZERO_ERROR; CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), status); - ulocimp_getSubtags(loc.data(), nullptr, nullptr, &fRegion, nullptr, nullptr, status); + ulocimp_getSubtags(loc.toStringPiece(), nullptr, nullptr, &fRegion, nullptr, nullptr, status); if (U_SUCCESS(status)) { useWorld = false; } diff --git a/deps/icu-small/source/i18n/tzrule.cpp b/deps/icu-small/source/i18n/tzrule.cpp index 7507068c8807d8..8d6a37a844cbd8 100644 --- a/deps/icu-small/source/i18n/tzrule.cpp +++ b/deps/icu-small/source/i18n/tzrule.cpp @@ -355,9 +355,8 @@ AnnualTimeZoneRule::getNextStart(UDate base, int32_t prevDSTSavings, UBool inclusive, UDate& result) const { - int32_t year, month, dom, dow, doy, mid; UErrorCode status = U_ZERO_ERROR; - Grego::timeToFields(base, year, month, dom, dow, doy, mid, status); + int32_t year = Grego::timeToYear(base, status); U_ASSERT(U_SUCCESS(status)); if (year < fStartYear) { return getFirstStart(prevRawOffset, prevDSTSavings, result); @@ -381,9 +380,8 @@ AnnualTimeZoneRule::getPreviousStart(UDate base, int32_t prevDSTSavings, UBool inclusive, UDate& result) const { - int32_t year, month, dom, dow, doy, mid; UErrorCode status = U_ZERO_ERROR; - Grego::timeToFields(base, year, month, dom, dow, doy, mid, status); + int32_t year = Grego::timeToYear(base, status); U_ASSERT(U_SUCCESS(status)); if (year > fEndYear) { return getFinalStart(prevRawOffset, prevDSTSavings, result); diff --git a/deps/icu-small/source/i18n/ucln_in.h b/deps/icu-small/source/i18n/ucln_in.h index 765cdd559fb4e2..74868891c83744 100644 --- a/deps/icu-small/source/i18n/ucln_in.h +++ b/deps/icu-small/source/i18n/ucln_in.h @@ -39,6 +39,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_HEBREW_CALENDAR, UCLN_I18N_ASTRO_CALENDAR, UCLN_I18N_DANGI_CALENDAR, + UCLN_I18N_PERSIAN_CALENDAR, UCLN_I18N_CALENDAR, UCLN_I18N_TIMEZONEFORMAT, UCLN_I18N_TZDBTIMEZONENAMES, @@ -62,6 +63,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_REGION, UCLN_I18N_LIST_FORMATTER, UCLN_I18N_NUMSYS, + UCLN_I18N_MF2_UNISETS, UCLN_I18N_COUNT /* This must be last */ } ECleanupI18NType; diff --git a/deps/icu-small/source/i18n/ucol_sit.cpp b/deps/icu-small/source/i18n/ucol_sit.cpp index 87387f879d8392..f8fa02fad71997 100644 --- a/deps/icu-small/source/i18n/ucol_sit.cpp +++ b/deps/icu-small/source/i18n/ucol_sit.cpp @@ -450,7 +450,7 @@ ucol_prepareShortStringOpen( const char *definition, ucol_sit_readSpecs(&s, definition, parseError, status); ucol_sit_calculateWholeLocale(&s, *status); - CharString buffer = ulocimp_canonicalize(s.locale.data(), *status); + CharString buffer = ulocimp_canonicalize(s.locale.toStringPiece(), *status); UResourceBundle *b = ures_open(U_ICUDATA_COLL, buffer.data(), status); /* we try to find stuff from keyword */ @@ -514,7 +514,7 @@ ucol_openFromShortString( const char *definition, #ifdef UCOL_TRACE_SIT fprintf(stderr, "DEF %s, DATA %s, ERR %s\n", definition, s.locale.data(), u_errorName(*status)); #endif - CharString buffer = ulocimp_canonicalize(s.locale.data(), *status); + CharString buffer = ulocimp_canonicalize(s.locale.toStringPiece(), *status); UCollator *result = ucol_open(buffer.data(), status); int32_t i = 0; diff --git a/deps/icu-small/source/i18n/unicode/calendar.h b/deps/icu-small/source/i18n/unicode/calendar.h index a04f5b65bd5ba6..4499e281f9c55e 100644 --- a/deps/icu-small/source/i18n/unicode/calendar.h +++ b/deps/icu-small/source/i18n/unicode/calendar.h @@ -55,6 +55,7 @@ class ICUServiceFactory; typedef int32_t UFieldResolutionTable[12][8]; class BasicTimeZone; +class CharString; /** * `Calendar` is an abstract base class for converting between * a `UDate` object and a set of integer fields such as @@ -1692,10 +1693,9 @@ class U_I18N_API Calendar : public UObject { * 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; - + virtual int32_t handleGetYearLength(int32_t eyear, UErrorCode& status) const; /** * Return the extended year defined by the current fields. This will @@ -1881,42 +1881,7 @@ class U_I18N_API Calendar : public UObject { */ int32_t getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const; - protected: - /** - * The flag which indicates if the current time is set in the calendar. - * @stable ICU 2.0 - */ - UBool fIsTimeSet; - - /** - * True if the fields are in sync with the currently set time of this Calendar. - * If false, then the next attempt to get the value of a field will - * force a recomputation of all fields from the current value of the time - * field. - *

- * This should really be named areFieldsInSync, but the old name is retained - * for backward compatibility. - * @stable ICU 2.0 - */ - UBool fAreFieldsSet; - - /** - * True if all of the fields have been set. This is initially false, and set to - * true by computeFields(). - * @stable ICU 2.0 - */ - UBool fAreAllFieldsSet; - - /** - * True if all fields have been virtually set, but have not yet been - * computed. This occurs only in setTimeInMillis(). A calendar set - * to this state will compute all fields from the time if it becomes - * necessary, but otherwise will delay such computation. - * @stable ICU 3.0 - */ - UBool fAreFieldsVirtuallySet; - /** * Get the current time without recomputing. * @@ -1940,14 +1905,7 @@ class U_I18N_API Calendar : public UObject { */ int32_t fFields[UCAL_FIELD_COUNT]; -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * The flags which tell if a specified time field for the calendar is set. - * @deprecated ICU 2.8 use (fStamp[n]!=kUnset) - */ - UBool fIsSet[UCAL_FIELD_COUNT]; -#endif // U_FORCE_HIDE_DEPRECATED_API - +protected: /** Special values of stamp[] * @stable ICU 2.0 */ @@ -1957,14 +1915,15 @@ class U_I18N_API Calendar : public UObject { kMinimumUserStamp }; +private: /** * Pseudo-time-stamps which specify when each field was set. There * are two special values, UNSET and INTERNALLY_SET. Values from - * MINIMUM_USER_SET to Integer.MAX_VALUE are legal user set values. - * @stable ICU 2.0 + * MINIMUM_USER_SET to STAMP_MAX are legal user set values. */ - int32_t fStamp[UCAL_FIELD_COUNT]; + int8_t fStamp[UCAL_FIELD_COUNT]; +protected: /** * Subclasses may override this method to compute several fields * specific to each calendar system. These are: @@ -2178,7 +2137,7 @@ class U_I18N_API Calendar : public UObject { /** * The next available value for fStamp[] */ - int32_t fNextStamp;// = MINIMUM_USER_STAMP; + int8_t fNextStamp = kMinimumUserStamp; /** * Recalculates the time stamp array (fStamp). @@ -2189,30 +2148,60 @@ class U_I18N_API Calendar : public UObject { /** * The current time set for the calendar. */ - UDate fTime; + UDate fTime = 0; /** - * @see #setLenient + * Time zone affects the time calculation done by Calendar. Calendar subclasses use + * the time zone data to produce the local time. Always set; never nullptr. */ - UBool fLenient; + TimeZone* fZone = nullptr; /** - * Time zone affects the time calculation done by Calendar. Calendar subclasses use - * the time zone data to produce the local time. Always set; never nullptr. + * The flag which indicates if the current time is set in the calendar. + */ + bool fIsTimeSet:1; + + /** + * True if the fields are in sync with the currently set time of this Calendar. + * If false, then the next attempt to get the value of a field will + * force a recomputation of all fields from the current value of the time + * field. + *

+ * This should really be named areFieldsInSync, but the old name is retained + * for backward compatibility. + */ + bool fAreFieldsSet:1; + + /** + * True if all of the fields have been set. This is initially false, and set to + * true by computeFields(). + */ + bool fAreAllFieldsSet:1; + + /** + * True if all fields have been virtually set, but have not yet been + * computed. This occurs only in setTimeInMillis(). A calendar set + * to this state will compute all fields from the time if it becomes + * necessary, but otherwise will delay such computation. */ - TimeZone* fZone; + bool fAreFieldsVirtuallySet:1; + + /** + * @see #setLenient + */ + bool fLenient:1; /** * Option for repeated wall time * @see #setRepeatedWallTimeOption */ - UCalendarWallTimeOption fRepeatedWallTime; + UCalendarWallTimeOption fRepeatedWallTime:3; // Somehow MSVC need 3 bits for UCalendarWallTimeOption /** * Option for skipped wall time * @see #setSkippedWallTimeOption */ - UCalendarWallTimeOption fSkippedWallTime; + UCalendarWallTimeOption fSkippedWallTime:3; // Somehow MSVC need 3 bits for UCalendarWallTimeOption /** * Both firstDayOfWeek and minimalDaysInFirstWeek are locale-dependent. They are @@ -2222,11 +2211,14 @@ class U_I18N_API Calendar : public UObject { * out the week count for a specific date for a given locale. These must be set when * a Calendar is constructed. */ - UCalendarDaysOfWeek fFirstDayOfWeek; - uint8_t fMinimalDaysInFirstWeek; - UCalendarDaysOfWeek fWeekendOnset; + UCalendarDaysOfWeek fFirstDayOfWeek:4; // Somehow MSVC need 4 bits for + // UCalendarDaysOfWeek + UCalendarDaysOfWeek fWeekendOnset:4; // Somehow MSVC need 4 bits for + // UCalendarDaysOfWeek + UCalendarDaysOfWeek fWeekendCease:4; // Somehow MSVC need 4 bits for + // UCalendarDaysOfWeek + uint8_t fMinimalDaysInFirstWeek; int32_t fWeekendOnsetMillis; - UCalendarDaysOfWeek fWeekendCease; int32_t fWeekendCeaseMillis; /** @@ -2264,32 +2256,24 @@ class U_I18N_API Calendar : public UObject { * returned by getGregorianMonth(). * @see #computeGregorianFields */ - int32_t fGregorianMonth; + int8_t fGregorianMonth; /** - * The Gregorian day of the year, as computed by - * computeGregorianFields() and returned by getGregorianDayOfYear(). + * The Gregorian day of the month, as computed by + * computeGregorianFields() and returned by getGregorianDayOfMonth(). * @see #computeGregorianFields */ - int32_t fGregorianDayOfYear; + int8_t fGregorianDayOfMonth; /** - * The Gregorian day of the month, as computed by - * computeGregorianFields() and returned by getGregorianDayOfMonth(). + * The Gregorian day of the year, as computed by + * computeGregorianFields() and returned by getGregorianDayOfYear(). * @see #computeGregorianFields */ - int32_t fGregorianDayOfMonth; + int16_t fGregorianDayOfYear; /* calculations */ - /** - * 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 computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec); - protected: /** @@ -2359,8 +2343,8 @@ class U_I18N_API Calendar : public UObject { #endif /* U_HIDE_INTERNAL_API */ private: - char validLocale[ULOC_FULLNAME_CAPACITY]; - char actualLocale[ULOC_FULLNAME_CAPACITY]; + CharString* validLocale = nullptr; + CharString* actualLocale = nullptr; public: #if !UCONFIG_NO_SERVICE @@ -2563,7 +2547,6 @@ Calendar::internalSet(UCalendarDateFields field, int32_t value) { fFields[field] = value; fStamp[field] = kInternallySet; - fIsSet[field] = true; // Remove later } /** diff --git a/deps/icu-small/source/i18n/unicode/dcfmtsym.h b/deps/icu-small/source/i18n/unicode/dcfmtsym.h index 02e12f9c39b2dc..3aecceda03a6ee 100644 --- a/deps/icu-small/source/i18n/unicode/dcfmtsym.h +++ b/deps/icu-small/source/i18n/unicode/dcfmtsym.h @@ -48,6 +48,7 @@ U_NAMESPACE_BEGIN +class CharString; /** * This class represents the set of symbols needed by DecimalFormat * to format numbers. DecimalFormat creates for itself an instance of @@ -504,8 +505,8 @@ class U_I18N_API DecimalFormatSymbols : public UObject { Locale locale; - char actualLocale[ULOC_FULLNAME_CAPACITY]; - char validLocale[ULOC_FULLNAME_CAPACITY]; + CharString* actualLocale = nullptr; + CharString* validLocale = nullptr; const char16_t* currPattern = nullptr; UnicodeString currencySpcBeforeSym[UNUM_CURRENCY_SPACING_COUNT]; diff --git a/deps/icu-small/source/i18n/unicode/dtfmtsym.h b/deps/icu-small/source/i18n/unicode/dtfmtsym.h index df8da36d8153fb..18e2641b588fcd 100644 --- a/deps/icu-small/source/i18n/unicode/dtfmtsym.h +++ b/deps/icu-small/source/i18n/unicode/dtfmtsym.h @@ -43,6 +43,7 @@ U_NAMESPACE_BEGIN /* forward declaration */ class SimpleDateFormat; class Hashtable; +class CharString; /** * DateFormatSymbols is a public class for encapsulating localizable date-time @@ -917,8 +918,8 @@ class U_I18N_API DateFormatSymbols final : public UObject { /** valid/actual locale information * these are always ICU locales, so the length should not be a problem */ - char validLocale[ULOC_FULLNAME_CAPACITY]; - char actualLocale[ULOC_FULLNAME_CAPACITY]; + CharString* validLocale = nullptr; + CharString* actualLocale = nullptr; DateFormatSymbols() = delete; // default constructor not implemented diff --git a/deps/icu-small/source/i18n/unicode/format.h b/deps/icu-small/source/i18n/unicode/format.h index a21e61ad56d85c..3d435f0de7e367 100644 --- a/deps/icu-small/source/i18n/unicode/format.h +++ b/deps/icu-small/source/i18n/unicode/format.h @@ -45,6 +45,7 @@ U_NAMESPACE_BEGIN +class CharString; /** * Base class for all formats. This is an abstract base class which * specifies the protocol for classes which convert other objects or @@ -297,8 +298,8 @@ class U_I18N_API Format : public UObject { UParseError& parseError); private: - char actualLocale[ULOC_FULLNAME_CAPACITY]; - char validLocale[ULOC_FULLNAME_CAPACITY]; + CharString* actualLocale = nullptr; + CharString* validLocale = nullptr; }; U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/unicode/gregocal.h b/deps/icu-small/source/i18n/unicode/gregocal.h index 5112548522d557..cd84471c9ba1a6 100644 --- a/deps/icu-small/source/i18n/unicode/gregocal.h +++ b/deps/icu-small/source/i18n/unicode/gregocal.h @@ -518,7 +518,7 @@ class U_I18N_API GregorianCalendar: public Calendar { * default implementation in Calendar. * @stable ICU 2.0 */ - virtual int32_t handleGetYearLength(int32_t eyear) const override; + virtual int32_t handleGetYearLength(int32_t eyear, UErrorCode& status) const override; /** * return the length of the given month. diff --git a/deps/icu-small/source/i18n/unicode/measunit.h b/deps/icu-small/source/i18n/unicode/measunit.h index b23897192eb4cb..f0abd4f4f92f44 100644 --- a/deps/icu-small/source/i18n/unicode/measunit.h +++ b/deps/icu-small/source/i18n/unicode/measunit.h @@ -105,21 +105,19 @@ typedef enum UMeasurePrefix { */ UMEASURE_PREFIX_YOTTA = UMEASURE_PREFIX_ONE + 24, -#ifndef U_HIDE_DRAFT_API /** * SI prefix: ronna, 10^27. * - * @draft ICU 75 + * @stable ICU 75 */ UMEASURE_PREFIX_RONNA = UMEASURE_PREFIX_ONE + 27, /** * SI prefix: quetta, 10^30. * - * @draft ICU 75 + * @stable ICU 75 */ UMEASURE_PREFIX_QUETTA = UMEASURE_PREFIX_ONE + 30, -#endif /* U_HIDE_DRAFT_API */ #ifndef U_HIDE_INTERNAL_API /** @@ -268,21 +266,19 @@ typedef enum UMeasurePrefix { */ UMEASURE_PREFIX_YOCTO = UMEASURE_PREFIX_ONE + -24, -#ifndef U_HIDE_DRAFT_API /** * SI prefix: ronto, 10^-27. * - * @draft ICU 75 + * @stable ICU 75 */ UMEASURE_PREFIX_RONTO = UMEASURE_PREFIX_ONE + -27, /** * SI prefix: quecto, 10^-30. * - * @draft ICU 75 + * @stable ICU 75 */ UMEASURE_PREFIX_QUECTO = UMEASURE_PREFIX_ONE + -30, -#endif /* U_HIDE_DRAFT_API */ #ifndef U_HIDE_INTERNAL_API /** @@ -430,16 +426,19 @@ class U_I18N_API MeasureUnit: public UObject { MeasureUnit(MeasureUnit &&other) noexcept; /** - * Construct a MeasureUnit from a CLDR Core Unit Identifier, defined in UTS - * 35. (Core unit identifiers and mixed unit identifiers are supported, long - * unit identifiers are not.) Validates and canonicalizes the identifier. + * Constructs a MeasureUnit from a CLDR Core Unit Identifier, as defined in UTS 35. + * This method supports core unit identifiers and mixed unit identifiers. + * It validates and canonicalizes the given identifier. + * * + * Example usage: *

-     * MeasureUnit example = MeasureUnit::forIdentifier("furlong-per-nanosecond")
+     * MeasureUnit example = MeasureUnit::forIdentifier("meter-per-second", status);
      * 
* - * @param identifier The CLDR Unit Identifier. - * @param status Set if the identifier is invalid. + * @param identifier the CLDR Unit Identifier + * @param status Set error if the identifier is invalid. + * @return the corresponding MeasureUnit * @stable ICU 67 */ static MeasureUnit forIdentifier(StringPiece identifier, UErrorCode& status); @@ -552,6 +551,44 @@ class U_I18N_API MeasureUnit: public UObject { */ UMeasurePrefix getPrefix(UErrorCode& status) const; +#ifndef U_HIDE_DRAFT_API + + /** + * Creates a new MeasureUnit with a specified constant denominator. + * + * This method is applicable only to COMPOUND and SINGLE units. If invoked on a + * MIXED unit, an error will be set in the status. + * + * NOTE: If the constant denominator is set to 0, it means that you are removing + * the constant denominator. + * + * @param denominator The constant denominator to set. + * @param status Set if this is not a COMPOUND or SINGLE unit or if another error occurs. + * @return A new MeasureUnit with the specified constant denominator. + * @draft ICU 77 + */ + MeasureUnit withConstantDenominator(uint64_t denominator, UErrorCode &status) const; + + /** + * Retrieves the constant denominator for this COMPOUND unit. + * + * Examples: + * - For the unit "liter-per-1000-kiloliter", the constant denominator is 1000. + * - For the unit "liter-per-kilometer", the constant denominator is zero. + * + * This method is applicable only to COMPOUND and SINGLE units. If invoked on + * a MIXED unit, an error will be set in the status. + * + * NOTE: If no constant denominator exists, the method returns 0. + * + * @param status Set if this is not a COMPOUND or SINGLE unit or if another error occurs. + * @return The value of the constant denominator. + * @draft ICU 77 + */ + uint64_t getConstantDenominator(UErrorCode &status) const; + +#endif /* U_HIDE_DRAFT_API */ + /** * Creates a MeasureUnit which is this SINGLE unit augmented with the specified dimensionality * (power). For example, if dimensionality is 2, the unit will be squared. @@ -591,7 +628,9 @@ class U_I18N_API MeasureUnit: public UObject { * NOTE: Only works on SINGLE and COMPOUND units. If this is a MIXED unit, an error will * occur. For more information, see UMeasureUnitComplexity. * - * @param status Set if this is a MIXED unit or if another error occurs. + * NOTE: An Error will be returned for units that have a constant denominator. + * + * @param status Set if this is a MIXED unit, has a constant denominator or if another error occurs. * @return The reciprocal of the target unit. * @stable ICU 67 */ @@ -627,6 +666,10 @@ class U_I18N_API MeasureUnit: public UObject { * * If this is a SINGLE unit, an array of length 1 will be returned. * + * NOTE: For units with a constant denominator, the returned single units will + * not include the constant denominator. To obtain the constant denominator, + * retrieve it from the original unit. + * * @param status Set if an error occurs. * @return A pair with the list of units as a LocalArray and the number of units in the list. * @stable ICU 68 @@ -1152,6 +1195,24 @@ class U_I18N_API MeasureUnit: public UObject { */ static MeasureUnit getPermyriad(); +#ifndef U_HIDE_DRAFT_API + /** + * Returns by pointer, unit of concentr: portion-per-1e9. + * Caller owns returned value and must free it. + * Also see {@link #getPortionPer1E9()}. + * @param status ICU error code. + * @draft ICU 77 + */ + static MeasureUnit *createPortionPer1E9(UErrorCode &status); + + /** + * Returns by value, unit of concentr: portion-per-1e9. + * Also see {@link #createPortionPer1E9()}. + * @draft ICU 77 + */ + static MeasureUnit getPortionPer1E9(); +#endif /* U_HIDE_DRAFT_API */ + /** * Returns by pointer, unit of consumption: liter-per-100-kilometer. * Caller owns returned value and must free it. diff --git a/deps/icu-small/source/i18n/unicode/messageformat2.h b/deps/icu-small/source/i18n/unicode/messageformat2.h index c5459f042f40f8..926d14318d17eb 100644 --- a/deps/icu-small/source/i18n/unicode/messageformat2.h +++ b/deps/icu-small/source/i18n/unicode/messageformat2.h @@ -8,6 +8,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -20,6 +22,7 @@ #include "unicode/messageformat2_arguments.h" #include "unicode/messageformat2_data_model.h" #include "unicode/messageformat2_function_registry.h" +#include "unicode/normalizer2.h" #include "unicode/unistr.h" #ifndef U_HIDE_DEPRECATED_API @@ -30,8 +33,8 @@ namespace message2 { class Environment; class MessageContext; - class ResolvedSelector; class StaticErrors; + class InternalValue; /** *

MessageFormatter is a Technical Preview API implementing MessageFormat 2.0. @@ -325,6 +328,8 @@ namespace message2 { private: friend class Builder; + friend class Checker; + friend class MessageArguments; friend class MessageContext; MessageFormatter(const MessageFormatter::Builder& builder, UErrorCode &status); @@ -334,9 +339,6 @@ namespace message2 { // Do not define default assignment operator const MessageFormatter &operator=(const MessageFormatter &) = delete; - ResolvedSelector resolveVariables(const Environment& env, const data_model::Operand&, MessageContext&, UErrorCode &) const; - ResolvedSelector resolveVariables(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode &) const; - // Selection methods // Takes a vector of FormattedPlaceholders @@ -346,31 +348,35 @@ namespace message2 { // Takes a vector of vectors of strings (input) and a vector of PrioritizedVariants (input/output) void sortVariants(const UVector&, UVector&, UErrorCode&) const; // Takes a vector of strings (input) and a vector of strings (output) - void matchSelectorKeys(const UVector&, MessageContext&, ResolvedSelector&& rv, UVector&, UErrorCode&) const; + void matchSelectorKeys(const UVector&, MessageContext&, InternalValue* rv, UVector&, UErrorCode&) const; // Takes a vector of FormattedPlaceholders (input), // and a vector of vectors of strings (output) void resolvePreferences(MessageContext&, UVector&, UVector&, UErrorCode&) const; // Formatting methods + + // Used for normalizing variable names and keys for comparison + UnicodeString normalizeNFC(const UnicodeString&) const; [[nodiscard]] FormattedPlaceholder formatLiteral(const data_model::Literal&) const; void formatPattern(MessageContext&, const Environment&, const data_model::Pattern&, UErrorCode&, UnicodeString&) const; - // Formats a call to a formatting function + // Evaluates a function call // Dispatches on argument type - [[nodiscard]] FormattedPlaceholder evalFormatterCall(FormattedPlaceholder&& argument, - MessageContext& context, - UErrorCode& status) const; + [[nodiscard]] InternalValue* evalFunctionCall(FormattedPlaceholder&& argument, + MessageContext& context, + UErrorCode& status) const; // Dispatches on function name - [[nodiscard]] FormattedPlaceholder evalFormatterCall(const FunctionName& functionName, - FormattedPlaceholder&& argument, - FunctionOptions&& options, - MessageContext& context, - UErrorCode& status) const; - // Formats an expression that appears as a selector - ResolvedSelector formatSelectorExpression(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode&) const; + [[nodiscard]] InternalValue* evalFunctionCall(const FunctionName& functionName, + InternalValue* argument, + FunctionOptions&& options, + MessageContext& context, + UErrorCode& status) const; // Formats an expression that appears in a pattern or as the definition of a local variable - [[nodiscard]] FormattedPlaceholder formatExpression(const Environment&, const data_model::Expression&, MessageContext&, UErrorCode&) const; + [[nodiscard]] InternalValue* formatExpression(const Environment&, + const data_model::Expression&, + MessageContext&, + UErrorCode&) const; [[nodiscard]] FunctionOptions resolveOptions(const Environment& env, const OptionMap&, MessageContext&, UErrorCode&) const; - [[nodiscard]] FormattedPlaceholder formatOperand(const Environment&, const data_model::Operand&, MessageContext&, UErrorCode&) const; + [[nodiscard]] InternalValue* formatOperand(const Environment&, const data_model::Operand&, MessageContext&, UErrorCode&) const; [[nodiscard]] FormattedPlaceholder evalArgument(const data_model::VariableName&, MessageContext&, UErrorCode&) const; void formatSelectors(MessageContext& context, const Environment& env, UErrorCode &status, UnicodeString& result) const; @@ -445,6 +451,10 @@ namespace message2 { // formatting methods return best-effort output. // The default is false. bool signalErrors = false; + + // Used for implementing normalizeNFC() + const Normalizer2* nfcNormalizer = nullptr; + }; // class MessageFormatter } // namespace message2 @@ -457,6 +467,8 @@ U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT2_H diff --git a/deps/icu-small/source/i18n/unicode/messageformat2_arguments.h b/deps/icu-small/source/i18n/unicode/messageformat2_arguments.h index c43d96191f16f1..07c96f892bcaff 100644 --- a/deps/icu-small/source/i18n/unicode/messageformat2_arguments.h +++ b/deps/icu-small/source/i18n/unicode/messageformat2_arguments.h @@ -8,6 +8,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -43,7 +45,7 @@ template class U_I18N_API LocalArray; namespace message2 { - class MessageContext; + class MessageFormatter; // Arguments // ---------- @@ -112,7 +114,9 @@ namespace message2 { private: friend class MessageContext; - const Formattable* getArgument(const data_model::VariableName&, UErrorCode&) const; + const Formattable* getArgument(const MessageFormatter&, + const data_model::VariableName&, + UErrorCode&) const; // Avoids using Hashtable so that code constructing a Hashtable // doesn't have to appear in this header file @@ -131,6 +135,8 @@ U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT2_ARGUMENTS_H diff --git a/deps/icu-small/source/i18n/unicode/messageformat2_data_model.h b/deps/icu-small/source/i18n/unicode/messageformat2_data_model.h index 0c836af1bdfd35..fd9b6432a5d1ee 100644 --- a/deps/icu-small/source/i18n/unicode/messageformat2_data_model.h +++ b/deps/icu-small/source/i18n/unicode/messageformat2_data_model.h @@ -8,6 +8,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -2211,7 +2213,7 @@ namespace message2 { friend class MFDataModel; - Matcher(Expression* ss, int32_t ns, Variant* vs, int32_t nv); + Matcher(VariableName* ss, int32_t ns, Variant* vs, int32_t nv); Matcher() {} // A Matcher may have numSelectors=0 and numVariants=0 @@ -2219,8 +2221,8 @@ namespace message2 { // So we have to keep a separate flag to track failed copies. bool bogus = false; - // The expressions that are being matched on. - LocalArray selectors; + // The variables that are being matched on. + LocalArray selectors; // The number of selectors int32_t numSelectors = 0; // The list of `when` clauses (case arms). @@ -2328,13 +2330,13 @@ namespace message2 { * @internal ICU 75 technology preview * @deprecated This API is for technology preview only. */ - const std::vector getSelectors() const { + std::vector getSelectors() const { if (std::holds_alternative(body)) { return {}; } const Matcher* match = std::get_if(&body); // match must be non-null, given the previous check - return toStdVector(match->selectors.getAlias(), match->numSelectors); + return toStdVector(match->selectors.getAlias(), match->numSelectors); } /** * Accesses the variants. Returns an empty vector if this is a pattern message. @@ -2462,17 +2464,17 @@ namespace message2 { */ Builder& addBinding(Binding&& b, UErrorCode& status); /** - * Adds a selector expression. Copies `expression`. + * Adds a selector variable. * If a pattern was previously set, clears the pattern. * - * @param selector Expression to add as a selector. Passed by move. + * @param selector Variable to add as a selector. Passed by move. * @param errorCode Input/output error code * @return A reference to the builder. * * @internal ICU 75 technology preview * @deprecated This API is for technology preview only. */ - Builder& addSelector(Expression&& selector, UErrorCode& errorCode) noexcept; + Builder& addSelector(VariableName&& selector, UErrorCode& errorCode); /** * Adds a single variant. * If a pattern was previously set using `setPattern()`, clears the pattern. @@ -2564,7 +2566,7 @@ namespace message2 { int32_t bindingsLen = 0; const Binding* getLocalVariablesInternal() const; - const Expression* getSelectorsInternal() const; + const VariableName* getSelectorsInternal() const; const Variant* getVariantsInternal() const; int32_t numSelectors() const { @@ -2592,6 +2594,8 @@ U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT_DATA_MODEL_H diff --git a/deps/icu-small/source/i18n/unicode/messageformat2_formattable.h b/deps/icu-small/source/i18n/unicode/messageformat2_formattable.h index 8a779adb9ab348..d7f4130f493b99 100644 --- a/deps/icu-small/source/i18n/unicode/messageformat2_formattable.h +++ b/deps/icu-small/source/i18n/unicode/messageformat2_formattable.h @@ -8,6 +8,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -549,6 +551,7 @@ class U_I18N_API FunctionOptions : public UObject { */ FunctionOptions& operator=(const FunctionOptions&) = delete; private: + friend class InternalValue; friend class MessageFormatter; friend class StandardFunctions; @@ -566,12 +569,10 @@ class U_I18N_API FunctionOptions : public UObject { // that code in the header because it would have to call internal Hashtable methods. ResolvedFunctionOption* options; int32_t functionOptionsLen = 0; -}; // class FunctionOptions - - // TODO doc comments - // Encapsulates either a formatted string or formatted number; - // more output types could be added in the future. + // Returns a new FunctionOptions + FunctionOptions mergeOptions(FunctionOptions&& other, UErrorCode&); +}; // class FunctionOptions /** * A `FormattedValue` represents the result of formatting a `message2::Formattable`. @@ -1010,6 +1011,8 @@ U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT2_FORMATTABLE_H diff --git a/deps/icu-small/source/i18n/unicode/messageformat2_function_registry.h b/deps/icu-small/source/i18n/unicode/messageformat2_function_registry.h index b8429e3b83aa91..37690d5e04a1e3 100644 --- a/deps/icu-small/source/i18n/unicode/messageformat2_function_registry.h +++ b/deps/icu-small/source/i18n/unicode/messageformat2_function_registry.h @@ -8,6 +8,8 @@ #if U_SHOW_CPLUSPLUS_API +#if !UCONFIG_NO_NORMALIZATION + #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 @@ -422,6 +424,8 @@ U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT2_FUNCTION_REGISTRY_H diff --git a/deps/icu-small/source/i18n/unicode/numberformatter.h b/deps/icu-small/source/i18n/unicode/numberformatter.h index b02d987ce2bcba..28531afeb10fd3 100644 --- a/deps/icu-small/source/i18n/unicode/numberformatter.h +++ b/deps/icu-small/source/i18n/unicode/numberformatter.h @@ -2609,12 +2609,11 @@ class U_I18N_API LocalizedNumberFormatter */ Format* toFormat(UErrorCode& status) const; -#ifndef U_HIDE_DRAFT_API /** * Disassociate the locale from this formatter. * * @return The fluent chain. - * @draft ICU 75 + * @stable ICU 75 */ UnlocalizedNumberFormatter withoutLocale() const &; @@ -2623,10 +2622,9 @@ class U_I18N_API LocalizedNumberFormatter * * @return The fluent chain. * @see #withoutLocale - * @draft ICU 75 + * @stable ICU 75 */ UnlocalizedNumberFormatter withoutLocale() &&; -#endif // U_HIDE_DRAFT_API /** * Default constructor: puts the formatter into a valid but undefined state. diff --git a/deps/icu-small/source/i18n/unicode/numberrangeformatter.h b/deps/icu-small/source/i18n/unicode/numberrangeformatter.h index b8bbc1ba072d5b..bb6e43bed8cf25 100644 --- a/deps/icu-small/source/i18n/unicode/numberrangeformatter.h +++ b/deps/icu-small/source/i18n/unicode/numberrangeformatter.h @@ -503,12 +503,11 @@ class U_I18N_API LocalizedNumberRangeFormatter FormattedNumberRange formatFormattableRange( const Formattable& first, const Formattable& second, UErrorCode& status) const; -#ifndef U_HIDE_DRAFT_API /** * Disassociate the locale from this formatter. * * @return The fluent chain. - * @draft ICU 75 + * @stable ICU 75 */ UnlocalizedNumberRangeFormatter withoutLocale() const &; @@ -517,10 +516,9 @@ class U_I18N_API LocalizedNumberRangeFormatter * * @return The fluent chain. * @see #withoutLocale - * @draft ICU 75 + * @stable ICU 75 */ UnlocalizedNumberRangeFormatter withoutLocale() &&; -#endif // U_HIDE_DRAFT_API /** * Default constructor: puts the formatter into a valid but undefined state. diff --git a/deps/icu-small/source/i18n/unicode/rbnf.h b/deps/icu-small/source/i18n/unicode/rbnf.h index f42d91d776fed9..5a23f723363c9b 100644 --- a/deps/icu-small/source/i18n/unicode/rbnf.h +++ b/deps/icu-small/source/i18n/unicode/rbnf.h @@ -88,23 +88,24 @@ enum URBNFRuleSetTag { }; /** - * The RuleBasedNumberFormat class formats numbers according to a set of rules. This number formatter is - * typically used for spelling out numeric values in words (e.g., 25,3476 as - * "twenty-five thousand three hundred seventy-six" or "vingt-cinq mille trois + * The RuleBasedNumberFormat class formats numbers according to a set of rules. + * + *

This number formatter is typically used for spelling out numeric values in words (e.g., 25,3476 + * as "twenty-five thousand three hundred seventy-six" or "vingt-cinq mille trois * cents soixante-seize" or * "fünfundzwanzigtausenddreihundertsechsundsiebzig"), but can also be used for * other complicated formatting tasks, such as formatting a number of seconds as hours, - * minutes and seconds (e.g., 3,730 as "1:02:10"). + * minutes and seconds (e.g., 3,730 as "1:02:10").

* *

The resources contain three predefined formatters for each locale: spellout, which * spells out a value in words (123 is "one hundred twenty-three"); ordinal, which * appends an ordinal suffix to the end of a numeral (123 is "123rd"); and * duration, which shows a duration in seconds as hours, minutes, and seconds (123 is - * "2:03").  The client can also define more specialized RuleBasedNumberFormats + * "2:03").  The client can also define more specialized RuleBasedNumberFormats * by supplying programmer-defined rule sets.

* - *

The behavior of a RuleBasedNumberFormat is specified by a textual description - * that is either passed to the constructor as a String or loaded from a resource + *

The behavior of a RuleBasedNumberFormat is specified by a textual description + * that is either passed to the constructor as a String or loaded from a resource * bundle. In its simplest form, the description consists of a semicolon-delimited list of rules. * Each rule has a string of output text and a value or range of values it is applicable to. * In a typical spellout rule set, the first twenty rules are the words for the numbers from @@ -116,7 +117,8 @@ enum URBNFRuleSetTag { *

For larger numbers, we can use the preceding set of rules to format the ones place, and * we only have to supply the words for the multiples of 10:

* - *
 20: twenty[->>];
+ * 
+ * 20: twenty[->>];
  * 30: thirty[->>];
  * 40: forty[->>];
  * 50: fifty[->>];
@@ -137,7 +139,8 @@ enum URBNFRuleSetTag {
  * 

For even larger numbers, we can actually look up several parts of the number in the * list:

* - *
100: << hundred[ >>];
+ *
+ * 100: << hundred[ >>];
* *

The "<<" represents a new kind of substitution. The << isolates * the hundreds digit (and any digits to its left), formats it using this same rule set, and @@ -155,13 +158,15 @@ enum URBNFRuleSetTag { * *

This rule covers values up to 999, at which point we add another rule:

* - *
1000: << thousand[ >>];
+ *
+ * 1000: << thousand[ >>];
* *

Again, the meanings of the brackets and substitution tokens shift because the rule's * base value is a higher power of 10, changing the rule's divisor. This rule can actually be * used all the way up to 999,999. This allows us to finish out the rules as follows:

* - *
 1,000,000: << million[ >>];
+ * 
+ * 1,000,000: << million[ >>];
  * 1,000,000,000: << billion[ >>];
  * 1,000,000,000,000: << trillion[ >>];
  * 1,000,000,000,000,000: OUT OF RANGE!;
@@ -177,30 +182,30 @@ enum URBNFRuleSetTag { *

To see how these rules actually work in practice, consider the following example: * Formatting 25,430 with this rule set would work like this:

* - * + *
* - * - * + * + * * * - * - * + * + * * * - * - * + * + * * * - * - * + * + * * * - * - * + * + * * * - * - * + * * *
<< thousand >>[the rule whose base value is 1,000 is applicable to 25,340]<< thousand >>[the rule whose base value is 1,000 is applicable to 25,340]
twenty->> thousand >>[25,340 over 1,000 is 25. The rule for 20 applies.]twenty->> thousand >>[25,340 over 1,000 is 25. The rule for 20 applies.]
twenty-five thousand >>[25 mod 10 is 5. The rule for 5 is "five."twenty-five thousand >>[25 mod 10 is 5. The rule for 5 is "five."
twenty-five thousand << hundred >>[25,340 mod 1,000 is 340. The rule for 100 applies.]twenty-five thousand << hundred >>[25,340 mod 1,000 is 340. The rule for 100 applies.]
twenty-five thousand three hundred >>[340 over 100 is 3. The rule for 3 is "three."]twenty-five thousand three hundred >>[340 over 100 is 3. The rule for 3 is "three."]
twenty-five thousand three hundred forty[340 mod 100 is 40. The rule for 40 applies. Since 40 divides + * twenty-five thousand three hundred forty[340 mod 100 is 40. The rule for 40 applies. Since 40 divides * evenly by 10, the hyphen and substitution in the brackets are omitted.]
@@ -237,20 +242,20 @@ enum URBNFRuleSetTag { * *
* - *

The description of a RuleBasedNumberFormat's behavior consists of one or more rule + *

The description of a RuleBasedNumberFormat's behavior consists of one or more rule * sets. Each rule set consists of a name, a colon, and a list of rules. A rule * set name must begin with a % sign. Rule sets with names that begin with a single % sign * are public: the caller can specify that they be used to format and parse numbers. * Rule sets with names that begin with %% are private: they exist only for the use * of other rule sets. If a formatter only has one rule set, the name may be omitted.

* - *

The user can also specify a special "rule set" named %%lenient-parse. - * The body of %%lenient-parse isn't a set of number-formatting rules, but a RuleBasedCollator + *

The user can also specify a special "rule set" named %%lenient-parse. + * The body of %%lenient-parse isn't a set of number-formatting rules, but a RuleBasedCollator * description which is used to define equivalences for lenient parsing. For more information - * on the syntax, see RuleBasedCollator. For more information on lenient parsing, - * see setLenientParse(). Note: symbols that have syntactic meaning + * on the syntax, see RuleBasedCollator. For more information on lenient parsing, + * see setLenientParse(). Note: symbols that have syntactic meaning * in collation rules, such as '&', have no particular meaning when appearing outside - * of the lenient-parse rule set.

+ * of the lenient-parse rule set.

* *

The body of a rule set consists of an ordered, semicolon-delimited list of rules. * Internally, every rule has a base value, a divisor, rule text, and zero, one, or two substitutions. @@ -260,42 +265,46 @@ enum URBNFRuleSetTag { *

A rule descriptor can take one of the following forms (text in italics is the * name of a token):

* - * + *
* - * - * + * + * + * + * + * * - * - * - * + * + * * - * - * - * + * + * * - * - * - * + * + * * - * - * - * + * + * + * * - * - * - * + * + * * - * - * - * + * + * + * the punctuation of either the full stop or comma * - * - * - * + * + * + * the punctuation of either the full stop or comma * - * - * - * + * + * + * * - * - * - * + * + * + * * - * - * - * + * + * @@ -352,8 +361,8 @@ enum URBNFRuleSetTag { * algorithms: If the rule set is a regular rule set, do the following: * *
    - *
  • If the rule set includes a default rule (and the number was passed in as a double), - * use the default rule.  (If the number being formatted was passed in as a long, + *
  • If the rule set includes a default rule (and the number was passed in as a double), + * use the default rule.  (If the number being formatted was passed in as a long, * the default rule is ignored.)
  • *
  • If the number is negative, use the negative-number rule.
  • *
  • If the number has a fractional part and is greater than 1, use the improper fraction @@ -400,42 +409,43 @@ enum URBNFRuleSetTag { * *

    The meanings of the substitution token characters are as follows:

    * - *
bv:bv specifies the rule's base value. bv is a decimal + * DescriptorDescription
bv:bv specifies the rule's base value. bv is a decimal * number expressed using ASCII digits. bv may contain spaces, period, and commas, * which are ignored. The rule's divisor is the highest power of 10 less than or equal to * the base value.
bv/rad:bv specifies the rule's base value. The rule's divisor is the + *
bv/rad:bv specifies the rule's base value. The rule's divisor is the * highest power of rad less than or equal to the base value.
bv>:bv specifies the rule's base value. To calculate the divisor, + *
bv>:bv specifies the rule's base value. To calculate the divisor, * let the radix be 10, and the exponent be the highest exponent of the radix that yields a * result less than or equal to the base value. Every > character after the base value * decreases the exponent by 1. If the exponent is positive or 0, the divisor is the radix * raised to the power of the exponent; otherwise, the divisor is 1.
bv/rad>:bv specifies the rule's base value. To calculate the divisor, + *
bv/rad>:bv specifies the rule's base value. To calculate the divisor, * let the radix be rad, and the exponent be the highest exponent of the radix that * yields a result less than or equal to the base value. Every > character after the radix * decreases the exponent by 1. If the exponent is positive or 0, the divisor is the radix * raised to the power of the exponent; otherwise, the divisor is 1.
-x:The rule is a negative-number rule.
-x:The rule is a negative-number rule.
x.x:The rule is an improper fraction rule. If the full stop in + *
x.x:The rule is an improper fraction rule. If the full stop in * the middle of the rule name is replaced with the decimal point * that is used in the language or DecimalFormatSymbols, then that rule will * have precedence when formatting and parsing this rule. For example, some @@ -304,39 +313,39 @@ enum URBNFRuleSetTag { * handle the decimal point that matches the language's natural spelling of * the punctuation of either the full stop or comma.
0.x:The rule is a proper fraction rule. If the full stop in + *
0.x:The rule is a proper fraction rule. If the full stop in * the middle of the rule name is replaced with the decimal point * that is used in the language or DecimalFormatSymbols, then that rule will * have precedence when formatting and parsing this rule. For example, some * languages use the comma, and can thus be written as 0,x instead. For example, * you can use "0.x: point >>;0,x: comma >>;" to * handle the decimal point that matches the language's natural spelling of - * the punctuation of either the full stop or comma.
x.0:The rule is a default rule. If the full stop in + *
x.0:The rule is a default rule. If the full stop in * the middle of the rule name is replaced with the decimal point * that is used in the language or DecimalFormatSymbols, then that rule will * have precedence when formatting and parsing this rule. For example, some * languages use the comma, and can thus be written as x,0 instead. For example, * you can use "x.0: << point;x,0: << comma;" to * handle the decimal point that matches the language's natural spelling of - * the punctuation of either the full stop or comma.
Inf:The rule for infinity.
Inf:The rule for infinity.
NaN:The rule for an IEEE 754 NaN (not a number).
NaN:The rule for an IEEE 754 NaN (not a number).
nothingIf the rule's rule descriptor is left out, the base value is one plus the + *
nothingIf the rule's rule descriptor is left out, the base value is one plus the * preceding rule's base value (or zero if this is the first rule in the list) in a normal * rule set.  In a fraction rule set, the base value is the same as the preceding rule's * base value.
+ *
* - * - * + * + * + * + * + * + * + * * * * - * - * + * * * * - * - * + * * * * - * - * + * * * - * - * - * + * + * + * * * * - * - * + * * * - * - * - * + * + * + * * * * - * - * + * * * * - * - * + * * * * - * - * + * * * - * - * - * + * + * + * * * - * - * - * - * + * + * + * + * * * - * - * - * + * + * * * - * - * - * + * + * * * - * - * - * + * + * * * - * - * + * * * * - * - * - * + * + * * - * - * - * - * + * + * + * * * - * - * - * - * + * + * + * * * *
>>in normal ruleSyntaxUsageDescription
>>in normal ruleDivide the number by the rule's divisor and format the remainder
in negative-number rulein negative-number ruleFind the absolute value of the number and format the result
in fraction or default rulein fraction or default ruleIsolate the number's fractional part and format it.
in rule in fraction rule setin rule in fraction rule setNot allowed.
>>>in normal rule
>>>in normal ruleDivide the number by the rule's divisor and format the remainder, * but bypass the normal rule-selection process and just use the * rule that precedes this one in this rule list.
in all other rulesin all other rulesNot allowed.
<<in normal rule
<<in normal ruleDivide the number by the rule's divisor, perform floor() on the quotient, * and format the resulting value.
* If there is a DecimalFormat pattern between the < characters and the @@ -448,73 +458,93 @@ enum URBNFRuleSetTag { *
in negative-number rulein negative-number ruleNot allowed.
in fraction or default rulein fraction or default ruleIsolate the number's integral part and format it.
in rule in fraction rule setin rule in fraction rule setMultiply the number by the rule's base value and format the result.
==in all rule sets
==in all rule setsFormat the number unchanged
[]in normal ruleOmit the optional text if the number is an even multiple of the rule's divisor
[]
[|]
in normal rule + *
    + *
  • When the number is not an even multiple of the rule's divisor, use the text and rules between the beginning square bracket, + * and the end square bracket or the | symbol.
  • + *
  • When the number is an even multiple of the rule's divisor, and no | symbol is used, omit the text.
  • + *
  • When the number is an even multiple of the rule's divisor, and | symbol is used, use the text and rules between the | symbol, + * and the end square bracket.
  • + *
+ *
in negative-number ruleNot allowed.in improper-fraction ruleThis syntax is the same as specifying both an x.x rule and a 0.x rule. + *
    + *
  • When the number is not between 0 and 1, use the text and rules between the beginning square bracket, + * and the end square bracket or the | symbol.
  • + *
  • When the number is between 0 and 1, and no | symbol is used, omit the text.
  • + *
  • When the number is between 0 and 1, and | symbol is used, use the text and rules between the | symbol, + * and the end square bracket.
  • + *
+ *
in improper-fraction ruleOmit the optional text if the number is between 0 and 1 (same as specifying both an - * x.x rule and a 0.x rule)in default ruleThis syntax is the same as specifying both an x.x rule and an x.0 rule. + *
    + *
  • When the number is not an integer, use the text and rules between the beginning square bracket, + * and the end square bracket or the | symbol.
  • + *
  • When the number is an integer, and no | symbol is used, omit the text.
  • + *
  • When the number is an integer, and | symbol is used, use the text and rules between the | symbol, + * and the end square bracket.
  • + *
+ *
in default ruleOmit the optional text if the number is an integer (same as specifying both an x.x - * rule and an x.0 rule)in rule in fraction rule set + *
    + *
  • When multiplying the number by the rule's base value does not yield 1, use the text and rules between the beginning square bracket, + * and the end square bracket or the | symbol.
  • + *
  • When multiplying the number by the rule's base value yields 1, and no | symbol is used, omit the text.
  • + *
  • When multiplying the number by the rule's base value yields 1, and | symbol is used, use the text and rules between the | symbol, + * and the end square bracket.
  • + *
+ *
in proper-fraction rulein proper-fraction ruleNot allowed.
in rule in fraction rule setOmit the optional text if multiplying the number by the rule's base value yields 1.in negative-number ruleNot allowed.
$(cardinal,plural syntax)$in all rule sets
$(cardinal,plural syntax)$in all rule setsThis provides the ability to choose a word based on the number divided by the radix to the power of the * exponent of the base value for the specified locale, which is normally equivalent to the << value. - * This uses the cardinal plural rules from PluralFormat. All strings used in the plural format are treated + * This uses the cardinal plural rules from {@link PluralFormat}. All strings used in the plural format are treated * as the same base value for parsing.
$(ordinal,plural syntax)$in all rule sets
$(ordinal,plural syntax)$in all rule setsThis provides the ability to choose a word based on the number divided by the radix to the power of the * exponent of the base value for the specified locale, which is normally equivalent to the << value. - * This uses the ordinal plural rules from PluralFormat. All strings used in the plural format are treated + * This uses the ordinal plural rules from {@link PluralFormat}. All strings used in the plural format are treated * as the same base value for parsing.
@@ -522,22 +552,25 @@ enum URBNFRuleSetTag { *

The substitution descriptor (i.e., the text between the token characters) may take one * of three forms:

* - * + *
* - * + * + * + * + * + * * * - * - * + * + * * * - * - * + * + * *
a rule set nameDescriptorDescription
a rule set namePerform the mathematical operation on the number, and format the result using the * named rule set.
a DecimalFormat pattern
a DecimalFormat patternPerform the mathematical operation on the number, and format the result using a * DecimalFormat with the specified pattern.  The pattern must begin with 0 or #.
nothing
nothingPerform the mathematical operation on the number, and format the result using the rule - * set containing the current rule, except: - *
    + * set containing the current rule, except:
      *
    • You can't have an empty substitution descriptor with a == substitution.
    • *
    • If you omit the substitution descriptor in a >> substitution in a fraction rule, * format the result one digit at a time using the rule set containing the current rule.
    • diff --git a/deps/icu-small/source/i18n/unicode/simplenumberformatter.h b/deps/icu-small/source/i18n/unicode/simplenumberformatter.h index d0121c2b97ec52..83fa8a8b1c92ca 100644 --- a/deps/icu-small/source/i18n/unicode/simplenumberformatter.h +++ b/deps/icu-small/source/i18n/unicode/simplenumberformatter.h @@ -78,14 +78,12 @@ class U_I18N_API SimpleNumber : public UMemory { */ void roundTo(int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode& status); -#ifndef U_HIDE_DRAFT_API /** * Sets the number of integer digits to the given amount, truncating if necessary. * - * @draft ICU 75 + * @stable ICU 75 */ void setMaximumIntegerDigits(uint32_t maximumIntegerDigits, UErrorCode& status); -#endif // U_HIDE_DRAFT_API /** * Pads the beginning of the number with zeros up to the given minimum number of integer digits. diff --git a/deps/icu-small/source/i18n/unicode/timezone.h b/deps/icu-small/source/i18n/unicode/timezone.h index b19900b76712c4..9591b7d90affad 100644 --- a/deps/icu-small/source/i18n/unicode/timezone.h +++ b/deps/icu-small/source/i18n/unicode/timezone.h @@ -323,7 +323,7 @@ class U_I18N_API TimeZone : public UObject { * @see #countEquivalentIDs * @stable ICU 2.0 */ - static const UnicodeString U_EXPORT2 getEquivalentID(const UnicodeString& id, + static UnicodeString U_EXPORT2 getEquivalentID(const UnicodeString& id, int32_t index); /** diff --git a/deps/icu-small/source/i18n/unicode/ucol.h b/deps/icu-small/source/i18n/unicode/ucol.h index ae4f29c3c6c4e6..9f721f67864255 100644 --- a/deps/icu-small/source/i18n/unicode/ucol.h +++ b/deps/icu-small/source/i18n/unicode/ucol.h @@ -1526,7 +1526,6 @@ ucol_openBinary(const uint8_t *bin, int32_t length, #include #include "unicode/char16ptr.h" -#include "unicode/stringpiece.h" #include "unicode/unistr.h" namespace U_HEADER_ONLY_NAMESPACE { @@ -1547,6 +1546,7 @@ class Predicate { /** @internal */ explicit Predicate(const UCollator* ucol) : collator(ucol) {} +#if U_SHOW_CPLUSPLUS_API /** @internal */ template < typename T, typename U, @@ -1554,6 +1554,28 @@ class Predicate { bool operator()(const T& lhs, const U& rhs) const { return match(UnicodeString::readOnlyAlias(lhs), UnicodeString::readOnlyAlias(rhs)); } +#else + /** @internal */ + bool operator()(std::u16string_view lhs, std::u16string_view rhs) const { + return match(lhs, rhs); + } + +#if !U_CHAR16_IS_TYPEDEF && (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION < 180000) + /** @internal */ + bool operator()(std::basic_string_view lhs, std::basic_string_view rhs) const { + return match({uprv_char16PtrFromUint16(lhs.data()), lhs.length()}, + {uprv_char16PtrFromUint16(rhs.data()), rhs.length()}); + } +#endif + +#if U_SIZEOF_WCHAR_T==2 + /** @internal */ + bool operator()(std::wstring_view lhs, std::wstring_view rhs) const { + return match({uprv_char16PtrFromWchar(lhs.data()), lhs.length()}, + {uprv_char16PtrFromWchar(rhs.data()), rhs.length()}); + } +#endif +#endif /** @internal */ bool operator()(std::string_view lhs, std::string_view rhs) const { @@ -1563,27 +1585,28 @@ class Predicate { #if defined(__cpp_char8_t) /** @internal */ bool operator()(std::u8string_view lhs, std::u8string_view rhs) const { - return match(lhs, rhs); + return match({reinterpret_cast(lhs.data()), lhs.length()}, + {reinterpret_cast(rhs.data()), rhs.length()}); } #endif private: - bool match(UnicodeString lhs, UnicodeString rhs) const { + bool match(std::u16string_view lhs, std::u16string_view rhs) const { return compare( ucol_strcoll( collator, - toUCharPtr(lhs.getBuffer()), lhs.length(), - toUCharPtr(rhs.getBuffer()), rhs.length()), + toUCharPtr(lhs.data()), static_cast(lhs.length()), + toUCharPtr(rhs.data()), static_cast(rhs.length())), result); } - bool match(StringPiece lhs, StringPiece rhs) const { + bool match(std::string_view lhs, std::string_view rhs) const { UErrorCode status = U_ZERO_ERROR; return compare( ucol_strcollUTF8( collator, - lhs.data(), lhs.length(), - rhs.data(), rhs.length(), + lhs.data(), static_cast(lhs.length()), + rhs.data(), static_cast(rhs.length()), &status), result); } diff --git a/deps/icu-small/source/i18n/unicode/usimplenumberformatter.h b/deps/icu-small/source/i18n/unicode/usimplenumberformatter.h index 22e81ba2c939b0..ccc5754b4348bb 100644 --- a/deps/icu-small/source/i18n/unicode/usimplenumberformatter.h +++ b/deps/icu-small/source/i18n/unicode/usimplenumberformatter.h @@ -156,15 +156,13 @@ U_CAPI void U_EXPORT2 usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec); -#ifndef U_HIDE_DRAFT_API /** * Sets the number of integer digits to the given amount, truncating if necessary. * - * @draft ICU 75 + * @stable ICU 75 */ U_CAPI void U_EXPORT2 usnum_setMaximumIntegerDigits(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec); -#endif // U_HIDE_DRAFT_API /** diff --git a/deps/icu-small/source/i18n/units_converter.cpp b/deps/icu-small/source/i18n/units_converter.cpp index 3ccb0065bfca34..1ab60bd4c06064 100644 --- a/deps/icu-small/source/i18n/units_converter.cpp +++ b/deps/icu-small/source/i18n/units_converter.cpp @@ -49,6 +49,8 @@ void U_I18N_API Factor::divideBy(const Factor &rhs) { offset = std::max(rhs.offset, offset); } +void U_I18N_API Factor::divideBy(const uint64_t constant) { factorDen *= constant; } + void U_I18N_API Factor::power(int32_t power) { // multiply all the constant by the power. for (int i = 0; i < CONSTANTS_COUNT; i++) { @@ -239,6 +241,12 @@ Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates & result.multiplyBy(singleFactor); } + // If the source has a constant denominator, then we need to divide the + // factor by the constant denominator. + if (source.constantDenominator != 0) { + result.divideBy(source.constantDenominator); + } + return result; } diff --git a/deps/icu-small/source/i18n/units_converter.h b/deps/icu-small/source/i18n/units_converter.h index 01fa557062feae..6f4b55f81d9bff 100644 --- a/deps/icu-small/source/i18n/units_converter.h +++ b/deps/icu-small/source/i18n/units_converter.h @@ -82,6 +82,7 @@ struct U_I18N_API Factor { void multiplyBy(const Factor &rhs); void divideBy(const Factor &rhs); + void divideBy(const uint64_t constant); // Apply the power to the factor. void power(int32_t power); diff --git a/deps/icu-small/source/i18n/vtzone.cpp b/deps/icu-small/source/i18n/vtzone.cpp index 6067c5490e8d46..8c0295bdcbe8b5 100644 --- a/deps/icu-small/source/i18n/vtzone.cpp +++ b/deps/icu-small/source/i18n/vtzone.cpp @@ -182,8 +182,9 @@ static UnicodeString& appendMillis(UDate date, UnicodeString& str) { */ static UnicodeString& getDateTimeString(UDate time, UnicodeString& str, UErrorCode& status) { if (U_FAILURE(status)) {return str;} - int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(time, year, month, dom, dow, doy, mid, status); + int32_t year, mid; + int8_t month, dom, dow; + Grego::timeToFields(time, year, month, dom, dow, mid, status); if (U_FAILURE(status)) {return str;} str.remove(); @@ -675,9 +676,10 @@ static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOff } // Calculate start/end year and missing fields - int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID; + int32_t startYear, startMID; + int8_t startMonth, startDOM; Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM, - startDOW, startDOY, startMID, status); + startMID, status); if (U_FAILURE(status)) { return nullptr; } @@ -692,8 +694,7 @@ static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOff int32_t endYear; if (until != MIN_MILLIS) { - int32_t endMonth, endDOM, endDOW, endDOY, endMID; - Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID, status); + endYear = Grego::timeToYear(until, status); if (U_FAILURE(status)) return nullptr; } else { endYear = AnnualTimeZoneRule::MAX_YEAR; @@ -1674,8 +1675,7 @@ VTimeZone::parse(UErrorCode& status) { status); } else { // Update the end year - int32_t y, m, d, dow, doy, mid; - Grego::timeToFields(start, y, m, d, dow, doy, mid, status); + int32_t y = Grego::timeToYear(start, status); if (U_FAILURE(status)) return; newRule.adoptInsteadAndCheckErrorCode( new AnnualTimeZoneRule( @@ -1902,7 +1902,8 @@ VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz, int32_t stdCount = 0; AnnualTimeZoneRule *finalStdRule = nullptr; - int32_t year, month, dom, dow, doy, mid; + int32_t year, mid; + int8_t month, dom, dow; UBool hasTransitions = false; TimeZoneTransition tzt; UBool tztAvail; @@ -1922,7 +1923,7 @@ VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz, int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings(); int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings(); int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings(); - Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid, status); + Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, mid, status); if (U_FAILURE(status)) return; int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); UBool sameRule = false; diff --git a/deps/icu-small/source/i18n/windtfmt.cpp b/deps/icu-small/source/i18n/windtfmt.cpp index 0241ec3b4424d0..f1d5c25f3ead9b 100644 --- a/deps/icu-small/source/i18n/windtfmt.cpp +++ b/deps/icu-small/source/i18n/windtfmt.cpp @@ -46,6 +46,7 @@ # define NOIME # define NOMCX #include +#include U_NAMESPACE_BEGIN @@ -251,7 +252,7 @@ UnicodeString &Win32DateFormat::format(Calendar &cal, UnicodeString &appendTo, F formatDate(&st_local, date); formatTime(&st_local, time); - if (strcmp(fCalendar->getType(), cal.getType()) != 0) { + if (typeid(cal) != typeid(*fCalendar)) { pattern = getTimeDateFormat(&cal, &fLocale, status); } @@ -272,7 +273,7 @@ void Win32DateFormat::parse(const UnicodeString& /* text */, Calendar& /* cal */ void Win32DateFormat::adoptCalendar(Calendar *newCalendar) { - if (fCalendar == nullptr || strcmp(fCalendar->getType(), newCalendar->getType()) != 0) { + if (fCalendar == nullptr || typeid(*fCalendar) != typeid(*newCalendar)) { UErrorCode status = U_ZERO_ERROR; if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) { diff --git a/deps/icu-small/source/tools/genrb/parse.cpp b/deps/icu-small/source/tools/genrb/parse.cpp index f487241cc18990..eb85d5157a6f9a 100644 --- a/deps/icu-small/source/tools/genrb/parse.cpp +++ b/deps/icu-small/source/tools/genrb/parse.cpp @@ -1153,7 +1153,7 @@ addCollation(ParseState* state, TableResource *result, const char *collationTyp struct UString *tokenValue; struct UString comment; enum ETokenType token; - char subtag[1024]; + CharString subtag; UnicodeString rules; UBool haveRules = false; UVersionInfo version; @@ -1189,15 +1189,15 @@ addCollation(ParseState* state, TableResource *result, const char *collationTyp return nullptr; } - u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); - + subtag.clear(); + subtag.appendInvariantChars(tokenValue->fChars, u_strlen(tokenValue->fChars), *status); if (U_FAILURE(*status)) { res_close(result); return nullptr; } - member = parseResource(state, subtag, nullptr, status); + member = parseResource(state, subtag.data(), nullptr, status); if (U_FAILURE(*status)) { @@ -1208,7 +1208,7 @@ addCollation(ParseState* state, TableResource *result, const char *collationTyp { // Ignore the parsed resources, continue parsing. } - else if (uprv_strcmp(subtag, "Version") == 0 && member->isString()) + else if (uprv_strcmp(subtag.data(), "Version") == 0 && member->isString()) { StringResource *sr = static_cast(member); char ver[40]; @@ -1225,11 +1225,11 @@ addCollation(ParseState* state, TableResource *result, const char *collationTyp result->add(member, line, *status); member = nullptr; } - else if(uprv_strcmp(subtag, "%%CollationBin")==0) + else if(uprv_strcmp(subtag.data(), "%%CollationBin")==0) { /* discard duplicate %%CollationBin if any*/ } - else if (uprv_strcmp(subtag, "Sequence") == 0 && member->isString()) + else if (uprv_strcmp(subtag.data(), "Sequence") == 0 && member->isString()) { StringResource *sr = static_cast(member); rules = sr->fString; @@ -1395,7 +1395,7 @@ parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool n struct UString *tokenValue; struct UString comment; enum ETokenType token; - char subtag[1024], typeKeyword[1024]; + CharString subtag, typeKeyword; uint32_t line; result = table_open(state->bundle, tag, nullptr, status); @@ -1437,7 +1437,8 @@ parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool n return nullptr; } - u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); + subtag.clear(); + subtag.appendInvariantChars(tokenValue->fChars, u_strlen(tokenValue->fChars), *status); if (U_FAILURE(*status)) { @@ -1445,9 +1446,9 @@ parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool n return nullptr; } - if (uprv_strcmp(subtag, "default") == 0) + if (uprv_strcmp(subtag.data(), "default") == 0) { - member = parseResource(state, subtag, nullptr, status); + member = parseResource(state, subtag.data(), nullptr, status); if (U_FAILURE(*status)) { @@ -1466,22 +1467,29 @@ parseCollationElements(ParseState* state, char *tag, uint32_t startline, UBool n if(token == TOK_OPEN_BRACE) { token = getToken(state, &tokenValue, &comment, &line, status); TableResource *collationRes; - if (keepCollationType(subtag)) { - collationRes = table_open(state->bundle, subtag, nullptr, status); + if (keepCollationType(subtag.data())) { + collationRes = table_open(state->bundle, subtag.data(), nullptr, status); } else { collationRes = nullptr; } // need to parse the collation data regardless - collationRes = addCollation(state, collationRes, subtag, startline, status); + collationRes = addCollation(state, collationRes, subtag.data(), startline, status); if (collationRes != nullptr) { result->add(collationRes, startline, *status); } } else if(token == TOK_COLON) { /* right now, we'll just try to see if we have aliases */ /* we could have a table too */ token = peekToken(state, 1, &tokenValue, &line, &comment, status); - u_UCharsToChars(tokenValue->fChars, typeKeyword, u_strlen(tokenValue->fChars) + 1); - if(uprv_strcmp(typeKeyword, "alias") == 0) { - member = parseResource(state, subtag, nullptr, status); + typeKeyword.clear(); + typeKeyword.appendInvariantChars(tokenValue->fChars, u_strlen(tokenValue->fChars), *status); + if (U_FAILURE(*status)) + { + res_close(result); + return nullptr; + } + + if(uprv_strcmp(typeKeyword.data(), "alias") == 0) { + member = parseResource(state, subtag.data(), nullptr, status); if (U_FAILURE(*status)) { res_close(result); @@ -1523,7 +1531,7 @@ realParseTable(ParseState* state, TableResource *table, char *tag, uint32_t star struct UString *tokenValue=nullptr; struct UString comment; enum ETokenType token; - char subtag[1024]; + CharString subtag; uint32_t line; UBool readToken = false; @@ -1562,7 +1570,8 @@ realParseTable(ParseState* state, TableResource *table, char *tag, uint32_t star } if(uprv_isInvariantUString(tokenValue->fChars, -1)) { - u_UCharsToChars(tokenValue->fChars, subtag, u_strlen(tokenValue->fChars) + 1); + subtag.clear(); + subtag.appendInvariantChars(tokenValue->fChars, u_strlen(tokenValue->fChars), *status); } else { *status = U_INVALID_FORMAT_ERROR; error(line, "invariant characters required for table keys"); @@ -1575,7 +1584,7 @@ realParseTable(ParseState* state, TableResource *table, char *tag, uint32_t star return nullptr; } - member = parseResource(state, subtag, &comment, status); + member = parseResource(state, subtag.data(), &comment, status); if (member == nullptr || U_FAILURE(*status)) { diff --git a/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp b/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp index 987a6bc7f6f8f1..2856873f977400 100644 --- a/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp +++ b/deps/icu-small/source/tools/icuexportdata/icuexportdata.cpp @@ -47,68 +47,88 @@ int16_t DATAEXPORT_SCRIPT_X_WITH_INHERITED = 0x0800; int16_t DATAEXPORT_SCRIPT_X_WITH_OTHER = 0x0c00; // TODO(ICU-21821): Replace this with a call to a library function +// This is an array of all code points with explicit scx values, and can be generated the quick and dirty +// way with this script: +// +// # = 0); - uint32_t maxValue = u_getIntPropertyMaxValue(UCHAR_GENERAL_CATEGORY); + int32_t maxValue = u_getIntPropertyMaxValue(UCHAR_GENERAL_CATEGORY); U_ASSERT(maxValue >= 0); fprintf(f, "values = [\n"); - for (uint32_t v = minValue; v <= maxValue; v++) { + for (int32_t v = minValue; v <= maxValue; v++) { dumpValueEntry(uproperty, U_MASK(v), true, f); // We want to dump these masks "in order", which means they @@ -489,12 +509,29 @@ FILE* prepareOutputFile(const char* basename) { #if !UCONFIG_NO_NORMALIZATION -struct PendingDescriptor { +class PendingDescriptor { +public: UChar32 scalar; - uint32_t descriptor; + uint32_t descriptorOrFlags; + // If false, we use the above fields only. If true, descriptor only + // contains the two highest-bit flags and the rest is computed later + // from the fields below. + UBool complex; UBool supplementary; + UBool onlyNonStartersInTrail; + uint32_t len; + uint32_t offset; + + PendingDescriptor(UChar32 scalar, uint32_t descriptor); + PendingDescriptor(UChar32 scalar, uint32_t flags, UBool supplementary, UBool onlyNonStartersInTrail, uint32_t len, uint32_t offset); }; +PendingDescriptor::PendingDescriptor(UChar32 scalar, uint32_t descriptor) + : scalar(scalar), descriptorOrFlags(descriptor), complex(false), supplementary(false), onlyNonStartersInTrail(false), len(0), offset(0) {} + +PendingDescriptor::PendingDescriptor(UChar32 scalar, uint32_t flags, UBool supplementary, UBool onlyNonStartersInTrail, uint32_t len, uint32_t offset) + : scalar(scalar), descriptorOrFlags(flags), complex(true), supplementary(supplementary), onlyNonStartersInTrail(onlyNonStartersInTrail), len(len), offset(offset) {} + void writeCanonicalCompositions(USet* backwardCombiningStarters) { IcuToolErrorCode status("icuexportdata: computeCanonicalCompositions"); const char* basename = "compositions"; @@ -557,21 +594,18 @@ void writeDecompositionTables(const char* basename, const uint16_t* ptr16, size_ fclose(f); } -void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t baseSize32, uint32_t supplementSize16, USet* uset, USet* reference, const std::vector& pendingTrieInsertions, char16_t passthroughCap) { - IcuToolErrorCode status("icuexportdata: writeDecompositionData"); - FILE* f = prepareOutputFile(basename); - - // Zero is a magic number that means the character decomposes to itself. - LocalUMutableCPTriePointer builder(umutablecptrie_open(0, 0, status)); - +void pendingInsertionsToTrie(const char* basename, UMutableCPTrie* trie, const std::vector& pendingTrieInsertions, uint32_t baseSize16, uint32_t baseSize32, uint32_t supplementSize16) { + IcuToolErrorCode status("icuexportdata: pendingInsertionsToTrie"); // Iterate backwards to insert lower code points in the trie first in case it matters // for trie block allocation. for (int32_t i = pendingTrieInsertions.size() - 1; i >= 0; --i) { const PendingDescriptor& pending = pendingTrieInsertions[i]; - uint32_t additional = 0; - if (!(pending.descriptor & 0xFFFC0000)) { - uint32_t offset = pending.descriptor & 0xFFF; + if (pending.complex) { + uint32_t additional = 0; + uint32_t offset = pending.offset; + uint32_t len = pending.len; if (!pending.supplementary) { + len -= 2; if (offset >= baseSize16) { // This is a offset to supplementary 16-bit data. We have // 16-bit base data and 32-bit base data before. However, @@ -579,6 +613,7 @@ void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t additional = baseSize32; } } else { + len -= 1; if (offset >= baseSize32) { // This is an offset to supplementary 32-bit data. We have 16-bit // base data, 32-bit base data, and 16-bit supplementary data before. @@ -591,21 +626,55 @@ void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t additional = baseSize16; } } + // +1 to make offset always non-zero + offset += 1; if (offset + additional > 0xFFF) { status.set(U_INTERNAL_PROGRAM_ERROR); handleError(status, __LINE__, basename); } + if (len > 7) { + status.set(U_INTERNAL_PROGRAM_ERROR); + handleError(status, __LINE__, basename); + } + umutablecptrie_set(trie, pending.scalar, pending.descriptorOrFlags | (uint32_t(pending.onlyNonStartersInTrail) << 4) | len | (offset + additional) << 16, status); + } else { + umutablecptrie_set(trie, pending.scalar, pending.descriptorOrFlags, status); } - // It turns out it's better to swap the halves compared to the initial - // idea in order to put special marker values close to zero so that - // an important marker value becomes 1, so it's efficient to compare - // "1 or 0". Unfortunately, going through all the code to swap - // things is too error prone, so let's do the swapping here in one - // place. - uint32_t oldTrieValue = pending.descriptor + additional; - uint32_t swappedTrieValue = (oldTrieValue >> 16) | (oldTrieValue << 16); - umutablecptrie_set(builder.getAlias(), pending.scalar, swappedTrieValue, status); } +} + +/// Marker that the decomposition does not round trip via NFC. +const uint32_t NON_ROUND_TRIP_MASK = (1 << 30); + +/// Marker that the first character of the decomposition can combine +/// backwards. +const uint32_t BACKWARD_COMBINING_MASK = (1 << 31); + +void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t baseSize32, uint32_t supplementSize16, USet* uset, USet* reference, const std::vector& pendingTrieInsertions, const std::vector& nfdPendingTrieInsertions, char16_t passthroughCap) { + IcuToolErrorCode status("icuexportdata: writeDecompositionData"); + FILE* f = prepareOutputFile(basename); + + // Zero is a magic number that means the character decomposes to itself. + LocalUMutableCPTriePointer builder(umutablecptrie_open(0, 0, status)); + + if (uprv_strcmp(basename, "uts46d") != 0) { + // Make surrogates decompose to U+FFFD. Don't do this for UTS 46, since this + // optimization is only used by the UTF-16 slice mode, and UTS 46 is not + // supported in slice modes (which do not support ignorables). + // Mark these as potentially backward-combining, to make lead surrogates + // for non-BMP characters that are backward-combining count as + // backward-combining just in case, though the backward-combiningness + // is not actually being looked at today. + umutablecptrie_setRange(builder.getAlias(), 0xD800, 0xDFFF, NON_ROUND_TRIP_MASK | BACKWARD_COMBINING_MASK | 0xFFFD, status); + } + + // Add a marker value for Hangul syllables + umutablecptrie_setRange(builder.getAlias(), 0xAC00, 0xD7A3, 1, status); + + // First put the NFD data in the trie, to be partially overwritten in the NFKD and UTS 46 cases. + // This is easier that changing the logic that computes the pending insertions. + pendingInsertionsToTrie(basename, builder.getAlias(), nfdPendingTrieInsertions, baseSize16, baseSize32, supplementSize16); + pendingInsertionsToTrie(basename, builder.getAlias(), pendingTrieInsertions, baseSize16, baseSize32, supplementSize16); LocalUCPTriePointer utrie(umutablecptrie_buildImmutable( builder.getAlias(), trieType, @@ -613,6 +682,7 @@ void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t status)); handleError(status, __LINE__, basename); + // The ICU4X side has changed enough this whole block of expectation checking might be more appropriate to remove. if (reference) { if (uset_contains(reference, 0xFF9E) || uset_contains(reference, 0xFF9F) || !uset_contains(reference, 0x0345)) { // NFD expectations don't hold. The set must not contain the half-width @@ -628,13 +698,9 @@ void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t USet* iotaSubscript = uset_openEmpty(); uset_add(iotaSubscript, 0x0345); - uint8_t flags = 0; - USet* halfWidthCheck = uset_cloneAsThawed(uset); uset_removeAll(halfWidthCheck, reference); - if (uset_equals(halfWidthCheck, halfWidthVoicing)) { - flags |= 1; - } else if (!uset_isEmpty(halfWidthCheck)) { + if (!uset_equals(halfWidthCheck, halfWidthVoicing) && !uset_isEmpty(halfWidthCheck)) { // The result was neither empty nor contained exactly // the two half-width voicing marks. The ICU4X // normalizer doesn't know how to deal with this case. @@ -655,72 +721,14 @@ void writeDecompositionData(const char* basename, uint32_t baseSize16, uint32_t uset_close(iotaSubscript); uset_close(halfWidthVoicing); - - fprintf(f, "flags = 0x%X\n", flags); - fprintf(f, "cap = 0x%X\n", passthroughCap); } + fprintf(f, "cap = 0x%X\n", passthroughCap); fprintf(f, "[trie]\n"); usrc_writeUCPTrie(f, "trie", utrie.getAlias(), UPRV_TARGET_SYNTAX_TOML); fclose(f); handleError(status, __LINE__, basename); } -// Special marker for the NFKD form of U+FDFA -const int32_t FDFA_MARKER = 3; - -// Special marker for characters whose decomposition starts with a non-starter -// and the decomposition isn't the character itself. -const int32_t SPECIAL_NON_STARTER_DECOMPOSITION_MARKER = 2; - -// Special marker for starters that decompose to themselves but that may -// combine backwards under canonical composition -const int32_t BACKWARD_COMBINING_STARTER_MARKER = 1; - -/// Marker that a complex decomposition isn't round-trippable -/// under re-composition. -/// -/// TODO: When taking a data format break, swap this around with -/// `BACKWARD_COMBINING_STARTER_DECOMPOSITION_MARKER`. -const uint32_t NON_ROUND_TRIP_MARKER = 1; - -/// Marker that a complex decomposition starts with a starter -/// that can combine backwards. -/// -/// TODO: When taking a data format break, swap this around with -/// `NON_ROUND_TRIP_MARKER` to use the same bit as with characters -/// that decompose to self but can combine backwards. -const uint32_t BACKWARD_COMBINING_STARTER_DECOMPOSITION_MARKER = 2; - -UBool permissibleBmpPair(UBool knownToRoundTrip, UChar32 c, UChar32 second) { - if (knownToRoundTrip) { - return true; - } - // Nuktas, Hebrew presentation forms and polytonic Greek with oxia - // are special-cased in ICU4X. - if (c >= 0xFB1D && c <= 0xFB4E) { - // Hebrew presentation forms - return true; - } - if (c >= 0x1F71 && c <= 0x1FFB) { - // Polytonic Greek with oxia - return true; - } - if ((second & 0x7F) == 0x3C && second >= 0x0900 && second <= 0x0BFF) { - // Nukta - return true; - } - // To avoid more branchiness, 4 characters that decompose to - // a BMP starter followed by a BMP non-starter are excluded - // from being encoded directly into the trie value and are - // handled as complex decompositions instead. These are: - // U+0F76 TIBETAN VOWEL SIGN VOCALIC R - // U+0F78 TIBETAN VOWEL SIGN VOCALIC L - // U+212B ANGSTROM SIGN - // U+2ADC FORKING - return false; -} - - // Find the slice `needle` within `storage` and return its index, failing which, // append all elements of `needle` to `storage` and return the index of it at the end. template @@ -749,6 +757,8 @@ size_t findOrAppend(std::vector& storage, const UChar32* needle, size_t needl // Computes data for canonical decompositions +// See components/normalizer/trie-value-format.md in the ICU4X repo +// for documentation of the trie value format. void computeDecompositions(const char* basename, const USet* backwardCombiningStarters, std::vector& storage16, @@ -814,12 +824,23 @@ void computeDecompositions(const char* basename, // Surrogate continue; } + if (c == 0xFFFD) { + // REPLACEMENT CHARACTER + // This character is a starter that decomposes to self, + // so without a special case here it would end up as + // passthrough-eligible in all normalizations forms. + // However, in the potentially-ill-formed UTF-8 case + // UTF-8 errors return U+FFFD from the iterator, and + // errors need to be treated as ineligible for + // passthrough on the slice fast path. By giving + // U+FFFD a trie value whose flags make it ineligible + // for passthrough avoids a specific U+FFFD branch on + // the passthrough fast path. + pendingTrieInsertions.push_back({c, NON_ROUND_TRIP_MASK | BACKWARD_COMBINING_MASK}); + continue; + } UnicodeString src; UnicodeString dst; - // True if we're building non-NFD or we're building NFD but - // the `c` round trips to NFC. - // False if we're building NFD and `c` does not round trip to NFC. - UBool nonNfdOrRoundTrips = true; src.append(c); if (mainNormalizer != nfdNormalizer) { UnicodeString inter; @@ -827,39 +848,12 @@ void computeDecompositions(const char* basename, nfdNormalizer->normalize(inter, dst, status); } else { nfdNormalizer->normalize(src, dst, status); - UnicodeString nfc; - nfcNormalizer->normalize(dst, nfc, status); - nonNfdOrRoundTrips = (src == nfc); - } - if (uts46) { - // Work around https://unicode-org.atlassian.net/browse/ICU-22658 - // TODO: Remove the workaround after data corresponding to - // https://www.unicode.org/L2/L2024/24061.htm#179-C36 lands - // for Unicode 16. - switch (c) { - case 0x2F868: - dst.truncate(0); - dst.append(static_cast(0x36FC)); - break; - case 0x2F874: - dst.truncate(0); - dst.append(static_cast(0x5F53)); - break; - case 0x2F91F: - dst.truncate(0); - dst.append(static_cast(0x243AB)); - break; - case 0x2F95F: - dst.truncate(0); - dst.append(static_cast(0x7AEE)); - break; - case 0x2F9BF: - dst.truncate(0); - dst.append(static_cast(0x45D7)); - break; - } } + UnicodeString nfc; + nfcNormalizer->normalize(dst, nfc, status); + UBool roundTripsViaCanonicalComposition = (src == nfc); + int32_t len = dst.toUTF32(utf32, DECOMPOSITION_BUFFER_SIZE, status); if (!len || (len == 1 && utf32[0] == 0xFFFD && c != 0xFFFD)) { @@ -880,7 +874,7 @@ void computeDecompositions(const char* basename, compositionPassthroughBound = c; uset_add(decompositionStartsWithNonStarter, c); if (src != dst) { - if (c == 0x0340 || c == 0x0341 || c == 0x0343 || c == 0x0344 || c == 0x0F73 || c == 0x0F75 || c == 0x0F81 || c == 0xFF9E || c == 0xFF9F) { + if (c == 0x0340 || c == 0x0341 || c == 0x0343 || c == 0x0344 || c == 0x0F73 || c == 0x0F75 || c == 0x0F81 || (c == 0xFF9E && utf32[0] == 0x3099) || (c == 0xFF9F && utf32[0] == 0x309A)) { specialNonStarterDecomposition = true; } else { // A character whose decomposition starts with a non-starter and isn't the same as the character itself and isn't already hard-coded into ICU4X. @@ -893,18 +887,6 @@ void computeDecompositions(const char* basename, startsWithBackwardCombiningStarter = true; uset_add(decompositionStartsWithBackwardCombiningStarter, c); } - if (c != BACKWARD_COMBINING_STARTER_MARKER && len == 1 && utf32[0] == BACKWARD_COMBINING_STARTER_MARKER) { - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } - if (c != SPECIAL_NON_STARTER_DECOMPOSITION_MARKER && len == 1 && utf32[0] == SPECIAL_NON_STARTER_DECOMPOSITION_MARKER) { - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } - if (c != FDFA_MARKER && len == 1 && utf32[0] == FDFA_MARKER) { - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } if (mainNormalizer != nfdNormalizer) { UnicodeString nfd; nfdNormalizer->normalize(src, nfd, status); @@ -913,24 +895,29 @@ void computeDecompositions(const char* basename, } decompositionPassthroughBound = c; compositionPassthroughBound = c; - } else if (firstCombiningClass) { + } + if (firstCombiningClass) { len = 1; if (specialNonStarterDecomposition) { - utf32[0] = SPECIAL_NON_STARTER_DECOMPOSITION_MARKER; // magic value + // Special marker + pendingTrieInsertions.push_back({c, NON_ROUND_TRIP_MASK | BACKWARD_COMBINING_MASK | 0xD900 | u_getCombiningClass(c)}); } else { // Use the surrogate range to store the canonical combining class - utf32[0] = 0xD800 | static_cast(firstCombiningClass); + // XXX: Should non-started that decompose to self be marked as non-round-trippable in + // case such semantics turn out to be more useful for `NON_ROUND_TRIP_MASK`? + pendingTrieInsertions.push_back({c, BACKWARD_COMBINING_MASK | 0xD800 | static_cast(firstCombiningClass)}); } + continue; } else { if (src == dst) { if (startsWithBackwardCombiningStarter) { - pendingTrieInsertions.push_back({c, BACKWARD_COMBINING_STARTER_MARKER << 16, false}); + pendingTrieInsertions.push_back({c, BACKWARD_COMBINING_MASK}); } continue; } decompositionPassthroughBound = c; // ICU4X hard-codes ANGSTROM SIGN - if (c != 0x212B) { + if (c != 0x212B && mainNormalizer == nfdNormalizer) { UnicodeString raw; if (!nfdNormalizer->getRawDecomposition(c, raw)) { // We're always supposed to have a non-recursive decomposition @@ -978,7 +965,7 @@ void computeDecompositions(const char* basename, } } } - if (!nonNfdOrRoundTrips) { + if (!roundTripsViaCanonicalComposition) { compositionPassthroughBound = c; } if (!len) { @@ -986,7 +973,7 @@ void computeDecompositions(const char* basename, status.set(U_INTERNAL_PROGRAM_ERROR); handleError(status, __LINE__, basename); } - pendingTrieInsertions.push_back({c, 0xFFFFFFFF, false}); + pendingTrieInsertions.push_back({c, uint32_t(0xFFFFFFFF)}); } else if (len == 1 && ((utf32[0] >= 0x1161 && utf32[0] <= 0x1175) || (utf32[0] >= 0x11A8 && utf32[0] <= 0x11C2))) { // Singleton decompositions to conjoining jamo. if (mainNormalizer == nfdNormalizer) { @@ -994,16 +981,18 @@ void computeDecompositions(const char* basename, status.set(U_INTERNAL_PROGRAM_ERROR); handleError(status, __LINE__, basename); } - pendingTrieInsertions.push_back({c, static_cast(utf32[0]) << 16, false}); + pendingTrieInsertions.push_back({c, static_cast(utf32[0]) | NON_ROUND_TRIP_MASK | (startsWithBackwardCombiningStarter ? BACKWARD_COMBINING_MASK : 0)}); } else if (!startsWithBackwardCombiningStarter && len == 1 && utf32[0] <= 0xFFFF) { - pendingTrieInsertions.push_back({c, static_cast(utf32[0]) << 16, false}); - } else if (!startsWithBackwardCombiningStarter && + pendingTrieInsertions.push_back({c, static_cast(utf32[0]) | NON_ROUND_TRIP_MASK | (startsWithBackwardCombiningStarter ? BACKWARD_COMBINING_MASK : 0)}); + } else if (c != 0x212B && // ANGSTROM SIGN is special to make the Harfbuzz case branch less in the more common case. + !startsWithBackwardCombiningStarter && len == 2 && - utf32[0] <= 0xFFFF && - utf32[1] <= 0xFFFF && + utf32[0] <= 0x7FFF && + utf32[1] <= 0x7FFF && + utf32[0] > 0x1F && + utf32[1] > 0x1F && !u_getCombiningClass(utf32[0]) && - u_getCombiningClass(utf32[1]) && - permissibleBmpPair(nonNfdOrRoundTrips, c, utf32[1])) { + u_getCombiningClass(utf32[1])) { for (int32_t i = 0; i < len; ++i) { if (((utf32[i] == 0x0345) && (uprv_strcmp(basename, "uts46d") == 0)) || utf32[i] == 0xFF9E || utf32[i] == 0xFF9F) { // Assert that iota subscript and half-width voicing marks never occur in these @@ -1012,7 +1001,7 @@ void computeDecompositions(const char* basename, handleError(status, __LINE__, basename); } } - pendingTrieInsertions.push_back({c, (static_cast(utf32[0]) << 16) | static_cast(utf32[1]), false}); + pendingTrieInsertions.push_back({c, static_cast(utf32[0]) | (static_cast(utf32[1]) << 15) | (roundTripsViaCanonicalComposition ? 0 : NON_ROUND_TRIP_MASK)}); } else { UBool supplementary = false; UBool nonInitialStarter = false; @@ -1046,73 +1035,38 @@ void computeDecompositions(const char* basename, if (len > LONGEST_ENCODABLE_LENGTH_16 || !len || len == 1) { if (len == 18 && c == 0xFDFA) { // Special marker for the one character whose decomposition - // is too long. - pendingTrieInsertions.push_back({c, FDFA_MARKER << 16, supplementary}); + // is too long. (Too long even if we took the fourth bit into use!) + pendingTrieInsertions.push_back({c, NON_ROUND_TRIP_MASK | 1}); continue; } else { + // Note: There's a fourth bit available, but let's error out + // if it's ever needed so that it doesn't get used without + // updating docs. status.set(U_INTERNAL_PROGRAM_ERROR); handleError(status, __LINE__, basename); } } } else if (len > LONGEST_ENCODABLE_LENGTH_32 || !len) { + // Note: There's a fourth bit available, but let's error out + // if it's ever needed so that it doesn't get used without + // updating docs. status.set(U_INTERNAL_PROGRAM_ERROR); handleError(status, __LINE__, basename); } - // Complex decomposition - // Format for 16-bit value: - // 15..13: length minus two for 16-bit case and length minus one for - // the 32-bit case. Length 8 needs to fit in three bits in - // the 16-bit case, and this way the value is future-proofed - // up to 9 in the 16-bit case. Zero is unused and length one - // in the 16-bit case goes directly into the trie. - // 12: 1 if all trailing characters are guaranteed non-starters, - // 0 if no guarantees about non-starterness. - // Note: The bit choice is this way around to allow for - // dynamically falling back to not having this but instead - // having one more bit for length by merely choosing - // different masks. - // 11..0: Start offset in storage. The offset is to the logical - // sequence of scalars16, scalars32, supplementary_scalars16, - // supplementary_scalars32. - uint32_t descriptor = static_cast(!nonInitialStarter) << 12; - if (!supplementary) { - descriptor |= (static_cast(len) - 2) << 13; - } else { - descriptor |= (static_cast(len) - 1) << 13; - } - if (descriptor & 0xFFF) { - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } + size_t index = 0; if (!supplementary) { index = findOrAppend(storage16, utf32, len); } else { index = findOrAppend(storage32, utf32, len); } - if (index > 0xFFF) { - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } - descriptor |= static_cast(index); - if (!descriptor || descriptor > 0xFFFF) { - // > 0xFFFF should never happen if the code above is correct. - // == 0 should not happen due to the nature of the data. - status.set(U_INTERNAL_PROGRAM_ERROR); - handleError(status, __LINE__, basename); - } - uint32_t nonRoundTripMarker = 0; - if (!nonNfdOrRoundTrips) { - nonRoundTripMarker = (NON_ROUND_TRIP_MARKER << 16); - } - uint32_t canCombineBackwardsMarker = 0; - if (startsWithBackwardCombiningStarter) { - canCombineBackwardsMarker = (BACKWARD_COMBINING_STARTER_DECOMPOSITION_MARKER << 16); - } - pendingTrieInsertions.push_back({c, descriptor | nonRoundTripMarker | canCombineBackwardsMarker, supplementary}); + pendingTrieInsertions.push_back({c, (startsWithBackwardCombiningStarter ? BACKWARD_COMBINING_MASK : 0) | (roundTripsViaCanonicalComposition ? 0 : NON_ROUND_TRIP_MASK), supplementary, !nonInitialStarter, uint32_t(len), uint32_t(index)}); } } if (storage16.size() + storage32.size() > 0xFFF) { + // We actually have 14 bits available, but let's error out so + // that docs can be updated when taking a reserved bit out of + // potential future flag usage. status.set(U_INTERNAL_PROGRAM_ERROR); } if (f) { @@ -1489,9 +1443,9 @@ int exportNorm() { uint32_t supplementSize16 = storage16.size() - baseSize16; uint32_t supplementSize32 = storage32.size() - baseSize32; - writeDecompositionData("nfd", baseSize16, baseSize32, supplementSize16, nfdDecompositionStartsWithNonStarter, nullptr, nfdPendingTrieInsertions, static_cast(nfcBound)); - writeDecompositionData("nfkd", baseSize16, baseSize32, supplementSize16, nfkdDecompositionStartsWithNonStarter, nfdDecompositionStartsWithNonStarter, nfkdPendingTrieInsertions, static_cast(nfkcBound)); - writeDecompositionData("uts46d", baseSize16, baseSize32, supplementSize16, uts46DecompositionStartsWithNonStarter, nfdDecompositionStartsWithNonStarter, uts46PendingTrieInsertions, static_cast(uts46Bound)); + writeDecompositionData("nfd", baseSize16, baseSize32, supplementSize16, nfdDecompositionStartsWithNonStarter, nullptr, nfdPendingTrieInsertions, nfdPendingTrieInsertions, static_cast(nfcBound)); + writeDecompositionData("nfkd", baseSize16, baseSize32, supplementSize16, nfkdDecompositionStartsWithNonStarter, nfdDecompositionStartsWithNonStarter, nfkdPendingTrieInsertions, nfdPendingTrieInsertions, static_cast(nfkcBound)); + writeDecompositionData("uts46d", baseSize16, baseSize32, supplementSize16, uts46DecompositionStartsWithNonStarter, nfdDecompositionStartsWithNonStarter, uts46PendingTrieInsertions, nfdPendingTrieInsertions, static_cast(uts46Bound)); writeDecompositionTables("nfdex", storage16.data(), baseSize16, storage32.data(), baseSize32); writeDecompositionTables("nfkdex", storage16.data() + baseSize16, supplementSize16, storage32.data() + baseSize32, supplementSize32); diff --git a/deps/icu-small/source/tools/toolutil/ucm.cpp b/deps/icu-small/source/tools/toolutil/ucm.cpp index 923041a53f607b..824362a6939205 100644 --- a/deps/icu-small/source/tools/toolutil/ucm.cpp +++ b/deps/icu-small/source/tools/toolutil/ucm.cpp @@ -310,7 +310,7 @@ enum { static uint8_t checkBaseExtUnicode(UCMStates *baseStates, UCMTable *base, UCMTable *ext, - UBool moveToExt, UBool intersectBase) { + UBool moveToExt, int8_t intersectBase) { (void)baseStates; UCMapping *mb, *me, *mbLimit, *meLimit; @@ -416,7 +416,7 @@ checkBaseExtUnicode(UCMStates *baseStates, UCMTable *base, UCMTable *ext, static uint8_t checkBaseExtBytes(UCMStates *baseStates, UCMTable *base, UCMTable *ext, - UBool moveToExt, UBool intersectBase) { + UBool moveToExt, int8_t intersectBase) { UCMapping *mb, *me; int32_t *baseMap, *extMap; int32_t b, e, bLimit, eLimit, cmp; @@ -556,7 +556,7 @@ ucm_checkValidity(UCMTable *table, UCMStates *baseStates) { U_CAPI UBool U_EXPORT2 ucm_checkBaseExt(UCMStates *baseStates, UCMTable *base, UCMTable *ext, UCMTable *moveTarget, - UBool intersectBase) { + int8_t intersectBase) { uint8_t result; /* if we have an extension table, we must always use precision flags */ @@ -735,7 +735,7 @@ ucm_separateMappings(UCMFile *ucm, UBool isSISO) { } if(needsMove) { ucm_moveMappings(ucm->base, ucm->ext); - return ucm_checkBaseExt(&ucm->states, ucm->base, ucm->ext, ucm->ext, false); + return ucm_checkBaseExt(&ucm->states, ucm->base, ucm->ext, ucm->ext, 0); } else { ucm_sortTable(ucm->base); return true; diff --git a/deps/icu-small/source/tools/toolutil/ucm.h b/deps/icu-small/source/tools/toolutil/ucm.h index 8ea90604d475ed..8f78b52e9687cd 100644 --- a/deps/icu-small/source/tools/toolutil/ucm.h +++ b/deps/icu-small/source/tools/toolutil/ucm.h @@ -227,7 +227,7 @@ ucm_checkValidity(UCMTable *ext, UCMStates *baseStates); */ U_CAPI UBool U_EXPORT2 ucm_checkBaseExt(UCMStates *baseStates, UCMTable *base, UCMTable *ext, - UCMTable *moveTarget, UBool intersectBase); + UCMTable *moveTarget, int8_t intersectBase); U_CAPI void U_EXPORT2 ucm_printTable(UCMTable *table, FILE *f, UBool byUnicode); diff --git a/tools/icu/current_ver.dep b/tools/icu/current_ver.dep index a2c73ee25ae6c4..da52397ed40e5d 100644 --- a/tools/icu/current_ver.dep +++ b/tools/icu/current_ver.dep @@ -1,6 +1,6 @@ [ { - "url": "https://github.com/unicode-org/icu/releases/download/release-76-1/icu4c-76_1-src.tgz", - "md5": "857fdafff8127139cc175a3ec9b43bd6" + "url": "https://github.com/unicode-org/icu/releases/download/release-77-1/icu4c-77_1-src.tgz", + "md5": "bc0132b4c43db8455d2446c3bae58898" } ]