| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ******************************************************************************* |
| * Copyright (C) 1996-2015, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ******************************************************************************* |
| * file name: ucol.cpp |
| * encoding: UTF-8 |
| * tab size: 8 (not used) |
| * indentation:4 |
| * |
| * Modification history |
| * Date Name Comments |
| * 1996-1999 various members of ICU team maintained C API for collation framework |
| * 02/16/2001 synwee Added internal method getPrevSpecialCE |
| * 03/01/2001 synwee Added maxexpansion functionality. |
| * 03/16/2001 weiv Collation framework is rewritten in C and made UCA compliant |
| * 2012-2014 markus Rewritten in C++ again. |
| */ |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_COLLATION |
| |
| #if defined(STARBOARD) |
| #include "starboard/client_porting/poem/assert_poem.h" |
| #include "starboard/client_porting/poem/string_poem.h" |
| #endif // defined(STARBOARD) |
| #include "unicode/coll.h" |
| #include "unicode/tblcoll.h" |
| #include "unicode/bytestream.h" |
| #include "unicode/coleitr.h" |
| #include "unicode/ucoleitr.h" |
| #include "unicode/ustring.h" |
| #include "cmemory.h" |
| #include "collation.h" |
| #include "cstring.h" |
| #include "putilimp.h" |
| #include "uassert.h" |
| #include "utracimp.h" |
| |
| U_NAMESPACE_USE |
| |
| U_CAPI UCollator* U_EXPORT2 |
| ucol_openBinary(const uint8_t *bin, int32_t length, |
| const UCollator *base, |
| UErrorCode *status) |
| { |
| if(U_FAILURE(*status)) { return NULL; } |
| RuleBasedCollator *coll = new RuleBasedCollator( |
| bin, length, |
| RuleBasedCollator::rbcFromUCollator(base), |
| *status); |
| if(coll == NULL) { |
| *status = U_MEMORY_ALLOCATION_ERROR; |
| return NULL; |
| } |
| if(U_FAILURE(*status)) { |
| delete coll; |
| return NULL; |
| } |
| return coll->toUCollator(); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_cloneBinary(const UCollator *coll, |
| uint8_t *buffer, int32_t capacity, |
| UErrorCode *status) |
| { |
| if(U_FAILURE(*status)) { |
| return 0; |
| } |
| const RuleBasedCollator *rbc = RuleBasedCollator::rbcFromUCollator(coll); |
| if(rbc == NULL && coll != NULL) { |
| *status = U_UNSUPPORTED_ERROR; |
| return 0; |
| } |
| return rbc->cloneBinary(buffer, capacity, *status); |
| } |
| |
| U_CAPI UCollator* U_EXPORT2 |
| ucol_safeClone(const UCollator *coll, void * /*stackBuffer*/, int32_t * pBufferSize, UErrorCode *status) |
| { |
| if (status == NULL || U_FAILURE(*status)){ |
| return NULL; |
| } |
| if (coll == NULL) { |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| return NULL; |
| } |
| if (pBufferSize != NULL) { |
| int32_t inputSize = *pBufferSize; |
| *pBufferSize = 1; |
| if (inputSize == 0) { |
| return NULL; // preflighting for deprecated functionality |
| } |
| } |
| Collator *newColl = Collator::fromUCollator(coll)->clone(); |
| if (newColl == NULL) { |
| *status = U_MEMORY_ALLOCATION_ERROR; |
| return nullptr; |
| } else { |
| *status = U_SAFECLONE_ALLOCATED_WARNING; |
| } |
| return newColl->toUCollator(); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_close(UCollator *coll) |
| { |
| UTRACE_ENTRY_OC(UTRACE_UCOL_CLOSE); |
| UTRACE_DATA1(UTRACE_INFO, "coll = %p", coll); |
| if(coll != NULL) { |
| delete Collator::fromUCollator(coll); |
| } |
| UTRACE_EXIT(); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_mergeSortkeys(const uint8_t *src1, int32_t src1Length, |
| const uint8_t *src2, int32_t src2Length, |
| uint8_t *dest, int32_t destCapacity) { |
| /* check arguments */ |
| if( src1==NULL || src1Length<-1 || src1Length==0 || (src1Length>0 && src1[src1Length-1]!=0) || |
| src2==NULL || src2Length<-1 || src2Length==0 || (src2Length>0 && src2[src2Length-1]!=0) || |
| destCapacity<0 || (destCapacity>0 && dest==NULL) |
| ) { |
| /* error, attempt to write a zero byte and return 0 */ |
| if(dest!=NULL && destCapacity>0) { |
| *dest=0; |
| } |
| return 0; |
| } |
| |
| /* check lengths and capacity */ |
| if(src1Length<0) { |
| src1Length=(int32_t)uprv_strlen((const char *)src1)+1; |
| } |
| if(src2Length<0) { |
| src2Length=(int32_t)uprv_strlen((const char *)src2)+1; |
| } |
| |
| int32_t destLength=src1Length+src2Length; |
| if(destLength>destCapacity) { |
| /* the merged sort key does not fit into the destination */ |
| return destLength; |
| } |
| |
| /* merge the sort keys with the same number of levels */ |
| uint8_t *p=dest; |
| for(;;) { |
| /* copy level from src1 not including 00 or 01 */ |
| uint8_t b; |
| while((b=*src1)>=2) { |
| ++src1; |
| *p++=b; |
| } |
| |
| /* add a 02 merge separator */ |
| *p++=2; |
| |
| /* copy level from src2 not including 00 or 01 */ |
| while((b=*src2)>=2) { |
| ++src2; |
| *p++=b; |
| } |
| |
| /* if both sort keys have another level, then add a 01 level separator and continue */ |
| if(*src1==1 && *src2==1) { |
| ++src1; |
| ++src2; |
| *p++=1; |
| } else { |
| break; |
| } |
| } |
| |
| /* |
| * here, at least one sort key is finished now, but the other one |
| * might have some contents left from containing more levels; |
| * that contents is just appended to the result |
| */ |
| if(*src1!=0) { |
| /* src1 is not finished, therefore *src2==0, and src1 is appended */ |
| src2=src1; |
| } |
| /* append src2, "the other, unfinished sort key" */ |
| while((*p++=*src2++)!=0) {} |
| |
| /* the actual length might be less than destLength if either sort key contained illegally embedded zero bytes */ |
| return (int32_t)(p-dest); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getSortKey(const UCollator *coll, |
| const UChar *source, |
| int32_t sourceLength, |
| uint8_t *result, |
| int32_t resultLength) |
| { |
| UTRACE_ENTRY(UTRACE_UCOL_GET_SORTKEY); |
| if (UTRACE_LEVEL(UTRACE_VERBOSE)) { |
| UTRACE_DATA3(UTRACE_VERBOSE, "coll=%p, source string = %vh ", coll, source, |
| ((sourceLength==-1 && source!=NULL) ? u_strlen(source) : sourceLength)); |
| } |
| |
| int32_t keySize = Collator::fromUCollator(coll)-> |
| getSortKey(source, sourceLength, result, resultLength); |
| |
| UTRACE_DATA2(UTRACE_VERBOSE, "Sort Key = %vb", result, keySize); |
| UTRACE_EXIT_VALUE(keySize); |
| return keySize; |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_nextSortKeyPart(const UCollator *coll, |
| UCharIterator *iter, |
| uint32_t state[2], |
| uint8_t *dest, int32_t count, |
| UErrorCode *status) |
| { |
| /* error checking */ |
| if(status==NULL || U_FAILURE(*status)) { |
| return 0; |
| } |
| UTRACE_ENTRY(UTRACE_UCOL_NEXTSORTKEYPART); |
| UTRACE_DATA6(UTRACE_VERBOSE, "coll=%p, iter=%p, state=%d %d, dest=%p, count=%d", |
| coll, iter, state[0], state[1], dest, count); |
| |
| int32_t i = Collator::fromUCollator(coll)-> |
| internalNextSortKeyPart(iter, state, dest, count, *status); |
| |
| // Return number of meaningful sortkey bytes. |
| UTRACE_DATA4(UTRACE_VERBOSE, "dest = %vb, state=%d %d", |
| dest,i, state[0], state[1]); |
| UTRACE_EXIT_VALUE_STATUS(i, *status); |
| return i; |
| } |
| |
| /** |
| * Produce a bound for a given sortkey and a number of levels. |
| */ |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getBound(const uint8_t *source, |
| int32_t sourceLength, |
| UColBoundMode boundType, |
| uint32_t noOfLevels, |
| uint8_t *result, |
| int32_t resultLength, |
| UErrorCode *status) |
| { |
| // consistency checks |
| if(status == NULL || U_FAILURE(*status)) { |
| return 0; |
| } |
| if(source == NULL) { |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| |
| int32_t sourceIndex = 0; |
| // Scan the string until we skip enough of the key OR reach the end of the key |
| do { |
| sourceIndex++; |
| if(source[sourceIndex] == Collation::LEVEL_SEPARATOR_BYTE) { |
| noOfLevels--; |
| } |
| } while (noOfLevels > 0 |
| && (source[sourceIndex] != 0 || sourceIndex < sourceLength)); |
| |
| if((source[sourceIndex] == 0 || sourceIndex == sourceLength) |
| && noOfLevels > 0) { |
| *status = U_SORT_KEY_TOO_SHORT_WARNING; |
| } |
| |
| |
| // READ ME: this code assumes that the values for boundType |
| // enum will not changes. They are set so that the enum value |
| // corresponds to the number of extra bytes each bound type |
| // needs. |
| if(result != NULL && resultLength >= sourceIndex+boundType) { |
| uprv_memcpy(result, source, sourceIndex); |
| switch(boundType) { |
| // Lower bound just gets terminated. No extra bytes |
| case UCOL_BOUND_LOWER: // = 0 |
| break; |
| // Upper bound needs one extra byte |
| case UCOL_BOUND_UPPER: // = 1 |
| result[sourceIndex++] = 2; |
| break; |
| // Upper long bound needs two extra bytes |
| case UCOL_BOUND_UPPER_LONG: // = 2 |
| result[sourceIndex++] = 0xFF; |
| result[sourceIndex++] = 0xFF; |
| break; |
| default: |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| result[sourceIndex++] = 0; |
| |
| return sourceIndex; |
| } else { |
| return sourceIndex+boundType+1; |
| } |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setMaxVariable(UCollator *coll, UColReorderCode group, UErrorCode *pErrorCode) { |
| if(U_FAILURE(*pErrorCode)) { return; } |
| Collator::fromUCollator(coll)->setMaxVariable(group, *pErrorCode); |
| } |
| |
| U_CAPI UColReorderCode U_EXPORT2 |
| ucol_getMaxVariable(const UCollator *coll) { |
| return Collator::fromUCollator(coll)->getMaxVariable(); |
| } |
| |
| U_CAPI uint32_t U_EXPORT2 |
| ucol_setVariableTop(UCollator *coll, const UChar *varTop, int32_t len, UErrorCode *status) { |
| if(U_FAILURE(*status) || coll == NULL) { |
| return 0; |
| } |
| return Collator::fromUCollator(coll)->setVariableTop(varTop, len, *status); |
| } |
| |
| U_CAPI uint32_t U_EXPORT2 ucol_getVariableTop(const UCollator *coll, UErrorCode *status) { |
| if(U_FAILURE(*status) || coll == NULL) { |
| return 0; |
| } |
| return Collator::fromUCollator(coll)->getVariableTop(*status); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_restoreVariableTop(UCollator *coll, const uint32_t varTop, UErrorCode *status) { |
| if(U_FAILURE(*status) || coll == NULL) { |
| return; |
| } |
| Collator::fromUCollator(coll)->setVariableTop(varTop, *status); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setAttribute(UCollator *coll, UColAttribute attr, UColAttributeValue value, UErrorCode *status) { |
| if(U_FAILURE(*status) || coll == NULL) { |
| return; |
| } |
| |
| Collator::fromUCollator(coll)->setAttribute(attr, value, *status); |
| } |
| |
| U_CAPI UColAttributeValue U_EXPORT2 |
| ucol_getAttribute(const UCollator *coll, UColAttribute attr, UErrorCode *status) { |
| if(U_FAILURE(*status) || coll == NULL) { |
| return UCOL_DEFAULT; |
| } |
| |
| return Collator::fromUCollator(coll)->getAttribute(attr, *status); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setStrength( UCollator *coll, |
| UCollationStrength strength) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| ucol_setAttribute(coll, UCOL_STRENGTH, strength, &status); |
| } |
| |
| U_CAPI UCollationStrength U_EXPORT2 |
| ucol_getStrength(const UCollator *coll) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| return ucol_getAttribute(coll, UCOL_STRENGTH, &status); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getReorderCodes(const UCollator *coll, |
| int32_t *dest, |
| int32_t destCapacity, |
| UErrorCode *status) { |
| if (U_FAILURE(*status)) { |
| return 0; |
| } |
| |
| return Collator::fromUCollator(coll)->getReorderCodes(dest, destCapacity, *status); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setReorderCodes(UCollator* coll, |
| const int32_t* reorderCodes, |
| int32_t reorderCodesLength, |
| UErrorCode *status) { |
| if (U_FAILURE(*status)) { |
| return; |
| } |
| |
| Collator::fromUCollator(coll)->setReorderCodes(reorderCodes, reorderCodesLength, *status); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getEquivalentReorderCodes(int32_t reorderCode, |
| int32_t* dest, |
| int32_t destCapacity, |
| UErrorCode *pErrorCode) { |
| return Collator::getEquivalentReorderCodes(reorderCode, dest, destCapacity, *pErrorCode); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_getVersion(const UCollator* coll, |
| UVersionInfo versionInfo) |
| { |
| Collator::fromUCollator(coll)->getVersion(versionInfo); |
| } |
| |
| U_CAPI UCollationResult U_EXPORT2 |
| ucol_strcollIter( const UCollator *coll, |
| UCharIterator *sIter, |
| UCharIterator *tIter, |
| UErrorCode *status) |
| { |
| if(!status || U_FAILURE(*status)) { |
| return UCOL_EQUAL; |
| } |
| |
| UTRACE_ENTRY(UTRACE_UCOL_STRCOLLITER); |
| UTRACE_DATA3(UTRACE_VERBOSE, "coll=%p, sIter=%p, tIter=%p", coll, sIter, tIter); |
| |
| if(sIter == NULL || tIter == NULL || coll == NULL) { |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| UTRACE_EXIT_VALUE_STATUS(UCOL_EQUAL, *status); |
| return UCOL_EQUAL; |
| } |
| |
| UCollationResult result = Collator::fromUCollator(coll)->compare(*sIter, *tIter, *status); |
| |
| UTRACE_EXIT_VALUE_STATUS(result, *status); |
| return result; |
| } |
| |
| |
| /* */ |
| /* ucol_strcoll Main public API string comparison function */ |
| /* */ |
| U_CAPI UCollationResult U_EXPORT2 |
| ucol_strcoll( const UCollator *coll, |
| const UChar *source, |
| int32_t sourceLength, |
| const UChar *target, |
| int32_t targetLength) |
| { |
| UTRACE_ENTRY(UTRACE_UCOL_STRCOLL); |
| if (UTRACE_LEVEL(UTRACE_VERBOSE)) { |
| UTRACE_DATA3(UTRACE_VERBOSE, "coll=%p, source=%p, target=%p", coll, source, target); |
| UTRACE_DATA2(UTRACE_VERBOSE, "source string = %vh ", source, sourceLength); |
| UTRACE_DATA2(UTRACE_VERBOSE, "target string = %vh ", target, targetLength); |
| } |
| |
| UErrorCode status = U_ZERO_ERROR; |
| UCollationResult returnVal = Collator::fromUCollator(coll)-> |
| compare(source, sourceLength, target, targetLength, status); |
| UTRACE_EXIT_VALUE_STATUS(returnVal, status); |
| return returnVal; |
| } |
| |
| U_CAPI UCollationResult U_EXPORT2 |
| ucol_strcollUTF8( |
| const UCollator *coll, |
| const char *source, |
| int32_t sourceLength, |
| const char *target, |
| int32_t targetLength, |
| UErrorCode *status) |
| { |
| UTRACE_ENTRY(UTRACE_UCOL_STRCOLLUTF8); |
| if (UTRACE_LEVEL(UTRACE_VERBOSE)) { |
| UTRACE_DATA3(UTRACE_VERBOSE, "coll=%p, source=%p, target=%p", coll, source, target); |
| UTRACE_DATA2(UTRACE_VERBOSE, "source string = %vb ", source, sourceLength); |
| UTRACE_DATA2(UTRACE_VERBOSE, "target string = %vb ", target, targetLength); |
| } |
| |
| if (U_FAILURE(*status)) { |
| /* do nothing */ |
| UTRACE_EXIT_VALUE_STATUS(UCOL_EQUAL, *status); |
| return UCOL_EQUAL; |
| } |
| |
| UCollationResult returnVal = Collator::fromUCollator(coll)->internalCompareUTF8( |
| source, sourceLength, target, targetLength, *status); |
| UTRACE_EXIT_VALUE_STATUS(returnVal, *status); |
| return returnVal; |
| } |
| |
| |
| /* convenience function for comparing strings */ |
| U_CAPI UBool U_EXPORT2 |
| ucol_greater( const UCollator *coll, |
| const UChar *source, |
| int32_t sourceLength, |
| const UChar *target, |
| int32_t targetLength) |
| { |
| return (ucol_strcoll(coll, source, sourceLength, target, targetLength) |
| == UCOL_GREATER); |
| } |
| |
| /* convenience function for comparing strings */ |
| U_CAPI UBool U_EXPORT2 |
| ucol_greaterOrEqual( const UCollator *coll, |
| const UChar *source, |
| int32_t sourceLength, |
| const UChar *target, |
| int32_t targetLength) |
| { |
| return (ucol_strcoll(coll, source, sourceLength, target, targetLength) |
| != UCOL_LESS); |
| } |
| |
| /* convenience function for comparing strings */ |
| U_CAPI UBool U_EXPORT2 |
| ucol_equal( const UCollator *coll, |
| const UChar *source, |
| int32_t sourceLength, |
| const UChar *target, |
| int32_t targetLength) |
| { |
| return (ucol_strcoll(coll, source, sourceLength, target, targetLength) |
| == UCOL_EQUAL); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_getUCAVersion(const UCollator* coll, UVersionInfo info) { |
| const Collator *c = Collator::fromUCollator(coll); |
| if(c != NULL) { |
| UVersionInfo v; |
| c->getVersion(v); |
| // Note: This is tied to how the current implementation encodes the UCA version |
| // in the overall getVersion(). |
| // Alternatively, we could load the root collator and get at lower-level data from there. |
| // Either way, it will reflect the input collator's UCA version only |
| // if it is a known implementation. |
| // It would be cleaner to make this a virtual Collator method. |
| info[0] = v[1] >> 3; |
| info[1] = v[1] & 7; |
| info[2] = v[2] >> 6; |
| info[3] = 0; |
| } |
| } |
| |
| U_CAPI const UChar * U_EXPORT2 |
| ucol_getRules(const UCollator *coll, int32_t *length) { |
| const RuleBasedCollator *rbc = RuleBasedCollator::rbcFromUCollator(coll); |
| // OK to crash if coll==NULL: We do not want to check "this" pointers. |
| if(rbc != NULL || coll == NULL) { |
| const UnicodeString &rules = rbc->getRules(); |
| U_ASSERT(rules.getBuffer()[rules.length()] == 0); |
| *length = rules.length(); |
| return rules.getBuffer(); |
| } |
| static const UChar _NUL = 0; |
| *length = 0; |
| return &_NUL; |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getRulesEx(const UCollator *coll, UColRuleOption delta, UChar *buffer, int32_t bufferLen) { |
| UnicodeString rules; |
| const RuleBasedCollator *rbc = RuleBasedCollator::rbcFromUCollator(coll); |
| if(rbc != NULL || coll == NULL) { |
| rbc->getRules(delta, rules); |
| } |
| if(buffer != NULL && bufferLen > 0) { |
| UErrorCode errorCode = U_ZERO_ERROR; |
| return rules.extract(buffer, bufferLen, errorCode); |
| } else { |
| return rules.length(); |
| } |
| } |
| |
| U_CAPI const char * U_EXPORT2 |
| ucol_getLocale(const UCollator *coll, ULocDataLocaleType type, UErrorCode *status) { |
| return ucol_getLocaleByType(coll, type, status); |
| } |
| |
| U_CAPI const char * U_EXPORT2 |
| ucol_getLocaleByType(const UCollator *coll, ULocDataLocaleType type, UErrorCode *status) { |
| if(U_FAILURE(*status)) { |
| return NULL; |
| } |
| UTRACE_ENTRY(UTRACE_UCOL_GETLOCALE); |
| UTRACE_DATA1(UTRACE_INFO, "coll=%p", coll); |
| |
| const char *result; |
| const RuleBasedCollator *rbc = RuleBasedCollator::rbcFromUCollator(coll); |
| if(rbc == NULL && coll != NULL) { |
| *status = U_UNSUPPORTED_ERROR; |
| result = NULL; |
| } else { |
| result = rbc->internalGetLocaleID(type, *status); |
| } |
| |
| UTRACE_DATA1(UTRACE_INFO, "result = %s", result); |
| UTRACE_EXIT_STATUS(*status); |
| return result; |
| } |
| |
| U_CAPI USet * U_EXPORT2 |
| ucol_getTailoredSet(const UCollator *coll, UErrorCode *status) { |
| if(U_FAILURE(*status)) { |
| return NULL; |
| } |
| UnicodeSet *set = Collator::fromUCollator(coll)->getTailoredSet(*status); |
| if(U_FAILURE(*status)) { |
| delete set; |
| return NULL; |
| } |
| return set->toUSet(); |
| } |
| |
| U_CAPI UBool U_EXPORT2 |
| ucol_equals(const UCollator *source, const UCollator *target) { |
| return source == target || |
| (*Collator::fromUCollator(source)) == (*Collator::fromUCollator(target)); |
| } |
| |
| #endif /* #if !UCONFIG_NO_COLLATION */ |