Coverage Report

Created: 2025-09-05 07:16

/src/icu/icu4c/source/common/normalizer2impl.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
*
6
*   Copyright (C) 2009-2014, International Business Machines
7
*   Corporation and others.  All Rights Reserved.
8
*
9
*******************************************************************************
10
*   file name:  normalizer2impl.cpp
11
*   encoding:   UTF-8
12
*   tab size:   8 (not used)
13
*   indentation:4
14
*
15
*   created on: 2009nov22
16
*   created by: Markus W. Scherer
17
*/
18
19
// #define UCPTRIE_DEBUG
20
21
#include "unicode/utypes.h"
22
23
#if !UCONFIG_NO_NORMALIZATION
24
25
#include "unicode/bytestream.h"
26
#include "unicode/edits.h"
27
#include "unicode/normalizer2.h"
28
#include "unicode/stringoptions.h"
29
#include "unicode/ucptrie.h"
30
#include "unicode/udata.h"
31
#include "unicode/umutablecptrie.h"
32
#include "unicode/ustring.h"
33
#include "unicode/utf16.h"
34
#include "unicode/utf8.h"
35
#include "bytesinkutil.h"
36
#include "cmemory.h"
37
#include "mutex.h"
38
#include "normalizer2impl.h"
39
#include "putilimp.h"
40
#include "uassert.h"
41
#include "ucptrie_impl.h"
42
#include "uset_imp.h"
43
#include "uvector.h"
44
45
U_NAMESPACE_BEGIN
46
47
namespace {
48
49
/**
50
 * UTF-8 lead byte for minNoMaybeCP.
51
 * Can be lower than the actual lead byte for c.
52
 * Typically U+0300 for NFC/NFD, U+00A0 for NFKC/NFKD, U+0041 for NFKC_Casefold.
53
 */
54
0
inline uint8_t leadByteForCP(UChar32 c) {
55
0
    if (c <= 0x7f) {
56
0
        return static_cast<uint8_t>(c);
57
0
    } else if (c <= 0x7ff) {
58
0
        return static_cast<uint8_t>(0xc0 + (c >> 6));
59
0
    } else {
60
        // Should not occur because ccc(U+0300)!=0.
61
0
        return 0xe0;
62
0
    }
63
0
}
64
65
/**
66
 * Returns the code point from one single well-formed UTF-8 byte sequence
67
 * between cpStart and cpLimit.
68
 *
69
 * Trie UTF-8 macros do not assemble whole code points (for efficiency).
70
 * When we do need the code point, we call this function.
71
 * We should not need it for normalization-inert data (norm16==0).
72
 * Illegal sequences yield the error value norm16==0 just like real normalization-inert code points.
73
 */
74
0
UChar32 codePointFromValidUTF8(const uint8_t *cpStart, const uint8_t *cpLimit) {
75
    // Similar to U8_NEXT_UNSAFE(s, i, c).
76
0
    U_ASSERT(cpStart < cpLimit);
77
0
    uint8_t c = *cpStart;
78
0
    switch(cpLimit-cpStart) {
79
0
    case 1:
80
0
        return c;
81
0
    case 2:
82
0
        return ((c&0x1f)<<6) | (cpStart[1]&0x3f);
83
0
    case 3:
84
        // no need for (c&0xf) because the upper bits are truncated after <<12 in the cast to (char16_t)
85
0
        return static_cast<char16_t>((c << 12) | ((cpStart[1] & 0x3f) << 6) | (cpStart[2] & 0x3f));
86
0
    case 4:
87
0
        return ((c&7)<<18) | ((cpStart[1]&0x3f)<<12) | ((cpStart[2]&0x3f)<<6) | (cpStart[3]&0x3f);
88
0
    default:
89
0
        UPRV_UNREACHABLE_EXIT;  // Should not occur.
90
0
    }
91
0
}
92
93
/**
94
 * Returns the last code point in [start, p[ if it is valid and in U+1000..U+D7FF.
95
 * Otherwise returns a negative value.
96
 */
97
0
UChar32 previousHangulOrJamo(const uint8_t *start, const uint8_t *p) {
98
0
    if ((p - start) >= 3) {
99
0
        p -= 3;
100
0
        uint8_t l = *p;
101
0
        uint8_t t1, t2;
102
0
        if (0xe1 <= l && l <= 0xed &&
103
0
                (t1 = static_cast<uint8_t>(p[1] - 0x80)) <= 0x3f &&
104
0
                (t2 = static_cast<uint8_t>(p[2] - 0x80)) <= 0x3f &&
105
0
                (l < 0xed || t1 <= 0x1f)) {
106
0
            return ((l & 0xf) << 12) | (t1 << 6) | t2;
107
0
        }
108
0
    }
109
0
    return U_SENTINEL;
110
0
}
111
112
/**
113
 * Returns the offset from the Jamo T base if [src, limit[ starts with a single Jamo T code point.
114
 * Otherwise returns a negative value.
115
 */
116
0
int32_t getJamoTMinusBase(const uint8_t *src, const uint8_t *limit) {
117
    // Jamo T: E1 86 A8..E1 87 82
118
0
    if ((limit - src) >= 3 && *src == 0xe1) {
119
0
        if (src[1] == 0x86) {
120
0
            uint8_t t = src[2];
121
            // The first Jamo T is U+11A8 but JAMO_T_BASE is 11A7.
122
            // Offset 0 does not correspond to any conjoining Jamo.
123
0
            if (0xa8 <= t && t <= 0xbf) {
124
0
                return t - 0xa7;
125
0
            }
126
0
        } else if (src[1] == 0x87) {
127
0
            uint8_t t = src[2];
128
0
            if (static_cast<int8_t>(t) <= static_cast<int8_t>(0x82u)) {
129
0
                return t - (0xa7 - 0x40);
130
0
            }
131
0
        }
132
0
    }
133
0
    return -1;
134
0
}
135
136
void
137
appendCodePointDelta(const uint8_t *cpStart, const uint8_t *cpLimit, int32_t delta,
138
0
                     ByteSink &sink, Edits *edits) {
139
0
    char buffer[U8_MAX_LENGTH];
140
0
    int32_t length;
141
0
    int32_t cpLength = static_cast<int32_t>(cpLimit - cpStart);
142
0
    if (cpLength == 1) {
143
        // The builder makes ASCII map to ASCII.
144
0
        buffer[0] = static_cast<uint8_t>(*cpStart + delta);
145
0
        length = 1;
146
0
    } else {
147
0
        int32_t trail = *(cpLimit-1) + delta;
148
0
        if (0x80 <= trail && trail <= 0xbf) {
149
            // The delta only changes the last trail byte.
150
0
            --cpLimit;
151
0
            length = 0;
152
0
            do { buffer[length++] = *cpStart++; } while (cpStart < cpLimit);
153
0
            buffer[length++] = static_cast<uint8_t>(trail);
154
0
        } else {
155
            // Decode the code point, add the delta, re-encode.
156
0
            UChar32 c = codePointFromValidUTF8(cpStart, cpLimit) + delta;
157
0
            length = 0;
158
0
            U8_APPEND_UNSAFE(buffer, length, c);
159
0
        }
160
0
    }
161
0
    if (edits != nullptr) {
162
0
        edits->addReplace(cpLength, length);
163
0
    }
164
0
    sink.Append(buffer, length);
165
0
}
166
167
}  // namespace
168
169
// ReorderingBuffer -------------------------------------------------------- ***
170
171
ReorderingBuffer::ReorderingBuffer(const Normalizer2Impl &ni, UnicodeString &dest,
172
                                   UErrorCode &errorCode) :
173
0
        impl(ni), str(dest),
174
0
        start(str.getBuffer(8)), reorderStart(start), limit(start),
175
0
        remainingCapacity(str.getCapacity()), lastCC(0) {
176
0
    if (start == nullptr && U_SUCCESS(errorCode)) {
177
        // getBuffer() already did str.setToBogus()
178
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
179
0
    }
180
0
}
181
182
118M
UBool ReorderingBuffer::init(int32_t destCapacity, UErrorCode &errorCode) {
183
118M
    int32_t length=str.length();
184
118M
    start=str.getBuffer(destCapacity);
185
118M
    if(start==nullptr) {
186
        // getBuffer() already did str.setToBogus()
187
0
        errorCode=U_MEMORY_ALLOCATION_ERROR;
188
0
        return false;
189
0
    }
190
118M
    limit=start+length;
191
118M
    remainingCapacity=str.getCapacity()-length;
192
118M
    reorderStart=start;
193
118M
    if(start==limit) {
194
118M
        lastCC=0;
195
118M
    } else {
196
4.88k
        setIterator();
197
4.88k
        lastCC=previousCC();
198
        // Set reorderStart after the last code point with cc<=1 if there is one.
199
4.88k
        if(lastCC>1) {
200
304k
            while(previousCC()>1) {}
201
1.66k
        }
202
4.88k
        reorderStart=codePointLimit;
203
4.88k
    }
204
118M
    return true;
205
118M
}
206
207
11.4k
UBool ReorderingBuffer::equals(const char16_t *otherStart, const char16_t *otherLimit) const {
208
11.4k
    int32_t length = static_cast<int32_t>(limit - start);
209
11.4k
    return
210
11.4k
        length == static_cast<int32_t>(otherLimit - otherStart) &&
211
11.4k
        0==u_memcmp(start, otherStart, length);
212
11.4k
}
213
214
0
UBool ReorderingBuffer::equals(const uint8_t *otherStart, const uint8_t *otherLimit) const {
215
0
    U_ASSERT((otherLimit - otherStart) <= INT32_MAX);  // ensured by caller
216
0
    int32_t length = static_cast<int32_t>(limit - start);
217
0
    int32_t otherLength = static_cast<int32_t>(otherLimit - otherStart);
218
    // For equal strings, UTF-8 is at least as long as UTF-16, and at most three times as long.
219
0
    if (otherLength < length || (otherLength / 3) > length) {
220
0
        return false;
221
0
    }
222
    // Compare valid strings from between normalization boundaries.
223
    // (Invalid sequences are normalization-inert.)
224
0
    for (int32_t i = 0, j = 0;;) {
225
0
        if (i >= length) {
226
0
            return j >= otherLength;
227
0
        } else if (j >= otherLength) {
228
0
            return false;
229
0
        }
230
        // Not at the end of either string yet.
231
0
        UChar32 c, other;
232
0
        U16_NEXT_UNSAFE(start, i, c);
233
0
        U8_NEXT_UNSAFE(otherStart, j, other);
234
0
        if (c != other) {
235
0
            return false;
236
0
        }
237
0
    }
238
0
}
239
240
1.43M
UBool ReorderingBuffer::appendSupplementary(UChar32 c, uint8_t cc, UErrorCode &errorCode) {
241
1.43M
    if(remainingCapacity<2 && !resize(2, errorCode)) {
242
0
        return false;
243
0
    }
244
1.43M
    if(lastCC<=cc || cc==0) {
245
1.34M
        limit[0]=U16_LEAD(c);
246
1.34M
        limit[1]=U16_TRAIL(c);
247
1.34M
        limit+=2;
248
1.34M
        lastCC=cc;
249
1.34M
        if(cc<=1) {
250
25.9k
            reorderStart=limit;
251
25.9k
        }
252
1.34M
    } else {
253
98.7k
        insert(c, cc);
254
98.7k
    }
255
1.43M
    remainingCapacity-=2;
256
1.43M
    return true;
257
1.43M
}
258
259
UBool ReorderingBuffer::append(const char16_t *s, int32_t length, UBool isNFD,
260
                               uint8_t leadCC, uint8_t trailCC,
261
38.5M
                               UErrorCode &errorCode) {
262
38.5M
    if(length==0) {
263
2.99k
        return true;
264
2.99k
    }
265
38.5M
    if(remainingCapacity<length && !resize(length, errorCode)) {
266
0
        return false;
267
0
    }
268
38.5M
    remainingCapacity-=length;
269
38.5M
    if(lastCC<=leadCC || leadCC==0) {
270
34.1M
        if(trailCC<=1) {
271
8.25M
            reorderStart=limit+length;
272
25.9M
        } else if(leadCC<=1) {
273
17.6M
            reorderStart=limit+1;  // Ok if not a code point boundary.
274
17.6M
        }
275
34.1M
        const char16_t *sLimit=s+length;
276
101M
        do { *limit++=*s++; } while(s!=sLimit);
277
34.1M
        lastCC=trailCC;
278
34.1M
    } else {
279
4.36M
        int32_t i=0;
280
4.36M
        UChar32 c;
281
4.36M
        U16_NEXT(s, i, length, c);
282
4.36M
        insert(c, leadCC);  // insert first code point
283
8.57M
        while(i<length) {
284
4.20M
            U16_NEXT(s, i, length, c);
285
4.20M
            if(i<length) {
286
9.05k
                if (isNFD) {
287
0
                    leadCC = Normalizer2Impl::getCCFromYesOrMaybeYes(impl.getRawNorm16(c));
288
9.05k
                } else {
289
9.05k
                    leadCC = impl.getCC(impl.getNorm16(c));
290
9.05k
                }
291
4.19M
            } else {
292
4.19M
                leadCC=trailCC;
293
4.19M
            }
294
4.20M
            append(c, leadCC, errorCode);
295
4.20M
        }
296
4.36M
    }
297
38.5M
    return true;
298
38.5M
}
299
300
1.89k
UBool ReorderingBuffer::appendZeroCC(UChar32 c, UErrorCode &errorCode) {
301
1.89k
    int32_t cpLength=U16_LENGTH(c);
302
1.89k
    if(remainingCapacity<cpLength && !resize(cpLength, errorCode)) {
303
0
        return false;
304
0
    }
305
1.89k
    remainingCapacity-=cpLength;
306
1.89k
    if(cpLength==1) {
307
1.73k
        *limit++ = static_cast<char16_t>(c);
308
1.73k
    } else {
309
153
        limit[0]=U16_LEAD(c);
310
153
        limit[1]=U16_TRAIL(c);
311
153
        limit+=2;
312
153
    }
313
1.89k
    lastCC=0;
314
1.89k
    reorderStart=limit;
315
1.89k
    return true;
316
1.89k
}
317
318
84.3M
UBool ReorderingBuffer::appendZeroCC(const char16_t *s, const char16_t *sLimit, UErrorCode &errorCode) {
319
84.3M
    if(s==sLimit) {
320
1.13k
        return true;
321
1.13k
    }
322
84.3M
    int32_t length = static_cast<int32_t>(sLimit - s);
323
84.3M
    if(remainingCapacity<length && !resize(length, errorCode)) {
324
0
        return false;
325
0
    }
326
84.3M
    u_memcpy(limit, s, length);
327
84.3M
    limit+=length;
328
84.3M
    remainingCapacity-=length;
329
84.3M
    lastCC=0;
330
84.3M
    reorderStart=limit;
331
84.3M
    return true;
332
84.3M
}
333
334
4.64k
void ReorderingBuffer::remove() {
335
4.64k
    reorderStart=limit=start;
336
4.64k
    remainingCapacity=str.getCapacity();
337
4.64k
    lastCC=0;
338
4.64k
}
339
340
3.71k
void ReorderingBuffer::removeSuffix(int32_t suffixLength) {
341
3.71k
    if(suffixLength<(limit-start)) {
342
2.52k
        limit-=suffixLength;
343
2.52k
        remainingCapacity+=suffixLength;
344
2.52k
    } else {
345
1.18k
        limit=start;
346
1.18k
        remainingCapacity=str.getCapacity();
347
1.18k
    }
348
3.71k
    lastCC=0;
349
3.71k
    reorderStart=limit;
350
3.71k
}
351
352
45.2k
UBool ReorderingBuffer::resize(int32_t appendLength, UErrorCode &errorCode) {
353
45.2k
    int32_t reorderStartIndex = static_cast<int32_t>(reorderStart - start);
354
45.2k
    int32_t length = static_cast<int32_t>(limit - start);
355
45.2k
    str.releaseBuffer(length);
356
45.2k
    int32_t newCapacity=length+appendLength;
357
45.2k
    int32_t doubleCapacity=2*str.getCapacity();
358
45.2k
    if(newCapacity<doubleCapacity) {
359
45.2k
        newCapacity=doubleCapacity;
360
45.2k
    }
361
45.2k
    if(newCapacity<256) {
362
37.1k
        newCapacity=256;
363
37.1k
    }
364
45.2k
    start=str.getBuffer(newCapacity);
365
45.2k
    if(start==nullptr) {
366
        // getBuffer() already did str.setToBogus()
367
0
        errorCode=U_MEMORY_ALLOCATION_ERROR;
368
0
        return false;
369
0
    }
370
45.2k
    reorderStart=start+reorderStartIndex;
371
45.2k
    limit=start+length;
372
45.2k
    remainingCapacity=str.getCapacity()-length;
373
45.2k
    return true;
374
45.2k
}
375
376
13.5M
void ReorderingBuffer::skipPrevious() {
377
13.5M
    codePointLimit=codePointStart;
378
13.5M
    char16_t c=*--codePointStart;
379
13.5M
    if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(*(codePointStart-1))) {
380
760k
        --codePointStart;
381
760k
    }
