]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/normalizer2impl.cpp
ICU-531.48.tar.gz
[apple/icu.git] / icuSources / common / normalizer2impl.cpp
CommitLineData
729e4ab9
A
1/*
2*******************************************************************************
3*
57a6839d 4* Copyright (C) 2009-2014, International Business Machines
729e4ab9
A
5* Corporation and others. All Rights Reserved.
6*
7*******************************************************************************
8* file name: normalizer2impl.cpp
9* encoding: US-ASCII
10* tab size: 8 (not used)
11* indentation:4
12*
13* created on: 2009nov22
14* created by: Markus W. Scherer
15*/
16
17#include "unicode/utypes.h"
18
19#if !UCONFIG_NO_NORMALIZATION
20
21#include "unicode/normalizer2.h"
22#include "unicode/udata.h"
23#include "unicode/ustring.h"
4388f060 24#include "unicode/utf16.h"
729e4ab9
A
25#include "cmemory.h"
26#include "mutex.h"
27#include "normalizer2impl.h"
4388f060 28#include "putilimp.h"
729e4ab9 29#include "uassert.h"
729e4ab9
A
30#include "uset_imp.h"
31#include "utrie2.h"
32#include "uvector.h"
33
34U_NAMESPACE_BEGIN
35
36// ReorderingBuffer -------------------------------------------------------- ***
37
38UBool ReorderingBuffer::init(int32_t destCapacity, UErrorCode &errorCode) {
39 int32_t length=str.length();
40 start=str.getBuffer(destCapacity);
41 if(start==NULL) {
42 // getBuffer() already did str.setToBogus()
43 errorCode=U_MEMORY_ALLOCATION_ERROR;
44 return FALSE;
45 }
46 limit=start+length;
47 remainingCapacity=str.getCapacity()-length;
48 reorderStart=start;
49 if(start==limit) {
50 lastCC=0;
51 } else {
52 setIterator();
53 lastCC=previousCC();
54 // Set reorderStart after the last code point with cc<=1 if there is one.
55 if(lastCC>1) {
56 while(previousCC()>1) {}
57 }
58 reorderStart=codePointLimit;
59 }
60 return TRUE;
61}
62
63UBool ReorderingBuffer::equals(const UChar *otherStart, const UChar *otherLimit) const {
64 int32_t length=(int32_t)(limit-start);
65 return
66 length==(int32_t)(otherLimit-otherStart) &&
67 0==u_memcmp(start, otherStart, length);
68}
69
70UBool ReorderingBuffer::appendSupplementary(UChar32 c, uint8_t cc, UErrorCode &errorCode) {
71 if(remainingCapacity<2 && !resize(2, errorCode)) {
72 return FALSE;
73 }
74 if(lastCC<=cc || cc==0) {
75 limit[0]=U16_LEAD(c);
76 limit[1]=U16_TRAIL(c);
77 limit+=2;
78 lastCC=cc;
79 if(cc<=1) {
80 reorderStart=limit;
81 }
82 } else {
83 insert(c, cc);
84 }
85 remainingCapacity-=2;
86 return TRUE;
87}
88
89UBool ReorderingBuffer::append(const UChar *s, int32_t length,
90 uint8_t leadCC, uint8_t trailCC,
91 UErrorCode &errorCode) {
92 if(length==0) {
93 return TRUE;
94 }
95 if(remainingCapacity<length && !resize(length, errorCode)) {
96 return FALSE;
97 }
98 remainingCapacity-=length;
99 if(lastCC<=leadCC || leadCC==0) {
100 if(trailCC<=1) {
101 reorderStart=limit+length;
102 } else if(leadCC<=1) {
103 reorderStart=limit+1; // Ok if not a code point boundary.
104 }
105 const UChar *sLimit=s+length;
106 do { *limit++=*s++; } while(s!=sLimit);
107 lastCC=trailCC;
108 } else {
109 int32_t i=0;
110 UChar32 c;
111 U16_NEXT(s, i, length, c);
112 insert(c, leadCC); // insert first code point
113 while(i<length) {
114 U16_NEXT(s, i, length, c);
115 if(i<length) {
116 // s must be in NFD, otherwise we need to use getCC().
117 leadCC=Normalizer2Impl::getCCFromYesOrMaybe(impl.getNorm16(c));
118 } else {
119 leadCC=trailCC;
120 }
121 append(c, leadCC, errorCode);
122 }
123 }
124 return TRUE;
125}
126
127UBool ReorderingBuffer::appendZeroCC(UChar32 c, UErrorCode &errorCode) {
128 int32_t cpLength=U16_LENGTH(c);
129 if(remainingCapacity<cpLength && !resize(cpLength, errorCode)) {
130 return FALSE;
131 }
132 remainingCapacity-=cpLength;
133 if(cpLength==1) {
134 *limit++=(UChar)c;
135 } else {
136 limit[0]=U16_LEAD(c);
137 limit[1]=U16_TRAIL(c);
138 limit+=2;
139 }
140 lastCC=0;
141 reorderStart=limit;
142 return TRUE;
143}
144
145UBool ReorderingBuffer::appendZeroCC(const UChar *s, const UChar *sLimit, UErrorCode &errorCode) {
146 if(s==sLimit) {
147 return TRUE;
148 }
149 int32_t length=(int32_t)(sLimit-s);
150 if(remainingCapacity<length && !resize(length, errorCode)) {
151 return FALSE;
152 }
153 u_memcpy(limit, s, length);
154 limit+=length;
155 remainingCapacity-=length;
156 lastCC=0;
157 reorderStart=limit;
158 return TRUE;
159}
160
161void ReorderingBuffer::remove() {
162 reorderStart=limit=start;
163 remainingCapacity=str.getCapacity();
164 lastCC=0;
165}
166
167void ReorderingBuffer::removeSuffix(int32_t suffixLength) {
168 if(suffixLength<(limit-start)) {
169 limit-=suffixLength;
170 remainingCapacity+=suffixLength;
171 } else {
172 limit=start;
173 remainingCapacity=str.getCapacity();
174 }
175 lastCC=0;
176 reorderStart=limit;
177}
178
179UBool ReorderingBuffer::resize(int32_t appendLength, UErrorCode &errorCode) {
180 int32_t reorderStartIndex=(int32_t)(reorderStart-start);
181 int32_t length=(int32_t)(limit-start);
182 str.releaseBuffer(length);
183 int32_t newCapacity=length+appendLength;
184 int32_t doubleCapacity=2*str.getCapacity();
185 if(newCapacity<doubleCapacity) {
186 newCapacity=doubleCapacity;
187 }
188 if(newCapacity<256) {
189 newCapacity=256;
190 }
191 start=str.getBuffer(newCapacity);
192 if(start==NULL) {
193 // getBuffer() already did str.setToBogus()
194 errorCode=U_MEMORY_ALLOCATION_ERROR;
195 return FALSE;
196 }
197 reorderStart=start+reorderStartIndex;
198 limit=start+length;
199 remainingCapacity=str.getCapacity()-length;
200 return TRUE;
201}
202
203void ReorderingBuffer::skipPrevious() {
204 codePointLimit=codePointStart;
205 UChar c=*--codePointStart;
206 if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(*(codePointStart-1))) {
207 --codePointStart;
208 }
209}
210
211uint8_t ReorderingBuffer::previousCC() {
212 codePointLimit=codePointStart;
213 if(reorderStart>=codePointStart) {
214 return 0;
215 }
216 UChar32 c=*--codePointStart;
217 if(c<Normalizer2Impl::MIN_CCC_LCCC_CP) {
218 return 0;
219 }
220
221 UChar c2;
222 if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(c2=*(codePointStart-1))) {
223 --codePointStart;
224 c=U16_GET_SUPPLEMENTARY(c2, c);
225 }
226 return Normalizer2Impl::getCCFromYesOrMaybe(impl.getNorm16(c));
227}
228
229// Inserts c somewhere before the last character.
230// Requires 0<cc<lastCC which implies reorderStart<limit.
231void ReorderingBuffer::insert(UChar32 c, uint8_t cc) {
232 for(setIterator(), skipPrevious(); previousCC()>cc;) {}
233 // insert c at codePointLimit, after the character with prevCC<=cc
234 UChar *q=limit;
235 UChar *r=limit+=U16_LENGTH(c);
236 do {
237 *--r=*--q;
238 } while(codePointLimit!=q);
239 writeCodePoint(q, c);
240 if(cc<=1) {
241 reorderStart=r;
242 }
243}
244
245// Normalizer2Impl --------------------------------------------------------- ***
246
247struct CanonIterData : public UMemory {
248 CanonIterData(UErrorCode &errorCode);
249 ~CanonIterData();
250 void addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode);
251 UTrie2 *trie;
252 UVector canonStartSets; // contains UnicodeSet *
253};
254
255Normalizer2Impl::~Normalizer2Impl() {
256 udata_close(memory);
257 utrie2_close(normTrie);
57a6839d 258 delete fCanonIterData;
729e4ab9
A
259}
260
261UBool U_CALLCONV
262Normalizer2Impl::isAcceptable(void *context,
263 const char * /* type */, const char * /*name*/,
264 const UDataInfo *pInfo) {
265 if(
266 pInfo->size>=20 &&
267 pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
268 pInfo->charsetFamily==U_CHARSET_FAMILY &&
269 pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */
270 pInfo->dataFormat[1]==0x72 &&
271 pInfo->dataFormat[2]==0x6d &&
272 pInfo->dataFormat[3]==0x32 &&
4388f060 273 pInfo->formatVersion[0]==2
729e4ab9
A
274 ) {
275 Normalizer2Impl *me=(Normalizer2Impl *)context;
276 uprv_memcpy(me->dataVersion, pInfo->dataVersion, 4);
277 return TRUE;
278 } else {
279 return FALSE;
280 }
281}
282
283void
284Normalizer2Impl::load(const char *packageName, const char *name, UErrorCode &errorCode) {
285 if(U_FAILURE(errorCode)) {
286 return;
287 }
288 memory=udata_openChoice(packageName, "nrm", name, isAcceptable, this, &errorCode);
289 if(U_FAILURE(errorCode)) {
290 return;
291 }
292 const uint8_t *inBytes=(const uint8_t *)udata_getMemory(memory);
293 const int32_t *inIndexes=(const int32_t *)inBytes;
294 int32_t indexesLength=inIndexes[IX_NORM_TRIE_OFFSET]/4;
295 if(indexesLength<=IX_MIN_MAYBE_YES) {
296 errorCode=U_INVALID_FORMAT_ERROR; // Not enough indexes.
297 return;
298 }
299
300 minDecompNoCP=inIndexes[IX_MIN_DECOMP_NO_CP];
301 minCompNoMaybeCP=inIndexes[IX_MIN_COMP_NO_MAYBE_CP];
302
303 minYesNo=inIndexes[IX_MIN_YES_NO];
4388f060 304 minYesNoMappingsOnly=inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY];
729e4ab9
A
305 minNoNo=inIndexes[IX_MIN_NO_NO];
306 limitNoNo=inIndexes[IX_LIMIT_NO_NO];
307 minMaybeYes=inIndexes[IX_MIN_MAYBE_YES];
308
309 int32_t offset=inIndexes[IX_NORM_TRIE_OFFSET];
310 int32_t nextOffset=inIndexes[IX_EXTRA_DATA_OFFSET];
311 normTrie=utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS,
312 inBytes+offset, nextOffset-offset, NULL,
313 &errorCode);
314 if(U_FAILURE(errorCode)) {
315 return;
316 }
317
318 offset=nextOffset;
4388f060 319 nextOffset=inIndexes[IX_SMALL_FCD_OFFSET];
729e4ab9
A
320 maybeYesCompositions=(const uint16_t *)(inBytes+offset);
321 extraData=maybeYesCompositions+(MIN_NORMAL_MAYBE_YES-minMaybeYes);
4388f060
A
322
323 // smallFCD: new in formatVersion 2
324 offset=nextOffset;
325 smallFCD=inBytes+offset;
326
327 // Build tccc180[].
328 // gennorm2 enforces lccc=0 for c<MIN_CCC_LCCC_CP=U+0300.
329 uint8_t bits=0;
330 for(UChar c=0; c<0x180; bits>>=1) {
331 if((c&0xff)==0) {
332 bits=smallFCD[c>>8]; // one byte per 0x100 code points
333 }
334 if(bits&1) {
335 for(int i=0; i<0x20; ++i, ++c) {
336 tccc180[c]=(uint8_t)getFCD16FromNormData(c);
337 }
338 } else {
339 uprv_memset(tccc180+c, 0, 0x20);
340 c+=0x20;
341 }
342 }
729e4ab9
A
343}
344
345uint8_t Normalizer2Impl::getTrailCCFromCompYesAndZeroCC(const UChar *cpStart, const UChar *cpLimit) const {
346 UChar32 c;
347 if(cpStart==(cpLimit-1)) {
348 c=*cpStart;
349 } else {
350 c=U16_GET_SUPPLEMENTARY(cpStart[0], cpStart[1]);
351 }
352 uint16_t prevNorm16=getNorm16(c);
353 if(prevNorm16<=minYesNo) {
354 return 0; // yesYes and Hangul LV/LVT have ccc=tccc=0
355 } else {
356 return (uint8_t)(*getMapping(prevNorm16)>>8); // tccc from yesNo
357 }
358}
359
57a6839d
A
360namespace {
361
362class LcccContext {
363public:
364 LcccContext(const Normalizer2Impl &ni, UnicodeSet &s) : impl(ni), set(s) {}
365
366 void handleRange(UChar32 start, UChar32 end, uint16_t norm16) {
367 if(impl.isAlgorithmicNoNo(norm16)) {
368 // Range of code points with same-norm16-value algorithmic decompositions.
369 // They might have different non-zero FCD16 values.
370 do {
371 uint16_t fcd16=impl.getFCD16(start);
372 if(fcd16>0xff) { set.add(start); }
373 } while(++start<=end);
374 } else {
375 uint16_t fcd16=impl.getFCD16(start);
376 if(fcd16>0xff) { set.add(start, end); }
377 }
378 }
379
380private:
381 const Normalizer2Impl &impl;
382 UnicodeSet &set;
383};
384
385struct PropertyStartsContext {
386 PropertyStartsContext(const Normalizer2Impl &ni, const USetAdder *adder)
387 : impl(ni), sa(adder) {}
388
389 const Normalizer2Impl &impl;
390 const USetAdder *sa;
391};
392
393} // namespace
394
729e4ab9
A
395U_CDECL_BEGIN
396
57a6839d
A
397static UBool U_CALLCONV
398enumLcccRange(const void *context, UChar32 start, UChar32 end, uint32_t value) {
399 ((LcccContext *)context)->handleRange(start, end, (uint16_t)value);
400 return TRUE;
401}
402
403static UBool U_CALLCONV
404enumNorm16PropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) {
405 /* add the start code point to the USet */
406 const PropertyStartsContext *ctx=(const PropertyStartsContext *)context;
407 const USetAdder *sa=ctx->sa;
408 sa->add(sa->set, start);
409 if(start!=end && ctx->impl.isAlgorithmicNoNo((uint16_t)value)) {
410 // Range of code points with same-norm16-value algorithmic decompositions.
411 // They might have different non-zero FCD16 values.
412 uint16_t prevFCD16=ctx->impl.getFCD16(start);
413 while(++start<=end) {
414 uint16_t fcd16=ctx->impl.getFCD16(start);
415 if(fcd16!=prevFCD16) {
416 sa->add(sa->set, start);
417 prevFCD16=fcd16;
418 }
419 }
420 }
421 return TRUE;
422}
423
729e4ab9
A
424static UBool U_CALLCONV
425enumPropertyStartsRange(const void *context, UChar32 start, UChar32 /*end*/, uint32_t /*value*/) {
426 /* add the start code point to the USet */
427 const USetAdder *sa=(const USetAdder *)context;
428 sa->add(sa->set, start);
429 return TRUE;
430}
431
432static uint32_t U_CALLCONV
433segmentStarterMapper(const void * /*context*/, uint32_t value) {
434 return value&CANON_NOT_SEGMENT_STARTER;
435}
436
437U_CDECL_END
438
57a6839d
A
439void
440Normalizer2Impl::addLcccChars(UnicodeSet &set) const {
441 /* add the start code point of each same-value range of each trie */
442 LcccContext context(*this, set);
443 utrie2_enum(normTrie, NULL, enumLcccRange, &context);
444}
445
729e4ab9
A
446void
447Normalizer2Impl::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const {
448 /* add the start code point of each same-value range of each trie */
57a6839d
A
449 PropertyStartsContext context(*this, sa);
450 utrie2_enum(normTrie, NULL, enumNorm16PropertyStartsRange, &context);
729e4ab9
A
451
452 /* add Hangul LV syllables and LV+1 because of skippables */
453 for(UChar c=Hangul::HANGUL_BASE; c<Hangul::HANGUL_LIMIT; c+=Hangul::JAMO_T_COUNT) {
454 sa->add(sa->set, c);
455 sa->add(sa->set, c+1);
456 }
457 sa->add(sa->set, Hangul::HANGUL_LIMIT); /* add Hangul+1 to continue with other properties */
458}
459
460void
461Normalizer2Impl::addCanonIterPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const {
462 /* add the start code point of each same-value range of the canonical iterator data trie */
463 if(ensureCanonIterData(errorCode)) {
464 // currently only used for the SEGMENT_STARTER property
57a6839d 465 utrie2_enum(fCanonIterData->trie, segmentStarterMapper, enumPropertyStartsRange, sa);
729e4ab9
A
466 }
467}
468
469const UChar *
470Normalizer2Impl::copyLowPrefixFromNulTerminated(const UChar *src,
471 UChar32 minNeedDataCP,
472 ReorderingBuffer *buffer,
473 UErrorCode &errorCode) const {
474 // Make some effort to support NUL-terminated strings reasonably.
475 // Take the part of the fast quick check loop that does not look up
476 // data and check the first part of the string.
477 // After this prefix, determine the string length to simplify the rest
478 // of the code.
479 const UChar *prevSrc=src;
480 UChar c;
481 while((c=*src++)<minNeedDataCP && c!=0) {}
482 // Back out the last character for full processing.
483 // Copy this prefix.
484 if(--src!=prevSrc) {
485 if(buffer!=NULL) {
486 buffer->appendZeroCC(prevSrc, src, errorCode);
487 }
488 }
489 return src;
490}
491
57a6839d
A
492UnicodeString &
493Normalizer2Impl::decompose(const UnicodeString &src, UnicodeString &dest,
494 UErrorCode &errorCode) const {
495 if(U_FAILURE(errorCode)) {
496 dest.setToBogus();
497 return dest;
498 }
499 const UChar *sArray=src.getBuffer();
500 if(&dest==&src || sArray==NULL) {
501 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
502 dest.setToBogus();
503 return dest;
504 }
505 decompose(sArray, sArray+src.length(), dest, src.length(), errorCode);
506 return dest;
507}
508
509void
510Normalizer2Impl::decompose(const UChar *src, const UChar *limit,
511 UnicodeString &dest,
512 int32_t destLengthEstimate,
513 UErrorCode &errorCode) const {
514 if(destLengthEstimate<0 && limit!=NULL) {
515 destLengthEstimate=(int32_t)(limit-src);
516 }
517 dest.remove();
518 ReorderingBuffer buffer(*this, dest);
519 if(buffer.init(destLengthEstimate, errorCode)) {
520 decompose(src, limit, &buffer, errorCode);
521 }
522}
523
729e4ab9
A
524// Dual functionality:
525// buffer!=NULL: normalize
526// buffer==NULL: isNormalized/spanQuickCheckYes
527const UChar *
528Normalizer2Impl::decompose(const UChar *src, const UChar *limit,
529 ReorderingBuffer *buffer,
530 UErrorCode &errorCode) const {
531 UChar32 minNoCP=minDecompNoCP;
532 if(limit==NULL) {
533 src=copyLowPrefixFromNulTerminated(src, minNoCP, buffer, errorCode);
534 if(U_FAILURE(errorCode)) {
535 return src;
536 }
537 limit=u_strchr(src, 0);
538 }
539
540 const UChar *prevSrc;
541 UChar32 c=0;
542 uint16_t norm16=0;
543
544 // only for quick check
545 const UChar *prevBoundary=src;
546 uint8_t prevCC=0;
547
548 for(;;) {
549 // count code units below the minimum or with irrelevant data for the quick check
550 for(prevSrc=src; src!=limit;) {
551 if( (c=*src)<minNoCP ||
552 isMostDecompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
553 ) {
554 ++src;
555 } else if(!U16_IS_SURROGATE(c)) {
556 break;
557 } else {
558 UChar c2;
559 if(U16_IS_SURROGATE_LEAD(c)) {
560 if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
561 c=U16_GET_SUPPLEMENTARY(c, c2);
562 }
563 } else /* trail surrogate */ {
564 if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
565 --src;
566 c=U16_GET_SUPPLEMENTARY(c2, c);
567 }
568 }
569 if(isMostDecompYesAndZeroCC(norm16=getNorm16(c))) {
570 src+=U16_LENGTH(c);
571 } else {
572 break;
573 }
574 }
575 }
576 // copy these code units all at once
577 if(src!=prevSrc) {
578 if(buffer!=NULL) {
579 if(!buffer->appendZeroCC(prevSrc, src, errorCode)) {
580 break;
581 }
582 } else {
583 prevCC=0;
584 prevBoundary=src;
585 }
586 }
587 if(src==limit) {
588 break;
589 }
590
591 // Check one above-minimum, relevant code point.
592 src+=U16_LENGTH(c);
593 if(buffer!=NULL) {
594 if(!decompose(c, norm16, *buffer, errorCode)) {
595 break;
596 }
597 } else {
598 if(isDecompYes(norm16)) {
599 uint8_t cc=getCCFromYesOrMaybe(norm16);
600 if(prevCC<=cc || cc==0) {
601 prevCC=cc;
602 if(cc<=1) {
603 prevBoundary=src;
604 }
605 continue;
606 }
607 }
608 return prevBoundary; // "no" or cc out of order
609 }
610 }
611 return src;
612}
613
614// Decompose a short piece of text which is likely to contain characters that
615// fail the quick check loop and/or where the quick check loop's overhead
616// is unlikely to be amortized.
617// Called by the compose() and makeFCD() implementations.
618UBool Normalizer2Impl::decomposeShort(const UChar *src, const UChar *limit,
619 ReorderingBuffer &buffer,
620 UErrorCode &errorCode) const {
621 while(src<limit) {
622 UChar32 c;
623 uint16_t norm16;
624 UTRIE2_U16_NEXT16(normTrie, src, limit, c, norm16);
625 if(!decompose(c, norm16, buffer, errorCode)) {
626 return FALSE;
627 }
628 }
629 return TRUE;
630}
631
632UBool Normalizer2Impl::decompose(UChar32 c, uint16_t norm16,
633 ReorderingBuffer &buffer,
634 UErrorCode &errorCode) const {
635 // Only loops for 1:1 algorithmic mappings.
636 for(;;) {
637 // get the decomposition and the lead and trail cc's
638 if(isDecompYes(norm16)) {
639 // c does not decompose
640 return buffer.append(c, getCCFromYesOrMaybe(norm16), errorCode);
641 } else if(isHangul(norm16)) {
642 // Hangul syllable: decompose algorithmically
643 UChar jamos[3];
644 return buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode);
645 } else if(isDecompNoAlgorithmic(norm16)) {
646 c=mapAlgorithmic(c, norm16);
647 norm16=getNorm16(c);
648 } else {
649 // c decomposes, get everything from the variable-length extra data
650 const uint16_t *mapping=getMapping(norm16);
4388f060 651 uint16_t firstUnit=*mapping;
729e4ab9
A
652 int32_t length=firstUnit&MAPPING_LENGTH_MASK;
653 uint8_t leadCC, trailCC;
654 trailCC=(uint8_t)(firstUnit>>8);
655 if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
4388f060 656 leadCC=(uint8_t)(*(mapping-1)>>8);
729e4ab9
A
657 } else {
658 leadCC=0;
659 }
4388f060 660 return buffer.append((const UChar *)mapping+1, length, leadCC, trailCC, errorCode);
729e4ab9
A
661 }
662 }
663}
664
665const UChar *
666Normalizer2Impl::getDecomposition(UChar32 c, UChar buffer[4], int32_t &length) const {
667 const UChar *decomp=NULL;
668 uint16_t norm16;
669 for(;;) {
670 if(c<minDecompNoCP || isDecompYes(norm16=getNorm16(c))) {
671 // c does not decompose
672 return decomp;
673 } else if(isHangul(norm16)) {
674 // Hangul syllable: decompose algorithmically
675 length=Hangul::decompose(c, buffer);
676 return buffer;
677 } else if(isDecompNoAlgorithmic(norm16)) {
678 c=mapAlgorithmic(c, norm16);
679 decomp=buffer;
680 length=0;
681 U16_APPEND_UNSAFE(buffer, length, c);
682 } else {
683 // c decomposes, get everything from the variable-length extra data
684 const uint16_t *mapping=getMapping(norm16);
4388f060
A
685 length=*mapping&MAPPING_LENGTH_MASK;
686 return (const UChar *)mapping+1;
687 }
688 }
689}
690
691// The capacity of the buffer must be 30=MAPPING_LENGTH_MASK-1
692// so that a raw mapping fits that consists of one unit ("rm0")
693// plus all but the first two code units of the normal mapping.
694// The maximum length of a normal mapping is 31=MAPPING_LENGTH_MASK.
695const UChar *
696Normalizer2Impl::getRawDecomposition(UChar32 c, UChar buffer[30], int32_t &length) const {
697 // We do not loop in this method because an algorithmic mapping itself
698 // becomes a final result rather than having to be decomposed recursively.
699 uint16_t norm16;
700 if(c<minDecompNoCP || isDecompYes(norm16=getNorm16(c))) {
701 // c does not decompose
702 return NULL;
703 } else if(isHangul(norm16)) {
704 // Hangul syllable: decompose algorithmically
705 Hangul::getRawDecomposition(c, buffer);
706 length=2;
707 return buffer;
708 } else if(isDecompNoAlgorithmic(norm16)) {
709 c=mapAlgorithmic(c, norm16);
710 length=0;
711 U16_APPEND_UNSAFE(buffer, length, c);
712 return buffer;
713 } else {
714 // c decomposes, get everything from the variable-length extra data
715 const uint16_t *mapping=getMapping(norm16);
716 uint16_t firstUnit=*mapping;
717 int32_t mLength=firstUnit&MAPPING_LENGTH_MASK; // length of normal mapping
718 if(firstUnit&MAPPING_HAS_RAW_MAPPING) {
719 // Read the raw mapping from before the firstUnit and before the optional ccc/lccc word.
720 // Bit 7=MAPPING_HAS_CCC_LCCC_WORD
721 const uint16_t *rawMapping=mapping-((firstUnit>>7)&1)-1;
722 uint16_t rm0=*rawMapping;
723 if(rm0<=MAPPING_LENGTH_MASK) {
724 length=rm0;
725 return (const UChar *)rawMapping-rm0;
726 } else {
727 // Copy the normal mapping and replace its first two code units with rm0.
728 buffer[0]=(UChar)rm0;
729 u_memcpy(buffer+1, (const UChar *)mapping+1+2, mLength-2);
730 length=mLength-1;
731 return buffer;
729e4ab9 732 }
4388f060
A
733 } else {
734 length=mLength;
735 return (const UChar *)mapping+1;
729e4ab9
A
736 }
737 }
738}
739
740void Normalizer2Impl::decomposeAndAppend(const UChar *src, const UChar *limit,
741 UBool doDecompose,
4388f060 742 UnicodeString &safeMiddle,
729e4ab9
A
743 ReorderingBuffer &buffer,
744 UErrorCode &errorCode) const {
4388f060 745 buffer.copyReorderableSuffixTo(safeMiddle);
729e4ab9
A
746 if(doDecompose) {
747 decompose(src, limit, &buffer, errorCode);
748 return;
749 }
750 // Just merge the strings at the boundary.
751 ForwardUTrie2StringIterator iter(normTrie, src, limit);
752 uint8_t firstCC, prevCC, cc;
753 firstCC=prevCC=cc=getCC(iter.next16());
754 while(cc!=0) {
755 prevCC=cc;
756 cc=getCC(iter.next16());
757 };
4388f060
A
758 if(limit==NULL) { // appendZeroCC() needs limit!=NULL
759 limit=u_strchr(iter.codePointStart, 0);
760 }
51004dcb
A
761
762 if (buffer.append(src, (int32_t)(iter.codePointStart-src), firstCC, prevCC, errorCode)) {
729e4ab9 763 buffer.appendZeroCC(iter.codePointStart, limit, errorCode);
51004dcb 764 }
729e4ab9
A
765}
766
767// Note: hasDecompBoundary() could be implemented as aliases to
768// hasFCDBoundaryBefore() and hasFCDBoundaryAfter()
769// at the cost of building the FCD trie for a decomposition normalizer.
770UBool Normalizer2Impl::hasDecompBoundary(UChar32 c, UBool before) const {
771 for(;;) {
772 if(c<minDecompNoCP) {
773 return TRUE;
774 }
775 uint16_t norm16=getNorm16(c);
776 if(isHangul(norm16) || isDecompYesAndZeroCC(norm16)) {
777 return TRUE;
778 } else if(norm16>MIN_NORMAL_MAYBE_YES) {
779 return FALSE; // ccc!=0
780 } else if(isDecompNoAlgorithmic(norm16)) {
781 c=mapAlgorithmic(c, norm16);
782 } else {
783 // c decomposes, get everything from the variable-length extra data
784 const uint16_t *mapping=getMapping(norm16);
4388f060 785 uint16_t firstUnit=*mapping;
729e4ab9
A
786 if((firstUnit&MAPPING_LENGTH_MASK)==0) {
787 return FALSE;
788 }
789 if(!before) {
790 // decomp after-boundary: same as hasFCDBoundaryAfter(),
791 // fcd16<=1 || trailCC==0
792 if(firstUnit>0x1ff) {
793 return FALSE; // trailCC>1
794 }
795 if(firstUnit<=0xff) {
796 return TRUE; // trailCC==0
797 }
798 // if(trailCC==1) test leadCC==0, same as checking for before-boundary
799 }
800 // TRUE if leadCC==0 (hasFCDBoundaryBefore())
4388f060 801 return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0;
729e4ab9
A
802 }
803 }
804}
805
806/*
807 * Finds the recomposition result for
808 * a forward-combining "lead" character,
809 * specified with a pointer to its compositions list,
810 * and a backward-combining "trail" character.
811 *
812 * If the lead and trail characters combine, then this function returns
813 * the following "compositeAndFwd" value:
814 * Bits 21..1 composite character
815 * Bit 0 set if the composite is a forward-combining starter
816 * otherwise it returns -1.
817 *
818 * The compositions list has (trail, compositeAndFwd) pair entries,
819 * encoded as either pairs or triples of 16-bit units.
820 * The last entry has the high bit of its first unit set.
821 *
822 * The list is sorted by ascending trail characters (there are no duplicates).
823 * A linear search is used.
824 *
825 * See normalizer2impl.h for a more detailed description
826 * of the compositions list format.
827 */
828int32_t Normalizer2Impl::combine(const uint16_t *list, UChar32 trail) {
829 uint16_t key1, firstUnit;
830 if(trail<COMP_1_TRAIL_LIMIT) {
831 // trail character is 0..33FF
832 // result entry may have 2 or 3 units
833 key1=(uint16_t)(trail<<1);
834 while(key1>(firstUnit=*list)) {
835 list+=2+(firstUnit&COMP_1_TRIPLE);
836 }
837 if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
838 if(firstUnit&COMP_1_TRIPLE) {
839 return ((int32_t)list[1]<<16)|list[2];
840 } else {
841 return list[1];
842 }
843 }
844 } else {
845 // trail character is 3400..10FFFF
846 // result entry has 3 units
847 key1=(uint16_t)(COMP_1_TRAIL_LIMIT+
848 (((trail>>COMP_1_TRAIL_SHIFT))&
849 ~COMP_1_TRIPLE));
850 uint16_t key2=(uint16_t)(trail<<COMP_2_TRAIL_SHIFT);
851 uint16_t secondUnit;
852 for(;;) {
853 if(key1>(firstUnit=*list)) {
854 list+=2+(firstUnit&COMP_1_TRIPLE);
855 } else if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
856 if(key2>(secondUnit=list[1])) {
857 if(firstUnit&COMP_1_LAST_TUPLE) {
858 break;
859 } else {
860 list+=3;
861 }
862 } else if(key2==(secondUnit&COMP_2_TRAIL_MASK)) {
863 return ((int32_t)(secondUnit&~COMP_2_TRAIL_MASK)<<16)|list[2];
864 } else {
865 break;
866 }
867 } else {
868 break;
869 }
870 }
871 }
872 return -1;
873}
874
875/**
876 * @param list some character's compositions list
877 * @param set recursively receives the composites from these compositions
878 */
879void Normalizer2Impl::addComposites(const uint16_t *list, UnicodeSet &set) const {
880 uint16_t firstUnit;
881 int32_t compositeAndFwd;
882 do {
883 firstUnit=*list;
884 if((firstUnit&COMP_1_TRIPLE)==0) {
885 compositeAndFwd=list[1];
886 list+=2;
887 } else {
888 compositeAndFwd=(((int32_t)list[1]&~COMP_2_TRAIL_MASK)<<16)|list[2];
889 list+=3;
890 }
891 UChar32 composite=compositeAndFwd>>1;
892 if((compositeAndFwd&1)!=0) {
893 addComposites(getCompositionsListForComposite(getNorm16(composite)), set);
894 }
895 set.add(composite);
896 } while((firstUnit&COMP_1_LAST_TUPLE)==0);
897}
898
899/*
900 * Recomposes the buffer text starting at recomposeStartIndex
901 * (which is in NFD - decomposed and canonically ordered),
902 * and truncates the buffer contents.
903 *
904 * Note that recomposition never lengthens the text:
905 * Any character consists of either one or two code units;
906 * a composition may contain at most one more code unit than the original starter,
907 * while the combining mark that is removed has at least one code unit.
908 */
909void Normalizer2Impl::recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex,
910 UBool onlyContiguous) const {
911 UChar *p=buffer.getStart()+recomposeStartIndex;
912 UChar *limit=buffer.getLimit();
913 if(p==limit) {
914 return;
915 }
916
917 UChar *starter, *pRemove, *q, *r;
918 const uint16_t *compositionsList;
919 UChar32 c, compositeAndFwd;
920 uint16_t norm16;
921 uint8_t cc, prevCC;
922 UBool starterIsSupplementary;
923
924 // Some of the following variables are not used until we have a forward-combining starter
925 // and are only initialized now to avoid compiler warnings.
926 compositionsList=NULL; // used as indicator for whether we have a forward-combining starter
927 starter=NULL;
928 starterIsSupplementary=FALSE;
929 prevCC=0;
930
931 for(;;) {
932 UTRIE2_U16_NEXT16(normTrie, p, limit, c, norm16);
933 cc=getCCFromYesOrMaybe(norm16);
934 if( // this character combines backward and
935 isMaybe(norm16) &&
936 // we have seen a starter that combines forward and
937 compositionsList!=NULL &&
938 // the backward-combining character is not blocked
939 (prevCC<cc || prevCC==0)
940 ) {
941 if(isJamoVT(norm16)) {
942 // c is a Jamo V/T, see if we can compose it with the previous character.
943 if(c<Hangul::JAMO_T_BASE) {
944 // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T.
945 UChar prev=(UChar)(*starter-Hangul::JAMO_L_BASE);
946 if(prev<Hangul::JAMO_L_COUNT) {
947 pRemove=p-1;
948 UChar syllable=(UChar)
949 (Hangul::HANGUL_BASE+
950 (prev*Hangul::JAMO_V_COUNT+(c-Hangul::JAMO_V_BASE))*
951 Hangul::JAMO_T_COUNT);
952 UChar t;
953 if(p!=limit && (t=(UChar)(*p-Hangul::JAMO_T_BASE))<Hangul::JAMO_T_COUNT) {
954 ++p;
955 syllable+=t; // The next character was a Jamo T.
956 }
957 *starter=syllable;
958 // remove the Jamo V/T
959 q=pRemove;
960 r=p;
961 while(r<limit) {
962 *q++=*r++;
963 }
964 limit=q;
965 p=pRemove;
966 }
967 }
968 /*
969 * No "else" for Jamo T:
970 * Since the input is in NFD, there are no Hangul LV syllables that
971 * a Jamo T could combine with.
972 * All Jamo Ts are combined above when handling Jamo Vs.
973 */
974 if(p==limit) {
975 break;
976 }
977 compositionsList=NULL;
978 continue;
979 } else if((compositeAndFwd=combine(compositionsList, c))>=0) {
980 // The starter and the combining mark (c) do combine.
981 UChar32 composite=compositeAndFwd>>1;
982
983 // Replace the starter with the composite, remove the combining mark.
984 pRemove=p-U16_LENGTH(c); // pRemove & p: start & limit of the combining mark
985 if(starterIsSupplementary) {
986 if(U_IS_SUPPLEMENTARY(composite)) {
987 // both are supplementary
988 starter[0]=U16_LEAD(composite);
989 starter[1]=U16_TRAIL(composite);
990 } else {
991 *starter=(UChar)composite;
992 // The composite is shorter than the starter,
993 // move the intermediate characters forward one.
994 starterIsSupplementary=FALSE;
995 q=starter+1;
996 r=q+1;
997 while(r<pRemove) {
998 *q++=*r++;
999 }
1000 --pRemove;
1001 }
1002 } else if(U_IS_SUPPLEMENTARY(composite)) {
1003 // The composite is longer than the starter,
1004 // move the intermediate characters back one.
1005 starterIsSupplementary=TRUE;
1006 ++starter; // temporarily increment for the loop boundary
1007 q=pRemove;
1008 r=++pRemove;
1009 while(starter<q) {
1010 *--r=*--q;
1011 }
1012 *starter=U16_TRAIL(composite);
1013 *--starter=U16_LEAD(composite); // undo the temporary increment
1014 } else {
1015 // both are on the BMP
1016 *starter=(UChar)composite;
1017 }
1018
1019 /* remove the combining mark by moving the following text over it */
1020 if(pRemove<p) {
1021 q=pRemove;
1022 r=p;
1023 while(r<limit) {
1024 *q++=*r++;
1025 }
1026 limit=q;
1027 p=pRemove;
1028 }
1029 // Keep prevCC because we removed the combining mark.
1030
1031 if(p==limit) {
1032 break;
1033 }
1034 // Is the composite a starter that combines forward?
1035 if(compositeAndFwd&1) {
1036 compositionsList=
1037 getCompositionsListForComposite(getNorm16(composite));
1038 } else {
1039 compositionsList=NULL;
1040 }
1041
1042 // We combined; continue with looking for compositions.
1043 continue;
1044 }
1045 }
1046
1047 // no combination this time
1048 prevCC=cc;
1049 if(p==limit) {
1050 break;
1051 }
1052
1053 // If c did not combine, then check if it is a starter.
1054 if(cc==0) {
1055 // Found a new starter.
1056 if((compositionsList=getCompositionsListForDecompYes(norm16))!=NULL) {
1057 // It may combine with something, prepare for it.
1058 if(U_IS_BMP(c)) {
1059 starterIsSupplementary=FALSE;
1060 starter=p-1;
1061 } else {
1062 starterIsSupplementary=TRUE;
1063 starter=p-2;
1064 }
1065 }
1066 } else if(onlyContiguous) {
1067 // FCC: no discontiguous compositions; any intervening character blocks.
1068 compositionsList=NULL;
1069 }
1070 }
1071 buffer.setReorderingLimit(limit);
1072}
1073
4388f060
A
1074UChar32
1075Normalizer2Impl::composePair(UChar32 a, UChar32 b) const {
1076 uint16_t norm16=getNorm16(a); // maps an out-of-range 'a' to inert norm16=0
1077 const uint16_t *list;
1078 if(isInert(norm16)) {
1079 return U_SENTINEL;
1080 } else if(norm16<minYesNoMappingsOnly) {
1081 if(isJamoL(norm16)) {
1082 b-=Hangul::JAMO_V_BASE;
1083 if(0<=b && b<Hangul::JAMO_V_COUNT) {
1084 return
1085 (Hangul::HANGUL_BASE+
1086 ((a-Hangul::JAMO_L_BASE)*Hangul::JAMO_V_COUNT+b)*
1087 Hangul::JAMO_T_COUNT);
1088 } else {
1089 return U_SENTINEL;
1090 }
1091 } else if(isHangul(norm16)) {
1092 b-=Hangul::JAMO_T_BASE;
1093 if(Hangul::isHangulWithoutJamoT(a) && 0<b && b<Hangul::JAMO_T_COUNT) { // not b==0!
1094 return a+b;
1095 } else {
1096 return U_SENTINEL;
1097 }
1098 } else {
1099 // 'a' has a compositions list in extraData
1100 list=extraData+norm16;
1101 if(norm16>minYesNo) { // composite 'a' has both mapping & compositions list
1102 list+= // mapping pointer
1103 1+ // +1 to skip the first unit with the mapping lenth
1104 (*list&MAPPING_LENGTH_MASK); // + mapping length
1105 }
1106 }
1107 } else if(norm16<minMaybeYes || MIN_NORMAL_MAYBE_YES<=norm16) {
1108 return U_SENTINEL;
1109 } else {
1110 list=maybeYesCompositions+norm16-minMaybeYes;
1111 }
1112 if(b<0 || 0x10ffff<b) { // combine(list, b) requires a valid code point b
1113 return U_SENTINEL;
1114 }
1115#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC
1116 return combine(list, b)>>1;
1117#else
1118 int32_t compositeAndFwd=combine(list, b);
1119 return compositeAndFwd>=0 ? compositeAndFwd>>1 : U_SENTINEL;
1120#endif
1121}
1122
729e4ab9
A
1123// Very similar to composeQuickCheck(): Make the same changes in both places if relevant.
1124// doCompose: normalize
1125// !doCompose: isNormalized (buffer must be empty and initialized)
1126UBool
1127Normalizer2Impl::compose(const UChar *src, const UChar *limit,
1128 UBool onlyContiguous,
1129 UBool doCompose,
1130 ReorderingBuffer &buffer,
1131 UErrorCode &errorCode) const {
1132 /*
1133 * prevBoundary points to the last character before the current one
1134 * that has a composition boundary before it with ccc==0 and quick check "yes".
1135 * Keeping track of prevBoundary saves us looking for a composition boundary
1136 * when we find a "no" or "maybe".
1137 *
1138 * When we back out from prevSrc back to prevBoundary,
1139 * then we also remove those same characters (which had been simply copied
1140 * or canonically-order-inserted) from the ReorderingBuffer.
1141 * Therefore, at all times, the [prevBoundary..prevSrc[ source units
1142 * must correspond 1:1 to destination units at the end of the destination buffer.
1143 */
1144 const UChar *prevBoundary=src;
1145 UChar32 minNoMaybeCP=minCompNoMaybeCP;
1146 if(limit==NULL) {
1147 src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP,
1148 doCompose ? &buffer : NULL,
1149 errorCode);
1150 if(U_FAILURE(errorCode)) {
1151 return FALSE;
1152 }
1153 if(prevBoundary<src) {
1154 // Set prevBoundary to the last character in the prefix.
1155 prevBoundary=src-1;
1156 }
1157 limit=u_strchr(src, 0);
1158 }
1159
1160 const UChar *prevSrc;
1161 UChar32 c=0;
1162 uint16_t norm16=0;
1163
1164 // only for isNormalized
1165 uint8_t prevCC=0;
1166
1167 for(;;) {
1168 // count code units below the minimum or with irrelevant data for the quick check
1169 for(prevSrc=src; src!=limit;) {
1170 if( (c=*src)<minNoMaybeCP ||
1171 isCompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
1172 ) {
1173 ++src;
1174 } else if(!U16_IS_SURROGATE(c)) {
1175 break;
1176 } else {
1177 UChar c2;
1178 if(U16_IS_SURROGATE_LEAD(c)) {
1179 if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
1180 c=U16_GET_SUPPLEMENTARY(c, c2);
1181 }
1182 } else /* trail surrogate */ {
1183 if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
1184 --src;
1185 c=U16_GET_SUPPLEMENTARY(c2, c);
1186 }
1187 }
1188 if(isCompYesAndZeroCC(norm16=getNorm16(c))) {
1189 src+=U16_LENGTH(c);
1190 } else {
1191 break;
1192 }
1193 }
1194 }
1195 // copy these code units all at once
1196 if(src!=prevSrc) {
1197 if(doCompose) {
1198 if(!buffer.appendZeroCC(prevSrc, src, errorCode)) {
1199 break;
1200 }
1201 } else {
1202 prevCC=0;
1203 }
1204 if(src==limit) {
1205 break;
1206 }
1207 // Set prevBoundary to the last character in the quick check loop.
1208 prevBoundary=src-1;
1209 if( U16_IS_TRAIL(*prevBoundary) && prevSrc<prevBoundary &&
1210 U16_IS_LEAD(*(prevBoundary-1))
1211 ) {
1212 --prevBoundary;
1213 }
1214 // The start of the current character (c).
1215 prevSrc=src;
1216 } else if(src==limit) {
1217 break;
1218 }
1219
1220 src+=U16_LENGTH(c);
1221 /*
1222 * isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
1223 * c is either a "noNo" (has a mapping) or a "maybeYes" (combines backward)
1224 * or has ccc!=0.
1225 * Check for Jamo V/T, then for regular characters.
1226 * c is not a Hangul syllable or Jamo L because those have "yes" properties.
1227 */
1228 if(isJamoVT(norm16) && prevBoundary!=prevSrc) {
1229 UChar prev=*(prevSrc-1);
1230 UBool needToDecompose=FALSE;
1231 if(c<Hangul::JAMO_T_BASE) {
1232 // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T.
1233 prev=(UChar)(prev-Hangul::JAMO_L_BASE);
1234 if(prev<Hangul::JAMO_L_COUNT) {
1235 if(!doCompose) {
1236 return FALSE;
1237 }
1238 UChar syllable=(UChar)
1239 (Hangul::HANGUL_BASE+
1240 (prev*Hangul::JAMO_V_COUNT+(c-Hangul::JAMO_V_BASE))*
1241 Hangul::JAMO_T_COUNT);
1242 UChar t;
1243 if(src!=limit && (t=(UChar)(*src-Hangul::JAMO_T_BASE))<Hangul::JAMO_T_COUNT) {
1244 ++src;
1245 syllable+=t; // The next character was a Jamo T.
1246 prevBoundary=src;
1247 buffer.setLastChar(syllable);
1248 continue;
1249 }
1250 // If we see L+V+x where x!=T then we drop to the slow path,
1251 // decompose and recompose.
1252 // This is to deal with NFKC finding normal L and V but a
1253 // compatibility variant of a T. We need to either fully compose that
1254 // combination here (which would complicate the code and may not work
1255 // with strange custom data) or use the slow path -- or else our replacing
1256 // two input characters (L+V) with one output character (LV syllable)
1257 // would violate the invariant that [prevBoundary..prevSrc[ has the same
1258 // length as what we appended to the buffer since prevBoundary.
1259 needToDecompose=TRUE;
1260 }
1261 } else if(Hangul::isHangulWithoutJamoT(prev)) {
1262 // c is a Jamo Trailing consonant,
1263 // compose with previous Hangul LV that does not contain a Jamo T.
1264 if(!doCompose) {
1265 return FALSE;
1266 }
1267 buffer.setLastChar((UChar)(prev+c-Hangul::JAMO_T_BASE));
1268 prevBoundary=src;
1269 continue;
1270 }
1271 if(!needToDecompose) {
1272 // The Jamo V/T did not compose into a Hangul syllable.
1273 if(doCompose) {
1274 if(!buffer.appendBMP((UChar)c, 0, errorCode)) {
1275 break;
1276 }
1277 } else {
1278 prevCC=0;
1279 }
1280 continue;
1281 }
1282 }
1283 /*
1284 * Source buffer pointers:
1285 *
1286 * all done quick check current char not yet
1287 * "yes" but (c) processed
1288 * may combine
1289 * forward
1290 * [-------------[-------------[-------------[-------------[
1291 * | | | | |
1292 * orig. src prevBoundary prevSrc src limit
1293 *
1294 *
1295 * Destination buffer pointers inside the ReorderingBuffer:
1296 *
1297 * all done might take not filled yet
1298 * characters for
1299 * reordering
1300 * [-------------[-------------[-------------[
1301 * | | | |
1302 * start reorderStart limit |
1303 * +remainingCap.+
1304 */
1305 if(norm16>=MIN_YES_YES_WITH_CC) {
1306 uint8_t cc=(uint8_t)norm16; // cc!=0
1307 if( onlyContiguous && // FCC
1308 (doCompose ? buffer.getLastCC() : prevCC)==0 &&
1309 prevBoundary<prevSrc &&
1310 // buffer.getLastCC()==0 && prevBoundary<prevSrc tell us that
1311 // [prevBoundary..prevSrc[ (which is exactly one character under these conditions)
1312 // passed the quick check "yes && ccc==0" test.
1313 // Check whether the last character was a "yesYes" or a "yesNo".
1314 // If a "yesNo", then we get its trailing ccc from its
1315 // mapping and check for canonical order.
1316 // All other cases are ok.
1317 getTrailCCFromCompYesAndZeroCC(prevBoundary, prevSrc)>cc
1318 ) {
1319 // Fails FCD test, need to decompose and contiguously recompose.
1320 if(!doCompose) {
1321 return FALSE;
1322 }
1323 } else if(doCompose) {
1324 if(!buffer.append(c, cc, errorCode)) {
1325 break;
1326 }
1327 continue;
1328 } else if(prevCC<=cc) {
1329 prevCC=cc;
1330 continue;
1331 } else {
1332 return FALSE;
1333 }
1334 } else if(!doCompose && !isMaybeOrNonZeroCC(norm16)) {
1335 return FALSE;
1336 }
1337
1338 /*
1339 * Find appropriate boundaries around this character,
1340 * decompose the source text from between the boundaries,
1341 * and recompose it.
1342 *
1343 * We may need to remove the last few characters from the ReorderingBuffer
1344 * to account for source text that was copied or appended
1345 * but needs to take part in the recomposition.
1346 */
1347
1348 /*
1349 * Find the last composition boundary in [prevBoundary..src[.
1350 * It is either the decomposition of the current character (at prevSrc),
1351 * or prevBoundary.
1352 */
1353 if(hasCompBoundaryBefore(c, norm16)) {
1354 prevBoundary=prevSrc;
1355 } else if(doCompose) {
1356 buffer.removeSuffix((int32_t)(prevSrc-prevBoundary));
1357 }
1358
1359 // Find the next composition boundary in [src..limit[ -
1360 // modifies src to point to the next starter.
1361 src=(UChar *)findNextCompBoundary(src, limit);
1362
1363 // Decompose [prevBoundary..src[ into the buffer and then recompose that part of it.
1364 int32_t recomposeStartIndex=buffer.length();
1365 if(!decomposeShort(prevBoundary, src, buffer, errorCode)) {
1366 break;
1367 }
1368 recompose(buffer, recomposeStartIndex, onlyContiguous);
1369 if(!doCompose) {
1370 if(!buffer.equals(prevBoundary, src)) {
1371 return FALSE;
1372 }
1373 buffer.remove();
1374 prevCC=0;
1375 }
1376
1377 // Move to the next starter. We never need to look back before this point again.
1378 prevBoundary=src;
1379 }
1380 return TRUE;
1381}
1382
1383// Very similar to compose(): Make the same changes in both places if relevant.
1384// pQCResult==NULL: spanQuickCheckYes
1385// pQCResult!=NULL: quickCheck (*pQCResult must be UNORM_YES)
1386const UChar *
1387Normalizer2Impl::composeQuickCheck(const UChar *src, const UChar *limit,
1388 UBool onlyContiguous,
1389 UNormalizationCheckResult *pQCResult) const {
1390 /*
1391 * prevBoundary points to the last character before the current one
1392 * that has a composition boundary before it with ccc==0 and quick check "yes".
1393 */
1394 const UChar *prevBoundary=src;
1395 UChar32 minNoMaybeCP=minCompNoMaybeCP;
1396 if(limit==NULL) {
1397 UErrorCode errorCode=U_ZERO_ERROR;
1398 src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, NULL, errorCode);
1399 if(prevBoundary<src) {
1400 // Set prevBoundary to the last character in the prefix.
1401 prevBoundary=src-1;
1402 }
1403 limit=u_strchr(src, 0);
1404 }
1405
1406 const UChar *prevSrc;
1407 UChar32 c=0;
1408 uint16_t norm16=0;
1409 uint8_t prevCC=0;
1410
1411 for(;;) {
1412 // count code units below the minimum or with irrelevant data for the quick check
1413 for(prevSrc=src;;) {
1414 if(src==limit) {
1415 return src;
1416 }
1417 if( (c=*src)<minNoMaybeCP ||
1418 isCompYesAndZeroCC(norm16=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(normTrie, c))
1419 ) {
1420 ++src;
1421 } else if(!U16_IS_SURROGATE(c)) {
1422 break;
1423 } else {
1424 UChar c2;
1425 if(U16_IS_SURROGATE_LEAD(c)) {
1426 if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
1427 c=U16_GET_SUPPLEMENTARY(c, c2);
1428 }
1429 } else /* trail surrogate */ {
1430 if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
1431 --src;
1432 c=U16_GET_SUPPLEMENTARY(c2, c);
1433 }
1434 }
1435 if(isCompYesAndZeroCC(norm16=getNorm16(c))) {
1436 src+=U16_LENGTH(c);
1437 } else {
1438 break;
1439 }
1440 }
1441 }
1442 if(src!=prevSrc) {
1443 // Set prevBoundary to the last character in the quick check loop.
1444 prevBoundary=src-1;
1445 if( U16_IS_TRAIL(*prevBoundary) && prevSrc<prevBoundary &&
1446 U16_IS_LEAD(*(prevBoundary-1))
1447 ) {
1448 --prevBoundary;
1449 }
1450 prevCC=0;
1451 // The start of the current character (c).
1452 prevSrc=src;
1453 }
1454
1455 src+=U16_LENGTH(c);
1456 /*
1457 * isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
1458 * c is either a "noNo" (has a mapping) or a "maybeYes" (combines backward)
1459 * or has ccc!=0.
1460 */
1461 if(isMaybeOrNonZeroCC(norm16)) {
1462 uint8_t cc=getCCFromYesOrMaybe(norm16);
1463 if( onlyContiguous && // FCC
1464 cc!=0 &&
1465 prevCC==0 &&
1466 prevBoundary<prevSrc &&
1467 // prevCC==0 && prevBoundary<prevSrc tell us that
1468 // [prevBoundary..prevSrc[ (which is exactly one character under these conditions)
1469 // passed the quick check "yes && ccc==0" test.
1470 // Check whether the last character was a "yesYes" or a "yesNo".
1471 // If a "yesNo", then we get its trailing ccc from its
1472 // mapping and check for canonical order.
1473 // All other cases are ok.
1474 getTrailCCFromCompYesAndZeroCC(prevBoundary, prevSrc)>cc
1475 ) {
1476 // Fails FCD test.
1477 } else if(prevCC<=cc || cc==0) {
1478 prevCC=cc;
1479 if(norm16<MIN_YES_YES_WITH_CC) {
1480 if(pQCResult!=NULL) {
1481 *pQCResult=UNORM_MAYBE;
1482 } else {
1483 return prevBoundary;
1484 }
1485 }
1486 continue;
1487 }
1488 }
1489 if(pQCResult!=NULL) {
1490 *pQCResult=UNORM_NO;
1491 }
1492 return prevBoundary;
1493 }
1494}
1495
1496void Normalizer2Impl::composeAndAppend(const UChar *src, const UChar *limit,
1497 UBool doCompose,
1498 UBool onlyContiguous,
4388f060 1499 UnicodeString &safeMiddle,
729e4ab9
A
1500 ReorderingBuffer &buffer,
1501 UErrorCode &errorCode) const {
1502 if(!buffer.isEmpty()) {
1503 const UChar *firstStarterInSrc=findNextCompBoundary(src, limit);
1504 if(src!=firstStarterInSrc) {
1505 const UChar *lastStarterInDest=findPreviousCompBoundary(buffer.getStart(),
1506 buffer.getLimit());
4388f060
A
1507 int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastStarterInDest);
1508 UnicodeString middle(lastStarterInDest, destSuffixLength);
1509 buffer.removeSuffix(destSuffixLength);
1510 safeMiddle=middle;
729e4ab9
A
1511 middle.append(src, (int32_t)(firstStarterInSrc-src));
1512 const UChar *middleStart=middle.getBuffer();
1513 compose(middleStart, middleStart+middle.length(), onlyContiguous,
1514 TRUE, buffer, errorCode);
1515 if(U_FAILURE(errorCode)) {
1516 return;
1517 }
1518 src=firstStarterInSrc;
1519 }
1520 }
1521 if(doCompose) {
1522 compose(src, limit, onlyContiguous, TRUE, buffer, errorCode);
1523 } else {
4388f060
A
1524 if(limit==NULL) { // appendZeroCC() needs limit!=NULL
1525 limit=u_strchr(src, 0);
1526 }
729e4ab9
A
1527 buffer.appendZeroCC(src, limit, errorCode);
1528 }
1529}
1530
1531/**
1532 * Does c have a composition boundary before it?
1533 * True if its decomposition begins with a character that has
1534 * ccc=0 && NFC_QC=Yes (isCompYesAndZeroCC()).
1535 * As a shortcut, this is true if c itself has ccc=0 && NFC_QC=Yes
1536 * (isCompYesAndZeroCC()) so we need not decompose.
1537 */
1538UBool Normalizer2Impl::hasCompBoundaryBefore(UChar32 c, uint16_t norm16) const {
1539 for(;;) {
1540 if(isCompYesAndZeroCC(norm16)) {
1541 return TRUE;
1542 } else if(isMaybeOrNonZeroCC(norm16)) {
1543 return FALSE;
1544 } else if(isDecompNoAlgorithmic(norm16)) {
1545 c=mapAlgorithmic(c, norm16);
1546 norm16=getNorm16(c);
1547 } else {
1548 // c decomposes, get everything from the variable-length extra data
1549 const uint16_t *mapping=getMapping(norm16);
4388f060 1550 uint16_t firstUnit=*mapping;
729e4ab9
A
1551 if((firstUnit&MAPPING_LENGTH_MASK)==0) {
1552 return FALSE;
1553 }
4388f060 1554 if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD) && (*(mapping-1)&0xff00)) {
729e4ab9
A
1555 return FALSE; // non-zero leadCC
1556 }
4388f060 1557 int32_t i=1; // skip over the firstUnit
729e4ab9
A
1558 UChar32 c;
1559 U16_NEXT_UNSAFE(mapping, i, c);
1560 return isCompYesAndZeroCC(getNorm16(c));
1561 }
1562 }
1563}
1564
1565UBool Normalizer2Impl::hasCompBoundaryAfter(UChar32 c, UBool onlyContiguous, UBool testInert) const {
1566 for(;;) {
1567 uint16_t norm16=getNorm16(c);
1568 if(isInert(norm16)) {
1569 return TRUE;
1570 } else if(norm16<=minYesNo) {
4388f060
A
1571 // Hangul: norm16==minYesNo
1572 // Hangul LVT has a boundary after it.
729e4ab9
A
1573 // Hangul LV and non-inert yesYes characters combine forward.
1574 return isHangul(norm16) && !Hangul::isHangulWithoutJamoT((UChar)c);
1575 } else if(norm16>= (testInert ? minNoNo : minMaybeYes)) {
1576 return FALSE;
1577 } else if(isDecompNoAlgorithmic(norm16)) {
1578 c=mapAlgorithmic(c, norm16);
1579 } else {
1580 // c decomposes, get everything from the variable-length extra data.
1581 // If testInert, then c must be a yesNo character which has lccc=0,
1582 // otherwise it could be a noNo.
1583 const uint16_t *mapping=getMapping(norm16);
1584 uint16_t firstUnit=*mapping;
1585 // TRUE if
4388f060
A
1586 // not MAPPING_NO_COMP_BOUNDARY_AFTER
1587 // (which is set if
1588 // c is not deleted, and
1589 // it and its decomposition do not combine forward, and it has a starter)
1590 // and if FCC then trailCC<=1
729e4ab9 1591 return
4388f060 1592 (firstUnit&MAPPING_NO_COMP_BOUNDARY_AFTER)==0 &&
729e4ab9
A
1593 (!onlyContiguous || firstUnit<=0x1ff);
1594 }
1595 }
1596}
1597
1598const UChar *Normalizer2Impl::findPreviousCompBoundary(const UChar *start, const UChar *p) const {
1599 BackwardUTrie2StringIterator iter(normTrie, start, p);
1600 uint16_t norm16;
1601 do {
1602 norm16=iter.previous16();
1603 } while(!hasCompBoundaryBefore(iter.codePoint, norm16));
1604 // We could also test hasCompBoundaryAfter() and return iter.codePointLimit,
1605 // but that's probably not worth the extra cost.
1606 return iter.codePointStart;
1607}
1608
1609const UChar *Normalizer2Impl::findNextCompBoundary(const UChar *p, const UChar *limit) const {
1610 ForwardUTrie2StringIterator iter(normTrie, p, limit);
1611 uint16_t norm16;
1612 do {
1613 norm16=iter.next16();
1614 } while(!hasCompBoundaryBefore(iter.codePoint, norm16));
1615 return iter.codePointStart;
1616}
1617
4388f060
A
1618// Note: normalizer2impl.cpp r30982 (2011-nov-27)
1619// still had getFCDTrie() which built and cached an FCD trie.
1620// That provided faster access to FCD data than getFCD16FromNormData()
1621// but required synchronization and consumed some 10kB of heap memory
1622// in any process that uses FCD (e.g., via collation).
1623// tccc180[] and smallFCD[] are intended to help with any loss of performance,
1624// at least for Latin & CJK.
729e4ab9 1625
4388f060
A
1626// Gets the FCD value from the regular normalization data.
1627uint16_t Normalizer2Impl::getFCD16FromNormData(UChar32 c) const {
729e4ab9
A
1628 // Only loops for 1:1 algorithmic mappings.
1629 for(;;) {
4388f060
A
1630 uint16_t norm16=getNorm16(c);
1631 if(norm16<=minYesNo) {
729e4ab9 1632 // no decomposition or Hangul syllable, all zeros
4388f060
A
1633 return 0;
1634 } else if(norm16>=MIN_NORMAL_MAYBE_YES) {
1635 // combining mark
1636 norm16&=0xff;
1637 return norm16|(norm16<<8);
1638 } else if(norm16>=minMaybeYes) {
1639 return 0;
1640 } else if(isDecompNoAlgorithmic(norm16)) {
1641 c=mapAlgorithmic(c, norm16);
729e4ab9
A
1642 } else {
1643 // c decomposes, get everything from the variable-length extra data
1644 const uint16_t *mapping=getMapping(norm16);
1645 uint16_t firstUnit=*mapping;
1646 if((firstUnit&MAPPING_LENGTH_MASK)==0) {
1647 // A character that is deleted (maps to an empty string) must
1648 // get the worst-case lccc and tccc values because arbitrary
1649 // characters on both sides will become adjacent.
4388f060 1650 return 0x1ff;
729e4ab9 1651 } else {
4388f060 1652 norm16=firstUnit>>8; // tccc
729e4ab9 1653 if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
4388f060 1654 norm16|=*(mapping-1)&0xff00; // lccc
729e4ab9 1655 }
4388f060 1656 return norm16;
729e4ab9
A
1657 }
1658 }
729e4ab9
A
1659 }
1660}
1661
729e4ab9
A
1662// Dual functionality:
1663// buffer!=NULL: normalize
1664// buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes
1665const UChar *
1666Normalizer2Impl::makeFCD(const UChar *src, const UChar *limit,
1667 ReorderingBuffer *buffer,
1668 UErrorCode &errorCode) const {
1669 // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered tccc<=1.
1670 // Similar to the prevBoundary in the compose() implementation.
1671 const UChar *prevBoundary=src;
1672 int32_t prevFCD16=0;
1673 if(limit==NULL) {
1674 src=copyLowPrefixFromNulTerminated(src, MIN_CCC_LCCC_CP, buffer, errorCode);
1675 if(U_FAILURE(errorCode)) {
1676 return src;
1677 }
1678 if(prevBoundary<src) {
1679 prevBoundary=src;
1680 // We know that the previous character's lccc==0.
1681 // Fetching the fcd16 value was deferred for this below-U+0300 code point.
4388f060 1682 prevFCD16=getFCD16(*(src-1));
729e4ab9
A
1683 if(prevFCD16>1) {
1684 --prevBoundary;
1685 }
1686 }
1687 limit=u_strchr(src, 0);
1688 }
1689
1690 // Note: In this function we use buffer->appendZeroCC() because we track
1691 // the lead and trail combining classes here, rather than leaving it to
1692 // the ReorderingBuffer.
1693 // The exception is the call to decomposeShort() which uses the buffer
1694 // in the normal way.
1695
729e4ab9
A
1696 const UChar *prevSrc;
1697 UChar32 c=0;
1698 uint16_t fcd16=0;
1699
1700 for(;;) {
1701 // count code units with lccc==0
1702 for(prevSrc=src; src!=limit;) {
1703 if((c=*src)<MIN_CCC_LCCC_CP) {
1704 prevFCD16=~c;
1705 ++src;
4388f060
A
1706 } else if(!singleLeadMightHaveNonZeroFCD16(c)) {
1707 prevFCD16=0;
729e4ab9 1708 ++src;
729e4ab9 1709 } else {
4388f060
A
1710 if(U16_IS_SURROGATE(c)) {
1711 UChar c2;
1712 if(U16_IS_SURROGATE_LEAD(c)) {
1713 if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
1714 c=U16_GET_SUPPLEMENTARY(c, c2);
1715 }
1716 } else /* trail surrogate */ {
1717 if(prevSrc<src && U16_IS_LEAD(c2=*(src-1))) {
1718 --src;
1719 c=U16_GET_SUPPLEMENTARY(c2, c);
1720 }
729e4ab9
A
1721 }
1722 }
4388f060 1723 if((fcd16=getFCD16FromNormData(c))<=0xff) {
729e4ab9
A
1724 prevFCD16=fcd16;
1725 src+=U16_LENGTH(c);
1726 } else {
1727 break;
1728 }
1729 }
1730 }
1731 // copy these code units all at once
1732 if(src!=prevSrc) {
1733 if(buffer!=NULL && !buffer->appendZeroCC(prevSrc, src, errorCode)) {
1734 break;
1735 }
1736 if(src==limit) {
1737 break;
1738 }
1739 prevBoundary=src;
1740 // We know that the previous character's lccc==0.
1741 if(prevFCD16<0) {
1742 // Fetching the fcd16 value was deferred for this below-U+0300 code point.
4388f060
A
1743 UChar32 prev=~prevFCD16;
1744 prevFCD16= prev<0x180 ? tccc180[prev] : getFCD16FromNormData(prev);
729e4ab9
A
1745 if(prevFCD16>1) {
1746 --prevBoundary;
1747 }
1748 } else {
1749 const UChar *p=src-1;
1750 if(U16_IS_TRAIL(*p) && prevSrc<p && U16_IS_LEAD(*(p-1))) {
1751 --p;
1752 // Need to fetch the previous character's FCD value because
1753 // prevFCD16 was just for the trail surrogate code point.
4388f060 1754 prevFCD16=getFCD16FromNormData(U16_GET_SUPPLEMENTARY(p[0], p[1]));
729e4ab9
A
1755 // Still known to have lccc==0 because its lead surrogate unit had lccc==0.
1756 }
1757 if(prevFCD16>1) {
1758 prevBoundary=p;
1759 }
1760 }
1761 // The start of the current character (c).
1762 prevSrc=src;
1763 } else if(src==limit) {
1764 break;
1765 }
1766
1767 src+=U16_LENGTH(c);
1768 // The current character (c) at [prevSrc..src[ has a non-zero lead combining class.
1769 // Check for proper order, and decompose locally if necessary.
1770 if((prevFCD16&0xff)<=(fcd16>>8)) {
1771 // proper order: prev tccc <= current lccc
1772 if((fcd16&0xff)<=1) {
1773 prevBoundary=src;
1774 }
1775 if(buffer!=NULL && !buffer->appendZeroCC(c, errorCode)) {
1776 break;
1777 }
1778 prevFCD16=fcd16;
1779 continue;
1780 } else if(buffer==NULL) {
1781 return prevBoundary; // quick check "no"
1782 } else {
1783 /*
1784 * Back out the part of the source that we copied or appended
1785 * already but is now going to be decomposed.
1786 * prevSrc is set to after what was copied/appended.
1787 */
1788 buffer->removeSuffix((int32_t)(prevSrc-prevBoundary));
1789 /*
1790 * Find the part of the source that needs to be decomposed,
1791 * up to the next safe boundary.
1792 */
1793 src=findNextFCDBoundary(src, limit);
1794 /*
1795 * The source text does not fulfill the conditions for FCD.
1796 * Decompose and reorder a limited piece of the text.
1797 */
1798 if(!decomposeShort(prevBoundary, src, *buffer, errorCode)) {
1799 break;
1800 }
1801 prevBoundary=src;
1802 prevFCD16=0;
1803 }
1804 }
1805 return src;
1806}
1807
1808void Normalizer2Impl::makeFCDAndAppend(const UChar *src, const UChar *limit,
1809 UBool doMakeFCD,
4388f060 1810 UnicodeString &safeMiddle,
729e4ab9
A
1811 ReorderingBuffer &buffer,
1812 UErrorCode &errorCode) const {
1813 if(!buffer.isEmpty()) {
1814 const UChar *firstBoundaryInSrc=findNextFCDBoundary(src, limit);
1815 if(src!=firstBoundaryInSrc) {
1816 const UChar *lastBoundaryInDest=findPreviousFCDBoundary(buffer.getStart(),
1817 buffer.getLimit());
4388f060
A
1818 int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastBoundaryInDest);
1819 UnicodeString middle(lastBoundaryInDest, destSuffixLength);
1820 buffer.removeSuffix(destSuffixLength);
1821 safeMiddle=middle;
729e4ab9
A
1822 middle.append(src, (int32_t)(firstBoundaryInSrc-src));
1823 const UChar *middleStart=middle.getBuffer();
1824 makeFCD(middleStart, middleStart+middle.length(), &buffer, errorCode);
1825 if(U_FAILURE(errorCode)) {
1826 return;
1827 }
1828 src=firstBoundaryInSrc;
1829 }
1830 }
1831 if(doMakeFCD) {
1832 makeFCD(src, limit, &buffer, errorCode);
1833 } else {
4388f060
A
1834 if(limit==NULL) { // appendZeroCC() needs limit!=NULL
1835 limit=u_strchr(src, 0);
1836 }
729e4ab9
A
1837 buffer.appendZeroCC(src, limit, errorCode);
1838 }
1839}
1840
1841const UChar *Normalizer2Impl::findPreviousFCDBoundary(const UChar *start, const UChar *p) const {
4388f060
A
1842 while(start<p && previousFCD16(start, p)>0xff) {}
1843 return p;
729e4ab9
A
1844}
1845
1846const UChar *Normalizer2Impl::findNextFCDBoundary(const UChar *p, const UChar *limit) const {
4388f060
A
1847 while(p<limit) {
1848 const UChar *codePointStart=p;
1849 if(nextFCD16(p, limit)<=0xff) {
1850 return codePointStart;
1851 }
1852 }
1853 return p;
729e4ab9
A
1854}
1855
1856// CanonicalIterator data -------------------------------------------------- ***
1857
1858CanonIterData::CanonIterData(UErrorCode &errorCode) :
1859 trie(utrie2_open(0, 0, &errorCode)),
4388f060 1860 canonStartSets(uprv_deleteUObject, NULL, errorCode) {}
729e4ab9
A
1861
1862CanonIterData::~CanonIterData() {
1863 utrie2_close(trie);
1864}
1865
1866void CanonIterData::addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode) {
1867 uint32_t canonValue=utrie2_get32(trie, decompLead);
1868 if((canonValue&(CANON_HAS_SET|CANON_VALUE_MASK))==0 && origin!=0) {
1869 // origin is the first character whose decomposition starts with
1870 // the character for which we are setting the value.
1871 utrie2_set32(trie, decompLead, canonValue|origin, &errorCode);
1872 } else {
1873 // origin is not the first character, or it is U+0000.
1874 UnicodeSet *set;
1875 if((canonValue&CANON_HAS_SET)==0) {
1876 set=new UnicodeSet;
1877 if(set==NULL) {
1878 errorCode=U_MEMORY_ALLOCATION_ERROR;
1879 return;
1880 }
1881 UChar32 firstOrigin=(UChar32)(canonValue&CANON_VALUE_MASK);
1882 canonValue=(canonValue&~CANON_VALUE_MASK)|CANON_HAS_SET|(uint32_t)canonStartSets.size();
1883 utrie2_set32(trie, decompLead, canonValue, &errorCode);
1884 canonStartSets.addElement(set, errorCode);
1885 if(firstOrigin!=0) {
1886 set->add(firstOrigin);
1887 }
1888 } else {
1889 set=(UnicodeSet *)canonStartSets[(int32_t)(canonValue&CANON_VALUE_MASK)];
1890 }
1891 set->add(origin);
1892 }
1893}
1894
729e4ab9
A
1895U_CDECL_BEGIN
1896
1897// Call Normalizer2Impl::makeCanonIterDataFromNorm16() for a range of same-norm16 characters.
57a6839d 1898// context: the Normalizer2Impl
729e4ab9
A
1899static UBool U_CALLCONV
1900enumCIDRangeHandler(const void *context, UChar32 start, UChar32 end, uint32_t value) {
57a6839d
A
1901 UErrorCode errorCode = U_ZERO_ERROR;
1902 if (value != 0) {
1903 Normalizer2Impl *impl = (Normalizer2Impl *)context;
1904 impl->makeCanonIterDataFromNorm16(
1905 start, end, (uint16_t)value, *impl->fCanonIterData, errorCode);
1906 }
1907 return U_SUCCESS(errorCode);
729e4ab9
A
1908}
1909
729e4ab9 1910
57a6839d
A
1911
1912// UInitOnce instantiation function for CanonIterData
1913
1914static void U_CALLCONV
1915initCanonIterData(Normalizer2Impl *impl, UErrorCode &errorCode) {
1916 U_ASSERT(impl->fCanonIterData == NULL);
1917 impl->fCanonIterData = new CanonIterData(errorCode);
1918 if (impl->fCanonIterData == NULL) {
729e4ab9 1919 errorCode=U_MEMORY_ALLOCATION_ERROR;
729e4ab9 1920 }
57a6839d
A
1921 if (U_SUCCESS(errorCode)) {
1922 utrie2_enum(impl->getNormTrie(), NULL, enumCIDRangeHandler, impl);
1923 utrie2_freeze(impl->fCanonIterData->trie, UTRIE2_32_VALUE_BITS, &errorCode);
1924 }
1925 if (U_FAILURE(errorCode)) {
1926 delete impl->fCanonIterData;
1927 impl->fCanonIterData = NULL;
729e4ab9 1928 }
729e4ab9
A
1929}
1930
57a6839d
A
1931U_CDECL_END
1932
729e4ab9
A
1933void Normalizer2Impl::makeCanonIterDataFromNorm16(UChar32 start, UChar32 end, uint16_t norm16,
1934 CanonIterData &newData,
1935 UErrorCode &errorCode) const {
1936 if(norm16==0 || (minYesNo<=norm16 && norm16<minNoNo)) {
1937 // Inert, or 2-way mapping (including Hangul syllable).
1938 // We do not write a canonStartSet for any yesNo character.
1939 // Composites from 2-way mappings are added at runtime from the
1940 // starter's compositions list, and the other characters in
1941 // 2-way mappings get CANON_NOT_SEGMENT_STARTER set because they are
1942 // "maybe" characters.
1943 return;
1944 }
1945 for(UChar32 c=start; c<=end; ++c) {
1946 uint32_t oldValue=utrie2_get32(newData.trie, c);
1947 uint32_t newValue=oldValue;
1948 if(norm16>=minMaybeYes) {
1949 // not a segment starter if it occurs in a decomposition or has cc!=0
1950 newValue|=CANON_NOT_SEGMENT_STARTER;
1951 if(norm16<MIN_NORMAL_MAYBE_YES) {
1952 newValue|=CANON_HAS_COMPOSITIONS;
1953 }
1954 } else if(norm16<minYesNo) {
1955 newValue|=CANON_HAS_COMPOSITIONS;
1956 } else {
1957 // c has a one-way decomposition
1958 UChar32 c2=c;
1959 uint16_t norm16_2=norm16;
1960 while(limitNoNo<=norm16_2 && norm16_2<minMaybeYes) {
1961 c2=mapAlgorithmic(c2, norm16_2);
1962 norm16_2=getNorm16(c2);
1963 }
1964 if(minYesNo<=norm16_2 && norm16_2<limitNoNo) {
1965 // c decomposes, get everything from the variable-length extra data
1966 const uint16_t *mapping=getMapping(norm16_2);
4388f060 1967 uint16_t firstUnit=*mapping;
729e4ab9
A
1968 int32_t length=firstUnit&MAPPING_LENGTH_MASK;
1969 if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD)!=0) {
4388f060 1970 if(c==c2 && (*(mapping-1)&0xff)!=0) {
729e4ab9
A
1971 newValue|=CANON_NOT_SEGMENT_STARTER; // original c has cc!=0
1972 }
729e4ab9
A
1973 }
1974 // Skip empty mappings (no characters in the decomposition).
1975 if(length!=0) {
4388f060 1976 ++mapping; // skip over the firstUnit
729e4ab9
A
1977 // add c to first code point's start set
1978 int32_t i=0;
1979 U16_NEXT_UNSAFE(mapping, i, c2);
1980 newData.addToStartSet(c, c2, errorCode);
1981 // Set CANON_NOT_SEGMENT_STARTER for each remaining code point of a
1982 // one-way mapping. A 2-way mapping is possible here after
1983 // intermediate algorithmic mapping.
1984 if(norm16_2>=minNoNo) {
1985 while(i<length) {
1986 U16_NEXT_UNSAFE(mapping, i, c2);
1987 uint32_t c2Value=utrie2_get32(newData.trie, c2);
1988 if((c2Value&CANON_NOT_SEGMENT_STARTER)==0) {
1989 utrie2_set32(newData.trie, c2, c2Value|CANON_NOT_SEGMENT_STARTER,
1990 &errorCode);
1991 }
1992 }
1993 }
1994 }
1995 } else {
1996 // c decomposed to c2 algorithmically; c has cc==0
1997 newData.addToStartSet(c, c2, errorCode);
1998 }
1999 }
2000 if(newValue!=oldValue) {
2001 utrie2_set32(newData.trie, c, newValue, &errorCode);
2002 }
2003 }
2004}
2005
2006UBool Normalizer2Impl::ensureCanonIterData(UErrorCode &errorCode) const {
2007 // Logically const: Synchronized instantiation.
2008 Normalizer2Impl *me=const_cast<Normalizer2Impl *>(this);
57a6839d 2009 umtx_initOnce(me->fCanonIterDataInitOnce, &initCanonIterData, me, errorCode);
729e4ab9
A
2010 return U_SUCCESS(errorCode);
2011}
2012
2013int32_t Normalizer2Impl::getCanonValue(UChar32 c) const {
57a6839d 2014 return (int32_t)utrie2_get32(fCanonIterData->trie, c);
729e4ab9
A
2015}
2016
2017const UnicodeSet &Normalizer2Impl::getCanonStartSet(int32_t n) const {
57a6839d 2018 return *(const UnicodeSet *)fCanonIterData->canonStartSets[n];
729e4ab9
A
2019}
2020
2021UBool Normalizer2Impl::isCanonSegmentStarter(UChar32 c) const {
2022 return getCanonValue(c)>=0;
2023}
2024
2025UBool Normalizer2Impl::getCanonStartSet(UChar32 c, UnicodeSet &set) const {
2026 int32_t canonValue=getCanonValue(c)&~CANON_NOT_SEGMENT_STARTER;
2027 if(canonValue==0) {
2028 return FALSE;
2029 }
2030 set.clear();
2031 int32_t value=canonValue&CANON_VALUE_MASK;
2032 if((canonValue&CANON_HAS_SET)!=0) {
2033 set.addAll(getCanonStartSet(value));
2034 } else if(value!=0) {
2035 set.add(value);
2036 }
2037 if((canonValue&CANON_HAS_COMPOSITIONS)!=0) {
2038 uint16_t norm16=getNorm16(c);
2039 if(norm16==JAMO_L) {
2040 UChar32 syllable=
2041 (UChar32)(Hangul::HANGUL_BASE+(c-Hangul::JAMO_L_BASE)*Hangul::JAMO_VT_COUNT);
2042 set.add(syllable, syllable+Hangul::JAMO_VT_COUNT-1);
2043 } else {
2044 addComposites(getCompositionsList(norm16), set);
2045 }
2046 }
2047 return TRUE;
2048}
2049
2050U_NAMESPACE_END
2051
2052// Normalizer2 data swapping ----------------------------------------------- ***
2053
2054U_NAMESPACE_USE
2055
2056U_CAPI int32_t U_EXPORT2
2057unorm2_swap(const UDataSwapper *ds,
2058 const void *inData, int32_t length, void *outData,
2059 UErrorCode *pErrorCode) {
2060 const UDataInfo *pInfo;
2061 int32_t headerSize;
2062
2063 const uint8_t *inBytes;
2064 uint8_t *outBytes;
2065
2066 const int32_t *inIndexes;
2067 int32_t indexes[Normalizer2Impl::IX_MIN_MAYBE_YES+1];
2068
2069 int32_t i, offset, nextOffset, size;
2070
2071 /* udata_swapDataHeader checks the arguments */
2072 headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
2073 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
2074 return 0;
2075 }
2076
2077 /* check data format and format version */
2078 pInfo=(const UDataInfo *)((const char *)inData+4);
2079 if(!(
2080 pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */
2081 pInfo->dataFormat[1]==0x72 &&
2082 pInfo->dataFormat[2]==0x6d &&
2083 pInfo->dataFormat[3]==0x32 &&
4388f060 2084 (pInfo->formatVersion[0]==1 || pInfo->formatVersion[0]==2)
729e4ab9
A
2085 )) {
2086 udata_printError(ds, "unorm2_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as Normalizer2 data\n",
2087 pInfo->dataFormat[0], pInfo->dataFormat[1],
2088 pInfo->dataFormat[2], pInfo->dataFormat[3],
2089 pInfo->formatVersion[0]);
2090 *pErrorCode=U_UNSUPPORTED_ERROR;
2091 return 0;
2092 }
2093
2094 inBytes=(const uint8_t *)inData+headerSize;
2095 outBytes=(uint8_t *)outData+headerSize;
2096
2097 inIndexes=(const int32_t *)inBytes;
2098
2099 if(length>=0) {
2100 length-=headerSize;
2101 if(length<(int32_t)sizeof(indexes)) {
2102 udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for Normalizer2 data\n",
2103 length);
2104 *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
2105 return 0;
2106 }
2107 }
2108
2109 /* read the first few indexes */
2110 for(i=0; i<=Normalizer2Impl::IX_MIN_MAYBE_YES; ++i) {
2111 indexes[i]=udata_readInt32(ds, inIndexes[i]);
2112 }
2113
2114 /* get the total length of the data */
2115 size=indexes[Normalizer2Impl::IX_TOTAL_SIZE];
2116
2117 if(length>=0) {
2118 if(length<size) {
2119 udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for all of Normalizer2 data\n",
2120 length);
2121 *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
2122 return 0;
2123 }
2124
2125 /* copy the data for inaccessible bytes */
2126 if(inBytes!=outBytes) {
2127 uprv_memcpy(outBytes, inBytes, size);
2128 }
2129
2130 offset=0;
2131
2132 /* swap the int32_t indexes[] */
2133 nextOffset=indexes[Normalizer2Impl::IX_NORM_TRIE_OFFSET];
2134 ds->swapArray32(ds, inBytes, nextOffset-offset, outBytes, pErrorCode);
2135 offset=nextOffset;
2136
2137 /* swap the UTrie2 */
2138 nextOffset=indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET];
2139 utrie2_swap(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
2140 offset=nextOffset;
2141
2142 /* swap the uint16_t extraData[] */
4388f060 2143 nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET];
729e4ab9
A
2144 ds->swapArray16(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
2145 offset=nextOffset;
2146
4388f060
A
2147 /* no need to swap the uint8_t smallFCD[] (new in formatVersion 2) */
2148 nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET+1];
2149 offset=nextOffset;
2150
729e4ab9
A
2151 U_ASSERT(offset==size);
2152 }
2153
2154 return headerSize+size;
2155}
2156
2157#endif // !UCONFIG_NO_NORMALIZATION