// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
* Copyright (C) 2001-2016, 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), (size_t)(count) * sizeof (src)[0])
#define NEW_ARRAY(type, count) (type *) uprv_malloc((size_t)(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);
U_FALLTHROUGH;
case UCOL_SECONDARY:
secondary = ucol_secondaryOrder(ce);
U_FALLTHROUGH;
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 */