Coverage Report

Created: 2026-03-31 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/common/ubidiwrt.cpp
Line
Count
Source
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) 2000-2015, International Business Machines
7
*   Corporation and others.  All Rights Reserved.
8
*
9
******************************************************************************
10
*   file name:  ubidiwrt.c
11
*   encoding:   UTF-8
12
*   tab size:   8 (not used)
13
*   indentation:4
14
*
15
*   created on: 1999aug06
16
*   created by: Markus W. Scherer, updated by Matitiahu Allouche
17
*
18
* This file contains implementations for BiDi functions that use
19
* the core algorithm and core API to write reordered text.
20
*/
21
22
#include "unicode/utypes.h"
23
#include "unicode/ustring.h"
24
#include "unicode/uchar.h"
25
#include "unicode/ubidi.h"
26
#include "unicode/utf16.h"
27
#include "cmemory.h"
28
#include "ustr_imp.h"
29
#include "ubidiimp.h"
30
31
/*
32
 * The function implementations in this file are designed
33
 * for UTF-16 and UTF-32, not for UTF-8.
34
 *
35
 * Assumptions that are not true for UTF-8:
36
 * - Any code point always needs the same number of code units
37
 *   ("minimum-length-problem" of UTF-8)
38
 * - The BiDi control characters need only one code unit each
39
 *
40
 * Further assumptions for all UTFs:
41
 * - u_charMirror(c) needs the same number of code units as c
42
 */
43
#if defined(UTF_SIZE) && UTF_SIZE==8
44
# error reimplement ubidi_writeReordered() for UTF-8, see comment above
45
#endif
46
47
15.5k
#define IS_COMBINING(type) ((1UL<<(type))&(1UL<<U_NON_SPACING_MARK|1UL<<U_COMBINING_SPACING_MARK|1UL<<U_ENCLOSING_MARK))
48
49
/*
50
 * When we have UBIDI_OUTPUT_REVERSE set on ubidi_writeReordered(), then we
51
 * semantically write RTL runs in reverse and later reverse them again.
52
 * Instead, we actually write them in forward order to begin with.
53
 * However, if the RTL run was to be mirrored, we need to mirror here now
54
 * since the implicit second reversal must not do it.
55
 * It looks strange to do mirroring in LTR output, but it is only because
56
 * we are writing RTL output in reverse.
57
 */
