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