382
13.5M
}
383
384
890M
uint8_t ReorderingBuffer::previousCC() {
385
890M
    codePointLimit=codePointStart;
386
890M
    if(reorderStart>=codePointStart) {
387
869k
        return 0;
388
869k
    }
389
889M
    UChar32 c=*--codePointStart;
390
889M
    char16_t c2;
391
889M
    if(U16_IS_TRAIL(c) && start<codePointStart && U16_IS_LEAD(c2=*(codePointStart-1))) {
392
642k
        --codePointStart;
393
642k
        c=U16_GET_SUPPLEMENTARY(c2, c);
394
642k
    }
395
889M
    return impl.getCCFromYesOrMaybeYesCP(c);
396
890M
}
397
398
// Inserts c somewhere before the last character.
399
// Requires 0<cc<lastCC which implies reorderStart<limit.
400
13.5M
void ReorderingBuffer::insert(UChar32 c, uint8_t cc) {
401
890M
    for(setIterator(), skipPrevious(); previousCC()>cc;) {}
402
    // insert c at codePointLimit, after the character with prevCC<=cc
403
13.5M
    char16_t *q=limit;
404
13.5M
    char16_t *r=limit+=U16_LENGTH(c);
405
891M
    do {
406
891M
        *--r=*--q;
407
891M
    } while(codePointLimit!=q);
408
13.5M
    writeCodePoint(q, c);
409
13.5M
    if(cc<=1) {
410
11.3k
        reorderStart=r;
411
11.3k
    }
412
13.5M
}
413
414
// Normalizer2Impl --------------------------------------------------------- ***
415
416
struct CanonIterData : public UMemory {
417
    CanonIterData(UErrorCode &errorCode);
418
    ~CanonIterData();
419
    void addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode);
420
    UMutableCPTrie *mutableTrie;
421
    UCPTrie *trie;
422
    UVector canonStartSets;  // contains UnicodeSet *
423
};
424
425
0
Normalizer2Impl::~Normalizer2Impl() {
426
0
    delete fCanonIterData;
427
0
}
428
429
void
430
Normalizer2Impl::init(const int32_t *inIndexes, const UCPTrie *inTrie,
431
15
                      const uint16_t *inExtraData, const uint8_t *inSmallFCD) {
432
15
    minDecompNoCP = static_cast<char16_t>(inIndexes[IX_MIN_DECOMP_NO_CP]);
433
15
    minCompNoMaybeCP = static_cast<char16_t>(inIndexes[IX_MIN_COMP_NO_MAYBE_CP]);
434
15
    minLcccCP = static_cast<char16_t>(inIndexes[IX_MIN_LCCC_CP]);
435
436
15
    minYesNo = static_cast<uint16_t>(inIndexes[IX_MIN_YES_NO]);
437
15
    minYesNoMappingsOnly = static_cast<uint16_t>(inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY]);
438
15
    minNoNo = static_cast<uint16_t>(inIndexes[IX_MIN_NO_NO]);
439
15
    minNoNoCompBoundaryBefore = static_cast<uint16_t>(inIndexes[IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]);
440
15
    minNoNoCompNoMaybeCC = static_cast<uint16_t>(inIndexes[IX_MIN_NO_NO_COMP_NO_MAYBE_CC]);
441
15
    minNoNoEmpty = static_cast<uint16_t>(inIndexes[IX_MIN_NO_NO_EMPTY]);
442
15
    limitNoNo = static_cast<uint16_t>(inIndexes[IX_LIMIT_NO_NO]);
443
15
    minMaybeNo = static_cast<uint16_t>(inIndexes[IX_MIN_MAYBE_NO]);
444
15
    minMaybeNoCombinesFwd = static_cast<uint16_t>(inIndexes[IX_MIN_MAYBE_NO_COMBINES_FWD]);
445
15
    minMaybeYes = static_cast<uint16_t>(inIndexes[IX_MIN_MAYBE_YES]);
446
15
    U_ASSERT((minMaybeNo & 7) == 0);  // 8-aligned for noNoDelta bit fields
447
15
    centerNoNoDelta = (minMaybeNo >> DELTA_SHIFT) - MAX_DELTA - 1;
448
449
15
    normTrie=inTrie;
450
15
    extraData=inExtraData;
451
15
    smallFCD=inSmallFCD;
452
15
}
453
454
U_CDECL_BEGIN
455
456
static uint32_t U_CALLCONV
457
2.09k
segmentStarterMapper(const void * /*context*/, uint32_t value) {
458
2.09k
    return value&CANON_NOT_SEGMENT_STARTER;
459
2.09k
}
460
461
U_CDECL_END
462
463
void
464
0
Normalizer2Impl::addLcccChars(UnicodeSet &set) const {
465
0
    UChar32 start = 0, end;
466
0
    uint32_t norm16;
467
0
    while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT,
468
0
                                   nullptr, nullptr, &norm16)) >= 0) {
469
0
        if (norm16 > Normalizer2Impl::MIN_NORMAL_MAYBE_YES &&
470
0
                norm16 != Normalizer2Impl::JAMO_VT) {
471
0
            set.add(start, end);
472
0
        } else if (minNoNoCompNoMaybeCC <= norm16 && norm16 < limitNoNo) {
473
0
            uint16_t fcd16 = getFCD16(start);
474
0
            if (fcd16 > 0xff) { set.add(start, end); }
475
0
        }
476
0
        start = end + 1;
477
0
    }
478
0
}
479
480
void
481
10
Normalizer2Impl::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const {
482
    // Add the start code point of each same-value range of the trie.
483
10
    UChar32 start = 0, end;
484
10
    uint32_t value;
485
48.5k
    while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT,
486
48.5k
                                   nullptr, nullptr, &value)) >= 0) {
487
48.5k
        sa->add(sa->set, start);
488
48.5k
        if (start != end && isAlgorithmicNoNo(static_cast<uint16_t>(value)) &&
489
48.5k
                (value & Normalizer2Impl::DELTA_TCCC_MASK) > Normalizer2Impl::DELTA_TCCC_1) {
490
            // Range of code points with same-norm16-value algorithmic decompositions.
491
            // They might have different non-zero FCD16 values.
492
16
            uint16_t prevFCD16 = getFCD16(start);
493
83
            while (++start <= end) {
494
67
                uint16_t fcd16 = getFCD16(start);
495
67
                if (fcd16 != prevFCD16) {
496
1
                    sa->add(sa->set, start);
497
1
                    prevFCD16 = fcd16;
498
1
                }
499
67
            }
500
16
        }
501
48.5k
        start = end + 1;
502
48.5k
    }
503
504
    /* add Hangul LV syllables and LV+1 because of skippables */
505
4.00k
    for(char16_t c=Hangul::HANGUL_BASE; c<Hangul::HANGUL_LIMIT; c+=Hangul::JAMO_T_COUNT) {
506
3.99k
        sa->add(sa->set, c);
507
3.99k
        sa->add(sa->set, c+1);
508
3.99k
    }
509
10
    sa->add(sa->set, Hangul::HANGUL_LIMIT); /* add Hangul+1 to continue with other properties */
510
10
}
511
512
void
513
1
Normalizer2Impl::addCanonIterPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const {
514
    // Add the start code point of each same-value range of the canonical iterator data trie.
515
1
    if (!ensureCanonIterData(errorCode)) { return; }
516
    // Currently only used for the SEGMENT_STARTER property.
517
1
    UChar32 start = 0, end;
518
1
    uint32_t value;
519
470
    while ((end = ucptrie_getRange(fCanonIterData->trie, start, UCPMAP_RANGE_NORMAL, 0,
520
470
                                   segmentStarterMapper, nullptr, &value)) >= 0) {
521
469
        sa->add(sa->set, start);
522
469
        start = end + 1;
523
469
    }
524
1
}
525
526
const char16_t *
527
Normalizer2Impl::copyLowPrefixFromNulTerminated(const char16_t *src,
528
                                                UChar32 minNeedDataCP,
529
                                                ReorderingBuffer *buffer,
530
0
                                                UErrorCode &errorCode) const {
531
    // Make some effort to support NUL-terminated strings reasonably.
532
    // Take the part of the fast quick check loop that does not look up
533
    // data and check the first part of the string.
534
    // After this prefix, determine the string length to simplify the rest
535
    // of the code.
536
0
    const char16_t *prevSrc=src;
537
0
    char16_t c;
538
0
    while((c=*src++)<minNeedDataCP && c!=0) {}
539
    // Back out the last character for full processing.
540
    // Copy this prefix.
541
0
    if(--src!=prevSrc) {
542
0
        if(buffer!=nullptr) {
543
0
            buffer->appendZeroCC(prevSrc, src, errorCode);
544
0
        }
545
0
    }
546
0
    return src;
547
0
}
548
549
UnicodeString &
550
Normalizer2Impl::decompose(const UnicodeString &src, UnicodeString &dest,
551
0
                           UErrorCode &errorCode) const {
552
0
    if(U_FAILURE(errorCode)) {
553
0
        dest.setToBogus();
554
0
        return dest;
555
0
    }
556
0
    const char16_t *sArray=src.getBuffer();
557
0
    if(&dest==&src || sArray==nullptr) {
558
0
        errorCode=U_ILLEGAL_ARGUMENT_ERROR;
559
0
        dest.setToBogus();
560
0
        return dest;
561
0
    }
562
0
    decompose(sArray, sArray+src.length(), dest, src.length(), errorCode);
563
0
    return dest;
564
0
}
565
566
void
567
Normalizer2Impl::decompose(const char16_t *src, const char16_t *limit,
568
                           UnicodeString &dest,
569
                           int32_t destLengthEstimate,
570
45.6k
                           UErrorCode &errorCode) const {
571
45.6k
    if(destLengthEstimate<0 && limit!=nullptr) {
572
0
        destLengthEstimate = static_cast<int32_t>(limit - src);
573
0
    }
574
45.6k
    dest.remove();
575
45.6k
    ReorderingBuffer buffer(*this, dest);
576
45.6k
    if(buffer.init(destLengthEstimate, errorCode)) {
577
45.6k
        decompose(src, limit, &buffer, errorCode);
578
45.6k
    }
579
45.6k
}
580
581
// Dual functionality:
582
// buffer!=nullptr: normalize
583
// buffer==nullptr: isNormalized/spanQuickCheckYes
584
const char16_t *
585
Normalizer2Impl::decompose(const char16_t *src, const char16_t *limit,
586
                           ReorderingBuffer *buffer,
587
51.0M
                           UErrorCode &errorCode) const {
588
51.0M
    UChar32 minNoCP=minDecompNoCP;
589
51.0M
    if(limit==nullptr) {
590
0
        src=copyLowPrefixFromNulTerminated(src, minNoCP, buffer, errorCode);
591
0
        if(U_FAILURE(errorCode)) {
592
0
            return src;
593
0
        }
594
0
        limit=u_strchr(src, 0);
595
0
    }
596
597
51.0M
    const char16_t *prevSrc;
598
51.0M
    UChar32 c=0;
599
51.0M
    uint16_t norm16=0;
600
601
    // only for quick check
602
51.0M
    const char16_t *prevBoundary=src;
603
51.0M
    uint8_t prevCC=0;
604
605
122M
    for(;;) {
606
        // count code units below the minimum or with irrelevant data for the quick check
607
145M
        for(prevSrc=src; src!=limit;) {
608
94.2M
            if( (c=*src)<minNoCP ||
609
94.2M
                isMostDecompYesAndZeroCC(norm16=UCPTRIE_FAST_BMP_GET(normTrie, UCPTRIE_16, c))
610
94.2M
            ) {
611
23.0M
                ++src;
612
71.1M
            } else if(!U16_IS_LEAD(c)) {
613
70.8M
                break;
614
70.8M
            } else {
615
360k
                char16_t c2;
616
360k
                if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
617
358k
                    c=U16_GET_SUPPLEMENTARY(c, c2);
618
358k
                    norm16=UCPTRIE_FAST_SUPP_GET(normTrie, UCPTRIE_16, c);
619
358k
                    if(isMostDecompYesAndZeroCC(norm16)) {
620
28.0k
                        src+=2;
621
330k
                    } else {
622
330k
                        break;
623
330k
                    }
624
358k
                } else {
625
2.18k
                    ++src;  // unpaired lead surrogate: inert
626
2.18k
                }
627
360k
            }
628
94.2M
        }
629
        // copy these code units all at once
630
122M
        if(src!=prevSrc) {
631
19.8M
            if(buffer!=nullptr) {
632
19.8M
                if(!buffer->appendZeroCC(prevSrc, src, errorCode)) {
633
0
                    break;
634
0
                }
635
19.8M
            } else {
636
1.40k
                prevCC=0;
637
1.40k
                prevBoundary=src;
638
1.40k
            }
639
19.8M
        }
640
122M
        if(src==limit) {
641
51.0M
            break;
642
51.0M
        }
643
644
        // Check one above-minimum, relevant code point.
645
71.1M
        src+=U16_LENGTH(c);
646
71.1M
        if(buffer!=nullptr) {
647
71.1M
            if(!decompose(c, norm16, *buffer, errorCode)) {
648
0
                break;
649
0
            }
650
71.1M
        } else {
651
7.03k
            if(isDecompYes(norm16)) {
652
6.06k
                uint8_t cc=getCCFromYesOrMaybeYes(norm16);
653
6.06k
                if(prevCC<=cc || cc==0) {
654
5.92k
                    prevCC=cc;
655
5.92k
                    if(cc<=1) {
656
1.37k
                        prevBoundary=src;
657
1.37k
                    }
658
5.92k
                    continue;
659
5.92k
                }
660
6.06k
            }
661
1.11k
            return prevBoundary;  // "no" or cc out of order
662
7.03k
        }
663
71.1M
    }
664
51.0M
    return src;
665
51.0M
}
666
667
// Decompose a short piece of text which is likely to contain characters that
668
// fail the quick check loop and/or where the quick check loop's overhead
669
// is unlikely to be amortized.
670
// Called by the compose() and makeFCD() implementations.
671
const char16_t *
672
Normalizer2Impl::decomposeShort(const char16_t *src, const char16_t *limit,
673
                                UBool stopAtCompBoundary, UBool onlyContiguous,
674
16.0M
                                ReorderingBuffer &buffer, UErrorCode &errorCode) const {
675
16.0M
    if (U_FAILURE(errorCode)) {
676
0
        return nullptr;
677
0
    }
678
29.2M
    while(src<limit) {
679
13.2M
        if (stopAtCompBoundary && *src < minCompNoMaybeCP) {
680
1.48k
            return src;
681
1.48k
        }
682
13.2M
        const char16_t *prevSrc = src;
683
13.2M
        UChar32 c;
684
13.2M
        uint16_t norm16;
685
13.2M
        UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, src, limit, c, norm16);
686
13.2M
        if (stopAtCompBoundary && norm16HasCompBoundaryBefore(norm16)) {
687
32.6k
            return prevSrc;
688
32.6k
        }
689
13.2M
        if(!decompose(c, norm16, buffer, errorCode)) {
690
0
            return nullptr;
691
0
        }
692
13.2M
        if (stopAtCompBoundary && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) {
693
0
            return src;
694
0
        }
695
13.2M
    }
696
15.9M
    return src;
697
16.0M
}
698
699
UBool Normalizer2Impl::decompose(UChar32 c, uint16_t norm16,
700
                                 ReorderingBuffer &buffer,
701
84.3M
                                 UErrorCode &errorCode) const {
702
    // get the decomposition and the lead and trail cc's
703
84.3M
    if (norm16 >= limitNoNo) {
704
35.7M
        if (isMaybeYesOrNonZeroCC(norm16)) {
705
35.7M
            return buffer.append(c, getCCFromYesOrMaybeYes(norm16), errorCode);
706
35.7M
        } else if (norm16 < minMaybeNo) {
707
            // Maps to an isCompYesAndZeroCC.
708
12.9k
            c=mapAlgorithmic(c, norm16);
709
12.9k
            norm16=getRawNorm16(c);
710
12.9k
        }
711
35.7M
    }
712
48.6M
    if (norm16 < minYesNo) {
713
        // c does not decompose
714
19.0k
        return buffer.append(c, 0, errorCode);
715
48.6M
    } else if(isHangulLV(norm16) || isHangulLVT(norm16)) {
716
        // Hangul syllable: decompose algorithmically
717
10.0M
        char16_t jamos[3];
718
10.0M
        return buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode);
719
10.0M
    }
720
    // c decomposes, get everything from the variable-length extra data
721
38.5M
    const uint16_t *mapping=getData(norm16);
722
38.5M
    uint16_t firstUnit=*mapping;
723
38.5M
    int32_t length=firstUnit&MAPPING_LENGTH_MASK;
724
38.5M
    uint8_t leadCC, trailCC;
725
38.5M
    trailCC = static_cast<uint8_t>(firstUnit >> 8);
726
38.5M
    if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
727
12.6M
        leadCC = static_cast<uint8_t>(*(mapping - 1) >> 8);
728
25.8M
    } else {
729
25.8M
        leadCC=0;
730
25.8M
    }
731
38.5M
    return buffer.append(reinterpret_cast<const char16_t*>(mapping) + 1, length, true, leadCC, trailCC, errorCode);