58
static int32_t
59
doWriteForward(const char16_t *src, int32_t srcLength,
60
               char16_t *dest, int32_t destSize,
61
               uint16_t options,
62
16.3k
               UErrorCode *pErrorCode) {
63
16.3k
    if (srcLength<=0) {
64
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
65
0
        return 0;
66
0
    }
67
    /* optimize for several combinations of options */
68
16.3k
    switch(options&(UBIDI_REMOVE_BIDI_CONTROLS|UBIDI_DO_MIRRORING)) {
69
4.78k
    case 0: {
70
        /* simply copy the LTR run to the destination */
71
4.78k
        int32_t length=srcLength;
72
4.78k
        if(destSize<length) {
73
1.94k
            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
74
1.94k
            return srcLength;
75
1.94k
        }
76
7.18k
        do {
77
7.18k
            *dest++=*src++;
78
7.18k
        } while(--length>0);
79
2.84k
        return srcLength;
80
4.78k
    }
81
2.38k
    case UBIDI_DO_MIRRORING: {
82
        /* do mirroring */
83
2.38k
        int32_t i=0, j=0;
84
2.38k
        UChar32 c;
85
86
2.38k
        if(destSize<srcLength) {
87
823
            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
88
823
            return srcLength;
89
823
        }
90
4.47k
        do {
91
4.47k
            U16_NEXT(src, i, srcLength, c);
92
4.47k
            c=u_charMirror(c);
93
4.47k
            U16_APPEND_UNSAFE(dest, j, c);
94
4.47k
        } while(i<srcLength);
95
1.55k
        return srcLength;
96
2.38k
    }
97
5.26k
    case UBIDI_REMOVE_BIDI_CONTROLS: {
98
        /* copy the LTR run and remove any BiDi control characters */
99
5.26k
        int32_t remaining=destSize;
100
5.26k
        char16_t c;
101
17.3k
        do {
102
17.3k
            c=*src++;
103
17.3k
            if(!IS_BIDI_CONTROL_CHAR(c)) {
104
8.34k
                if(--remaining<0) {
105
1.04k
                    *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
106
107
                    /* preflight the length */
108
4.94k
                    while(--srcLength>0) {
109
3.90k
                        c=*src++;
110
3.90k
                        if(!IS_BIDI_CONTROL_CHAR(c)) {
111
3.10k
                            --remaining;
112
3.10k
                        }
113
3.90k
                    }
114
1.04k
                    return destSize-remaining;
115
1.04k
                }
116
7.29k
                *dest++=c;
117
7.29k
            }
118
17.3k
        } while(--srcLength>0);
119
4.21k
        return destSize-remaining;
120
5.26k
    }
121
3.91k
    default: {
122
        /* remove BiDi control characters and do mirroring */
123
3.91k
        int32_t remaining=destSize;
124
3.91k
        int32_t i, j=0;
125
3.91k
        UChar32 c;
126
13.6k
        do {
127
13.6k
            i=0;
128
13.6k
            U16_NEXT(src, i, srcLength, c);
129
13.6k
            src+=i;
130
13.6k
            srcLength-=i;
131
13.6k
            if(!IS_BIDI_CONTROL_CHAR(c)) {
132
7.98k
                remaining-=i;
133
7.98k
                if(remaining<0) {
134
1.16k
                    *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
135
136
                    /* preflight the length */
137
3.80k
                    while(srcLength>0) {
138
2.63k
                        c=*src++;
139
2.63k
                        if(!IS_BIDI_CONTROL_CHAR(c)) {
140
1.74k
                            --remaining;
141
1.74k
                        }
142
2.63k
                        --srcLength;
143
2.63k
                    }
144
1.16k
                    return destSize-remaining;
145
1.16k
                }
146
6.81k
                c=u_charMirror(c);
147
6.81k
                U16_APPEND_UNSAFE(dest, j, c);
148
6.81k
            }
149
13.6k
        } while(srcLength>0);
150
2.74k
        return j;
151
3.91k
    }
152
16.3k
    } /* end of switch */
153
16.3k
}
154
155
static int32_t
156
doWriteReverse(const char16_t *src, int32_t srcLength,
157
               char16_t *dest, int32_t destSize,
158
               uint16_t options,
159
15.2k
               UErrorCode *pErrorCode) {
160
    /*
161
     * RTL run -
162
     *
163
     * RTL runs need to be copied to the destination in reverse order
164
     * of code points, not code units, to keep Unicode characters intact.
165
     *
166
     * The general strategy for this is to read the source text
167
     * in backward order, collect all code units for a code point
168
     * (and optionally following combining characters, see below),
169
     * and copy all these code units in ascending order
170
     * to the destination for this run.
171
     *
172
     * Several options request whether combining characters
173
     * should be kept after their base characters,
174
     * whether BiDi control characters should be removed, and
175
     * whether characters should be replaced by their mirror-image
176
     * equivalent Unicode characters.
177
     */
178
15.2k
    int32_t i, j;
179
15.2k
    UChar32 c;
180
181
15.2k
    if(srcLength<=0) {
182
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
183
0
        return 0;
184
0
    }
185
186
    /* optimize for several combinations of options */
187
15.2k
    switch(options&(UBIDI_REMOVE_BIDI_CONTROLS|UBIDI_DO_MIRRORING|UBIDI_KEEP_BASE_COMBINING)) {
188
2.74k
    case 0:
189
        /*
190
         * With none of the "complicated" options set, the destination
191
         * run will have the same length as the source run,
192
         * and there is no mirroring and no keeping combining characters
193
         * with their base characters.
194
         */
195
2.74k
        if(destSize<srcLength) {
196
782
            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
197
782
            return srcLength;
198
782
        }
199
1.96k
        destSize=srcLength;
200
201
        /* preserve character integrity */
202
5.99k
        do {
203
            /* i is always after the last code unit known to need to be kept in this segment */
204
5.99k
            i=srcLength;
205
206
            /* collect code units for one base character */
207
5.99k
            U16_BACK_1(src, 0, srcLength);
208
209
            /* copy this base character */
210
5.99k
            j=srcLength;
211
6.26k
            do {
212
6.26k
                *dest++=src[j++];
213
6.26k
            } while(j<i);
214
5.99k
        } while(srcLength>0);
215
1.96k
        break;
216
2.72k
    case UBIDI_KEEP_BASE_COMBINING:
217
        /*
218
         * Here, too, the destination
219
         * run will have the same length as the source run,
220
         * and there is no mirroring.
221
         * We do need to keep combining characters with their base characters.
222
         */
223
2.72k
        if(destSize<srcLength) {
224
914
            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
225
914
            return srcLength;
226
914
        }
227
1.80k
        destSize=srcLength;
228
229
        /* preserve character integrity */
230
4.86k
        do {
231
            /* i is always after the last code unit known to need to be kept in this segment */
232
4.86k
            i=srcLength;
233
234
            /* collect code units and modifier letters for one base character */
235
5.41k
            do {
236
5.41k
                U16_PREV(src, 0, srcLength, c);
237
5.41k
            } while(srcLength>0 && IS_COMBINING(u_charType(c)));
238
239
            /* copy this "user character" */
240
4.86k
            j=srcLength;
241
5.64k
            do {
242
5.64k
                *dest++=src[j++];
243
5.64k
            } while(j<i);
244
4.86k
        } while(srcLength>0);
245
1.80k
        break;
246
9.82k
    default:
247
        /*
248
         * With several "complicated" options set, this is the most
249
         * general and the slowest copying of an RTL run.
250
         * We will do mirroring, remove BiDi controls, and
251
         * keep combining characters with their base characters
252
         * as requested.
253
         */
254
9.82k
        if(!(options&UBIDI_REMOVE_BIDI_CONTROLS)) {
255
1.56k
            i=srcLength;
256
8.26k
        } else {
257
            /* we need to find out the destination length of the run,
258
               which will not include the BiDi control characters */
259
8.26k
            int32_t length=srcLength;
260
8.26k
            char16_t ch;
261
262
8.26k
            i=0;
263
27.3k
            do {
264
27.3k
                ch=*src++;
265
27.3k
                if(!IS_BIDI_CONTROL_CHAR(ch)) {
266
18.5k
                    ++i;
267
18.5k
                }
268
27.3k
            } while(--length>0);
269
8.26k
            src-=srcLength;
270
8.26k
        }
271
272
9.82k
        if(destSize<i) {
273
2.46k
            *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
274
2.46k
            return i;
275
2.46k
        }
276
7.36k
        destSize=i;
277
278
        /* preserve character integrity */
279
22.5k
        do {
280
            /* i is always after the last code unit known to need to be kept in this segment */
281
22.5k
            i=srcLength;
282
283
            /* collect code units for one base character */
284
22.5k
            U16_PREV(src, 0, srcLength, c);
285
22.5k
            if(options&UBIDI_KEEP_BASE_COMBINING) {
286
                /* collect modifier letters for this base character */
287
17.9k
                while(srcLength>0 && IS_COMBINING(u_charType(c))) {
288
1.26k
                    U16_PREV(src, 0, srcLength, c);
289
1.26k
                }
290
16.6k
            }
291
292
22.5k
            if(options&UBIDI_REMOVE_BIDI_CONTROLS && IS_BIDI_CONTROL_CHAR(c)) {
293
                /* do not copy this BiDi control character */
294
8.47k
                continue;
295
8.47k
            }
296
297
            /* copy this "user character" */
298
14.0k
            j=srcLength;
299
14.0k
            if(options&UBIDI_DO_MIRRORING) {
300
                /* mirror only the base character */
301
3.00k
                int32_t k=0;
302
3.00k
                c=u_charMirror(c);
303
3.00k
                U16_APPEND_UNSAFE(dest, k, c);
304
3.00k
                dest+=k;
305
3.00k
                j+=k;
306
3.00k
            }
307
26.8k
            while(j<i) {
308
12.7k
                *dest++=src[j++];
309
12.7k
            }
310
22.5k
        } while(srcLength>0);
311
7.36k
        break;
312
15.2k
    } /* end of switch */
313
314
11.1k
    return destSize;
315
15.2k
}
316
317
U_CAPI int32_t U_EXPORT2
318
ubidi_writeReverse(const char16_t *src, int32_t srcLength,
319
                   char16_t *dest, int32_t destSize,
320
                   uint16_t options,
321
0
                   UErrorCode *pErrorCode) {
322
0
    int32_t destLength;
323
324
0
    if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
325
0
        return 0;
326
0
    }
