Coverage Report

Created: 2025-06-13 06:38

/src/icu/icu4c/source/common/ubidiln.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) 1999-2015, International Business Machines
7
*   Corporation and others.  All Rights Reserved.
8
*
9
******************************************************************************
10
*   file name:  ubidiln.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
19
#include "cmemory.h"
20
#include "unicode/utypes.h"
21
#include "unicode/ustring.h"
22
#include "unicode/uchar.h"
23
#include "unicode/ubidi.h"
24
#include "ubidiimp.h"
25
#include "uassert.h"
26
27
/*
28
 * General remarks about the functions in this file:
29
 *
30
 * These functions deal with the aspects of potentially mixed-directional
31
 * text in a single paragraph or in a line of a single paragraph
32
 * which has already been processed according to
33
 * the Unicode 6.3 BiDi algorithm as defined in
34
 * https://www.unicode.org/reports/tr9/ , version 28,
35
 * also described in The Unicode Standard, Version 6.3.0 .
36
 *
37
 * This means that there is a UBiDi object with a levels
38
 * and a dirProps array.
39
 * paraLevel and direction are also set.
40
 * Only if the length of the text is zero, then levels==dirProps==nullptr.
41
 *
42
 * The overall directionality of the paragraph
43
 * or line is used to bypass the reordering steps if possible.
44
 * Even purely RTL text does not need reordering there because
45
 * the ubidi_getLogical/VisualIndex() functions can compute the
46
 * index on the fly in such a case.
47
 *
48
 * The implementation of the access to same-level-runs and of the reordering
49
 * do attempt to provide better performance and less memory usage compared to
50
 * a direct implementation of especially rule (L2) with an array of
51
 * one (32-bit) integer per text character.
52
 *
53
 * Here, the levels array is scanned as soon as necessary, and a vector of
54
 * same-level-runs is created. Reordering then is done on this vector.
55
 * For each run of text positions that were resolved to the same level,
56
 * only 8 bytes are stored: the first text position of the run and the visual
57
 * position behind the run after reordering.
58
 * One sign bit is used to hold the directionality of the run.
59
 * This is inefficient if there are many very short runs. If the average run
60
 * length is <2, then this uses more memory.
61
 *
62
 * In a further attempt to save memory, the levels array is never changed
63
 * after all the resolution rules (Xn, Wn, Nn, In).
64
 * Many functions have to consider the field trailingWSStart:
65
 * if it is less than length, then there is an implicit trailing run
66
 * at the paraLevel,
67
 * which is not reflected in the levels array.
68
 * This allows a line UBiDi object to use the same levels array as
69
 * its paragraph parent object.
70
 *
71
 * When a UBiDi object is created for a line of a paragraph, then the
72
 * paragraph's levels and dirProps arrays are reused by way of setting
73
 * a pointer into them, not by copying. This again saves memory and forbids to
74
 * change the now shared levels for (L1).
75
 */
76
77
/* handle trailing WS (L1) -------------------------------------------------- */
78
79
/*
80
 * setTrailingWSStart() sets the start index for a trailing
81
 * run of WS in the line. This is necessary because we do not modify
82
 * the paragraph's levels array that we just point into.
83
 * Using trailingWSStart is another form of performing (L1).
84
 *
85
 * To make subsequent operations easier, we also include the run
86
 * before the WS if it is at the paraLevel - we merge the two here.
87
 *
88
 * This function is called only from ubidi_setLine(), so pBiDi->paraLevel is
89
 * set correctly for the line even when contextual multiple paragraphs.
90
 */
91
static void
92
0
setTrailingWSStart(UBiDi *pBiDi) {
93
    /* pBiDi->direction!=UBIDI_MIXED */
94
95
0
    const DirProp *dirProps=pBiDi->dirProps;
96
0
    UBiDiLevel *levels=pBiDi->levels;
97
0
    int32_t start=pBiDi->length;
98
0
    UBiDiLevel paraLevel=pBiDi->paraLevel;
99
100
    /* If the line is terminated by a block separator, all preceding WS etc...
101
       are already set to paragraph level.
102
       Setting trailingWSStart to pBidi->length will avoid changing the
103
       level of B chars from 0 to paraLevel in ubidi_getLevels when
104
       orderParagraphsLTR==true.
105
     */
106
0
    if(dirProps[start-1]==B) {
107
0
        pBiDi->trailingWSStart=start;   /* currently == pBiDi->length */
108
0
        return;
109
0
    }
110
    /* go backwards across all WS, BN, explicit codes */
111
0
    while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) {
112
0
        --start;
113
0
    }
114
115
    /* if the WS run can be merged with the previous run then do so here */
116
0
    while(start>0 && levels[start-1]==paraLevel) {
117
0
        --start;
118
0
    }
119
120
0
    pBiDi->trailingWSStart=start;
