]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/collationiterator.cpp
ICU-57166.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / collationiterator.cpp
CommitLineData
57a6839d
A
1/*
2*******************************************************************************
3* Copyright (C) 2010-2014, International Business Machines
4* Corporation and others. All Rights Reserved.
5*******************************************************************************
6* collationiterator.cpp
7*
8* created on: 2010oct27
9* created by: Markus W. Scherer
10*/
11
12#include "utypeinfo.h" // for 'typeid' to work
13
14#include "unicode/utypes.h"
15
16#if !UCONFIG_NO_COLLATION
17
18#include "unicode/ucharstrie.h"
19#include "unicode/ustringtrie.h"
20#include "charstr.h"
21#include "cmemory.h"
22#include "collation.h"
23#include "collationdata.h"
24#include "collationfcd.h"
25#include "collationiterator.h"
26#include "normalizer2impl.h"
27#include "uassert.h"
28#include "uvectr32.h"
29
30U_NAMESPACE_BEGIN
31
32CollationIterator::CEBuffer::~CEBuffer() {}
33
34UBool
35CollationIterator::CEBuffer::ensureAppendCapacity(int32_t appCap, UErrorCode &errorCode) {
36 int32_t capacity = buffer.getCapacity();
37 if((length + appCap) <= capacity) { return TRUE; }
38 if(U_FAILURE(errorCode)) { return FALSE; }
39 do {
40 if(capacity < 1000) {
41 capacity *= 4;
42 } else {
43 capacity *= 2;
44 }
45 } while(capacity < (length + appCap));
46 int64_t *p = buffer.resize(capacity, length);
47 if(p == NULL) {
48 errorCode = U_MEMORY_ALLOCATION_ERROR;
49 return FALSE;
50 }
51 return TRUE;
52}
53
54// State of combining marks skipped in discontiguous contraction.
55// We create a state object on first use and keep it around deactivated between uses.
56class SkippedState : public UMemory {
57public:
58 // Born active but empty.
59 SkippedState() : pos(0), skipLengthAtMatch(0) {}
60 void clear() {
61 oldBuffer.remove();
62 pos = 0;
63 // The newBuffer is reset by setFirstSkipped().
64 }
65
66 UBool isEmpty() const { return oldBuffer.isEmpty(); }
67
68 UBool hasNext() const { return pos < oldBuffer.length(); }
69
70 // Requires hasNext().
71 UChar32 next() {
72 UChar32 c = oldBuffer.char32At(pos);
73 pos += U16_LENGTH(c);
74 return c;
75 }
76
77 // Accounts for one more input code point read beyond the end of the marks buffer.
78 void incBeyond() {
79 U_ASSERT(!hasNext());
80 ++pos;
81 }
82
83 // Goes backward through the skipped-marks buffer.
84 // Returns the number of code points read beyond the skipped marks
85 // that need to be backtracked through normal input.
86 int32_t backwardNumCodePoints(int32_t n) {
87 int32_t length = oldBuffer.length();
88 int32_t beyond = pos - length;
89 if(beyond > 0) {
90 if(beyond >= n) {
91 // Not back far enough to re-enter the oldBuffer.
92 pos -= n;
93 return n;
94 } else {
95 // Back out all beyond-oldBuffer code points and re-enter the buffer.
96 pos = oldBuffer.moveIndex32(length, beyond - n);
97 return beyond;
98 }
99 } else {
100 // Go backwards from inside the oldBuffer.
101 pos = oldBuffer.moveIndex32(pos, -n);
102 return 0;
103 }
104 }
105
106 void setFirstSkipped(UChar32 c) {
107 skipLengthAtMatch = 0;
108 newBuffer.setTo(c);
109 }
110
111 void skip(UChar32 c) {
112 newBuffer.append(c);
113 }
114
115 void recordMatch() { skipLengthAtMatch = newBuffer.length(); }
116
117 // Replaces the characters we consumed with the newly skipped ones.
118 void replaceMatch() {
119 // Note: UnicodeString.replace() pins pos to at most length().
120 oldBuffer.replace(0, pos, newBuffer, 0, skipLengthAtMatch);
121 pos = 0;
122 }
123
124 void saveTrieState(const UCharsTrie &trie) { trie.saveState(state); }
125 void resetToTrieState(UCharsTrie &trie) const { trie.resetToState(state); }
126
127private:
128 // Combining marks skipped in previous discontiguous-contraction matching.
129 // After that discontiguous contraction was completed, we start reading them from here.
130 UnicodeString oldBuffer;
131 // Combining marks newly skipped in current discontiguous-contraction matching.
132 // These might have been read from the normal text or from the oldBuffer.
133 UnicodeString newBuffer;
134 // Reading index in oldBuffer,
135 // or counter for how many code points have been read beyond oldBuffer (pos-oldBuffer.length()).
136 int32_t pos;
137 // newBuffer.length() at the time of the last matching character.
138 // When a partial match fails, we back out skipped and partial-matching input characters.
139 int32_t skipLengthAtMatch;
140 // We save the trie state before we attempt to match a character,
141 // so that we can skip it and try the next one.
142 UCharsTrie::State state;
143};
144
145CollationIterator::CollationIterator(const CollationIterator &other)
146 : UObject(other),
147 trie(other.trie),
148 data(other.data),
149 cesIndex(other.cesIndex),
150 skipped(NULL),
151 numCpFwd(other.numCpFwd),
152 isNumeric(other.isNumeric) {
153 UErrorCode errorCode = U_ZERO_ERROR;
154 int32_t length = other.ceBuffer.length;
155 if(length > 0 && ceBuffer.ensureAppendCapacity(length, errorCode)) {
156 for(int32_t i = 0; i < length; ++i) {
157 ceBuffer.set(i, other.ceBuffer.get(i));
158 }
159 ceBuffer.length = length;
160 } else {
161 cesIndex = 0;
162 }
163}
164
165CollationIterator::~CollationIterator() {
166 delete skipped;
167}
168
169UBool
170CollationIterator::operator==(const CollationIterator &other) const {
171 // Subclasses: Call this method and then add more specific checks.
172 // Compare the iterator state but not the collation data (trie & data fields):
173 // Assume that the caller compares the data.
174 // Ignore skipped since that should be unused between calls to nextCE().
175 // (It only stays around to avoid another memory allocation.)
176 if(!(typeid(*this) == typeid(other) &&
177 ceBuffer.length == other.ceBuffer.length &&
178 cesIndex == other.cesIndex &&
179 numCpFwd == other.numCpFwd &&
180 isNumeric == other.isNumeric)) {
181 return FALSE;
182 }
183 for(int32_t i = 0; i < ceBuffer.length; ++i) {
184 if(ceBuffer.get(i) != other.ceBuffer.get(i)) { return FALSE; }
185 }
186 return TRUE;
187}
188
189void
190CollationIterator::reset() {
191 cesIndex = ceBuffer.length = 0;
192 if(skipped != NULL) { skipped->clear(); }
193}
194
195int32_t
196CollationIterator::fetchCEs(UErrorCode &errorCode) {
197 while(U_SUCCESS(errorCode) && nextCE(errorCode) != Collation::NO_CE) {
198 // No need to loop for each expansion CE.
199 cesIndex = ceBuffer.length;
200 }
201 return ceBuffer.length;
202}
203
204uint32_t
205CollationIterator::handleNextCE32(UChar32 &c, UErrorCode &errorCode) {
206 c = nextCodePoint(errorCode);
207 return (c < 0) ? Collation::FALLBACK_CE32 : data->getCE32(c);
208}
209
210UChar
211CollationIterator::handleGetTrailSurrogate() {
212 return 0;
213}
214
215UBool
216CollationIterator::foundNULTerminator() {
217 return FALSE;
218}
219
220UBool
221CollationIterator::forbidSurrogateCodePoints() const {
222 return FALSE;
223}
224
225uint32_t
226CollationIterator::getDataCE32(UChar32 c) const {
227 return data->getCE32(c);
228}
229
230uint32_t
231CollationIterator::getCE32FromBuilderData(uint32_t /*ce32*/, UErrorCode &errorCode) {
232 if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; }
233 return 0;
234}
235
236int64_t
237CollationIterator::nextCEFromCE32(const CollationData *d, UChar32 c, uint32_t ce32,
238 UErrorCode &errorCode) {
239 --ceBuffer.length; // Undo ceBuffer.incLength().
240 appendCEsFromCE32(d, c, ce32, TRUE, errorCode);
241 if(U_SUCCESS(errorCode)) {
242 return ceBuffer.get(cesIndex++);
243 } else {
244 return Collation::NO_CE_PRIMARY;
245 }
246}
247
248void
249CollationIterator::appendCEsFromCE32(const CollationData *d, UChar32 c, uint32_t ce32,
250 UBool forward, UErrorCode &errorCode) {
251 while(Collation::isSpecialCE32(ce32)) {
252 switch(Collation::tagFromCE32(ce32)) {
253 case Collation::FALLBACK_TAG:
254 case Collation::RESERVED_TAG_3:
255 if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; }
256 return;
257 case Collation::LONG_PRIMARY_TAG:
258 ceBuffer.append(Collation::ceFromLongPrimaryCE32(ce32), errorCode);
259 return;
260 case Collation::LONG_SECONDARY_TAG:
261 ceBuffer.append(Collation::ceFromLongSecondaryCE32(ce32), errorCode);
262 return;
263 case Collation::LATIN_EXPANSION_TAG:
264 if(ceBuffer.ensureAppendCapacity(2, errorCode)) {
265 ceBuffer.set(ceBuffer.length, Collation::latinCE0FromCE32(ce32));
266 ceBuffer.set(ceBuffer.length + 1, Collation::latinCE1FromCE32(ce32));
267 ceBuffer.length += 2;
268 }
269 return;
270 case Collation::EXPANSION32_TAG: {
271 const uint32_t *ce32s = d->ce32s + Collation::indexFromCE32(ce32);
272 int32_t length = Collation::lengthFromCE32(ce32);
273 if(ceBuffer.ensureAppendCapacity(length, errorCode)) {
274 do {
275 ceBuffer.appendUnsafe(Collation::ceFromCE32(*ce32s++));
276 } while(--length > 0);
277 }
278 return;
279 }
280 case Collation::EXPANSION_TAG: {
281 const int64_t *ces = d->ces + Collation::indexFromCE32(ce32);
282 int32_t length = Collation::lengthFromCE32(ce32);
283 if(ceBuffer.ensureAppendCapacity(length, errorCode)) {
284 do {
285 ceBuffer.appendUnsafe(*ces++);
286 } while(--length > 0);
287 }
288 return;
289 }
290 case Collation::BUILDER_DATA_TAG:
291 ce32 = getCE32FromBuilderData(ce32, errorCode);
292 if(U_FAILURE(errorCode)) { return; }
293 if(ce32 == Collation::FALLBACK_CE32) {
294 d = data->base;
295 ce32 = d->getCE32(c);
296 }
297 break;
298 case Collation::PREFIX_TAG:
299 if(forward) { backwardNumCodePoints(1, errorCode); }
300 ce32 = getCE32FromPrefix(d, ce32, errorCode);
301 if(forward) { forwardNumCodePoints(1, errorCode); }
302 break;
303 case Collation::CONTRACTION_TAG: {
304 const UChar *p = d->contexts + Collation::indexFromCE32(ce32);
305 uint32_t defaultCE32 = CollationData::readCE32(p); // Default if no suffix match.
306 if(!forward) {
307 // Backward contractions are handled by previousCEUnsafe().
308 // c has contractions but they were not found.
309 ce32 = defaultCE32;
310 break;
311 }
312 UChar32 nextCp;
313 if(skipped == NULL && numCpFwd < 0) {
314 // Some portion of nextCE32FromContraction() pulled out here as an ASCII fast path,
315 // avoiding the function call and the nextSkippedCodePoint() overhead.
316 nextCp = nextCodePoint(errorCode);
317 if(nextCp < 0) {
318 // No more text.
319 ce32 = defaultCE32;
320 break;
321 } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 &&
322 !CollationFCD::mayHaveLccc(nextCp)) {
323 // All contraction suffixes start with characters with lccc!=0
324 // but the next code point has lccc==0.
325 backwardNumCodePoints(1, errorCode);
326 ce32 = defaultCE32;
327 break;
328 }
329 } else {
330 nextCp = nextSkippedCodePoint(errorCode);
331 if(nextCp < 0) {
332 // No more text.
333 ce32 = defaultCE32;
334 break;
335 } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 &&
336 !CollationFCD::mayHaveLccc(nextCp)) {
337 // All contraction suffixes start with characters with lccc!=0
338 // but the next code point has lccc==0.
339 backwardNumSkipped(1, errorCode);
340 ce32 = defaultCE32;
341 break;
342 }
343 }
344 ce32 = nextCE32FromContraction(d, ce32, p + 2, defaultCE32, nextCp, errorCode);
345 if(ce32 == Collation::NO_CE32) {
346 // CEs from a discontiguous contraction plus the skipped combining marks
347 // have been appended already.
348 return;
349 }
350 break;
351 }
352 case Collation::DIGIT_TAG:
353 if(isNumeric) {
354 appendNumericCEs(ce32, forward, errorCode);
355 return;
356 } else {
357 // Fetch the non-numeric-collation CE32 and continue.
358 ce32 = d->ce32s[Collation::indexFromCE32(ce32)];
359 break;
360 }
361 case Collation::U0000_TAG:
362 U_ASSERT(c == 0);
363 if(forward && foundNULTerminator()) {
364 // Handle NUL-termination. (Not needed in Java.)
365 ceBuffer.append(Collation::NO_CE, errorCode);
366 return;
367 } else {
368 // Fetch the normal ce32 for U+0000 and continue.
369 ce32 = d->ce32s[0];
370 break;
371 }
372 case Collation::HANGUL_TAG: {
373 const uint32_t *jamoCE32s = d->jamoCE32s;
374 c -= Hangul::HANGUL_BASE;
375 UChar32 t = c % Hangul::JAMO_T_COUNT;
376 c /= Hangul::JAMO_T_COUNT;
377 UChar32 v = c % Hangul::JAMO_V_COUNT;
378 c /= Hangul::JAMO_V_COUNT;
379 if((ce32 & Collation::HANGUL_NO_SPECIAL_JAMO) != 0) {
380 // None of the Jamo CE32s are isSpecialCE32().
381 // Avoid recursive function calls and per-Jamo tests.
382 if(ceBuffer.ensureAppendCapacity(t == 0 ? 2 : 3, errorCode)) {
383 ceBuffer.set(ceBuffer.length, Collation::ceFromCE32(jamoCE32s[c]));
384 ceBuffer.set(ceBuffer.length + 1, Collation::ceFromCE32(jamoCE32s[19 + v]));
385 ceBuffer.length += 2;
386 if(t != 0) {
387 ceBuffer.appendUnsafe(Collation::ceFromCE32(jamoCE32s[39 + t]));
388 }
389 }
390 return;
391 } else {
392 // We should not need to compute each Jamo code point.
393 // In particular, there should be no offset or implicit ce32.
394 appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[c], forward, errorCode);
395 appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[19 + v], forward, errorCode);
396 if(t == 0) { return; }
397 // offset 39 = 19 + 21 - 1:
398 // 19 = JAMO_L_COUNT
399 // 21 = JAMO_T_COUNT
400 // -1 = omit t==0
401 ce32 = jamoCE32s[39 + t];
402 c = U_SENTINEL;
403 break;
404 }
405 }
406 case Collation::LEAD_SURROGATE_TAG: {
407 U_ASSERT(forward); // Backward iteration should never see lead surrogate code _unit_ data.
408 U_ASSERT(U16_IS_LEAD(c));
409 UChar trail;
410 if(U16_IS_TRAIL(trail = handleGetTrailSurrogate())) {
411 c = U16_GET_SUPPLEMENTARY(c, trail);
412 ce32 &= Collation::LEAD_TYPE_MASK;
413 if(ce32 == Collation::LEAD_ALL_UNASSIGNED) {
414 ce32 = Collation::UNASSIGNED_CE32; // unassigned-implicit
415 } else if(ce32 == Collation::LEAD_ALL_FALLBACK ||
416 (ce32 = d->getCE32FromSupplementary(c)) == Collation::FALLBACK_CE32) {
417 // fall back to the base data
418 d = d->base;
419 ce32 = d->getCE32FromSupplementary(c);
420 }
421 } else {
422 // c is an unpaired surrogate.
423 ce32 = Collation::UNASSIGNED_CE32;
424 }
425 break;
426 }
427 case Collation::OFFSET_TAG:
428 U_ASSERT(c >= 0);
429 ceBuffer.append(d->getCEFromOffsetCE32(c, ce32), errorCode);
430 return;
431 case Collation::IMPLICIT_TAG:
432 U_ASSERT(c >= 0);
433 if(U_IS_SURROGATE(c) && forbidSurrogateCodePoints()) {
434 ce32 = Collation::FFFD_CE32;
435 break;
436 } else {
437 ceBuffer.append(Collation::unassignedCEFromCodePoint(c), errorCode);
438 return;
439 }
440 }
441 }
442 ceBuffer.append(Collation::ceFromSimpleCE32(ce32), errorCode);
443}
444
445uint32_t
446CollationIterator::getCE32FromPrefix(const CollationData *d, uint32_t ce32,
447 UErrorCode &errorCode) {
448 const UChar *p = d->contexts + Collation::indexFromCE32(ce32);
449 ce32 = CollationData::readCE32(p); // Default if no prefix match.
450 p += 2;
451 // Number of code points read before the original code point.
452 int32_t lookBehind = 0;
453 UCharsTrie prefixes(p);
454 for(;;) {
455 UChar32 c = previousCodePoint(errorCode);
456 if(c < 0) { break; }
457 ++lookBehind;
458 UStringTrieResult match = prefixes.nextForCodePoint(c);
459 if(USTRINGTRIE_HAS_VALUE(match)) {
460 ce32 = (uint32_t)prefixes.getValue();
461 }
462 if(!USTRINGTRIE_HAS_NEXT(match)) { break; }
463 }
464 forwardNumCodePoints(lookBehind, errorCode);
465 return ce32;
466}
467
468UChar32
469CollationIterator::nextSkippedCodePoint(UErrorCode &errorCode) {
470 if(skipped != NULL && skipped->hasNext()) { return skipped->next(); }
471 if(numCpFwd == 0) { return U_SENTINEL; }
472 UChar32 c = nextCodePoint(errorCode);
473 if(skipped != NULL && !skipped->isEmpty() && c >= 0) { skipped->incBeyond(); }
474 if(numCpFwd > 0 && c >= 0) { --numCpFwd; }
475 return c;
476}
477
478void
479CollationIterator::backwardNumSkipped(int32_t n, UErrorCode &errorCode) {
480 if(skipped != NULL && !skipped->isEmpty()) {
481 n = skipped->backwardNumCodePoints(n);
482 }
483 backwardNumCodePoints(n, errorCode);
484 if(numCpFwd >= 0) { numCpFwd += n; }
485}
486
487uint32_t
488CollationIterator::nextCE32FromContraction(const CollationData *d, uint32_t contractionCE32,
489 const UChar *p, uint32_t ce32, UChar32 c,
490 UErrorCode &errorCode) {
491 // c: next code point after the original one
492
493 // Number of code points read beyond the original code point.
494 // Needed for discontiguous contraction matching.
495 int32_t lookAhead = 1;
496 // Number of code points read since the last match (initially only c).
497 int32_t sinceMatch = 1;
498 // Normally we only need a contiguous match,
499 // and therefore need not remember the suffixes state from before a mismatch for retrying.
500 // If we are already processing skipped combining marks, then we do track the state.
501 UCharsTrie suffixes(p);
502 if(skipped != NULL && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); }
503 UStringTrieResult match = suffixes.firstForCodePoint(c);
504 for(;;) {
505 UChar32 nextCp;
506 if(USTRINGTRIE_HAS_VALUE(match)) {
507 ce32 = (uint32_t)suffixes.getValue();
508 if(!USTRINGTRIE_HAS_NEXT(match) || (c = nextSkippedCodePoint(errorCode)) < 0) {
509 return ce32;
510 }
511 if(skipped != NULL && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); }
512 sinceMatch = 1;
513 } else if(match == USTRINGTRIE_NO_MATCH || (nextCp = nextSkippedCodePoint(errorCode)) < 0) {
514 // No match for c, or partial match (USTRINGTRIE_NO_VALUE) and no further text.
515 // Back up if necessary, and try a discontiguous contraction.
516 if((contractionCE32 & Collation::CONTRACT_TRAILING_CCC) != 0 &&
517 // Discontiguous contraction matching extends an existing match.
518 // If there is no match yet, then there is nothing to do.
519 ((contractionCE32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) == 0 ||
520 sinceMatch < lookAhead)) {
521 // The last character of at least one suffix has lccc!=0,
522 // allowing for discontiguous contractions.
523 // UCA S2.1.1 only processes non-starters immediately following
524 // "a match in the table" (sinceMatch=1).
525 if(sinceMatch > 1) {
526 // Return to the state after the last match.
527 // (Return to sinceMatch=0 and re-fetch the first partially-matched character.)
528 backwardNumSkipped(sinceMatch, errorCode);
529 c = nextSkippedCodePoint(errorCode);
530 lookAhead -= sinceMatch - 1;
531 sinceMatch = 1;
532 }
533 if(d->getFCD16(c) > 0xff) {
534 return nextCE32FromDiscontiguousContraction(
535 d, suffixes, ce32, lookAhead, c, errorCode);
536 }
537 }
538 break;
539 } else {
540 // Continue after partial match (USTRINGTRIE_NO_VALUE) for c.
541 // It does not have a result value, therefore it is not itself "a match in the table".
542 // If a partially-matched c has ccc!=0 then
543 // it might be skipped in discontiguous contraction.
544 c = nextCp;
545 ++sinceMatch;
546 }
547 ++lookAhead;
548 match = suffixes.nextForCodePoint(c);
549 }
550 backwardNumSkipped(sinceMatch, errorCode);
551 return ce32;
552}
553
554uint32_t
555CollationIterator::nextCE32FromDiscontiguousContraction(
556 const CollationData *d, UCharsTrie &suffixes, uint32_t ce32,
557 int32_t lookAhead, UChar32 c,
558 UErrorCode &errorCode) {
559 if(U_FAILURE(errorCode)) { return 0; }
560
561 // UCA section 3.3.2 Contractions:
562 // Contractions that end with non-starter characters
563 // are known as discontiguous contractions.
564 // ... discontiguous contractions must be detected in input text
565 // whenever the final sequence of non-starter characters could be rearranged
566 // so as to make a contiguous matching sequence that is canonically equivalent.
567
568 // UCA: http://www.unicode.org/reports/tr10/#S2.1
569 // S2.1 Find the longest initial substring S at each point that has a match in the table.
570 // S2.1.1 If there are any non-starters following S, process each non-starter C.
571 // S2.1.2 If C is not blocked from S, find if S + C has a match in the table.
572 // Note: A non-starter in a string is called blocked
573 // if there is another non-starter of the same canonical combining class or zero
574 // between it and the last character of canonical combining class 0.
575 // S2.1.3 If there is a match, replace S by S + C, and remove C.
576
577 // First: Is a discontiguous contraction even possible?
578 uint16_t fcd16 = d->getFCD16(c);
579 U_ASSERT(fcd16 > 0xff); // The caller checked this already, as a shortcut.
580 UChar32 nextCp = nextSkippedCodePoint(errorCode);
581 if(nextCp < 0) {
582 // No further text.
583 backwardNumSkipped(1, errorCode);
584 return ce32;
585 }
586 ++lookAhead;
587 uint8_t prevCC = (uint8_t)fcd16;
588 fcd16 = d->getFCD16(nextCp);
589 if(fcd16 <= 0xff) {
590 // The next code point after c is a starter (S2.1.1 "process each non-starter").
591 backwardNumSkipped(2, errorCode);
592 return ce32;
593 }
594
595 // We have read and matched (lookAhead-2) code points,
596 // read non-matching c and peeked ahead at nextCp.
597 // Return to the state before the mismatch and continue matching with nextCp.
598 if(skipped == NULL || skipped->isEmpty()) {
599 if(skipped == NULL) {
600 skipped = new SkippedState();
601 if(skipped == NULL) {
602 errorCode = U_MEMORY_ALLOCATION_ERROR;
603 return 0;
604 }
605 }
606 suffixes.reset();
607 if(lookAhead > 2) {
608 // Replay the partial match so far.
609 backwardNumCodePoints(lookAhead, errorCode);
610 suffixes.firstForCodePoint(nextCodePoint(errorCode));
611 for(int32_t i = 3; i < lookAhead; ++i) {
612 suffixes.nextForCodePoint(nextCodePoint(errorCode));
613 }
614 // Skip c (which did not match) and nextCp (which we will try now).
615 forwardNumCodePoints(2, errorCode);
616 }
617 skipped->saveTrieState(suffixes);
618 } else {
619 // Reset to the trie state before the failed match of c.
620 skipped->resetToTrieState(suffixes);
621 }
622
623 skipped->setFirstSkipped(c);
624 // Number of code points read since the last match (at this point: c and nextCp).
625 int32_t sinceMatch = 2;
626 c = nextCp;
627 for(;;) {
628 UStringTrieResult match;
629 // "If C is not blocked from S, find if S + C has a match in the table." (S2.1.2)
630 if(prevCC < (fcd16 >> 8) && USTRINGTRIE_HAS_VALUE(match = suffixes.nextForCodePoint(c))) {
631 // "If there is a match, replace S by S + C, and remove C." (S2.1.3)
632 // Keep prevCC unchanged.
633 ce32 = (uint32_t)suffixes.getValue();
634 sinceMatch = 0;
635 skipped->recordMatch();
636 if(!USTRINGTRIE_HAS_NEXT(match)) { break; }
637 skipped->saveTrieState(suffixes);
638 } else {
639 // No match for "S + C", skip C.
640 skipped->skip(c);
641 skipped->resetToTrieState(suffixes);
642 prevCC = (uint8_t)fcd16;
643 }
644 if((c = nextSkippedCodePoint(errorCode)) < 0) { break; }
645 ++sinceMatch;
646 fcd16 = d->getFCD16(c);
647 if(fcd16 <= 0xff) {
648 // The next code point after c is a starter (S2.1.1 "process each non-starter").
649 break;
650 }
651 }
652 backwardNumSkipped(sinceMatch, errorCode);
653 UBool isTopDiscontiguous = skipped->isEmpty();
654 skipped->replaceMatch();
655 if(isTopDiscontiguous && !skipped->isEmpty()) {
656 // We did get a match after skipping one or more combining marks,
657 // and we are not in a recursive discontiguous contraction.
658 // Append CEs from the contraction ce32
659 // and then from the combining marks that we skipped before the match.
660 c = U_SENTINEL;
661 for(;;) {
662 appendCEsFromCE32(d, c, ce32, TRUE, errorCode);
663 // Fetch CE32s for skipped combining marks from the normal data, with fallback,
664 // rather than from the CollationData where we found the contraction.
665 if(!skipped->hasNext()) { break; }
666 c = skipped->next();
667 ce32 = getDataCE32(c);
668 if(ce32 == Collation::FALLBACK_CE32) {
669 d = data->base;
670 ce32 = d->getCE32(c);
671 } else {
672 d = data;
673 }
674 // Note: A nested discontiguous-contraction match
675 // replaces consumed combining marks with newly skipped ones
676 // and resets the reading position to the beginning.
677 }
678 skipped->clear();
679 ce32 = Collation::NO_CE32; // Signal to the caller that the result is in the ceBuffer.
680 }
681 return ce32;
682}
683
684void
685CollationIterator::appendNumericCEs(uint32_t ce32, UBool forward, UErrorCode &errorCode) {
686 // Collect digits.
687 CharString digits;
688 if(forward) {
689 for(;;) {
690 char digit = Collation::digitFromCE32(ce32);
691 digits.append(digit, errorCode);
692 if(numCpFwd == 0) { break; }
693 UChar32 c = nextCodePoint(errorCode);
694 if(c < 0) { break; }
695 ce32 = data->getCE32(c);
696 if(ce32 == Collation::FALLBACK_CE32) {
697 ce32 = data->base->getCE32(c);
698 }
699 if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) {
700 backwardNumCodePoints(1, errorCode);
701 break;
702 }
703 if(numCpFwd > 0) { --numCpFwd; }
704 }
705 } else {
706 for(;;) {
707 char digit = Collation::digitFromCE32(ce32);
708 digits.append(digit, errorCode);
709 UChar32 c = previousCodePoint(errorCode);
710 if(c < 0) { break; }
711 ce32 = data->getCE32(c);
712 if(ce32 == Collation::FALLBACK_CE32) {
713 ce32 = data->base->getCE32(c);
714 }
715 if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) {
716 forwardNumCodePoints(1, errorCode);
717 break;
718 }
719 }
720 // Reverse the digit string.
721 char *p = digits.data();
722 char *q = p + digits.length() - 1;
723 while(p < q) {
724 char digit = *p;
725 *p++ = *q;
726 *q-- = digit;
727 }
728 }
729 if(U_FAILURE(errorCode)) { return; }
730 int32_t pos = 0;
731 do {
732 // Skip leading zeros.
733 while(pos < (digits.length() - 1) && digits[pos] == 0) { ++pos; }
734 // Write a sequence of CEs for at most 254 digits at a time.
735 int32_t segmentLength = digits.length() - pos;
736 if(segmentLength > 254) { segmentLength = 254; }
737 appendNumericSegmentCEs(digits.data() + pos, segmentLength, errorCode);
738 pos += segmentLength;
739 } while(U_SUCCESS(errorCode) && pos < digits.length());
740}
741
742void
743CollationIterator::appendNumericSegmentCEs(const char *digits, int32_t length, UErrorCode &errorCode) {
744 U_ASSERT(1 <= length && length <= 254);
745 U_ASSERT(length == 1 || digits[0] != 0);
746 uint32_t numericPrimary = data->numericPrimary;
747 // Note: We use primary byte values 2..255: digits are not compressible.
748 if(length <= 7) {
749 // Very dense encoding for small numbers.
750 int32_t value = digits[0];
751 for(int32_t i = 1; i < length; ++i) {
752 value = value * 10 + digits[i];
753 }
754 // Primary weight second byte values:
755 // 74 byte values 2.. 75 for small numbers in two-byte primary weights.
756 // 40 byte values 76..115 for medium numbers in three-byte primary weights.
757 // 16 byte values 116..131 for large numbers in four-byte primary weights.
758 // 124 byte values 132..255 for very large numbers with 4..127 digit pairs.
759 int32_t firstByte = 2;
760 int32_t numBytes = 74;
761 if(value < numBytes) {
762 // Two-byte primary for 0..73, good for day & month numbers etc.
763 uint32_t primary = numericPrimary | ((firstByte + value) << 16);
764 ceBuffer.append(Collation::makeCE(primary), errorCode);
765 return;
766 }
767 value -= numBytes;
768 firstByte += numBytes;
769 numBytes = 40;
770 if(value < numBytes * 254) {
771 // Three-byte primary for 74..10233=74+40*254-1, good for year numbers and more.
772 uint32_t primary = numericPrimary |
773 ((firstByte + value / 254) << 16) | ((2 + value % 254) << 8);
774 ceBuffer.append(Collation::makeCE(primary), errorCode);
775 return;
776 }
777 value -= numBytes * 254;
778 firstByte += numBytes;
779 numBytes = 16;
780 if(value < numBytes * 254 * 254) {
781 // Four-byte primary for 10234..1042489=10234+16*254*254-1.
782 uint32_t primary = numericPrimary | (2 + value % 254);
783 value /= 254;
784 primary |= (2 + value % 254) << 8;
785 value /= 254;
786 primary |= (firstByte + value % 254) << 16;
787 ceBuffer.append(Collation::makeCE(primary), errorCode);
788 return;
789 }
790 // original value > 1042489
791 }
792 U_ASSERT(length >= 7);
793
794 // The second primary byte value 132..255 indicates the number of digit pairs (4..127),
795 // then we generate primary bytes with those pairs.
796 // Omit trailing 00 pairs.
797 // Decrement the value for the last pair.
798
799 // Set the exponent. 4 pairs->132, 5 pairs->133, ..., 127 pairs->255.
800 int32_t numPairs = (length + 1) / 2;
801 uint32_t primary = numericPrimary | ((132 - 4 + numPairs) << 16);
802 // Find the length without trailing 00 pairs.
803 while(digits[length - 1] == 0 && digits[length - 2] == 0) {
804 length -= 2;
805 }
806 // Read the first pair.
807 uint32_t pair;
808 int32_t pos;
809 if(length & 1) {
810 // Only "half a pair" if we have an odd number of digits.
811 pair = digits[0];
812 pos = 1;
813 } else {
814 pair = digits[0] * 10 + digits[1];
815 pos = 2;
816 }
817 pair = 11 + 2 * pair;
818 // Add the pairs of digits between pos and length.
819 int32_t shift = 8;
820 while(pos < length) {
821 if(shift == 0) {
822 // Every three pairs/bytes we need to store a 4-byte-primary CE
823 // and start with a new CE with the '0' primary lead byte.
824 primary |= pair;
825 ceBuffer.append(Collation::makeCE(primary), errorCode);
826 primary = numericPrimary;
827 shift = 16;
828 } else {
829 primary |= pair << shift;
830 shift -= 8;
831 }
832 pair = 11 + 2 * (digits[pos] * 10 + digits[pos + 1]);
833 pos += 2;
834 }
835 primary |= (pair - 1) << shift;
836 ceBuffer.append(Collation::makeCE(primary), errorCode);
837}
838
839int64_t
840CollationIterator::previousCE(UVector32 &offsets, UErrorCode &errorCode) {
841 if(ceBuffer.length > 0) {
842 // Return the previous buffered CE.
843 return ceBuffer.get(--ceBuffer.length);
844 }
845 offsets.removeAllElements();
846 int32_t limitOffset = getOffset();
847 UChar32 c = previousCodePoint(errorCode);
848 if(c < 0) { return Collation::NO_CE; }
849 if(data->isUnsafeBackward(c, isNumeric)) {
850 return previousCEUnsafe(c, offsets, errorCode);
851 }
852 // Simple, safe-backwards iteration:
853 // Get a CE going backwards, handle prefixes but no contractions.
854 uint32_t ce32 = data->getCE32(c);
855 const CollationData *d;
856 if(ce32 == Collation::FALLBACK_CE32) {
857 d = data->base;
858 ce32 = d->getCE32(c);
859 } else {
860 d = data;
861 }
862 if(Collation::isSimpleOrLongCE32(ce32)) {
863 return Collation::ceFromCE32(ce32);
864 }
865 appendCEsFromCE32(d, c, ce32, FALSE, errorCode);
866 if(U_SUCCESS(errorCode)) {
867 if(ceBuffer.length > 1) {
868 offsets.addElement(getOffset(), errorCode);
869 // For an expansion, the offset of each non-initial CE is the limit offset,
870 // consistent with forward iteration.
871 while(offsets.size() <= ceBuffer.length) {
872 offsets.addElement(limitOffset, errorCode);
873 };
874 }
875 return ceBuffer.get(--ceBuffer.length);
876 } else {
877 return Collation::NO_CE_PRIMARY;
878 }
879}
880
881int64_t
882CollationIterator::previousCEUnsafe(UChar32 c, UVector32 &offsets, UErrorCode &errorCode) {
883 // We just move through the input counting safe and unsafe code points
884 // without collecting the unsafe-backward substring into a buffer and
885 // switching to it.
886 // This is to keep the logic simple. Otherwise we would have to handle
887 // prefix matching going before the backward buffer, switching
888 // to iteration and back, etc.
889 // In the most important case of iterating over a normal string,
890 // reading from the string itself is already maximally fast.
891 // The only drawback there is that after getting the CEs we always
892 // skip backward to the safe character rather than switching out
893 // of a backwardBuffer.
894 // But this should not be the common case for previousCE(),
895 // and correctness and maintainability are more important than
896 // complex optimizations.
897 // Find the first safe character before c.
898 int32_t numBackward = 1;
899 while((c = previousCodePoint(errorCode)) >= 0) {
900 ++numBackward;
901 if(!data->isUnsafeBackward(c, isNumeric)) {
902 break;
903 }
904 }
905 // Set the forward iteration limit.
906 // Note: This counts code points.
907 // We cannot enforce a limit in the middle of a surrogate pair or similar.
908 numCpFwd = numBackward;
909 // Reset the forward iterator.
910 cesIndex = 0;
911 U_ASSERT(ceBuffer.length == 0);
912 // Go forward and collect the CEs.
913 int32_t offset = getOffset();
914 while(numCpFwd > 0) {
915 // nextCE() normally reads one code point.
916 // Contraction matching and digit specials read more and check numCpFwd.
917 --numCpFwd;
918 // Append one or more CEs to the ceBuffer.
919 (void)nextCE(errorCode);
920 U_ASSERT(U_FAILURE(errorCode) || ceBuffer.get(ceBuffer.length - 1) != Collation::NO_CE);
921 // No need to loop for getting each expansion CE from nextCE().
922 cesIndex = ceBuffer.length;
923 // However, we need to write an offset for each CE.
924 // This is for CollationElementIterator::getOffset() to return
925 // intermediate offsets from the unsafe-backwards segment.
926 U_ASSERT(offsets.size() < ceBuffer.length);
927 offsets.addElement(offset, errorCode);
928 // For an expansion, the offset of each non-initial CE is the limit offset,
929 // consistent with forward iteration.
930 offset = getOffset();
931 while(offsets.size() < ceBuffer.length) {
932 offsets.addElement(offset, errorCode);
933 };
934 }
935 U_ASSERT(offsets.size() == ceBuffer.length);
936 // End offset corresponding to just after the unsafe-backwards segment.
937 offsets.addElement(offset, errorCode);
938 // Reset the forward iteration limit
939 // and move backward to before the segment for which we fetched CEs.
940 numCpFwd = -1;
941 backwardNumCodePoints(numBackward, errorCode);
942 // Use the collected CEs and return the last one.
943 cesIndex = 0; // Avoid cesIndex > ceBuffer.length when that gets decremented.
944 if(U_SUCCESS(errorCode)) {
945 return ceBuffer.get(--ceBuffer.length);
946 } else {
947 return Collation::NO_CE_PRIMARY;
948 }
949}
950
951U_NAMESPACE_END
952
953#endif // !UCONFIG_NO_COLLATION