327
328
    /* more error checking */
329
0
    if( src==nullptr || srcLength<-1 ||
330
0
        destSize<0 || (destSize>0 && dest==nullptr))
331
0
    {
332
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
333
0
        return 0;
334
0
    }
335
336
    /* do input and output overlap? */
337
0
    if( dest!=nullptr &&
338
0
        ((src>=dest && src<dest+destSize) ||
339
0
         (dest>=src && dest<src+srcLength)))
340
0
    {
341
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
342
0
        return 0;
343
0
    }
344
345
0
    if(srcLength==-1) {
346
0
        srcLength=u_strlen(src);
347
0
    }
348
0
    if(srcLength>0) {
349
0
        destLength=doWriteReverse(src, srcLength, dest, destSize, options, pErrorCode);
350
0
    } else {
351
        /* nothing to do */
352
0
        destLength=0;
353
0
    }
354
355
0
    return u_terminateUChars(dest, destSize, destLength, pErrorCode);
356
0
}
357
358
// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this
359
// function on Windows ARM64. As a work-around, we disable optimizations for this function.
360
// This work-around could/should be removed once the following versions of Visual Studio are no
361
// longer supported: All versions of VS2017, and versions of VS2019 below 16.4.
362
#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924))
363
#pragma optimize( "", off )
364
#endif
365
U_CAPI int32_t U_EXPORT2
366
ubidi_writeReordered(UBiDi *pBiDi,
367
                     char16_t *dest, int32_t destSize,
368
                     uint16_t options,
369
4.08k
                     UErrorCode *pErrorCode) {
370
4.08k
    const char16_t *text;
371
4.08k
    char16_t *saveDest;
372
4.08k
    int32_t length, destCapacity;
373
4.08k
    int32_t run, runCount, logicalStart, runLength;
374
375
4.08k
    if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
376
0
        return 0;
377
0
    }
