+ 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;
+}
+
+struct PCEI
+{
+ uint64_t ce;
+ int32_t low;
+ int32_t high;
+};
+
+struct PCEBuffer
+{
+ PCEI defaultBuffer[DEFAULT_BUFFER_SIZE];
+ PCEI *buffer;
+ int32_t bufferIndex;
+ int32_t bufferSize;
+
+ PCEBuffer();
+ ~PCEBuffer();
+
+ void reset();
+ UBool empty() const;
+ void put(uint64_t ce, int32_t ixLow, int32_t ixHigh);
+ const PCEI *get();
+};
+
+PCEBuffer::PCEBuffer()
+{
+ buffer = defaultBuffer;
+ bufferIndex = 0;
+ bufferSize = DEFAULT_BUFFER_SIZE;
+}
+
+PCEBuffer::~PCEBuffer()
+{
+ if (buffer != defaultBuffer) {
+ DELETE_ARRAY(buffer);
+ }
+}
+
+void PCEBuffer::reset()
+{
+ bufferIndex = 0;
+}
+
+UBool PCEBuffer::empty() const
+{
+ return bufferIndex <= 0;
+}
+
+void PCEBuffer::put(uint64_t ce, int32_t ixLow, int32_t ixHigh)
+{
+ if (bufferIndex >= bufferSize) {
+ PCEI *newBuffer = NEW_ARRAY(PCEI, bufferSize + BUFFER_GROW);
+
+ 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;
+}
+
+/*
+ * This inherits from UObject so that
+ * it can be allocated by new and the
+ * constructor for PCEBuffer is called.
+ */
+struct UCollationPCE : public UObject
+{
+ PCEBuffer pceBuffer;
+ UCollationStrength strength;
+ UBool toShift;
+ UBool isShifted;
+ uint32_t variableTop;
+
+ UCollationPCE(UCollationElements *elems);
+ ~UCollationPCE();
+
+ void init(const UCollator *coll);
+
+ virtual UClassID getDynamicClassID() const;
+ static UClassID getStaticClassID();
+};
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UCollationPCE)
+
+UCollationPCE::UCollationPCE(UCollationElements *elems)
+{
+ init(elems->iteratordata_.coll);
+}
+
+void UCollationPCE::init(const UCollator *coll)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ strength = ucol_getStrength(coll);
+ toShift = ucol_getAttribute(coll, UCOL_ALTERNATE_HANDLING, &status) == UCOL_SHIFTED;
+ isShifted = FALSE;
+ variableTop = coll->variableTopValue << 16;
+}
+
+UCollationPCE::~UCollationPCE()
+{
+ // nothing to do
+}
+
+
+U_NAMESPACE_END
+
+
+inline uint64_t processCE(UCollationElements *elems, 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(elems->pce->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);
+ }
+
+ // Continuation?
+ if (elems->pce->toShift && (elems->pce->variableTop > ce && primary != 0)
+ || (elems->pce->isShifted && primary == 0)) {
+
+ if (primary == 0) {
+ return UCOL_IGNORABLE;
+ }
+
+ if (elems->pce->strength >= UCOL_QUATERNARY) {
+ quaternary = primary;
+ }
+
+ primary = secondary = tertiary = 0;
+ elems->pce->isShifted = TRUE;
+ } else {
+ if (elems->pce->strength >= UCOL_QUATERNARY) {
+ quaternary = 0xFFFF;
+ }
+
+ elems->pce->isShifted = FALSE;
+ }
+
+
+ return primary << 48 | secondary << 32 | tertiary << 16 | quaternary;
+}
+
+U_CAPI void U_EXPORT2
+uprv_init_pce(const UCollationElements *elems)
+{
+ if (elems->pce != NULL) {
+ elems->pce->init(elems->iteratordata_.coll);
+ }