intl/icu/source/common/ubidiln.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/common/ubidiln.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1351 @@
     1.4 +/*
     1.5 +******************************************************************************
     1.6 +*
     1.7 +*   Copyright (C) 1999-2013, International Business Machines
     1.8 +*   Corporation and others.  All Rights Reserved.
     1.9 +*
    1.10 +******************************************************************************
    1.11 +*   file name:  ubidiln.c
    1.12 +*   encoding:   US-ASCII
    1.13 +*   tab size:   8 (not used)
    1.14 +*   indentation:4
    1.15 +*
    1.16 +*   created on: 1999aug06
    1.17 +*   created by: Markus W. Scherer, updated by Matitiahu Allouche
    1.18 +*/
    1.19 +
    1.20 +#include "cmemory.h"
    1.21 +#include "unicode/utypes.h"
    1.22 +#include "unicode/ustring.h"
    1.23 +#include "unicode/uchar.h"
    1.24 +#include "unicode/ubidi.h"
    1.25 +#include "ubidiimp.h"
    1.26 +#include "uassert.h"
    1.27 +
    1.28 +#ifndef U_COMMON_IMPLEMENTATION
    1.29 +#error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see http://userguide.icu-project.org/howtouseicu
    1.30 +#endif
    1.31 +
    1.32 +/*
    1.33 + * General remarks about the functions in this file:
    1.34 + *
    1.35 + * These functions deal with the aspects of potentially mixed-directional
    1.36 + * text in a single paragraph or in a line of a single paragraph
    1.37 + * which has already been processed according to
    1.38 + * the Unicode 6.3 BiDi algorithm as defined in
    1.39 + * http://www.unicode.org/unicode/reports/tr9/ , version 28,
    1.40 + * also described in The Unicode Standard, Version 6.3.0 .
    1.41 + *
    1.42 + * This means that there is a UBiDi object with a levels
    1.43 + * and a dirProps array.
    1.44 + * paraLevel and direction are also set.
    1.45 + * Only if the length of the text is zero, then levels==dirProps==NULL.
    1.46 + *
    1.47 + * The overall directionality of the paragraph
    1.48 + * or line is used to bypass the reordering steps if possible.
    1.49 + * Even purely RTL text does not need reordering there because
    1.50 + * the ubidi_getLogical/VisualIndex() functions can compute the
    1.51 + * index on the fly in such a case.
    1.52 + *
    1.53 + * The implementation of the access to same-level-runs and of the reordering
    1.54 + * do attempt to provide better performance and less memory usage compared to
    1.55 + * a direct implementation of especially rule (L2) with an array of
    1.56 + * one (32-bit) integer per text character.
    1.57 + *
    1.58 + * Here, the levels array is scanned as soon as necessary, and a vector of
    1.59 + * same-level-runs is created. Reordering then is done on this vector.
    1.60 + * For each run of text positions that were resolved to the same level,
    1.61 + * only 8 bytes are stored: the first text position of the run and the visual
    1.62 + * position behind the run after reordering.
    1.63 + * One sign bit is used to hold the directionality of the run.
    1.64 + * This is inefficient if there are many very short runs. If the average run
    1.65 + * length is <2, then this uses more memory.
    1.66 + *
    1.67 + * In a further attempt to save memory, the levels array is never changed
    1.68 + * after all the resolution rules (Xn, Wn, Nn, In).
    1.69 + * Many functions have to consider the field trailingWSStart:
    1.70 + * if it is less than length, then there is an implicit trailing run
    1.71 + * at the paraLevel,
    1.72 + * which is not reflected in the levels array.
    1.73 + * This allows a line UBiDi object to use the same levels array as
    1.74 + * its paragraph parent object.
    1.75 + *
    1.76 + * When a UBiDi object is created for a line of a paragraph, then the
    1.77 + * paragraph's levels and dirProps arrays are reused by way of setting
    1.78 + * a pointer into them, not by copying. This again saves memory and forbids to
    1.79 + * change the now shared levels for (L1).
    1.80 + */
    1.81 +
    1.82 +/* handle trailing WS (L1) -------------------------------------------------- */
    1.83 +
    1.84 +/*
    1.85 + * setTrailingWSStart() sets the start index for a trailing
    1.86 + * run of WS in the line. This is necessary because we do not modify
    1.87 + * the paragraph's levels array that we just point into.
    1.88 + * Using trailingWSStart is another form of performing (L1).
    1.89 + *
    1.90 + * To make subsequent operations easier, we also include the run
    1.91 + * before the WS if it is at the paraLevel - we merge the two here.
    1.92 + *
    1.93 + * This function is called only from ubidi_setLine(), so pBiDi->paraLevel is
    1.94 + * set correctly for the line even when contextual multiple paragraphs.
    1.95 + */
    1.96 +static void
    1.97 +setTrailingWSStart(UBiDi *pBiDi) {
    1.98 +    /* pBiDi->direction!=UBIDI_MIXED */
    1.99 +
   1.100 +    const DirProp *dirProps=pBiDi->dirProps;
   1.101 +    UBiDiLevel *levels=pBiDi->levels;
   1.102 +    int32_t start=pBiDi->length;
   1.103 +    UBiDiLevel paraLevel=pBiDi->paraLevel;
   1.104 +
   1.105 +    /* If the line is terminated by a block separator, all preceding WS etc...
   1.106 +       are already set to paragraph level.
   1.107 +       Setting trailingWSStart to pBidi->length will avoid changing the
   1.108 +       level of B chars from 0 to paraLevel in ubidi_getLevels when
   1.109 +       orderParagraphsLTR==TRUE.
   1.110 +     */
   1.111 +    if(dirProps[start-1]==B) {
   1.112 +        pBiDi->trailingWSStart=start;   /* currently == pBiDi->length */
   1.113 +        return;
   1.114 +    }
   1.115 +    /* go backwards across all WS, BN, explicit codes */
   1.116 +    while(start>0 && DIRPROP_FLAG(PURE_DIRPROP(dirProps[start-1]))&MASK_WS) {
   1.117 +        --start;
   1.118 +    }
   1.119 +
   1.120 +    /* if the WS run can be merged with the previous run then do so here */
   1.121 +    while(start>0 && levels[start-1]==paraLevel) {
   1.122 +        --start;
   1.123 +    }
   1.124 +
   1.125 +    pBiDi->trailingWSStart=start;
   1.126 +}
   1.127 +
   1.128 +/* ubidi_setLine ------------------------------------------------------------ */
   1.129 +
   1.130 +U_CAPI void U_EXPORT2
   1.131 +ubidi_setLine(const UBiDi *pParaBiDi,
   1.132 +              int32_t start, int32_t limit,
   1.133 +              UBiDi *pLineBiDi,
   1.134 +              UErrorCode *pErrorCode) {
   1.135 +    int32_t length;
   1.136 +
   1.137 +    /* check the argument values */
   1.138 +    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
   1.139 +    RETURN_VOID_IF_NOT_VALID_PARA(pParaBiDi, *pErrorCode);
   1.140 +    RETURN_VOID_IF_BAD_RANGE(start, 0, limit, *pErrorCode);
   1.141 +    RETURN_VOID_IF_BAD_RANGE(limit, 0, pParaBiDi->length+1, *pErrorCode);
   1.142 +    if(pLineBiDi==NULL) {
   1.143 +        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
   1.144 +        return;
   1.145 +    }
   1.146 +    if(ubidi_getParagraph(pParaBiDi, start, NULL, NULL, NULL, pErrorCode) !=
   1.147 +       ubidi_getParagraph(pParaBiDi, limit-1, NULL, NULL, NULL, pErrorCode)) {
   1.148 +        /* the line crosses a paragraph boundary */
   1.149 +        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
   1.150 +        return;
   1.151 +    }
   1.152 +
   1.153 +    /* set the values in pLineBiDi from its pParaBiDi parent */
   1.154 +    pLineBiDi->pParaBiDi=NULL;          /* mark unfinished setLine */
   1.155 +    pLineBiDi->text=pParaBiDi->text+start;
   1.156 +    length=pLineBiDi->length=limit-start;
   1.157 +    pLineBiDi->resultLength=pLineBiDi->originalLength=length;
   1.158 +    pLineBiDi->paraLevel=GET_PARALEVEL(pParaBiDi, start);
   1.159 +    pLineBiDi->paraCount=pParaBiDi->paraCount;
   1.160 +    pLineBiDi->runs=NULL;
   1.161 +    pLineBiDi->flags=0;
   1.162 +    pLineBiDi->reorderingMode=pParaBiDi->reorderingMode;
   1.163 +    pLineBiDi->reorderingOptions=pParaBiDi->reorderingOptions;
   1.164 +    pLineBiDi->controlCount=0;
   1.165 +    if(pParaBiDi->controlCount>0) {
   1.166 +        int32_t j;
   1.167 +        for(j=start; j<limit; j++) {
   1.168 +            if(IS_BIDI_CONTROL_CHAR(pParaBiDi->text[j])) {
   1.169 +                pLineBiDi->controlCount++;
   1.170 +            }
   1.171 +        }
   1.172 +        pLineBiDi->resultLength-=pLineBiDi->controlCount;
   1.173 +    }
   1.174 +
   1.175 +    pLineBiDi->dirProps=pParaBiDi->dirProps+start;
   1.176 +    pLineBiDi->levels=pParaBiDi->levels+start;
   1.177 +    pLineBiDi->runCount=-1;
   1.178 +
   1.179 +    if(pParaBiDi->direction!=UBIDI_MIXED) {
   1.180 +        /* the parent is already trivial */
   1.181 +        pLineBiDi->direction=pParaBiDi->direction;
   1.182 +
   1.183 +        /*
   1.184 +         * The parent's levels are all either
   1.185 +         * implicitly or explicitly ==paraLevel;
   1.186 +         * do the same here.
   1.187 +         */
   1.188 +        if(pParaBiDi->trailingWSStart<=start) {
   1.189 +            pLineBiDi->trailingWSStart=0;
   1.190 +        } else if(pParaBiDi->trailingWSStart<limit) {
   1.191 +            pLineBiDi->trailingWSStart=pParaBiDi->trailingWSStart-start;
   1.192 +        } else {
   1.193 +            pLineBiDi->trailingWSStart=length;
   1.194 +        }
   1.195 +    } else {
   1.196 +        const UBiDiLevel *levels=pLineBiDi->levels;
   1.197 +        int32_t i, trailingWSStart;
   1.198 +        UBiDiLevel level;
   1.199 +
   1.200 +        setTrailingWSStart(pLineBiDi);
   1.201 +        trailingWSStart=pLineBiDi->trailingWSStart;
   1.202 +
   1.203 +        /* recalculate pLineBiDi->direction */
   1.204 +        if(trailingWSStart==0) {
   1.205 +            /* all levels are at paraLevel */
   1.206 +            pLineBiDi->direction=(UBiDiDirection)(pLineBiDi->paraLevel&1);
   1.207 +        } else {
   1.208 +            /* get the level of the first character */
   1.209 +            level=(UBiDiLevel)(levels[0]&1);
   1.210 +
   1.211 +            /* if there is anything of a different level, then the line is mixed */
   1.212 +            if(trailingWSStart<length && (pLineBiDi->paraLevel&1)!=level) {
   1.213 +                /* the trailing WS is at paraLevel, which differs from levels[0] */
   1.214 +                pLineBiDi->direction=UBIDI_MIXED;
   1.215 +            } else {
   1.216 +                /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */
   1.217 +                i=1;
   1.218 +                for(;;) {
   1.219 +                    if(i==trailingWSStart) {
   1.220 +                        /* the direction values match those in level */
   1.221 +                        pLineBiDi->direction=(UBiDiDirection)level;
   1.222 +                        break;
   1.223 +                    } else if((levels[i]&1)!=level) {
   1.224 +                        pLineBiDi->direction=UBIDI_MIXED;
   1.225 +                        break;
   1.226 +                    }
   1.227 +                    ++i;
   1.228 +                }
   1.229 +            }
   1.230 +        }
   1.231 +
   1.232 +        switch(pLineBiDi->direction) {
   1.233 +        case UBIDI_LTR:
   1.234 +            /* make sure paraLevel is even */
   1.235 +            pLineBiDi->paraLevel=(UBiDiLevel)((pLineBiDi->paraLevel+1)&~1);
   1.236 +
   1.237 +            /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */
   1.238 +            pLineBiDi->trailingWSStart=0;
   1.239 +            break;
   1.240 +        case UBIDI_RTL:
   1.241 +            /* make sure paraLevel is odd */
   1.242 +            pLineBiDi->paraLevel|=1;
   1.243 +
   1.244 +            /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */
   1.245 +            pLineBiDi->trailingWSStart=0;
   1.246 +            break;
   1.247 +        default:
   1.248 +            break;
   1.249 +        }
   1.250 +    }
   1.251 +    pLineBiDi->pParaBiDi=pParaBiDi;     /* mark successful setLine */
   1.252 +    return;
   1.253 +}
   1.254 +
   1.255 +U_CAPI UBiDiLevel U_EXPORT2
   1.256 +ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex) {
   1.257 +    /* return paraLevel if in the trailing WS run, otherwise the real level */
   1.258 +    if(!IS_VALID_PARA_OR_LINE(pBiDi) || charIndex<0 || pBiDi->length<=charIndex) {
   1.259 +        return 0;
   1.260 +    } else if(pBiDi->direction!=UBIDI_MIXED || charIndex>=pBiDi->trailingWSStart) {
   1.261 +        return GET_PARALEVEL(pBiDi, charIndex);
   1.262 +    } else {
   1.263 +        return pBiDi->levels[charIndex];
   1.264 +    }
   1.265 +}
   1.266 +
   1.267 +U_CAPI const UBiDiLevel * U_EXPORT2
   1.268 +ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) {
   1.269 +    int32_t start, length;
   1.270 +
   1.271 +    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, NULL);
   1.272 +    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, NULL);
   1.273 +    if((length=pBiDi->length)<=0) {
   1.274 +        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
   1.275 +        return NULL;
   1.276 +    }
   1.277 +    if((start=pBiDi->trailingWSStart)==length) {
   1.278 +        /* the current levels array reflects the WS run */
   1.279 +        return pBiDi->levels;
   1.280 +    }
   1.281 +
   1.282 +    /*
   1.283 +     * After the previous if(), we know that the levels array
   1.284 +     * has an implicit trailing WS run and therefore does not fully
   1.285 +     * reflect itself all the levels.
   1.286 +     * This must be a UBiDi object for a line, and
   1.287 +     * we need to create a new levels array.
   1.288 +     */
   1.289 +    if(getLevelsMemory(pBiDi, length)) {
   1.290 +        UBiDiLevel *levels=pBiDi->levelsMemory;
   1.291 +
   1.292 +        if(start>0 && levels!=pBiDi->levels) {
   1.293 +            uprv_memcpy(levels, pBiDi->levels, start);
   1.294 +        }
   1.295 +        /* pBiDi->paraLevel is ok even if contextual multiple paragraphs,
   1.296 +           since pBidi is a line object                                     */
   1.297 +        uprv_memset(levels+start, pBiDi->paraLevel, length-start);
   1.298 +
   1.299 +        /* this new levels array is set for the line and reflects the WS run */
   1.300 +        pBiDi->trailingWSStart=length;
   1.301 +        return pBiDi->levels=levels;
   1.302 +    } else {
   1.303 +        /* out of memory */
   1.304 +        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
   1.305 +        return NULL;
   1.306 +    }
   1.307 +}
   1.308 +
   1.309 +U_CAPI void U_EXPORT2
   1.310 +ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition,
   1.311 +                    int32_t *pLogicalLimit, UBiDiLevel *pLevel) {
   1.312 +    UErrorCode errorCode;
   1.313 +    int32_t runCount, visualStart, logicalLimit, logicalFirst, i;
   1.314 +    Run iRun;
   1.315 +
   1.316 +    errorCode=U_ZERO_ERROR;
   1.317 +    RETURN_VOID_IF_BAD_RANGE(logicalPosition, 0, pBiDi->length, errorCode);
   1.318 +    /* ubidi_countRuns will check VALID_PARA_OR_LINE */
   1.319 +    runCount=ubidi_countRuns((UBiDi *)pBiDi, &errorCode);
   1.320 +    if(U_FAILURE(errorCode)) {
   1.321 +        return;
   1.322 +    }
   1.323 +    /* this is done based on runs rather than on levels since levels have
   1.324 +       a special interpretation when UBIDI_REORDER_RUNS_ONLY
   1.325 +     */
   1.326 +    visualStart=logicalLimit=0;
   1.327 +    iRun=pBiDi->runs[0];
   1.328 +
   1.329 +    for(i=0; i<runCount; i++) {
   1.330 +        iRun = pBiDi->runs[i];
   1.331 +        logicalFirst=GET_INDEX(iRun.logicalStart);
   1.332 +        logicalLimit=logicalFirst+iRun.visualLimit-visualStart;
   1.333 +        if((logicalPosition>=logicalFirst) &&
   1.334 +           (logicalPosition<logicalLimit)) {
   1.335 +            break;
   1.336 +        }
   1.337 +        visualStart = iRun.visualLimit;
   1.338 +    }
   1.339 +    if(pLogicalLimit) {
   1.340 +        *pLogicalLimit=logicalLimit;
   1.341 +    }
   1.342 +    if(pLevel) {
   1.343 +        if(pBiDi->reorderingMode==UBIDI_REORDER_RUNS_ONLY) {
   1.344 +            *pLevel=(UBiDiLevel)GET_ODD_BIT(iRun.logicalStart);
   1.345 +        }
   1.346 +        else if(pBiDi->direction!=UBIDI_MIXED || logicalPosition>=pBiDi->trailingWSStart) {
   1.347 +            *pLevel=GET_PARALEVEL(pBiDi, logicalPosition);
   1.348 +        } else {
   1.349 +        *pLevel=pBiDi->levels[logicalPosition];
   1.350 +        }
   1.351 +    }
   1.352 +}
   1.353 +
   1.354 +/* runs API functions ------------------------------------------------------- */
   1.355 +
   1.356 +U_CAPI int32_t U_EXPORT2
   1.357 +ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) {
   1.358 +    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
   1.359 +    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
   1.360 +    ubidi_getRuns(pBiDi, pErrorCode);
   1.361 +    if(U_FAILURE(*pErrorCode)) {
   1.362 +        return -1;
   1.363 +    }
   1.364 +    return pBiDi->runCount;
   1.365 +}
   1.366 +
   1.367 +U_CAPI UBiDiDirection U_EXPORT2
   1.368 +ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex,
   1.369 +                   int32_t *pLogicalStart, int32_t *pLength)
   1.370 +{
   1.371 +    int32_t start;
   1.372 +    UErrorCode errorCode = U_ZERO_ERROR;
   1.373 +    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, errorCode, UBIDI_LTR);
   1.374 +    ubidi_getRuns(pBiDi, &errorCode);
   1.375 +    if(U_FAILURE(errorCode)) {
   1.376 +        return UBIDI_LTR;
   1.377 +    }
   1.378 +    RETURN_IF_BAD_RANGE(runIndex, 0, pBiDi->runCount, errorCode, UBIDI_LTR);
   1.379 +
   1.380 +    start=pBiDi->runs[runIndex].logicalStart;
   1.381 +    if(pLogicalStart!=NULL) {
   1.382 +        *pLogicalStart=GET_INDEX(start);
   1.383 +    }
   1.384 +    if(pLength!=NULL) {
   1.385 +        if(runIndex>0) {
   1.386 +            *pLength=pBiDi->runs[runIndex].visualLimit-
   1.387 +                     pBiDi->runs[runIndex-1].visualLimit;
   1.388 +        } else {
   1.389 +            *pLength=pBiDi->runs[0].visualLimit;
   1.390 +        }
   1.391 +    }
   1.392 +    return (UBiDiDirection)GET_ODD_BIT(start);
   1.393 +}
   1.394 +
   1.395 +/* in trivial cases there is only one trivial run; called by ubidi_getRuns() */
   1.396 +static void
   1.397 +getSingleRun(UBiDi *pBiDi, UBiDiLevel level) {
   1.398 +    /* simple, single-run case */
   1.399 +    pBiDi->runs=pBiDi->simpleRuns;
   1.400 +    pBiDi->runCount=1;
   1.401 +
   1.402 +    /* fill and reorder the single run */
   1.403 +    pBiDi->runs[0].logicalStart=MAKE_INDEX_ODD_PAIR(0, level);
   1.404 +    pBiDi->runs[0].visualLimit=pBiDi->length;
   1.405 +    pBiDi->runs[0].insertRemove=0;
   1.406 +}
   1.407 +
   1.408 +/* reorder the runs array (L2) ---------------------------------------------- */
   1.409 +
   1.410 +/*
   1.411 + * Reorder the same-level runs in the runs array.
   1.412 + * Here, runCount>1 and maxLevel>=minLevel>=paraLevel.
   1.413 + * All the visualStart fields=logical start before reordering.
   1.414 + * The "odd" bits are not set yet.
   1.415 + *
   1.416 + * Reordering with this data structure lends itself to some handy shortcuts:
   1.417 + *
   1.418 + * Since each run is moved but not modified, and since at the initial maxLevel
   1.419 + * each sequence of same-level runs consists of only one run each, we
   1.420 + * don't need to do anything there and can predecrement maxLevel.
   1.421 + * In many simple cases, the reordering is thus done entirely in the
   1.422 + * index mapping.
   1.423 + * Also, reordering occurs only down to the lowest odd level that occurs,
   1.424 + * which is minLevel|1. However, if the lowest level itself is odd, then
   1.425 + * in the last reordering the sequence of the runs at this level or higher
   1.426 + * will be all runs, and we don't need the elaborate loop to search for them.
   1.427 + * This is covered by ++minLevel instead of minLevel|=1 followed
   1.428 + * by an extra reorder-all after the reorder-some loop.
   1.429 + * About a trailing WS run:
   1.430 + * Such a run would need special treatment because its level is not
   1.431 + * reflected in levels[] if this is not a paragraph object.
   1.432 + * Instead, all characters from trailingWSStart on are implicitly at
   1.433 + * paraLevel.
   1.434 + * However, for all maxLevel>paraLevel, this run will never be reordered
   1.435 + * and does not need to be taken into account. maxLevel==paraLevel is only reordered
   1.436 + * if minLevel==paraLevel is odd, which is done in the extra segment.
   1.437 + * This means that for the main reordering loop we don't need to consider
   1.438 + * this run and can --runCount. If it is later part of the all-runs
   1.439 + * reordering, then runCount is adjusted accordingly.
   1.440 + */
   1.441 +static void
   1.442 +reorderLine(UBiDi *pBiDi, UBiDiLevel minLevel, UBiDiLevel maxLevel) {
   1.443 +    Run *runs, tempRun;
   1.444 +    UBiDiLevel *levels;
   1.445 +    int32_t firstRun, endRun, limitRun, runCount;
   1.446 +
   1.447 +    /* nothing to do? */
   1.448 +    if(maxLevel<=(minLevel|1)) {
   1.449 +        return;
   1.450 +    }
   1.451 +
   1.452 +    /*
   1.453 +     * Reorder only down to the lowest odd level
   1.454 +     * and reorder at an odd minLevel in a separate, simpler loop.
   1.455 +     * See comments above for why minLevel is always incremented.
   1.456 +     */
   1.457 +    ++minLevel;
   1.458 +
   1.459 +    runs=pBiDi->runs;
   1.460 +    levels=pBiDi->levels;
   1.461 +    runCount=pBiDi->runCount;
   1.462 +
   1.463 +    /* do not include the WS run at paraLevel<=old minLevel except in the simple loop */
   1.464 +    if(pBiDi->trailingWSStart<pBiDi->length) {
   1.465 +        --runCount;
   1.466 +    }
   1.467 +
   1.468 +    while(--maxLevel>=minLevel) {
   1.469 +        firstRun=0;
   1.470 +
   1.471 +        /* loop for all sequences of runs */
   1.472 +        for(;;) {
   1.473 +            /* look for a sequence of runs that are all at >=maxLevel */
   1.474 +            /* look for the first run of such a sequence */
   1.475 +            while(firstRun<runCount && levels[runs[firstRun].logicalStart]<maxLevel) {
   1.476 +                ++firstRun;
   1.477 +            }
   1.478 +            if(firstRun>=runCount) {
   1.479 +                break;  /* no more such runs */
   1.480 +            }
   1.481 +
   1.482 +            /* look for the limit run of such a sequence (the run behind it) */
   1.483 +            for(limitRun=firstRun; ++limitRun<runCount && levels[runs[limitRun].logicalStart]>=maxLevel;) {}
   1.484 +
   1.485 +            /* Swap the entire sequence of runs from firstRun to limitRun-1. */
   1.486 +            endRun=limitRun-1;
   1.487 +            while(firstRun<endRun) {
   1.488 +                tempRun = runs[firstRun];
   1.489 +                runs[firstRun]=runs[endRun];
   1.490 +                runs[endRun]=tempRun;
   1.491 +                ++firstRun;
   1.492 +                --endRun;
   1.493 +            }
   1.494 +
   1.495 +            if(limitRun==runCount) {
   1.496 +                break;  /* no more such runs */
   1.497 +            } else {
   1.498 +                firstRun=limitRun+1;
   1.499 +            }
   1.500 +        }
   1.501 +    }
   1.502 +
   1.503 +    /* now do maxLevel==old minLevel (==odd!), see above */
   1.504 +    if(!(minLevel&1)) {
   1.505 +        firstRun=0;
   1.506 +
   1.507 +        /* include the trailing WS run in this complete reordering */
   1.508 +        if(pBiDi->trailingWSStart==pBiDi->length) {
   1.509 +            --runCount;
   1.510 +        }
   1.511 +
   1.512 +        /* Swap the entire sequence of all runs. (endRun==runCount) */
   1.513 +        while(firstRun<runCount) {
   1.514 +            tempRun=runs[firstRun];
   1.515 +            runs[firstRun]=runs[runCount];
   1.516 +            runs[runCount]=tempRun;
   1.517 +            ++firstRun;
   1.518 +            --runCount;
   1.519 +        }
   1.520 +    }
   1.521 +}
   1.522 +
   1.523 +/* compute the runs array --------------------------------------------------- */
   1.524 +
   1.525 +static int32_t getRunFromLogicalIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) {
   1.526 +    Run *runs=pBiDi->runs;
   1.527 +    int32_t runCount=pBiDi->runCount, visualStart=0, i, length, logicalStart;
   1.528 +
   1.529 +    for(i=0; i<runCount; i++) {
   1.530 +        length=runs[i].visualLimit-visualStart;
   1.531 +        logicalStart=GET_INDEX(runs[i].logicalStart);
   1.532 +        if((logicalIndex>=logicalStart) && (logicalIndex<(logicalStart+length))) {
   1.533 +            return i;
   1.534 +        }
   1.535 +        visualStart+=length;
   1.536 +    }
   1.537 +    /* we should never get here */
   1.538 +    U_ASSERT(FALSE);
   1.539 +    *pErrorCode = U_INVALID_STATE_ERROR;
   1.540 +    return 0;
   1.541 +}
   1.542 +
   1.543 +/*
   1.544 + * Compute the runs array from the levels array.
   1.545 + * After ubidi_getRuns() returns TRUE, runCount is guaranteed to be >0
   1.546 + * and the runs are reordered.
   1.547 + * Odd-level runs have visualStart on their visual right edge and
   1.548 + * they progress visually to the left.
   1.549 + * If option UBIDI_OPTION_INSERT_MARKS is set, insertRemove will contain the
   1.550 + * sum of appropriate LRM/RLM_BEFORE/AFTER flags.
   1.551 + * If option UBIDI_OPTION_REMOVE_CONTROLS is set, insertRemove will contain the
   1.552 + * negative number of BiDi control characters within this run.
   1.553 + */
   1.554 +U_CFUNC UBool
   1.555 +ubidi_getRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) {
   1.556 +    /*
   1.557 +     * This method returns immediately if the runs are already set. This
   1.558 +     * includes the case of length==0 (handled in setPara)..
   1.559 +     */
   1.560 +    if (pBiDi->runCount>=0) {
   1.561 +        return TRUE;
   1.562 +    }
   1.563 +
   1.564 +    if(pBiDi->direction!=UBIDI_MIXED) {
   1.565 +        /* simple, single-run case - this covers length==0 */
   1.566 +        /* pBiDi->paraLevel is ok even for contextual multiple paragraphs */
   1.567 +        getSingleRun(pBiDi, pBiDi->paraLevel);
   1.568 +    } else /* UBIDI_MIXED, length>0 */ {
   1.569 +        /* mixed directionality */
   1.570 +        int32_t length=pBiDi->length, limit;
   1.571 +        UBiDiLevel *levels=pBiDi->levels;
   1.572 +        int32_t i, runCount;
   1.573 +        UBiDiLevel level=UBIDI_DEFAULT_LTR;   /* initialize with no valid level */
   1.574 +        /*
   1.575 +         * If there are WS characters at the end of the line
   1.576 +         * and the run preceding them has a level different from
   1.577 +         * paraLevel, then they will form their own run at paraLevel (L1).
   1.578 +         * Count them separately.
   1.579 +         * We need some special treatment for this in order to not
   1.580 +         * modify the levels array which a line UBiDi object shares
   1.581 +         * with its paragraph parent and its other line siblings.
   1.582 +         * In other words, for the trailing WS, it may be
   1.583 +         * levels[]!=paraLevel but we have to treat it like it were so.
   1.584 +         */
   1.585 +        limit=pBiDi->trailingWSStart;
   1.586 +        /* count the runs, there is at least one non-WS run, and limit>0 */
   1.587 +        runCount=0;
   1.588 +        for(i=0; i<limit; ++i) {
   1.589 +            /* increment runCount at the start of each run */
   1.590 +            if(levels[i]!=level) {
   1.591 +                ++runCount;
   1.592 +                level=levels[i];
   1.593 +            }
   1.594 +        }
   1.595 +
   1.596 +        /*
   1.597 +         * We don't need to see if the last run can be merged with a trailing
   1.598 +         * WS run because setTrailingWSStart() would have done that.
   1.599 +         */
   1.600 +        if(runCount==1 && limit==length) {
   1.601 +            /* There is only one non-WS run and no trailing WS-run. */
   1.602 +            getSingleRun(pBiDi, levels[0]);
   1.603 +        } else /* runCount>1 || limit<length */ {
   1.604 +            /* allocate and set the runs */
   1.605 +            Run *runs;
   1.606 +            int32_t runIndex, start;
   1.607 +            UBiDiLevel minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1, maxLevel=0;
   1.608 +
   1.609 +            /* now, count a (non-mergeable) WS run */
   1.610 +            if(limit<length) {
   1.611 +                ++runCount;
   1.612 +            }
   1.613 +
   1.614 +            /* runCount>1 */
   1.615 +            if(getRunsMemory(pBiDi, runCount)) {
   1.616 +                runs=pBiDi->runsMemory;
   1.617 +            } else {
   1.618 +                return FALSE;
   1.619 +            }
   1.620 +
   1.621 +            /* set the runs */
   1.622 +            /* FOOD FOR THOUGHT: this could be optimized, e.g.:
   1.623 +             * 464->444, 484->444, 575->555, 595->555
   1.624 +             * However, that would take longer. Check also how it would
   1.625 +             * interact with BiDi control removal and inserting Marks.
   1.626 +             */
   1.627 +            runIndex=0;
   1.628 +
   1.629 +            /* search for the run limits and initialize visualLimit values with the run lengths */
   1.630 +            i=0;
   1.631 +            do {
   1.632 +                /* prepare this run */
   1.633 +                start=i;
   1.634 +                level=levels[i];
   1.635 +                if(level<minLevel) {
   1.636 +                    minLevel=level;
   1.637 +                }
   1.638 +                if(level>maxLevel) {
   1.639 +                    maxLevel=level;
   1.640 +                }
   1.641 +
   1.642 +                /* look for the run limit */
   1.643 +                while(++i<limit && levels[i]==level) {}
   1.644 +
   1.645 +                /* i is another run limit */
   1.646 +                runs[runIndex].logicalStart=start;
   1.647 +                runs[runIndex].visualLimit=i-start;
   1.648 +                runs[runIndex].insertRemove=0;
   1.649 +                ++runIndex;
   1.650 +            } while(i<limit);
   1.651 +
   1.652 +            if(limit<length) {
   1.653 +                /* there is a separate WS run */
   1.654 +                runs[runIndex].logicalStart=limit;
   1.655 +                runs[runIndex].visualLimit=length-limit;
   1.656 +                /* For the trailing WS run, pBiDi->paraLevel is ok even
   1.657 +                   if contextual multiple paragraphs.                   */
   1.658 +                if(pBiDi->paraLevel<minLevel) {
   1.659 +                    minLevel=pBiDi->paraLevel;
   1.660 +                }
   1.661 +            }
   1.662 +
   1.663 +            /* set the object fields */
   1.664 +            pBiDi->runs=runs;
   1.665 +            pBiDi->runCount=runCount;
   1.666 +
   1.667 +            reorderLine(pBiDi, minLevel, maxLevel);
   1.668 +
   1.669 +            /* now add the direction flags and adjust the visualLimit's to be just that */
   1.670 +            /* this loop will also handle the trailing WS run */
   1.671 +            limit=0;
   1.672 +            for(i=0; i<runCount; ++i) {
   1.673 +                ADD_ODD_BIT_FROM_LEVEL(runs[i].logicalStart, levels[runs[i].logicalStart]);
   1.674 +                limit+=runs[i].visualLimit;
   1.675 +                runs[i].visualLimit=limit;
   1.676 +            }
   1.677 +
   1.678 +            /* Set the "odd" bit for the trailing WS run. */
   1.679 +            /* For a RTL paragraph, it will be the *first* run in visual order. */
   1.680 +            /* For the trailing WS run, pBiDi->paraLevel is ok even if
   1.681 +               contextual multiple paragraphs.                          */
   1.682 +            if(runIndex<runCount) {
   1.683 +                int32_t trailingRun = ((pBiDi->paraLevel & 1) != 0)? 0 : runIndex;
   1.684 +
   1.685 +                ADD_ODD_BIT_FROM_LEVEL(runs[trailingRun].logicalStart, pBiDi->paraLevel);
   1.686 +            }
   1.687 +        }
   1.688 +    }
   1.689 +
   1.690 +    /* handle insert LRM/RLM BEFORE/AFTER run */
   1.691 +    if(pBiDi->insertPoints.size>0) {
   1.692 +        Point *point, *start=pBiDi->insertPoints.points,
   1.693 +                      *limit=start+pBiDi->insertPoints.size;
   1.694 +        int32_t runIndex;
   1.695 +        for(point=start; point<limit; point++) {
   1.696 +            runIndex=getRunFromLogicalIndex(pBiDi, point->pos, pErrorCode);
   1.697 +            pBiDi->runs[runIndex].insertRemove|=point->flag;
   1.698 +        }
   1.699 +    }
   1.700 +
   1.701 +    /* handle remove BiDi control characters */
   1.702 +    if(pBiDi->controlCount>0) {
   1.703 +        int32_t runIndex;
   1.704 +        const UChar *start=pBiDi->text, *limit=start+pBiDi->length, *pu;
   1.705 +        for(pu=start; pu<limit; pu++) {
   1.706 +            if(IS_BIDI_CONTROL_CHAR(*pu)) {
   1.707 +                runIndex=getRunFromLogicalIndex(pBiDi, (int32_t)(pu-start), pErrorCode);
   1.708 +                pBiDi->runs[runIndex].insertRemove--;
   1.709 +            }
   1.710 +        }
   1.711 +    }
   1.712 +
   1.713 +    return TRUE;
   1.714 +}
   1.715 +
   1.716 +static UBool
   1.717 +prepareReorder(const UBiDiLevel *levels, int32_t length,
   1.718 +               int32_t *indexMap,
   1.719 +               UBiDiLevel *pMinLevel, UBiDiLevel *pMaxLevel) {
   1.720 +    int32_t start;
   1.721 +    UBiDiLevel level, minLevel, maxLevel;
   1.722 +
   1.723 +    if(levels==NULL || length<=0) {
   1.724 +        return FALSE;
   1.725 +    }
   1.726 +
   1.727 +    /* determine minLevel and maxLevel */
   1.728 +    minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1;
   1.729 +    maxLevel=0;
   1.730 +    for(start=length; start>0;) {
   1.731 +        level=levels[--start];
   1.732 +        if(level>UBIDI_MAX_EXPLICIT_LEVEL+1) {
   1.733 +            return FALSE;
   1.734 +        }
   1.735 +        if(level<minLevel) {
   1.736 +            minLevel=level;
   1.737 +        }
   1.738 +        if(level>maxLevel) {
   1.739 +            maxLevel=level;
   1.740 +        }
   1.741 +    }
   1.742 +    *pMinLevel=minLevel;
   1.743 +    *pMaxLevel=maxLevel;
   1.744 +
   1.745 +    /* initialize the index map */
   1.746 +    for(start=length; start>0;) {
   1.747 +        --start;
   1.748 +        indexMap[start]=start;
   1.749 +    }
   1.750 +
   1.751 +    return TRUE;
   1.752 +}
   1.753 +
   1.754 +/* reorder a line based on a levels array (L2) ------------------------------ */
   1.755 +
   1.756 +U_CAPI void U_EXPORT2
   1.757 +ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) {
   1.758 +    int32_t start, limit, sumOfSosEos;
   1.759 +    UBiDiLevel minLevel = 0, maxLevel = 0;
   1.760 +
   1.761 +    if(indexMap==NULL || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) {
   1.762 +        return;
   1.763 +    }
   1.764 +
   1.765 +    /* nothing to do? */
   1.766 +    if(minLevel==maxLevel && (minLevel&1)==0) {
   1.767 +        return;
   1.768 +    }
   1.769 +
   1.770 +    /* reorder only down to the lowest odd level */
   1.771 +    minLevel|=1;
   1.772 +
   1.773 +    /* loop maxLevel..minLevel */
   1.774 +    do {
   1.775 +        start=0;
   1.776 +
   1.777 +        /* loop for all sequences of levels to reorder at the current maxLevel */
   1.778 +        for(;;) {
   1.779 +            /* look for a sequence of levels that are all at >=maxLevel */
   1.780 +            /* look for the first index of such a sequence */
   1.781 +            while(start<length && levels[start]<maxLevel) {
   1.782 +                ++start;
   1.783 +            }
   1.784 +            if(start>=length) {
   1.785 +                break;  /* no more such sequences */
   1.786 +            }
   1.787 +
   1.788 +            /* look for the limit of such a sequence (the index behind it) */
   1.789 +            for(limit=start; ++limit<length && levels[limit]>=maxLevel;) {}
   1.790 +
   1.791 +            /*
   1.792 +             * sos=start of sequence, eos=end of sequence
   1.793 +             *
   1.794 +             * The closed (inclusive) interval from sos to eos includes all the logical
   1.795 +             * and visual indexes within this sequence. They are logically and
   1.796 +             * visually contiguous and in the same range.
   1.797 +             *
   1.798 +             * For each run, the new visual index=sos+eos-old visual index;
   1.799 +             * we pre-add sos+eos into sumOfSosEos ->
   1.800 +             * new visual index=sumOfSosEos-old visual index;
   1.801 +             */
   1.802 +            sumOfSosEos=start+limit-1;
   1.803 +
   1.804 +            /* reorder each index in the sequence */
   1.805 +            do {
   1.806 +                indexMap[start]=sumOfSosEos-indexMap[start];
   1.807 +            } while(++start<limit);
   1.808 +
   1.809 +            /* start==limit */
   1.810 +            if(limit==length) {
   1.811 +                break;  /* no more such sequences */
   1.812 +            } else {
   1.813 +                start=limit+1;
   1.814 +            }
   1.815 +        }
   1.816 +    } while(--maxLevel>=minLevel);
   1.817 +}
   1.818 +
   1.819 +U_CAPI void U_EXPORT2
   1.820 +ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) {
   1.821 +    int32_t start, end, limit, temp;
   1.822 +    UBiDiLevel minLevel = 0, maxLevel = 0;
   1.823 +
   1.824 +    if(indexMap==NULL || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) {
   1.825 +        return;
   1.826 +    }
   1.827 +
   1.828 +    /* nothing to do? */
   1.829 +    if(minLevel==maxLevel && (minLevel&1)==0) {
   1.830 +        return;
   1.831 +    }
   1.832 +
   1.833 +    /* reorder only down to the lowest odd level */
   1.834 +    minLevel|=1;
   1.835 +
   1.836 +    /* loop maxLevel..minLevel */
   1.837 +    do {
   1.838 +        start=0;
   1.839 +
   1.840 +        /* loop for all sequences of levels to reorder at the current maxLevel */
   1.841 +        for(;;) {
   1.842 +            /* look for a sequence of levels that are all at >=maxLevel */
   1.843 +            /* look for the first index of such a sequence */
   1.844 +            while(start<length && levels[start]<maxLevel) {
   1.845 +                ++start;
   1.846 +            }
   1.847 +            if(start>=length) {
   1.848 +                break;  /* no more such runs */
   1.849 +            }
   1.850 +
   1.851 +            /* look for the limit of such a sequence (the index behind it) */
   1.852 +            for(limit=start; ++limit<length && levels[limit]>=maxLevel;) {}
   1.853 +
   1.854 +            /*
   1.855 +             * Swap the entire interval of indexes from start to limit-1.
   1.856 +             * We don't need to swap the levels for the purpose of this
   1.857 +             * algorithm: the sequence of levels that we look at does not
   1.858 +             * move anyway.
   1.859 +             */
   1.860 +            end=limit-1;
   1.861 +            while(start<end) {
   1.862 +                temp=indexMap[start];
   1.863 +                indexMap[start]=indexMap[end];
   1.864 +                indexMap[end]=temp;
   1.865 +
   1.866 +                ++start;
   1.867 +                --end;
   1.868 +            }
   1.869 +
   1.870 +            if(limit==length) {
   1.871 +                break;  /* no more such sequences */
   1.872 +            } else {
   1.873 +                start=limit+1;
   1.874 +            }
   1.875 +        }
   1.876 +    } while(--maxLevel>=minLevel);
   1.877 +}
   1.878 +
   1.879 +/* API functions for logical<->visual mapping ------------------------------- */
   1.880 +
   1.881 +U_CAPI int32_t U_EXPORT2
   1.882 +ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) {
   1.883 +    int32_t visualIndex=UBIDI_MAP_NOWHERE;
   1.884 +    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
   1.885 +    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
   1.886 +    RETURN_IF_BAD_RANGE(logicalIndex, 0, pBiDi->length, *pErrorCode, -1);
   1.887 +
   1.888 +    /* we can do the trivial cases without the runs array */
   1.889 +    switch(pBiDi->direction) {
   1.890 +    case UBIDI_LTR:
   1.891 +        visualIndex=logicalIndex;
   1.892 +        break;
   1.893 +    case UBIDI_RTL:
   1.894 +        visualIndex=pBiDi->length-logicalIndex-1;
   1.895 +        break;
   1.896 +    default:
   1.897 +        if(!ubidi_getRuns(pBiDi, pErrorCode)) {
   1.898 +            *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
   1.899 +            return -1;
   1.900 +        } else {
   1.901 +            Run *runs=pBiDi->runs;
   1.902 +            int32_t i, visualStart=0, offset, length;
   1.903 +
   1.904 +            /* linear search for the run, search on the visual runs */
   1.905 +            for(i=0; i<pBiDi->runCount; ++i) {
   1.906 +                length=runs[i].visualLimit-visualStart;
   1.907 +                offset=logicalIndex-GET_INDEX(runs[i].logicalStart);
   1.908 +                if(offset>=0 && offset<length) {
   1.909 +                    if(IS_EVEN_RUN(runs[i].logicalStart)) {
   1.910 +                        /* LTR */
   1.911 +                        visualIndex=visualStart+offset;
   1.912 +                    } else {
   1.913 +                        /* RTL */
   1.914 +                        visualIndex=visualStart+length-offset-1;
   1.915 +                    }
   1.916 +                    break;          /* exit for loop */
   1.917 +                }
   1.918 +                visualStart+=length;
   1.919 +            }
   1.920 +            if(i>=pBiDi->runCount) {
   1.921 +                return UBIDI_MAP_NOWHERE;
   1.922 +            }
   1.923 +        }
   1.924 +    }
   1.925 +
   1.926 +    if(pBiDi->insertPoints.size>0) {
   1.927 +        /* add the number of added marks until the calculated visual index */
   1.928 +        Run *runs=pBiDi->runs;
   1.929 +        int32_t i, length, insertRemove;
   1.930 +        int32_t visualStart=0, markFound=0;
   1.931 +        for(i=0; ; i++, visualStart+=length) {
   1.932 +            length=runs[i].visualLimit-visualStart;
   1.933 +            insertRemove=runs[i].insertRemove;
   1.934 +            if(insertRemove & (LRM_BEFORE|RLM_BEFORE)) {
   1.935 +                markFound++;
   1.936 +            }
   1.937 +            /* is it the run containing the visual index? */
   1.938 +            if(visualIndex<runs[i].visualLimit) {
   1.939 +                return visualIndex+markFound;
   1.940 +            }
   1.941 +            if(insertRemove & (LRM_AFTER|RLM_AFTER)) {
   1.942 +                markFound++;
   1.943 +            }
   1.944 +        }
   1.945 +    }
   1.946 +    else if(pBiDi->controlCount>0) {
   1.947 +        /* subtract the number of controls until the calculated visual index */
   1.948 +        Run *runs=pBiDi->runs;
   1.949 +        int32_t i, j, start, limit, length, insertRemove;
   1.950 +        int32_t visualStart=0, controlFound=0;
   1.951 +        UChar uchar=pBiDi->text[logicalIndex];
   1.952 +        /* is the logical index pointing to a control ? */
   1.953 +        if(IS_BIDI_CONTROL_CHAR(uchar)) {
   1.954 +            return UBIDI_MAP_NOWHERE;
   1.955 +        }
   1.956 +        /* loop on runs */
   1.957 +        for(i=0; ; i++, visualStart+=length) {
   1.958 +            length=runs[i].visualLimit-visualStart;
   1.959 +            insertRemove=runs[i].insertRemove;
   1.960 +            /* calculated visual index is beyond this run? */
   1.961 +            if(visualIndex>=runs[i].visualLimit) {
   1.962 +                controlFound-=insertRemove;
   1.963 +                continue;
   1.964 +            }
   1.965 +            /* calculated visual index must be within current run */
   1.966 +            if(insertRemove==0) {
   1.967 +                return visualIndex-controlFound;
   1.968 +            }
   1.969 +            if(IS_EVEN_RUN(runs[i].logicalStart)) {
   1.970 +                /* LTR: check from run start to logical index */
   1.971 +                start=runs[i].logicalStart;
   1.972 +                limit=logicalIndex;
   1.973 +            } else {
   1.974 +                /* RTL: check from logical index to run end */
   1.975 +                start=logicalIndex+1;
   1.976 +                limit=GET_INDEX(runs[i].logicalStart)+length;
   1.977 +            }
   1.978 +            for(j=start; j<limit; j++) {
   1.979 +                uchar=pBiDi->text[j];
   1.980 +                if(IS_BIDI_CONTROL_CHAR(uchar)) {
   1.981 +                    controlFound++;
   1.982 +                }
   1.983 +            }
   1.984 +            return visualIndex-controlFound;
   1.985 +        }
   1.986 +    }
   1.987 +
   1.988 +    return visualIndex;
   1.989 +}
   1.990 +
   1.991 +U_CAPI int32_t U_EXPORT2
   1.992 +ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) {
   1.993 +    Run *runs;
   1.994 +    int32_t i, runCount, start;
   1.995 +    RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1);
   1.996 +    RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1);
   1.997 +    RETURN_IF_BAD_RANGE(visualIndex, 0, pBiDi->resultLength, *pErrorCode, -1);
   1.998 +    /* we can do the trivial cases without the runs array */
   1.999 +    if(pBiDi->insertPoints.size==0 && pBiDi->controlCount==0) {
  1.1000 +        if(pBiDi->direction==UBIDI_LTR) {
  1.1001 +            return visualIndex;
  1.1002 +        }
  1.1003 +        else if(pBiDi->direction==UBIDI_RTL) {
  1.1004 +            return pBiDi->length-visualIndex-1;
  1.1005 +        }
  1.1006 +    }
  1.1007 +    if(!ubidi_getRuns(pBiDi, pErrorCode)) {
  1.1008 +        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
  1.1009 +        return -1;
  1.1010 +    }
  1.1011 +
  1.1012 +    runs=pBiDi->runs;
  1.1013 +    runCount=pBiDi->runCount;
  1.1014 +    if(pBiDi->insertPoints.size>0) {
  1.1015 +        /* handle inserted LRM/RLM */
  1.1016 +        int32_t markFound=0, insertRemove;
  1.1017 +        int32_t visualStart=0, length;
  1.1018 +        runs=pBiDi->runs;
  1.1019 +        /* subtract number of marks until visual index */
  1.1020 +        for(i=0; ; i++, visualStart+=length) {
  1.1021 +            length=runs[i].visualLimit-visualStart;
  1.1022 +            insertRemove=runs[i].insertRemove;
  1.1023 +            if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
  1.1024 +                if(visualIndex<=(visualStart+markFound)) {
  1.1025 +                    return UBIDI_MAP_NOWHERE;
  1.1026 +                }
  1.1027 +                markFound++;
  1.1028 +            }
  1.1029 +            /* is adjusted visual index within this run? */
  1.1030 +            if(visualIndex<(runs[i].visualLimit+markFound)) {
  1.1031 +                visualIndex-=markFound;
  1.1032 +                break;
  1.1033 +            }
  1.1034 +            if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
  1.1035 +                if(visualIndex==(visualStart+length+markFound)) {
  1.1036 +                    return UBIDI_MAP_NOWHERE;
  1.1037 +                }
  1.1038 +                markFound++;
  1.1039 +            }
  1.1040 +        }
  1.1041 +    }
  1.1042 +    else if(pBiDi->controlCount>0) {
  1.1043 +        /* handle removed BiDi control characters */
  1.1044 +        int32_t controlFound=0, insertRemove, length;
  1.1045 +        int32_t logicalStart, logicalEnd, visualStart=0, j, k;
  1.1046 +        UChar uchar;
  1.1047 +        UBool evenRun;
  1.1048 +        /* add number of controls until visual index */
  1.1049 +        for(i=0; ; i++, visualStart+=length) {
  1.1050 +            length=runs[i].visualLimit-visualStart;
  1.1051 +            insertRemove=runs[i].insertRemove;
  1.1052 +            /* is adjusted visual index beyond current run? */
  1.1053 +            if(visualIndex>=(runs[i].visualLimit-controlFound+insertRemove)) {
  1.1054 +                controlFound-=insertRemove;
  1.1055 +                continue;
  1.1056 +            }
  1.1057 +            /* adjusted visual index is within current run */
  1.1058 +            if(insertRemove==0) {
  1.1059 +                visualIndex+=controlFound;
  1.1060 +                break;
  1.1061 +            }
  1.1062 +            /* count non-control chars until visualIndex */
  1.1063 +            logicalStart=runs[i].logicalStart;
  1.1064 +            evenRun=IS_EVEN_RUN(logicalStart);
  1.1065 +            REMOVE_ODD_BIT(logicalStart);
  1.1066 +            logicalEnd=logicalStart+length-1;
  1.1067 +            for(j=0; j<length; j++) {
  1.1068 +                k= evenRun ? logicalStart+j : logicalEnd-j;
  1.1069 +                uchar=pBiDi->text[k];
  1.1070 +                if(IS_BIDI_CONTROL_CHAR(uchar)) {
  1.1071 +                    controlFound++;
  1.1072 +                }
  1.1073 +                if((visualIndex+controlFound)==(visualStart+j)) {
  1.1074 +                    break;
  1.1075 +                }
  1.1076 +            }
  1.1077 +            visualIndex+=controlFound;
  1.1078 +            break;
  1.1079 +        }
  1.1080 +    }
  1.1081 +    /* handle all cases */
  1.1082 +    if(runCount<=10) {
  1.1083 +        /* linear search for the run */
  1.1084 +        for(i=0; visualIndex>=runs[i].visualLimit; ++i) {}
  1.1085 +    } else {
  1.1086 +        /* binary search for the run */
  1.1087 +        int32_t begin=0, limit=runCount;
  1.1088 +
  1.1089 +        /* the middle if() is guaranteed to find the run, we don't need a loop limit */
  1.1090 +        for(;;) {
  1.1091 +            i=(begin+limit)/2;
  1.1092 +            if(visualIndex>=runs[i].visualLimit) {
  1.1093 +                begin=i+1;
  1.1094 +            } else if(i==0 || visualIndex>=runs[i-1].visualLimit) {
  1.1095 +                break;
  1.1096 +            } else {
  1.1097 +                limit=i;
  1.1098 +            }
  1.1099 +        }
  1.1100 +    }
  1.1101 +
  1.1102 +    start=runs[i].logicalStart;
  1.1103 +    if(IS_EVEN_RUN(start)) {
  1.1104 +        /* LTR */
  1.1105 +        /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */
  1.1106 +        if(i>0) {
  1.1107 +            visualIndex-=runs[i-1].visualLimit;
  1.1108 +        }
  1.1109 +        return start+visualIndex;
  1.1110 +    } else {
  1.1111 +        /* RTL */
  1.1112 +        return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1;
  1.1113 +    }
  1.1114 +}
  1.1115 +
  1.1116 +U_CAPI void U_EXPORT2
  1.1117 +ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) {
  1.1118 +    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
  1.1119 +    /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */
  1.1120 +    ubidi_countRuns(pBiDi, pErrorCode);
  1.1121 +    if(U_FAILURE(*pErrorCode)) {
  1.1122 +        /* no op */
  1.1123 +    } else if(indexMap==NULL) {
  1.1124 +        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
  1.1125 +    } else {
  1.1126 +        /* fill a logical-to-visual index map using the runs[] */
  1.1127 +        int32_t visualStart, visualLimit, i, j, k;
  1.1128 +        int32_t logicalStart, logicalLimit;
  1.1129 +        Run *runs=pBiDi->runs;
  1.1130 +        if (pBiDi->length<=0) {
  1.1131 +            return;
  1.1132 +        }
  1.1133 +        if (pBiDi->length>pBiDi->resultLength) {
  1.1134 +            uprv_memset(indexMap, 0xFF, pBiDi->length*sizeof(int32_t));
  1.1135 +        }
  1.1136 +
  1.1137 +        visualStart=0;
  1.1138 +        for(j=0; j<pBiDi->runCount; ++j) {
  1.1139 +            logicalStart=GET_INDEX(runs[j].logicalStart);
  1.1140 +            visualLimit=runs[j].visualLimit;
  1.1141 +            if(IS_EVEN_RUN(runs[j].logicalStart)) {
  1.1142 +                do { /* LTR */
  1.1143 +                    indexMap[logicalStart++]=visualStart++;
  1.1144 +                } while(visualStart<visualLimit);
  1.1145 +            } else {
  1.1146 +                logicalStart+=visualLimit-visualStart;  /* logicalLimit */
  1.1147 +                do { /* RTL */
  1.1148 +                    indexMap[--logicalStart]=visualStart++;
  1.1149 +                } while(visualStart<visualLimit);
  1.1150 +            }
  1.1151 +            /* visualStart==visualLimit; */
  1.1152 +        }
  1.1153 +
  1.1154 +        if(pBiDi->insertPoints.size>0) {
  1.1155 +            int32_t markFound=0, runCount=pBiDi->runCount;
  1.1156 +            int32_t length, insertRemove;
  1.1157 +            visualStart=0;
  1.1158 +            /* add number of marks found until each index */
  1.1159 +            for(i=0; i<runCount; i++, visualStart+=length) {
  1.1160 +                length=runs[i].visualLimit-visualStart;
  1.1161 +                insertRemove=runs[i].insertRemove;
  1.1162 +                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
  1.1163 +                    markFound++;
  1.1164 +                }
  1.1165 +                if(markFound>0) {
  1.1166 +                    logicalStart=GET_INDEX(runs[i].logicalStart);
  1.1167 +                    logicalLimit=logicalStart+length;
  1.1168 +                    for(j=logicalStart; j<logicalLimit; j++) {
  1.1169 +                        indexMap[j]+=markFound;
  1.1170 +                    }
  1.1171 +                }
  1.1172 +                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
  1.1173 +                    markFound++;
  1.1174 +                }
  1.1175 +            }
  1.1176 +        }
  1.1177 +        else if(pBiDi->controlCount>0) {
  1.1178 +            int32_t controlFound=0, runCount=pBiDi->runCount;
  1.1179 +            int32_t length, insertRemove;
  1.1180 +            UBool evenRun;
  1.1181 +            UChar uchar;
  1.1182 +            visualStart=0;
  1.1183 +            /* subtract number of controls found until each index */
  1.1184 +            for(i=0; i<runCount; i++, visualStart+=length) {
  1.1185 +                length=runs[i].visualLimit-visualStart;
  1.1186 +                insertRemove=runs[i].insertRemove;
  1.1187 +                /* no control found within previous runs nor within this run */
  1.1188 +                if((controlFound-insertRemove)==0) {
  1.1189 +                    continue;
  1.1190 +                }
  1.1191 +                logicalStart=runs[i].logicalStart;
  1.1192 +                evenRun=IS_EVEN_RUN(logicalStart);
  1.1193 +                REMOVE_ODD_BIT(logicalStart);
  1.1194 +                logicalLimit=logicalStart+length;
  1.1195 +                /* if no control within this run */
  1.1196 +                if(insertRemove==0) {
  1.1197 +                    for(j=logicalStart; j<logicalLimit; j++) {
  1.1198 +                        indexMap[j]-=controlFound;
  1.1199 +                    }
  1.1200 +                    continue;
  1.1201 +                }
  1.1202 +                for(j=0; j<length; j++) {
  1.1203 +                    k= evenRun ? logicalStart+j : logicalLimit-j-1;
  1.1204 +                    uchar=pBiDi->text[k];
  1.1205 +                    if(IS_BIDI_CONTROL_CHAR(uchar)) {
  1.1206 +                        controlFound++;
  1.1207 +                        indexMap[k]=UBIDI_MAP_NOWHERE;
  1.1208 +                        continue;
  1.1209 +                    }
  1.1210 +                    indexMap[k]-=controlFound;
  1.1211 +                }
  1.1212 +            }
  1.1213 +        }
  1.1214 +    }
  1.1215 +}
  1.1216 +
  1.1217 +U_CAPI void U_EXPORT2
  1.1218 +ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) {
  1.1219 +    RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode);
  1.1220 +    if(indexMap==NULL) {
  1.1221 +        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
  1.1222 +        return;
  1.1223 +    }
  1.1224 +    /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */
  1.1225 +    ubidi_countRuns(pBiDi, pErrorCode);
  1.1226 +    if(U_SUCCESS(*pErrorCode)) {
  1.1227 +        /* fill a visual-to-logical index map using the runs[] */
  1.1228 +        Run *runs=pBiDi->runs, *runsLimit=runs+pBiDi->runCount;
  1.1229 +        int32_t logicalStart, visualStart, visualLimit, *pi=indexMap;
  1.1230 +
  1.1231 +        if (pBiDi->resultLength<=0) {
  1.1232 +            return;
  1.1233 +        }
  1.1234 +        visualStart=0;
  1.1235 +        for(; runs<runsLimit; ++runs) {
  1.1236 +            logicalStart=runs->logicalStart;
  1.1237 +            visualLimit=runs->visualLimit;
  1.1238 +            if(IS_EVEN_RUN(logicalStart)) {
  1.1239 +                do { /* LTR */
  1.1240 +                    *pi++ = logicalStart++;
  1.1241 +                } while(++visualStart<visualLimit);
  1.1242 +            } else {
  1.1243 +                REMOVE_ODD_BIT(logicalStart);
  1.1244 +                logicalStart+=visualLimit-visualStart;  /* logicalLimit */
  1.1245 +                do { /* RTL */
  1.1246 +                    *pi++ = --logicalStart;
  1.1247 +                } while(++visualStart<visualLimit);
  1.1248 +            }
  1.1249 +            /* visualStart==visualLimit; */
  1.1250 +        }
  1.1251 +
  1.1252 +        if(pBiDi->insertPoints.size>0) {
  1.1253 +            int32_t markFound=0, runCount=pBiDi->runCount;
  1.1254 +            int32_t insertRemove, i, j, k;
  1.1255 +            runs=pBiDi->runs;
  1.1256 +            /* count all inserted marks */
  1.1257 +            for(i=0; i<runCount; i++) {
  1.1258 +                insertRemove=runs[i].insertRemove;
  1.1259 +                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
  1.1260 +                    markFound++;
  1.1261 +                }
  1.1262 +                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
  1.1263 +                    markFound++;
  1.1264 +                }
  1.1265 +            }
  1.1266 +            /* move back indexes by number of preceding marks */
  1.1267 +            k=pBiDi->resultLength;
  1.1268 +            for(i=runCount-1; i>=0 && markFound>0; i--) {
  1.1269 +                insertRemove=runs[i].insertRemove;
  1.1270 +                if(insertRemove&(LRM_AFTER|RLM_AFTER)) {
  1.1271 +                    indexMap[--k]= UBIDI_MAP_NOWHERE;
  1.1272 +                    markFound--;
  1.1273 +                }
  1.1274 +                visualStart= i>0 ? runs[i-1].visualLimit : 0;
  1.1275 +                for(j=runs[i].visualLimit-1; j>=visualStart && markFound>0; j--) {
  1.1276 +                    indexMap[--k]=indexMap[j];
  1.1277 +                }
  1.1278 +                if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) {
  1.1279 +                    indexMap[--k]= UBIDI_MAP_NOWHERE;
  1.1280 +                    markFound--;
  1.1281 +                }
  1.1282 +            }
  1.1283 +        }
  1.1284 +        else if(pBiDi->controlCount>0) {
  1.1285 +            int32_t runCount=pBiDi->runCount, logicalEnd;
  1.1286 +            int32_t insertRemove, length, i, j, k, m;
  1.1287 +            UChar uchar;
  1.1288 +            UBool evenRun;
  1.1289 +            runs=pBiDi->runs;
  1.1290 +            visualStart=0;
  1.1291 +            /* move forward indexes by number of preceding controls */
  1.1292 +            k=0;
  1.1293 +            for(i=0; i<runCount; i++, visualStart+=length) {
  1.1294 +                length=runs[i].visualLimit-visualStart;
  1.1295 +                insertRemove=runs[i].insertRemove;
  1.1296 +                /* if no control found yet, nothing to do in this run */
  1.1297 +                if((insertRemove==0)&&(k==visualStart)) {
  1.1298 +                    k+=length;
  1.1299 +                    continue;
  1.1300 +                }
  1.1301 +                /* if no control in this run */
  1.1302 +                if(insertRemove==0) {
  1.1303 +                    visualLimit=runs[i].visualLimit;
  1.1304 +                    for(j=visualStart; j<visualLimit; j++) {
  1.1305 +                        indexMap[k++]=indexMap[j];
  1.1306 +                    }
  1.1307 +                    continue;
  1.1308 +                }
  1.1309 +                logicalStart=runs[i].logicalStart;
  1.1310 +                evenRun=IS_EVEN_RUN(logicalStart);
  1.1311 +                REMOVE_ODD_BIT(logicalStart);
  1.1312 +                logicalEnd=logicalStart+length-1;
  1.1313 +                for(j=0; j<length; j++) {
  1.1314 +                    m= evenRun ? logicalStart+j : logicalEnd-j;
  1.1315 +                    uchar=pBiDi->text[m];
  1.1316 +                    if(!IS_BIDI_CONTROL_CHAR(uchar)) {
  1.1317 +                        indexMap[k++]=m;
  1.1318 +                    }
  1.1319 +                }
  1.1320 +            }
  1.1321 +        }
  1.1322 +    }
  1.1323 +}
  1.1324 +
  1.1325 +U_CAPI void U_EXPORT2
  1.1326 +ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length) {
  1.1327 +    if(srcMap!=NULL && destMap!=NULL && length>0) {
  1.1328 +        const int32_t *pi;
  1.1329 +        int32_t destLength=-1, count=0;
  1.1330 +        /* find highest value and count positive indexes in srcMap */
  1.1331 +        pi=srcMap+length;
  1.1332 +        while(pi>srcMap) {
  1.1333 +            if(*--pi>destLength) {
  1.1334 +                destLength=*pi;
  1.1335 +            }
  1.1336 +            if(*pi>=0) {
  1.1337 +                count++;
  1.1338 +            }
  1.1339 +        }
  1.1340 +        destLength++;           /* add 1 for origin 0 */
  1.1341 +        if(count<destLength) {
  1.1342 +            /* we must fill unmatched destMap entries with -1 */
  1.1343 +            uprv_memset(destMap, 0xFF, destLength*sizeof(int32_t));
  1.1344 +        }
  1.1345 +        pi=srcMap+length;
  1.1346 +        while(length>0) {
  1.1347 +            if(*--pi>=0) {
  1.1348 +                destMap[*pi]=--length;
  1.1349 +            } else {
  1.1350 +                --length;
  1.1351 +            }
  1.1352 +        }
  1.1353 +    }
  1.1354 +}

mercurial