378
379
    /* more error checking */
380
4.08k
    if( pBiDi==nullptr ||
381
4.08k
        (text=pBiDi->text)==nullptr || (length=pBiDi->length)<0 ||
382
4.08k
        destSize<0 || (destSize>0 && dest==nullptr))
383
0
    {
384
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
385
0
        return 0;
386
0
    }
387
388
    /* do input and output overlap? */
389
4.08k
    if( dest!=nullptr &&
390
4.08k
        ((text>=dest && text<dest+destSize) ||
391
4.08k
         (dest>=text && dest<text+pBiDi->originalLength)))
392
0
    {
393
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
394
0
        return 0;
395
0
    }
396
397
4.08k
    if(length==0) {
398
        /* nothing to do */
399
40
        return u_terminateUChars(dest, destSize, 0, pErrorCode);
400
40
    }
401
402
4.04k
    runCount=ubidi_countRuns(pBiDi, pErrorCode);
403
4.04k
    if(U_FAILURE(*pErrorCode)) {
404
0
        return 0;
405
0
    }
406
407
    /* destSize shrinks, later destination length=destCapacity-destSize */
408
4.04k
    saveDest=dest;
409
4.04k
    destCapacity=destSize;
410
4.04k
    runLength = 0;
411
412
    /*
413
     * Option "insert marks" implies UBIDI_INSERT_LRM_FOR_NUMERIC if the
414
     * reordering mode (checked below) is appropriate.
415
     */
