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