732
48.6M
}
733
734
// Dual functionality:
735
// sink != nullptr: normalize
736
// sink == nullptr: isNormalized/spanQuickCheckYes
737
const uint8_t *
738
Normalizer2Impl::decomposeUTF8(uint32_t options,
739
                               const uint8_t *src, const uint8_t *limit,
740
0
                               ByteSink *sink, Edits *edits, UErrorCode &errorCode) const {
741
0
    U_ASSERT(limit != nullptr);
742
0
    UnicodeString s16;
743
0
    uint8_t minNoLead = leadByteForCP(minDecompNoCP);
744
745
0
    const uint8_t *prevBoundary = src;
746
    // only for quick check
747
0
    uint8_t prevCC = 0;
748
749
0
    for (;;) {
750
        // Fast path: Scan over a sequence of characters below the minimum "no" code point,
751
        // or with (decompYes && ccc==0) properties.
752
0
        const uint8_t *fastStart = src;
753
0
        const uint8_t *prevSrc;
754
0
        uint16_t norm16 = 0;
755
756
0
        for (;;) {
757
0
            if (src == limit) {
758
0
                if (prevBoundary != limit && sink != nullptr) {
759
0
                    ByteSinkUtil::appendUnchanged(prevBoundary, limit,
760
0
                                                  *sink, options, edits, errorCode);
761
0
                }
762
0
                return src;
763
0
            }
764
0
            if (*src < minNoLead) {
765
0
                ++src;
766
0
            } else {
767
0
                prevSrc = src;
768
0
                UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16);
769
0
                if (!isMostDecompYesAndZeroCC(norm16)) {
770
0
                    break;
771
0
                }
772
0
            }
773
0
        }
774
        // isMostDecompYesAndZeroCC(norm16) is false, that is, norm16>=minYesNo,
775
        // and the current character at [prevSrc..src[ is not a common case with cc=0
776
        // (MIN_NORMAL_MAYBE_YES or JAMO_VT).
777
        // It could still be a maybeYes with cc=0.
778
0
        if (prevSrc != fastStart) {
779
            // The fast path looped over yes/0 characters before the current one.
780
0
            if (sink != nullptr &&
781
0
                    !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
782
0
                                                   *sink, options, edits, errorCode)) {
783
0
                break;
784
0
            }
785
0
            prevBoundary = prevSrc;
786
0
            prevCC = 0;
787
0
        }
788
789
        // Medium-fast path: Quick check.
790
0
        if (isMaybeYesOrNonZeroCC(norm16)) {
791
            // Does not decompose.
792
0
            uint8_t cc = getCCFromYesOrMaybeYes(norm16);
793
0
            if (prevCC <= cc || cc == 0) {
794
0
                prevCC = cc;
795
0
                if (cc <= 1) {
796
0
                    if (sink != nullptr &&
797
0
                            !ByteSinkUtil::appendUnchanged(prevBoundary, src,
798
0
                                                           *sink, options, edits, errorCode)) {
799
0
                        break;
800
0
                    }
801
0
                    prevBoundary = src;
802
0
                }
803
0
                continue;
804
0
            }
805
0
        }
806
0
        if (sink == nullptr) {
807
0
            return prevBoundary;  // quick check: "no" or cc out of order
808
0
        }
809
810
        // Slow path
811
        // Decompose up to and including the current character.
812
0
        if (prevBoundary != prevSrc && norm16HasDecompBoundaryBefore(norm16)) {
813
0
            if (!ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
814
0
                                               *sink, options, edits, errorCode)) {
815
0
                break;
816
0
            }
817
0
            prevBoundary = prevSrc;
818
0
        }
819
0
        ReorderingBuffer buffer(*this, s16, errorCode);
820
0
        if (U_FAILURE(errorCode)) {
821
0
            break;
822
0
        }
823
0
        decomposeShort(prevBoundary, src, STOP_AT_LIMIT, false /* onlyContiguous */,
824
0
                       buffer, errorCode);
825
        // Decompose until the next boundary.
826
0
        if (buffer.getLastCC() > 1) {
827
0
            src = decomposeShort(src, limit, STOP_AT_DECOMP_BOUNDARY, false /* onlyContiguous */,
828
0
                                 buffer, errorCode);
829
0
        }
830
0
        if (U_FAILURE(errorCode)) {
831
0
            break;
832
0
        }
833
0
        if ((src - prevSrc) > INT32_MAX) {  // guard before buffer.equals()
834
0
            errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
835
0
            break;
836
0
        }
837
        // We already know there was a change if the original character decomposed;
838
        // otherwise compare.
839
0
        if (isMaybeYesOrNonZeroCC(norm16) && buffer.equals(prevBoundary, src)) {
840
0
            if (!ByteSinkUtil::appendUnchanged(prevBoundary, src,
841
0
                                               *sink, options, edits, errorCode)) {
842
0
                break;
843
0
            }
844
0
        } else {
845
0
            if (!ByteSinkUtil::appendChange(prevBoundary, src, buffer.getStart(), buffer.length(),
846
0
                                            *sink, edits, errorCode)) {
847
0
                break;
848
0
            }
849
0
        }
850
0
        prevBoundary = src;
851
0
        prevCC = 0;
852
0
    }
853
0
    return src;
854
0
}
855
856
const uint8_t *
857
Normalizer2Impl::decomposeShort(const uint8_t *src, const uint8_t *limit,
858
                                StopAt stopAt, UBool onlyContiguous,
859
0
                                ReorderingBuffer &buffer, UErrorCode &errorCode) const {
860
0
    if (U_FAILURE(errorCode)) {
861
0
        return nullptr;
862
0
    }
863
0
    while (src < limit) {
864
0
        const uint8_t *prevSrc = src;
865
0
        uint16_t norm16;
866
0
        UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16);
867
        // Get the decomposition and the lead and trail cc's.
868
0
        UChar32 c = U_SENTINEL;
869
0
        if (norm16 >= limitNoNo) {
870
0
            if (isMaybeYesOrNonZeroCC(norm16)) {
871
                // No comp boundaries around this character.
872
0
                uint8_t cc = getCCFromYesOrMaybeYes(norm16);
873
0
                if (cc == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) {
874
0
                    return prevSrc;
875
0
                }
876
0
                c = codePointFromValidUTF8(prevSrc, src);
877
0
                if (!buffer.append(c, cc, errorCode)) {
878
0
                    return nullptr;
879
0
                }
880
0
                if (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1) {
881
0
                    return src;
882
0
                }
883
0
                continue;
884
0
            } else if (norm16 < minMaybeNo) {
885
                // Maps to an isCompYesAndZeroCC.
886
0
                if (stopAt != STOP_AT_LIMIT) {
887
0
                    return prevSrc;
888
0
                }
889
0
                c = codePointFromValidUTF8(prevSrc, src);
890
0
                c = mapAlgorithmic(c, norm16);
891
0
                norm16 = getRawNorm16(c);
892
0
            }
893
0
        } else if (stopAt != STOP_AT_LIMIT && norm16 < minNoNoCompNoMaybeCC) {
894
0
            return prevSrc;
895
0
        }
896
        // norm16!=INERT guarantees that [prevSrc, src[ is valid UTF-8.
897
        // We do not see invalid UTF-8 here because
898
        // its norm16==INERT is normalization-inert,
899
        // so it gets copied unchanged in the fast path,
900
        // and we stop the slow path where invalid UTF-8 begins.
901
        // c >= 0 is the result of an algorithmic mapping.
902
0
        U_ASSERT(c >= 0 || norm16 != INERT);
903
0
        if (norm16 < minYesNo) {
904
0
            if (c < 0) {
905
0
                c = codePointFromValidUTF8(prevSrc, src);
906
0
            }
907
            // does not decompose
908
0
            if (!buffer.append(c, 0, errorCode)) {
909
0
                return nullptr;
910
0
            }
911
0
        } else if (isHangulLV(norm16) || isHangulLVT(norm16)) {
912
            // Hangul syllable: decompose algorithmically
913
0
            if (c < 0) {
914
0
                c = codePointFromValidUTF8(prevSrc, src);
915
0
            }
916
0
            char16_t jamos[3];
917
0
            if (!buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode)) {
918
0
                return nullptr;
919
0
            }
920
0
        } else {
921
            // The character decomposes, get everything from the variable-length extra data.
922
0
            const uint16_t *mapping = getData(norm16);
923
0
            uint16_t firstUnit = *mapping;
924
0
            int32_t length = firstUnit & MAPPING_LENGTH_MASK;
925
0
            uint8_t trailCC = static_cast<uint8_t>(firstUnit >> 8);
926
0
            uint8_t leadCC;
927
0
            if (firstUnit & MAPPING_HAS_CCC_LCCC_WORD) {
928
0
                leadCC = static_cast<uint8_t>(*(mapping - 1) >> 8);
929
0
            } else {
930
0
                leadCC = 0;
931
0
            }
932
0
            if (leadCC == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) {
933
0
                return prevSrc;
934
0
            }
935
0
            if (!buffer.append(reinterpret_cast<const char16_t*>(mapping) + 1, length, true, leadCC, trailCC, errorCode)) {
936
0
                return nullptr;
937
0
            }
938
0
        }
939
0
        if ((stopAt == STOP_AT_COMP_BOUNDARY && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) ||
940
0
                (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1)) {
941
0
            return src;
942
0
        }
943
0
    }
944
0
    return src;
945
0
}
946
947
const char16_t *
948
9.08M
Normalizer2Impl::getDecomposition(UChar32 c, char16_t buffer[4], int32_t &length) const {
949
9.08M
    uint16_t norm16;
950
9.08M
    if(c<minDecompNoCP || isMaybeYesOrNonZeroCC(norm16=getNorm16(c))) {
951
        // c does not decompose
952
4.57k
        return nullptr;
953
4.57k
    }
954
9.07M
    const char16_t *decomp = nullptr;
955
9.07M
    if(isDecompNoAlgorithmic(norm16)) {
956
        // Maps to an isCompYesAndZeroCC.
957
9.05k
        c=mapAlgorithmic(c, norm16);
958
9.05k
        decomp=buffer;
959
9.05k
        length=0;
960
9.05k
        U16_APPEND_UNSAFE(buffer, length, c);
961
        // The mapping might decompose further.
962
9.05k
        norm16 = getRawNorm16(c);
963
9.05k
    }
964
9.07M
    if (norm16 < minYesNo) {
965
18.5k
        return decomp;
966
9.05M
    } else if(isHangulLV(norm16) || isHangulLVT(norm16)) {
967
        // Hangul syllable: decompose algorithmically
968
2.55k
        length=Hangul::decompose(c, buffer);
969
2.55k
        return buffer;
970
2.55k
    }
971
    // c decomposes, get everything from the variable-length extra data
972
9.05M
    const uint16_t *mapping=getData(norm16);
973
9.05M
    length=*mapping&MAPPING_LENGTH_MASK;
974
9.05M
    return reinterpret_cast<const char16_t*>(mapping) + 1;
975
9.07M
}
976
977
// The capacity of the buffer must be 30=MAPPING_LENGTH_MASK-1
978
// so that a raw mapping fits that consists of one unit ("rm0")
979
// plus all but the first two code units of the normal mapping.
980
// The maximum length of a normal mapping is 31=MAPPING_LENGTH_MASK.
981
const char16_t *
982
2.94k
Normalizer2Impl::getRawDecomposition(UChar32 c, char16_t buffer[30], int32_t &length) const {
983
2.94k
    uint16_t norm16;
984
2.94k
    if(c<minDecompNoCP || isDecompYes(norm16=getNorm16(c))) {
985
        // c does not decompose
986
2.52k
        return nullptr;
987
2.52k
    } else if(isHangulLV(norm16) || isHangulLVT(norm16)) {
988
        // Hangul syllable: decompose algorithmically
989
142
        Hangul::getRawDecomposition(c, buffer);
990
142
        length=2;
991
142
        return buffer;
992
282
    } else if(isDecompNoAlgorithmic(norm16)) {
993
79
        c=mapAlgorithmic(c, norm16);
994
79
        length=0;
995
79
        U16_APPEND_UNSAFE(buffer, length, c);
996
79
        return buffer;
997
79
    }
998
    // c decomposes, get everything from the variable-length extra data
999
203
    const uint16_t *mapping=getData(norm16);
1000
203
    uint16_t firstUnit=*mapping;
1001
203
    int32_t mLength=firstUnit&MAPPING_LENGTH_MASK;  // length of normal mapping
1002
203
    if(firstUnit&MAPPING_HAS_RAW_MAPPING) {
1003
        // Read the raw mapping from before the firstUnit and before the optional ccc/lccc word.
1004
        // Bit 7=MAPPING_HAS_CCC_LCCC_WORD
1005
37
        const uint16_t *rawMapping=mapping-((firstUnit>>7)&1)-1;
1006
37
        uint16_t rm0=*rawMapping;
1007
37
        if(rm0<=MAPPING_LENGTH_MASK) {
1008
8
            length=rm0;
1009
8
            return reinterpret_cast<const char16_t*>(rawMapping) - rm0;
1010
29
        } else {
1011
            // Copy the normal mapping and replace its first two code units with rm0.
1012
29
            buffer[0] = static_cast<char16_t>(rm0);
1013
29
            u_memcpy(buffer + 1, reinterpret_cast<const char16_t*>(mapping) + 1 + 2, mLength - 2);
1014
29
            length=mLength-1;
1015
29
            return buffer;
1016
29
        }
1017
166
    } else {
1018
166
        length=mLength;
1019
166
        return reinterpret_cast<const char16_t*>(mapping) + 1;
1020
166
    }
1021
203
}
1022
1023
void Normalizer2Impl::decomposeAndAppend(const char16_t *src, const char16_t *limit,
1024
                                         UBool doDecompose,
1025
                                         UnicodeString &safeMiddle,
1026
                                         ReorderingBuffer &buffer,
1027
1.74k
                                         UErrorCode &errorCode) const {
1028
1.74k
    buffer.copyReorderableSuffixTo(safeMiddle);
1029
1.74k
    if(doDecompose) {
1030
872
        decompose(src, limit, &buffer, errorCode);
1031
872
        return;
1032
872
    }
1033
    // Just merge the strings at the boundary.
1034
872
    bool isFirst = true;
1035
872
    uint8_t firstCC = 0, prevCC = 0, cc;
1036
872
    const char16_t *p = src;
1037
11.4k
    while (p != limit) {
1038
10.9k
        const char16_t *codePointStart = p;
1039
10.9k
        UChar32 c;
1040
10.9k
        uint16_t norm16;
1041
10.9k
        UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16);
1042
10.9k
        if ((cc = getCC(norm16)) == 0) {
1043
412
            p = codePointStart;
1044
412
            break;
1045
412
        }
1046
10.5k
        if (isFirst) {
1047
221
            firstCC = cc;
1048
221
            isFirst = false;
1049
221
        }
1050
10.5k
        prevCC = cc;
1051
10.5k
    }
1052
872
    if(limit==nullptr) {  // appendZeroCC() needs limit!=nullptr
1053
0
        limit=u_strchr(p, 0);
1054
0
    }
1055
1056
872
    if (buffer.append(src, static_cast<int32_t>(p - src), false, firstCC, prevCC, errorCode)) {
1057
872
        buffer.appendZeroCC(p, limit, errorCode);
1058
872
    }
1059
872
}
1060
1061
872
UBool Normalizer2Impl::hasDecompBoundaryBefore(UChar32 c) const {
1062
872
    return c < minLcccCP || (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) ||
1063
872
        norm16HasDecompBoundaryBefore(getNorm16(c));
1064
872
}
1065
1066
16.2k
UBool Normalizer2Impl::norm16HasDecompBoundaryBefore(uint16_t norm16) const {
1067
16.2k
    if (norm16 < minNoNoCompNoMaybeCC) {
1068
1.85k
        return true;
1069
1.85k
    }
1070
14.4k
    if (norm16 >= limitNoNo) {
1071
1.66k
        return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT;
1072
1.66k
    }
1073
    // c decomposes, get everything from the variable-length extra data
1074
12.7k
    const uint16_t *mapping=getDataForYesOrNo(norm16);
1075
12.7k
    uint16_t firstUnit=*mapping;
1076
    // true if leadCC==0 (hasFCDBoundaryBefore())
1077
12.7k
    return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0;
1078
14.4k
}
1079
1080
872
UBool Normalizer2Impl::hasDecompBoundaryAfter(UChar32 c) const {
1081
872
    if (c < minDecompNoCP) {
1082
239
        return true;
1083
239
    }
1084
633
    if (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) {
1085
122
        return true;
1086
122
    }
1087
511
    return norm16HasDecompBoundaryAfter(getNorm16(c));
1088
633
}
1089
1090
14.8k
UBool Normalizer2Impl::norm16HasDecompBoundaryAfter(uint16_t norm16) const {
1091
14.8k
    if(norm16 <= minYesNo || isHangulLVT(norm16)) {
1092
387
        return true;
1093
387
    }
1094
14.4k
    if (norm16 >= limitNoNo) {
1095
1.66k
        if (isMaybeYesOrNonZeroCC(norm16)) {
1096
1.65k
            return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT;
1097
1.65k
        } else if (norm16 < minMaybeNo) {
1098
            // Maps to an isCompYesAndZeroCC.
1099
3
            return (norm16 & DELTA_TCCC_MASK) <= DELTA_TCCC_1;
1100
3
        }
1101
1.66k
    }
1102
    // c decomposes, get everything from the variable-length extra data
1103
12.8k
    const uint16_t *mapping=getData(norm16);
1104
12.8k
    uint16_t firstUnit=*mapping;
1105
    // decomp after-boundary: same as hasFCDBoundaryAfter(),
1106
    // fcd16<=1 || trailCC==0
1107
12.8k
    if(firstUnit>0x1ff) {
1108
12.8k
        return false;  // trailCC>1
1109
12.8k
    }
1110
27
    if(firstUnit<=0xff) {
1111
26
        return true;  // trailCC==0
1112
26
    }
1113
    // if(trailCC==1) test leadCC==0, same as checking for before-boundary
1114
    // true if leadCC==0 (hasFCDBoundaryBefore())
1115
1
    return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0;
1116
27
}
1117
1118
/*
1119
 * Finds the recomposition result for
1120
 * a forward-combining "lead" character,
1121
 * specified with a pointer to its compositions list,
1122
 * and a backward-combining "trail" character.
1123
 *
1124
 * If the lead and trail characters combine, then this function returns
1125
 * the following "compositeAndFwd" value:
1126
 * Bits 21..1  composite character
1127
 * Bit      0  set if the composite is a forward-combining starter
1128
 * otherwise it returns -1.
1129
 *
1130
 * The compositions list has (trail, compositeAndFwd) pair entries,
1131
 * encoded as either pairs or triples of 16-bit units.
1132
 * The last entry has the high bit of its first unit set.
1133
 *
1134
 * The list is sorted by ascending trail characters (there are no duplicates).
1135
 * A linear search is used.
1136
 *
1137
 * See normalizer2impl.h for a more detailed description
1138
 * of the compositions list format.
1139
 */