416
4.04k
    if(pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) {
417
0
        options|=UBIDI_INSERT_LRM_FOR_NUMERIC;
418
0
        options&=~UBIDI_REMOVE_BIDI_CONTROLS;
419
0
    }
420
    /*
421
     * Option "remove controls" implies UBIDI_REMOVE_BIDI_CONTROLS
422
     * and cancels UBIDI_INSERT_LRM_FOR_NUMERIC.
423
     */
424
4.04k
    if(pBiDi->reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) {
425
0
        options|=UBIDI_REMOVE_BIDI_CONTROLS;
426
0
        options&=~UBIDI_INSERT_LRM_FOR_NUMERIC;
427
0
    }
428
    /*
429
     * If we do not perform the "inverse BiDi" algorithm, then we
430
     * don't need to insert any LRMs, and don't need to test for it.
431
     */
432
4.04k
    if((pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_NUMBERS_AS_L) &&
433
3.23k
       (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_LIKE_DIRECT)  &&
434
3.23k
       (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL) &&
435
3.23k
       (pBiDi->reorderingMode != UBIDI_REORDER_RUNS_ONLY)) {
436
3.23k
        options&=~UBIDI_INSERT_LRM_FOR_NUMERIC;
437
3.23k
    }
438
    /*
439
     * Iterate through all visual runs and copy the run text segments to
440
     * the destination, according to the options.
441
     *
442
     * The tests for where to insert LRMs ignore the fact that there may be
443
     * BN codes or non-BMP code points at the beginning and end of a run;
444
     * they may insert LRMs unnecessarily but the tests are faster this way
445
     * (this would have to be improved for UTF-8).
446
     *
447
     * Note that the only errors that are set by doWriteXY() are buffer overflow
448
     * errors. Ignore them until the end, and continue for preflighting.
449
     */