121
0
}
122
123
/* ubidi_setLine ------------------------------------------------------------ */
124
125
U_CAPI void U_EXPORT2
126
ubidi_setLine(const UBiDi *pParaBiDi,
127
              int32_t start, int32_t limit,
128
              UBiDi *pLineBiDi,
129
0
              UErrorCode *pErrorCode) {
130
0
    int32_t length;
131
132
    /* check the argument values */
133
0
    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
134
0
    RETURN_VOID_IF_NOT_VALID_PARA(pParaBiDi, *pErrorCode);
135
0
    RETURN_VOID_IF_BAD_RANGE(start, 0, limit, *pErrorCode);
136
0
    RETURN_VOID_IF_BAD_RANGE(limit, 0, pParaBiDi->length+1, *pErrorCode);
137
0
    if(pLineBiDi==nullptr) {
138
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
139
0
        return;
140
0
    }
141
0
    if(ubidi_getParagraph(pParaBiDi, start, nullptr, nullptr, nullptr, pErrorCode) !=
142
0
       ubidi_getParagraph(pParaBiDi, limit-1, nullptr, nullptr, nullptr, pErrorCode)) {
143
        /* the line crosses a paragraph boundary */
144
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
145
0
        return;
146
0
    }
147
148
    /* set the values in pLineBiDi from its pParaBiDi parent */
149
0
    pLineBiDi->pParaBiDi=nullptr;          /* mark unfinished setLine */
150
0
    pLineBiDi->text=pParaBiDi->text+start;
151
0
    length=pLineBiDi->length=limit-start;
152
0
    pLineBiDi->resultLength=pLineBiDi->originalLength=length;
153
0
    pLineBiDi->paraLevel=GET_PARALEVEL(pParaBiDi, start);
154
0
    pLineBiDi->paraCount=pParaBiDi->paraCount;
155
0
    pLineBiDi->runs=nullptr;
156
0
    pLineBiDi->flags=0;
157
0
    pLineBiDi->reorderingMode=pParaBiDi->reorderingMode;
158
0
    pLineBiDi->reorderingOptions=pParaBiDi->reorderingOptions;
159
0
    pLineBiDi->controlCount=0;
160
0
    if(pParaBiDi->controlCount>0) {
161
0
        int32_t j;
162
0
        for(j=start; j<limit; j++) {
163
0
            if(IS_BIDI_CONTROL_CHAR(pParaBiDi->text[j])) {
164
0
                pLineBiDi->controlCount++;
165
0
            }
166
0
        }
167
0
        pLineBiDi->resultLength-=pLineBiDi->controlCount;
168
0
    }
169
170
0
    pLineBiDi->dirProps=pParaBiDi->dirProps+start;
171
0
    pLineBiDi->levels=pParaBiDi->levels+start;
172
0
    pLineBiDi->runCount=-1;
173
174
0
    if(pParaBiDi->direction!=UBIDI_MIXED) {
175
        /* the parent is already trivial */
176
0
        pLineBiDi->direction=pParaBiDi->direction;
177
178
        /*
179
         * The parent's levels are all either
180
         * implicitly or explicitly ==paraLevel;
181
         * do the same here.
182
         */
183
0
        if(pParaBiDi->trailingWSStart<=start) {
184
0
            pLineBiDi->trailingWSStart=0;
185
0
        } else if(pParaBiDi->trailingWSStart<limit) {
186
0
            pLineBiDi->trailingWSStart=pParaBiDi->trailingWSStart-start;
187
0
        } else {
188
0
            pLineBiDi->trailingWSStart=length;
189
0
        }
190
0
    } else {
191
0
        const UBiDiLevel *levels=pLineBiDi->levels;
192
0
        int32_t i, trailingWSStart;
193
0
        UBiDiLevel level;
194
195
0
        setTrailingWSStart(pLineBiDi);
196
0
        trailingWSStart=pLineBiDi->trailingWSStart;
197
198
        /* recalculate pLineBiDi->direction */
199
0
        if(trailingWSStart==0) {
200
            /* all levels are at paraLevel */
201
0
            pLineBiDi->direction=(UBiDiDirection)(pLineBiDi->paraLevel&1);
202
0
        } else {
203
            /* get the level of the first character */
204
0
            level=(UBiDiLevel)(levels[0]&1);
205
206
            /* if there is anything of a different level, then the line is mixed */
207
0
            if(trailingWSStart<length && (pLineBiDi->paraLevel&1)!=level) {
208
                /* the trailing WS is at paraLevel, which differs from levels[0] */
209
0
                pLineBiDi->direction=UBIDI_MIXED;
210
0
            } else {
211
                /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */
212
0
                i=1;
213
0
                for(;;) {
214
0
                    if(i==trailingWSStart) {
215
                        /* the direction values match those in level */
216
0
                        pLineBiDi->direction=(UBiDiDirection)level;
217
0
                        break;
218
0
                    } else if((levels[i]&1)!=level) {
219
0
                        pLineBiDi->direction=UBIDI_MIXED;
220
0
                        break;
221
0
                    }
222
0
                    ++i;
223
0
                }
224
0
            }
225
0
        }
226
227
0
        switch(pLineBiDi->direction) {
228
0
        case UBIDI_LTR:
229
            /* make sure paraLevel is even */
230
0
            pLineBiDi->paraLevel=(UBiDiLevel)((pLineBiDi->paraLevel+1)&~1);
231
232
            /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */
233
0
            pLineBiDi->trailingWSStart=0;
234
0
            break;
235
0
        case UBIDI_RTL:
236
            /* make sure paraLevel is odd */
237
0
            pLineBiDi->paraLevel|=1;
238
239
            /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */
240
0
            pLineBiDi->trailingWSStart=0;
241
0
            break;
242
0
        default:
243
0
            break;
244
0
        }
245
0
    }
246
0
    pLineBiDi->pParaBiDi=pParaBiDi;     /* mark successful setLine */
247
0
}
248
249
U_CAPI UBiDiLevel U_EXPORT2
250
0
ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex) {
251
    /* return paraLevel if in the trailing WS run, otherwise the real level */
252
0
    if(!IS_VALID_PARA_OR_LINE(pBiDi) || charIndex<0 || pBiDi->length<=charIndex) {
253
0
        return 0;
254
0
    } else if(pBiDi->direction!=UBIDI_MIXED || charIndex>=pBiDi->trailingWSStart) {
255
0
        return GET_PARALEVEL(pBiDi, charIndex);
256
0
    } else {
257
0
        return pBiDi->levels[charIndex];
258
0
    }
259
0
}
260
261
U_CAPI const UBiDiLevel * U_EXPORT2
262
0
ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) {
263
0
    int32_t start, length;
264
265
0
    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, nullptr);
266
0
    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, nullptr);
267
0
    if((length=pBiDi->length)<=0) {
268
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
269
0
        return nullptr;
270
0
    }
271
0
    if((start=pBiDi->trailingWSStart)==length) {
272
        /* the current levels array reflects the WS run */
273
0
        return pBiDi->levels;
274
0
    }
275
276
    /*
277
     * After the previous if(), we know that the levels array
278
     * has an implicit trailing WS run and therefore does not fully
279
     * reflect itself all the levels.
280
     * This must be a UBiDi object for a line, and
281
     * we need to create a new levels array.
282
     */
283
0
    if(getLevelsMemory(pBiDi, length)) {
284
0
        UBiDiLevel *levels=pBiDi->levelsMemory;
285
286
0
        if(start>0 && levels!=pBiDi->levels) {
287
0
            uprv_memcpy(levels, pBiDi->levels, start);
288
0
        }
289
        /* pBiDi->paraLevel is ok even if contextual multiple paragraphs,
290
           since pBidi is a line object                                     */
291
0
        uprv_memset(levels+start, pBiDi->paraLevel, length-start);
292
293
        /* this new levels array is set for the line and reflects the WS run */
294
0
        pBiDi->trailingWSStart=length;
295
0
        return pBiDi->levels=levels;
296
0
    } else {
297
        /* out of memory */
298
0
        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
299
0
        return nullptr;
300
0
    }
301
0
}
302
303
U_CAPI void U_EXPORT2
304
ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition,
305
49.7k
                    int32_t *pLogicalLimit, UBiDiLevel *pLevel) {
306
49.7k
    UErrorCode errorCode;
307
49.7k
    int32_t runCount, visualStart, logicalLimit, logicalFirst, i;
308
49.7k
    Run iRun;
309
310
49.7k
    errorCode=U_ZERO_ERROR;
311
49.7k
    RETURN_VOID_IF_BAD_RANGE(logicalPosition, 0, pBiDi->length, errorCode);
312
    /* ubidi_countRuns will check VALID_PARA_OR_LINE */
313
49.7k
    runCount=ubidi_countRuns((UBiDi *)pBiDi, &errorCode);
314
49.7k
    if(U_FAILURE(errorCode)) {
315
0
        return;
316
0
    }
317
    /* this is done based on runs rather than on levels since levels have
318
       a special interpretation when UBIDI_REORDER_RUNS_ONLY
319
     */
320
49.7k
    visualStart=logicalLimit=0;
321
49.7k
    iRun=pBiDi->runs[0];
322
323
520k
    for(i=0; i<runCount; i++) {
324
520k
        iRun = pBiDi->runs[i];
325
520k
        logicalFirst=GET_INDEX(iRun.logicalStart);
326
520k
        logicalLimit=logicalFirst+iRun.visualLimit-visualStart;
327
520k
        if((logicalPosition>=logicalFirst) &&
328
520k
           (logicalPosition<logicalLimit)) {
329
49.7k
            break;
330
49.7k
        }
331
470k
        visualStart = iRun.visualLimit;
332
470k
    }
333
49.7k
    if(pLogicalLimit) {
334
0
        *pLogicalLimit=logicalLimit;
335
0
    }
336
49.7k
    if(pLevel) {
337
49.7k
        if(pBiDi->reorderingMode==UBIDI_REORDER_RUNS_ONLY) {
338
0
            *pLevel=(UBiDiLevel)GET_ODD_BIT(iRun.logicalStart);
339
0
        }
340
49.7k
        else if(pBiDi->direction!=UBIDI_MIXED || logicalPosition>=pBiDi->trailingWSStart) {
341
12.4k
            *pLevel=GET_PARALEVEL(pBiDi, logicalPosition);
342
37.2k
        } else {
343
37.2k
        *pLevel=pBiDi->levels[logicalPosition];
344
37.2k
        }
345
49.7k
    }
346
49.7k
}
347
348
/* runs API functions ------------------------------------------------------- */
349
350
U_CAPI int32_t U_EXPORT2
351
55.5k
ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) {
352
55.5k
    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
353
55.5k
    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
354
55.5k
    ubidi_getRuns(pBiDi, pErrorCode);
355
55.5k
    if(U_FAILURE(*pErrorCode)) {
356
0
        return -1;
357
0
    }
358
55.5k
    return pBiDi->runCount;
359
55.5k
}
360
361
U_CAPI UBiDiDirection U_EXPORT2
362
ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex,
363
                   int32_t *pLogicalStart, int32_t *pLength)
