Bug 2058356 - Use mozilla::Span for LineBreaker. r=TYLin

Currently, we are using pointer for text and break position state, but it is more safe to use Span.

Differential Revision: https://phabricator.services.mozilla.com/D314550
This commit is contained in:
Makoto Kato
2026-07-30 08:56:27 +00:00
committed by m_kato@ga2.so-net.ne.jp
parent 9f4bdbdd64
commit c7ebb59cdc
5 changed files with 63 additions and 48 deletions
+11 -7
View File
@@ -17,6 +17,7 @@
#include "nsHyphenator.h"
using mozilla::AutoRestore;
using mozilla::Span;
using mozilla::intl::LineBreaker;
using mozilla::intl::LineBreakRule;
using mozilla::intl::Locale;
@@ -158,9 +159,8 @@ nsresult nsLineBreaker::FlushCurrentWord() {
gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
length * sizeof(uint8_t));
} else {
LineBreaker::ComputeBreakPositions(
mCurrentWord.Elements(), length, mWordBreak, mLineBreak,
mScriptIsChineseOrJapanese, breakState.Elements());
LineBreaker::ComputeBreakPositions(mCurrentWord, mWordBreak, mLineBreak,
mScriptIsChineseOrJapanese, breakState);
}
bool autoHyphenate = mCurrentWordLanguage && !mCurrentWordContainsMixedLang;
@@ -341,8 +341,10 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
// will set it to false.
AutoRestore<uint8_t> saveWordStartBreakState(breakState[wordStart]);
LineBreaker::ComputeBreakPositions(
aText + wordStart, offset - wordStart, mWordBreak, mLineBreak,
mScriptIsChineseOrJapanese, breakState.Elements() + wordStart);
Span<const char16_t>(aText + wordStart, offset - wordStart),
mWordBreak, mLineBreak, mScriptIsChineseOrJapanese,
Span<uint8_t>(breakState.Elements() + wordStart,
offset - wordStart));
}
if (hyphenator) {
FindHyphenationPoints(hyphenator, aText + wordStart, aText + offset,
@@ -601,8 +603,10 @@ nsresult nsLineBreaker::AppendText(nsAtom* aHyphenationLanguage,
// will set it to false.
AutoRestore<uint8_t> saveWordStartBreakState(breakState[wordStart]);
LineBreaker::ComputeBreakPositions(
aText + wordStart, offset - wordStart, mWordBreak, mLineBreak,
mScriptIsChineseOrJapanese, breakState.Elements() + wordStart);
Span<const uint8_t>(aText + wordStart, offset - wordStart),
mWordBreak, mLineBreak, mScriptIsChineseOrJapanese,
Span<uint8_t>(breakState.Elements() + wordStart,
offset - wordStart));
}
}
+6 -5
View File
@@ -11,6 +11,7 @@
#include "nsThreadUtils.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MruCache.h"
#include "mozilla/Span.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/intl/Segmenter.h"
@@ -20,7 +21,7 @@ namespace intl {
namespace detail {
struct LBCacheKey {
const char16_t* mText;
uint32_t mLength;
size_t mLength;
// ICU4X segmenter results depend on these flags, so they need to be part
// of the cache key. (Legacy ComplexBreaker just leaves them as default.)
WordBreakRule mWordBreak = WordBreakRule::Normal;
@@ -74,10 +75,10 @@ class LineBreakCache : public MruCache<detail::LBCacheKey, detail::LBCacheEntry,
}
static void CopyAndFill(const nsTArray<uint8_t>& aCachedBreakBefore,
uint8_t* aBreakBefore, uint8_t* aEndBreakBefore) {
auto* startFill = std::copy(aCachedBreakBefore.begin(),
aCachedBreakBefore.end(), aBreakBefore);
std::fill(startFill, aEndBreakBefore, false);
Span<uint8_t> aBreakBefore) {
auto startFill = std::copy(aCachedBreakBefore.begin(),
aCachedBreakBefore.end(), aBreakBefore.begin());
std::fill(startFill, aBreakBefore.end(), 0);
}
class Observer final : public nsIObserver {
+36 -27
View File
@@ -109,10 +109,15 @@ static capi::LineSegmenter* GetLineSegmenter(bool aUseDefault,
locale, options.AsFFI());
}
void LineBreaker::ComputeBreakPositions(
const char16_t* aChars, uint32_t aLength, WordBreakRule aWordBreak,
LineBreakRule aLevel, bool aIsChineseOrJapanese, uint8_t* aBreakBefore) {
if (aLength == 1) {
void LineBreaker::ComputeBreakPositions(Span<const char16_t> aText,
WordBreakRule aWordBreak,
LineBreakRule aLevel,
bool aIsChineseOrJapanese,
Span<uint8_t> aBreakBefore) {
MOZ_ASSERT(aText.Length() == aBreakBefore.Length());
const size_t length = aText.Length();
if (length == 1) {
// Although UAX#14 LB2 rule requires never breaking at the start of text
// (SOT), ICU4X line segmenter API is designed to match other segmenter in
// UAX#29 to always break at the start of text. Hence the optimization
@@ -126,8 +131,8 @@ void LineBreaker::ComputeBreakPositions(
// to make that decision, we probe every /kStride/ characters.
bool useCache = [=]() {
const uint32_t kStride = 8;
for (uint32_t i = 0; i < aLength; i += kStride) {
if (intl::UnicodeProperties::IsScriptioContinua(aChars[i])) {
for (uint32_t i = 0; i < length; i += kStride) {
if (intl::UnicodeProperties::IsScriptioContinua(aText[i])) {
return true;
}
}
@@ -135,31 +140,31 @@ void LineBreaker::ComputeBreakPositions(
}();
Maybe<LineBreakCache::Entry> entry;
if (useCache) {
LineBreakCache::KeyType key{aChars, aLength, aWordBreak, aLevel,
LineBreakCache::KeyType key{aText.Elements(), length, aWordBreak, aLevel,
aIsChineseOrJapanese};
entry.emplace(LineBreakCache::Cache()->Lookup(key));
if (*entry) {
auto& breakBefore = entry->Data().mBreaks;
LineBreakCache::CopyAndFill(breakBefore, aBreakBefore,
aBreakBefore + aLength);
LineBreakCache::CopyAndFill(breakBefore, aBreakBefore);
return;
}
}
memset(aBreakBefore, 0, aLength);
std::fill(aBreakBefore.begin(), aBreakBefore.end(), 0);
CheckedInt<int32_t> length = aLength;
if (length.isValid()) {
CheckedInt<int32_t> checkedLength = length;
if (checkedLength.isValid()) {
const bool useDefault =
UseDefaultLineSegmenter(aWordBreak, aLevel, aIsChineseOrJapanese);
auto lineSegmenter =
GetLineSegmenter(useDefault, aWordBreak, aLevel, aIsChineseOrJapanese);
auto segmenter = LineSegmenter::FromFFI(lineSegmenter);
auto iterator = segmenter->segment16(std::u16string_view{aChars, aLength});
auto iterator =
segmenter->segment16(std::u16string_view{aText.Elements(), length});
while (true) {
const int32_t nextPos = iterator->next();
if (nextPos < 0 || nextPos >= length.value()) {
if (nextPos < 0 || nextPos >= checkedLength.value()) {
break;
}
aBreakBefore[nextPos] = 1;
@@ -173,26 +178,30 @@ void LineBreaker::ComputeBreakPositions(
if (useCache) {
// As a very simple memory saving measure we trim off trailing elements
// that are false before caching.
auto* afterLastTrue = aBreakBefore + aLength;
auto* afterLastTrue = aBreakBefore.Elements() + length;
while (!*(afterLastTrue - 1)) {
if (--afterLastTrue == aBreakBefore) {
if (--afterLastTrue == aBreakBefore.Elements()) {
break;
}
}
entry->Set(LineBreakCache::EntryType{
nsString(aChars, aLength),
nsTArray<uint8_t>(aBreakBefore, afterLastTrue - aBreakBefore),
nsString(aText.Elements(), aText.Length()),
nsTArray<uint8_t>(aBreakBefore.Elements(),
afterLastTrue - aBreakBefore.Elements()),
aWordBreak, aLevel, aIsChineseOrJapanese});
}
}
void LineBreaker::ComputeBreakPositions(const uint8_t* aChars, uint32_t aLength,
void LineBreaker::ComputeBreakPositions(Span<const uint8_t> aText,
WordBreakRule aWordBreak,
LineBreakRule aLevel,
bool aIsChineseOrJapanese,
uint8_t* aBreakBefore) {
if (aLength == 1) {
Span<uint8_t> aBreakBefore) {
MOZ_ASSERT(aText.Length() == aBreakBefore.Length());
const size_t length = aText.Length();
if (length == 1) {
// Although UAX#14 LB2 rule requires never breaking at the start of text
// (SOT), ICU4X line segmenter API is designed to match other segmenter in
// UAX#29 to always break at the start of text. Hence the optimization
@@ -201,10 +210,10 @@ void LineBreaker::ComputeBreakPositions(const uint8_t* aChars, uint32_t aLength,
return;
}
memset(aBreakBefore, 0, aLength);
std::fill(aBreakBefore.begin(), aBreakBefore.end(), 0);
CheckedInt<int32_t> length = aLength;
if (!length.isValid()) {
CheckedInt<int32_t> checkedLength = length;
if (!checkedLength.isValid()) {
return;
}
@@ -213,12 +222,12 @@ void LineBreaker::ComputeBreakPositions(const uint8_t* aChars, uint32_t aLength,
auto lineSegmenter =
GetLineSegmenter(useDefault, aWordBreak, aLevel, aIsChineseOrJapanese);
auto segmenter = icu4x::LineSegmenter::FromFFI(lineSegmenter);
auto iterator =
segmenter->segment_latin1(diplomat::span<const uint8_t>{aChars, aLength});
auto iterator = segmenter->segment_latin1(
diplomat::span<const uint8_t>{aText.Elements(), length});
while (true) {
const int32_t nextPos = iterator->next();
if (nextPos < 0 || nextPos >= length.value()) {
if (nextPos < 0 || nextPos >= checkedLength.value()) {
break;
}
aBreakBefore[nextPos] = 1;
+7 -6
View File
@@ -4,6 +4,8 @@
#ifndef mozilla_intl_LineBreaker_h_
#define mozilla_intl_LineBreaker_h_
#include "mozilla/Span.h"
#include <cstdint>
#define NS_LINEBREAKER_NEED_MORE_TEXT -1
@@ -24,18 +26,17 @@ class LineBreaker final {
// rules to find breaks inside the word. aBreakBefore is set to the break-
// before status of each character; aBreakBefore[0] will always be false
// because we never return a break before the first character.
// aLength is the length of the aText array and also the length of the
// aBreakBefore output array.
static void ComputeBreakPositions(const char16_t* aText, uint32_t aLength,
// The aBreakBefore output array must have the same length as aText.
static void ComputeBreakPositions(Span<const char16_t> aText,
WordBreakRule aWordBreak,
LineBreakRule aLevel,
bool aIsChineseOrJapanese,
uint8_t* aBreakBefore);
static void ComputeBreakPositions(const uint8_t* aText, uint32_t aLength,
Span<uint8_t> aBreakBefore);
static void ComputeBreakPositions(Span<const uint8_t> aText,
WordBreakRule aWordBreak,
LineBreakRule aLevel,
bool aIsChineseOrJapanese,
uint8_t* aBreakBefore);
Span<uint8_t> aBreakBefore);
static void Shutdown();
};
+3 -3
View File
@@ -88,9 +88,9 @@ static void TestSegmenterBench(const nsString& aStr, bool aIsJaOrZh,
breakState.SetLength(aStr.Length());
for (size_t i = 0; i < aCount; i++) {
LineBreaker::ComputeBreakPositions(
aStr.get(), aStr.Length(), WordBreakRule::Normal, LineBreakRule::Strict,
aIsJaOrZh, breakState.Elements());
LineBreaker::ComputeBreakPositions(aStr, WordBreakRule::Normal,
LineBreakRule::Strict, aIsJaOrZh,
breakState);
}
}