| /* |
| ****************************************************************************** |
| * Copyright (C) 2001-2015, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ****************************************************************************** |
| * |
| * File ucoleitr.cpp |
| * |
| * Modification History: |
| * |
| * Date Name Description |
| * 02/15/2001 synwee Modified all methods to process its own function |
| * instead of calling the equivalent c++ api (coleitr.h) |
| * 2012-2014 markus Rewritten in C++ again. |
| ******************************************************************************/ |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_COLLATION |
| |
| #include "unicode/coleitr.h" |
| #include "unicode/tblcoll.h" |
| #include "unicode/ucoleitr.h" |
| #include "unicode/ustring.h" |
| #include "unicode/sortkey.h" |
| #include "unicode/uobject.h" |
| #include "cmemory.h" |
| #include "usrchimp.h" |
| |
| U_NAMESPACE_USE |
| |
| #define BUFFER_LENGTH 100 |
| |
| #define DEFAULT_BUFFER_SIZE 16 |
| #define BUFFER_GROW 8 |
| |
| #define ARRAY_COPY(dst, src, count) uprv_memcpy((void *) (dst), (void *) (src), (count) * sizeof (src)[0]) |
| |
| #define NEW_ARRAY(type, count) (type *) uprv_malloc((count) * sizeof(type)) |
| |
| #define DELETE_ARRAY(array) uprv_free((void *) (array)) |
| |
| struct RCEI |
| { |
| uint32_t ce; |
| int32_t low; |
| int32_t high; |
| }; |
| |
| U_NAMESPACE_BEGIN |
| |
| struct RCEBuffer |
| { |
| RCEI defaultBuffer[DEFAULT_BUFFER_SIZE]; |
| RCEI *buffer; |
| int32_t bufferIndex; |
| int32_t bufferSize; |
| |
| RCEBuffer(); |
| ~RCEBuffer(); |
| |
| UBool isEmpty() const; |
| void put(uint32_t ce, int32_t ixLow, int32_t ixHigh, UErrorCode &errorCode); |
| const RCEI *get(); |
| }; |
| |
| RCEBuffer::RCEBuffer() |
| { |
| buffer = defaultBuffer; |
| bufferIndex = 0; |
| bufferSize = UPRV_LENGTHOF(defaultBuffer); |
| } |
| |
| RCEBuffer::~RCEBuffer() |
| { |
| if (buffer != defaultBuffer) { |
| DELETE_ARRAY(buffer); |
| } |
| } |
| |
| UBool RCEBuffer::isEmpty() const |
| { |
| return bufferIndex <= 0; |
| } |
| |
| void RCEBuffer::put(uint32_t ce, int32_t ixLow, int32_t ixHigh, UErrorCode &errorCode) |
| { |
| if (U_FAILURE(errorCode)) { |
| return; |
| } |
| if (bufferIndex >= bufferSize) { |
| RCEI *newBuffer = NEW_ARRAY(RCEI, bufferSize + BUFFER_GROW); |
| if (newBuffer == NULL) { |
| errorCode = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| |
| ARRAY_COPY(newBuffer, buffer, bufferSize); |
| |
| if (buffer != defaultBuffer) { |
| DELETE_ARRAY(buffer); |
| } |
| |
| buffer = newBuffer; |
| bufferSize += BUFFER_GROW; |
| } |
| |
| buffer[bufferIndex].ce = ce; |
| buffer[bufferIndex].low = ixLow; |
| buffer[bufferIndex].high = ixHigh; |
| |
| bufferIndex += 1; |
| } |
| |
| const RCEI *RCEBuffer::get() |
| { |
| if (bufferIndex > 0) { |
| return &buffer[--bufferIndex]; |
| } |
| |
| return NULL; |
| } |
| |
| PCEBuffer::PCEBuffer() |
| { |
| buffer = defaultBuffer; |
| bufferIndex = 0; |
| bufferSize = UPRV_LENGTHOF(defaultBuffer); |
| } |
| |
| PCEBuffer::~PCEBuffer() |
| { |
| if (buffer != defaultBuffer) { |
| DELETE_ARRAY(buffer); |
| } |
| } |
| |
| void PCEBuffer::reset() |
| { |
| bufferIndex = 0; |
| } |
| |
| UBool PCEBuffer::isEmpty() const |
| { |
| return bufferIndex <= 0; |
| } |
| |
| void PCEBuffer::put(uint64_t ce, int32_t ixLow, int32_t ixHigh, UErrorCode &errorCode) |
| { |
| if (U_FAILURE(errorCode)) { |
| return; |
| } |
| if (bufferIndex >= bufferSize) { |
| PCEI *newBuffer = NEW_ARRAY(PCEI, bufferSize + BUFFER_GROW); |
| if (newBuffer == NULL) { |
| errorCode = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| |
| ARRAY_COPY(newBuffer, buffer, bufferSize); |
| |
| if (buffer != defaultBuffer) { |
| DELETE_ARRAY(buffer); |
| } |
| |
| buffer = newBuffer; |
| bufferSize += BUFFER_GROW; |
| } |
| |
| buffer[bufferIndex].ce = ce; |
| buffer[bufferIndex].low = ixLow; |
| buffer[bufferIndex].high = ixHigh; |
| |
| bufferIndex += 1; |
| } |
| |
| const PCEI *PCEBuffer::get() |
| { |
| if (bufferIndex > 0) { |
| return &buffer[--bufferIndex]; |
| } |
| |
| return NULL; |
| } |
| |
| UCollationPCE::UCollationPCE(UCollationElements *elems) { init(elems); } |
| |
| UCollationPCE::UCollationPCE(CollationElementIterator *iter) { init(iter); } |
| |
| void UCollationPCE::init(UCollationElements *elems) { |
| init(CollationElementIterator::fromUCollationElements(elems)); |
| } |
| |
| void UCollationPCE::init(CollationElementIterator *iter) |
| { |
| cei = iter; |
| init(*iter->rbc_); |
| } |
| |
| void UCollationPCE::init(const Collator &coll) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| |
| strength = coll.getAttribute(UCOL_STRENGTH, status); |
| toShift = coll.getAttribute(UCOL_ALTERNATE_HANDLING, status) == UCOL_SHIFTED; |
| isShifted = FALSE; |
| variableTop = coll.getVariableTop(status); |
| } |
| |
| UCollationPCE::~UCollationPCE() |
| { |
| // nothing to do |
| } |
| |
| uint64_t UCollationPCE::processCE(uint32_t ce) |
| { |
| uint64_t primary = 0, secondary = 0, tertiary = 0, quaternary = 0; |
| |
| // This is clean, but somewhat slow... |
| // We could apply the mask to ce and then |
| // just get all three orders... |
| switch(strength) { |
| default: |
| tertiary = ucol_tertiaryOrder(ce); |
| /* note fall-through */ |
| |
| case UCOL_SECONDARY: |
| secondary = ucol_secondaryOrder(ce); |
| /* note fall-through */ |
| |
| case UCOL_PRIMARY: |
| primary = ucol_primaryOrder(ce); |
| } |
| |
| // **** This should probably handle continuations too. **** |
| // **** That means that we need 24 bits for the primary **** |
| // **** instead of the 16 that we're currently using. **** |
| // **** So we can lay out the 64 bits as: 24.12.12.16. **** |
| // **** Another complication with continuations is that **** |
| // **** the *second* CE is marked as a continuation, so **** |
| // **** we always have to peek ahead to know how long **** |
| // **** the primary is... **** |
| if ((toShift && variableTop > ce && primary != 0) |
| || (isShifted && primary == 0)) { |
| |
| if (primary == 0) { |
| return UCOL_IGNORABLE; |
| } |
| |
| if (strength >= UCOL_QUATERNARY) { |
| quaternary = primary; |
| } |
| |
| primary = secondary = tertiary = 0; |
| isShifted = TRUE; |
| } else { |
| if (strength >= UCOL_QUATERNARY) { |
| quaternary = 0xFFFF; |
| } |
| |
| isShifted = FALSE; |
| } |
| |
| return primary << 48 | secondary << 32 | tertiary << 16 | quaternary; |
| } |
| |
| U_NAMESPACE_END |
| |
| /* public methods ---------------------------------------------------- */ |
| |
| U_CAPI UCollationElements* U_EXPORT2 |
| ucol_openElements(const UCollator *coll, |
| const UChar *text, |
| int32_t textLength, |
| UErrorCode *status) |
| { |
| if (U_FAILURE(*status)) { |
| return NULL; |
| } |
| if (coll == NULL || (text == NULL && textLength != 0)) { |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| return NULL; |
| } |
| const RuleBasedCollator *rbc = RuleBasedCollator::rbcFromUCollator(coll); |
| if (rbc == NULL) { |
| *status = U_UNSUPPORTED_ERROR; // coll is a Collator but not a RuleBasedCollator |
| return NULL; |
| } |
| |
| UnicodeString s((UBool)(textLength < 0), text, textLength); |
| CollationElementIterator *cei = rbc->createCollationElementIterator(s); |
| if (cei == NULL) { |
| *status = U_MEMORY_ALLOCATION_ERROR; |
| return NULL; |
| } |
| |
| return cei->toUCollationElements(); |
| } |
| |
| |
| U_CAPI void U_EXPORT2 |
| ucol_closeElements(UCollationElements *elems) |
| { |
| delete CollationElementIterator::fromUCollationElements(elems); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_reset(UCollationElements *elems) |
| { |
| CollationElementIterator::fromUCollationElements(elems)->reset(); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_next(UCollationElements *elems, |
| UErrorCode *status) |
| { |
| if (U_FAILURE(*status)) { |
| return UCOL_NULLORDER; |
| } |
| |
| return CollationElementIterator::fromUCollationElements(elems)->next(*status); |
| } |
| |
| U_NAMESPACE_BEGIN |
| |
| int64_t |
| UCollationPCE::nextProcessed( |
| int32_t *ixLow, |
| int32_t *ixHigh, |
| UErrorCode *status) |
| { |
| int64_t result = UCOL_IGNORABLE; |
| uint32_t low = 0, high = 0; |
| |
| if (U_FAILURE(*status)) { |
| return UCOL_PROCESSED_NULLORDER; |
| } |
| |
| pceBuffer.reset(); |
| |
| do { |
| low = cei->getOffset(); |
| int32_t ce = cei->next(*status); |
| high = cei->getOffset(); |
| |
| if (ce == UCOL_NULLORDER) { |
| result = UCOL_PROCESSED_NULLORDER; |
| break; |
| } |
| |
| result = processCE((uint32_t)ce); |
| } while (result == UCOL_IGNORABLE); |
| |
| if (ixLow != NULL) { |
| *ixLow = low; |
| } |
| |
| if (ixHigh != NULL) { |
| *ixHigh = high; |
| } |
| |
| return result; |
| } |
| |
| U_NAMESPACE_END |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_previous(UCollationElements *elems, |
| UErrorCode *status) |
| { |
| if(U_FAILURE(*status)) { |
| return UCOL_NULLORDER; |
| } |
| return CollationElementIterator::fromUCollationElements(elems)->previous(*status); |
| } |
| |
| U_NAMESPACE_BEGIN |
| |
| int64_t |
| UCollationPCE::previousProcessed( |
| int32_t *ixLow, |
| int32_t *ixHigh, |
| UErrorCode *status) |
| { |
| int64_t result = UCOL_IGNORABLE; |
| int32_t low = 0, high = 0; |
| |
| if (U_FAILURE(*status)) { |
| return UCOL_PROCESSED_NULLORDER; |
| } |
| |
| // pceBuffer.reset(); |
| |
| while (pceBuffer.isEmpty()) { |
| // buffer raw CEs up to non-ignorable primary |
| RCEBuffer rceb; |
| int32_t ce; |
| |
| // **** do we need to reset rceb, or will it always be empty at this point **** |
| do { |
| high = cei->getOffset(); |
| ce = cei->previous(*status); |
| low = cei->getOffset(); |
| |
| if (ce == UCOL_NULLORDER) { |
| if (!rceb.isEmpty()) { |
| break; |
| } |
| |
| goto finish; |
| } |
| |
| rceb.put((uint32_t)ce, low, high, *status); |
| } while (U_SUCCESS(*status) && ((ce & UCOL_PRIMARYORDERMASK) == 0 || isContinuation(ce))); |
| |
| // process the raw CEs |
| while (U_SUCCESS(*status) && !rceb.isEmpty()) { |
| const RCEI *rcei = rceb.get(); |
| |
| result = processCE(rcei->ce); |
| |
| if (result != UCOL_IGNORABLE) { |
| pceBuffer.put(result, rcei->low, rcei->high, *status); |
| } |
| } |
| if (U_FAILURE(*status)) { |
| return UCOL_PROCESSED_NULLORDER; |
| } |
| } |
| |
| finish: |
| if (pceBuffer.isEmpty()) { |
| // **** Is -1 the right value for ixLow, ixHigh? **** |
| if (ixLow != NULL) { |
| *ixLow = -1; |
| } |
| |
| if (ixHigh != NULL) { |
| *ixHigh = -1 |
| ; |
| } |
| return UCOL_PROCESSED_NULLORDER; |
| } |
| |
| const PCEI *pcei = pceBuffer.get(); |
| |
| if (ixLow != NULL) { |
| *ixLow = pcei->low; |
| } |
| |
| if (ixHigh != NULL) { |
| *ixHigh = pcei->high; |
| } |
| |
| return pcei->ce; |
| } |
| |
| U_NAMESPACE_END |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getMaxExpansion(const UCollationElements *elems, |
| int32_t order) |
| { |
| return CollationElementIterator::fromUCollationElements(elems)->getMaxExpansion(order); |
| |
| // TODO: The old code masked the order according to strength and then did a binary search. |
| // However this was probably at least partially broken because of the following comment. |
| // Still, it might have found a match when this version may not. |
| |
| // FIXME: with a masked search, there might be more than one hit, |
| // so we need to look forward and backward from the match to find all |
| // of the hits... |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setText( UCollationElements *elems, |
| const UChar *text, |
| int32_t textLength, |
| UErrorCode *status) |
| { |
| if (U_FAILURE(*status)) { |
| return; |
| } |
| |
| if ((text == NULL && textLength != 0)) { |
| *status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| UnicodeString s((UBool)(textLength < 0), text, textLength); |
| return CollationElementIterator::fromUCollationElements(elems)->setText(s, *status); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_getOffset(const UCollationElements *elems) |
| { |
| return CollationElementIterator::fromUCollationElements(elems)->getOffset(); |
| } |
| |
| U_CAPI void U_EXPORT2 |
| ucol_setOffset(UCollationElements *elems, |
| int32_t offset, |
| UErrorCode *status) |
| { |
| if (U_FAILURE(*status)) { |
| return; |
| } |
| |
| CollationElementIterator::fromUCollationElements(elems)->setOffset(offset, *status); |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_primaryOrder (int32_t order) |
| { |
| return (order >> 16) & 0xffff; |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_secondaryOrder (int32_t order) |
| { |
| return (order >> 8) & 0xff; |
| } |
| |
| U_CAPI int32_t U_EXPORT2 |
| ucol_tertiaryOrder (int32_t order) |
| { |
| return order & 0xff; |
| } |
| |
| #endif /* #if !UCONFIG_NO_COLLATION */ |