1140
7.97M
int32_t Normalizer2Impl::combine(const uint16_t *list, UChar32 trail) {
1141
7.97M
    uint16_t key1, firstUnit;
1142
7.97M
    if(trail<COMP_1_TRAIL_LIMIT) {
1143
        // trail character is 0..33FF
1144
        // result entry may have 2 or 3 units
1145
7.96M
        key1 = static_cast<uint16_t>(trail << 1);
1146
8.16M
        while(key1>(firstUnit=*list)) {
1147
203k
            list+=2+(firstUnit&COMP_1_TRIPLE);
1148
203k
        }
1149
7.96M
        if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
1150
7.95M
            if(firstUnit&COMP_1_TRIPLE) {
1151
1.39k
                return (static_cast<int32_t>(list[1]) << 16) | list[2];
1152
7.95M
            } else {
1153
7.95M
                return list[1];
1154
7.95M
            }
1155
7.95M
        }
1156
7.96M
    } else {
1157
        // trail character is 3400..10FFFF
1158
        // result entry has 3 units
1159
11.4k
        key1 = static_cast<uint16_t>(COMP_1_TRAIL_LIMIT +
1160
11.4k
                        (((trail>>COMP_1_TRAIL_SHIFT))&
1161
11.4k
                          ~COMP_1_TRIPLE));
1162
11.4k
        uint16_t key2 = static_cast<uint16_t>(trail << COMP_2_TRAIL_SHIFT);
1163
11.4k
        uint16_t secondUnit;
1164
24.9k
        for(;;) {
1165
24.9k
            if(key1>(firstUnit=*list)) {
1166
1.31k
                list+=2+(firstUnit&COMP_1_TRIPLE);
1167
23.6k
            } else if(key1==(firstUnit&COMP_1_TRAIL_MASK)) {
1168
23.1k
                if(key2>(secondUnit=list[1])) {
1169
12.9k
                    if(firstUnit&COMP_1_LAST_TUPLE) {
1170
651
                        break;
1171
12.2k
                    } else {
1172
12.2k
                        list+=3;
1173
12.2k
                    }
1174
12.9k
                } else if(key2==(secondUnit&COMP_2_TRAIL_MASK)) {
1175
9.99k
                    return (static_cast<int32_t>(secondUnit & ~COMP_2_TRAIL_MASK) << 16) | list[2];
1176
9.99k
                } else {
1177
268
                    break;
1178
268
                }
1179
23.1k
            } else {
1180
510
                break;
1181
510
            }
1182
24.9k
        }
1183
11.4k
    }
1184
11.0k
    return -1;
1185
7.97M
}
1186
1187
/**
1188
  * @param list some character's compositions list
1189
  * @param set recursively receives the composites from these compositions
1190
  */
1191
3.61M
void Normalizer2Impl::addComposites(const uint16_t *list, UnicodeSet &set) const {
1192
3.61M
    uint16_t firstUnit;
1193
3.61M
    int32_t compositeAndFwd;
1194
16.3M
    do {
1195
16.3M
        firstUnit=*list;
1196
16.3M
        if((firstUnit&COMP_1_TRIPLE)==0) {
1197
16.3M
            compositeAndFwd=list[1];
1198
16.3M
            list+=2;
1199
16.3M
        } else {
1200
17.0k
            compositeAndFwd = ((static_cast<int32_t>(list[1]) & ~COMP_2_TRAIL_MASK) << 16) | list[2];
1201
17.0k
            list+=3;
1202
17.0k
        }
1203
16.3M
        UChar32 composite=compositeAndFwd>>1;
1204
16.3M
        if((compositeAndFwd&1)!=0) {
1205
2.60M
            addComposites(getCompositionsListForComposite(getRawNorm16(composite)), set);
1206
2.60M
        }
1207
16.3M
        set.add(composite);
1208
16.3M
    } while((firstUnit&COMP_1_LAST_TUPLE)==0);
1209
3.61M
}
1210
1211
/*
1212
 * Recomposes the buffer text starting at recomposeStartIndex
1213
 * (which is in NFD - decomposed and canonically ordered),
1214
 * and truncates the buffer contents.
1215
 *
1216
 * Note that recomposition never lengthens the text:
1217
 * Any character consists of either one or two code units;
1218
 * a composition may contain at most one more code unit than the original starter,
1219
 * while the combining mark that is removed has at least one code unit.
1220
 */
1221
void Normalizer2Impl::recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex,
1222
8.01M
                                UBool onlyContiguous) const {
1223
8.01M
    char16_t *p=buffer.getStart()+recomposeStartIndex;
1224
8.01M
    char16_t *limit=buffer.getLimit();
1225
8.01M
    if(p==limit) {
1226
0
        return;
1227
0
    }
1228
1229
8.01M
    char16_t *starter, *pRemove, *q, *r;
1230
8.01M
    const uint16_t *compositionsList;
1231
8.01M
    UChar32 c, compositeAndFwd;
1232
8.01M
    uint16_t norm16;
1233
8.01M
    uint8_t cc, prevCC;
1234
8.01M
    UBool starterIsSupplementary;
1235
1236
    // Some of the following variables are not used until we have a forward-combining starter
1237
    // and are only initialized now to avoid compiler warnings.
1238
8.01M
    compositionsList=nullptr;  // used as indicator for whether we have a forward-combining starter
1239
8.01M
    starter=nullptr;
1240
8.01M
    starterIsSupplementary=false;
1241
8.01M
    prevCC=0;
1242
1243
52.3M
    for(;;) {
1244
52.3M
        UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16);
1245
52.3M
        cc=getCCFromYesOrMaybeYes(norm16);
1246
52.3M
        if( // this character combines backward and
1247
52.3M
            isMaybe(norm16) &&
1248
            // we have seen a starter that combines forward and
1249
52.3M
            compositionsList!=nullptr &&
1250
            // the backward-combining character is not blocked
1251
52.3M
            (prevCC<cc || prevCC==0)
1252
52.3M
        ) {
1253
7.98M
            if(isJamoVT(norm16)) {
1254
                // c is a Jamo V/T, see if we can compose it with the previous character.
1255
7.27k
                if(c<Hangul::JAMO_T_BASE) {
1256
                    // c is a Jamo Vowel, compose with previous Jamo L and following Jamo T.
1257
6.23k
                    char16_t prev = static_cast<char16_t>(*starter - Hangul::JAMO_L_BASE);
1258
6.23k
                    if(prev<Hangul::JAMO_L_COUNT) {
1259
5.06k
                        pRemove=p-1;
1260
5.06k
                        char16_t syllable = static_cast<char16_t>(
1261
5.06k
                            Hangul::HANGUL_BASE +
1262
5.06k
                             (prev*Hangul::JAMO_V_COUNT+(c-Hangul::JAMO_V_BASE))*
1263
5.06k
                             Hangul::JAMO_T_COUNT);
1264
5.06k
                        char16_t t;
1265
5.06k
                        if (p != limit && (t = static_cast<char16_t>(*p - Hangul::JAMO_T_BASE)) < Hangul::JAMO_T_COUNT) {
1266
1.96k
                            ++p;
1267
1.96k
                            syllable+=t;  // The next character was a Jamo T.
1268
1.96k
                        }
1269
5.06k
                        *starter=syllable;
1270
                        // remove the Jamo V/T
1271
5.06k
                        q=pRemove;
1272
5.06k
                        r=p;
1273
14.9k
                        while(r<limit) {
1274
9.90k
                            *q++=*r++;
1275
9.90k
                        }
1276
5.06k
                        limit=q;
1277
5.06k
                        p=pRemove;
1278
5.06k
                    }
1279
6.23k
                }
1280
                /*
1281
                 * No "else" for Jamo T:
1282
                 * Since the input is in NFD, there are no Hangul LV syllables that
1283
                 * a Jamo T could combine with.
1284
                 * All Jamo Ts are combined above when handling Jamo Vs.
1285
                 */
1286
7.27k
                if(p==limit) {
1287
3.21k
                    break;
1288
3.21k
                }
1289
4.06k
                compositionsList=nullptr;
1290
4.06k
                continue;
1291
7.97M
            } else if((compositeAndFwd=combine(compositionsList, c))>=0) {
1292
                // The starter and the combining mark (c) do combine.
1293
7.96M
                UChar32 composite=compositeAndFwd>>1;
1294
1295
                // Replace the starter with the composite, remove the combining mark.
1296
7.96M
                pRemove=p-U16_LENGTH(c);  // pRemove & p: start & limit of the combining mark
1297
7.96M
                if(starterIsSupplementary) {
1298
11.3k
                    if(U_IS_SUPPLEMENTARY(composite)) {
1299
                        // both are supplementary
1300
11.3k
                        starter[0]=U16_LEAD(composite);
1301
11.3k
                        starter[1]=U16_TRAIL(composite);
1302
11.3k
                    } else {
1303
0
                        *starter = static_cast<char16_t>(composite);
1304
                        // The composite is shorter than the starter,
1305
                        // move the intermediate characters forward one.
1306
0
                        starterIsSupplementary=false;
1307
0
                        q=starter+1;
1308
0
                        r=q+1;
1309
0
                        while(r<pRemove) {
1310
0
                            *q++=*r++;
1311
0
                        }
1312
0
                        --pRemove;
1313
0
                    }
1314
7.95M
                } else if(U_IS_SUPPLEMENTARY(composite)) {
1315
                    // The composite is longer than the starter,
1316
                    // move the intermediate characters back one.
1317
0
                    starterIsSupplementary=true;
1318
0
                    ++starter;  // temporarily increment for the loop boundary
1319
0
                    q=pRemove;
1320
0
                    r=++pRemove;
1321
0
                    while(starter<q) {
1322
0
                        *--r=*--q;
1323
0
                    }
1324
0
                    *starter=U16_TRAIL(composite);
1325
0
                    *--starter=U16_LEAD(composite);  // undo the temporary increment
1326
7.95M
                } else {
1327
                    // both are on the BMP
1328
7.95M
                    *starter = static_cast<char16_t>(composite);
1329
7.95M
                }
1330
1331
                /* remove the combining mark by moving the following text over it */
1332
7.96M
                if(pRemove<p) {
1333
7.96M
                    q=pRemove;
1334
7.96M
                    r=p;
1335
30.2M
                    while(r<limit) {
1336
22.2M
                        *q++=*r++;
1337
22.2M
                    }
1338
7.96M
                    limit=q;
1339
7.96M
                    p=pRemove;
1340
7.96M
                }
1341
                // Keep prevCC because we removed the combining mark.
1342
1343
7.96M
                if(p==limit) {
1344
218k
                    break;
1345
218k
                }
1346
                // Is the composite a starter that combines forward?
1347
7.74M
                if(compositeAndFwd&1) {
1348
5.48k
                    compositionsList=
1349
5.48k
                        getCompositionsListForComposite(getRawNorm16(composite));
1350
7.74M
                } else {
1351
7.74M
                    compositionsList=nullptr;
1352
7.74M
                }
1353
1354
                // We combined; continue with looking for compositions.
1355
7.74M
                continue;
1356
7.96M
            }
1357
7.98M
        }
1358
1359
        // no combination this time
1360
44.3M
        prevCC=cc;
1361
44.3M
        if(p==limit) {
1362
7.78M
            break;
1363
7.78M
        }
1364
1365
        // If c did not combine, then check if it is a starter.
1366
36.6M
        if(cc==0) {
1367
            // Found a new starter.
1368
31.2M
            if((compositionsList=getCompositionsListForDecompYes(norm16))!=nullptr) {
1369
                // It may combine with something, prepare for it.
1370
15.5M
                if(U_IS_BMP(c)) {
1371
15.5M
                    starterIsSupplementary=false;
1372
15.5M
                    starter=p-1;
1373
15.5M
                } else {
1374
10.0k
                    starterIsSupplementary=true;
1375
10.0k
                    starter=p-2;
1376
10.0k
                }
1377
15.5M
            }
1378
31.2M
        } else if(onlyContiguous) {
1379
            // FCC: no discontiguous compositions; any intervening character blocks.
1380
0
            compositionsList=nullptr;
1381
0
        }
1382
36.6M
    }
1383
8.01M
    buffer.setReorderingLimit(limit);
1384
8.01M
}
1385
1386
UChar32
1387
2.94k
Normalizer2Impl::composePair(UChar32 a, UChar32 b) const {
1388
2.94k
    uint16_t norm16=getNorm16(a);  // maps an out-of-range 'a' to inert norm16
1389
2.94k
    const uint16_t *list;
1390
2.94k
    if(isInert(norm16)) {
1391
2.34k
        return U_SENTINEL;
1392
2.34k
    } else if(norm16<minYesNoMappingsOnly) {
1393
        // a combines forward.
1394
249
        if(isJamoL(norm16)) {
1395
55
            if (b < Hangul::JAMO_V_BASE) {
1396
20
                return U_SENTINEL;
1397
20
            }
1398
35
            b-=Hangul::JAMO_V_BASE;
1399
35
            if(b<Hangul::JAMO_V_COUNT) {
1400
6
                return
1401
6
                    (Hangul::HANGUL_BASE+
1402
6
                     ((a-Hangul::JAMO_L_BASE)*Hangul::JAMO_V_COUNT+b)*
1403
6
                     Hangul::JAMO_T_COUNT);
1404
29
            } else {
1405
29
                return U_SENTINEL;
1406
29
            }
1407
194
        } else if(isHangulLV(norm16)) {
1408
69
            if (b <= Hangul::JAMO_T_BASE) {
1409
36
               return U_SENTINEL;
1410
36
            }
1411
33
            b-=Hangul::JAMO_T_BASE;
1412
33
            if(b<Hangul::JAMO_T_COUNT) {  // not b==0!
1413
6
                return a+b;
1414
27
            } else {
1415
27
                return U_SENTINEL;
1416
27
            }
1417
125
        } else {
1418
            // 'a' has a compositions list in extraData
1419
125
            list=getDataForYesOrNo(norm16);
1420
125
            if(norm16>minYesNo) {  // composite 'a' has both mapping & compositions list
1421
46
                list+=  // mapping pointer
1422
46
                    1+  // +1 to skip the first unit with the mapping length
1423
46
                    (*list&MAPPING_LENGTH_MASK);  // + mapping length
1424
46
            }
1425
125
        }
1426
353
    } else if(norm16<minMaybeNoCombinesFwd || MIN_NORMAL_MAYBE_YES<=norm16) {
1427
320
        return U_SENTINEL;
1428
320
    } else {
1429
33
        list=getDataForMaybe(norm16);
1430
33
        if(norm16<minMaybeYes) {  // composite 'a' has both mapping & compositions list
1431
8
            list+=  // mapping pointer
1432
8
                1+  // +1 to skip the first unit with the mapping length
1433
8
                (*list&MAPPING_LENGTH_MASK);  // + mapping length
1434
8
        }
1435
33
    }
1436
158
    if(b<0 || 0x10ffff<b) {  // combine(list, b) requires a valid code point b
1437
32
        return U_SENTINEL;
1438
32
    }
1439
126
#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC
1440
126
    return combine(list, b)>>1;
1441
#else
1442
    int32_t compositeAndFwd=combine(list, b);
1443
    return compositeAndFwd>=0 ? compositeAndFwd>>1 : U_SENTINEL;
1444
#endif
1445
158
}
1446
1447
// Very similar to composeQuickCheck(): Make the same changes in both places if relevant.
1448
// doCompose: normalize
1449
// !doCompose: isNormalized (buffer must be empty and initialized)
1450
UBool
1451
Normalizer2Impl::compose(const char16_t *src, const char16_t *limit,
1452
                         UBool onlyContiguous,
1453
                         UBool doCompose,
1454
                         ReorderingBuffer &buffer,
1455
67.9M
                         UErrorCode &errorCode) const {
1456
67.9M
    const char16_t *prevBoundary=src;
1457
67.9M
    UChar32 minNoMaybeCP=minCompNoMaybeCP;
1458
67.9M
    if(limit==nullptr) {
1459
0
        src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP,
1460
0
                                           doCompose ? &buffer : nullptr,
1461
0
                                           errorCode);
1462
0
        if(U_FAILURE(errorCode)) {
1463
0
            return false;
1464
0
        }
1465
0
        limit=u_strchr(src, 0);
1466
0
        if (prevBoundary != src) {
1467
0
            if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) {
1468
0
                prevBoundary = src;
1469
0
            } else {
1470
0
                buffer.removeSuffix(1);
1471
0
                prevBoundary = --src;
1472
0
            }
1473
0
        }