364
32.4k
{
365
32.4k
    int32_t start;
366
32.4k
    UErrorCode errorCode = U_ZERO_ERROR;
367
32.4k
    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, errorCode, UBIDI_LTR);
368
32.4k
    ubidi_getRuns(pBiDi, &errorCode);
369
32.4k
    if(U_FAILURE(errorCode)) {
370
0
        return UBIDI_LTR;
371
0
    }
372
32.4k
    RETURN_IF_BAD_RANGE(runIndex, 0, pBiDi->runCount, errorCode, UBIDI_LTR);
373
374
32.4k
    start=pBiDi->runs[runIndex].logicalStart;
375
32.4k
    if(pLogicalStart!=nullptr) {
376
32.4k
        *pLogicalStart=GET_INDEX(start);
377
32.4k
    }
378
32.4k
    if(pLength!=nullptr) {
379
32.4k
        if(runIndex>0) {
380
28.3k
            *pLength=pBiDi->runs[runIndex].visualLimit-
381
28.3k
                     pBiDi->runs[runIndex-1].visualLimit;
382
28.3k
        } else {
383
4.05k
            *pLength=pBiDi->runs[0].visualLimit;
384
4.05k
        }
385
32.4k
    }
386
32.4k
    return (UBiDiDirection)GET_ODD_BIT(start);
387
32.4k
}
388
389
/* in trivial cases there is only one trivial run; called by ubidi_getRuns() */
390
static void
391
1.48k
getSingleRun(UBiDi *pBiDi, UBiDiLevel level) {
392
    /* simple, single-run case */
393
1.48k
    pBiDi->runs=pBiDi->simpleRuns;
394
1.48k
    pBiDi->runCount=1;
395
396
    /* fill and reorder the single run */
397
1.48k
    pBiDi->runs[0].logicalStart=MAKE_INDEX_ODD_PAIR(0, level);
398
1.48k
    pBiDi->runs[0].visualLimit=pBiDi->length;
399
1.48k
    pBiDi->runs[0].insertRemove=0;
400
1.48k
}
401
402
/* reorder the runs array (L2) ---------------------------------------------- */
403
404
/*
405
 * Reorder the same-level runs in the runs array.
406
 * Here, runCount>1 and maxLevel>=minLevel>=paraLevel.
407
 * All the visualStart fields=logical start before reordering.
408
 * The "odd" bits are not set yet.
409
 *
410
 * Reordering with this data structure lends itself to some handy shortcuts:
411
 *
412
 * Since each run is moved but not modified, and since at the initial maxLevel
413
 * each sequence of same-level runs consists of only one run each, we
414
 * don't need to do anything there and can predecrement maxLevel.
415
 * In many simple cases, the reordering is thus done entirely in the
416
 * index mapping.
417
 * Also, reordering occurs only down to the lowest odd level that occurs,
418
 * which is minLevel|1. However, if the lowest level itself is odd, then
419
 * in the last reordering the sequence of the runs at this level or higher
420
 * will be all runs, and we don't need the elaborate loop to search for them.
421
 * This is covered by ++minLevel instead of minLevel|=1 followed
422
 * by an extra reorder-all after the reorder-some loop.
423
 * About a trailing WS run:
424
 * Such a run would need special treatment because its level is not
425
 * reflected in levels[] if this is not a paragraph object.
426
 * Instead, all characters from trailingWSStart on are implicitly at
427
 * paraLevel.
428
 * However, for all maxLevel>paraLevel, this run will never be reordered
429
 * and does not need to be taken into account. maxLevel==paraLevel is only reordered
430
 * if minLevel==paraLevel is odd, which is done in the extra segment.
431
 * This means that for the main reordering loop we don't need to consider
432
 * this run and can --runCount. If it is later part of the all-runs
433
 * reordering, then runCount is adjusted accordingly.
434
 */
435
static void
436
1.36k
reorderLine(UBiDi *pBiDi, UBiDiLevel minLevel, UBiDiLevel maxLevel) {
437
1.36k
    Run *runs, tempRun;
438
1.36k
    UBiDiLevel *levels;
439
1.36k
    int32_t firstRun, endRun, limitRun, runCount;
440
441
    /* nothing to do? */
442
1.36k
    if(maxLevel<=(minLevel|1)) {
443
228
        return;
444
228
    }
445
446
    /*
447
     * Reorder only down to the lowest odd level
448
     * and reorder at an odd minLevel in a separate, simpler loop.
449
     * See comments above for why minLevel is always incremented.
450
     */
451
1.13k
    ++minLevel;
452
453
1.13k
    runs=pBiDi->runs;
454
1.13k
    levels=pBiDi->levels;
455
1.13k
    runCount=pBiDi->runCount;
456
457
    /* do not include the WS run at paraLevel<=old minLevel except in the simple loop */
458
1.13k
    if(pBiDi->trailingWSStart<pBiDi->length) {
459
0
        --runCount;
460
0
    }
461
462
12.3k
    while(--maxLevel>=minLevel) {
463
11.2k
        firstRun=0;
464
465
        /* loop for all sequences of runs */
466
20.4k
        for(;;) {
467
            /* look for a sequence of runs that are all at >=maxLevel */
468
            /* look for the first run of such a sequence */
469
303k
            while(firstRun<runCount && levels[runs[firstRun].logicalStart]<maxLevel) {
470
282k
                ++firstRun;
471
282k
            }
472
20.4k
            if(firstRun>=runCount) {
473
3.98k
                break;  /* no more such runs */
474
3.98k
            }
475
476
            /* look for the limit run of such a sequence (the run behind it) */
477
286k
            for(limitRun=firstRun; ++limitRun<runCount && levels[runs[limitRun].logicalStart]>=maxLevel;) {}
478
479
            /* Swap the entire sequence of runs from firstRun to limitRun-1. */
480
16.5k
            endRun=limitRun-1;
481
154k
            while(firstRun<endRun) {
482
137k
                tempRun = runs[firstRun];
483
137k
                runs[firstRun]=runs[endRun];
484
137k
                runs[endRun]=tempRun;
485
137k
                ++firstRun;
486
137k
                --endRun;
487
137k
            }
488
489
16.5k
            if(limitRun==runCount) {
490
7.22k
                break;  /* no more such runs */
491
9.27k
            } else {
492
9.27k
                firstRun=limitRun+1;
493
9.27k
            }
494
16.5k
        }
495
11.2k
    }
496
497
    /* now do maxLevel==old minLevel (==odd!), see above */
498
1.13k
    if(!(minLevel&1)) {
499
800
        firstRun=0;
500
501
        /* include the trailing WS run in this complete reordering */
502
800
        if(pBiDi->trailingWSStart==pBiDi->length) {
503
800
            --runCount;
504
800
        }
505
506
        /* Swap the entire sequence of all runs. (endRun==runCount) */
507
6.69k
        while(firstRun<runCount) {
508
5.89k
            tempRun=runs[firstRun];
509
5.89k
            runs[firstRun]=runs[runCount];
510
5.89k
            runs[runCount]=tempRun;
511
5.89k
            ++firstRun;
512
5.89k
            --runCount;
513
5.89k
        }
514
800
    }
515
1.13k
}
516
517
/* compute the runs array --------------------------------------------------- */
518
519
0
static int32_t getRunFromLogicalIndex(UBiDi *pBiDi, int32_t logicalIndex) {
520
0
    Run *runs=pBiDi->runs;
521
0
    int32_t runCount=pBiDi->runCount, visualStart=0, i, length, logicalStart;
522
523
0
    for(i=0; i<runCount; i++) {
524
0
        length=runs[i].visualLimit-visualStart;
525
0
        logicalStart=GET_INDEX(runs[i].logicalStart);
526
0
        if((logicalIndex>=logicalStart) && (logicalIndex<(logicalStart+length))) {
527
0
            return i;
528
0
        }
529
0
        visualStart+=length;
530
0
    }
531
    /* we should never get here */
532
0
    UPRV_UNREACHABLE_EXIT;
533
0
}
534
535
/*
536
 * Compute the runs array from the levels array.
537
 * After ubidi_getRuns() returns true, runCount is guaranteed to be >0
538
 * and the runs are reordered.
539
 * Odd-level runs have visualStart on their visual right edge and
540
 * they progress visually to the left.
541
 * If option UBIDI_OPTION_INSERT_MARKS is set, insertRemove will contain the
542
 * sum of appropriate LRM/RLM_BEFORE/AFTER flags.
543
 * If option UBIDI_OPTION_REMOVE_CONTROLS is set, insertRemove will contain the
544
 * negative number of BiDi control characters within this run.
545
 */
