U_CAPI int32_t U_EXPORT2 unorm_compare(const UChar *s1, int32_t length1, const UChar *s2, int32_t length2, uint32_t options, UErrorCode *pErrorCode) { UChar fcd1[300], fcd2[300]; UChar *d1, *d2; const UnicodeSet *nx; UNormalizationMode mode; int32_t normOptions; int32_t result; /* argument checking */ if(pErrorCode==0 || U_FAILURE(*pErrorCode)) { return 0; } if(s1==0 || length1<-1 || s2==0 || length2<-1) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } if(!unorm_haveData(pErrorCode)) { return 0; } if(!uprv_haveProperties(pErrorCode)) { return 0; } normOptions=(int32_t)(options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT); nx=unorm_getNX(normOptions, pErrorCode); if(U_FAILURE(*pErrorCode)) { return 0; } d1=d2=0; options|=_COMPARE_EQUIV; result=0; /* * UAX #21 Case Mappings, as fixed for Unicode version 4 * (see Jitterbug 2021), defines a canonical caseless match as * * A string X is a canonical caseless match * for a string Y if and only if * NFD(toCasefold(NFD(X))) = NFD(toCasefold(NFD(Y))) * * For better performance, we check for FCD (or let the caller tell us that * both strings are in FCD) for the inner normalization. * BasicNormalizerTest::FindFoldFCDExceptions() makes sure that * case-folding preserves the FCD-ness of a string. * The outer normalization is then only performed by unorm_cmpEquivFold() * when there is a difference. * * Exception: When using the Turkic case-folding option, we do perform * full NFD first. This is because in the Turkic case precomposed characters * with 0049 capital I or 0069 small i fold differently whether they * are first decomposed or not, so an FCD check - a check only for * canonical order - is not sufficient. */ if(options&U_FOLD_CASE_EXCLUDE_SPECIAL_I) { mode=UNORM_NFD; options&=~UNORM_INPUT_IS_FCD; } else { mode=UNORM_FCD; } if(!(options&UNORM_INPUT_IS_FCD)) { int32_t _len1, _len2; UBool isFCD1, isFCD2; // check if s1 and/or s2 fulfill the FCD conditions isFCD1= UNORM_YES==unorm_internalQuickCheck(s1, length1, mode, TRUE, nx, pErrorCode); isFCD2= UNORM_YES==unorm_internalQuickCheck(s2, length2, mode, TRUE, nx, pErrorCode); if(U_FAILURE(*pErrorCode)) { return 0; } /* * ICU 2.4 had a further optimization: * If both strings were not in FCD, then they were both NFD'ed, * and the _COMPARE_EQUIV option was turned off. * It is not entirely clear that this is valid with the current * definition of the canonical caseless match. * Therefore, ICU 2.6 removes that optimization. */ if(!isFCD1) { _len1=unorm_internalNormalizeWithNX(fcd1, LENGTHOF(fcd1), s1, length1, mode, normOptions, nx, pErrorCode); if(*pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { s1=fcd1; } else { d1=(UChar *)uprv_malloc(_len1*U_SIZEOF_UCHAR); if(d1==0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; goto cleanup; } *pErrorCode=U_ZERO_ERROR; _len1=unorm_internalNormalizeWithNX(d1, _len1, s1, length1, mode, normOptions, nx, pErrorCode); if(U_FAILURE(*pErrorCode)) { goto cleanup; } s1=d1; } length1=_len1; } if(!isFCD2) { _len2=unorm_internalNormalizeWithNX(fcd2, LENGTHOF(fcd2), s2, length2, mode, normOptions, nx, pErrorCode); if(*pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { s2=fcd2; } else { d2=(UChar *)uprv_malloc(_len2*U_SIZEOF_UCHAR); if(d2==0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; goto cleanup; } *pErrorCode=U_ZERO_ERROR; _len2=unorm_internalNormalizeWithNX(d2, _len2, s2, length2, mode, normOptions, nx, pErrorCode); if(U_FAILURE(*pErrorCode)) { goto cleanup; } s2=d2; } length2=_len2; } } if(U_SUCCESS(*pErrorCode)) { result=unorm_cmpEquivFold(s1, length1, s2, length2, options, pErrorCode); } cleanup: if(d1!=0) { uprv_free(d1); } if(d2!=0) { uprv_free(d2); } return result; }
U_CAPI int32_t U_EXPORT2 unorm_compare(const UChar *s1, int32_t length1, const UChar *s2, int32_t length2, uint32_t options, UErrorCode *pErrorCode) { /* argument checking */ if(U_FAILURE(*pErrorCode)) { return 0; } if(s1==0 || length1<-1 || s2==0 || length2<-1) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return 0; } UnicodeString fcd1, fcd2; int32_t normOptions=(int32_t)(options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT); options|=_COMPARE_EQUIV; /* * UAX #21 Case Mappings, as fixed for Unicode version 4 * (see Jitterbug 2021), defines a canonical caseless match as * * A string X is a canonical caseless match * for a string Y if and only if * NFD(toCasefold(NFD(X))) = NFD(toCasefold(NFD(Y))) * * For better performance, we check for FCD (or let the caller tell us that * both strings are in FCD) for the inner normalization. * BasicNormalizerTest::FindFoldFCDExceptions() makes sure that * case-folding preserves the FCD-ness of a string. * The outer normalization is then only performed by unorm_cmpEquivFold() * when there is a difference. * * Exception: When using the Turkic case-folding option, we do perform * full NFD first. This is because in the Turkic case precomposed characters * with 0049 capital I or 0069 small i fold differently whether they * are first decomposed or not, so an FCD check - a check only for * canonical order - is not sufficient. */ if(!(options&UNORM_INPUT_IS_FCD) || (options&U_FOLD_CASE_EXCLUDE_SPECIAL_I)) { const Normalizer2 *n2; if(options&U_FOLD_CASE_EXCLUDE_SPECIAL_I) { n2=Normalizer2Factory::getNFDInstance(*pErrorCode); } else { n2=Normalizer2Factory::getFCDInstance(*pErrorCode); } if (U_FAILURE(*pErrorCode)) { return 0; } // check if s1 and/or s2 fulfill the FCD conditions const UnicodeSet *uni32; if(normOptions&UNORM_UNICODE_3_2) { uni32=uniset_getUnicode32Instance(*pErrorCode); } else { uni32=NULL; // unused } FilteredNormalizer2 fn2(*n2, *uni32); if(normOptions&UNORM_UNICODE_3_2) { n2=&fn2; } UnicodeString str1(length1<0, s1, length1); UnicodeString str2(length2<0, s2, length2); int32_t spanQCYes1=n2->spanQuickCheckYes(str1, *pErrorCode); int32_t spanQCYes2=n2->spanQuickCheckYes(str2, *pErrorCode); if(U_FAILURE(*pErrorCode)) { return 0; } /* * ICU 2.4 had a further optimization: * If both strings were not in FCD, then they were both NFD'ed, * and the _COMPARE_EQUIV option was turned off. * It is not entirely clear that this is valid with the current * definition of the canonical caseless match. * Therefore, ICU 2.6 removes that optimization. */ if(spanQCYes1<str1.length()) { UnicodeString unnormalized=str1.tempSubString(spanQCYes1); fcd1.setTo(FALSE, str1.getBuffer(), spanQCYes1); n2->normalizeSecondAndAppend(fcd1, unnormalized, *pErrorCode); s1=fcd1.getBuffer(); length1=fcd1.length(); } if(spanQCYes2<str2.length()) { UnicodeString unnormalized=str2.tempSubString(spanQCYes2); fcd2.setTo(FALSE, str2.getBuffer(), spanQCYes2); n2->normalizeSecondAndAppend(fcd2, unnormalized, *pErrorCode); s2=fcd2.getBuffer(); length2=fcd2.length(); } } if(U_SUCCESS(*pErrorCode)) { return unorm_cmpEquivFold(s1, length1, s2, length2, options, pErrorCode); } else { return 0; } }