1474
0
    }
1475
1476
106M
    for (;;) {
1477
        // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point,
1478
        // or with (compYes && ccc==0) properties.
1479
106M
        const char16_t *prevSrc;
1480
106M
        UChar32 c = 0;
1481
106M
        uint16_t norm16 = 0;
1482
181M
        for (;;) {
1483
181M
            if (src == limit) {
1484
67.3M
                if (prevBoundary != limit && doCompose) {
1485
24.0M
                    buffer.appendZeroCC(prevBoundary, limit, errorCode);
1486
24.0M
                }
1487
67.3M
                return true;
1488
67.3M
            }
1489
114M
            if( (c=*src)<minNoMaybeCP ||
1490
114M
                isCompYesAndZeroCC(norm16=UCPTRIE_FAST_BMP_GET(normTrie, UCPTRIE_16, c))
1491
114M
            ) {
1492
75.7M
                ++src;
1493
75.7M
            } else {
1494
38.9M
                prevSrc = src++;
1495
38.9M
                if(!U16_IS_LEAD(c)) {
1496
38.8M
                    break;
1497
38.8M
                } else {
1498
83.3k
                    char16_t c2;
1499
83.3k
                    if(src!=limit && U16_IS_TRAIL(c2=*src)) {
1500
78.6k
                        ++src;
1501
78.6k
                        c=U16_GET_SUPPLEMENTARY(c, c2);
1502
78.6k
                        norm16=UCPTRIE_FAST_SUPP_GET(normTrie, UCPTRIE_16, c);
1503
78.6k
                        if(!isCompYesAndZeroCC(norm16)) {
1504
72.0k
                            break;
1505
72.0k
                        }
1506
78.6k
                    }
1507
83.3k
                }
1508
38.9M
            }
1509
114M
        }
1510
        // isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
1511
        // The current character is either a "noNo" (has a mapping)
1512
        // or a "maybeYes" / "maybeNo" (combines backward)
1513
        // or a "yesYes" with ccc!=0.
1514
        // It is not a Hangul syllable or Jamo L because those have "yes" properties.
1515
1516
        // Medium-fast path: Handle cases that do not require full decomposition and recomposition.
1517
38.9M
        if (norm16 < minMaybeNo) {  // minNoNo <= norm16 < minMaybeNo
1518
38.8M
            if (!doCompose) {
1519
578k
                return false;
1520
578k
            }
1521
            // Fast path for mapping a character that is immediately surrounded by boundaries.
1522
            // In this case, we need not decompose around the current character.
1523
38.2M
            if (isDecompNoAlgorithmic(norm16)) {
1524
                // Maps to a single isCompYesAndZeroCC character
1525
                // which also implies hasCompBoundaryBefore.
1526
11.6k
                if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) ||
1527
11.6k
                        hasCompBoundaryBefore(src, limit)) {
1528
10.5k
                    if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1529
0
                        break;
1530
0
                    }
1531
10.5k
                    if(!buffer.append(mapAlgorithmic(c, norm16), 0, errorCode)) {
1532
0
                        break;
1533
0
                    }
1534
10.5k
                    prevBoundary = src;
1535
10.5k
                    continue;
1536
10.5k
                }
1537
38.2M
            } else if (norm16 < minNoNoCompBoundaryBefore) {
1538
                // The mapping is comp-normalized which also implies hasCompBoundaryBefore.
1539
30.2M
                if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) ||
1540
30.2M
                        hasCompBoundaryBefore(src, limit)) {
1541
30.2M
                    if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1542
0
                        break;
1543
0
                    }
1544
30.2M
                    const char16_t *mapping = reinterpret_cast<const char16_t *>(getDataForYesOrNo(norm16));
1545
30.2M
                    int32_t length = *mapping++ & MAPPING_LENGTH_MASK;
1546
30.2M
                    if(!buffer.appendZeroCC(mapping, mapping + length, errorCode)) {
1547
0
                        break;
1548
0
                    }
1549
30.2M
                    prevBoundary = src;
1550
30.2M
                    continue;
1551
30.2M
                }
1552
30.2M
            } else if (norm16 >= minNoNoEmpty) {
1553
                // The current character maps to nothing.
1554
                // Simply omit it from the output if there is a boundary before _or_ after it.
1555
                // The character itself implies no boundaries.
1556
10.3k
                if (hasCompBoundaryBefore(src, limit) ||
1557
10.3k
                        hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) {
1558
9.51k
                    if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1559
0
                        break;
1560
0
                    }
1561
9.51k
                    prevBoundary = src;
1562
9.51k
                    continue;
1563
9.51k
                }
1564
10.3k
            }
1565
            // Other "noNo" type, or need to examine more text around this character:
1566
            // Fall through to the slow path.
1567
38.2M
        } else if (isJamoVT(norm16) && prevBoundary != prevSrc) {
1568
7.36k
            char16_t prev=*(prevSrc-1);
1569
7.36k
            if(c<Hangul::JAMO_T_BASE) {
1570
                // The current character is a Jamo Vowel,
1571
                // compose with previous Jamo L and following Jamo T.
1572
3.85k
                char16_t l = static_cast<char16_t>(prev - Hangul::JAMO_L_BASE);
1573
3.85k
                if(l<Hangul::JAMO_L_COUNT) {
1574
2.62k
                    if (!doCompose) {
1575
51
                        return false;
1576
51
                    }
1577
2.56k
                    int32_t t;
1578
2.56k
                    if (src != limit &&
1579
2.56k
                            0 < (t = (static_cast<int32_t>(*src) - Hangul::JAMO_T_BASE)) &&
1580
2.56k
                            t < Hangul::JAMO_T_COUNT) {
1581
                        // The next character is a Jamo T.
1582
468
                        ++src;
1583
2.10k
                    } else if (hasCompBoundaryBefore(src, limit)) {
1584
                        // No Jamo T follows, not even via decomposition.
1585
1.59k
                        t = 0;
1586
1.59k
                    } else {
1587
509
                        t = -1;
1588
509
                    }
1589
2.56k
                    if (t >= 0) {
1590
2.06k
                        UChar32 syllable = Hangul::HANGUL_BASE +
1591
2.06k
                            (l*Hangul::JAMO_V_COUNT + (c-Hangul::JAMO_V_BASE)) *
1592
2.06k
                            Hangul::JAMO_T_COUNT + t;
1593
2.06k
                        --prevSrc;  // Replace the Jamo L as well.
1594
2.06k
                        if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1595
0
                            break;
1596
0
                        }
1597
2.06k
                        if (!buffer.appendBMP(static_cast<char16_t>(syllable), 0, errorCode)) {
1598
0
                            break;
1599
0
                        }
1600
2.06k
                        prevBoundary = src;
1601
2.06k
                        continue;
1602
2.06k
                    }
1603
                    // If we see L+V+x where x!=T then we drop to the slow path,
1604
                    // decompose and recompose.
1605
                    // This is to deal with NFKC finding normal L and V but a
1606
                    // compatibility variant of a T.
1607
                    // We need to either fully compose that combination here
1608
                    // (which would complicate the code and may not work with strange custom data)
1609
                    // or use the slow path.
1610
2.56k
                }
1611
3.85k
            } else if (Hangul::isHangulLV(prev)) {
1612
                // The current character is a Jamo Trailing consonant,
1613
                // compose with previous Hangul LV that does not contain a Jamo T.
1614
981
                if (!doCompose) {
1615
20
                    return false;
1616
20
                }
1617
961
                UChar32 syllable = prev + c - Hangul::JAMO_T_BASE;
1618
961
                --prevSrc;  // Replace the Hangul LV as well.
1619
961
                if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1620
0
                    break;
1621
0
                }
1622
961
                if (!buffer.appendBMP(static_cast<char16_t>(syllable), 0, errorCode)) {
1623
0
                    break;
1624
0
                }
1625
961
                prevBoundary = src;
1626
961
                continue;
1627
961
            }
1628
            // No matching context, or may need to decompose surrounding text first:
1629
            // Fall through to the slow path.
1630
78.3k
        } else if (norm16 > JAMO_VT) {  // norm16 >= MIN_YES_YES_WITH_CC
1631
            // One or more combining marks that do not combine-back:
1632
            // Check for canonical order, copy unchanged if ok and
1633
            // if followed by a character with a boundary-before.
1634
58.1k
            uint8_t cc = getCCFromNormalYesOrMaybe(norm16);  // cc!=0
1635
58.1k
            if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) {
1636
                // Fails FCD test, need to decompose and contiguously recompose.
1637
0
                if (!doCompose) {
1638
0
                    return false;
1639
0
                }
1640
58.1k
            } else {
1641
                // If !onlyContiguous (not FCC), then we ignore the tccc of
1642
                // the previous character which passed the quick check "yes && ccc==0" test.
1643
58.1k
                const char16_t *nextSrc;
1644
58.1k
                uint16_t n16;
1645
4.01M
                for (;;) {
1646
4.01M
                    if (src == limit) {
1647
31.4k
                        if (doCompose) {
1648
8.40k
                            buffer.appendZeroCC(prevBoundary, limit, errorCode);
1649
8.40k
                        }
1650
31.4k
                        return true;
1651
31.4k
                    }
1652
3.97M
                    uint8_t prevCC = cc;
1653
3.97M
                    nextSrc = src;
1654
3.97M
                    UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, n16);
1655
3.97M
                    if (n16 >= MIN_YES_YES_WITH_CC) {
1656
3.95M
                        cc = getCCFromNormalYesOrMaybe(n16);
1657
3.95M
                        if (prevCC > cc) {
1658
313
                            if (!doCompose) {
1659
15
                                return false;
1660
15
                            }
1661
298
                            break;
1662
313
                        }
1663
3.95M
                    } else {
1664
26.3k
                        break;
1665
26.3k
                    }
1666
3.95M
                    src = nextSrc;
1667
3.95M
                }
1668
                // src is after the last in-order combining mark.
1669
                // If there is a boundary here, then we continue with no change.
1670
26.6k
                if (norm16HasCompBoundaryBefore(n16)) {
1671
11.3k
                    if (isCompYesAndZeroCC(n16)) {
1672
8.46k
                        src = nextSrc;
1673
8.46k
                    }
1674
11.3k
                    continue;
1675
11.3k
                }
1676
                // Use the slow path. There is no boundary in [prevSrc, src[.
1677
26.6k
            }
1678
58.1k
        }
1679
1680
        // Slow path: Find the nearest boundaries around the current character,
1681
        // decompose and recompose.
1682
8.01M
        if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) {
1683
50.9k
            const char16_t *p = prevSrc;
1684
50.9k
            UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, norm16);
1685
50.9k
            if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) {
1686
9.33k
                prevSrc = p;
1687
9.33k
            }
1688
50.9k
        }
1689
8.01M
        if (doCompose && prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) {
1690
0
            break;
1691
0
        }
1692
8.01M
        int32_t recomposeStartIndex=buffer.length();
1693
        // We know there is not a boundary here.
1694
8.01M
        decomposeShort(prevSrc, src, false /* !stopAtCompBoundary */, onlyContiguous,
1695
8.01M
                       buffer, errorCode);
1696
        // Decompose until the next boundary.
1697
8.01M
        src = decomposeShort(src, limit, true /* stopAtCompBoundary */, onlyContiguous,
1698
8.01M
                             buffer, errorCode);
1699
8.01M
        if (U_FAILURE(errorCode)) {
1700
0
            break;
1701
0
        }
1702
8.01M
        if ((src - prevSrc) > INT32_MAX) {  // guard before buffer.equals()
1703
0
            errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
1704
0
            return true;
1705
0
        }
1706
8.01M
        recompose(buffer, recomposeStartIndex, onlyContiguous);
1707
8.01M
        if(!doCompose) {
1708
11.4k
            if(!buffer.equals(prevSrc, src)) {
1709
6.84k
                return false;
1710
6.84k
            }
1711
4.64k
            buffer.remove();
1712
4.64k
        }
1713
8.00M
        prevBoundary=src;
1714
8.00M
    }
1715
0
    return true;
1716
67.9M
}
1717
1718
// Very similar to compose(): Make the same changes in both places if relevant.
1719
// pQCResult==nullptr: spanQuickCheckYes
1720
// pQCResult!=nullptr: quickCheck (*pQCResult must be UNORM_YES)
1721
const char16_t *
1722
Normalizer2Impl::composeQuickCheck(const char16_t *src, const char16_t *limit,
1723
                                   UBool onlyContiguous,
1724
2.07k
                                   UNormalizationCheckResult *pQCResult) const {
1725
2.07k
    const char16_t *prevBoundary=src;
1726
2.07k
    UChar32 minNoMaybeCP=minCompNoMaybeCP;
1727
2.07k
    if(limit==nullptr) {
1728
0
        UErrorCode errorCode=U_ZERO_ERROR;
1729
0
        src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, nullptr, errorCode);
1730
0
        limit=u_strchr(src, 0);
1731
0
        if (prevBoundary != src) {
1732
0
            if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) {
1733
0
                prevBoundary = src;
1734
0
            } else {
1735
0
                prevBoundary = --src;
1736
0
            }
1737
0
        }
1738
0
    }
1739
1740
8.21k
    for(;;) {
1741
        // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point,
1742
        // or with (compYes && ccc==0) properties.
1743
8.21k
        const char16_t *prevSrc;
1744
8.21k
        UChar32 c = 0;
1745
8.21k
        uint16_t norm16 = 0;
1746
17.1k
        for (;;) {
1747
17.1k
            if(src==limit) {
1748
551
                return src;
1749
551
            }
1750
16.5k
            if( (c=*src)<minNoMaybeCP ||
1751
16.5k
                isCompYesAndZeroCC(norm16=UCPTRIE_FAST_BMP_GET(normTrie, UCPTRIE_16, c))
1752
16.5k
            ) {
1753
8.17k
                ++src;
1754
8.37k
            } else {
1755
8.37k
                prevSrc = src++;
1756
8.37k
                if(!U16_IS_LEAD(c)) {
1757
6.80k
                    break;
1758
6.80k
                } else {
1759
1.57k
                    char16_t c2;
1760
1.57k
                    if(src!=limit && U16_IS_TRAIL(c2=*src)) {
1761
1.09k
                        ++src;
1762
1.09k
                        c=U16_GET_SUPPLEMENTARY(c, c2);
1763
1.09k
                        norm16=UCPTRIE_FAST_SUPP_GET(normTrie, UCPTRIE_16, c);
1764
1.09k
                        if(!isCompYesAndZeroCC(norm16)) {
1765
860
                            break;
1766
860
                        }
1767
1.09k
                    }
1768
1.57k
                }
1769
8.37k
            }
1770
16.5k
        }
1771
        // isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
1772
        // The current character is either a "noNo" (has a mapping)
1773
        // or a "maybeYes" / "maybeNo" (combines backward)
1774
        // or a "yesYes" with ccc!=0.
1775
        // It is not a Hangul syllable or Jamo L because those have "yes" properties.
1776
1777
7.66k
        uint16_t prevNorm16 = INERT;
1778
7.66k
        if (prevBoundary != prevSrc) {
1779
6.40k
            if (norm16HasCompBoundaryBefore(norm16)) {
1780
70
                prevBoundary = prevSrc;
1781
6.33k
            } else {
1782
6.33k
                const char16_t *p = prevSrc;
1783
6.33k
                uint16_t n16;
1784
6.33k
                UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, n16);
1785
6.33k
                if (norm16HasCompBoundaryAfter(n16, onlyContiguous)) {
1786
4.31k
                    prevBoundary = prevSrc;
1787
4.31k
                } else {
1788
2.01k
                    prevBoundary = p;
1789
2.01k
                    prevNorm16 = n16;
1790
2.01k
                }
1791
6.33k
            }
1792
6.40k
        }