450
4.04k
    if(!(options&UBIDI_OUTPUT_REVERSE)) {
451
        /* forward output */
452
1.86k
        if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) {
453
            /* do not insert BiDi controls */
454
11.3k
            for(run=0; run<runCount; ++run) {
455
9.74k
                if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) {
456
6.33k
                    runLength=doWriteForward(text+logicalStart, runLength,
457
6.33k
                                             dest, destSize,
458
6.33k
                                             (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode);
459
6.33k
                } else {
460
3.40k
                    runLength=doWriteReverse(text+logicalStart, runLength,
461
3.40k
                                             dest, destSize,
462
3.40k
                                             options, pErrorCode);
463
3.40k
                }
464
9.74k
                if(dest!=nullptr) {
465
9.74k
                  dest+=runLength;
466
9.74k
                }
467
9.74k
                destSize-=runLength;
468
9.74k
            }
469
1.58k
        } else {
470
            /* insert BiDi controls for "inverse BiDi" */
471
282
            const DirProp *dirProps=pBiDi->dirProps;
472
282
            const char16_t *src;
473
282
            char16_t uc;
474
282
            UBiDiDirection dir;
475
282
            int32_t markFlag;
476
477
5.00k
            for(run=0; run<runCount; ++run) {
478
4.72k
                dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength);
479
4.72k
                src=text+logicalStart;
480
                /* check if something relevant in insertPoints */
481
4.72k
                markFlag=pBiDi->runs[run].insertRemove;
482
4.72k
                if(markFlag<0) {        /* BiDi controls count */
483
0
                    markFlag=0;
484
0
                }
485
486
4.72k
                if(UBIDI_LTR==dir) {
487
2.38k
                    if((pBiDi->isInverse) &&
488
2.38k
                       (/*run>0 &&*/ dirProps[logicalStart]!=L)) {
489
1.56k
                        markFlag |= LRM_BEFORE;
490
1.56k
                    }
491
2.38k
                    if (markFlag & LRM_BEFORE) {
492
1.56k
                        uc=LRM_CHAR;
493
1.56k
                    }
494
829
                    else if (markFlag & RLM_BEFORE) {
495
0
                        uc=RLM_CHAR;
496
0
                    }
497
829
                    else  uc=0;
498
2.38k
                    if(uc) {
499
1.56k
                        if(destSize>0) {
500
350
                            *dest++=uc;
501
350
                        }
502
1.56k
                        --destSize;
503
1.56k
                    }
504
505
2.38k
                    runLength=doWriteForward(src, runLength,
506
2.38k
                                             dest, destSize,
507
2.38k
                                             (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode);
508
2.38k
                    if(dest!=nullptr) {
509
2.38k
                      dest+=runLength;
510
2.38k
                    }
511
2.38k
                    destSize-=runLength;
512
513
2.38k
                    if((pBiDi->isInverse) &&
514
2.38k
                           (/*run<runCount-1 &&*/
515
2.38k
                            runLength > 0 && // doWriteForward may return 0 if src
516
                                             // only include bidi control chars
517
1.96k
                            dirProps[logicalStart+runLength-1]!=L)) {
518
1.46k
                        markFlag |= LRM_AFTER;
519
1.46k
                    }
520
2.38k
                    if (markFlag & LRM_AFTER) {
521
1.46k
                        uc=LRM_CHAR;
522
1.46k
                    }
523
927
                    else if (markFlag & RLM_AFTER) {
524
0
                        uc=RLM_CHAR;
525
0
                    }
526
927
                    else  uc=0;
527
2.38k
                    if(uc) {
528
1.46k
                        if(destSize>0) {
529
235
                            *dest++=uc;
530
235
                        }
531
1.46k
                        --destSize;
532
1.46k
                    }
533
2.38k
                } else {                /* RTL run */
534
2.33k
                    if((pBiDi->isInverse) &&
535
2.33k
                       (/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1])))) {
536
1.51k
                        markFlag |= RLM_BEFORE;
537
1.51k
                    }
538
2.33k
                    if (markFlag & LRM_BEFORE) {
539
0
                        uc=LRM_CHAR;
540
0
                    }
541
2.33k
                    else if (markFlag & RLM_BEFORE) {
542
1.51k
                        uc=RLM_CHAR;
543
1.51k
                    }
544
815
                    else  uc=0;
545
2.33k
                    if(uc) {
546
1.51k
                        if(destSize>0) {
547
236
                            *dest++=uc;
548
236
                        }
549
1.51k
                        --destSize;
550
1.51k
                    }
551
552
2.33k
                    runLength=doWriteReverse(src, runLength,
553
2.33k
                                             dest, destSize,
554
2.33k
                                             options, pErrorCode);
555
2.33k
                    if(dest!=nullptr) {
556
2.33k
                      dest+=runLength;
557
2.33k
                    }
558
2.33k
                    destSize-=runLength;
