+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
*
*
*******************************************************************************
* file name: ustrcase.cpp
-* encoding: US-ASCII
+* encoding: UTF-8
* tab size: 8 (not used)
* indentation:4
*
#include "unicode/utypes.h"
#include "unicode/brkiter.h"
+#include "unicode/casemap.h"
+#include "unicode/edits.h"
+#include "unicode/stringoptions.h"
#include "unicode/ustring.h"
#include "unicode/ucasemap.h"
#include "unicode/ubrk.h"
#include "unicode/utf16.h"
#include "cmemory.h"
#include "ucase.h"
+#include "ucasemap_imp.h"
#include "ustr_imp.h"
#include "uassert.h"
-U_NAMESPACE_USE
+U_NAMESPACE_BEGIN
-/* string casing ------------------------------------------------------------ */
+namespace {
+
+int32_t checkOverflowAndEditsError(int32_t destIndex, int32_t destCapacity,
+ Edits *edits, UErrorCode &errorCode) {
+ if (U_SUCCESS(errorCode)) {
+ if (destIndex > destCapacity) {
+ errorCode = U_BUFFER_OVERFLOW_ERROR;
+ } else if (edits != NULL) {
+ edits->copyErrorTo(errorCode);
+ }
+ }
+ return destIndex;
+}
/* Appends a full case mapping result, see UCASE_MAX_STRING_LENGTH. */
-static inline int32_t
+inline int32_t
appendResult(UChar *dest, int32_t destIndex, int32_t destCapacity,
- int32_t result, const UChar *s) {
+ int32_t result, const UChar *s,
+ int32_t cpLength, uint32_t options, icu::Edits *edits) {
UChar32 c;
int32_t length;
/* decode the result */
if(result<0) {
/* (not) original code point */
+ if(edits!=NULL) {
+ edits->addUnchanged(cpLength);
+ }
+ if(options & U_OMIT_UNCHANGED_TEXT) {
+ return destIndex;
+ }
c=~result;
- length=-1;
- } else if(result<=UCASE_MAX_STRING_LENGTH) {
- c=U_SENTINEL;
- length=result;
+ if(destIndex<destCapacity && c<=0xffff) { // BMP slightly-fastpath
+ dest[destIndex++]=(UChar)c;
+ return destIndex;
+ }
+ length=cpLength;
} else {
- c=result;
- length=-1;
+ if(result<=UCASE_MAX_STRING_LENGTH) {
+ c=U_SENTINEL;
+ length=result;
+ } else if(destIndex<destCapacity && result<=0xffff) { // BMP slightly-fastpath
+ dest[destIndex++]=(UChar)result;
+ if(edits!=NULL) {
+ edits->addReplace(cpLength, 1);
+ }
+ return destIndex;
+ } else {
+ c=result;
+ length=U16_LENGTH(c);
+ }
+ if(edits!=NULL) {
+ edits->addReplace(cpLength, length);
+ }
+ }
+ if(length>(INT32_MAX-destIndex)) {
+ return -1; // integer overflow
}
if(destIndex<destCapacity) {
/* append the result */
- if(length<0) {
+ if(c>=0) {
/* code point */
UBool isError=FALSE;
U16_APPEND(dest, destIndex, destCapacity, c, isError);
if(isError) {
/* overflow, nothing written */
- destIndex+=U16_LENGTH(c);
+ destIndex+=length;
}
} else {
/* string */
}
} else {
/* preflight */
- if(length<0) {
- destIndex+=U16_LENGTH(c);
- } else {
- destIndex+=length;
- }
+ destIndex+=length;
}
return destIndex;
}
-static UChar32 U_CALLCONV
+inline int32_t
+appendUChar(UChar *dest, int32_t destIndex, int32_t destCapacity, UChar c) {
+ if(destIndex<destCapacity) {
+ dest[destIndex]=c;
+ } else if(destIndex==INT32_MAX) {
+ return -1; // integer overflow
+ }
+ return destIndex+1;
+}
+
+int32_t
+appendNonEmptyUnchanged(UChar *dest, int32_t destIndex, int32_t destCapacity,
+ const UChar *s, int32_t length, uint32_t options, icu::Edits *edits) {
+ if(edits!=NULL) {
+ edits->addUnchanged(length);
+ }
+ if(options & U_OMIT_UNCHANGED_TEXT) {
+ return destIndex;
+ }
+ if(length>(INT32_MAX-destIndex)) {
+ return -1; // integer overflow
+ }
+ if((destIndex+length)<=destCapacity) {
+ u_memcpy(dest+destIndex, s, length);
+ }
+ return destIndex + length;
+}
+
+inline int32_t
+appendUnchanged(UChar *dest, int32_t destIndex, int32_t destCapacity,
+ const UChar *s, int32_t length, uint32_t options, icu::Edits *edits) {
+ if (length <= 0) {
+ return destIndex;
+ }
+ return appendNonEmptyUnchanged(dest, destIndex, destCapacity, s, length, options, edits);
+}
+
+UChar32 U_CALLCONV
utf16_caseContextIterator(void *context, int8_t dir) {
UCaseContext *csc=(UCaseContext *)context;
UChar32 c;
return U_SENTINEL;
}
-/*
- * Case-maps [srcStart..srcLimit[ but takes
- * context [0..srcLength[ into account.
+/**
+ * caseLocale >= 0: Lowercases [srcStart..srcLimit[ but takes context [0..srcLength[ into account.
+ * caseLocale < 0: Case-folds [srcStart..srcLimit[.
*/
-static int32_t
-_caseMap(const UCaseMap *csm, UCaseMapFull *map,
- UChar *dest, int32_t destCapacity,
- const UChar *src, UCaseContext *csc,
- int32_t srcStart, int32_t srcLimit,
- UErrorCode *pErrorCode) {
- const UChar *s;
- UChar32 c, c2 = 0;
- int32_t srcIndex, destIndex;
- int32_t locCache;
-
- locCache=csm->locCache;
-
- /* case mapping loop */
- srcIndex=srcStart;
- destIndex=0;
- while(srcIndex<srcLimit) {
- csc->cpStart=srcIndex;
- U16_NEXT(src, srcIndex, srcLimit, c);
- csc->cpLimit=srcIndex;
- c=map(csm->csp, c, utf16_caseContextIterator, csc, &s, csm->locale, &locCache);
- if((destIndex<destCapacity) && (c<0 ? (c2=~c)<=0xffff : UCASE_MAX_STRING_LENGTH<c && (c2=c)<=0xffff)) {
- /* fast path version of appendResult() for BMP results */
- dest[destIndex++]=(UChar)c2;
+int32_t toLower(int32_t caseLocale, uint32_t options,
+ UChar *dest, int32_t destCapacity,
+ const UChar *src, UCaseContext *csc, int32_t srcStart, int32_t srcLimit,
+ icu::Edits *edits, UErrorCode &errorCode) {
+ const int8_t *latinToLower;
+ if (caseLocale == UCASE_LOC_ROOT ||
+ (caseLocale >= 0 ?
+ !(caseLocale == UCASE_LOC_TURKISH || caseLocale == UCASE_LOC_LITHUANIAN) :
+ (options & _FOLD_CASE_OPTIONS_MASK) == U_FOLD_CASE_DEFAULT)) {
+ latinToLower = LatinCase::TO_LOWER_NORMAL;
+ } else {
+ latinToLower = LatinCase::TO_LOWER_TR_LT;
+ }
+ const UTrie2 *trie = ucase_getTrie();
+ int32_t destIndex = 0;
+ int32_t prev = srcStart;
+ int32_t srcIndex = srcStart;
+ for (;;) {
+ // fast path for simple cases
+ UChar lead = 0;
+ while (srcIndex < srcLimit) {
+ lead = src[srcIndex];
+ int32_t delta;
+ if (lead < LatinCase::LONG_S) {
+ int8_t d = latinToLower[lead];
+ if (d == LatinCase::EXC) { break; }
+ ++srcIndex;
+ if (d == 0) { continue; }
+ delta = d;
+ } else if (lead >= 0xd800) {
+ break; // surrogate or higher
+ } else {
+ uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead);
+ if (UCASE_HAS_EXCEPTION(props)) { break; }
+ ++srcIndex;
+ if (!UCASE_IS_UPPER_OR_TITLE(props) || (delta = UCASE_GET_DELTA(props)) == 0) {
+ continue;
+ }
+ }
+ lead += static_cast<UChar>(delta);
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, srcIndex - 1 - prev, options, edits);
+ if (destIndex >= 0) {
+ destIndex = appendUChar(dest, destIndex, destCapacity, lead);
+ if (edits != nullptr) {
+ edits->addReplace(1, 1);
+ }
+ }
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ prev = srcIndex;
+ }
+ if (srcIndex >= srcLimit) {
+ break;
+ }
+ // slow path
+ int32_t cpStart = srcIndex++;
+ UChar trail;
+ UChar32 c;
+ if (U16_IS_LEAD(lead) && srcIndex < srcLimit && U16_IS_TRAIL(trail = src[srcIndex])) {
+ c = U16_GET_SUPPLEMENTARY(lead, trail);
+ ++srcIndex;
} else {
- destIndex=appendResult(dest, destIndex, destCapacity, c, s);
+ c = lead;
+ }
+ const UChar *s;
+ if (caseLocale >= 0) {
+ csc->cpStart = cpStart;
+ csc->cpLimit = srcIndex;
+ c = ucase_toFullLower(c, utf16_caseContextIterator, csc, &s, caseLocale);
+ } else {
+ c = ucase_toFullFolding(c, &s, options);
+ }
+ if (c >= 0) {
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, cpStart - prev, options, edits);
+ if (destIndex >= 0) {
+ destIndex = appendResult(dest, destIndex, destCapacity, c, s,
+ srcIndex - cpStart, options, edits);
+ }
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ prev = srcIndex;
}
}
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, srcIndex - prev, options, edits);
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ return destIndex;
+}
- if(destIndex>destCapacity) {
- *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
+int32_t toUpper(int32_t caseLocale, uint32_t options,
+ UChar *dest, int32_t destCapacity,
+ const UChar *src, UCaseContext *csc, int32_t srcLength,
+ icu::Edits *edits, UErrorCode &errorCode) {
+ const int8_t *latinToUpper;
+ if (caseLocale == UCASE_LOC_TURKISH) {
+ latinToUpper = LatinCase::TO_UPPER_TR;
+ } else {
+ latinToUpper = LatinCase::TO_UPPER_NORMAL;
+ }
+ const UTrie2 *trie = ucase_getTrie();
+ int32_t destIndex = 0;
+ int32_t prev = 0;
+ int32_t srcIndex = 0;
+ for (;;) {
+ // fast path for simple cases
+ UChar lead = 0;
+ while (srcIndex < srcLength) {
+ lead = src[srcIndex];
+ int32_t delta;
+ if (lead < LatinCase::LONG_S) {
+ int8_t d = latinToUpper[lead];
+ if (d == LatinCase::EXC) { break; }
+ ++srcIndex;
+ if (d == 0) { continue; }
+ delta = d;
+ } else if (lead >= 0xd800) {
+ break; // surrogate or higher
+ } else {
+ uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead);
+ if (UCASE_HAS_EXCEPTION(props)) { break; }
+ ++srcIndex;
+ if (UCASE_GET_TYPE(props) != UCASE_LOWER || (delta = UCASE_GET_DELTA(props)) == 0) {
+ continue;
+ }
+ }
+ lead += static_cast<UChar>(delta);
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, srcIndex - 1 - prev, options, edits);
+ if (destIndex >= 0) {
+ destIndex = appendUChar(dest, destIndex, destCapacity, lead);
+ if (edits != nullptr) {
+ edits->addReplace(1, 1);
+ }
+ }
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ prev = srcIndex;
+ }
+ if (srcIndex >= srcLength) {
+ break;
+ }
+ // slow path
+ int32_t cpStart;
+ csc->cpStart = cpStart = srcIndex++;
+ UChar trail;
+ UChar32 c;
+ if (U16_IS_LEAD(lead) && srcIndex < srcLength && U16_IS_TRAIL(trail = src[srcIndex])) {
+ c = U16_GET_SUPPLEMENTARY(lead, trail);
+ ++srcIndex;
+ } else {
+ c = lead;
+ }
+ csc->cpLimit = srcIndex;
+ const UChar *s;
+ c = ucase_toFullUpper(c, utf16_caseContextIterator, csc, &s, caseLocale);
+ if (c >= 0) {
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, cpStart - prev, options, edits);
+ if (destIndex >= 0) {
+ destIndex = appendResult(dest, destIndex, destCapacity, c, s,
+ srcIndex - cpStart, options, edits);
+ }
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ prev = srcIndex;
+ }
+ }
+ destIndex = appendUnchanged(dest, destIndex, destCapacity,
+ src + prev, srcIndex - prev, options, edits);
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
}
return destIndex;
}
+} // namespace
+
+U_NAMESPACE_END
+
+U_NAMESPACE_USE
+
#if !UCONFIG_NO_BREAK_ITERATION
U_CFUNC int32_t U_CALLCONV
-ustrcase_internalToTitle(const UCaseMap *csm,
+ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *iter,
UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
- UErrorCode *pErrorCode) {
- const UChar *s;
- UChar32 c;
- int32_t prev, titleStart, titleLimit, idx, destIndex, length;
- UBool isFirstIndex;
-
- if(U_FAILURE(*pErrorCode)) {
+ icu::Edits *edits,
+ UErrorCode &errorCode) {
+ if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) {
return 0;
}
- // Use the C++ abstract base class to minimize dependencies.
- // TODO: Change UCaseMap.iter to store a BreakIterator directly.
- BreakIterator *bi=reinterpret_cast<BreakIterator *>(csm->iter);
-
/* set up local variables */
- int32_t locCache=csm->locCache;
UCaseContext csc=UCASECONTEXT_INITIALIZER;
csc.p=(void *)src;
csc.limit=srcLength;
- destIndex=0;
- prev=0;
- isFirstIndex=TRUE;
+ int32_t destIndex=0;
+ int32_t prev=0;
+ UBool isFirstIndex=TRUE;
/* titlecasing loop */
while(prev<srcLength) {
/* find next index where to titlecase */
+ int32_t index;
if(isFirstIndex) {
isFirstIndex=FALSE;
- idx=bi->first();
+ index=iter->first();
} else {
- idx=bi->next();
+ index=iter->next();
}
- if(idx==UBRK_DONE || idx>srcLength) {
- idx=srcLength;
+ if(index==UBRK_DONE || index>srcLength) {
+ index=srcLength;
}
/*
- * Unicode 4 & 5 section 3.13 Default Case Operations:
- *
- * R3 toTitlecase(X): Find the word boundaries based on Unicode Standard Annex
- * #29, "Text Boundaries." Between each pair of word boundaries, find the first
- * cased character F. If F exists, map F to default_title(F); then map each
- * subsequent character C to default_lower(C).
- *
- * In this implementation, segment [prev..index[ into 3 parts:
- * a) uncased characters (copy as-is) [prev..titleStart[
- * b) first case letter (titlecase) [titleStart..titleLimit[
+ * Segment [prev..index[ into 3 parts:
+ * a) skipped characters (copy as-is) [prev..titleStart[
+ * b) first letter (titlecase) [titleStart..titleLimit[
* c) subsequent characters (lowercase) [titleLimit..index[
*/
- if(prev<idx) {
- /* find and copy uncased characters [prev..titleStart[ */
- titleStart=titleLimit=prev;
- U16_NEXT(src, titleLimit, idx, c);
- if((csm->options&U_TITLECASE_NO_BREAK_ADJUSTMENT)==0 && UCASE_NONE==ucase_getType(csm->csp, c)) {
- /* Adjust the titlecasing index (titleStart) to the next cased character. */
- for(;;) {
+ if(prev<index) {
+ // Find and copy skipped characters [prev..titleStart[
+ int32_t titleStart=prev;
+ int32_t titleLimit=prev;
+ UChar32 c;
+ U16_NEXT(src, titleLimit, index, c);
+ if ((options&U_TITLECASE_NO_BREAK_ADJUSTMENT)==0) {
+ // Adjust the titlecasing index to the next cased character,
+ // or to the next letter/number/symbol/private use.
+ // Stop with titleStart<titleLimit<=index
+ // if there is a character to be titlecased,
+ // or else stop with titleStart==titleLimit==index.
+ UBool toCased = (options&U_TITLECASE_ADJUST_TO_CASED) != 0;
+ while (toCased ? UCASE_NONE==ucase_getType(c) : !ustrcase_isLNS(c)) {
titleStart=titleLimit;
- if(titleLimit==idx) {
- /*
- * only uncased characters in [prev..index[
- * stop with titleStart==titleLimit==index
- */
+ if(titleLimit==index) {
break;
}
- U16_NEXT(src, titleLimit, idx, c);
- if(UCASE_NONE!=ucase_getType(csm->csp, c)) {
- break; /* cased letter at [titleStart..titleLimit[ */
- }
+ U16_NEXT(src, titleLimit, index, c);
}
- length=titleStart-prev;
- if(length>0) {
- if((destIndex+length)<=destCapacity) {
- uprv_memcpy(dest+destIndex, src+prev, length*U_SIZEOF_UCHAR);
+ if (prev < titleStart) {
+ destIndex=appendUnchanged(dest, destIndex, destCapacity,
+ src+prev, titleStart-prev, options, edits);
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
}
- destIndex+=length;
}
}
/* titlecase c which is from [titleStart..titleLimit[ */
csc.cpStart=titleStart;
csc.cpLimit=titleLimit;
- c=ucase_toFullTitle(csm->csp, c, utf16_caseContextIterator, &csc, &s, csm->locale, &locCache);
- destIndex=appendResult(dest, destIndex, destCapacity, c, s);
+ const UChar *s;
+ c=ucase_toFullTitle(c, utf16_caseContextIterator, &csc, &s, caseLocale);
+ destIndex=appendResult(dest, destIndex, destCapacity, c, s,
+ titleLimit-titleStart, options, edits);
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
/* Special case Dutch IJ titlecasing */
- if ( titleStart+1 < idx &&
- ucase_getCaseLocale(csm->locale,&locCache) == UCASE_LOC_DUTCH &&
- ( src[titleStart] == (UChar32) 0x0049 || src[titleStart] == (UChar32) 0x0069 ) &&
- ( src[titleStart+1] == (UChar32) 0x004A || src[titleStart+1] == (UChar32) 0x006A )) {
- c=(UChar32) 0x004A;
- destIndex=appendResult(dest, destIndex, destCapacity, c, s);
- titleLimit++;
+ if (titleStart+1 < index &&
+ caseLocale == UCASE_LOC_DUTCH &&
+ (src[titleStart] == 0x0049 || src[titleStart] == 0x0069)) {
+ if (src[titleStart+1] == 0x006A) {
+ destIndex=appendUChar(dest, destIndex, destCapacity, 0x004A);
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ if(edits!=NULL) {
+ edits->addReplace(1, 1);
+ }
+ titleLimit++;
+ } else if (src[titleStart+1] == 0x004A) {
+ // Keep the capital J from getting lowercased.
+ destIndex=appendUnchanged(dest, destIndex, destCapacity,
+ src+titleStart+1, 1, options, edits);
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ titleLimit++;
+ }
}
/* lowercase [titleLimit..index[ */
- if(titleLimit<idx) {
- if((csm->options&U_TITLECASE_NO_LOWERCASE)==0) {
+ if(titleLimit<index) {
+ if((options&U_TITLECASE_NO_LOWERCASE)==0) {
/* Normal operation: Lowercase the rest of the word. */
destIndex+=
- _caseMap(
- csm, ucase_toFullLower,
+ toLower(
+ caseLocale, options,
dest+destIndex, destCapacity-destIndex,
- src, &csc,
- titleLimit, idx,
- pErrorCode);
+ src, &csc, titleLimit, index,
+ edits, errorCode);
+ if(errorCode==U_BUFFER_OVERFLOW_ERROR) {
+ errorCode=U_ZERO_ERROR;
+ }
+ if(U_FAILURE(errorCode)) {
+ return destIndex;
+ }
} else {
/* Optionally just copy the rest of the word unchanged. */
- length=idx-titleLimit;
- if((destIndex+length)<=destCapacity) {
- uprv_memcpy(dest+destIndex, src+titleLimit, length*U_SIZEOF_UCHAR);
+ destIndex=appendUnchanged(dest, destIndex, destCapacity,
+ src+titleLimit, index-titleLimit, options, edits);
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
}
- destIndex+=length;
}
}
}
}
- prev=idx;
+ prev=index;
}
- if(destIndex>destCapacity) {
- *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
+ return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode);
+}
+
+#endif // !UCONFIG_NO_BREAK_ITERATION
+
+U_NAMESPACE_BEGIN
+namespace GreekUpper {
+
+// Data generated by prototype code, see
+// http://site.icu-project.org/design/case/greek-upper
+// TODO: Move this data into ucase.icu.
+static const uint16_t data0370[] = {
+ // U+0370..03FF
+ 0x0370,
+ 0x0370,
+ 0x0372,
+ 0x0372,
+ 0,
+ 0,
+ 0x0376,
+ 0x0376,
+ 0,
+ 0,
+ 0x037A,
+ 0x03FD,
+ 0x03FE,
+ 0x03FF,
+ 0,
+ 0x037F,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x0391 | HAS_VOWEL,
+ 0x0392,
+ 0x0393,
+ 0x0394,
+ 0x0395 | HAS_VOWEL,
+ 0x0396,
+ 0x0397 | HAS_VOWEL,
+ 0x0398,
+ 0x0399 | HAS_VOWEL,
+ 0x039A,
+ 0x039B,
+ 0x039C,
+ 0x039D,
+ 0x039E,
+ 0x039F | HAS_VOWEL,
+ 0x03A0,
+ 0x03A1,
+ 0,
+ 0x03A3,
+ 0x03A4,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A6,
+ 0x03A7,
+ 0x03A8,
+ 0x03A9 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_DIALYTIKA,
+ 0x03A5 | HAS_VOWEL | HAS_DIALYTIKA,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x0391 | HAS_VOWEL,
+ 0x0392,
+ 0x0393,
+ 0x0394,
+ 0x0395 | HAS_VOWEL,
+ 0x0396,
+ 0x0397 | HAS_VOWEL,
+ 0x0398,
+ 0x0399 | HAS_VOWEL,
+ 0x039A,
+ 0x039B,
+ 0x039C,
+ 0x039D,
+ 0x039E,
+ 0x039F | HAS_VOWEL,
+ 0x03A0,
+ 0x03A1,
+ 0x03A3,
+ 0x03A3,
+ 0x03A4,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A6,
+ 0x03A7,
+ 0x03A8,
+ 0x03A9 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_DIALYTIKA,
+ 0x03A5 | HAS_VOWEL | HAS_DIALYTIKA,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03CF,
+ 0x0392,
+ 0x0398,
+ 0x03D2,
+ 0x03D2 | HAS_ACCENT,
+ 0x03D2 | HAS_DIALYTIKA,
+ 0x03A6,
+ 0x03A0,
+ 0x03CF,
+ 0x03D8,
+ 0x03D8,
+ 0x03DA,
+ 0x03DA,
+ 0x03DC,
+ 0x03DC,
+ 0x03DE,
+ 0x03DE,
+ 0x03E0,
+ 0x03E0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x039A,
+ 0x03A1,
+ 0x03F9,
+ 0x037F,
+ 0x03F4,
+ 0x0395 | HAS_VOWEL,
+ 0,
+ 0x03F7,
+ 0x03F7,
+ 0x03F9,
+ 0x03FA,
+ 0x03FA,
+ 0x03FC,
+ 0x03FD,
+ 0x03FE,
+ 0x03FF,
+};
+
+static const uint16_t data1F00[] = {
+ // U+1F00..1FFF
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL,
+ 0x0395 | HAS_VOWEL,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0x0395 | HAS_VOWEL,
+ 0x0395 | HAS_VOWEL,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0x0397 | HAS_VOWEL,
+ 0x0397 | HAS_VOWEL,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL,
+ 0x0397 | HAS_VOWEL,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL,
+ 0x039F | HAS_VOWEL,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0x039F | HAS_VOWEL,
+ 0x039F | HAS_VOWEL,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x03A5 | HAS_VOWEL,
+ 0,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL,
+ 0x03A9 | HAS_VOWEL,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL,
+ 0x03A9 | HAS_VOWEL,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_ACCENT,
+ 0x0391 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0,
+ 0x0399 | HAS_VOWEL,
+ 0,
+ 0,
+ 0,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0395 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_ACCENT,
+ 0x0397 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0,
+ 0,
+ 0,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0,
+ 0,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0x0399 | HAS_VOWEL | HAS_ACCENT,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x03A1,
+ 0x03A1,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT | HAS_DIALYTIKA,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A5 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x039F | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_ACCENT,
+ 0x03A9 | HAS_VOWEL | HAS_YPOGEGRAMMENI,
+ 0,
+ 0,
+ 0,
+};
+
+// U+2126 Ohm sign
+static const uint16_t data2126 = 0x03A9 | HAS_VOWEL;
+
+uint32_t getLetterData(UChar32 c) {
+ if (c < 0x370 || 0x2126 < c || (0x3ff < c && c < 0x1f00)) {
+ return 0;
+ } else if (c <= 0x3ff) {
+ return data0370[c - 0x370];
+ } else if (c <= 0x1fff) {
+ return data1F00[c - 0x1f00];
+ } else if (c == 0x2126) {
+ return data2126;
+ } else {
+ return 0;
}
+}
+
+uint32_t getDiacriticData(UChar32 c) {
+ switch (c) {
+ case 0x0300: // varia
+ case 0x0301: // tonos = oxia
+ case 0x0342: // perispomeni
+ case 0x0302: // circumflex can look like perispomeni
+ case 0x0303: // tilde can look like perispomeni
+ case 0x0311: // inverted breve can look like perispomeni
+ return HAS_ACCENT;
+ case 0x0308: // dialytika = diaeresis
+ return HAS_COMBINING_DIALYTIKA;
+ case 0x0344: // dialytika tonos
+ return HAS_COMBINING_DIALYTIKA | HAS_ACCENT;
+ case 0x0345: // ypogegrammeni = iota subscript
+ return HAS_YPOGEGRAMMENI;
+ case 0x0304: // macron
+ case 0x0306: // breve
+ case 0x0313: // comma above
+ case 0x0314: // reversed comma above
+ case 0x0343: // koronis
+ return HAS_OTHER_GREEK_DIACRITIC;
+ default:
+ return 0;
+ }
+}
+
+UBool isFollowedByCasedLetter(const UChar *s, int32_t i, int32_t length) {
+ while (i < length) {
+ UChar32 c;
+ U16_NEXT(s, i, length, c);
+ int32_t type = ucase_getTypeOrIgnorable(c);
+ if ((type & UCASE_IGNORABLE) != 0) {
+ // Case-ignorable, continue with the loop.
+ } else if (type != UCASE_NONE) {
+ return TRUE; // Followed by cased letter.
+ } else {
+ return FALSE; // Uncased and not case-ignorable.
+ }
+ }
+ return FALSE; // Not followed by cased letter.
+}
+
+/**
+ * Greek string uppercasing with a state machine.
+ * Probably simpler than a stateless function that has to figure out complex context-before
+ * for each character.
+ * TODO: Try to re-consolidate one way or another with the non-Greek function.
+ */
+int32_t toUpper(uint32_t options,
+ UChar *dest, int32_t destCapacity,
+ const UChar *src, int32_t srcLength,
+ Edits *edits,
+ UErrorCode &errorCode) {
+ int32_t destIndex=0;
+ uint32_t state = 0;
+ for (int32_t i = 0; i < srcLength;) {
+ int32_t nextIndex = i;
+ UChar32 c;
+ U16_NEXT(src, nextIndex, srcLength, c);
+ uint32_t nextState = 0;
+ int32_t type = ucase_getTypeOrIgnorable(c);
+ if ((type & UCASE_IGNORABLE) != 0) {
+ // c is case-ignorable
+ nextState |= (state & AFTER_CASED);
+ } else if (type != UCASE_NONE) {
+ // c is cased
+ nextState |= AFTER_CASED;
+ }
+ uint32_t data = getLetterData(c);
+ if (data > 0) {
+ uint32_t upper = data & UPPER_MASK;
+ // Add a dialytika to this iota or ypsilon vowel
+ // if we removed a tonos from the previous vowel,
+ // and that previous vowel did not also have (or gain) a dialytika.
+ // Adding one only to the final vowel in a longer sequence
+ // (which does not occur in normal writing) would require lookahead.
+ // Set the same flag as for preserving an existing dialytika.
+ if ((data & HAS_VOWEL) != 0 && (state & AFTER_VOWEL_WITH_ACCENT) != 0 &&
+ (upper == 0x399 || upper == 0x3A5)) {
+ data |= HAS_DIALYTIKA;
+ }
+ int32_t numYpogegrammeni = 0; // Map each one to a trailing, spacing, capital iota.
+ if ((data & HAS_YPOGEGRAMMENI) != 0) {
+ numYpogegrammeni = 1;
+ }
+ // Skip combining diacritics after this Greek letter.
+ while (nextIndex < srcLength) {
+ uint32_t diacriticData = getDiacriticData(src[nextIndex]);
+ if (diacriticData != 0) {
+ data |= diacriticData;
+ if ((diacriticData & HAS_YPOGEGRAMMENI) != 0) {
+ ++numYpogegrammeni;
+ }
+ ++nextIndex;
+ } else {
+ break; // not a Greek diacritic
+ }
+ }
+ if ((data & HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA) == HAS_VOWEL_AND_ACCENT) {
+ nextState |= AFTER_VOWEL_WITH_ACCENT;
+ }
+ // Map according to Greek rules.
+ UBool addTonos = FALSE;
+ if (upper == 0x397 &&
+ (data & HAS_ACCENT) != 0 &&
+ numYpogegrammeni == 0 &&
+ (state & AFTER_CASED) == 0 &&
+ !isFollowedByCasedLetter(src, nextIndex, srcLength)) {
+ // Keep disjunctive "or" with (only) a tonos.
+ // We use the same "word boundary" conditions as for the Final_Sigma test.
+ if (i == nextIndex) {
+ upper = 0x389; // Preserve the precomposed form.
+ } else {
+ addTonos = TRUE;
+ }
+ } else if ((data & HAS_DIALYTIKA) != 0) {
+ // Preserve a vowel with dialytika in precomposed form if it exists.
+ if (upper == 0x399) {
+ upper = 0x3AA;
+ data &= ~HAS_EITHER_DIALYTIKA;
+ } else if (upper == 0x3A5) {
+ upper = 0x3AB;
+ data &= ~HAS_EITHER_DIALYTIKA;
+ }
+ }
+
+ UBool change;
+ if (edits == nullptr && (options & U_OMIT_UNCHANGED_TEXT) == 0) {
+ change = TRUE; // common, simple usage
+ } else {
+ // Find out first whether we are changing the text.
+ change = src[i] != upper || numYpogegrammeni > 0;
+ int32_t i2 = i + 1;
+ if ((data & HAS_EITHER_DIALYTIKA) != 0) {
+ change |= i2 >= nextIndex || src[i2] != 0x308;
+ ++i2;
+ }
+ if (addTonos) {
+ change |= i2 >= nextIndex || src[i2] != 0x301;
+ ++i2;
+ }
+ int32_t oldLength = nextIndex - i;
+ int32_t newLength = (i2 - i) + numYpogegrammeni;
+ change |= oldLength != newLength;
+ if (change) {
+ if (edits != NULL) {
+ edits->addReplace(oldLength, newLength);
+ }
+ } else {
+ if (edits != NULL) {
+ edits->addUnchanged(oldLength);
+ }
+ // Write unchanged text?
+ change = (options & U_OMIT_UNCHANGED_TEXT) == 0;
+ }
+ }
+
+ if (change) {
+ destIndex=appendUChar(dest, destIndex, destCapacity, (UChar)upper);
+ if (destIndex >= 0 && (data & HAS_EITHER_DIALYTIKA) != 0) {
+ destIndex=appendUChar(dest, destIndex, destCapacity, 0x308); // restore or add a dialytika
+ }
+ if (destIndex >= 0 && addTonos) {
+ destIndex=appendUChar(dest, destIndex, destCapacity, 0x301);
+ }
+ while (destIndex >= 0 && numYpogegrammeni > 0) {
+ destIndex=appendUChar(dest, destIndex, destCapacity, 0x399);
+ --numYpogegrammeni;
+ }
+ if(destIndex<0) {
+ errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ }
+ } else {
+ const UChar *s;
+ c=ucase_toFullUpper(c, NULL, NULL, &s, UCASE_LOC_GREEK);
+ destIndex = appendResult(dest, destIndex, destCapacity, c, s,
+ nextIndex - i, options, edits);
+ if (destIndex < 0) {
+ errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
+ return 0;
+ }
+ }
+ i = nextIndex;
+ state = nextState;
+ }
+
return destIndex;
}
-#endif // !UCONFIG_NO_BREAK_ITERATION
+} // namespace GreekUpper
+U_NAMESPACE_END
/* functions available in the common library (for unistr_case.cpp) */
U_CFUNC int32_t U_CALLCONV
-ustrcase_internalToLower(const UCaseMap *csm,
+ustrcase_internalToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED
UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
- UErrorCode *pErrorCode) {
+ icu::Edits *edits,
+ UErrorCode &errorCode) {
UCaseContext csc=UCASECONTEXT_INITIALIZER;
csc.p=(void *)src;
csc.limit=srcLength;
- return _caseMap(
- csm, ucase_toFullLower,
+ int32_t destIndex = toLower(
+ caseLocale, options,
dest, destCapacity,
src, &csc, 0, srcLength,
- pErrorCode);
+ edits, errorCode);
+ return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode);
}
U_CFUNC int32_t U_CALLCONV
-ustrcase_internalToUpper(const UCaseMap *csm,
+ustrcase_internalToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED
UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
- UErrorCode *pErrorCode) {
- UCaseContext csc=UCASECONTEXT_INITIALIZER;
- csc.p=(void *)src;
- csc.limit=srcLength;
- return _caseMap(
- csm, ucase_toFullUpper,
- dest, destCapacity,
- src, &csc, 0, srcLength,
- pErrorCode);
-}
-
-static int32_t
-ustr_foldCase(const UCaseProps *csp,
- UChar *dest, int32_t destCapacity,
- const UChar *src, int32_t srcLength,
- uint32_t options,
- UErrorCode *pErrorCode) {
- int32_t srcIndex, destIndex;
-
- const UChar *s;
- UChar32 c, c2 = 0;
-
- /* case mapping loop */
- srcIndex=destIndex=0;
- while(srcIndex<srcLength) {
- U16_NEXT(src, srcIndex, srcLength, c);
- c=ucase_toFullFolding(csp, c, &s, options);
- if((destIndex<destCapacity) && (c<0 ? (c2=~c)<=0xffff : UCASE_MAX_STRING_LENGTH<c && (c2=c)<=0xffff)) {
- /* fast path version of appendResult() for BMP results */
- dest[destIndex++]=(UChar)c2;
- } else {
- destIndex=appendResult(dest, destIndex, destCapacity, c, s);
- }
- }
-
- if(destIndex>destCapacity) {
- *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
+ icu::Edits *edits,
+ UErrorCode &errorCode) {
+ int32_t destIndex;
+ if (caseLocale == UCASE_LOC_GREEK) {
+ destIndex = GreekUpper::toUpper(options, dest, destCapacity,
+ src, srcLength, edits, errorCode);
+ } else {
+ UCaseContext csc=UCASECONTEXT_INITIALIZER;
+ csc.p=(void *)src;
+ csc.limit=srcLength;
+ destIndex = toUpper(
+ caseLocale, options,
+ dest, destCapacity,
+ src, &csc, srcLength,
+ edits, errorCode);
}
- return destIndex;
+ return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode);
}
U_CFUNC int32_t U_CALLCONV
-ustrcase_internalFold(const UCaseMap *csm,
+ustrcase_internalFold(int32_t /* caseLocale */, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED
UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
- UErrorCode *pErrorCode) {
- return ustr_foldCase(csm->csp, dest, destCapacity, src, srcLength, csm->options, pErrorCode);
+ icu::Edits *edits,
+ UErrorCode &errorCode) {
+ int32_t destIndex = toLower(
+ -1, options,
+ dest, destCapacity,
+ src, nullptr, 0, srcLength,
+ edits, errorCode);
+ return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode);
}
U_CFUNC int32_t
-ustrcase_map(const UCaseMap *csm,
+ustrcase_map(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM
UChar *dest, int32_t destCapacity,
const UChar *src, int32_t srcLength,
UStringCaseMapper *stringCaseMapper,
- UErrorCode *pErrorCode) {
+ icu::Edits *edits,
+ UErrorCode &errorCode) {
+ int32_t destLength;
+
+ /* check argument values */
+ if(U_FAILURE(errorCode)) {
+ return 0;
+ }
+ if( destCapacity<0 ||
+ (dest==NULL && destCapacity>0) ||
+ src==NULL ||
+ srcLength<-1
+ ) {
+ errorCode=U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ /* get the string length */
+ if(srcLength==-1) {
+ srcLength=u_strlen(src);
+ }
+
+ /* check for overlapping source and destination */
+ if( dest!=NULL &&
+ ((src>=dest && src<(dest+destCapacity)) ||
+ (dest>=src && dest<(src+srcLength)))
+ ) {
+ errorCode=U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) {
+ edits->reset();
+ }
+ destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR
+ dest, destCapacity, src, srcLength, edits, errorCode);
+ return u_terminateUChars(dest, destCapacity, destLength, &errorCode);
+}
+
+U_CFUNC int32_t
+ustrcase_mapWithOverlap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM
+ UChar *dest, int32_t destCapacity,
+ const UChar *src, int32_t srcLength,
+ UStringCaseMapper *stringCaseMapper,
+ UErrorCode &errorCode) {
UChar buffer[300];
UChar *temp;
int32_t destLength;
/* check argument values */
- if(U_FAILURE(*pErrorCode)) {
+ if(U_FAILURE(errorCode)) {
return 0;
}
if( destCapacity<0 ||
src==NULL ||
srcLength<-1
) {
- *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
+ errorCode=U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
/* allocate a buffer */
temp=(UChar *)uprv_malloc(destCapacity*U_SIZEOF_UCHAR);
if(temp==NULL) {
- *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
+ errorCode=U_MEMORY_ALLOCATION_ERROR;
return 0;
}
}
temp=dest;
}
- destLength=stringCaseMapper(csm, temp, destCapacity, src, srcLength, pErrorCode);
+ destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR
+ temp, destCapacity, src, srcLength, NULL, errorCode);
if(temp!=dest) {
/* copy the result string to the destination buffer */
- if(destLength>0) {
- int32_t copyLength= destLength<=destCapacity ? destLength : destCapacity;
- if(copyLength>0) {
- uprv_memmove(dest, temp, copyLength*U_SIZEOF_UCHAR);
- }
+ if (U_SUCCESS(errorCode) && 0 < destLength && destLength <= destCapacity) {
+ u_memmove(dest, temp, destLength);
}
if(temp!=buffer) {
uprv_free(temp);
}
}
- return u_terminateUChars(dest, destCapacity, destLength, pErrorCode);
+ return u_terminateUChars(dest, destCapacity, destLength, &errorCode);
}
/* public API functions */
const UChar *src, int32_t srcLength,
uint32_t options,
UErrorCode *pErrorCode) {
- UCaseMap csm=UCASEMAP_INITIALIZER;
- csm.csp=ucase_getSingleton();
- csm.options=options;
+ return ustrcase_mapWithOverlap(
+ UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL
+ dest, destCapacity,
+ src, srcLength,
+ ustrcase_internalFold, *pErrorCode);
+}
+
+U_NAMESPACE_BEGIN
+
+int32_t CaseMap::fold(
+ uint32_t options,
+ const UChar *src, int32_t srcLength,
+ UChar *dest, int32_t destCapacity, Edits *edits,
+ UErrorCode &errorCode) {
return ustrcase_map(
- &csm,
+ UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL
dest, destCapacity,
src, srcLength,
- ustrcase_internalFold, pErrorCode);
+ ustrcase_internalFold, edits, errorCode);
}
+U_NAMESPACE_END
+
/* case-insensitive string comparisons -------------------------------------- */
/*
UErrorCode *pErrorCode) {
int32_t cmpRes = 0;
- const UCaseProps *csp;
-
/* current-level start/limit - s1/s2 as current */
const UChar *start1, *start2, *limit1, *limit2;
* assume that at least the option U_COMPARE_IGNORE_CASE is set
* otherwise this function would have to behave exactly as uprv_strCompare()
*/
- csp=ucase_getSingleton();
if(U_FAILURE(*pErrorCode)) {
return 0;
}
*/
if( level1==0 &&
- (length=ucase_toFullFolding(csp, (UChar32)cp1, &p, options))>=0
+ (length=ucase_toFullFolding((UChar32)cp1, &p, options))>=0
) {
/* cp1 case-folds to the code point "length" or to p[length] */
if(U_IS_SURROGATE(c1)) {
}
if( level2==0 &&
- (length=ucase_toFullFolding(csp, (UChar32)cp2, &p, options))>=0
+ (length=ucase_toFullFolding((UChar32)cp2, &p, options))>=0
) {
/* cp2 case-folds to the code point "length" or to p[length] */
if(U_IS_SURROGATE(c2)) {
}
if(matchLen1) {
- *matchLen1=m1-org1;
- *matchLen2=m2-org2;
+ *matchLen1=static_cast<int32_t>(m1-org1);
+ *matchLen2=static_cast<int32_t>(m2-org2);
}
return cmpRes;
}