1793
1794
7.66k
        if (norm16 >= minMaybeNo) {
1795
6.87k
            uint16_t fcd16 = getFCD16FromMaybeOrNonZeroCC(norm16);
1796
6.87k
            uint8_t cc = fcd16 >> 8;
1797
6.87k
            if (onlyContiguous /* FCC */ && cc != 0 &&
1798
6.87k
                    getTrailCCFromCompYesAndZeroCC(prevNorm16) > cc) {
1799
                // The [prevBoundary..prevSrc[ character
1800
                // passed the quick check "yes && ccc==0" test
1801
                // but is out of canonical order with the current combining mark.
1802
6.87k
            } else {
1803
                // If !onlyContiguous (not FCC), then we ignore the tccc of
1804
                // the previous character which passed the quick check "yes && ccc==0" test.
1805
6.87k
                const char16_t *nextSrc;
1806
9.43k
                for (;;) {
1807
9.43k
                    if (norm16 < MIN_YES_YES_WITH_CC) {
1808
8.10k
                        if (pQCResult != nullptr) {
1809
8.10k
                            *pQCResult = UNORM_MAYBE;
1810
8.10k
                        } else {
1811
0
                            return prevBoundary;
1812
0
                        }
1813
8.10k
                    }
1814
9.43k
                    if (src == limit) {
1815
414
                        return src;
1816
414
                    }
1817
9.02k
                    uint8_t prevCC = fcd16;
1818
9.02k
                    nextSrc = src;
1819
9.02k
                    UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, norm16);
1820
9.02k
                    if (norm16 >= minMaybeNo) {
1821
2.63k
                        fcd16 = getFCD16FromMaybeOrNonZeroCC(norm16);
1822
2.63k
                        cc = fcd16 >> 8;
1823
2.63k
                        if (!(prevCC <= cc || cc == 0)) {
1824
73
                            break;
1825
73
                        }
1826
6.38k
                    } else {
1827
6.38k
                        break;
1828
6.38k
                    }
1829
2.56k
                    src = nextSrc;
1830
2.56k
                }
1831
                // src is after the last in-order combining mark.
1832
6.45k
                if (isCompYesAndZeroCC(norm16)) {
1833
6.14k
                    prevBoundary = src;
1834
6.14k
                    src = nextSrc;
1835
6.14k
                    continue;
1836
6.14k
                }
1837
6.45k
            }
1838
6.87k
        }
1839
1.10k
        if(pQCResult!=nullptr) {
1840
1.10k
            *pQCResult=UNORM_NO;
1841
1.10k
        }
1842
1.10k
        return prevBoundary;
1843
7.66k
    }
1844
2.07k
}
1845
1846
void Normalizer2Impl::composeAndAppend(const char16_t *src, const char16_t *limit,
1847
                                       UBool doCompose,
1848
                                       UBool onlyContiguous,
1849
                                       UnicodeString &safeMiddle,
1850
                                       ReorderingBuffer &buffer,
1851
4.14k
                                       UErrorCode &errorCode) const {
1852
4.14k
    if(!buffer.isEmpty()) {
1853
3.58k
        const char16_t *firstStarterInSrc=findNextCompBoundary(src, limit, onlyContiguous);
1854
3.58k
        if(src!=firstStarterInSrc) {
1855
1.87k
            const char16_t *lastStarterInDest=findPreviousCompBoundary(buffer.getStart(),
1856
1.87k
                                                                    buffer.getLimit(), onlyContiguous);
1857
1.87k
            int32_t destSuffixLength = static_cast<int32_t>(buffer.getLimit() - lastStarterInDest);
1858
1.87k
            UnicodeString middle(lastStarterInDest, destSuffixLength);
1859
1.87k
            buffer.removeSuffix(destSuffixLength);
1860
1.87k
            safeMiddle=middle;
1861
1.87k
            middle.append(src, static_cast<int32_t>(firstStarterInSrc - src));
1862
1.87k
            const char16_t *middleStart=middle.getBuffer();
1863
1.87k
            compose(middleStart, middleStart+middle.length(), onlyContiguous,
1864
1.87k
                    true, buffer, errorCode);
1865
1.87k
            if(U_FAILURE(errorCode)) {
1866
0
                return;
1867
0
            }
1868
1.87k
            src=firstStarterInSrc;
1869
1.87k
        }
1870
3.58k
    }
1871
4.14k
    if(doCompose) {
1872
2.07k
        compose(src, limit, onlyContiguous, true, buffer, errorCode);
1873
2.07k
    } else {
1874
2.07k
        if(limit==nullptr) {  // appendZeroCC() needs limit!=nullptr
1875
0
            limit=u_strchr(src, 0);
1876
0
        }
1877
2.07k
        buffer.appendZeroCC(src, limit, errorCode);
1878
2.07k
    }
1879
4.14k
}
1880
1881
UBool
1882
Normalizer2Impl::composeUTF8(uint32_t options, UBool onlyContiguous,
1883
                             const uint8_t *src, const uint8_t *limit,
1884
0
                             ByteSink *sink, Edits *edits, UErrorCode &errorCode) const {
1885
0
    U_ASSERT(limit != nullptr);
1886
0
    UnicodeString s16;
1887
0
    uint8_t minNoMaybeLead = leadByteForCP(minCompNoMaybeCP);
1888
0
    const uint8_t *prevBoundary = src;
1889
1890
0
    for (;;) {
1891
        // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point,
1892
        // or with (compYes && ccc==0) properties.
1893
0
        const uint8_t *prevSrc;
1894
0
        uint16_t norm16 = 0;
1895
0
        for (;;) {
1896
0
            if (src == limit) {
1897
0
                if (prevBoundary != limit && sink != nullptr) {
1898
0
                    ByteSinkUtil::appendUnchanged(prevBoundary, limit,
1899
0
                                                  *sink, options, edits, errorCode);
1900
0
                }
1901
0
                return true;
1902
0
            }
1903
0
            if (*src < minNoMaybeLead) {
1904
0
                ++src;
1905
0
            } else {
1906
0
                prevSrc = src;
1907
0
                UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16);
1908
0
                if (!isCompYesAndZeroCC(norm16)) {
1909
0
                    break;
1910
0
                }
1911
0
            }
1912
0
        }
1913
        // isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo.
1914
        // The current character is either a "noNo" (has a mapping)
1915
        // or a "maybeYes" / "maybeNo" (combines backward)
1916
        // or a "yesYes" with ccc!=0.
1917
        // It is not a Hangul syllable or Jamo L because those have "yes" properties.
1918
1919
        // Medium-fast path: Handle cases that do not require full decomposition and recomposition.
1920
0
        if (norm16 < minMaybeNo) {  // minNoNo <= norm16 < minMaybeNo
1921
0
            if (sink == nullptr) {
1922
0
                return false;
1923
0
            }
1924
            // Fast path for mapping a character that is immediately surrounded by boundaries.
1925
            // In this case, we need not decompose around the current character.
1926
0
            if (isDecompNoAlgorithmic(norm16)) {
1927
                // Maps to a single isCompYesAndZeroCC character
1928
                // which also implies hasCompBoundaryBefore.
1929
0
                if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) ||
1930
0
                        hasCompBoundaryBefore(src, limit)) {
1931
0
                    if (prevBoundary != prevSrc &&
1932
0
                            !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
1933
0
                                                           *sink, options, edits, errorCode)) {
1934
0
                        break;
1935
0
                    }
1936
0
                    appendCodePointDelta(prevSrc, src, getAlgorithmicDelta(norm16), *sink, edits);
1937
0
                    prevBoundary = src;
1938
0
                    continue;
1939
0
                }
1940
0
            } else if (norm16 < minNoNoCompBoundaryBefore) {
1941
                // The mapping is comp-normalized which also implies hasCompBoundaryBefore.
1942
0
                if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) ||
1943
0
                        hasCompBoundaryBefore(src, limit)) {
1944
0
                    if (prevBoundary != prevSrc &&
1945
0
                            !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
1946
0
                                                           *sink, options, edits, errorCode)) {
1947
0
                        break;
1948
0
                    }
1949
0
                    const uint16_t *mapping = getDataForYesOrNo(norm16);
1950
0
                    int32_t length = *mapping++ & MAPPING_LENGTH_MASK;
1951
0
                    if (!ByteSinkUtil::appendChange(prevSrc, src, reinterpret_cast<const char16_t*>(mapping), length,
1952
0
                                                    *sink, edits, errorCode)) {
1953
0
                        break;
1954
0
                    }
1955
0
                    prevBoundary = src;
1956
0
                    continue;
1957
0
                }
1958
0
            } else if (norm16 >= minNoNoEmpty) {
1959
                // The current character maps to nothing.
1960
                // Simply omit it from the output if there is a boundary before _or_ after it.
1961
                // The character itself implies no boundaries.
1962
0
                if (hasCompBoundaryBefore(src, limit) ||
1963
0
                        hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) {
1964
0
                    if (prevBoundary != prevSrc &&
1965
0
                            !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
1966
0
                                                           *sink, options, edits, errorCode)) {
1967
0
                        break;
1968
0
                    }
1969
0
                    if (edits != nullptr) {
1970
0
                        edits->addReplace(static_cast<int32_t>(src - prevSrc), 0);
1971
0
                    }
1972
0
                    prevBoundary = src;
1973
0
                    continue;
1974
0
                }
1975
0
            }
1976
            // Other "noNo" type, or need to examine more text around this character:
1977
            // Fall through to the slow path.
1978
0
        } else if (isJamoVT(norm16)) {
1979
            // Jamo L: E1 84 80..92
1980
            // Jamo V: E1 85 A1..B5
1981
            // Jamo T: E1 86 A8..E1 87 82
1982
0
            U_ASSERT((src - prevSrc) == 3 && *prevSrc == 0xe1);
1983
0
            UChar32 prev = previousHangulOrJamo(prevBoundary, prevSrc);
1984
0
            if (prevSrc[1] == 0x85) {
1985
                // The current character is a Jamo Vowel,
1986
                // compose with previous Jamo L and following Jamo T.
1987
0
                UChar32 l = prev - Hangul::JAMO_L_BASE;
1988
0
                if (static_cast<uint32_t>(l) < Hangul::JAMO_L_COUNT) {
1989
0
                    if (sink == nullptr) {
1990
0
                        return false;
1991
0
                    }
1992
0
                    int32_t t = getJamoTMinusBase(src, limit);
1993
0
                    if (t >= 0) {
1994
                        // The next character is a Jamo T.
1995
0
                        src += 3;
1996
0
                    } else if (hasCompBoundaryBefore(src, limit)) {
1997
                        // No Jamo T follows, not even via decomposition.
1998
0
                        t = 0;
1999
0
                    }
2000
0
                    if (t >= 0) {
2001
0
                        UChar32 syllable = Hangul::HANGUL_BASE +
2002
0
                            (l*Hangul::JAMO_V_COUNT + (prevSrc[2]-0xa1)) *
2003
0
                            Hangul::JAMO_T_COUNT + t;
2004
0
                        prevSrc -= 3;  // Replace the Jamo L as well.
2005
0
                        if (prevBoundary != prevSrc &&
2006
0
                                !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
2007
0
                                                               *sink, options, edits, errorCode)) {
2008
0
                            break;
2009
0
                        }
2010
0
                        ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits);
2011
0
                        prevBoundary = src;
2012
0
                        continue;
2013
0
                    }
2014
                    // If we see L+V+x where x!=T then we drop to the slow path,
2015
                    // decompose and recompose.
2016
                    // This is to deal with NFKC finding normal L and V but a
2017
                    // compatibility variant of a T.
2018
                    // We need to either fully compose that combination here
2019
                    // (which would complicate the code and may not work with strange custom data)
2020
                    // or use the slow path.
2021
0
                }
2022
0
            } else if (Hangul::isHangulLV(prev)) {
2023
                // The current character is a Jamo Trailing consonant,
2024
                // compose with previous Hangul LV that does not contain a Jamo T.
2025
0
                if (sink == nullptr) {
2026
0
                    return false;
2027
0
                }
2028
0
                UChar32 syllable = prev + getJamoTMinusBase(prevSrc, src);
2029
0
                prevSrc -= 3;  // Replace the Hangul LV as well.
2030
0
                if (prevBoundary != prevSrc &&
2031
0
                        !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
2032
0
                                                       *sink, options, edits, errorCode)) {
2033
0
                    break;
2034
0
                }
2035
0
                ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits);
2036
0
                prevBoundary = src;
2037
0
                continue;
2038
0
            }
2039
            // No matching context, or may need to decompose surrounding text first:
2040
            // Fall through to the slow path.
2041
0
        } else if (norm16 > JAMO_VT) {  // norm16 >= MIN_YES_YES_WITH_CC
2042
            // One or more combining marks that do not combine-back:
2043
            // Check for canonical order, copy unchanged if ok and
2044
            // if followed by a character with a boundary-before.
2045
0
            uint8_t cc = getCCFromNormalYesOrMaybe(norm16);  // cc!=0
2046
0
            if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) {
2047
                // Fails FCD test, need to decompose and contiguously recompose.
2048
0
                if (sink == nullptr) {
2049
0
                    return false;
2050
0
                }
2051
0
            } else {
2052
                // If !onlyContiguous (not FCC), then we ignore the tccc of
2053
                // the previous character which passed the quick check "yes && ccc==0" test.
2054
0
                const uint8_t *nextSrc;
2055
0
                uint16_t n16;
2056
0
                for (;;) {
2057
0
                    if (src == limit) {
2058
0
                        if (sink != nullptr) {
2059
0
                            ByteSinkUtil::appendUnchanged(prevBoundary, limit,
2060
0
                                                          *sink, options, edits, errorCode);
2061
0
                        }
2062
0
                        return true;
2063
0
                    }
2064
0
                    uint8_t prevCC = cc;
2065
0
                    nextSrc = src;
2066
0
                    UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, n16);
2067
0
                    if (n16 >= MIN_YES_YES_WITH_CC) {
2068
0
                        cc = getCCFromNormalYesOrMaybe(n16);
2069
0
                        if (prevCC > cc) {
2070
0
                            if (sink == nullptr) {
2071
0
                                return false;
2072
0
                            }
2073
0
                            break;
2074
0
                        }
2075
0
                    } else {
2076
0
                        break;
2077
0
                    }
2078
0
                    src = nextSrc;
2079
0
                }
2080
                // src is after the last in-order combining mark.
2081
                // If there is a boundary here, then we continue with no change.
2082
0
                if (norm16HasCompBoundaryBefore(n16)) {
2083
0
                    if (isCompYesAndZeroCC(n16)) {
2084
0
                        src = nextSrc;
2085
0
                    }
2086
0
                    continue;
2087
0
                }
2088
                // Use the slow path. There is no boundary in [prevSrc, src[.
2089
0
            }
2090
0
        }
2091
2092
        // Slow path: Find the nearest boundaries around the current character,
2093
        // decompose and recompose.
2094
0
        if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) {
2095
0
            const uint8_t *p = prevSrc;
2096
0
            UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, prevBoundary, p, norm16);
2097
0
            if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) {
2098
0
                prevSrc = p;
2099
0
            }
2100
0
        }
2101
0
        ReorderingBuffer buffer(*this, s16, errorCode);
2102
0
        if (U_FAILURE(errorCode)) {
2103
0
            break;
2104
0
        }
2105
        // We know there is not a boundary here.
2106
0
        decomposeShort(prevSrc, src, STOP_AT_LIMIT, onlyContiguous,
2107
0
                       buffer, errorCode);
2108
        // Decompose until the next boundary.
2109
0
        src = decomposeShort(src, limit, STOP_AT_COMP_BOUNDARY, onlyContiguous,
2110
0
                             buffer, errorCode);
2111
0
        if (U_FAILURE(errorCode)) {
2112
0
            break;
2113
0
        }
2114
0
        if ((src - prevSrc) > INT32_MAX) {  // guard before buffer.equals()
2115
0
            errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
2116
0
            return true;
2117
0
        }
2118
0
        recompose(buffer, 0, onlyContiguous);
2119
0
        if (!buffer.equals(prevSrc, src)) {
2120
0
            if (sink == nullptr) {
2121
0
                return false;
2122
0
            }
2123
0
            if (prevBoundary != prevSrc &&
2124
0
                    !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc,
2125
0
                                                   *sink, options, edits, errorCode)) {
2126
0
                break;
2127
0
            }
2128
0
            if (!ByteSinkUtil::appendChange(prevSrc, src, buffer.getStart(), buffer.length(),
2129
0
                                            *sink, edits, errorCode)) {
2130
0
                break;
2131
0
            }
2132
0
            prevBoundary = src;
2133
0
        }
2134
0
    }
2135
0
    return true;
2136
0
}
2137
2138
7.27M
UBool Normalizer2Impl::hasCompBoundaryBefore(const char16_t *src, const char16_t *limit) const {
2139
7.27M
    if (src == limit || *src < minCompNoMaybeCP) {
2140
7.22M
        return true;
2141
7.22M
    }
2142
42.1k
    UChar32 c;
2143
42.1k
    uint16_t norm16;
2144
42.1k
    UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, src, limit, c, norm16);
2145
42.1k
    return norm16HasCompBoundaryBefore(norm16);
2146
7.27M
}
2147
2148
0
UBool Normalizer2Impl::hasCompBoundaryBefore(const uint8_t *src, const uint8_t *limit) const {
2149
0
    if (src == limit) {
2150
0
        return true;
2151
0
    }
2152
0
    uint16_t norm16;
2153
0
    UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16);
2154
0
    return norm16HasCompBoundaryBefore(norm16);
2155
0
}
2156
2157
UBool Normalizer2Impl::hasCompBoundaryAfter(const char16_t *start, const char16_t *p,
2158
6.35k
                                            UBool onlyContiguous) const {
2159
6.35k
    if (start == p) {
2160
1.72k
        return true;
2161
1.72k
    }
2162
4.63k
    UChar32 c;
2163
4.63k
    uint16_t norm16;
2164
4.63k
    UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16);