546
U_CFUNC UBool
547
125k
ubidi_getRuns(UBiDi *pBiDi, UErrorCode*) {
548
    /*
549
     * This method returns immediately if the runs are already set. This
550
     * includes the case of length==0 (handled in setPara)..
551
     */
552
125k
    if (pBiDi->runCount>=0) {
553
122k
        return true;
554
122k
    }
555
556
2.84k
    if(pBiDi->direction!=UBIDI_MIXED) {
557
        /* simple, single-run case - this covers length==0 */
558
        /* pBiDi->paraLevel is ok even for contextual multiple paragraphs */
559
864
        getSingleRun(pBiDi, pBiDi->paraLevel);
560
1.98k
    } else /* UBIDI_MIXED, length>0 */ {
561
        /* mixed directionality */
562
1.98k
        int32_t length=pBiDi->length, limit;
563
1.98k
        UBiDiLevel *levels=pBiDi->levels;
564
1.98k
        int32_t i, runCount;
565
1.98k
        UBiDiLevel level=UBIDI_DEFAULT_LTR;   /* initialize with no valid level */
566
        /*
567
         * If there are WS characters at the end of the line
568
         * and the run preceding them has a level different from
569
         * paraLevel, then they will form their own run at paraLevel (L1).
570
         * Count them separately.
571
         * We need some special treatment for this in order to not
572
         * modify the levels array which a line UBiDi object shares
573
         * with its paragraph parent and its other line siblings.
574
         * In other words, for the trailing WS, it may be
575
         * levels[]!=paraLevel but we have to treat it like it were so.
576
         */
577
1.98k
        limit=pBiDi->trailingWSStart;
578
        /* count the runs, there is at least one non-WS run, and limit>0 */
579
1.98k
        runCount=0;
580
72.8k
        for(i=0; i<limit; ++i) {
581
            /* increment runCount at the start of each run */
582
70.8k
            if(levels[i]!=level) {
583
23.4k
                ++runCount;
584
23.4k
                level=levels[i];
585
23.4k
            }
586
70.8k
        }
587
588
        /*
589
         * We don't need to see if the last run can be merged with a trailing
590
         * WS run because setTrailingWSStart() would have done that.
591
         */
592
1.98k
        if(runCount==1 && limit==length) {
593
            /* There is only one non-WS run and no trailing WS-run. */
594
618
            getSingleRun(pBiDi, levels[0]);
595
1.36k
        } else /* runCount>1 || limit<length */ {
596
            /* allocate and set the runs */
597
1.36k
            Run *runs;
598
1.36k
            int32_t runIndex, start;
599
1.36k
            UBiDiLevel minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1, maxLevel=0;
600
601
            /* now, count a (non-mergeable) WS run */
602
1.36k
            if(limit<length) {
603
0
                ++runCount;
604
0
            }
605
606
            /* runCount>1 */
607
1.36k
            if(getRunsMemory(pBiDi, runCount)) {
608
1.36k
                runs=pBiDi->runsMemory;
609
1.36k
            } else {
610
0
                return false;
611
0
            }
612
613
            /* set the runs */
614
            /* FOOD FOR THOUGHT: this could be optimized, e.g.:
615
             * 464->444, 484->444, 575->555, 595->555
616
             * However, that would take longer. Check also how it would
617
             * interact with BiDi control removal and inserting Marks.
618
             */
619
1.36k
            runIndex=0;
620
621
            /* search for the run limits and initialize visualLimit values with the run lengths */
622
1.36k
            i=0;
623
22.8k
            do {
624
                /* prepare this run */
625
22.8k
                start=i;
626
22.8k
                level=levels[i];
627
22.8k
                if(level<minLevel) {
628
1.99k
                    minLevel=level;
629
1.99k
                }
630
22.8k
                if(level>maxLevel) {
631
7.14k
                    maxLevel=level;
632
7.14k
                }
633
634
                /* look for the run limit */
635
56.8k
                while(++i<limit && levels[i]==level) {}
636
637
                /* i is another run limit */
638
22.8k
                runs[runIndex].logicalStart=start;
639
22.8k
                runs[runIndex].visualLimit=i-start;
640
22.8k
                runs[runIndex].insertRemove=0;
641
22.8k
                ++runIndex;
642
22.8k
            } while(i<limit);
643
644
1.36k
            if(limit<length) {
645
                /* there is a separate WS run */
646
0
                runs[runIndex].logicalStart=limit;
647
0
                runs[runIndex].visualLimit=length-limit;
648
                /* For the trailing WS run, pBiDi->paraLevel is ok even
649
                   if contextual multiple paragraphs.                   */
650
0
                if(pBiDi->paraLevel<minLevel) {
651
0
                    minLevel=pBiDi->paraLevel;
652
0
                }
653
0
            }
654
655
            /* set the object fields */
656
1.36k
            pBiDi->runs=runs;
657
1.36k
            pBiDi->runCount=runCount;
658
659
1.36k
            reorderLine(pBiDi, minLevel, maxLevel);
660
661
            /* now add the direction flags and adjust the visualLimit's to be just that */
662
            /* this loop will also handle the trailing WS run */
663
1.36k
            limit=0;
664
24.2k
            for(i=0; i<runCount; ++i) {
665
22.8k
                ADD_ODD_BIT_FROM_LEVEL(runs[i].logicalStart, levels[runs[i].logicalStart]);
666
22.8k
                limit+=runs[i].visualLimit;
667
22.8k
                runs[i].visualLimit=limit;
668
22.8k
            }
669
670
            /* Set the "odd" bit for the trailing WS run. */
671
            /* For a RTL paragraph, it will be the *first* run in visual order. */
672
            /* For the trailing WS run, pBiDi->paraLevel is ok even if
673
               contextual multiple paragraphs.                          */
674
1.36k
            if(runIndex<runCount) {
675
0
                int32_t trailingRun = ((pBiDi->paraLevel & 1) != 0)? 0 : runIndex;
676
677
0
                ADD_ODD_BIT_FROM_LEVEL(runs[trailingRun].logicalStart, pBiDi->paraLevel);
678
0
            }
679
1.36k
        }
680
1.98k
    }
681
682
    /* handle insert LRM/RLM BEFORE/AFTER run */
683
2.84k
    if(pBiDi->insertPoints.size>0) {
684
0
        Point *point, *start=pBiDi->insertPoints.points,
685
0
                      *limit=start+pBiDi->insertPoints.size;
686
0
        int32_t runIndex;
687
0
        for(point=start; point<limit; point++) {
688
0
            runIndex=getRunFromLogicalIndex(pBiDi, point->pos);
689
0
            pBiDi->runs[runIndex].insertRemove|=point->flag;
690
0
        }
691
0
    }
692
693
    /* handle remove BiDi control characters */
694
2.84k
    if(pBiDi->controlCount>0) {
695
0
        int32_t runIndex;
696
0
        const char16_t *start=pBiDi->text, *limit=start+pBiDi->length, *pu;
697
0
        for(pu=start; pu<limit; pu++) {
698
0
            if(IS_BIDI_CONTROL_CHAR(*pu)) {
699
0
                runIndex=getRunFromLogicalIndex(pBiDi, (int32_t)(pu-start));
700
0
                pBiDi->runs[runIndex].insertRemove--;
701
0
            }
702
0
        }
703
0
    }
704
705
2.84k
    return true;
706
2.84k
}
707
708
static UBool
709
prepareReorder(const UBiDiLevel *levels, int32_t length,
710
               int32_t *indexMap,
711
0
               UBiDiLevel *pMinLevel, UBiDiLevel *pMaxLevel) {
712
0
    int32_t start;
713
0
    UBiDiLevel level, minLevel, maxLevel;
714
715
0
    if(levels==nullptr || length<=0) {
716
0
        return false;
717
0
    }
718
719
    /* determine minLevel and maxLevel */
720
0
    minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1;
721
0
    maxLevel=0;
722
0
    for(start=length; start>0;) {
723
0
        level=levels[--start];
724
0
        if(level>UBIDI_MAX_EXPLICIT_LEVEL+1) {
725
0
            return false;
726
0
        }
727
0
        if(level<minLevel) {
728
0
            minLevel=level;
729
0
        }
730
0
        if(level>maxLevel) {
731
0
            maxLevel=level;
732
0
        }
733
0
    }
734
0
    *pMinLevel=minLevel;
735
0
    *pMaxLevel=maxLevel;
736
737
    /* initialize the index map */
738
0
    for(start=length; start>0;) {
739
0
        --start;
740
0
        indexMap[start]=start;
741
0
    }
742
743
0
    return true;
744
0
}
745
746
/* reorder a line based on a levels array (L2) ------------------------------ */
747
748
U_CAPI void U_EXPORT2
749
0
ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) {
750
0
    int32_t start, limit, sumOfSosEos;
751
0
    UBiDiLevel minLevel = 0, maxLevel = 0;
752
753
0
    if(indexMap==nullptr || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) {
754
0
        return;
755
0
    }
756
757
    /* nothing to do? */
758
0
    if(minLevel==maxLevel && (minLevel&1)==0) {
759
0
        return;
760
0
    }
761
762
    /* reorder only down to the lowest odd level */
763
0
    minLevel|=1;
764
765
    /* loop maxLevel..minLevel */
766
0
    do {
767
0
        start=0;
768
769
        /* loop for all sequences of levels to reorder at the current maxLevel */
770
0
        for(;;) {
771
            /* look for a sequence of levels that are all at >=maxLevel */
772
            /* look for the first index of such a sequence */
773
0
            while(start<length && levels[start]<maxLevel) {
774
0
                ++start;
775
0
            }
776
0
            if(start>=length) {
777
0
                break;  /* no more such sequences */
778
0
            }
779
780
            /* look for the limit of such a sequence (the index behind it) */
781
0
            for(limit=start; ++limit<length && levels[limit]>=maxLevel;) {}
782
783
            /*
784
             * sos=start of sequence, eos=end of sequence
785
             *
786
             * The closed (inclusive) interval from sos to eos includes all the logical
787
             * and visual indexes within this sequence. They are logically and
788
             * visually contiguous and in the same range.
789
             *
790
             * For each run, the new visual index=sos+eos-old visual index;
791
             * we pre-add sos+eos into sumOfSosEos ->
792
             * new visual index=sumOfSosEos-old visual index;
793
             */
794
0
            sumOfSosEos=start+limit-1;
795
796
            /* reorder each index in the sequence */
797
0
            do {
798
0
                indexMap[start]=sumOfSosEos-indexMap[start];
799
0
            } while(++start<limit);
800
801
            /* start==limit */
802
0
            if(limit==length) {
803
0
                break;  /* no more such sequences */
804
0
            } else {
805
0
                start=limit+1;
806
0
            }
807
0
        }
808
0
    } while(--maxLevel>=minLevel);
809
0
}
810
811
U_CAPI void U_EXPORT2
812
0
ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) {
813
0
    int32_t start, end, limit, temp;
814
0
    UBiDiLevel minLevel = 0, maxLevel = 0;
815
816
0
    if(indexMap==nullptr || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) {
817
0
        return;
818
0
    }
819
820
    /* nothing to do? */
821
0
    if(minLevel==maxLevel && (minLevel&1)==0) {
822
0
        return;
823
0
    }
824
825
    /* reorder only down to the lowest odd level */
826
0
    minLevel|=1;
827
828
    /* loop maxLevel..minLevel */
829
0
    do {
830
0
        start=0;
831
832
        /* loop for all sequences of levels to reorder at the current maxLevel */
833
0
        for(;;) {
834
            /* look for a sequence of levels that are all at >=maxLevel */
835
            /* look for the first index of such a sequence */
836
0
            while(start<length && levels[start]<maxLevel) {
837
0
                ++start;
838
0
            }
839
0
            if(start>=length) {
840
0
                break;  /* no more such runs */
841
0
            }
842
843
            /* look for the limit of such a sequence (the index behind it) */
844
0
            for(limit=start; ++limit<length && levels[limit]>=maxLevel;) {}
845
846
            /*
847
             * Swap the entire interval of indexes from start to limit-1.
848
             * We don't need to swap the levels for the purpose of this
849
             * algorithm: the sequence of levels that we look at does not
850
             * move anyway.
851
             */
852
0
            end=limit-1;
853
0
            while(start<end) {
854
0
                temp=indexMap[start];
855
0
                indexMap[start]=indexMap[end];
856
0
                indexMap[end]=temp;
857
858
0
                ++start;
859
0
                --end;
860
0
            }
861
862
0
            if(limit==length) {
863
0
                break;  /* no more such sequences */
864
0
            } else {
865
0
                start=limit+1;
866
0
            }
867
0
        }
868
0
    } while(--maxLevel>=minLevel);
869
0
}
870
871
/* API functions for logical<->visual mapping ------------------------------- */
872
873
U_CAPI int32_t U_EXPORT2
874
24.8k
ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) {
875
24.8k
    int32_t visualIndex=UBIDI_MAP_NOWHERE;
876
24.8k
    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
877
24.8k
    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
878
24.8k
    RETURN_IF_BAD_RANGE(logicalIndex, 0, pBiDi->length, *pErrorCode, -1);
879
880
    /* we can do the trivial cases without the runs array */
881
24.8k
    switch(pBiDi->direction) {
882
4.34k
    case UBIDI_LTR:
883
4.34k
        visualIndex=logicalIndex;
884
4.34k
        break;
885
1.87k
    case UBIDI_RTL:
886
1.87k
        visualIndex=pBiDi->length-logicalIndex-1;
887
1.87k
        break;
888
18.6k
    default:
889
18.6k
        if(!ubidi_getRuns(pBiDi, pErrorCode)) {
890
0
            *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
891
0
            return -1;
892
18.6k
        } else {
893
18.6k
            Run *runs=pBiDi->runs;
894
18.6k
            int32_t i, visualStart=0, offset, length;
895
896
            /* linear search for the run, search on the visual runs */
897
253k
            for(i=0; i<pBiDi->runCount; ++i) {
898
253k
                length=runs[i].visualLimit-visualStart;
899
253k
                offset=logicalIndex-GET_INDEX(runs[i].logicalStart);
900
253k
                if(offset>=0 && offset<length) {
901
18.6k
                    if(IS_EVEN_RUN(runs[i].logicalStart)) {
902
                        /* LTR */
903
11.1k
                        visualIndex=visualStart+offset;
904
11.1k
                    } else {
905
                        /* RTL */
906
7.48k
                        visualIndex=visualStart+length-offset-1;
907
7.48k
                    }
908
18.6k
                    break;          /* exit for loop */
909
18.6k
                }
910
235k
                visualStart+=length;
911
235k
            }
912
18.6k
            if(i>=pBiDi->runCount) {
913
0
                return UBIDI_MAP_NOWHERE;
914
0
            }
915
18.6k
        }
916
24.8k
    }
917
918
24.8k
    if(pBiDi->insertPoints.size>0) {
919
        /* add the number of added marks until the calculated visual index */
920
0
        Run *runs=pBiDi->runs;
921
0
        int32_t i, length, insertRemove;
922
0
        int32_t visualStart=0, markFound=0;
923
0
        for(i=0; ; i++, visualStart+=length) {
924
0
            length=runs[i].visualLimit-visualStart;
925
0
            insertRemove=runs[i].insertRemove;
926
0
            if(insertRemove & (LRM_BEFORE|RLM_BEFORE)) {
927
0
                markFound++;
928
0
            }
929
            /* is it the run containing the visual index? */
930
0
            if(visualIndex<runs[i].visualLimit) {
931
0
                return visualIndex+markFound;
932
0
            }
933
0
            if(insertRemove & (LRM_AFTER|RLM_AFTER)) {
934
0
                markFound++;
935
0
            }
936
0
        }
937
0
    }
938
24.8k
    else if(pBiDi->controlCount>0) {
939
        /* subtract the number of controls until the calculated visual index */
940
0
        Run *runs=pBiDi->runs;
941
0
        int32_t i, j, start, limit, length, insertRemove;
942
0
        int32_t visualStart=0, controlFound=0;
943
0
        char16_t uchar=pBiDi->text[logicalIndex];
944
        /* is the logical index pointing to a control ? */
945
0
        if(IS_BIDI_CONTROL_CHAR(uchar)) {
946
0
            return UBIDI_MAP_NOWHERE;
947
0
        }
948
        /* loop on runs */
949
0
        for(i=0; ; i++, visualStart+=length) {
950
0
            length=runs[i].visualLimit-visualStart;
951
0
            insertRemove=runs[i].insertRemove;
952
            /* calculated visual index is beyond this run? */
953
0
            if(visualIndex>=runs[i].visualLimit) {
954
0
                controlFound-=insertRemove;
955
0
                continue;
956
0
            }
957
            /* calculated visual index must be within current run */
958
0
            if(insertRemove==0) {
959
0
                return visualIndex-controlFound;
960
0
            }
961
0
            if(IS_EVEN_RUN(runs[i].logicalStart)) {
962
                /* LTR: check from run start to logical index */
963
0
                start=runs[i].logicalStart;
964
0
                limit=logicalIndex;
965
0
            } else {
966
                /* RTL: check from logical index to run end */
967
0
                start=logicalIndex+1;
968
0
                limit=GET_INDEX(runs[i].logicalStart)+length;
969
0
            }
970
0
            for(j=start; j<limit; j++) {
971
0
                uchar=pBiDi->text[j];
972
0
                if(IS_BIDI_CONTROL_CHAR(uchar)) {
973
0
                    controlFound++;
974
0
                }
975
0
            }
976
0
            return visualIndex-controlFound;
977
0
        }
978
0
    }
979
980
24.8k
    return visualIndex;
981
24.8k
}
982
983
U_CAPI int32_t U_EXPORT2
984
24.8k
ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) {
985
24.8k
    Run *runs;
986
24.8k
    int32_t i, runCount, start;
987
24.8k
    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
988
24.8k
    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
989
24.8k
    RETURN_IF_BAD_RANGE(visualIndex, 0, pBiDi->resultLength, *pErrorCode, -1);
990
    /* we can do the trivial cases without the runs array */
991
24.8k
    if(pBiDi->insertPoints.size==0 && pBiDi->controlCount==0) {
992
24.8k
        if(pBiDi->direction==UBIDI_LTR) {
993
4.34k
            return visualIndex;
994
4.34k
        }
995
20.5k
        else if(pBiDi->direction==UBIDI_RTL) {
996
1.87k
            return pBiDi->length-visualIndex-1;
997
1.87k
        }
998
24.8k
    }
999
18.6k
    if(!ubidi_getRuns(pBiDi, pErrorCode)) {
1000
0
        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1001
0
        return -1;
1002
0
    }
1003
1004
18.6k
    runs=pBiDi->runs;
1005
18.6k
    runCount=pBiDi->runCount;
1006
18.6k
    if(pBiDi->insertPoints.size>0) {
1007
        /* handle inserted LRM/RLM */
1008
0
        int32_t markFound=0, insertRemove;
1009
0
        int32_t visualStart=0, length;
1010
0
        runs=pBiDi->runs;
1011
        /* subtract number of marks until visual index */
1012
0
        for(i=0; ; i++, visualStart+=length) {
1013
0
            length=runs[i].visualLimit-visualStart;
1014
0
            insertRemove=runs[i].insertRemove;
1015
0
            if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
1016
0
                if(visualIndex<=(visualStart+markFound)) {
1017
0
                    return UBIDI_MAP_NOWHERE;
1018
0
                }
1019
0
                markFound++;
1020
0
            }
1021
            /* is adjusted visual index within this run? */
1022
0
            if(visualIndex<(runs[i].visualLimit+markFound)) {
1023
0
                visualIndex-=markFound;
1024
0
                break;
1025
0
            }
1026
0
            if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
1027
0
                if(visualIndex==(visualStart+length+markFound)) {
1028
0
                    return UBIDI_MAP_NOWHERE;
1029
0
                }
1030
0
                markFound++;
1031
0
            }
1032
0
        }
