layout/base/nsBidi.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/base/nsBidi.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2219 @@
     1.4 +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsBidi.h"
    1.11 +#include "nsUnicodeProperties.h"
    1.12 +#include "nsCRTGlue.h"
    1.13 +
    1.14 +using namespace mozilla::unicode;
    1.15 +
    1.16 +// These are #defined in <sys/regset.h> under Solaris 10 x86
    1.17 +#undef CS
    1.18 +#undef ES
    1.19 +
    1.20 +/*  Comparing the description of the Bidi algorithm with this implementation
    1.21 +    is easier with the same names for the Bidi types in the code as there.
    1.22 +*/
    1.23 +enum { 
    1.24 +    L =   eCharType_LeftToRight,
    1.25 +    R =   eCharType_RightToLeft,
    1.26 +    EN =  eCharType_EuropeanNumber,
    1.27 +    ES =  eCharType_EuropeanNumberSeparator,
    1.28 +    ET =  eCharType_EuropeanNumberTerminator,
    1.29 +    AN =  eCharType_ArabicNumber,
    1.30 +    CS =  eCharType_CommonNumberSeparator,
    1.31 +    B =   eCharType_BlockSeparator,
    1.32 +    S =   eCharType_SegmentSeparator,
    1.33 +    WS =  eCharType_WhiteSpaceNeutral,
    1.34 +    O_N = eCharType_OtherNeutral,
    1.35 +    LRE = eCharType_LeftToRightEmbedding,
    1.36 +    LRO = eCharType_LeftToRightOverride,
    1.37 +    AL =  eCharType_RightToLeftArabic,
    1.38 +    RLE = eCharType_RightToLeftEmbedding,
    1.39 +    RLO = eCharType_RightToLeftOverride,
    1.40 +    PDF = eCharType_PopDirectionalFormat,
    1.41 +    NSM = eCharType_DirNonSpacingMark,
    1.42 +    BN =  eCharType_BoundaryNeutral,
    1.43 +    dirPropCount
    1.44 +};
    1.45 +
    1.46 +/* to avoid some conditional statements, use tiny constant arrays */
    1.47 +static Flags flagLR[2]={ DIRPROP_FLAG(L), DIRPROP_FLAG(R) };
    1.48 +static Flags flagE[2]={ DIRPROP_FLAG(LRE), DIRPROP_FLAG(RLE) };
    1.49 +static Flags flagO[2]={ DIRPROP_FLAG(LRO), DIRPROP_FLAG(RLO) };
    1.50 +
    1.51 +#define DIRPROP_FLAG_LR(level) flagLR[(level)&1]
    1.52 +#define DIRPROP_FLAG_E(level) flagE[(level)&1]
    1.53 +#define DIRPROP_FLAG_O(level) flagO[(level)&1]
    1.54 +
    1.55 +/*
    1.56 + * General implementation notes:
    1.57 + *
    1.58 + * Throughout the implementation, there are comments like (W2) that refer to
    1.59 + * rules of the Bidi algorithm in its version 5, in this example to the second
    1.60 + * rule of the resolution of weak types.
    1.61 + *
    1.62 + * For handling surrogate pairs, where two UChar's form one "abstract" (or UTF-32)
    1.63 + * character according to UTF-16, the second UChar gets the directional property of
    1.64 + * the entire character assigned, while the first one gets a BN, a boundary
    1.65 + * neutral, type, which is ignored by most of the algorithm according to
    1.66 + * rule (X9) and the implementation suggestions of the Bidi algorithm.
    1.67 + *
    1.68 + * Later, AdjustWSLevels() will set the level for each BN to that of the
    1.69 + * following character (UChar), which results in surrogate pairs getting the
    1.70 + * same level on each of their surrogates.
    1.71 + *
    1.72 + * In a UTF-8 implementation, the same thing could be done: the last byte of
    1.73 + * a multi-byte sequence would get the "real" property, while all previous
    1.74 + * bytes of that sequence would get BN.
    1.75 + *
    1.76 + * It is not possible to assign all those parts of a character the same real
    1.77 + * property because this would fail in the resolution of weak types with rules
    1.78 + * that look at immediately surrounding types.
    1.79 + *
    1.80 + * As a related topic, this implementation does not remove Boundary Neutral
    1.81 + * types from the input, but ignores them whenever this is relevant.
    1.82 + * For example, the loop for the resolution of the weak types reads
    1.83 + * types until it finds a non-BN.
    1.84 + * Also, explicit embedding codes are neither changed into BN nor removed.
    1.85 + * They are only treated the same way real BNs are.
    1.86 + * As stated before, AdjustWSLevels() takes care of them at the end.
    1.87 + * For the purpose of conformance, the levels of all these codes
    1.88 + * do not matter.
    1.89 + *
    1.90 + * Note that this implementation never modifies the dirProps
    1.91 + * after the initial setup.
    1.92 + *
    1.93 + *
    1.94 + * In this implementation, the resolution of weak types (Wn),
    1.95 + * neutrals (Nn), and the assignment of the resolved level (In)
    1.96 + * are all done in one single loop, in ResolveImplicitLevels().
    1.97 + * Changes of dirProp values are done on the fly, without writing
    1.98 + * them back to the dirProps array.
    1.99 + *
   1.100 + *
   1.101 + * This implementation contains code that allows to bypass steps of the
   1.102 + * algorithm that are not needed on the specific paragraph
   1.103 + * in order to speed up the most common cases considerably,
   1.104 + * like text that is entirely LTR, or RTL text without numbers.
   1.105 + *
   1.106 + * Most of this is done by setting a bit for each directional property
   1.107 + * in a flags variable and later checking for whether there are
   1.108 + * any LTR characters or any RTL characters, or both, whether
   1.109 + * there are any explicit embedding codes, etc.
   1.110 + *
   1.111 + * If the (Xn) steps are performed, then the flags are re-evaluated,
   1.112 + * because they will then not contain the embedding codes any more
   1.113 + * and will be adjusted for override codes, so that subsequently
   1.114 + * more bypassing may be possible than what the initial flags suggested.
   1.115 + *
   1.116 + * If the text is not mixed-directional, then the
   1.117 + * algorithm steps for the weak type resolution are not performed,
   1.118 + * and all levels are set to the paragraph level.
   1.119 + *
   1.120 + * If there are no explicit embedding codes, then the (Xn) steps
   1.121 + * are not performed.
   1.122 + *
   1.123 + * If embedding levels are supplied as a parameter, then all
   1.124 + * explicit embedding codes are ignored, and the (Xn) steps
   1.125 + * are not performed.
   1.126 + *
   1.127 + * White Space types could get the level of the run they belong to,
   1.128 + * and are checked with a test of (flags&MASK_EMBEDDING) to
   1.129 + * consider if the paragraph direction should be considered in
   1.130 + * the flags variable.
   1.131 + *
   1.132 + * If there are no White Space types in the paragraph, then
   1.133 + * (L1) is not necessary in AdjustWSLevels().
   1.134 + */
   1.135 +nsBidi::nsBidi()
   1.136 +{
   1.137 +  Init();
   1.138 +
   1.139 +  mMayAllocateText=true;
   1.140 +  mMayAllocateRuns=true;
   1.141 +}
   1.142 +
   1.143 +nsBidi::~nsBidi()
   1.144 +{
   1.145 +  Free();
   1.146 +}
   1.147 +
   1.148 +void nsBidi::Init()
   1.149 +{
   1.150 +  /* reset the object, all pointers nullptr, all flags false, all sizes 0 */
   1.151 +  mLength = 0;
   1.152 +  mParaLevel = 0;
   1.153 +  mFlags = 0;
   1.154 +  mDirection = NSBIDI_LTR;
   1.155 +  mTrailingWSStart = 0;
   1.156 +
   1.157 +  mDirPropsSize = 0;
   1.158 +  mLevelsSize = 0;
   1.159 +  mRunsSize = 0;
   1.160 +  mRunCount = -1;
   1.161 +
   1.162 +  mDirProps=nullptr;
   1.163 +  mLevels=nullptr;
   1.164 +  mRuns=nullptr;
   1.165 +
   1.166 +  mDirPropsMemory=nullptr;
   1.167 +  mLevelsMemory=nullptr;
   1.168 +  mRunsMemory=nullptr;
   1.169 +
   1.170 +  mMayAllocateText=false;
   1.171 +  mMayAllocateRuns=false;
   1.172 +  
   1.173 +}
   1.174 +
   1.175 +/*
   1.176 + * We are allowed to allocate memory if aMemory==nullptr or
   1.177 + * aMayAllocate==true for each array that we need.
   1.178 + * We also try to grow and shrink memory as needed if we
   1.179 + * allocate it.
   1.180 + *
   1.181 + * Assume aSizeNeeded>0.
   1.182 + * If *aMemory!=nullptr, then assume *aSize>0.
   1.183 + *
   1.184 + * ### this realloc() may unnecessarily copy the old data,
   1.185 + * which we know we don't need any more;
   1.186 + * is this the best way to do this??
   1.187 + */
   1.188 +bool nsBidi::GetMemory(void **aMemory, size_t *aSize, bool aMayAllocate, size_t aSizeNeeded)
   1.189 +{
   1.190 +  /* check for existing memory */
   1.191 +  if(*aMemory==nullptr) {
   1.192 +    /* we need to allocate memory */
   1.193 +    if(!aMayAllocate) {
   1.194 +      return false;
   1.195 +    } else {
   1.196 +      *aMemory=moz_malloc(aSizeNeeded);
   1.197 +      if (*aMemory!=nullptr) {
   1.198 +        *aSize=aSizeNeeded;
   1.199 +        return true;
   1.200 +      } else {
   1.201 +        *aSize=0;
   1.202 +        return false;
   1.203 +      }
   1.204 +    }
   1.205 +  } else {
   1.206 +    /* there is some memory, is it enough or too much? */
   1.207 +    if(aSizeNeeded>*aSize && !aMayAllocate) {
   1.208 +      /* not enough memory, and we must not allocate */
   1.209 +      return false;
   1.210 +    } else if(aSizeNeeded!=*aSize && aMayAllocate) {
   1.211 +      /* we may try to grow or shrink */
   1.212 +      void *memory=moz_realloc(*aMemory, aSizeNeeded);
   1.213 +
   1.214 +      if(memory!=nullptr) {
   1.215 +        *aMemory=memory;
   1.216 +        *aSize=aSizeNeeded;
   1.217 +        return true;
   1.218 +      } else {
   1.219 +        /* we failed to grow */
   1.220 +        return false;
   1.221 +      }
   1.222 +    } else {
   1.223 +      /* we have at least enough memory and must not allocate */
   1.224 +      return true;
   1.225 +    }
   1.226 +  }
   1.227 +}
   1.228 +
   1.229 +void nsBidi::Free()
   1.230 +{
   1.231 +  moz_free(mDirPropsMemory);
   1.232 +  mDirPropsMemory = nullptr;
   1.233 +  moz_free(mLevelsMemory);
   1.234 +  mLevelsMemory = nullptr;
   1.235 +  moz_free(mRunsMemory);
   1.236 +  mRunsMemory = nullptr;
   1.237 +}
   1.238 +
   1.239 +/* SetPara ------------------------------------------------------------ */
   1.240 +
   1.241 +nsresult nsBidi::SetPara(const char16_t *aText, int32_t aLength,
   1.242 +                         nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels)
   1.243 +{
   1.244 +  nsBidiDirection direction;
   1.245 +
   1.246 +  /* check the argument values */
   1.247 +  if(aText==nullptr ||
   1.248 +     ((NSBIDI_MAX_EXPLICIT_LEVEL<aParaLevel) && !IS_DEFAULT_LEVEL(aParaLevel)) ||
   1.249 +     aLength<-1
   1.250 +    ) {
   1.251 +    return NS_ERROR_INVALID_ARG;
   1.252 +  }
   1.253 +
   1.254 +  if(aLength==-1) {
   1.255 +    aLength = NS_strlen(aText);
   1.256 +  }
   1.257 +
   1.258 +  /* initialize member data */
   1.259 +  mLength=aLength;
   1.260 +  mParaLevel=aParaLevel;
   1.261 +  mDirection=NSBIDI_LTR;
   1.262 +  mTrailingWSStart=aLength;  /* the levels[] will reflect the WS run */
   1.263 +
   1.264 +  mDirProps=nullptr;
   1.265 +  mLevels=nullptr;
   1.266 +  mRuns=nullptr;
   1.267 +
   1.268 +  if(aLength==0) {
   1.269 +    /*
   1.270 +     * For an empty paragraph, create an nsBidi object with the aParaLevel and
   1.271 +     * the flags and the direction set but without allocating zero-length arrays.
   1.272 +     * There is nothing more to do.
   1.273 +     */
   1.274 +    if(IS_DEFAULT_LEVEL(aParaLevel)) {
   1.275 +      mParaLevel&=1;
   1.276 +    }
   1.277 +    if(aParaLevel&1) {
   1.278 +      mFlags=DIRPROP_FLAG(R);
   1.279 +      mDirection=NSBIDI_RTL;
   1.280 +    } else {
   1.281 +      mFlags=DIRPROP_FLAG(L);
   1.282 +      mDirection=NSBIDI_LTR;
   1.283 +    }
   1.284 +
   1.285 +    mRunCount=0;
   1.286 +    return NS_OK;
   1.287 +  }
   1.288 +
   1.289 +  mRunCount=-1;
   1.290 +
   1.291 +  /*
   1.292 +   * Get the directional properties,
   1.293 +   * the flags bit-set, and
   1.294 +   * determine the partagraph level if necessary.
   1.295 +   */
   1.296 +  if(GETDIRPROPSMEMORY(aLength)) {
   1.297 +    mDirProps=mDirPropsMemory;
   1.298 +    GetDirProps(aText);
   1.299 +  } else {
   1.300 +    return NS_ERROR_OUT_OF_MEMORY;
   1.301 +  }
   1.302 +
   1.303 +  /* are explicit levels specified? */
   1.304 +  if(aEmbeddingLevels==nullptr) {
   1.305 +    /* no: determine explicit levels according to the (Xn) rules */\
   1.306 +    if(GETLEVELSMEMORY(aLength)) {
   1.307 +      mLevels=mLevelsMemory;
   1.308 +      direction=ResolveExplicitLevels();
   1.309 +    } else {
   1.310 +      return NS_ERROR_OUT_OF_MEMORY;
   1.311 +    }
   1.312 +  } else {
   1.313 +    /* set BN for all explicit codes, check that all levels are aParaLevel..NSBIDI_MAX_EXPLICIT_LEVEL */
   1.314 +    mLevels=aEmbeddingLevels;
   1.315 +    nsresult rv = CheckExplicitLevels(&direction);
   1.316 +    if(NS_FAILED(rv)) {
   1.317 +      return rv;
   1.318 +    }
   1.319 +  }
   1.320 +
   1.321 +  /*
   1.322 +   * The steps after (X9) in the Bidi algorithm are performed only if
   1.323 +   * the paragraph text has mixed directionality!
   1.324 +   */
   1.325 +  switch(direction) {
   1.326 +    case NSBIDI_LTR:
   1.327 +      /* make sure paraLevel is even */
   1.328 +      mParaLevel=(mParaLevel+1)&~1;
   1.329 +
   1.330 +      /* all levels are implicitly at paraLevel (important for GetLevels()) */
   1.331 +      mTrailingWSStart=0;
   1.332 +      break;
   1.333 +    case NSBIDI_RTL:
   1.334 +      /* make sure paraLevel is odd */
   1.335 +      mParaLevel|=1;
   1.336 +
   1.337 +      /* all levels are implicitly at paraLevel (important for GetLevels()) */
   1.338 +      mTrailingWSStart=0;
   1.339 +      break;
   1.340 +    default:
   1.341 +      /*
   1.342 +       * If there are no external levels specified and there
   1.343 +       * are no significant explicit level codes in the text,
   1.344 +       * then we can treat the entire paragraph as one run.
   1.345 +       * Otherwise, we need to perform the following rules on runs of
   1.346 +       * the text with the same embedding levels. (X10)
   1.347 +       * "Significant" explicit level codes are ones that actually
   1.348 +       * affect non-BN characters.
   1.349 +       * Examples for "insignificant" ones are empty embeddings
   1.350 +       * LRE-PDF, LRE-RLE-PDF-PDF, etc.
   1.351 +       */
   1.352 +      if(aEmbeddingLevels==nullptr && !(mFlags&DIRPROP_FLAG_MULTI_RUNS)) {
   1.353 +        ResolveImplicitLevels(0, aLength,
   1.354 +                    GET_LR_FROM_LEVEL(mParaLevel),
   1.355 +                    GET_LR_FROM_LEVEL(mParaLevel));
   1.356 +      } else {
   1.357 +        /* sor, eor: start and end types of same-level-run */
   1.358 +        nsBidiLevel *levels=mLevels;
   1.359 +        int32_t start, limit=0;
   1.360 +        nsBidiLevel level, nextLevel;
   1.361 +        DirProp sor, eor;
   1.362 +
   1.363 +        /* determine the first sor and set eor to it because of the loop body (sor=eor there) */
   1.364 +        level=mParaLevel;
   1.365 +        nextLevel=levels[0];
   1.366 +        if(level<nextLevel) {
   1.367 +          eor=GET_LR_FROM_LEVEL(nextLevel);
   1.368 +        } else {
   1.369 +          eor=GET_LR_FROM_LEVEL(level);
   1.370 +        }
   1.371 +
   1.372 +        do {
   1.373 +          /* determine start and limit of the run (end points just behind the run) */
   1.374 +
   1.375 +          /* the values for this run's start are the same as for the previous run's end */
   1.376 +          sor=eor;
   1.377 +          start=limit;
   1.378 +          level=nextLevel;
   1.379 +
   1.380 +          /* search for the limit of this run */
   1.381 +          while(++limit<aLength && levels[limit]==level) {}
   1.382 +
   1.383 +          /* get the correct level of the next run */
   1.384 +          if(limit<aLength) {
   1.385 +            nextLevel=levels[limit];
   1.386 +          } else {
   1.387 +            nextLevel=mParaLevel;
   1.388 +          }
   1.389 +
   1.390 +          /* determine eor from max(level, nextLevel); sor is last run's eor */
   1.391 +          if((level&~NSBIDI_LEVEL_OVERRIDE)<(nextLevel&~NSBIDI_LEVEL_OVERRIDE)) {
   1.392 +            eor=GET_LR_FROM_LEVEL(nextLevel);
   1.393 +          } else {
   1.394 +            eor=GET_LR_FROM_LEVEL(level);
   1.395 +          }
   1.396 +
   1.397 +          /* if the run consists of overridden directional types, then there
   1.398 +          are no implicit types to be resolved */
   1.399 +          if(!(level&NSBIDI_LEVEL_OVERRIDE)) {
   1.400 +            ResolveImplicitLevels(start, limit, sor, eor);
   1.401 +          }
   1.402 +        } while(limit<aLength);
   1.403 +      }
   1.404 +
   1.405 +      /* reset the embedding levels for some non-graphic characters (L1), (X9) */
   1.406 +      AdjustWSLevels();
   1.407 +      break;
   1.408 +  }
   1.409 +
   1.410 +  mDirection=direction;
   1.411 +  return NS_OK;
   1.412 +}
   1.413 +
   1.414 +/* perform (P2)..(P3) ------------------------------------------------------- */
   1.415 +
   1.416 +/*
   1.417 + * Get the directional properties for the text,
   1.418 + * calculate the flags bit-set, and
   1.419 + * determine the partagraph level if necessary.
   1.420 + */
   1.421 +void nsBidi::GetDirProps(const char16_t *aText)
   1.422 +{
   1.423 +  DirProp *dirProps=mDirPropsMemory;    /* mDirProps is const */
   1.424 +
   1.425 +  int32_t i=0, length=mLength;
   1.426 +  Flags flags=0;      /* collect all directionalities in the text */
   1.427 +  char16_t uchar;
   1.428 +  DirProp dirProp;
   1.429 +
   1.430 +  if(IS_DEFAULT_LEVEL(mParaLevel)) {
   1.431 +    /* determine the paragraph level (P2..P3) */
   1.432 +    for(;;) {
   1.433 +      uchar=aText[i];
   1.434 +      if(!IS_FIRST_SURROGATE(uchar) || i+1==length || !IS_SECOND_SURROGATE(aText[i+1])) {
   1.435 +        /* not a surrogate pair */
   1.436 +        flags|=DIRPROP_FLAG(dirProps[i]=dirProp=GetBidiCat((uint32_t)uchar));
   1.437 +      } else {
   1.438 +        /* a surrogate pair */
   1.439 +        dirProps[i++]=BN;   /* first surrogate in the pair gets the BN type */
   1.440 +        flags|=DIRPROP_FLAG(dirProps[i]=dirProp=GetBidiCat(GET_UTF_32(uchar, aText[i])))|DIRPROP_FLAG(BN);
   1.441 +      }
   1.442 +      ++i;
   1.443 +      if(dirProp==L) {
   1.444 +        mParaLevel=0;
   1.445 +        break;
   1.446 +      } else if(dirProp==R || dirProp==AL) {
   1.447 +        mParaLevel=1;
   1.448 +        break;
   1.449 +      } else if(i==length) {
   1.450 +        /*
   1.451 +         * see comment in nsIBidi.h:
   1.452 +         * the DEFAULT_XXX values are designed so that
   1.453 +         * their bit 0 alone yields the intended default
   1.454 +         */
   1.455 +        mParaLevel&=1;
   1.456 +        break;
   1.457 +      }
   1.458 +    }
   1.459 +  }
   1.460 +
   1.461 +  /* get the rest of the directional properties and the flags bits */
   1.462 +  while(i<length) {
   1.463 +    uchar=aText[i];
   1.464 +    if(!IS_FIRST_SURROGATE(uchar) || i+1==length || !IS_SECOND_SURROGATE(aText[i+1])) {
   1.465 +      /* not a surrogate pair */
   1.466 +      flags|=DIRPROP_FLAG(dirProps[i]=GetBidiCat((uint32_t)uchar));
   1.467 +    } else {
   1.468 +      /* a surrogate pair */
   1.469 +      dirProps[i++]=BN;   /* second surrogate in the pair gets the BN type */
   1.470 +      flags|=DIRPROP_FLAG(dirProps[i]=GetBidiCat(GET_UTF_32(uchar, aText[i])))|DIRPROP_FLAG(BN);
   1.471 +    }
   1.472 +    ++i;
   1.473 +  }
   1.474 +  if(flags&MASK_EMBEDDING) {
   1.475 +    flags|=DIRPROP_FLAG_LR(mParaLevel);
   1.476 +  }
   1.477 +  mFlags=flags;
   1.478 +}
   1.479 +
   1.480 +/* perform (X1)..(X9) ------------------------------------------------------- */
   1.481 +
   1.482 +/*
   1.483 + * Resolve the explicit levels as specified by explicit embedding codes.
   1.484 + * Recalculate the flags to have them reflect the real properties
   1.485 + * after taking the explicit embeddings into account.
   1.486 + *
   1.487 + * The Bidi algorithm is designed to result in the same behavior whether embedding
   1.488 + * levels are externally specified (from "styled text", supposedly the preferred
   1.489 + * method) or set by explicit embedding codes (LRx, RLx, PDF) in the plain text.
   1.490 + * That is why (X9) instructs to remove all explicit codes (and BN).
   1.491 + * However, in a real implementation, this removal of these codes and their index
   1.492 + * positions in the plain text is undesirable since it would result in
   1.493 + * reallocated, reindexed text.
   1.494 + * Instead, this implementation leaves the codes in there and just ignores them
   1.495 + * in the subsequent processing.
   1.496 + * In order to get the same reordering behavior, positions with a BN or an
   1.497 + * explicit embedding code just get the same level assigned as the last "real"
   1.498 + * character.
   1.499 + *
   1.500 + * Some implementations, not this one, then overwrite some of these
   1.501 + * directionality properties at "real" same-level-run boundaries by
   1.502 + * L or R codes so that the resolution of weak types can be performed on the
   1.503 + * entire paragraph at once instead of having to parse it once more and
   1.504 + * perform that resolution on same-level-runs.
   1.505 + * This limits the scope of the implicit rules in effectively
   1.506 + * the same way as the run limits.
   1.507 + *
   1.508 + * Instead, this implementation does not modify these codes.
   1.509 + * On one hand, the paragraph has to be scanned for same-level-runs, but
   1.510 + * on the other hand, this saves another loop to reset these codes,
   1.511 + * or saves making and modifying a copy of dirProps[].
   1.512 + *
   1.513 + *
   1.514 + * Note that (Pn) and (Xn) changed significantly from version 4 of the Bidi algorithm.
   1.515 + *
   1.516 + *
   1.517 + * Handling the stack of explicit levels (Xn):
   1.518 + *
   1.519 + * With the Bidi stack of explicit levels,
   1.520 + * as pushed with each LRE, RLE, LRO, and RLO and popped with each PDF,
   1.521 + * the explicit level must never exceed NSBIDI_MAX_EXPLICIT_LEVEL==61.
   1.522 + *
   1.523 + * In order to have a correct push-pop semantics even in the case of overflows,
   1.524 + * there are two overflow counters:
   1.525 + * - countOver60 is incremented with each LRx at level 60
   1.526 + * - from level 60, one RLx increases the level to 61
   1.527 + * - countOver61 is incremented with each LRx and RLx at level 61
   1.528 + *
   1.529 + * Popping levels with PDF must work in the opposite order so that level 61
   1.530 + * is correct at the correct point. Underflows (too many PDFs) must be checked.
   1.531 + *
   1.532 + * This implementation assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
   1.533 + */
   1.534 +
   1.535 +nsBidiDirection nsBidi::ResolveExplicitLevels()
   1.536 +{
   1.537 +  const DirProp *dirProps=mDirProps;
   1.538 +  nsBidiLevel *levels=mLevels;
   1.539 +
   1.540 +  int32_t i=0, length=mLength;
   1.541 +  Flags flags=mFlags;       /* collect all directionalities in the text */
   1.542 +  DirProp dirProp;
   1.543 +  nsBidiLevel level=mParaLevel;
   1.544 +
   1.545 +  nsBidiDirection direction;
   1.546 +
   1.547 +  /* determine if the text is mixed-directional or single-directional */
   1.548 +  direction=DirectionFromFlags(flags);
   1.549 +
   1.550 +  /* we may not need to resolve any explicit levels */
   1.551 +  if(direction!=NSBIDI_MIXED) {
   1.552 +    /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */
   1.553 +  } else if(!(flags&MASK_EXPLICIT)) {
   1.554 +    /* mixed, but all characters are at the same embedding level */
   1.555 +    /* set all levels to the paragraph level */
   1.556 +    for(i=0; i<length; ++i) {
   1.557 +      levels[i]=level;
   1.558 +    }
   1.559 +  } else {
   1.560 +    /* continue to perform (Xn) */
   1.561 +
   1.562 +    /* (X1) level is set for all codes, embeddingLevel keeps track of the push/pop operations */
   1.563 +    /* both variables may carry the NSBIDI_LEVEL_OVERRIDE flag to indicate the override status */
   1.564 +    nsBidiLevel embeddingLevel=level, newLevel, stackTop=0;
   1.565 +
   1.566 +    nsBidiLevel stack[NSBIDI_MAX_EXPLICIT_LEVEL];        /* we never push anything >=NSBIDI_MAX_EXPLICIT_LEVEL */
   1.567 +    uint32_t countOver60=0, countOver61=0;  /* count overflows of explicit levels */
   1.568 +
   1.569 +    /* recalculate the flags */
   1.570 +    flags=0;
   1.571 +
   1.572 +    /* since we assume that this is a single paragraph, we ignore (X8) */
   1.573 +    for(i=0; i<length; ++i) {
   1.574 +      dirProp=dirProps[i];
   1.575 +      switch(dirProp) {
   1.576 +        case LRE:
   1.577 +        case LRO:
   1.578 +          /* (X3, X5) */
   1.579 +          newLevel=(embeddingLevel+2)&~(NSBIDI_LEVEL_OVERRIDE|1);    /* least greater even level */
   1.580 +          if(newLevel<=NSBIDI_MAX_EXPLICIT_LEVEL) {
   1.581 +            stack[stackTop]=embeddingLevel;
   1.582 +            ++stackTop;
   1.583 +            embeddingLevel=newLevel;
   1.584 +            if(dirProp==LRO) {
   1.585 +              embeddingLevel|=NSBIDI_LEVEL_OVERRIDE;
   1.586 +            } else {
   1.587 +              embeddingLevel&=~NSBIDI_LEVEL_OVERRIDE;
   1.588 +            }
   1.589 +          } else if((embeddingLevel&~NSBIDI_LEVEL_OVERRIDE)==NSBIDI_MAX_EXPLICIT_LEVEL) {
   1.590 +            ++countOver61;
   1.591 +          } else /* (embeddingLevel&~NSBIDI_LEVEL_OVERRIDE)==NSBIDI_MAX_EXPLICIT_LEVEL-1 */ {
   1.592 +            ++countOver60;
   1.593 +          }
   1.594 +          flags|=DIRPROP_FLAG(BN);
   1.595 +          break;
   1.596 +        case RLE:
   1.597 +        case RLO:
   1.598 +          /* (X2, X4) */
   1.599 +          newLevel=((embeddingLevel&~NSBIDI_LEVEL_OVERRIDE)+1)|1;    /* least greater odd level */
   1.600 +          if(newLevel<=NSBIDI_MAX_EXPLICIT_LEVEL) {
   1.601 +            stack[stackTop]=embeddingLevel;
   1.602 +            ++stackTop;
   1.603 +            embeddingLevel=newLevel;
   1.604 +            if(dirProp==RLO) {
   1.605 +              embeddingLevel|=NSBIDI_LEVEL_OVERRIDE;
   1.606 +            } else {
   1.607 +              embeddingLevel&=~NSBIDI_LEVEL_OVERRIDE;
   1.608 +            }
   1.609 +          } else {
   1.610 +            ++countOver61;
   1.611 +          }
   1.612 +          flags|=DIRPROP_FLAG(BN);
   1.613 +          break;
   1.614 +        case PDF:
   1.615 +          /* (X7) */
   1.616 +          /* handle all the overflow cases first */
   1.617 +          if(countOver61>0) {
   1.618 +            --countOver61;
   1.619 +          } else if(countOver60>0 && (embeddingLevel&~NSBIDI_LEVEL_OVERRIDE)!=NSBIDI_MAX_EXPLICIT_LEVEL) {
   1.620 +            /* handle LRx overflows from level 60 */
   1.621 +            --countOver60;
   1.622 +          } else if(stackTop>0) {
   1.623 +            /* this is the pop operation; it also pops level 61 while countOver60>0 */
   1.624 +            --stackTop;
   1.625 +            embeddingLevel=stack[stackTop];
   1.626 +            /* } else { (underflow) */
   1.627 +          }
   1.628 +          flags|=DIRPROP_FLAG(BN);
   1.629 +          break;
   1.630 +        case B:
   1.631 +          /*
   1.632 +           * We do not really expect to see a paragraph separator (B),
   1.633 +           * but we should do something reasonable with it,
   1.634 +           * especially at the end of the text.
   1.635 +           */
   1.636 +          stackTop=0;
   1.637 +          countOver60=countOver61=0;
   1.638 +          embeddingLevel=level=mParaLevel;
   1.639 +          flags|=DIRPROP_FLAG(B);
   1.640 +          break;
   1.641 +        case BN:
   1.642 +          /* BN, LRE, RLE, and PDF are supposed to be removed (X9) */
   1.643 +          /* they will get their levels set correctly in AdjustWSLevels() */
   1.644 +          flags|=DIRPROP_FLAG(BN);
   1.645 +          break;
   1.646 +        default:
   1.647 +          /* all other types get the "real" level */
   1.648 +          if(level!=embeddingLevel) {
   1.649 +            level=embeddingLevel;
   1.650 +            if(level&NSBIDI_LEVEL_OVERRIDE) {
   1.651 +              flags|=DIRPROP_FLAG_O(level)|DIRPROP_FLAG_MULTI_RUNS;
   1.652 +            } else {
   1.653 +              flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG_MULTI_RUNS;
   1.654 +            }
   1.655 +          }
   1.656 +          if(!(level&NSBIDI_LEVEL_OVERRIDE)) {
   1.657 +            flags|=DIRPROP_FLAG(dirProp);
   1.658 +          }
   1.659 +          break;
   1.660 +      }
   1.661 +
   1.662 +      /*
   1.663 +       * We need to set reasonable levels even on BN codes and
   1.664 +       * explicit codes because we will later look at same-level runs (X10).
   1.665 +       */
   1.666 +      levels[i]=level;
   1.667 +    }
   1.668 +    if(flags&MASK_EMBEDDING) {
   1.669 +      flags|=DIRPROP_FLAG_LR(mParaLevel);
   1.670 +    }
   1.671 +
   1.672 +    /* subsequently, ignore the explicit codes and BN (X9) */
   1.673 +
   1.674 +    /* again, determine if the text is mixed-directional or single-directional */
   1.675 +    mFlags=flags;
   1.676 +    direction=DirectionFromFlags(flags);
   1.677 +  }
   1.678 +  return direction;
   1.679 +}
   1.680 +
   1.681 +/*
   1.682 + * Use a pre-specified embedding levels array:
   1.683 + *
   1.684 + * Adjust the directional properties for overrides (->LEVEL_OVERRIDE),
   1.685 + * ignore all explicit codes (X9),
   1.686 + * and check all the preset levels.
   1.687 + *
   1.688 + * Recalculate the flags to have them reflect the real properties
   1.689 + * after taking the explicit embeddings into account.
   1.690 + */
   1.691 +nsresult nsBidi::CheckExplicitLevels(nsBidiDirection *aDirection)
   1.692 +{
   1.693 +  const DirProp *dirProps=mDirProps;
   1.694 +  nsBidiLevel *levels=mLevels;
   1.695 +
   1.696 +  int32_t i, length=mLength;
   1.697 +  Flags flags=0;  /* collect all directionalities in the text */
   1.698 +  nsBidiLevel level, paraLevel=mParaLevel;
   1.699 +
   1.700 +  for(i=0; i<length; ++i) {
   1.701 +    level=levels[i];
   1.702 +    if(level&NSBIDI_LEVEL_OVERRIDE) {
   1.703 +      /* keep the override flag in levels[i] but adjust the flags */
   1.704 +      level&=~NSBIDI_LEVEL_OVERRIDE;     /* make the range check below simpler */
   1.705 +      flags|=DIRPROP_FLAG_O(level);
   1.706 +    } else {
   1.707 +      /* set the flags */
   1.708 +      flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG(dirProps[i]);
   1.709 +    }
   1.710 +    if(level<paraLevel || NSBIDI_MAX_EXPLICIT_LEVEL<level) {
   1.711 +      /* level out of bounds */
   1.712 +      *aDirection = NSBIDI_LTR;
   1.713 +      return NS_ERROR_INVALID_ARG;
   1.714 +    }
   1.715 +  }
   1.716 +  if(flags&MASK_EMBEDDING) {
   1.717 +    flags|=DIRPROP_FLAG_LR(mParaLevel);
   1.718 +  }
   1.719 +
   1.720 +  /* determine if the text is mixed-directional or single-directional */
   1.721 +  mFlags=flags;
   1.722 +  *aDirection = DirectionFromFlags(flags);
   1.723 +  return NS_OK;
   1.724 +}
   1.725 +
   1.726 +/* determine if the text is mixed-directional or single-directional */
   1.727 +nsBidiDirection nsBidi::DirectionFromFlags(Flags aFlags)
   1.728 +{
   1.729 +  /* if the text contains AN and neutrals, then some neutrals may become RTL */
   1.730 +  if(!(aFlags&MASK_RTL || (aFlags&DIRPROP_FLAG(AN) && aFlags&MASK_POSSIBLE_N))) {
   1.731 +    return NSBIDI_LTR;
   1.732 +  } else if(!(aFlags&MASK_LTR)) {
   1.733 +    return NSBIDI_RTL;
   1.734 +  } else {
   1.735 +    return NSBIDI_MIXED;
   1.736 +  }
   1.737 +}
   1.738 +
   1.739 +/* perform rules (Wn), (Nn), and (In) on a run of the text ------------------ */
   1.740 +
   1.741 +/*
   1.742 + * This implementation of the (Wn) rules applies all rules in one pass.
   1.743 + * In order to do so, it needs a look-ahead of typically 1 character
   1.744 + * (except for W5: sequences of ET) and keeps track of changes
   1.745 + * in a rule Wp that affect a later Wq (p<q).
   1.746 + *
   1.747 + * historyOfEN is a variable-saver: it contains 4 boolean states;
   1.748 + * a bit in it set to 1 means:
   1.749 + *  bit 0: the current code is an EN after W2
   1.750 + *  bit 1: the current code is an EN after W4
   1.751 + *  bit 2: the previous code was an EN after W2
   1.752 + *  bit 3: the previous code was an EN after W4
   1.753 + * In other words, b0..1 have transitions of EN in the current iteration,
   1.754 + * while b2..3 have the transitions of EN in the previous iteration.
   1.755 + * A simple historyOfEN<<=2 suffices for the propagation.
   1.756 + *
   1.757 + * The (Nn) and (In) rules are also performed in that same single loop,
   1.758 + * but effectively one iteration behind for white space.
   1.759 + *
   1.760 + * Since all implicit rules are performed in one step, it is not necessary
   1.761 + * to actually store the intermediate directional properties in dirProps[].
   1.762 + */
   1.763 +
   1.764 +#define EN_SHIFT 2
   1.765 +#define EN_AFTER_W2 1
   1.766 +#define EN_AFTER_W4 2
   1.767 +#define EN_ALL 3
   1.768 +#define PREV_EN_AFTER_W2 4
   1.769 +#define PREV_EN_AFTER_W4 8
   1.770 +
   1.771 +void nsBidi::ResolveImplicitLevels(int32_t aStart, int32_t aLimit,
   1.772 +                   DirProp aSOR, DirProp aEOR)
   1.773 +{
   1.774 +  const DirProp *dirProps=mDirProps;
   1.775 +  nsBidiLevel *levels=mLevels;
   1.776 +
   1.777 +  int32_t i, next, neutralStart=-1;
   1.778 +  DirProp prevDirProp, dirProp, nextDirProp, lastStrong, beforeNeutral;
   1.779 +  uint8_t historyOfEN;
   1.780 +
   1.781 +  /* initialize: current at aSOR, next at aStart (it is aStart<aLimit) */
   1.782 +  next=aStart;
   1.783 +  beforeNeutral=dirProp=lastStrong=aSOR;
   1.784 +  nextDirProp=dirProps[next];
   1.785 +  historyOfEN=0;
   1.786 +
   1.787 +  /*
   1.788 +   * In all steps of this implementation, BN and explicit embedding codes
   1.789 +   * must be treated as if they didn't exist (X9).
   1.790 +   * They will get levels set before a non-neutral character, and remain
   1.791 +   * undefined before a neutral one, but AdjustWSLevels() will take care
   1.792 +   * of all of them.
   1.793 +   */
   1.794 +  while(DIRPROP_FLAG(nextDirProp)&MASK_BN_EXPLICIT) {
   1.795 +    if(++next<aLimit) {
   1.796 +      nextDirProp=dirProps[next];
   1.797 +    } else {
   1.798 +      nextDirProp=aEOR;
   1.799 +      break;
   1.800 +    }
   1.801 +  }
   1.802 +
   1.803 +  /* loop for entire run */
   1.804 +  while(next<aLimit) {
   1.805 +    /* advance */
   1.806 +    prevDirProp=dirProp;
   1.807 +    dirProp=nextDirProp;
   1.808 +    i=next;
   1.809 +    do {
   1.810 +      if(++next<aLimit) {
   1.811 +        nextDirProp=dirProps[next];
   1.812 +      } else {
   1.813 +        nextDirProp=aEOR;
   1.814 +        break;
   1.815 +      }
   1.816 +    } while(DIRPROP_FLAG(nextDirProp)&MASK_BN_EXPLICIT);
   1.817 +    historyOfEN<<=EN_SHIFT;
   1.818 +
   1.819 +    /* (W1..W7) */
   1.820 +    switch(dirProp) {
   1.821 +      case L:
   1.822 +        lastStrong=L;
   1.823 +        break;
   1.824 +      case R:
   1.825 +        lastStrong=R;
   1.826 +        break;
   1.827 +      case AL:
   1.828 +        /* (W3) */
   1.829 +        lastStrong=AL;
   1.830 +        dirProp=R;
   1.831 +        break;
   1.832 +      case EN:
   1.833 +        /* we have to set historyOfEN correctly */
   1.834 +        if(lastStrong==AL) {
   1.835 +          /* (W2) */
   1.836 +          dirProp=AN;
   1.837 +        } else {
   1.838 +          if(lastStrong==L) {
   1.839 +            /* (W7) */
   1.840 +            dirProp=L;
   1.841 +          }
   1.842 +          /* this EN stays after (W2) and (W4) - at least before (W7) */
   1.843 +          historyOfEN|=EN_ALL;
   1.844 +        }
   1.845 +        break;
   1.846 +      case ES:
   1.847 +        if( historyOfEN&PREV_EN_AFTER_W2 &&     /* previous was EN before (W4) */
   1.848 +            nextDirProp==EN && lastStrong!=AL   /* next is EN and (W2) won't make it AN */
   1.849 +          ) {
   1.850 +          /* (W4) */
   1.851 +          if(lastStrong!=L) {
   1.852 +            dirProp=EN;
   1.853 +          } else {
   1.854 +            /* (W7) */
   1.855 +            dirProp=L;
   1.856 +          }
   1.857 +          historyOfEN|=EN_AFTER_W4;
   1.858 +        } else {
   1.859 +          /* (W6) */
   1.860 +          dirProp=O_N;
   1.861 +        }
   1.862 +        break;
   1.863 +      case CS:
   1.864 +        if( historyOfEN&PREV_EN_AFTER_W2 &&     /* previous was EN before (W4) */
   1.865 +            nextDirProp==EN && lastStrong!=AL   /* next is EN and (W2) won't make it AN */
   1.866 +          ) {
   1.867 +          /* (W4) */
   1.868 +          if(lastStrong!=L) {
   1.869 +            dirProp=EN;
   1.870 +          } else {
   1.871 +            /* (W7) */
   1.872 +            dirProp=L;
   1.873 +          }
   1.874 +          historyOfEN|=EN_AFTER_W4;
   1.875 +        } else if(prevDirProp==AN &&                    /* previous was AN */
   1.876 +              (nextDirProp==AN ||                   /* next is AN */
   1.877 +               (nextDirProp==EN && lastStrong==AL))   /* or (W2) will make it one */
   1.878 +             ) {
   1.879 +          /* (W4) */
   1.880 +          dirProp=AN;
   1.881 +        } else {
   1.882 +          /* (W6) */
   1.883 +          dirProp=O_N;
   1.884 +        }
   1.885 +        break;
   1.886 +      case ET:
   1.887 +        /* get sequence of ET; advance only next, not current, previous or historyOfEN */
   1.888 +        while(next<aLimit && DIRPROP_FLAG(nextDirProp)&MASK_ET_NSM_BN /* (W1), (X9) */) {
   1.889 +          if(++next<aLimit) {
   1.890 +            nextDirProp=dirProps[next];
   1.891 +          } else {
   1.892 +            nextDirProp=aEOR;
   1.893 +            break;
   1.894 +          }
   1.895 +        }
   1.896 +
   1.897 +        if( historyOfEN&PREV_EN_AFTER_W4 ||     /* previous was EN before (W5) */
   1.898 +            (nextDirProp==EN && lastStrong!=AL)   /* next is EN and (W2) won't make it AN */
   1.899 +          ) {
   1.900 +          /* (W5) */
   1.901 +          if(lastStrong!=L) {
   1.902 +            dirProp=EN;
   1.903 +          } else {
   1.904 +            /* (W7) */
   1.905 +            dirProp=L;
   1.906 +          }
   1.907 +        } else {
   1.908 +          /* (W6) */
   1.909 +          dirProp=O_N;
   1.910 +        }
   1.911 +
   1.912 +        /* apply the result of (W1), (W5)..(W7) to the entire sequence of ET */
   1.913 +        break;
   1.914 +      case NSM:
   1.915 +        /* (W1) */
   1.916 +        dirProp=prevDirProp;
   1.917 +        /* set historyOfEN back to prevDirProp's historyOfEN */
   1.918 +        historyOfEN>>=EN_SHIFT;
   1.919 +        /*
   1.920 +         * Technically, this should be done before the switch() in the form
   1.921 +         *      if(nextDirProp==NSM) {
   1.922 +         *          dirProps[next]=nextDirProp=dirProp;
   1.923 +         *      }
   1.924 +         *
   1.925 +         * - effectively one iteration ahead.
   1.926 +         * However, whether the next dirProp is NSM or is equal to the current dirProp
   1.927 +         * does not change the outcome of any condition in (W2)..(W7).
   1.928 +         */
   1.929 +        break;
   1.930 +      default:
   1.931 +        break;
   1.932 +    }
   1.933 +
   1.934 +    /* here, it is always [prev,this,next]dirProp!=BN; it may be next>i+1 */
   1.935 +
   1.936 +    /* perform (Nn) - here, only L, R, EN, AN, and neutrals are left */
   1.937 +    /* this is one iteration late for the neutrals */
   1.938 +    if(DIRPROP_FLAG(dirProp)&MASK_N) {
   1.939 +      if(neutralStart<0) {
   1.940 +        /* start of a sequence of neutrals */
   1.941 +        neutralStart=i;
   1.942 +        beforeNeutral=prevDirProp;
   1.943 +      }
   1.944 +    } else /* not a neutral, can be only one of { L, R, EN, AN } */ {
   1.945 +      /*
   1.946 +       * Note that all levels[] values are still the same at this
   1.947 +       * point because this function is called for an entire
   1.948 +       * same-level run.
   1.949 +       * Therefore, we need to read only one actual level.
   1.950 +       */
   1.951 +      nsBidiLevel level=levels[i];
   1.952 +
   1.953 +      if(neutralStart>=0) {
   1.954 +        nsBidiLevel final;
   1.955 +        /* end of a sequence of neutrals (dirProp is "afterNeutral") */
   1.956 +        if(beforeNeutral==L) {
   1.957 +          if(dirProp==L) {
   1.958 +            final=0;                /* make all neutrals L (N1) */
   1.959 +          } else {
   1.960 +            final=level;            /* make all neutrals "e" (N2) */
   1.961 +          }
   1.962 +        } else /* beforeNeutral is one of { R, EN, AN } */ {
   1.963 +          if(dirProp==L) {
   1.964 +            final=level;            /* make all neutrals "e" (N2) */
   1.965 +          } else {
   1.966 +            final=1;                /* make all neutrals R (N1) */
   1.967 +          }
   1.968 +        }
   1.969 +        /* perform (In) on the sequence of neutrals */
   1.970 +        if((level^final)&1) {
   1.971 +          /* do something only if we need to _change_ the level */
   1.972 +          do {
   1.973 +            ++levels[neutralStart];
   1.974 +          } while(++neutralStart<i);
   1.975 +        }
   1.976 +        neutralStart=-1;
   1.977 +      }
   1.978 +
   1.979 +      /* perform (In) on the non-neutral character */
   1.980 +      /*
   1.981 +       * in the cases of (W5), processing a sequence of ET,
   1.982 +       * and of (X9), skipping BN,
   1.983 +       * there may be multiple characters from i to <next
   1.984 +       * that all get (virtually) the same dirProp and (really) the same level
   1.985 +       */
   1.986 +      if(dirProp==L) {
   1.987 +        if(level&1) {
   1.988 +          ++level;
   1.989 +        } else {
   1.990 +          i=next;     /* we keep the levels */
   1.991 +        }
   1.992 +      } else if(dirProp==R) {
   1.993 +        if(!(level&1)) {
   1.994 +          ++level;
   1.995 +        } else {
   1.996 +          i=next;     /* we keep the levels */
   1.997 +        }
   1.998 +      } else /* EN or AN */ {
   1.999 +        level=(level+2)&~1;     /* least greater even level */
  1.1000 +      }
  1.1001 +
  1.1002 +      /* apply the new level to the sequence, if necessary */
  1.1003 +      while(i<next) {
  1.1004 +        levels[i++]=level;
  1.1005 +      }
  1.1006 +    }
  1.1007 +  }
  1.1008 +
  1.1009 +  /* perform (Nn) - here,
  1.1010 +  the character after the neutrals is aEOR, which is either L or R */
  1.1011 +  /* this is one iteration late for the neutrals */
  1.1012 +  if(neutralStart>=0) {
  1.1013 +    /*
  1.1014 +     * Note that all levels[] values are still the same at this
  1.1015 +     * point because this function is called for an entire
  1.1016 +     * same-level run.
  1.1017 +     * Therefore, we need to read only one actual level.
  1.1018 +     */
  1.1019 +    nsBidiLevel level=levels[neutralStart], final;
  1.1020 +
  1.1021 +    /* end of a sequence of neutrals (aEOR is "afterNeutral") */
  1.1022 +    if(beforeNeutral==L) {
  1.1023 +      if(aEOR==L) {
  1.1024 +        final=0;                /* make all neutrals L (N1) */
  1.1025 +      } else {
  1.1026 +        final=level;            /* make all neutrals "e" (N2) */
  1.1027 +      }
  1.1028 +    } else /* beforeNeutral is one of { R, EN, AN } */ {
  1.1029 +      if(aEOR==L) {
  1.1030 +        final=level;            /* make all neutrals "e" (N2) */
  1.1031 +      } else {
  1.1032 +        final=1;                /* make all neutrals R (N1) */
  1.1033 +      }
  1.1034 +    }
  1.1035 +    /* perform (In) on the sequence of neutrals */
  1.1036 +    if((level^final)&1) {
  1.1037 +      /* do something only if we need to _change_ the level */
  1.1038 +      do {
  1.1039 +        ++levels[neutralStart];
  1.1040 +      } while(++neutralStart<aLimit);
  1.1041 +    }
  1.1042 +  }
  1.1043 +}
  1.1044 +
  1.1045 +/* perform (L1) and (X9) ---------------------------------------------------- */
  1.1046 +
  1.1047 +/*
  1.1048 + * Reset the embedding levels for some non-graphic characters (L1).
  1.1049 + * This function also sets appropriate levels for BN, and
  1.1050 + * explicit embedding types that are supposed to have been removed
  1.1051 + * from the paragraph in (X9).
  1.1052 + */
  1.1053 +void nsBidi::AdjustWSLevels()
  1.1054 +{
  1.1055 +  const DirProp *dirProps=mDirProps;
  1.1056 +  nsBidiLevel *levels=mLevels;
  1.1057 +  int32_t i;
  1.1058 +
  1.1059 +  if(mFlags&MASK_WS) {
  1.1060 +    nsBidiLevel paraLevel=mParaLevel;
  1.1061 +    Flags flag;
  1.1062 +
  1.1063 +    i=mTrailingWSStart;
  1.1064 +    while(i>0) {
  1.1065 +      /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */
  1.1066 +      while(i>0 && DIRPROP_FLAG(dirProps[--i])&MASK_WS) {
  1.1067 +        levels[i]=paraLevel;
  1.1068 +      }
  1.1069 +
  1.1070 +      /* reset BN to the next character's paraLevel until B/S, which restarts above loop */
  1.1071 +      /* here, i+1 is guaranteed to be <length */
  1.1072 +      while(i>0) {
  1.1073 +        flag=DIRPROP_FLAG(dirProps[--i]);
  1.1074 +        if(flag&MASK_BN_EXPLICIT) {
  1.1075 +          levels[i]=levels[i+1];
  1.1076 +        } else if(flag&MASK_B_S) {
  1.1077 +          levels[i]=paraLevel;
  1.1078 +          break;
  1.1079 +        }
  1.1080 +      }
  1.1081 +    }
  1.1082 +  }
  1.1083 +
  1.1084 +  /* now remove the NSBIDI_LEVEL_OVERRIDE flags, if any */
  1.1085 +  /* (a separate loop can be optimized more easily by a compiler) */
  1.1086 +  if(mFlags&MASK_OVERRIDE) {
  1.1087 +    for(i=mTrailingWSStart; i>0;) {
  1.1088 +      levels[--i]&=~NSBIDI_LEVEL_OVERRIDE;
  1.1089 +    }
  1.1090 +  }
  1.1091 +}
  1.1092 +
  1.1093 +nsresult nsBidi::GetDirection(nsBidiDirection* aDirection)
  1.1094 +{
  1.1095 +  *aDirection = mDirection;
  1.1096 +  return NS_OK;
  1.1097 +}
  1.1098 +
  1.1099 +nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel)
  1.1100 +{
  1.1101 +  *aParaLevel = mParaLevel;
  1.1102 +  return NS_OK;
  1.1103 +}
  1.1104 +#ifdef FULL_BIDI_ENGINE
  1.1105 +
  1.1106 +/* -------------------------------------------------------------------------- */
  1.1107 +
  1.1108 +nsresult nsBidi::GetLength(int32_t* aLength)
  1.1109 +{
  1.1110 +  *aLength = mLength;
  1.1111 +  return NS_OK;
  1.1112 +}
  1.1113 +
  1.1114 +/*
  1.1115 + * General remarks about the functions in this section:
  1.1116 + *
  1.1117 + * These functions deal with the aspects of potentially mixed-directional
  1.1118 + * text in a single paragraph or in a line of a single paragraph
  1.1119 + * which has already been processed according to
  1.1120 + * the Unicode 3.0 Bidi algorithm as defined in
  1.1121 + * http://www.unicode.org/unicode/reports/tr9/ , version 5,
  1.1122 + * also described in The Unicode Standard, Version 3.0 .
  1.1123 + *
  1.1124 + * This means that there is a nsBidi object with a levels
  1.1125 + * and a dirProps array.
  1.1126 + * paraLevel and direction are also set.
  1.1127 + * Only if the length of the text is zero, then levels==dirProps==nullptr.
  1.1128 + *
  1.1129 + * The overall directionality of the paragraph
  1.1130 + * or line is used to bypass the reordering steps if possible.
  1.1131 + * Even purely RTL text does not need reordering there because
  1.1132 + * the getLogical/VisualIndex() functions can compute the
  1.1133 + * index on the fly in such a case.
  1.1134 + *
  1.1135 + * The implementation of the access to same-level-runs and of the reordering
  1.1136 + * do attempt to provide better performance and less memory usage compared to
  1.1137 + * a direct implementation of especially rule (L2) with an array of
  1.1138 + * one (32-bit) integer per text character.
  1.1139 + *
  1.1140 + * Here, the levels array is scanned as soon as necessary, and a vector of
  1.1141 + * same-level-runs is created. Reordering then is done on this vector.
  1.1142 + * For each run of text positions that were resolved to the same level,
  1.1143 + * only 8 bytes are stored: the first text position of the run and the visual
  1.1144 + * position behind the run after reordering.
  1.1145 + * One sign bit is used to hold the directionality of the run.
  1.1146 + * This is inefficient if there are many very short runs. If the average run
  1.1147 + * length is <2, then this uses more memory.
  1.1148 + *
  1.1149 + * In a further attempt to save memory, the levels array is never changed
  1.1150 + * after all the resolution rules (Xn, Wn, Nn, In).
  1.1151 + * Many functions have to consider the field trailingWSStart:
  1.1152 + * if it is less than length, then there is an implicit trailing run
  1.1153 + * at the paraLevel,
  1.1154 + * which is not reflected in the levels array.
  1.1155 + * This allows a line nsBidi object to use the same levels array as
  1.1156 + * its paragraph parent object.
  1.1157 + *
  1.1158 + * When a nsBidi object is created for a line of a paragraph, then the
  1.1159 + * paragraph's levels and dirProps arrays are reused by way of setting
  1.1160 + * a pointer into them, not by copying. This again saves memory and forbids to
  1.1161 + * change the now shared levels for (L1).
  1.1162 + */
  1.1163 +nsresult nsBidi::SetLine(nsIBidi* aParaBidi, int32_t aStart, int32_t aLimit)
  1.1164 +{
  1.1165 +  nsBidi* pParent = (nsBidi*)aParaBidi;
  1.1166 +  int32_t length;
  1.1167 +
  1.1168 +  /* check the argument values */
  1.1169 +  if(pParent==nullptr) {
  1.1170 +    return NS_ERROR_INVALID_POINTER;
  1.1171 +  } else if(aStart<0 || aStart>aLimit || aLimit>pParent->mLength) {
  1.1172 +    return NS_ERROR_INVALID_ARG;
  1.1173 +  }
  1.1174 +
  1.1175 +  /* set members from our aParaBidi parent */
  1.1176 +  length=mLength=aLimit-aStart;
  1.1177 +  mParaLevel=pParent->mParaLevel;
  1.1178 +
  1.1179 +  mRuns=nullptr;
  1.1180 +  mFlags=0;
  1.1181 +
  1.1182 +  if(length>0) {
  1.1183 +    mDirProps=pParent->mDirProps+aStart;
  1.1184 +    mLevels=pParent->mLevels+aStart;
  1.1185 +    mRunCount=-1;
  1.1186 +
  1.1187 +    if(pParent->mDirection!=NSBIDI_MIXED) {
  1.1188 +      /* the parent is already trivial */
  1.1189 +      mDirection=pParent->mDirection;
  1.1190 +
  1.1191 +      /*
  1.1192 +       * The parent's levels are all either
  1.1193 +       * implicitly or explicitly ==paraLevel;
  1.1194 +       * do the same here.
  1.1195 +       */
  1.1196 +      if(pParent->mTrailingWSStart<=aStart) {
  1.1197 +        mTrailingWSStart=0;
  1.1198 +      } else if(pParent->mTrailingWSStart<aLimit) {
  1.1199 +        mTrailingWSStart=pParent->mTrailingWSStart-aStart;
  1.1200 +      } else {
  1.1201 +        mTrailingWSStart=length;
  1.1202 +      }
  1.1203 +    } else {
  1.1204 +      const nsBidiLevel *levels=mLevels;
  1.1205 +      int32_t i, trailingWSStart;
  1.1206 +      nsBidiLevel level;
  1.1207 +      Flags flags=0;
  1.1208 +
  1.1209 +      SetTrailingWSStart();
  1.1210 +      trailingWSStart=mTrailingWSStart;
  1.1211 +
  1.1212 +      /* recalculate pLineBidi->direction */
  1.1213 +      if(trailingWSStart==0) {
  1.1214 +        /* all levels are at paraLevel */
  1.1215 +        mDirection=(nsBidiDirection)(mParaLevel&1);
  1.1216 +      } else {
  1.1217 +        /* get the level of the first character */
  1.1218 +        level=levels[0]&1;
  1.1219 +
  1.1220 +        /* if there is anything of a different level, then the line is mixed */
  1.1221 +        if(trailingWSStart<length && (mParaLevel&1)!=level) {
  1.1222 +          /* the trailing WS is at paraLevel, which differs from levels[0] */
  1.1223 +          mDirection=NSBIDI_MIXED;
  1.1224 +        } else {
  1.1225 +          /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */
  1.1226 +          i=1;
  1.1227 +          for(;;) {
  1.1228 +            if(i==trailingWSStart) {
  1.1229 +              /* the direction values match those in level */
  1.1230 +              mDirection=(nsBidiDirection)level;
  1.1231 +              break;
  1.1232 +            } else if((levels[i]&1)!=level) {
  1.1233 +              mDirection=NSBIDI_MIXED;
  1.1234 +              break;
  1.1235 +            }
  1.1236 +            ++i;
  1.1237 +          }
  1.1238 +        }
  1.1239 +      }
  1.1240 +
  1.1241 +      switch(mDirection) {
  1.1242 +        case NSBIDI_LTR:
  1.1243 +          /* make sure paraLevel is even */
  1.1244 +          mParaLevel=(mParaLevel+1)&~1;
  1.1245 +
  1.1246 +          /* all levels are implicitly at paraLevel (important for GetLevels()) */
  1.1247 +          mTrailingWSStart=0;
  1.1248 +          break;
  1.1249 +        case NSBIDI_RTL:
  1.1250 +          /* make sure paraLevel is odd */
  1.1251 +          mParaLevel|=1;
  1.1252 +
  1.1253 +          /* all levels are implicitly at paraLevel (important for GetLevels()) */
  1.1254 +          mTrailingWSStart=0;
  1.1255 +          break;
  1.1256 +        default:
  1.1257 +          break;
  1.1258 +      }
  1.1259 +    }
  1.1260 +  } else {
  1.1261 +    /* create an object for a zero-length line */
  1.1262 +    mDirection=mParaLevel&1 ? NSBIDI_RTL : NSBIDI_LTR;
  1.1263 +    mTrailingWSStart=mRunCount=0;
  1.1264 +
  1.1265 +    mDirProps=nullptr;
  1.1266 +    mLevels=nullptr;
  1.1267 +  }
  1.1268 +  return NS_OK;
  1.1269 +}
  1.1270 +
  1.1271 +/* handle trailing WS (L1) -------------------------------------------------- */
  1.1272 +
  1.1273 +/*
  1.1274 + * SetTrailingWSStart() sets the start index for a trailing
  1.1275 + * run of WS in the line. This is necessary because we do not modify
  1.1276 + * the paragraph's levels array that we just point into.
  1.1277 + * Using trailingWSStart is another form of performing (L1).
  1.1278 + *
  1.1279 + * To make subsequent operations easier, we also include the run
  1.1280 + * before the WS if it is at the paraLevel - we merge the two here.
  1.1281 + */
  1.1282 +void nsBidi::SetTrailingWSStart() {
  1.1283 +  /* mDirection!=NSBIDI_MIXED */
  1.1284 +
  1.1285 +  const DirProp *dirProps=mDirProps;
  1.1286 +  nsBidiLevel *levels=mLevels;
  1.1287 +  int32_t start=mLength;
  1.1288 +  nsBidiLevel paraLevel=mParaLevel;
  1.1289 +
  1.1290 +  /* go backwards across all WS, BN, explicit codes */
  1.1291 +  while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) {
  1.1292 +    --start;
  1.1293 +  }
  1.1294 +
  1.1295 +  /* if the WS run can be merged with the previous run then do so here */
  1.1296 +  while(start>0 && levels[start-1]==paraLevel) {
  1.1297 +    --start;
  1.1298 +  }
  1.1299 +
  1.1300 +  mTrailingWSStart=start;
  1.1301 +}
  1.1302 +
  1.1303 +nsresult nsBidi::GetLevelAt(int32_t aCharIndex, nsBidiLevel* aLevel)
  1.1304 +{
  1.1305 +  /* return paraLevel if in the trailing WS run, otherwise the real level */
  1.1306 +  if(aCharIndex<0 || mLength<=aCharIndex) {
  1.1307 +    *aLevel = 0;
  1.1308 +  } else if(mDirection!=NSBIDI_MIXED || aCharIndex>=mTrailingWSStart) {
  1.1309 +    *aLevel = mParaLevel;
  1.1310 +  } else {
  1.1311 +    *aLevel = mLevels[aCharIndex];
  1.1312 +  }
  1.1313 +  return NS_OK;
  1.1314 +}
  1.1315 +
  1.1316 +nsresult nsBidi::GetLevels(nsBidiLevel** aLevels)
  1.1317 +{
  1.1318 +  int32_t start, length;
  1.1319 +
  1.1320 +  length = mLength;
  1.1321 +  if(length<=0) {
  1.1322 +    *aLevels = nullptr;
  1.1323 +    return NS_ERROR_INVALID_ARG;
  1.1324 +  }
  1.1325 +
  1.1326 +  start = mTrailingWSStart;
  1.1327 +  if(start==length) {
  1.1328 +    /* the current levels array reflects the WS run */
  1.1329 +    *aLevels = mLevels;
  1.1330 +    return NS_OK;
  1.1331 +  }
  1.1332 +
  1.1333 +  /*
  1.1334 +   * After the previous if(), we know that the levels array
  1.1335 +   * has an implicit trailing WS run and therefore does not fully
  1.1336 +   * reflect itself all the levels.
  1.1337 +   * This must be a nsBidi object for a line, and
  1.1338 +   * we need to create a new levels array.
  1.1339 +   */
  1.1340 +
  1.1341 +  if(GETLEVELSMEMORY(length)) {
  1.1342 +    nsBidiLevel *levels=mLevelsMemory;
  1.1343 +
  1.1344 +    if(start>0 && levels!=mLevels) {
  1.1345 +      memcpy(levels, mLevels, start);
  1.1346 +    }
  1.1347 +    memset(levels+start, mParaLevel, length-start);
  1.1348 +
  1.1349 +    /* this new levels array is set for the line and reflects the WS run */
  1.1350 +    mTrailingWSStart=length;
  1.1351 +    *aLevels=mLevels=levels;
  1.1352 +    return NS_OK;
  1.1353 +  } else {
  1.1354 +    /* out of memory */
  1.1355 +    *aLevels = nullptr;
  1.1356 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1357 +  }
  1.1358 +}
  1.1359 +#endif // FULL_BIDI_ENGINE
  1.1360 +
  1.1361 +nsresult nsBidi::GetCharTypeAt(int32_t aCharIndex, nsCharType* pType)
  1.1362 +{
  1.1363 +  if(aCharIndex<0 || mLength<=aCharIndex) {
  1.1364 +    return NS_ERROR_INVALID_ARG;
  1.1365 +  }
  1.1366 +  *pType = (nsCharType)mDirProps[aCharIndex];
  1.1367 +  return NS_OK;
  1.1368 +}
  1.1369 +
  1.1370 +nsresult nsBidi::GetLogicalRun(int32_t aLogicalStart, int32_t *aLogicalLimit, nsBidiLevel *aLevel)
  1.1371 +{
  1.1372 +  int32_t length = mLength;
  1.1373 +
  1.1374 +  if(aLogicalStart<0 || length<=aLogicalStart) {
  1.1375 +    return NS_ERROR_INVALID_ARG;
  1.1376 +  }
  1.1377 +
  1.1378 +  if(mDirection!=NSBIDI_MIXED || aLogicalStart>=mTrailingWSStart) {
  1.1379 +    if(aLogicalLimit!=nullptr) {
  1.1380 +      *aLogicalLimit=length;
  1.1381 +    }
  1.1382 +    if(aLevel!=nullptr) {
  1.1383 +      *aLevel=mParaLevel;
  1.1384 +    }
  1.1385 +  } else {
  1.1386 +    nsBidiLevel *levels=mLevels;
  1.1387 +    nsBidiLevel level=levels[aLogicalStart];
  1.1388 +
  1.1389 +    /* search for the end of the run */
  1.1390 +    length=mTrailingWSStart;
  1.1391 +    while(++aLogicalStart<length && level==levels[aLogicalStart]) {}
  1.1392 +
  1.1393 +    if(aLogicalLimit!=nullptr) {
  1.1394 +      *aLogicalLimit=aLogicalStart;
  1.1395 +    }
  1.1396 +    if(aLevel!=nullptr) {
  1.1397 +      *aLevel=level;
  1.1398 +    }
  1.1399 +  }
  1.1400 +  return NS_OK;
  1.1401 +}
  1.1402 +
  1.1403 +/* runs API functions ------------------------------------------------------- */
  1.1404 +
  1.1405 +nsresult nsBidi::CountRuns(int32_t* aRunCount)
  1.1406 +{
  1.1407 +  if(mRunCount<0 && !GetRuns()) {
  1.1408 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1409 +  } else {
  1.1410 +    if (aRunCount)
  1.1411 +      *aRunCount = mRunCount;
  1.1412 +    return NS_OK;
  1.1413 +  }
  1.1414 +}
  1.1415 +
  1.1416 +nsresult nsBidi::GetVisualRun(int32_t aRunIndex, int32_t *aLogicalStart, int32_t *aLength, nsBidiDirection *aDirection)
  1.1417 +{
  1.1418 +  if( aRunIndex<0 ||
  1.1419 +      (mRunCount==-1 && !GetRuns()) ||
  1.1420 +      aRunIndex>=mRunCount
  1.1421 +    ) {
  1.1422 +    *aDirection = NSBIDI_LTR;
  1.1423 +    return NS_OK;
  1.1424 +  } else {
  1.1425 +    int32_t start=mRuns[aRunIndex].logicalStart;
  1.1426 +    if(aLogicalStart!=nullptr) {
  1.1427 +      *aLogicalStart=GET_INDEX(start);
  1.1428 +    }
  1.1429 +    if(aLength!=nullptr) {
  1.1430 +      if(aRunIndex>0) {
  1.1431 +        *aLength=mRuns[aRunIndex].visualLimit-
  1.1432 +             mRuns[aRunIndex-1].visualLimit;
  1.1433 +      } else {
  1.1434 +        *aLength=mRuns[0].visualLimit;
  1.1435 +      }
  1.1436 +    }
  1.1437 +    *aDirection = (nsBidiDirection)GET_ODD_BIT(start);
  1.1438 +    return NS_OK;
  1.1439 +  }
  1.1440 +}
  1.1441 +
  1.1442 +/* compute the runs array --------------------------------------------------- */
  1.1443 +
  1.1444 +/*
  1.1445 + * Compute the runs array from the levels array.
  1.1446 + * After GetRuns() returns true, runCount is guaranteed to be >0
  1.1447 + * and the runs are reordered.
  1.1448 + * Odd-level runs have visualStart on their visual right edge and
  1.1449 + * they progress visually to the left.
  1.1450 + */
  1.1451 +bool nsBidi::GetRuns()
  1.1452 +{
  1.1453 +  if(mDirection!=NSBIDI_MIXED) {
  1.1454 +    /* simple, single-run case - this covers length==0 */
  1.1455 +    GetSingleRun(mParaLevel);
  1.1456 +  } else /* NSBIDI_MIXED, length>0 */ {
  1.1457 +    /* mixed directionality */
  1.1458 +    int32_t length=mLength, limit=length;
  1.1459 +
  1.1460 +    /*
  1.1461 +     * If there are WS characters at the end of the line
  1.1462 +     * and the run preceding them has a level different from
  1.1463 +     * paraLevel, then they will form their own run at paraLevel (L1).
  1.1464 +     * Count them separately.
  1.1465 +     * We need some special treatment for this in order to not
  1.1466 +     * modify the levels array which a line nsBidi object shares
  1.1467 +     * with its paragraph parent and its other line siblings.
  1.1468 +     * In other words, for the trailing WS, it may be
  1.1469 +     * levels[]!=paraLevel but we have to treat it like it were so.
  1.1470 +     */
  1.1471 +    limit=mTrailingWSStart;
  1.1472 +    if(limit==0) {
  1.1473 +      /* there is only WS on this line */
  1.1474 +      GetSingleRun(mParaLevel);
  1.1475 +    } else {
  1.1476 +      nsBidiLevel *levels=mLevels;
  1.1477 +      int32_t i, runCount;
  1.1478 +      nsBidiLevel level=NSBIDI_DEFAULT_LTR;   /* initialize with no valid level */
  1.1479 +
  1.1480 +      /* count the runs, there is at least one non-WS run, and limit>0 */
  1.1481 +      runCount=0;
  1.1482 +      for(i=0; i<limit; ++i) {
  1.1483 +        /* increment runCount at the start of each run */
  1.1484 +        if(levels[i]!=level) {
  1.1485 +          ++runCount;
  1.1486 +          level=levels[i];
  1.1487 +        }
  1.1488 +      }
  1.1489 +
  1.1490 +      /*
  1.1491 +       * We don't need to see if the last run can be merged with a trailing
  1.1492 +       * WS run because SetTrailingWSStart() would have done that.
  1.1493 +       */
  1.1494 +      if(runCount==1 && limit==length) {
  1.1495 +        /* There is only one non-WS run and no trailing WS-run. */
  1.1496 +        GetSingleRun(levels[0]);
  1.1497 +      } else /* runCount>1 || limit<length */ {
  1.1498 +        /* allocate and set the runs */
  1.1499 +        Run *runs;
  1.1500 +        int32_t runIndex, start;
  1.1501 +        nsBidiLevel minLevel=NSBIDI_MAX_EXPLICIT_LEVEL+1, maxLevel=0;
  1.1502 +
  1.1503 +        /* now, count a (non-mergable) WS run */
  1.1504 +        if(limit<length) {
  1.1505 +          ++runCount;
  1.1506 +        }
  1.1507 +
  1.1508 +        /* runCount>1 */
  1.1509 +        if(GETRUNSMEMORY(runCount)) {
  1.1510 +          runs=mRunsMemory;
  1.1511 +        } else {
  1.1512 +          return false;
  1.1513 +        }
  1.1514 +
  1.1515 +        /* set the runs */
  1.1516 +        /* this could be optimized, e.g.: 464->444, 484->444, 575->555, 595->555 */
  1.1517 +        /* however, that would take longer and make other functions more complicated */
  1.1518 +        runIndex=0;
  1.1519 +
  1.1520 +        /* search for the run ends */
  1.1521 +        start=0;
  1.1522 +        level=levels[0];
  1.1523 +        if(level<minLevel) {
  1.1524 +          minLevel=level;
  1.1525 +        }
  1.1526 +        if(level>maxLevel) {
  1.1527 +          maxLevel=level;
  1.1528 +        }
  1.1529 +
  1.1530 +        /* initialize visualLimit values with the run lengths */
  1.1531 +        for(i=1; i<limit; ++i) {
  1.1532 +          if(levels[i]!=level) {
  1.1533 +            /* i is another run limit */
  1.1534 +            runs[runIndex].logicalStart=start;
  1.1535 +            runs[runIndex].visualLimit=i-start;
  1.1536 +            start=i;
  1.1537 +
  1.1538 +            level=levels[i];
  1.1539 +            if(level<minLevel) {
  1.1540 +              minLevel=level;
  1.1541 +            }
  1.1542 +            if(level>maxLevel) {
  1.1543 +              maxLevel=level;
  1.1544 +            }
  1.1545 +            ++runIndex;
  1.1546 +          }
  1.1547 +        }
  1.1548 +
  1.1549 +        /* finish the last run at i==limit */
  1.1550 +        runs[runIndex].logicalStart=start;
  1.1551 +        runs[runIndex].visualLimit=limit-start;
  1.1552 +        ++runIndex;
  1.1553 +
  1.1554 +        if(limit<length) {
  1.1555 +          /* there is a separate WS run */
  1.1556 +          runs[runIndex].logicalStart=limit;
  1.1557 +          runs[runIndex].visualLimit=length-limit;
  1.1558 +          if(mParaLevel<minLevel) {
  1.1559 +            minLevel=mParaLevel;
  1.1560 +          }
  1.1561 +        }
  1.1562 +
  1.1563 +        /* set the object fields */
  1.1564 +        mRuns=runs;
  1.1565 +        mRunCount=runCount;
  1.1566 +
  1.1567 +        ReorderLine(minLevel, maxLevel);
  1.1568 +
  1.1569 +        /* now add the direction flags and adjust the visualLimit's to be just that */
  1.1570 +        ADD_ODD_BIT_FROM_LEVEL(runs[0].logicalStart, levels[runs[0].logicalStart]);
  1.1571 +        limit=runs[0].visualLimit;
  1.1572 +        for(i=1; i<runIndex; ++i) {
  1.1573 +          ADD_ODD_BIT_FROM_LEVEL(runs[i].logicalStart, levels[runs[i].logicalStart]);
  1.1574 +          limit=runs[i].visualLimit+=limit;
  1.1575 +        }
  1.1576 +
  1.1577 +        /* same for the trailing WS run */
  1.1578 +        if(runIndex<runCount) {
  1.1579 +          ADD_ODD_BIT_FROM_LEVEL(runs[i].logicalStart, mParaLevel);
  1.1580 +          runs[runIndex].visualLimit+=limit;
  1.1581 +        }
  1.1582 +      }
  1.1583 +    }
  1.1584 +  }
  1.1585 +  return true;
  1.1586 +}
  1.1587 +
  1.1588 +/* in trivial cases there is only one trivial run; called by GetRuns() */
  1.1589 +void nsBidi::GetSingleRun(nsBidiLevel aLevel)
  1.1590 +{
  1.1591 +  /* simple, single-run case */
  1.1592 +  mRuns=mSimpleRuns;
  1.1593 +  mRunCount=1;
  1.1594 +
  1.1595 +  /* fill and reorder the single run */
  1.1596 +  mRuns[0].logicalStart=MAKE_INDEX_ODD_PAIR(0, aLevel);
  1.1597 +  mRuns[0].visualLimit=mLength;
  1.1598 +}
  1.1599 +
  1.1600 +/* reorder the runs array (L2) ---------------------------------------------- */
  1.1601 +
  1.1602 +/*
  1.1603 + * Reorder the same-level runs in the runs array.
  1.1604 + * Here, runCount>1 and maxLevel>=minLevel>=paraLevel.
  1.1605 + * All the visualStart fields=logical start before reordering.
  1.1606 + * The "odd" bits are not set yet.
  1.1607 + *
  1.1608 + * Reordering with this data structure lends itself to some handy shortcuts:
  1.1609 + *
  1.1610 + * Since each run is moved but not modified, and since at the initial maxLevel
  1.1611 + * each sequence of same-level runs consists of only one run each, we
  1.1612 + * don't need to do anything there and can predecrement maxLevel.
  1.1613 + * In many simple cases, the reordering is thus done entirely in the
  1.1614 + * index mapping.
  1.1615 + * Also, reordering occurs only down to the lowest odd level that occurs,
  1.1616 + * which is minLevel|1. However, if the lowest level itself is odd, then
  1.1617 + * in the last reordering the sequence of the runs at this level or higher
  1.1618 + * will be all runs, and we don't need the elaborate loop to search for them.
  1.1619 + * This is covered by ++minLevel instead of minLevel|=1 followed
  1.1620 + * by an extra reorder-all after the reorder-some loop.
  1.1621 + * About a trailing WS run:
  1.1622 + * Such a run would need special treatment because its level is not
  1.1623 + * reflected in levels[] if this is not a paragraph object.
  1.1624 + * Instead, all characters from trailingWSStart on are implicitly at
  1.1625 + * paraLevel.
  1.1626 + * However, for all maxLevel>paraLevel, this run will never be reordered
  1.1627 + * and does not need to be taken into account. maxLevel==paraLevel is only reordered
  1.1628 + * if minLevel==paraLevel is odd, which is done in the extra segment.
  1.1629 + * This means that for the main reordering loop we don't need to consider
  1.1630 + * this run and can --runCount. If it is later part of the all-runs
  1.1631 + * reordering, then runCount is adjusted accordingly.
  1.1632 + */
  1.1633 +void nsBidi::ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel)
  1.1634 +{
  1.1635 +  Run *runs;
  1.1636 +  nsBidiLevel *levels;
  1.1637 +  int32_t firstRun, endRun, limitRun, runCount, temp;
  1.1638 +
  1.1639 +  /* nothing to do? */
  1.1640 +  if(aMaxLevel<=(aMinLevel|1)) {
  1.1641 +    return;
  1.1642 +  }
  1.1643 +
  1.1644 +  /*
  1.1645 +   * Reorder only down to the lowest odd level
  1.1646 +   * and reorder at an odd aMinLevel in a separate, simpler loop.
  1.1647 +   * See comments above for why aMinLevel is always incremented.
  1.1648 +   */
  1.1649 +  ++aMinLevel;
  1.1650 +
  1.1651 +  runs=mRuns;
  1.1652 +  levels=mLevels;
  1.1653 +  runCount=mRunCount;
  1.1654 +
  1.1655 +  /* do not include the WS run at paraLevel<=old aMinLevel except in the simple loop */
  1.1656 +  if(mTrailingWSStart<mLength) {
  1.1657 +    --runCount;
  1.1658 +  }
  1.1659 +
  1.1660 +  while(--aMaxLevel>=aMinLevel) {
  1.1661 +    firstRun=0;
  1.1662 +
  1.1663 +    /* loop for all sequences of runs */
  1.1664 +    for(;;) {
  1.1665 +      /* look for a sequence of runs that are all at >=aMaxLevel */
  1.1666 +      /* look for the first run of such a sequence */
  1.1667 +      while(firstRun<runCount && levels[runs[firstRun].logicalStart]<aMaxLevel) {
  1.1668 +        ++firstRun;
  1.1669 +      }
  1.1670 +      if(firstRun>=runCount) {
  1.1671 +        break;  /* no more such runs */
  1.1672 +      }
  1.1673 +
  1.1674 +      /* look for the limit run of such a sequence (the run behind it) */
  1.1675 +      for(limitRun=firstRun; ++limitRun<runCount && levels[runs[limitRun].logicalStart]>=aMaxLevel;) {}
  1.1676 +
  1.1677 +      /* Swap the entire sequence of runs from firstRun to limitRun-1. */
  1.1678 +      endRun=limitRun-1;
  1.1679 +      while(firstRun<endRun) {
  1.1680 +        temp=runs[firstRun].logicalStart;
  1.1681 +        runs[firstRun].logicalStart=runs[endRun].logicalStart;
  1.1682 +        runs[endRun].logicalStart=temp;
  1.1683 +
  1.1684 +        temp=runs[firstRun].visualLimit;
  1.1685 +        runs[firstRun].visualLimit=runs[endRun].visualLimit;
  1.1686 +        runs[endRun].visualLimit=temp;
  1.1687 +
  1.1688 +        ++firstRun;
  1.1689 +        --endRun;
  1.1690 +      }
  1.1691 +
  1.1692 +      if(limitRun==runCount) {
  1.1693 +        break;  /* no more such runs */
  1.1694 +      } else {
  1.1695 +        firstRun=limitRun+1;
  1.1696 +      }
  1.1697 +    }
  1.1698 +  }
  1.1699 +
  1.1700 +  /* now do aMaxLevel==old aMinLevel (==odd!), see above */
  1.1701 +  if(!(aMinLevel&1)) {
  1.1702 +    firstRun=0;
  1.1703 +
  1.1704 +    /* include the trailing WS run in this complete reordering */
  1.1705 +    if(mTrailingWSStart==mLength) {
  1.1706 +      --runCount;
  1.1707 +    }
  1.1708 +
  1.1709 +    /* Swap the entire sequence of all runs. (endRun==runCount) */
  1.1710 +    while(firstRun<runCount) {
  1.1711 +      temp=runs[firstRun].logicalStart;
  1.1712 +      runs[firstRun].logicalStart=runs[runCount].logicalStart;
  1.1713 +      runs[runCount].logicalStart=temp;
  1.1714 +
  1.1715 +      temp=runs[firstRun].visualLimit;
  1.1716 +      runs[firstRun].visualLimit=runs[runCount].visualLimit;
  1.1717 +      runs[runCount].visualLimit=temp;
  1.1718 +
  1.1719 +      ++firstRun;
  1.1720 +      --runCount;
  1.1721 +    }
  1.1722 +  }
  1.1723 +}
  1.1724 +
  1.1725 +nsresult nsBidi::ReorderVisual(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap)
  1.1726 +{
  1.1727 +  int32_t start, end, limit, temp;
  1.1728 +  nsBidiLevel minLevel, maxLevel;
  1.1729 +
  1.1730 +  if(aIndexMap==nullptr ||
  1.1731 +     !PrepareReorder(aLevels, aLength, aIndexMap, &minLevel, &maxLevel)) {
  1.1732 +    return NS_OK;
  1.1733 +  }
  1.1734 +
  1.1735 +  /* nothing to do? */
  1.1736 +  if(minLevel==maxLevel && (minLevel&1)==0) {
  1.1737 +    return NS_OK;
  1.1738 +  }
  1.1739 +
  1.1740 +  /* reorder only down to the lowest odd level */
  1.1741 +  minLevel|=1;
  1.1742 +
  1.1743 +  /* loop maxLevel..minLevel */
  1.1744 +  do {
  1.1745 +    start=0;
  1.1746 +
  1.1747 +    /* loop for all sequences of levels to reorder at the current maxLevel */
  1.1748 +    for(;;) {
  1.1749 +      /* look for a sequence of levels that are all at >=maxLevel */
  1.1750 +      /* look for the first index of such a sequence */
  1.1751 +      while(start<aLength && aLevels[start]<maxLevel) {
  1.1752 +        ++start;
  1.1753 +      }
  1.1754 +      if(start>=aLength) {
  1.1755 +        break;  /* no more such runs */
  1.1756 +      }
  1.1757 +
  1.1758 +      /* look for the limit of such a sequence (the index behind it) */
  1.1759 +      for(limit=start; ++limit<aLength && aLevels[limit]>=maxLevel;) {}
  1.1760 +
  1.1761 +      /*
  1.1762 +       * Swap the entire interval of indexes from start to limit-1.
  1.1763 +       * We don't need to swap the levels for the purpose of this
  1.1764 +       * algorithm: the sequence of levels that we look at does not
  1.1765 +       * move anyway.
  1.1766 +       */
  1.1767 +      end=limit-1;
  1.1768 +      while(start<end) {
  1.1769 +        temp=aIndexMap[start];
  1.1770 +        aIndexMap[start]=aIndexMap[end];
  1.1771 +        aIndexMap[end]=temp;
  1.1772 +
  1.1773 +        ++start;
  1.1774 +        --end;
  1.1775 +      }
  1.1776 +
  1.1777 +      if(limit==aLength) {
  1.1778 +        break;  /* no more such sequences */
  1.1779 +      } else {
  1.1780 +        start=limit+1;
  1.1781 +      }
  1.1782 +    }
  1.1783 +  } while(--maxLevel>=minLevel);
  1.1784 +
  1.1785 +  return NS_OK;
  1.1786 +}
  1.1787 +
  1.1788 +bool nsBidi::PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength,
  1.1789 +                int32_t *aIndexMap,
  1.1790 +                nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel)
  1.1791 +{
  1.1792 +  int32_t start;
  1.1793 +  nsBidiLevel level, minLevel, maxLevel;
  1.1794 +
  1.1795 +  if(aLevels==nullptr || aLength<=0) {
  1.1796 +    return false;
  1.1797 +  }
  1.1798 +
  1.1799 +  /* determine minLevel and maxLevel */
  1.1800 +  minLevel=NSBIDI_MAX_EXPLICIT_LEVEL+1;
  1.1801 +  maxLevel=0;
  1.1802 +  for(start=aLength; start>0;) {
  1.1803 +    level=aLevels[--start];
  1.1804 +    if(level>NSBIDI_MAX_EXPLICIT_LEVEL+1) {
  1.1805 +      return false;
  1.1806 +    }
  1.1807 +    if(level<minLevel) {
  1.1808 +      minLevel=level;
  1.1809 +    }
  1.1810 +    if(level>maxLevel) {
  1.1811 +      maxLevel=level;
  1.1812 +    }
  1.1813 +  }
  1.1814 +  *aMinLevel=minLevel;
  1.1815 +  *aMaxLevel=maxLevel;
  1.1816 +
  1.1817 +  /* initialize the index map */
  1.1818 +  for(start=aLength; start>0;) {
  1.1819 +    --start;
  1.1820 +    aIndexMap[start]=start;
  1.1821 +  }
  1.1822 +
  1.1823 +  return true;
  1.1824 +}
  1.1825 +
  1.1826 +#ifdef FULL_BIDI_ENGINE
  1.1827 +/* API functions for logical<->visual mapping ------------------------------- */
  1.1828 +
  1.1829 +nsresult nsBidi::GetVisualIndex(int32_t aLogicalIndex, int32_t* aVisualIndex) {
  1.1830 +  if(aLogicalIndex<0 || mLength<=aLogicalIndex) {
  1.1831 +    return NS_ERROR_INVALID_ARG;
  1.1832 +  } else {
  1.1833 +    /* we can do the trivial cases without the runs array */
  1.1834 +    switch(mDirection) {
  1.1835 +      case NSBIDI_LTR:
  1.1836 +        *aVisualIndex = aLogicalIndex;
  1.1837 +        return NS_OK;
  1.1838 +      case NSBIDI_RTL:
  1.1839 +        *aVisualIndex = mLength-aLogicalIndex-1;
  1.1840 +        return NS_OK;
  1.1841 +      default:
  1.1842 +        if(mRunCount<0 && !GetRuns()) {
  1.1843 +          return NS_ERROR_OUT_OF_MEMORY;
  1.1844 +        } else {
  1.1845 +          Run *runs=mRuns;
  1.1846 +          int32_t i, visualStart=0, offset, length;
  1.1847 +
  1.1848 +          /* linear search for the run, search on the visual runs */
  1.1849 +          for(i=0;; ++i) {
  1.1850 +            length=runs[i].visualLimit-visualStart;
  1.1851 +            offset=aLogicalIndex-GET_INDEX(runs[i].logicalStart);
  1.1852 +            if(offset>=0 && offset<length) {
  1.1853 +              if(IS_EVEN_RUN(runs[i].logicalStart)) {
  1.1854 +                /* LTR */
  1.1855 +                *aVisualIndex = visualStart+offset;
  1.1856 +                return NS_OK;
  1.1857 +              } else {
  1.1858 +                /* RTL */
  1.1859 +                *aVisualIndex = visualStart+length-offset-1;
  1.1860 +                return NS_OK;
  1.1861 +              }
  1.1862 +            }
  1.1863 +            visualStart+=length;
  1.1864 +          }
  1.1865 +        }
  1.1866 +    }
  1.1867 +  }
  1.1868 +}
  1.1869 +
  1.1870 +nsresult nsBidi::GetLogicalIndex(int32_t aVisualIndex, int32_t *aLogicalIndex)
  1.1871 +{
  1.1872 +  if(aVisualIndex<0 || mLength<=aVisualIndex) {
  1.1873 +    return NS_ERROR_INVALID_ARG;
  1.1874 +  } else {
  1.1875 +    /* we can do the trivial cases without the runs array */
  1.1876 +    switch(mDirection) {
  1.1877 +      case NSBIDI_LTR:
  1.1878 +        *aLogicalIndex = aVisualIndex;
  1.1879 +        return NS_OK;
  1.1880 +      case NSBIDI_RTL:
  1.1881 +        *aLogicalIndex = mLength-aVisualIndex-1;
  1.1882 +        return NS_OK;
  1.1883 +      default:
  1.1884 +        if(mRunCount<0 && !GetRuns()) {
  1.1885 +          return NS_ERROR_OUT_OF_MEMORY;
  1.1886 +        } else {
  1.1887 +          Run *runs=mRuns;
  1.1888 +          int32_t i, runCount=mRunCount, start;
  1.1889 +
  1.1890 +          if(runCount<=10) {
  1.1891 +            /* linear search for the run */
  1.1892 +            for(i=0; aVisualIndex>=runs[i].visualLimit; ++i) {}
  1.1893 +          } else {
  1.1894 +            /* binary search for the run */
  1.1895 +            int32_t start=0, limit=runCount;
  1.1896 +
  1.1897 +            /* the middle if() will guaranteed find the run, we don't need a loop limit */
  1.1898 +            for(;;) {
  1.1899 +              i=(start+limit)/2;
  1.1900 +              if(aVisualIndex>=runs[i].visualLimit) {
  1.1901 +                start=i+1;
  1.1902 +              } else if(i==0 || aVisualIndex>=runs[i-1].visualLimit) {
  1.1903 +                break;
  1.1904 +              } else {
  1.1905 +                limit=i;
  1.1906 +              }
  1.1907 +            }
  1.1908 +          }
  1.1909 +
  1.1910 +          start=runs[i].logicalStart;
  1.1911 +          if(IS_EVEN_RUN(start)) {
  1.1912 +            /* LTR */
  1.1913 +            /* the offset in runs[i] is aVisualIndex-runs[i-1].visualLimit */
  1.1914 +            if(i>0) {
  1.1915 +              aVisualIndex-=runs[i-1].visualLimit;
  1.1916 +            }
  1.1917 +            *aLogicalIndex = GET_INDEX(start)+aVisualIndex;
  1.1918 +            return NS_OK;
  1.1919 +          } else {
  1.1920 +            /* RTL */
  1.1921 +            *aLogicalIndex = GET_INDEX(start)+runs[i].visualLimit-aVisualIndex-1;
  1.1922 +            return NS_OK;
  1.1923 +          }
  1.1924 +        }
  1.1925 +    }
  1.1926 +  }
  1.1927 +}
  1.1928 +
  1.1929 +nsresult nsBidi::GetLogicalMap(int32_t *aIndexMap)
  1.1930 +{
  1.1931 +  nsBidiLevel *levels;
  1.1932 +  nsresult rv;
  1.1933 +
  1.1934 +  /* GetLevels() checks all of its and our arguments */
  1.1935 +  rv = GetLevels(&levels);
  1.1936 +  if(NS_FAILED(rv)) {
  1.1937 +    return rv;
  1.1938 +  } else if(aIndexMap==nullptr) {
  1.1939 +    return NS_ERROR_INVALID_ARG;
  1.1940 +  } else {
  1.1941 +    return ReorderLogical(levels, mLength, aIndexMap);
  1.1942 +  }
  1.1943 +}
  1.1944 +
  1.1945 +nsresult nsBidi::GetVisualMap(int32_t *aIndexMap)
  1.1946 +{
  1.1947 +  int32_t* runCount=nullptr;
  1.1948 +  nsresult rv;
  1.1949 +
  1.1950 +  /* CountRuns() checks all of its and our arguments */
  1.1951 +  rv = CountRuns(runCount);
  1.1952 +  if(NS_FAILED(rv)) {
  1.1953 +    return rv;
  1.1954 +  } else if(aIndexMap==nullptr) {
  1.1955 +    return NS_ERROR_INVALID_ARG;
  1.1956 +  } else {
  1.1957 +    /* fill a visual-to-logical index map using the runs[] */
  1.1958 +    Run *runs=mRuns, *runsLimit=runs+mRunCount;
  1.1959 +    int32_t logicalStart, visualStart, visualLimit;
  1.1960 +
  1.1961 +    visualStart=0;
  1.1962 +    for(; runs<runsLimit; ++runs) {
  1.1963 +      logicalStart=runs->logicalStart;
  1.1964 +      visualLimit=runs->visualLimit;
  1.1965 +      if(IS_EVEN_RUN(logicalStart)) {
  1.1966 +        do { /* LTR */
  1.1967 +          *aIndexMap++ = logicalStart++;
  1.1968 +        } while(++visualStart<visualLimit);
  1.1969 +      } else {
  1.1970 +        REMOVE_ODD_BIT(logicalStart);
  1.1971 +        logicalStart+=visualLimit-visualStart;  /* logicalLimit */
  1.1972 +        do { /* RTL */
  1.1973 +          *aIndexMap++ = --logicalStart;
  1.1974 +        } while(++visualStart<visualLimit);
  1.1975 +      }
  1.1976 +      /* visualStart==visualLimit; */
  1.1977 +    }
  1.1978 +    return NS_OK;
  1.1979 +  }
  1.1980 +}
  1.1981 +
  1.1982 +/* reorder a line based on a levels array (L2) ------------------------------ */
  1.1983 +
  1.1984 +nsresult nsBidi::ReorderLogical(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap)
  1.1985 +{
  1.1986 +  int32_t start, limit, sumOfSosEos;
  1.1987 +  nsBidiLevel minLevel, maxLevel;
  1.1988 +
  1.1989 +  if(aIndexMap==nullptr ||
  1.1990 +     !PrepareReorder(aLevels, aLength, aIndexMap, &minLevel, &maxLevel)) {
  1.1991 +    return NS_OK;
  1.1992 +  }
  1.1993 +
  1.1994 +  /* nothing to do? */
  1.1995 +  if(minLevel==maxLevel && (minLevel&1)==0) {
  1.1996 +    return NS_OK;
  1.1997 +  }
  1.1998 +
  1.1999 +  /* reorder only down to the lowest odd level */
  1.2000 +  minLevel|=1;
  1.2001 +
  1.2002 +  /* loop maxLevel..minLevel */
  1.2003 +  do {
  1.2004 +    start=0;
  1.2005 +
  1.2006 +    /* loop for all sequences of levels to reorder at the current maxLevel */
  1.2007 +    for(;;) {
  1.2008 +      /* look for a sequence of levels that are all at >=maxLevel */
  1.2009 +      /* look for the first index of such a sequence */
  1.2010 +      while(start<aLength && aLevels[start]<maxLevel) {
  1.2011 +        ++start;
  1.2012 +      }
  1.2013 +      if(start>=aLength) {
  1.2014 +        break;  /* no more such sequences */
  1.2015 +      }
  1.2016 +
  1.2017 +      /* look for the limit of such a sequence (the index behind it) */
  1.2018 +      for(limit=start; ++limit<aLength && aLevels[limit]>=maxLevel;) {}
  1.2019 +
  1.2020 +      /*
  1.2021 +       * sos=start of sequence, eos=end of sequence
  1.2022 +       *
  1.2023 +       * The closed (inclusive) interval from sos to eos includes all the logical
  1.2024 +       * and visual indexes within this sequence. They are logically and
  1.2025 +       * visually contiguous and in the same range.
  1.2026 +       *
  1.2027 +       * For each run, the new visual index=sos+eos-old visual index;
  1.2028 +       * we pre-add sos+eos into sumOfSosEos ->
  1.2029 +       * new visual index=sumOfSosEos-old visual index;
  1.2030 +       */
  1.2031 +      sumOfSosEos=start+limit-1;
  1.2032 +
  1.2033 +      /* reorder each index in the sequence */
  1.2034 +      do {
  1.2035 +        aIndexMap[start]=sumOfSosEos-aIndexMap[start];
  1.2036 +      } while(++start<limit);
  1.2037 +
  1.2038 +      /* start==limit */
  1.2039 +      if(limit==aLength) {
  1.2040 +        break;  /* no more such sequences */
  1.2041 +      } else {
  1.2042 +        start=limit+1;
  1.2043 +      }
  1.2044 +    }
  1.2045 +  } while(--maxLevel>=minLevel);
  1.2046 +
  1.2047 +  return NS_OK;
  1.2048 +}
  1.2049 +
  1.2050 +nsresult nsBidi::InvertMap(const int32_t *aSrcMap, int32_t *aDestMap, int32_t aLength)
  1.2051 +{
  1.2052 +  if(aSrcMap!=nullptr && aDestMap!=nullptr) {
  1.2053 +    aSrcMap+=aLength;
  1.2054 +    while(aLength>0) {
  1.2055 +      aDestMap[*--aSrcMap]=--aLength;
  1.2056 +    }
  1.2057 +  }
  1.2058 +  return NS_OK;
  1.2059 +}
  1.2060 +
  1.2061 +int32_t nsBidi::doWriteReverse(const char16_t *src, int32_t srcLength,
  1.2062 +                               char16_t *dest, uint16_t options) {
  1.2063 +  /*
  1.2064 +   * RTL run -
  1.2065 +   *
  1.2066 +   * RTL runs need to be copied to the destination in reverse order
  1.2067 +   * of code points, not code units, to keep Unicode characters intact.
  1.2068 +   *
  1.2069 +   * The general strategy for this is to read the source text
  1.2070 +   * in backward order, collect all code units for a code point
  1.2071 +   * (and optionally following combining characters, see below),
  1.2072 +   * and copy all these code units in ascending order
  1.2073 +   * to the destination for this run.
  1.2074 +   *
  1.2075 +   * Several options request whether combining characters
  1.2076 +   * should be kept after their base characters,
  1.2077 +   * whether Bidi control characters should be removed, and
  1.2078 +   * whether characters should be replaced by their mirror-image
  1.2079 +   * equivalent Unicode characters.
  1.2080 +   */
  1.2081 +  int32_t i, j, destSize;
  1.2082 +  uint32_t c;
  1.2083 +
  1.2084 +  /* optimize for several combinations of options */
  1.2085 +  switch(options&(NSBIDI_REMOVE_BIDI_CONTROLS|NSBIDI_DO_MIRRORING|NSBIDI_KEEP_BASE_COMBINING)) {
  1.2086 +    case 0:
  1.2087 +    /*
  1.2088 +         * With none of the "complicated" options set, the destination
  1.2089 +         * run will have the same length as the source run,
  1.2090 +         * and there is no mirroring and no keeping combining characters
  1.2091 +         * with their base characters.
  1.2092 +         */
  1.2093 +      destSize=srcLength;
  1.2094 +
  1.2095 +    /* preserve character integrity */
  1.2096 +      do {
  1.2097 +      /* i is always after the last code unit known to need to be kept in this segment */
  1.2098 +        i=srcLength;
  1.2099 +
  1.2100 +      /* collect code units for one base character */
  1.2101 +        UTF_BACK_1(src, 0, srcLength);
  1.2102 +
  1.2103 +      /* copy this base character */
  1.2104 +        j=srcLength;
  1.2105 +        do {
  1.2106 +          *dest++=src[j++];
  1.2107 +        } while(j<i);
  1.2108 +      } while(srcLength>0);
  1.2109 +      break;
  1.2110 +    case NSBIDI_KEEP_BASE_COMBINING:
  1.2111 +    /*
  1.2112 +         * Here, too, the destination
  1.2113 +         * run will have the same length as the source run,
  1.2114 +         * and there is no mirroring.
  1.2115 +         * We do need to keep combining characters with their base characters.
  1.2116 +         */
  1.2117 +      destSize=srcLength;
  1.2118 +
  1.2119 +    /* preserve character integrity */
  1.2120 +      do {
  1.2121 +      /* i is always after the last code unit known to need to be kept in this segment */
  1.2122 +        i=srcLength;
  1.2123 +
  1.2124 +      /* collect code units and modifier letters for one base character */
  1.2125 +        do {
  1.2126 +          UTF_PREV_CHAR(src, 0, srcLength, c);
  1.2127 +        } while(srcLength>0 && IsBidiCategory(c, eBidiCat_NSM));
  1.2128 +
  1.2129 +      /* copy this "user character" */
  1.2130 +        j=srcLength;
  1.2131 +        do {
  1.2132 +          *dest++=src[j++];
  1.2133 +        } while(j<i);
  1.2134 +      } while(srcLength>0);
  1.2135 +      break;
  1.2136 +    default:
  1.2137 +    /*
  1.2138 +         * With several "complicated" options set, this is the most
  1.2139 +         * general and the slowest copying of an RTL run.
  1.2140 +         * We will do mirroring, remove Bidi controls, and
  1.2141 +         * keep combining characters with their base characters
  1.2142 +         * as requested.
  1.2143 +         */
  1.2144 +      if(!(options&NSBIDI_REMOVE_BIDI_CONTROLS)) {
  1.2145 +        i=srcLength;
  1.2146 +      } else {
  1.2147 +      /* we need to find out the destination length of the run,
  1.2148 +               which will not include the Bidi control characters */
  1.2149 +        int32_t length=srcLength;
  1.2150 +        char16_t ch;
  1.2151 +
  1.2152 +        i=0;
  1.2153 +        do {
  1.2154 +          ch=*src++;
  1.2155 +          if (!IsBidiControl((uint32_t)ch)) {
  1.2156 +            ++i;
  1.2157 +          }
  1.2158 +        } while(--length>0);
  1.2159 +        src-=srcLength;
  1.2160 +      }
  1.2161 +      destSize=i;
  1.2162 +
  1.2163 +    /* preserve character integrity */
  1.2164 +      do {
  1.2165 +      /* i is always after the last code unit known to need to be kept in this segment */
  1.2166 +        i=srcLength;
  1.2167 +
  1.2168 +      /* collect code units for one base character */
  1.2169 +        UTF_PREV_CHAR(src, 0, srcLength, c);
  1.2170 +        if(options&NSBIDI_KEEP_BASE_COMBINING) {
  1.2171 +        /* collect modifier letters for this base character */
  1.2172 +          while(srcLength>0 && IsBidiCategory(c, eBidiCat_NSM)) {
  1.2173 +            UTF_PREV_CHAR(src, 0, srcLength, c);
  1.2174 +          }
  1.2175 +        }
  1.2176 +
  1.2177 +        if(options&NSBIDI_REMOVE_BIDI_CONTROLS && IsBidiControl(c)) {
  1.2178 +        /* do not copy this Bidi control character */
  1.2179 +          continue;
  1.2180 +        }
  1.2181 +
  1.2182 +      /* copy this "user character" */
  1.2183 +        j=srcLength;
  1.2184 +        if(options&NSBIDI_DO_MIRRORING) {
  1.2185 +          /* mirror only the base character */
  1.2186 +          c = SymmSwap(c);
  1.2187 +
  1.2188 +          int32_t k=0;
  1.2189 +          UTF_APPEND_CHAR_UNSAFE(dest, k, c);
  1.2190 +          dest+=k;
  1.2191 +          j+=k;
  1.2192 +        }
  1.2193 +        while(j<i) {
  1.2194 +          *dest++=src[j++];
  1.2195 +        }
  1.2196 +      } while(srcLength>0);
  1.2197 +      break;
  1.2198 +  } /* end of switch */
  1.2199 +  return destSize;
  1.2200 +}
  1.2201 +
  1.2202 +nsresult nsBidi::WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize)
  1.2203 +{
  1.2204 +  if( aSrc==nullptr || aSrcLength<0 ||
  1.2205 +      aDest==nullptr
  1.2206 +    ) {
  1.2207 +    return NS_ERROR_INVALID_ARG;
  1.2208 +  }
  1.2209 +
  1.2210 +  /* do input and output overlap? */
  1.2211 +  if( aSrc>=aDest && aSrc<aDest+aSrcLength ||
  1.2212 +      aDest>=aSrc && aDest<aSrc+aSrcLength
  1.2213 +    ) {
  1.2214 +    return NS_ERROR_INVALID_ARG;
  1.2215 +  }
  1.2216 +
  1.2217 +  if(aSrcLength>0) {
  1.2218 +    *aDestSize = doWriteReverse(aSrc, aSrcLength, aDest, aOptions);
  1.2219 +  }
  1.2220 +  return NS_OK;
  1.2221 +}
  1.2222 +#endif // FULL_BIDI_ENGINE

mercurial