2165
4.63k
    return norm16HasCompBoundaryAfter(norm16, onlyContiguous);
2166
6.35k
}
2167
2168
UBool Normalizer2Impl::hasCompBoundaryAfter(const uint8_t *start, const uint8_t *p,
2169
0
                                            UBool onlyContiguous) const {
2170
0
    if (start == p) {
2171
0
        return true;
2172
0
    }
2173
0
    uint16_t norm16;
2174
0
    UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, start, p, norm16);
2175
0
    return norm16HasCompBoundaryAfter(norm16, onlyContiguous);
2176
0
}
2177
2178
const char16_t *Normalizer2Impl::findPreviousCompBoundary(const char16_t *start, const char16_t *p,
2179
1.87k
                                                       UBool onlyContiguous) const {
2180
274k
    while (p != start) {
2181
274k
        const char16_t *codePointLimit = p;
2182
274k
        UChar32 c;
2183
274k
        uint16_t norm16;
2184
274k
        UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16);
2185
274k
        if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) {
2186
682
            return codePointLimit;
2187
682
        }
2188
273k
        if (hasCompBoundaryBefore(c, norm16)) {
2189
330
            return p;
2190
330
        }
2191
273k
    }
2192
864
    return p;
2193
1.87k
}
2194
2195
const char16_t *Normalizer2Impl::findNextCompBoundary(const char16_t *p, const char16_t *limit,
2196
3.58k
                                                   UBool onlyContiguous) const {
2197
108k
    while (p != limit) {
2198
107k
        const char16_t *codePointStart = p;
2199
107k
        UChar32 c;
2200
107k
        uint16_t norm16;
2201
107k
        UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16);
2202
107k
        if (hasCompBoundaryBefore(c, norm16)) {
2203
2.77k
            return codePointStart;
2204
2.77k
        }
2205
105k
        if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) {
2206
0
            return p;
2207
0
        }
2208
105k
    }
2209
808
    return p;
2210
3.58k
}
2211
2212
0
uint8_t Normalizer2Impl::getPreviousTrailCC(const char16_t *start, const char16_t *p) const {
2213
0
    if (start == p) {
2214
0
        return 0;
2215
0
    }
2216
0
    int32_t i = static_cast<int32_t>(p - start);
2217
0
    UChar32 c;
2218
0
    U16_PREV(start, 0, i, c);
2219
0
    return static_cast<uint8_t>(getFCD16(c));
2220
0
}
2221
2222
0
uint8_t Normalizer2Impl::getPreviousTrailCC(const uint8_t *start, const uint8_t *p) const {
2223
0
    if (start == p) {
2224
0
        return 0;
2225
0
    }
2226
0
    int32_t i = static_cast<int32_t>(p - start);
2227
0
    UChar32 c;
2228
0
    U8_PREV(start, 0, i, c);
2229
0
    return static_cast<uint8_t>(getFCD16(c));
2230
0
}
2231
2232
// Note: normalizer2impl.cpp r30982 (2011-nov-27)
2233
// still had getFCDTrie() which built and cached an FCD trie.
2234
// That provided faster access to FCD data than getFCD16FromNormData()
2235
// but required synchronization and consumed some 10kB of heap memory
2236
// in any process that uses FCD (e.g., via collation).
2237
// minDecompNoCP etc. and smallFCD[] are intended to help with any loss of performance,
2238
// at least for ASCII & CJK.
2239
2240
// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this
2241
// function on Windows ARM64. As a work-around, we disable optimizations for this function.
2242
// This work-around could/should be removed once the following versions of Visual Studio are no
2243
// longer supported: All versions of VS2017, and versions of VS2019 below 16.4.
2244
#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924))
2245
#pragma optimize( "", off )
2246
#endif
2247
// Gets the FCD value from the regular normalization data.
2248
175M
uint16_t Normalizer2Impl::getFCD16FromNormData(UChar32 c) const {
2249
175M
    uint16_t norm16=getNorm16(c);
2250
175M
    if (norm16 >= limitNoNo) {
2251
111M
        if(norm16>=MIN_NORMAL_MAYBE_YES) {
2252
            // combining mark
2253
111M
            norm16=getCCFromNormalYesOrMaybe(norm16);
2254
111M
            return norm16|(norm16<<8);
2255
111M
        } else if(norm16>=minMaybeYes) {
2256
8.03k
            return 0;
2257
10.6k
        } else if(norm16<minMaybeNo) {  // isDecompNoAlgorithmic(norm16)
2258
83
            uint16_t deltaTrailCC = norm16 & DELTA_TCCC_MASK;
2259
83
            if (deltaTrailCC <= DELTA_TCCC_1) {
2260
0
                return deltaTrailCC >> OFFSET_SHIFT;
2261
0
            }
2262
            // Maps to an isCompYesAndZeroCC.
2263
83
            c=mapAlgorithmic(c, norm16);
2264
83
            norm16=getRawNorm16(c);
2265
83
        }
2266
111M
    }
2267
64.4M
    if(norm16<=minYesNo || isHangulLVT(norm16)) {
2268
        // no decomposition or Hangul syllable, all zeros
2269
8.97M
        return 0;
2270
8.97M
    }
2271
    // c decomposes, get everything from the variable-length extra data
2272
55.4M
    const uint16_t *mapping=getData(norm16);
2273
55.4M
    uint16_t firstUnit=*mapping;
2274
55.4M
    norm16=firstUnit>>8;  // tccc
2275
55.4M
    if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) {
2276
36.1M
        norm16|=*(mapping-1)&0xff00;  // lccc
2277
36.1M
    }
2278
55.4M
    return norm16;
2279
64.4M
}
2280
#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924))
2281
#pragma optimize( "", on )
2282
#endif
2283
2284
9.51k
uint16_t Normalizer2Impl::getFCD16FromMaybeOrNonZeroCC(uint16_t norm16) const {
2285
9.51k
    U_ASSERT(norm16 >= minMaybeNo);
2286
9.51k
    if (norm16 >= MIN_NORMAL_MAYBE_YES) {
2287
        // combining mark
2288
7.92k
        norm16 = getCCFromNormalYesOrMaybe(norm16);
2289
7.92k
        return norm16 | (norm16<<8);
2290
7.92k
    } else if (norm16 >= minMaybeYes) {
2291
650
        return 0;
2292
650
    }
2293
    // c decomposes, get everything from the variable-length extra data
2294
936
    const uint16_t *mapping = getDataForMaybe(norm16);
2295
936
    uint16_t firstUnit = *mapping;
2296
    // maybeNo has lccc = 0
2297
936
    U_ASSERT((firstUnit & MAPPING_HAS_CCC_LCCC_WORD) == 0 || (*(mapping - 1) & 0xff00) == 0);
2298
936
    return firstUnit >> 8;  // tccc
2299
9.51k
}
2300
2301
// Dual functionality:
2302
// buffer!=nullptr: normalize
2303
// buffer==nullptr: isNormalized/quickCheck/spanQuickCheckYes
2304
const char16_t *
2305
Normalizer2Impl::makeFCD(const char16_t *src, const char16_t *limit,
2306
                         ReorderingBuffer *buffer,
2307
8.58M
                         UErrorCode &errorCode) const {
2308
    // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered tccc<=1.
2309
    // Similar to the prevBoundary in the compose() implementation.
2310
8.58M
    const char16_t *prevBoundary=src;
2311
8.58M
    int32_t prevFCD16=0;
2312
8.58M
    if(limit==nullptr) {
2313
0
        src=copyLowPrefixFromNulTerminated(src, minLcccCP, buffer, errorCode);
2314
0
        if(U_FAILURE(errorCode)) {
2315
0
            return src;
2316
0
        }
2317
0
        if(prevBoundary<src) {
2318
0
            prevBoundary=src;
2319
            // We know that the previous character's lccc==0.
2320
            // Fetching the fcd16 value was deferred for this below-U+0300 code point.
2321
0
            prevFCD16=getFCD16(*(src-1));
2322
0
            if(prevFCD16>1) {
2323
0
                --prevBoundary;
2324
0
            }
2325
0
        }
2326
0
        limit=u_strchr(src, 0);
2327
0
    }
2328
2329
    // Note: In this function we use buffer->appendZeroCC() because we track
2330
    // the lead and trail combining classes here, rather than leaving it to
2331
    // the ReorderingBuffer.
2332
    // The exception is the call to decomposeShort() which uses the buffer
2333
    // in the normal way.
2334
2335
8.58M
    const char16_t *prevSrc;
2336
8.58M
    UChar32 c=0;
2337
8.58M
    uint16_t fcd16=0;
2338
2339
58.2M
    for(;;) {
2340
        // count code units with lccc==0
2341
173M
        for(prevSrc=src; src!=limit;) {
2342
166M
            if((c=*src)<minLcccCP) {
2343
16.2M
                prevFCD16=~c;
2344
16.2M
                ++src;
2345
150M
            } else if(!singleLeadMightHaveNonZeroFCD16(c)) {
2346
95.6M
                prevFCD16=0;
2347
95.6M
                ++src;
2348
95.6M
            } else {
2349
54.4M
                if(U16_IS_LEAD(c)) {
2350
358k
                    char16_t c2;
2351
358k
                    if((src+1)!=limit && U16_IS_TRAIL(c2=src[1])) {
2352
357k
                        c=U16_GET_SUPPLEMENTARY(c, c2);
2353
357k
                    }
2354
358k
                }
2355
54.4M
                if((fcd16=getFCD16FromNormData(c))<=0xff) {
2356
3.68M
                    prevFCD16=fcd16;
2357
3.68M
                    src+=U16_LENGTH(c);
2358
50.7M
                } else {
2359
50.7M
                    break;
2360
50.7M
                }
2361
54.4M
            }
2362
166M
        }
2363
        // copy these code units all at once
2364
58.2M
        if(src!=prevSrc) {
2365
54.1M
            if(buffer!=nullptr && !buffer->appendZeroCC(prevSrc, src, errorCode)) {
2366
0
                break;
2367
0
            }
2368
54.1M
            if(src==limit) {
2369
6.09M
                break;
2370
6.09M
            }
2371
48.0M
            prevBoundary=src;
2372
            // We know that the previous character's lccc==0.
2373
48.0M
            if(prevFCD16<0) {
2374
                // Fetching the fcd16 value was deferred for this below-minLcccCP code point.
2375
2.19M
                UChar32 prev=~prevFCD16;
2376
2.19M
                if(prev<minDecompNoCP) {
2377
1.78M
                    prevFCD16=0;
2378
1.78M
                } else {
2379
415k
                    prevFCD16=getFCD16FromNormData(prev);
2380
415k
                    if(prevFCD16>1) {
2381
394k
                        --prevBoundary;
2382
394k
                    }
2383
415k
                }
2384
45.8M
            } else {
2385
45.8M
                const char16_t *p=src-1;
2386
45.8M
                if(U16_IS_TRAIL(*p) && prevSrc<p && U16_IS_LEAD(*(p-1))) {
2387
9.21k
                    --p;
2388
                    // Need to fetch the previous character's FCD value because
2389
                    // prevFCD16 was just for the trail surrogate code point.
2390
9.21k
                    prevFCD16=getFCD16FromNormData(U16_GET_SUPPLEMENTARY(p[0], p[1]));
2391
                    // Still known to have lccc==0 because its lead surrogate unit had lccc==0.
2392
9.21k
                }
2393
45.8M
                if(prevFCD16>1) {
2394
302k
                    prevBoundary=p;
2395
302k
                }
2396
45.8M
            }
2397
            // The start of the current character (c).
2398
48.0M
            prevSrc=src;
2399
48.0M
        } else if(src==limit) {
2400
1.42M
            break;
2401
1.42M
        }
2402
2403
50.7M
        src+=U16_LENGTH(c);
2404
        // The current character (c) at [prevSrc..src[ has a non-zero lead combining class.
2405
        // Check for proper order, and decompose locally if necessary.
2406
50.7M
        if((prevFCD16&0xff)<=(fcd16>>8)) {
2407
            // proper order: prev tccc <= current lccc
2408
49.6M
            if((fcd16&0xff)<=1) {
2409
36.2k
                prevBoundary=src;
2410
36.2k
            }
2411
49.6M
            if(buffer!=nullptr && !buffer->appendZeroCC(c, errorCode)) {
2412
0
                break;
2413
0
            }
2414
49.6M
            prevFCD16=fcd16;
2415
49.6M
            continue;
2416
49.6M
        } else if(buffer==nullptr) {
2417
1.06M
            return prevBoundary;  // quick check "no"
2418
1.06M
        } else {
2419
            /*
2420
             * Back out the part of the source that we copied or appended
2421
             * already but is now going to be decomposed.
2422
             * prevSrc is set to after what was copied/appended.
2423
             */
2424
1.83k
            buffer->removeSuffix(static_cast<int32_t>(prevSrc - prevBoundary));
2425
            /*
2426
             * Find the part of the source that needs to be decomposed,
2427
             * up to the next safe boundary.
2428
             */
2429
1.83k
            src=findNextFCDBoundary(src, limit);
2430
            /*
2431
             * The source text does not fulfill the conditions for FCD.
2432
             * Decompose and reorder a limited piece of the text.
2433
             */
2434
1.83k
            decomposeShort(prevBoundary, src, false, false, *buffer, errorCode);
2435
1.83k
            if (U_FAILURE(errorCode)) {
2436
0
                break;
2437
0
            }
2438
1.83k
            prevBoundary=src;
2439
1.83k
            prevFCD16=0;
2440
1.83k
        }
2441
50.7M
    }
2442
7.51M
    return src;
2443
8.58M
}
2444
2445
void Normalizer2Impl::makeFCDAndAppend(const char16_t *src, const char16_t *limit,
2446
                                       UBool doMakeFCD,
2447
                                       UnicodeString &safeMiddle,
2448
                                       ReorderingBuffer &buffer,
2449
0
                                       UErrorCode &errorCode) const {
2450
0
    if(!buffer.isEmpty()) {
2451
0
        const char16_t *firstBoundaryInSrc=findNextFCDBoundary(src, limit);
2452
0
        if(src!=firstBoundaryInSrc) {
2453
0
            const char16_t *lastBoundaryInDest=findPreviousFCDBoundary(buffer.getStart(),
2454
0
                                                                    buffer.getLimit());
2455
0
            int32_t destSuffixLength = static_cast<int32_t>(buffer.getLimit() - lastBoundaryInDest);
2456
0
            UnicodeString middle(lastBoundaryInDest, destSuffixLength);
2457
0
            buffer.removeSuffix(destSuffixLength);
2458
0
            safeMiddle=middle;
2459
0
            middle.append(src, static_cast<int32_t>(firstBoundaryInSrc - src));
2460
0
            const char16_t *middleStart=middle.getBuffer();
2461
0
            makeFCD(middleStart, middleStart+middle.length(), &buffer, errorCode);
2462
0
            if(U_FAILURE(errorCode)) {
2463
0
                return;
2464
0
            }
2465
0
            src=firstBoundaryInSrc;
2466
0
        }
2467
0
    }
2468
0
    if(doMakeFCD) {
2469
0
        makeFCD(src, limit, &buffer, errorCode);
2470
0
    } else {
2471
0
        if(limit==nullptr) {  // appendZeroCC() needs limit!=nullptr
2472
0
            limit=u_strchr(src, 0);
2473
0
        }
2474
0
        buffer.appendZeroCC(src, limit, errorCode);
2475
0
    }
2476
0
}
2477
2478
0
const char16_t *Normalizer2Impl::findPreviousFCDBoundary(const char16_t *start, const char16_t *p) const {
2479
0
    while(start<p) {
2480
0
        const char16_t *codePointLimit = p;
2481
0
        UChar32 c;
2482
0
        uint16_t norm16;
2483
0
        UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16);
2484
0
        if (c < minDecompNoCP || norm16HasDecompBoundaryAfter(norm16)) {
2485
0
            return codePointLimit;
2486
0
        }
2487
0
        if (norm16HasDecompBoundaryBefore(norm16)) {
2488
0
            return p;
2489
0
        }
2490
0
    }
2491
0
    return p;
2492
0
}
2493
2494
1.83k
const char16_t *Normalizer2Impl::findNextFCDBoundary(const char16_t *p, const char16_t *limit) const {
2495
16.2k
    while(p<limit) {
2496
15.8k
        const char16_t *codePointStart=p;
2497
15.8k
        UChar32 c;
2498
15.8k
        uint16_t norm16;
2499
15.8k
        UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16);
2500
15.8k
        if (c < minLcccCP || norm16HasDecompBoundaryBefore(norm16)) {
2501
1.50k
            return codePointStart;
2502
1.50k
        }
2503
14.3k
        if (norm16HasDecompBoundaryAfter(norm16)) {
2504
0
            return p;
2505
0
        }
2506
14.3k
    }
2507
330
    return p;
2508
1.83k
}
2509
2510
// CanonicalIterator data -------------------------------------------------- ***
2511
2512
CanonIterData::CanonIterData(UErrorCode &errorCode) :
2513
2
        mutableTrie(umutablecptrie_open(0, 0, &errorCode)), trie(nullptr),
2514
2
        canonStartSets(uprv_deleteUObject, nullptr, errorCode) {}