1033
0
    }
1034
18.6k
    else if(pBiDi->controlCount>0) {
1035
        /* handle removed BiDi control characters */
1036
0
        int32_t controlFound=0, insertRemove, length;
1037
0
        int32_t logicalStart, logicalEnd, visualStart=0, j, k;
1038
0
        char16_t uchar;
1039
0
        UBool evenRun;
1040
        /* add number of controls until visual index */
1041
0
        for(i=0; ; i++, visualStart+=length) {
1042
0
            length=runs[i].visualLimit-visualStart;
1043
0
            insertRemove=runs[i].insertRemove;
1044
            /* is adjusted visual index beyond current run? */
1045
0
            if(visualIndex>=(runs[i].visualLimit-controlFound+insertRemove)) {
1046
0
                controlFound-=insertRemove;
1047
0
                continue;
1048
0
            }
1049
            /* adjusted visual index is within current run */
1050
0
            if(insertRemove==0) {
1051
0
                visualIndex+=controlFound;
1052
0
                break;
1053
0
            }
1054
            /* count non-control chars until visualIndex */
1055
0
            logicalStart=runs[i].logicalStart;
1056
0
            evenRun=IS_EVEN_RUN(logicalStart);
1057
0
            REMOVE_ODD_BIT(logicalStart);
1058
0
            logicalEnd=logicalStart+length-1;
1059
0
            for(j=0; j<length; j++) {
1060
0
                k= evenRun ? logicalStart+j : logicalEnd-j;
1061
0
                uchar=pBiDi->text[k];
1062
0
                if(IS_BIDI_CONTROL_CHAR(uchar)) {
1063
0
                    controlFound++;
1064
0
                }
1065
0
                if((visualIndex+controlFound)==(visualStart+j)) {
1066
0
                    break;
1067
0
                }
1068
0
            }
1069
0
            visualIndex+=controlFound;
1070
0
            break;
1071
0
        }
1072
0
    }
