Coverage Report

Created: 2018-09-25 14:53

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