2515
2516
0
CanonIterData::~CanonIterData() {
2517
0
    umutablecptrie_close(mutableTrie);
2518
0
    ucptrie_close(trie);
2519
0
}
2520
2521
2.24k
void CanonIterData::addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode) {
2522
2.24k
    uint32_t canonValue = umutablecptrie_get(mutableTrie, decompLead);
2523
2.24k
    if((canonValue&(CANON_HAS_SET|CANON_VALUE_MASK))==0 && origin!=0) {
2524
        // origin is the first character whose decomposition starts with
2525
        // the character for which we are setting the value.
2526
1.98k
        umutablecptrie_set(mutableTrie, decompLead, canonValue|origin, &errorCode);
2527
1.98k
    } else {
2528
        // origin is not the first character, or it is U+0000.
2529
252
        UnicodeSet *set;
2530
252
        if((canonValue&CANON_HAS_SET)==0) {
2531
204
            LocalPointer<UnicodeSet> lpSet(new UnicodeSet, errorCode);
2532
204
            set=lpSet.getAlias();
2533
204
            if(U_FAILURE(errorCode)) {
2534
0
                return;
2535
0
            }
2536
204
            UChar32 firstOrigin = static_cast<UChar32>(canonValue & CANON_VALUE_MASK);
2537
204
            canonValue = (canonValue & ~CANON_VALUE_MASK) | CANON_HAS_SET | static_cast<uint32_t>(canonStartSets.size());
2538
204
            umutablecptrie_set(mutableTrie, decompLead, canonValue, &errorCode);
2539
204
            canonStartSets.adoptElement(lpSet.orphan(), errorCode);
2540
204
            if (U_FAILURE(errorCode)) {
2541
0
                return;
2542
0
            }
2543
204
            if(firstOrigin!=0) {
2544
204
                set->add(firstOrigin);
2545
204
            }
2546
204
        } else {
2547
48
            set = static_cast<UnicodeSet*>(canonStartSets[static_cast<int32_t>(canonValue & CANON_VALUE_MASK)]);
2548
48
        }
2549
252
        set->add(origin);
2550
252
    }
2551
2.24k
}
2552
2553
// C++ class for friend access to private Normalizer2Impl members.
2554
class InitCanonIterData {
2555
public:
2556
    static void doInit(Normalizer2Impl *impl, UErrorCode &errorCode);
2557
};
2558
2559
U_CDECL_BEGIN
2560
2561
// UInitOnce instantiation function for CanonIterData
2562
static void U_CALLCONV
2563
2
initCanonIterData(Normalizer2Impl *impl, UErrorCode &errorCode) {
2564
2
    InitCanonIterData::doInit(impl, errorCode);
2565
2
}
2566
2567
U_CDECL_END
2568
2569
2
void InitCanonIterData::doInit(Normalizer2Impl *impl, UErrorCode &errorCode) {
2570
2
    U_ASSERT(impl->fCanonIterData == nullptr);
2571
2
    impl->fCanonIterData = new CanonIterData(errorCode);
2572
2
    if (impl->fCanonIterData == nullptr) {
2573
0
        errorCode=U_MEMORY_ALLOCATION_ERROR;
2574
0
    }
2575
2
    if (U_SUCCESS(errorCode)) {
2576
2
        UChar32 start = 0, end;
2577
2
        uint32_t value;
2578
8.05k
        while ((end = ucptrie_getRange(impl->normTrie, start,
2579
8.05k
                                       UCPMAP_RANGE_FIXED_LEAD_SURROGATES, Normalizer2Impl::INERT,
2580
8.05k
                                       nullptr, nullptr, &value)) >= 0) {
2581
            // Call Normalizer2Impl::makeCanonIterDataFromNorm16() for a range of same-norm16 characters.
2582
8.05k
            if (value != Normalizer2Impl::INERT) {
2583
7.13k
                impl->makeCanonIterDataFromNorm16(start, end, value, *impl->fCanonIterData, errorCode);
2584
7.13k
            }
2585
8.05k
            start = end + 1;
2586
8.05k
        }
2587
#ifdef UCPTRIE_DEBUG
2588
        umutablecptrie_setName(impl->fCanonIterData->mutableTrie, "CanonIterData");
2589
#endif
2590
2
        impl->fCanonIterData->trie = umutablecptrie_buildImmutable(
2591
2
            impl->fCanonIterData->mutableTrie, UCPTRIE_TYPE_SMALL, UCPTRIE_VALUE_BITS_32, &errorCode);
2592
2
        umutablecptrie_close(impl->fCanonIterData->mutableTrie);
2593
2
        impl->fCanonIterData->mutableTrie = nullptr;
2594
2
    }
2595
2
    if (U_FAILURE(errorCode)) {
2596
0
        delete impl->fCanonIterData;
2597
0
        impl->fCanonIterData = nullptr;
2598
0
    }
2599
2
}
2600
2601
void Normalizer2Impl::makeCanonIterDataFromNorm16(UChar32 start, UChar32 end, const uint16_t norm16,
2602
                                                  CanonIterData &newData,
2603
7.13k
                                                  UErrorCode &errorCode) const {
2604
7.13k
    if(isInert(norm16) ||
2605
7.13k
            (minYesNo<=norm16 && norm16<minNoNo) ||
2606
7.13k
            (minMaybeNo<=norm16 && norm16<minMaybeYes)) {
2607
        // Inert, or 2-way mapping (including Hangul syllable).
2608
        // We do not write a canonStartSet for any yesNo/maybeNo character.
2609
        // Composites from 2-way mappings are added at runtime from the
2610
        // starter's compositions list, and the other characters in
2611
        // 2-way mappings get CANON_NOT_SEGMENT_STARTER set because they are
2612
        // "maybe" characters.
2613
3.51k
        return;
2614
3.51k
    }
2615
8.48k
    for(UChar32 c=start; c<=end; ++c) {
2616
4.86k
        uint32_t oldValue = umutablecptrie_get(newData.mutableTrie, c);
2617
4.86k
        uint32_t newValue=oldValue;
2618
4.86k
        if(isMaybeYesOrNonZeroCC(norm16)) {
2619
            // not a segment starter if it occurs in a decomposition or has cc!=0
2620
2.09k
            newValue|=CANON_NOT_SEGMENT_STARTER;
2621
2.09k
            if(norm16<MIN_NORMAL_MAYBE_YES) {
2622
8
                newValue|=CANON_HAS_COMPOSITIONS;
2623
8
            }
2624
2.77k
        } else if(norm16<minYesNo) {
2625
538
            newValue|=CANON_HAS_COMPOSITIONS;
2626
2.24k
        } else {
2627
            // c has a one-way decomposition
2628
2.24k
            UChar32 c2=c;
2629
            // Do not modify the whole-range norm16 value.
2630
2.24k
            uint16_t norm16_2=norm16;
2631
2.24k
            if (isDecompNoAlgorithmic(norm16_2)) {
2632
                // Maps to an isCompYesAndZeroCC.
2633
4
                c2 = mapAlgorithmic(c2, norm16_2);
2634
4
                norm16_2 = getRawNorm16(c2);
2635
                // No compatibility mappings for the CanonicalIterator.
2636
4
                U_ASSERT(!(isHangulLV(norm16_2) || isHangulLVT(norm16_2)));
2637
4
            }
2638
2.24k
            if (norm16_2 > minYesNo) {
2639
                // c decomposes, get everything from the variable-length extra data
2640
2.23k
                const uint16_t *mapping=getDataForYesOrNo(norm16_2);
2641
2.23k
                uint16_t firstUnit=*mapping;
2642
2.23k
                int32_t length=firstUnit&MAPPING_LENGTH_MASK;
2643
2.23k
                if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD)!=0) {
2644
14
                    if(c==c2 && (*(mapping-1)&0xff)!=0) {
2645
8
                        newValue|=CANON_NOT_SEGMENT_STARTER;  // original c has cc!=0
2646
8
                    }
2647
14
                }
2648
                // Skip empty mappings (no characters in the decomposition).
2649
2.23k
                if(length!=0) {
2650
2.23k
                    ++mapping;  // skip over the firstUnit
2651
                    // add c to first code point's start set
2652
2.23k
                    int32_t i=0;
2653
2.23k
                    U16_NEXT_UNSAFE(mapping, i, c2);
2654
2.23k
                    newData.addToStartSet(c, c2, errorCode);
2655
                    // Set CANON_NOT_SEGMENT_STARTER for each remaining code point of a
2656
                    // one-way mapping. A 2-way mapping is possible here after
2657
                    // intermediate algorithmic mapping.
2658
2.23k
                    if(norm16_2>=minNoNo) {
2659
2.46k
                        while(i<length) {
2660
232
                            U16_NEXT_UNSAFE(mapping, i, c2);
2661
232
                            uint32_t c2Value = umutablecptrie_get(newData.mutableTrie, c2);
2662
232
                            if((c2Value&CANON_NOT_SEGMENT_STARTER)==0) {
2663
20
                                umutablecptrie_set(newData.mutableTrie, c2,
2664
20
                                                   c2Value|CANON_NOT_SEGMENT_STARTER, &errorCode);
2665
20
                            }
2666
232
                        }
2667
2.23k
                    }
2668
2.23k
                }
2669
2.23k
            } else {
2670
                // c decomposed to c2 algorithmically; c has cc==0
2671
4
                newData.addToStartSet(c, c2, errorCode);
2672
4
            }
2673
2.24k
        }
2674
4.86k
        if(newValue!=oldValue) {
2675
2.62k
            umutablecptrie_set(newData.mutableTrie, c, newValue, &errorCode);
2676
2.62k
        }
2677
4.86k
    }
2678
3.61k
}
2679
2680
5.71M
UBool Normalizer2Impl::ensureCanonIterData(UErrorCode &errorCode) const {
2681
    // Logically const: Synchronized instantiation.
2682
5.71M
    Normalizer2Impl *me=const_cast<Normalizer2Impl *>(this);
2683
5.71M
    umtx_initOnce(me->fCanonIterDataInitOnce, &initCanonIterData, me, errorCode);
2684
5.71M
    return U_SUCCESS(errorCode);
2685
5.71M
}
2686
2687
17.7M
int32_t Normalizer2Impl::getCanonValue(UChar32 c) const {
2688
17.7M
    return static_cast<int32_t>(ucptrie_get(fCanonIterData->trie, c));
2689
17.7M
}
2690
2691
790k
const UnicodeSet &Normalizer2Impl::getCanonStartSet(int32_t n) const {
2692
790k
    return *static_cast<const UnicodeSet*>(fCanonIterData->canonStartSets[n]);
2693
790k
}
2694
2695
1.37M
UBool Normalizer2Impl::isCanonSegmentStarter(UChar32 c) const {
2696
1.37M
    return getCanonValue(c)>=0;
2697
1.37M
}
2698
2699
16.3M
UBool Normalizer2Impl::getCanonStartSet(UChar32 c, UnicodeSet &set) const {
2700
16.3M
    int32_t canonValue=getCanonValue(c)&~CANON_NOT_SEGMENT_STARTER;
2701
16.3M
    if(canonValue==0) {
2702
13.9M
        return false;
2703
13.9M
    }
2704
2.34M
    set.clear();
2705
2.34M
    int32_t value=canonValue&CANON_VALUE_MASK;
2706
2.34M
    if((canonValue&CANON_HAS_SET)!=0) {
2707
790k
        set.addAll(getCanonStartSet(value));
2708
1.55M
    } else if(value!=0) {
2709
608k
        set.add(value);
2710
608k
    }
2711
2.34M
    if((canonValue&CANON_HAS_COMPOSITIONS)!=0) {
2712
1.03M
        uint16_t norm16=getRawNorm16(c);
2713
1.03M
        if(norm16==JAMO_L) {
2714
17.0k
            UChar32 syllable=
2715
17.0k
                static_cast<UChar32>(Hangul::HANGUL_BASE + (c - Hangul::JAMO_L_BASE) * Hangul::JAMO_VT_COUNT);
2716
17.0k
            set.add(syllable, syllable+Hangul::JAMO_VT_COUNT-1);
2717
1.01M
        } else {
2718
1.01M
            addComposites(getCompositionsList(norm16), set);
2719
1.01M
        }
2720
1.03M
    }
2721
2.34M
    return true;
2722
16.3M
}
2723
2724
U_NAMESPACE_END
2725
2726
// Normalizer2 data swapping ----------------------------------------------- ***
2727
2728
U_NAMESPACE_USE
2729
2730
U_CAPI int32_t U_EXPORT2
2731
unorm2_swap(const UDataSwapper *ds,
2732
            const void *inData, int32_t length, void *outData,
2733
0
            UErrorCode *pErrorCode) {
2734
0
    const UDataInfo *pInfo;
2735
0
    int32_t headerSize;
2736
2737
0
    const uint8_t *inBytes;
2738
0
    uint8_t *outBytes;
2739
2740
0
    const int32_t *inIndexes;
2741
0
    int32_t indexes[Normalizer2Impl::IX_TOTAL_SIZE+1];
2742
2743
0
    int32_t i, offset, nextOffset, size;
2744
2745
    /* udata_swapDataHeader checks the arguments */
2746
0
    headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
2747
0
    if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
2748
0
        return 0;
2749
0
    }
2750
2751
    /* check data format and format version */
2752
0
    pInfo=(const UDataInfo *)((const char *)inData+4);
2753
0
    uint8_t formatVersion0=pInfo->formatVersion[0];
2754
0
    if(!(
2755
0
        pInfo->dataFormat[0]==0x4e &&   /* dataFormat="Nrm2" */
2756
0
        pInfo->dataFormat[1]==0x72 &&
2757
0
        pInfo->dataFormat[2]==0x6d &&
2758
0
        pInfo->dataFormat[3]==0x32 &&
2759
0
        (1<=formatVersion0 && formatVersion0<=5)
2760
0
    )) {
2761
0
        udata_printError(ds, "unorm2_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as Normalizer2 data\n",
2762
0
                         pInfo->dataFormat[0], pInfo->dataFormat[1],
2763
0
                         pInfo->dataFormat[2], pInfo->dataFormat[3],
2764
0
                         pInfo->formatVersion[0]);
2765
0
        *pErrorCode=U_UNSUPPORTED_ERROR;
2766
0
        return 0;
2767
0
    }
2768
2769
0
    inBytes=(const uint8_t *)inData+headerSize;
2770
0
    outBytes=(outData == nullptr) ? nullptr : (uint8_t *)outData+headerSize;
2771
2772
0
    inIndexes=(const int32_t *)inBytes;
2773
0
    int32_t minIndexesLength;
2774
0
    if(formatVersion0==1) {
2775
0
        minIndexesLength=Normalizer2Impl::IX_MIN_MAYBE_YES+1;
2776
0
    } else if(formatVersion0==2) {
2777
0
        minIndexesLength=Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY+1;
2778
0
    } else if(formatVersion0<=4) {
2779
0
        minIndexesLength=Normalizer2Impl::IX_MIN_LCCC_CP+1;
2780
0
    } else {
2781
0
        minIndexesLength=Normalizer2Impl::IX_MIN_MAYBE_NO_COMBINES_FWD+1;
2782
0
    }
2783
2784
0
    if(length>=0) {
2785
0
        length-=headerSize;
2786
0
        if(length<minIndexesLength*4) {
2787
0
            udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for Normalizer2 data\n",
2788
0
                             length);
2789
0
            *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
2790
0
            return 0;
2791
0
        }
2792
0
    }
2793
2794
    /* read the first few indexes */
2795
0
    for(i=0; i<UPRV_LENGTHOF(indexes); ++i) {
2796
0
        indexes[i]=udata_readInt32(ds, inIndexes[i]);
2797
0
    }
2798
2799
    /* get the total length of the data */
2800
0
    size=indexes[Normalizer2Impl::IX_TOTAL_SIZE];
2801
2802
0
    if(length>=0) {
2803
0
        if(length<size) {
2804
0
            udata_printError(ds, "unorm2_swap(): too few bytes (%d after header) for all of Normalizer2 data\n",
2805
0
                             length);
2806
0
            *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
2807
0
            return 0;
2808
0
        }
2809
2810
        /* copy the data for inaccessible bytes */
2811
0
        if(inBytes!=outBytes) {
2812
0
            uprv_memcpy(outBytes, inBytes, size);
2813
0
        }
2814
2815
0
        offset=0;
2816
2817
        /* swap the int32_t indexes[] */
2818
0
        nextOffset=indexes[Normalizer2Impl::IX_NORM_TRIE_OFFSET];
2819
0
        ds->swapArray32(ds, inBytes, nextOffset-offset, outBytes, pErrorCode);
2820
0
        offset=nextOffset;
2821
2822
        /* swap the trie */
2823
0
        nextOffset=indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET];
2824
0
        utrie_swapAnyVersion(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
2825
0
        offset=nextOffset;
2826
2827
        /* swap the uint16_t extraData[] */
2828
0
        nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET];
2829
0
        ds->swapArray16(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode);
2830
0
        offset=nextOffset;
2831
2832
        /* no need to swap the uint8_t smallFCD[] (new in formatVersion 2) */
2833
0
        nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET+1];
2834
0
        offset=nextOffset;
2835
2836
0
        U_ASSERT(offset==size);
2837
0
    }
2838
2839
0
    return headerSize+size;
2840
0
}
2841
2842
#endif  // !UCONFIG_NO_NORMALIZATION