1073
    /* handle all cases */
1074
18.6k
    if(runCount<=10) {
1075
        /* linear search for the run */
1076
20.9k
        for(i=0; visualIndex>=runs[i].visualLimit; ++i) {}
1077
9.95k
    } else {
1078
        /* binary search for the run */
1079
8.68k
        int32_t begin=0, limit=runCount;
1080
1081
        /* the middle if() is guaranteed to find the run, we don't need a loop limit */
1082
40.1k
        for(;;) {
1083
40.1k
            i=(begin+limit)/2;
1084
40.1k
            if(visualIndex>=runs[i].visualLimit) {
1085
16.6k
                begin=i+1;
1086
23.4k
            } else if(i==0 || visualIndex>=runs[i-1].visualLimit) {
1087
8.68k
                break;
1088
14.7k
            } else {
1089
14.7k
                limit=i;
1090
14.7k
            }
1091
40.1k
        }
1092
8.68k
    }
1093
1094
18.6k
    start=runs[i].logicalStart;
1095
18.6k
    if(IS_EVEN_RUN(start)) {
1096
        /* LTR */
1097
        /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */
1098
11.1k
        if(i>0) {
1099
8.16k
            visualIndex-=runs[i-1].visualLimit;
1100
8.16k
        }
1101
11.1k
        return start+visualIndex;
1102
11.1k
    } else {
1103
        /* RTL */
1104
7.48k
        return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1;
1105
7.48k
    }
