tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Bidi.cpp (6670B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "mozilla/intl/Bidi.h"
      6 #include "mozilla/Casting.h"
      7 #include "mozilla/intl/ICU4CGlue.h"
      8 
      9 #if !USE_RUST_UNICODE_BIDI
     10 #  include "unicode/ubidi.h"
     11 #endif
     12 
     13 namespace mozilla::intl {
     14 
     15 #if USE_RUST_UNICODE_BIDI
     16 using namespace ffi;
     17 
     18 Bidi::Bidi() = default;
     19 Bidi::~Bidi() = default;
     20 #else
     21 Bidi::Bidi() { mBidi = ubidi_open(); }
     22 Bidi::~Bidi() { ubidi_close(mBidi.GetMut()); }
     23 #endif
     24 
     25 ICUResult Bidi::SetParagraph(Span<const char16_t> aParagraph,
     26                             BidiEmbeddingLevel aLevel) {
     27 #if USE_RUST_UNICODE_BIDI
     28  const auto* text = reinterpret_cast<const uint16_t*>(aParagraph.Elements());
     29  mBidi.reset(bidi_new(text, aParagraph.Length(), aLevel));
     30 
     31  return ToICUResult(U_ZERO_ERROR);
     32 #else
     33  // Do not allow any reordering of the runs, as this can change the
     34  // performance characteristics of working with runs. In the default mode,
     35  // the levels can be iterated over directly, rather than relying on computing
     36  // logical runs on the fly. This can have negative performance characteristics
     37  // compared to iterating over the levels.
     38  //
     39  // In the UBIDI_REORDER_RUNS_ONLY the levels are encoded with additional
     40  // information which can be safely ignored in this Bidi implementation.
     41  // Note that this check is here since setting the mode must be done before
     42  // calls to setting the paragraph.
     43  MOZ_ASSERT(ubidi_getReorderingMode(mBidi.GetMut()) == UBIDI_REORDER_DEFAULT);
     44 
     45  UErrorCode status = U_ZERO_ERROR;
     46  ubidi_setPara(mBidi.GetMut(), aParagraph.Elements(),
     47                AssertedCast<int32_t>(aParagraph.Length()), aLevel, nullptr,
     48                &status);
     49 
     50  mLevels = nullptr;
     51 
     52  return ToICUResult(status);
     53 #endif
     54 }
     55 
     56 Bidi::ParagraphDirection Bidi::GetParagraphDirection() const {
     57 #if USE_RUST_UNICODE_BIDI
     58  auto dir = bidi_get_direction(mBidi.get());
     59  switch (dir) {
     60    case -1:
     61      return Bidi::ParagraphDirection::RTL;
     62    case 0:
     63      return Bidi::ParagraphDirection::Mixed;
     64    case 1:
     65      return Bidi::ParagraphDirection::LTR;
     66    default:
     67      MOZ_ASSERT_UNREACHABLE("Bad direction value");
     68      return Bidi::ParagraphDirection::Mixed;
     69  }
     70 #else
     71  switch (ubidi_getDirection(mBidi.GetConst())) {
     72    case UBIDI_LTR:
     73      return Bidi::ParagraphDirection::LTR;
     74    case UBIDI_RTL:
     75      return Bidi::ParagraphDirection::RTL;
     76    case UBIDI_MIXED:
     77      return Bidi::ParagraphDirection::Mixed;
     78    case UBIDI_NEUTRAL:
     79      // This is only used in `ubidi_getBaseDirection` which is unused in this
     80      // API.
     81      MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
     82  };
     83  return Bidi::ParagraphDirection::Mixed;
     84 #endif
     85 }
     86 
     87 /* static */
     88 void Bidi::ReorderVisual(const BidiEmbeddingLevel* aLevels, int32_t aLength,
     89                         int32_t* aIndexMap) {
     90 #if USE_RUST_UNICODE_BIDI
     91  bidi_reorder_visual(reinterpret_cast<const uint8_t*>(aLevels), aLength,
     92                      aIndexMap);
     93 #else
     94  ubidi_reorderVisual(reinterpret_cast<const uint8_t*>(aLevels), aLength,
     95                      aIndexMap);
     96 #endif
     97 }
     98 
     99 /* static */
    100 Bidi::BaseDirection Bidi::GetBaseDirection(Span<const char16_t> aText) {
    101 #if USE_RUST_UNICODE_BIDI
    102  const auto* text = reinterpret_cast<const uint16_t*>(aText.Elements());
    103  switch (bidi_get_base_direction(text, aText.Length(), false)) {
    104    case -1:
    105      return Bidi::BaseDirection::RTL;
    106    case 0:
    107      return Bidi::BaseDirection::Neutral;
    108    case 1:
    109      return Bidi::BaseDirection::LTR;
    110    default:
    111      MOZ_ASSERT_UNREACHABLE("Bad base direction value");
    112      return Bidi::BaseDirection::Neutral;
    113  }
    114 #else
    115  UBiDiDirection direction = ubidi_getBaseDirection(
    116      aText.Elements(), AssertedCast<int32_t>(aText.Length()));
    117  switch (direction) {
    118    case UBIDI_LTR:
    119      return Bidi::BaseDirection::LTR;
    120    case UBIDI_RTL:
    121      return Bidi::BaseDirection::RTL;
    122    case UBIDI_NEUTRAL:
    123      return Bidi::BaseDirection::Neutral;
    124    case UBIDI_MIXED:
    125      MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
    126  }
    127  return Bidi::BaseDirection::Neutral;
    128 #endif
    129 }
    130 
    131 #if !USE_RUST_UNICODE_BIDI
    132 static BidiDirection ToBidiDirection(UBiDiDirection aDirection) {
    133  switch (aDirection) {
    134    case UBIDI_LTR:
    135      return BidiDirection::LTR;
    136    case UBIDI_RTL:
    137      return BidiDirection::RTL;
    138    case UBIDI_MIXED:
    139    case UBIDI_NEUTRAL:
    140      MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
    141  }
    142  return BidiDirection::LTR;
    143 }
    144 #endif
    145 
    146 Result<int32_t, ICUError> Bidi::CountRuns() {
    147 #if USE_RUST_UNICODE_BIDI
    148  return bidi_count_runs(mBidi.get());
    149 #else
    150  UErrorCode status = U_ZERO_ERROR;
    151  int32_t runCount = ubidi_countRuns(mBidi.GetMut(), &status);
    152  if (U_FAILURE(status)) {
    153    return Err(ToICUError(status));
    154  }
    155 
    156  mLength = ubidi_getProcessedLength(mBidi.GetConst());
    157  mLevels = mLength > 0 ? reinterpret_cast<const BidiEmbeddingLevel*>(
    158                              ubidi_getLevels(mBidi.GetMut(), &status))
    159                        : nullptr;
    160  if (U_FAILURE(status)) {
    161    return Err(ToICUError(status));
    162  }
    163 
    164  return runCount;
    165 #endif
    166 }
    167 
    168 void Bidi::GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimitOut,
    169                         BidiEmbeddingLevel* aLevelOut) {
    170 #if USE_RUST_UNICODE_BIDI
    171  const int32_t length = bidi_get_length(mBidi.get());
    172  MOZ_DIAGNOSTIC_ASSERT(aLogicalStart < length);
    173  const auto* levels = bidi_get_levels(mBidi.get());
    174 #else
    175  MOZ_ASSERT(mLevels, "CountRuns hasn't been run?");
    176  MOZ_RELEASE_ASSERT(aLogicalStart < mLength, "Out of bound");
    177  const int32_t length = mLength;
    178  const auto* levels = mLevels;
    179 #endif
    180  const uint8_t level = levels[aLogicalStart];
    181  int32_t limit;
    182  for (limit = aLogicalStart + 1; limit < length; limit++) {
    183    if (levels[limit] != level) {
    184      break;
    185    }
    186  }
    187  *aLogicalLimitOut = limit;
    188  *aLevelOut = BidiEmbeddingLevel(level);
    189 }
    190 
    191 BidiEmbeddingLevel Bidi::GetParagraphEmbeddingLevel() const {
    192 #if USE_RUST_UNICODE_BIDI
    193  return BidiEmbeddingLevel(bidi_get_paragraph_level(mBidi.get()));
    194 #else
    195  return BidiEmbeddingLevel(ubidi_getParaLevel(mBidi.GetConst()));
    196 #endif
    197 }
    198 
    199 BidiDirection Bidi::GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
    200                                 int32_t* aLength) {
    201 #if USE_RUST_UNICODE_BIDI
    202  auto run = bidi_get_visual_run(mBidi.get(), aRunIndex);
    203  *aLogicalStart = run.start;
    204  *aLength = run.length;
    205  return BidiEmbeddingLevel(run.level).Direction();
    206 #else
    207  return ToBidiDirection(
    208      ubidi_getVisualRun(mBidi.GetMut(), aRunIndex, aLogicalStart, aLength));
    209 #endif
    210 }
    211 
    212 }  // namespace mozilla::intl