559
560
2.33k
                    if((pBiDi->isInverse) &&
561
2.33k
                       (/*run<runCount-1 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart])))) {
562
1.52k
                        markFlag |= RLM_AFTER;
563
1.52k
                    }
564
2.33k
                    if (markFlag & LRM_AFTER) {
565
0
                        uc=LRM_CHAR;
566
0
                    }
567
2.33k
                    else if (markFlag & RLM_AFTER) {
568
1.52k
                        uc=RLM_CHAR;
569
1.52k
                    }
570
808
                    else  uc=0;
571
2.33k
                    if(uc) {
572
1.52k
                        if(destSize>0) {
573
229
                            *dest++=uc;
574
229
                        }
575
1.52k
                        --destSize;
576
1.52k
                    }
577
2.33k
                }
578
4.72k
            }
579
282
        }
580
2.18k
    } else {
581
        /* reverse output */
582
2.18k
        if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) {
583
            /* do not insert BiDi controls */
584
13.9k
            for(run=runCount; --run>=0;) {
585
12.1k
                if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) {
586
7.05k
                    runLength=doWriteReverse(text+logicalStart, runLength,
587
7.05k
                                             dest, destSize,
588
7.05k
                                             (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode);
589
7.05k
                } else {
590
5.09k
                    runLength=doWriteForward(text+logicalStart, runLength,
591
5.09k
                                             dest, destSize,
592
5.09k
                                             options, pErrorCode);
593
5.09k
                }
594
12.1k
                if(dest!=nullptr) {
595
12.1k
                  dest+=runLength;
596
12.1k
                }
597
12.1k
                destSize-=runLength;
598
12.1k
            }
599
1.80k
        } else {
600
            /* insert BiDi controls for "inverse BiDi" */
601
371
            const DirProp *dirProps=pBiDi->dirProps;
602
371
            const char16_t *src;
603
371
            UBiDiDirection dir;
604
605
5.39k
            for(run=runCount; --run>=0;) {
606
                /* reverse output */
607
5.02k
                dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength);
608
5.02k
                src=text+logicalStart;
609
610
5.02k
                if(UBIDI_LTR==dir) {
611
2.50k
                    if(/*run<runCount-1 &&*/ dirProps[logicalStart+runLength-1]!=L) {
612
1.41k
                        if(destSize>0) {
613
321
                            *dest++=LRM_CHAR;
614
321
                        }
615
1.41k
                        --destSize;
616
1.41k
                    }
617
618
2.50k
                    runLength=doWriteReverse(src, runLength,
619
2.50k
                                             dest, destSize,
620
2.50k
                                             (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode);
621
2.50k
                    if(dest!=nullptr) {
622
2.50k
                      dest+=runLength;
623
2.50k
                    }
624
2.50k
                    destSize-=runLength;
625
626
2.50k
                    if(/*run>0 &&*/ dirProps[logicalStart]!=L) {
627
1.51k
                        if(destSize>0) {
628
406
                            *dest++=LRM_CHAR;
629
406
                        }
630
1.51k
                        --destSize;
631
1.51k
                    }
632
2.51k
                } else {
633
2.51k
                    if(/*run<runCount-1 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart]))) {
634
1.51k
                        if(destSize>0) {
635
373
                            *dest++=RLM_CHAR;
636
373
                        }
637
1.51k
                        --destSize;
638
1.51k
                    }
639
640
2.51k
                    runLength=doWriteForward(src, runLength,
641
2.51k
                                             dest, destSize,
642
2.51k
                                             options, pErrorCode);
643
2.51k
                    if(dest!=nullptr) {
644
2.51k
                      dest+=runLength;
645
2.51k
                    }
646
2.51k
                    destSize-=runLength;
647
648
2.51k
                    if(/*run>0 &&*/
649
2.51k
                            runLength > 0 && // doWriteForward may return 0 if src
650
                                             // only include bidi control chars
651
2.28k
                            !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1]))) {
652
1.49k
                        if(destSize>0) {
653
342
                            *dest++=RLM_CHAR;
654
342
                        }
655
1.49k
                        --destSize;
656
1.49k
                    }
657
2.51k
                }
658
5.02k
            }
659
371
        }
660
2.18k
    }
661
662
4.04k
    return u_terminateUChars(saveDest, destCapacity, destCapacity-destSize, pErrorCode);
663
4.04k
}
664
#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924))
665
#pragma optimize( "", on )
666
#endif