1106
18.6k
}
1107
1108
U_CAPI void U_EXPORT2
1109
881
ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) {
1110
881
    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
1111
    /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */
1112
881
    ubidi_countRuns(pBiDi, pErrorCode);
1113
881
    if(U_FAILURE(*pErrorCode)) {
1114
        /* no op */
1115
881
    } else if(indexMap==nullptr) {
1116
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
1117
881
    } else {
1118
        /* fill a logical-to-visual index map using the runs[] */
1119
881
        int32_t visualStart, visualLimit, i, j, k;
1120
881
        int32_t logicalStart, logicalLimit;
1121
881
        Run *runs=pBiDi->runs;
1122
881
        if (pBiDi->length<=0) {
1123
36
            return;
1124
36
        }
1125
845
        if (pBiDi->length>pBiDi->resultLength) {
1126
0
            uprv_memset(indexMap, 0xFF, pBiDi->length*sizeof(int32_t));
1127
0
        }
1128
1129
845
        visualStart=0;
1130
6.64k
        for(j=0; j<pBiDi->runCount; ++j) {
1131
5.80k
            logicalStart=GET_INDEX(runs[j].logicalStart);
1132
5.80k
            visualLimit=runs[j].visualLimit;
1133
5.80k
            if(IS_EVEN_RUN(runs[j].logicalStart)) {
1134
15.4k
                do { /* LTR */
1135
15.4k
                    indexMap[logicalStart++]=visualStart++;
1136
15.4k
                } while(visualStart<visualLimit);
1137
4.01k
            } else {
1138
1.79k
                logicalStart+=visualLimit-visualStart;  /* logicalLimit */
1139
9.36k
                do { /* RTL */
1140
9.36k
                    indexMap[--logicalStart]=visualStart++;
1141
9.36k
                } while(visualStart<visualLimit);
1142
1.79k
            }
1143
            /* visualStart==visualLimit; */
1144
5.80k
        }
1145
1146
845
        if(pBiDi->insertPoints.size>0) {
1147
0
            int32_t markFound=0, runCount=pBiDi->runCount;
1148
0
            int32_t length, insertRemove;
1149
0
            visualStart=0;
1150
            /* add number of marks found until each index */
1151
0
            for(i=0; i<runCount; i++, visualStart+=length) {
1152
0
                length=runs[i].visualLimit-visualStart;
1153
0
                insertRemove=runs[i].insertRemove;
1154
0
                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
1155
0
                    markFound++;
1156
0
                }
1157
0
                if(markFound>0) {
1158
0
                    logicalStart=GET_INDEX(runs[i].logicalStart);
1159
0
                    logicalLimit=logicalStart+length;
1160
0
                    for(j=logicalStart; j<logicalLimit; j++) {
1161
0
                        indexMap[j]+=markFound;
1162
0
                    }
1163
0
                }
1164
0
                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
1165
0
                    markFound++;
1166
0
                }
1167
0
            }
1168
0
        }
1169
845
        else if(pBiDi->controlCount>0) {
1170
0
            int32_t controlFound=0, runCount=pBiDi->runCount;
1171
0
            int32_t length, insertRemove;
1172
0
            UBool evenRun;
1173
0
            char16_t uchar;
1174
0
            visualStart=0;
1175
            /* subtract number of controls found until each index */
1176
0
            for(i=0; i<runCount; i++, visualStart+=length) {
1177
0
                length=runs[i].visualLimit-visualStart;
1178
0
                insertRemove=runs[i].insertRemove;
1179
                /* no control found within previous runs nor within this run */
1180
0
                if((controlFound-insertRemove)==0) {
1181
0
                    continue;
1182
0
                }
1183
0
                logicalStart=runs[i].logicalStart;
1184
0
                evenRun=IS_EVEN_RUN(logicalStart);
1185
0
                REMOVE_ODD_BIT(logicalStart);
1186
0
                logicalLimit=logicalStart+length;
1187
                /* if no control within this run */
1188
0
                if(insertRemove==0) {
1189
0
                    for(j=logicalStart; j<logicalLimit; j++) {
1190
0
                        indexMap[j]-=controlFound;
1191
0
                    }
1192
0
                    continue;
1193
0
                }
1194
0
                for(j=0; j<length; j++) {
1195
0
                    k= evenRun ? logicalStart+j : logicalLimit-j-1;
1196
0
                    uchar=pBiDi->text[k];
1197
0
                    if(IS_BIDI_CONTROL_CHAR(uchar)) {
1198
0
                        controlFound++;
1199
0
                        indexMap[k]=UBIDI_MAP_NOWHERE;
1200
0
                        continue;
1201
0
                    }
1202
0
                    indexMap[k]-=controlFound;
1203
0
                }
1204
0
            }
1205
0
        }
1206
845
    }
1207
881
}
1208
1209
U_CAPI void U_EXPORT2
1210
881
ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) {
1211
881
    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
1212
881
    if(indexMap==nullptr) {
1213
0
        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
1214
0
        return;
1215
0
    }
1216
    /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */
1217
881
    ubidi_countRuns(pBiDi, pErrorCode);
1218
881
    if(U_SUCCESS(*pErrorCode)) {
1219
        /* fill a visual-to-logical index map using the runs[] */
1220
881
        Run *runs=pBiDi->runs, *runsLimit=runs+pBiDi->runCount;
1221
881
        int32_t logicalStart, visualStart, visualLimit, *pi=indexMap;
1222
1223
881
        if (pBiDi->resultLength<=0) {
1224
36
            return;
1225
36
        }
1226
845
        visualStart=0;
1227
6.64k
        for(; runs<runsLimit; ++runs) {
1228
5.80k
            logicalStart=runs->logicalStart;
1229
5.80k
            visualLimit=runs->visualLimit;
1230
5.80k
            if(IS_EVEN_RUN(logicalStart)) {
1231
15.4k
                do { /* LTR */
1232
15.4k
                    *pi++ = logicalStart++;
1233
15.4k
                } while(++visualStart<visualLimit);
1234
4.01k
            } else {
1235
1.79k
                REMOVE_ODD_BIT(logicalStart);
1236
1.79k
                logicalStart+=visualLimit-visualStart;  /* logicalLimit */
1237
9.36k
                do { /* RTL */
1238
9.36k
                    *pi++ = --logicalStart;
1239
9.36k
                } while(++visualStart<visualLimit);
1240
1.79k
            }
1241
            /* visualStart==visualLimit; */
1242
5.80k
        }
1243
1244
845
        if(pBiDi->insertPoints.size>0) {
1245
0
            int32_t markFound=0, runCount=pBiDi->runCount;
1246
0
            int32_t insertRemove, i, j, k;
1247
0
            runs=pBiDi->runs;
1248
            /* count all inserted marks */
1249
0
            for(i=0; i<runCount; i++) {
1250
0
                insertRemove=runs[i].insertRemove;
1251
0
                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
1252
0
                    markFound++;
1253
0
                }
1254
0
                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
1255
0
                    markFound++;
1256
0
                }
1257
0
            }
1258
            /* move back indexes by number of preceding marks */
1259
0
            k=pBiDi->resultLength;
1260
0
            for(i=runCount-1; i>=0 && markFound>0; i--) {
1261
0
                insertRemove=runs[i].insertRemove;
1262
0
                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
1263
0
                    indexMap[--k]= UBIDI_MAP_NOWHERE;
1264
0
                    markFound--;
1265
0
                }
1266
0
                visualStart= i>0 ? runs[i-1].visualLimit : 0;
1267
0
                for(j=runs[i].visualLimit-1; j>=visualStart && markFound>0; j--) {
1268
0
                    indexMap[--k]=indexMap[j];
1269
0
                }
1270
0
                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
1271
0
                    indexMap[--k]= UBIDI_MAP_NOWHERE;
1272
0
                    markFound--;
1273
0
                }
1274
0
            }
1275
0
        }
1276
845
        else if(pBiDi->controlCount>0) {
1277
0
            int32_t runCount=pBiDi->runCount, logicalEnd;
1278
0
            int32_t insertRemove, length, i, j, k, m;
1279
0
            char16_t uchar;
1280
0
            UBool evenRun;
1281
0
            runs=pBiDi->runs;
1282
0
            visualStart=0;
1283
            /* move forward indexes by number of preceding controls */
1284
0
            k=0;
1285
0
            for(i=0; i<runCount; i++, visualStart+=length) {
1286
0
                length=runs[i].visualLimit-visualStart;
1287
0
                insertRemove=runs[i].insertRemove;
1288
                /* if no control found yet, nothing to do in this run */
1289
0
                if((insertRemove==0)&&(k==visualStart)) {
1290
0
                    k+=length;
1291
0
                    continue;
1292
0
                }
1293
                /* if no control in this run */
1294
0
                if(insertRemove==0) {
1295
0
                    visualLimit=runs[i].visualLimit;
1296
0
                    for(j=visualStart; j<visualLimit; j++) {
1297
0
                        indexMap[k++]=indexMap[j];
1298
0
                    }
1299
0
                    continue;
1300
0
                }
1301
0
                logicalStart=runs[i].logicalStart;
1302
0
                evenRun=IS_EVEN_RUN(logicalStart);
1303
0
                REMOVE_ODD_BIT(logicalStart);
1304
0
                logicalEnd=logicalStart+length-1;
1305
0
                for(j=0; j<length; j++) {
1306
0
                    m= evenRun ? logicalStart+j : logicalEnd-j;
1307
0
                    uchar=pBiDi->text[m];
1308
0
                    if(!IS_BIDI_CONTROL_CHAR(uchar)) {
1309
0
                        indexMap[k++]=m;
1310
0
                    }
1311
0
                }
1312
0
            }
1313
0
        }
1314
845
    }
1315
881
}
1316
1317
U_CAPI void U_EXPORT2
1318
0
ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length) {
1319
0
    if(srcMap!=nullptr && destMap!=nullptr && length>0) {
1320
0
        const int32_t *pi;
1321
0
        int32_t destLength=-1, count=0;
1322
        /* find highest value and count positive indexes in srcMap */
1323
0
        pi=srcMap+length;
1324
0
        while(pi>srcMap) {
1325
0
            if(*--pi>destLength) {
1326
0
                destLength=*pi;
1327
0
            }
1328
0
            if(*pi>=0) {
1329
0
                count++;
1330
0
            }
1331
0
        }
1332
0
        destLength++;           /* add 1 for origin 0 */
1333
0
        if(count<destLength) {
1334
            /* we must fill unmatched destMap entries with -1 */
1335
0
            uprv_memset(destMap, 0xFF, destLength*sizeof(int32_t));
1336
0
        }
1337
0
        pi=srcMap+length;
1338
0
        while(length>0) {
1339
0
            if(*--pi>=0) {
1340
0
                destMap[*pi]=--length;
1341
0
            } else {
1342
0
                --length;
1343
0
            }
1344
0
        }
1345
0
    }
1346
0
}