зеркало из https://github.com/mozilla/gecko-dev.git
bug 955957 - pt 1 - reimplement gfxSkipChars and gfxSkipCharsIterator to perform better with huge text runs. r=roc
This commit is contained in:
Родитель
ac974f92d1
Коммит
a19636ff66
|
@ -5,227 +5,136 @@
|
|||
|
||||
#include "gfxSkipChars.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define SHORTCUT_FREQUENCY 256
|
||||
|
||||
// Even numbered list entries are "keep" entries
|
||||
static bool
|
||||
IsKeepEntry(uint32_t aEntry)
|
||||
{
|
||||
return !(aEntry & 1);
|
||||
}
|
||||
|
||||
void
|
||||
gfxSkipChars::BuildShortcuts()
|
||||
gfxSkipCharsIterator::SetOriginalOffset(int32_t aOffset)
|
||||
{
|
||||
if (!mList || mCharCount < SHORTCUT_FREQUENCY)
|
||||
return;
|
||||
|
||||
mShortcuts = new Shortcut[mCharCount/SHORTCUT_FREQUENCY];
|
||||
if (!mShortcuts)
|
||||
return;
|
||||
|
||||
uint32_t i;
|
||||
uint32_t nextShortcutIndex = 0;
|
||||
uint32_t originalCharOffset = 0;
|
||||
uint32_t skippedCharOffset = 0;
|
||||
for (i = 0; i < mListLength; ++i) {
|
||||
uint8_t len = mList[i];
|
||||
|
||||
// We use >= here to ensure that when mCharCount is a multiple of
|
||||
// SHORTCUT_FREQUENCY, we fill in the final shortcut with a reference
|
||||
// to the last element of mList. This means that in general when a list
|
||||
// element ends on an offset that's a multiple of SHORTCUT_FREQUENCY,
|
||||
// that list element is the shortcut for that offset, which is
|
||||
// slightly suboptimal (the *next* element is the one we really want),
|
||||
// but it's all correct and simpler this way.
|
||||
while (originalCharOffset + len >= (nextShortcutIndex + 1)*SHORTCUT_FREQUENCY) {
|
||||
mShortcuts[nextShortcutIndex] =
|
||||
Shortcut(i, originalCharOffset, skippedCharOffset);
|
||||
++nextShortcutIndex;
|
||||
}
|
||||
|
||||
originalCharOffset += len;
|
||||
if (IsKeepEntry(i)) {
|
||||
skippedCharOffset += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxSkipCharsIterator::SetOffsets(uint32_t aOffset, bool aInOriginalString)
|
||||
{
|
||||
NS_ASSERTION(aOffset <= mSkipChars->mCharCount,
|
||||
aOffset += mOriginalStringToSkipCharsOffset;
|
||||
NS_ASSERTION(uint32_t(aOffset) <= mSkipChars->mCharCount,
|
||||
"Invalid offset");
|
||||
|
||||
if (mSkipChars->mListLength == 0) {
|
||||
mOriginalStringOffset = mSkippedStringOffset = aOffset;
|
||||
mOriginalStringOffset = aOffset;
|
||||
|
||||
uint32_t rangeCount = mSkipChars->mRanges.Length();
|
||||
if (rangeCount == 0) {
|
||||
mSkippedStringOffset = aOffset;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// at start of string?
|
||||
if (aOffset == 0) {
|
||||
// Start from the beginning of the string.
|
||||
mSkippedStringOffset = 0;
|
||||
mOriginalStringOffset = 0;
|
||||
mListPrefixLength = 0;
|
||||
mListPrefixKeepCharCount = 0;
|
||||
mListPrefixCharCount = 0;
|
||||
if (aInOriginalString) {
|
||||
// Nothing more to do!
|
||||
mCurrentRangeIndex =
|
||||
rangeCount && mSkipChars->mRanges[0].Start() == 0 ? 0 : -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// find the range that includes or precedes aOffset
|
||||
uint32_t lo = 0, hi = rangeCount;
|
||||
const gfxSkipChars::SkippedRange* ranges = mSkipChars->mRanges.Elements();
|
||||
while (lo < hi) {
|
||||
uint32_t mid = (lo + hi) / 2;
|
||||
if (uint32_t(aOffset) < ranges[mid].Start()) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lo == rangeCount) {
|
||||
mCurrentRangeIndex = rangeCount - 1;
|
||||
} else if (uint32_t(aOffset) < ranges[lo].Start()) {
|
||||
mCurrentRangeIndex = lo - 1;
|
||||
if (mCurrentRangeIndex == -1) {
|
||||
mSkippedStringOffset = aOffset;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mCurrentRangeIndex = lo;
|
||||
}
|
||||
|
||||
if (aInOriginalString && mSkipChars->mShortcuts &&
|
||||
abs(int32_t(aOffset) - int32_t(mListPrefixCharCount)) > SHORTCUT_FREQUENCY) {
|
||||
// Take a shortcut. This makes SetOffsets(..., true) O(1) by bounding
|
||||
// the iterations in the loop below to at most SHORTCUT_FREQUENCY iterations
|
||||
uint32_t shortcutIndex = aOffset/SHORTCUT_FREQUENCY;
|
||||
if (shortcutIndex == 0) {
|
||||
mListPrefixLength = 0;
|
||||
mListPrefixKeepCharCount = 0;
|
||||
mListPrefixCharCount = 0;
|
||||
|
||||
const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
|
||||
if (uint32_t(aOffset) < r.End()) {
|
||||
mSkippedStringOffset = r.SkippedOffset();
|
||||
return;
|
||||
}
|
||||
|
||||
mSkippedStringOffset = aOffset - r.NextDelta();
|
||||
}
|
||||
|
||||
void
|
||||
gfxSkipCharsIterator::SetSkippedOffset(uint32_t aOffset)
|
||||
{
|
||||
NS_ASSERTION((mSkipChars->mRanges.IsEmpty() &&
|
||||
aOffset <= mSkipChars->mCharCount) ||
|
||||
(aOffset <= mSkipChars->LastRange().SkippedOffset() +
|
||||
mSkipChars->mCharCount -
|
||||
mSkipChars->LastRange().End()),
|
||||
"Invalid skipped offset");
|
||||
mSkippedStringOffset = aOffset;
|
||||
|
||||
uint32_t rangeCount = mSkipChars->mRanges.Length();
|
||||
if (rangeCount == 0) {
|
||||
mOriginalStringOffset = aOffset;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t lo = 0, hi = rangeCount;
|
||||
const gfxSkipChars::SkippedRange* ranges = mSkipChars->mRanges.Elements();
|
||||
while (lo < hi) {
|
||||
uint32_t mid = (lo + hi) / 2;
|
||||
if (aOffset < ranges[mid].SkippedOffset()) {
|
||||
hi = mid;
|
||||
} else {
|
||||
const gfxSkipChars::Shortcut& shortcut = mSkipChars->mShortcuts[shortcutIndex - 1];
|
||||
mListPrefixLength = shortcut.mListPrefixLength;
|
||||
mListPrefixKeepCharCount = shortcut.mListPrefixKeepCharCount;
|
||||
mListPrefixCharCount = shortcut.mListPrefixCharCount;
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t currentRunLength = mSkipChars->mList[mListPrefixLength];
|
||||
for (;;) {
|
||||
// See if aOffset is in the string segment described by
|
||||
// mSkipChars->mList[mListPrefixLength]
|
||||
uint32_t segmentOffset = aInOriginalString ? mListPrefixCharCount : mListPrefixKeepCharCount;
|
||||
if ((aInOriginalString || IsKeepEntry(mListPrefixLength)) &&
|
||||
aOffset >= segmentOffset && aOffset < segmentOffset + currentRunLength) {
|
||||
int32_t offsetInSegment = aOffset - segmentOffset;
|
||||
mOriginalStringOffset = mListPrefixCharCount + offsetInSegment;
|
||||
mSkippedStringOffset = mListPrefixKeepCharCount;
|
||||
if (IsKeepEntry(mListPrefixLength)) {
|
||||
mSkippedStringOffset += offsetInSegment;
|
||||
}
|
||||
|
||||
if (lo == rangeCount) {
|
||||
mCurrentRangeIndex = rangeCount - 1;
|
||||
} else if (aOffset < ranges[lo].SkippedOffset()) {
|
||||
mCurrentRangeIndex = lo - 1;
|
||||
if (mCurrentRangeIndex == -1) {
|
||||
mOriginalStringOffset = aOffset;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOffset < segmentOffset) {
|
||||
// We need to move backwards
|
||||
if (mListPrefixLength <= 0) {
|
||||
// nowhere to go backwards
|
||||
mOriginalStringOffset = mSkippedStringOffset = 0;
|
||||
return;
|
||||
}
|
||||
// Go backwards one segment and restore invariants
|
||||
--mListPrefixLength;
|
||||
currentRunLength = mSkipChars->mList[mListPrefixLength];
|
||||
mListPrefixCharCount -= currentRunLength;
|
||||
if (IsKeepEntry(mListPrefixLength)) {
|
||||
mListPrefixKeepCharCount -= currentRunLength;
|
||||
}
|
||||
} else {
|
||||
// We need to move forwards
|
||||
if (mListPrefixLength >= mSkipChars->mListLength - 1) {
|
||||
// nowhere to go forwards
|
||||
mOriginalStringOffset = mListPrefixCharCount + currentRunLength;
|
||||
mSkippedStringOffset = mListPrefixKeepCharCount;
|
||||
if (IsKeepEntry(mListPrefixLength)) {
|
||||
mSkippedStringOffset += currentRunLength;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Go forwards one segment and restore invariants
|
||||
mListPrefixCharCount += currentRunLength;
|
||||
if (IsKeepEntry(mListPrefixLength)) {
|
||||
mListPrefixKeepCharCount += currentRunLength;
|
||||
}
|
||||
++mListPrefixLength;
|
||||
currentRunLength = mSkipChars->mList[mListPrefixLength];
|
||||
}
|
||||
} else {
|
||||
mCurrentRangeIndex = lo;
|
||||
}
|
||||
|
||||
const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
|
||||
mOriginalStringOffset = r.End() + aOffset - r.SkippedOffset();
|
||||
}
|
||||
|
||||
bool
|
||||
gfxSkipCharsIterator::IsOriginalCharSkipped(int32_t* aRunLength) const
|
||||
{
|
||||
if (mSkipChars->mListLength == 0) {
|
||||
if (mCurrentRangeIndex == -1) {
|
||||
// we're before the first skipped range (if any)
|
||||
if (aRunLength) {
|
||||
*aRunLength = mSkipChars->mCharCount - mOriginalStringOffset;
|
||||
uint32_t end = mSkipChars->mRanges.IsEmpty() ?
|
||||
mSkipChars->mCharCount : mSkipChars->mRanges[0].Start();
|
||||
*aRunLength = end - mOriginalStringOffset;
|
||||
}
|
||||
return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
|
||||
}
|
||||
|
||||
uint32_t listPrefixLength = mListPrefixLength;
|
||||
// figure out which segment we're in
|
||||
uint32_t currentRunLength = mSkipChars->mList[listPrefixLength];
|
||||
// Zero-length list entries are possible. Advance until mListPrefixLength
|
||||
// is pointing to a run with real characters (or we're at the end of the
|
||||
// string).
|
||||
while (currentRunLength == 0 && listPrefixLength < mSkipChars->mListLength - 1) {
|
||||
++listPrefixLength;
|
||||
// This does not break the iterator's invariant because no skipped
|
||||
// or kept characters are being added
|
||||
currentRunLength = mSkipChars->mList[listPrefixLength];
|
||||
}
|
||||
NS_ASSERTION(uint32_t(mOriginalStringOffset) >= mListPrefixCharCount,
|
||||
"Invariant violation");
|
||||
uint32_t offsetIntoCurrentRun =
|
||||
uint32_t(mOriginalStringOffset) - mListPrefixCharCount;
|
||||
if (listPrefixLength >= mSkipChars->mListLength - 1 &&
|
||||
offsetIntoCurrentRun >= currentRunLength) {
|
||||
NS_ASSERTION(listPrefixLength == mSkipChars->mListLength - 1 &&
|
||||
offsetIntoCurrentRun == currentRunLength,
|
||||
"Overran end of string");
|
||||
// We're at the end of the string
|
||||
|
||||
const gfxSkipChars::SkippedRange& range =
|
||||
mSkipChars->mRanges[mCurrentRangeIndex];
|
||||
|
||||
if (uint32_t(mOriginalStringOffset) < range.End()) {
|
||||
if (aRunLength) {
|
||||
*aRunLength = 0;
|
||||
*aRunLength = range.End() - mOriginalStringOffset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isSkipped = !IsKeepEntry(listPrefixLength);
|
||||
if (aRunLength) {
|
||||
// Long runs of all-skipped or all-kept characters will be encoded as
|
||||
// sequences of 255, 0, 255, 0 etc. Compute the maximum run length by skipping
|
||||
// over zero entries.
|
||||
uint32_t runLength = currentRunLength - offsetIntoCurrentRun;
|
||||
for (uint32_t i = listPrefixLength + 2; i < mSkipChars->mListLength; i += 2) {
|
||||
if (mSkipChars->mList[i - 1] != 0)
|
||||
break;
|
||||
runLength += mSkipChars->mList[i];
|
||||
}
|
||||
*aRunLength = runLength;
|
||||
}
|
||||
return isSkipped;
|
||||
}
|
||||
|
||||
void
|
||||
gfxSkipCharsBuilder::FlushRun()
|
||||
{
|
||||
NS_ASSERTION((mBuffer.Length() & 1) == mRunSkipped,
|
||||
"out of sync?");
|
||||
// Fill in buffer entries starting at mBufferLength, as many as necessary
|
||||
uint32_t charCount = mRunCharCount;
|
||||
for (;;) {
|
||||
uint32_t chars = std::min<uint32_t>(255, charCount);
|
||||
if (!mBuffer.AppendElement(chars)) {
|
||||
mInErrorState = true;
|
||||
return;
|
||||
}
|
||||
charCount -= chars;
|
||||
if (charCount == 0)
|
||||
break;
|
||||
if (!mBuffer.AppendElement(0)) {
|
||||
mInErrorState = true;
|
||||
return;
|
||||
}
|
||||
if (aRunLength) {
|
||||
uint32_t end =
|
||||
uint32_t(mCurrentRangeIndex) + 1 < mSkipChars->mRanges.Length() ?
|
||||
mSkipChars->mRanges[mCurrentRangeIndex + 1].Start() :
|
||||
mSkipChars->mCharCount;
|
||||
*aRunLength = end - mOriginalStringOffset;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mCharCount + mRunCharCount >= mCharCount,
|
||||
"String length overflow");
|
||||
mCharCount += mRunCharCount;
|
||||
mRunCharCount = 0;
|
||||
mRunSkipped = !mRunSkipped;
|
||||
|
||||
return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#ifndef GFX_SKIP_CHARS_H
|
||||
#define GFX_SKIP_CHARS_H
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
/*
|
||||
|
@ -20,146 +19,126 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* gfxSkipCharsBuilder is a helper class that accumulates a list of (skip, keep)
|
||||
* commands and can eventually be used to construct a real gfxSkipChars.
|
||||
* gfxSkipCharsBuilder objects are quite large so don't keep these around.
|
||||
* On the positive side, the Skip/KeepChar(s) methods are very efficient,
|
||||
* especially when you have runs of all-kept or all-skipped characters.
|
||||
*
|
||||
* mBuffer is an array of bytes; even numbered bytes represent characters kept,
|
||||
* odd numbered bytes represent characters skipped. After those characters
|
||||
* are accounted for, we have mRunCharCount characters which are kept or
|
||||
* skipped depending on the value of mRunSkipped.
|
||||
*
|
||||
* mCharCount is the sum of counts of all skipped and kept characters, i.e.,
|
||||
* the length of the original string.
|
||||
*/
|
||||
class gfxSkipCharsBuilder {
|
||||
public:
|
||||
gfxSkipCharsBuilder() :
|
||||
mCharCount(0), mRunCharCount(0), mRunSkipped(false), mInErrorState(false)
|
||||
{}
|
||||
|
||||
void SkipChars(uint32_t aChars) {
|
||||
DoChars(aChars, true);
|
||||
}
|
||||
void KeepChars(uint32_t aChars) {
|
||||
DoChars(aChars, false);
|
||||
}
|
||||
void SkipChar() {
|
||||
SkipChars(1);
|
||||
}
|
||||
void KeepChar() {
|
||||
KeepChars(1);
|
||||
}
|
||||
void DoChars(uint32_t aChars, bool aSkipped) {
|
||||
if (aSkipped != mRunSkipped && aChars > 0) {
|
||||
FlushRun();
|
||||
}
|
||||
NS_ASSERTION(mRunCharCount + aChars > mRunCharCount,
|
||||
"Character count overflow");
|
||||
mRunCharCount += aChars;
|
||||
}
|
||||
|
||||
bool IsOK() { return !mInErrorState; }
|
||||
|
||||
uint32_t GetCharCount() { return mCharCount + mRunCharCount; }
|
||||
bool GetAllCharsKept() { return mBuffer.Length() == 0; }
|
||||
|
||||
friend class gfxSkipChars;
|
||||
|
||||
private:
|
||||
typedef AutoFallibleTArray<uint8_t,256> Buffer;
|
||||
|
||||
/**
|
||||
* Moves mRunCharCount/mRunSkipped to the buffer (updating mCharCount),
|
||||
* sets mRunCharCount to zero and toggles mRunSkipped.
|
||||
*/
|
||||
void FlushRun();
|
||||
|
||||
Buffer mBuffer;
|
||||
uint32_t mCharCount;
|
||||
uint32_t mRunCharCount;
|
||||
bool mRunSkipped; // == mBuffer.Length()&1
|
||||
bool mInErrorState;
|
||||
};
|
||||
|
||||
/**
|
||||
* The gfxSkipChars list is represented as a list of bytes of the form
|
||||
* [chars to keep, chars to skip, chars to keep, chars to skip, ...]
|
||||
* In the special case where all chars are to be kept, the list is length
|
||||
* zero.
|
||||
*
|
||||
* The gfxSkipChars is represented as a sorted array of skipped ranges.
|
||||
*
|
||||
* A freshly-created gfxSkipChars means "all chars kept".
|
||||
*/
|
||||
class gfxSkipChars {
|
||||
class gfxSkipChars
|
||||
{
|
||||
private:
|
||||
class SkippedRange
|
||||
{
|
||||
public:
|
||||
SkippedRange(uint32_t aOffset, uint32_t aLength, uint32_t aDelta)
|
||||
: mOffset(aOffset), mLength(aLength), mDelta(aDelta)
|
||||
{ }
|
||||
|
||||
uint32_t Start() const
|
||||
{
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
uint32_t End() const
|
||||
{
|
||||
return mOffset + mLength;
|
||||
}
|
||||
|
||||
uint32_t Length() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
uint32_t SkippedOffset() const
|
||||
{
|
||||
return mOffset - mDelta;
|
||||
}
|
||||
|
||||
uint32_t Delta() const
|
||||
{
|
||||
return mDelta;
|
||||
}
|
||||
|
||||
uint32_t NextDelta() const
|
||||
{
|
||||
return mDelta + mLength;
|
||||
}
|
||||
|
||||
void Extend(uint32_t aChars)
|
||||
{
|
||||
mLength += aChars;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mOffset; // original-string offset at which we want to skip
|
||||
uint32_t mLength; // number of skipped chars at this offset
|
||||
uint32_t mDelta; // sum of lengths of preceding skipped-ranges
|
||||
};
|
||||
|
||||
public:
|
||||
gfxSkipChars() : mListLength(0), mCharCount(0) {}
|
||||
|
||||
void TakeFrom(gfxSkipChars* aSkipChars) {
|
||||
mList = aSkipChars->mList.forget();
|
||||
mListLength = aSkipChars->mListLength;
|
||||
gfxSkipChars()
|
||||
: mCharCount(0)
|
||||
{ }
|
||||
|
||||
void SkipChars(uint32_t aChars)
|
||||
{
|
||||
NS_ASSERTION(mCharCount + aChars > mCharCount,
|
||||
"Character count overflow");
|
||||
uint32_t rangeCount = mRanges.Length();
|
||||
uint32_t delta = 0;
|
||||
if (rangeCount > 0) {
|
||||
SkippedRange& lastRange = mRanges[rangeCount - 1];
|
||||
if (lastRange.End() == mCharCount) {
|
||||
lastRange.Extend(aChars);
|
||||
mCharCount += aChars;
|
||||
return;
|
||||
}
|
||||
delta = lastRange.NextDelta();
|
||||
}
|
||||
mRanges.AppendElement(SkippedRange(mCharCount, aChars, delta));
|
||||
mCharCount += aChars;
|
||||
}
|
||||
|
||||
void KeepChars(uint32_t aChars)
|
||||
{
|
||||
NS_ASSERTION(mCharCount + aChars > mCharCount,
|
||||
"Character count overflow");
|
||||
mCharCount += aChars;
|
||||
}
|
||||
|
||||
void SkipChar()
|
||||
{
|
||||
SkipChars(1);
|
||||
}
|
||||
|
||||
void KeepChar()
|
||||
{
|
||||
KeepChars(1);
|
||||
}
|
||||
|
||||
void TakeFrom(gfxSkipChars* aSkipChars)
|
||||
{
|
||||
mRanges.SwapElements(aSkipChars->mRanges);
|
||||
mCharCount = aSkipChars->mCharCount;
|
||||
aSkipChars->mCharCount = 0;
|
||||
aSkipChars->mListLength = 0;
|
||||
BuildShortcuts();
|
||||
}
|
||||
|
||||
void TakeFrom(gfxSkipCharsBuilder* aSkipCharsBuilder) {
|
||||
if (!aSkipCharsBuilder->mBuffer.Length()) {
|
||||
NS_ASSERTION(!aSkipCharsBuilder->mRunSkipped, "out of sync");
|
||||
// all characters kept
|
||||
mCharCount = aSkipCharsBuilder->mRunCharCount;
|
||||
mList = nullptr;
|
||||
mListLength = 0;
|
||||
} else {
|
||||
aSkipCharsBuilder->FlushRun();
|
||||
mCharCount = aSkipCharsBuilder->mCharCount;
|
||||
mList = new uint8_t[aSkipCharsBuilder->mBuffer.Length()];
|
||||
if (!mList) {
|
||||
mListLength = 0;
|
||||
} else {
|
||||
mListLength = aSkipCharsBuilder->mBuffer.Length();
|
||||
memcpy(mList, aSkipCharsBuilder->mBuffer.Elements(), mListLength);
|
||||
}
|
||||
}
|
||||
aSkipCharsBuilder->mBuffer.Clear();
|
||||
aSkipCharsBuilder->mCharCount = 0;
|
||||
aSkipCharsBuilder->mRunCharCount = 0;
|
||||
aSkipCharsBuilder->mRunSkipped = false;
|
||||
BuildShortcuts();
|
||||
|
||||
int32_t GetOriginalCharCount() const
|
||||
{
|
||||
return mCharCount;
|
||||
}
|
||||
|
||||
void SetAllKeep(uint32_t aLength) {
|
||||
mCharCount = aLength;
|
||||
mList = nullptr;
|
||||
mListLength = 0;
|
||||
|
||||
const SkippedRange& LastRange() const
|
||||
{
|
||||
// this is only valid if mRanges is non-empty; no assertion here
|
||||
// because nsTArray will already assert if we abuse it
|
||||
return mRanges[mRanges.Length() - 1];
|
||||
}
|
||||
|
||||
int32_t GetOriginalCharCount() const { return mCharCount; }
|
||||
|
||||
friend class gfxSkipCharsIterator;
|
||||
|
||||
private:
|
||||
struct Shortcut {
|
||||
uint32_t mListPrefixLength;
|
||||
uint32_t mListPrefixCharCount;
|
||||
uint32_t mListPrefixKeepCharCount;
|
||||
|
||||
Shortcut() {}
|
||||
Shortcut(uint32_t aListPrefixLength, uint32_t aListPrefixCharCount,
|
||||
uint32_t aListPrefixKeepCharCount) :
|
||||
mListPrefixLength(aListPrefixLength),
|
||||
mListPrefixCharCount(aListPrefixCharCount),
|
||||
mListPrefixKeepCharCount(aListPrefixKeepCharCount) {}
|
||||
};
|
||||
|
||||
void BuildShortcuts();
|
||||
|
||||
nsAutoArrayPtr<uint8_t> mList;
|
||||
nsAutoArrayPtr<Shortcut> mShortcuts;
|
||||
uint32_t mListLength;
|
||||
uint32_t mCharCount;
|
||||
nsTArray<SkippedRange> mRanges;
|
||||
uint32_t mCharCount;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -169,17 +148,18 @@ private:
|
|||
* incoming original string offsets and subtracted from all outgoing original
|
||||
* string offsets --- useful when the gfxSkipChars corresponds to something
|
||||
* offset from the original DOM coordinates, which it often does for gfxTextRuns.
|
||||
*
|
||||
*
|
||||
* The current positions (in both the original and skipped strings) are
|
||||
* always constrained to be >= 0 and <= the string length. When the position
|
||||
* is equal to the string length, it is at the end of the string. The current
|
||||
* positions do not include any aOriginalStringToSkipCharsOffset.
|
||||
*
|
||||
*
|
||||
* When the position in the original string corresponds to a skipped character,
|
||||
* the skipped-characters offset is the offset of the next unskipped character,
|
||||
* or the skipped-characters string length if there is no next unskipped character.
|
||||
*/
|
||||
class gfxSkipCharsIterator {
|
||||
class gfxSkipCharsIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aOriginalStringToSkipCharsOffset add this to all incoming and
|
||||
|
@ -189,66 +169,72 @@ public:
|
|||
int32_t aOriginalStringToSkipCharsOffset,
|
||||
int32_t aOriginalStringOffset)
|
||||
: mSkipChars(&aSkipChars),
|
||||
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
|
||||
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
|
||||
mOriginalStringOffset(0),
|
||||
mSkippedStringOffset(0),
|
||||
mCurrentRangeIndex(-1),
|
||||
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
|
||||
{
|
||||
SetOriginalOffset(aOriginalStringOffset);
|
||||
}
|
||||
|
||||
gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
|
||||
int32_t aOriginalStringToSkipCharsOffset = 0)
|
||||
: mSkipChars(&aSkipChars),
|
||||
mOriginalStringOffset(0), mSkippedStringOffset(0),
|
||||
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
|
||||
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
|
||||
}
|
||||
mOriginalStringOffset(0),
|
||||
mSkippedStringOffset(0),
|
||||
mCurrentRangeIndex(-1),
|
||||
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset)
|
||||
{ }
|
||||
|
||||
gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
|
||||
: mSkipChars(aIterator.mSkipChars),
|
||||
mOriginalStringOffset(aIterator.mOriginalStringOffset),
|
||||
mSkippedStringOffset(aIterator.mSkippedStringOffset),
|
||||
mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset),
|
||||
mListPrefixLength(aIterator.mListPrefixLength),
|
||||
mListPrefixCharCount(aIterator.mListPrefixCharCount),
|
||||
mListPrefixKeepCharCount(aIterator.mListPrefixKeepCharCount)
|
||||
{}
|
||||
|
||||
mCurrentRangeIndex(aIterator.mCurrentRangeIndex),
|
||||
mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* The empty constructor creates an object that is useless until it is assigned.
|
||||
*/
|
||||
gfxSkipCharsIterator() : mSkipChars(nullptr) {}
|
||||
gfxSkipCharsIterator()
|
||||
: mSkipChars(nullptr)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Return true if this iterator is properly initialized and usable.
|
||||
*/
|
||||
bool IsInitialized() { return mSkipChars != nullptr; }
|
||||
*/
|
||||
bool IsInitialized()
|
||||
{
|
||||
return mSkipChars != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the iterator to aOriginalStringOffset in the original string.
|
||||
* This can efficiently move forward or backward from the current position.
|
||||
* aOriginalStringOffset is clamped to [0,originalStringLength].
|
||||
*/
|
||||
void SetOriginalOffset(int32_t aOriginalStringOffset) {
|
||||
SetOffsets(aOriginalStringOffset + mOriginalStringToSkipCharsOffset, true);
|
||||
}
|
||||
|
||||
void SetOriginalOffset(int32_t aOriginalStringOffset);
|
||||
|
||||
/**
|
||||
* Set the iterator to aSkippedStringOffset in the skipped string.
|
||||
* This can efficiently move forward or backward from the current position.
|
||||
* aSkippedStringOffset is clamped to [0,skippedStringLength].
|
||||
*/
|
||||
void SetSkippedOffset(uint32_t aSkippedStringOffset) {
|
||||
SetOffsets(aSkippedStringOffset, false);
|
||||
}
|
||||
|
||||
uint32_t ConvertOriginalToSkipped(int32_t aOriginalStringOffset) {
|
||||
void SetSkippedOffset(uint32_t aSkippedStringOffset);
|
||||
|
||||
uint32_t ConvertOriginalToSkipped(int32_t aOriginalStringOffset)
|
||||
{
|
||||
SetOriginalOffset(aOriginalStringOffset);
|
||||
return GetSkippedOffset();
|
||||
}
|
||||
uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset) {
|
||||
|
||||
uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset)
|
||||
{
|
||||
SetSkippedOffset(aSkippedStringOffset);
|
||||
return GetOriginalOffset();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the character at the current position in the original string
|
||||
* is skipped or not. If aRunLength is non-null, then *aRunLength is set
|
||||
|
@ -257,20 +243,25 @@ public:
|
|||
* string, we return true and *aRunLength is set to zero.
|
||||
*/
|
||||
bool IsOriginalCharSkipped(int32_t* aRunLength = nullptr) const;
|
||||
|
||||
void AdvanceOriginal(int32_t aDelta) {
|
||||
SetOffsets(mOriginalStringOffset + aDelta, true);
|
||||
|
||||
void AdvanceOriginal(int32_t aDelta)
|
||||
{
|
||||
SetOriginalOffset(GetOriginalOffset() + aDelta);
|
||||
}
|
||||
void AdvanceSkipped(int32_t aDelta) {
|
||||
SetOffsets(mSkippedStringOffset + aDelta, false);
|
||||
|
||||
void AdvanceSkipped(int32_t aDelta)
|
||||
{
|
||||
SetSkippedOffset(GetSkippedOffset() + aDelta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the offset within the original string
|
||||
*/
|
||||
int32_t GetOriginalOffset() const {
|
||||
int32_t GetOriginalOffset() const
|
||||
{
|
||||
return mOriginalStringOffset - mOriginalStringToSkipCharsOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the offset within the skipped string corresponding to the
|
||||
* current position in the original string. If the current position
|
||||
|
@ -279,38 +270,33 @@ public:
|
|||
* original string after the current position, or the length of the skipped
|
||||
* string if there is no such character.
|
||||
*/
|
||||
uint32_t GetSkippedOffset() const { return mSkippedStringOffset; }
|
||||
uint32_t GetSkippedOffset() const
|
||||
{
|
||||
return mSkippedStringOffset;
|
||||
}
|
||||
|
||||
int32_t GetOriginalEnd() const {
|
||||
int32_t GetOriginalEnd() const
|
||||
{
|
||||
return mSkipChars->GetOriginalCharCount() -
|
||||
mOriginalStringToSkipCharsOffset;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetOffsets(uint32_t aOffset, bool aInOriginalString);
|
||||
|
||||
const gfxSkipChars* mSkipChars;
|
||||
|
||||
// Current position
|
||||
int32_t mOriginalStringOffset;
|
||||
uint32_t mSkippedStringOffset;
|
||||
|
||||
// Index of the last skippedRange that precedes or contains the current
|
||||
// position in the original string.
|
||||
// If index == -1 then we are before the first skipped char.
|
||||
int32_t mCurrentRangeIndex;
|
||||
|
||||
// This offset is added to map from "skipped+unskipped characters in
|
||||
// the original DOM string" character space to "skipped+unskipped
|
||||
// characters in the textrun's gfxSkipChars" character space
|
||||
int32_t mOriginalStringToSkipCharsOffset;
|
||||
|
||||
/*
|
||||
* This is used to speed up cursor-style traversal. The invariant is that
|
||||
* the first mListPrefixLength bytes of mSkipChars.mList sum to
|
||||
* mListPrefixCharCount, and the even-indexed bytes in that prefix sum to
|
||||
* mListPrefixKeepCharCount.
|
||||
* Also, 0 <= mListPrefixLength < mSkipChars.mListLength, or else
|
||||
* mSkipChars.mListLength is zero.
|
||||
* Also, mListPrefixCharCount <= mOriginalStringOffset (and therefore
|
||||
* mListPrefixKeepCharCount < mSkippedStringOffset).
|
||||
*/
|
||||
uint32_t mListPrefixLength;
|
||||
uint32_t mListPrefixCharCount;
|
||||
uint32_t mListPrefixKeepCharCount;
|
||||
};
|
||||
|
||||
#endif /*GFX_SKIP_CHARS_H*/
|
||||
|
|
|
@ -1888,7 +1888,7 @@ static const nsTextFrameUtils::CompressionMode CSSWhitespaceToCompressionMode[]
|
|||
gfxTextRun*
|
||||
BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||
{
|
||||
gfxSkipCharsBuilder builder;
|
||||
gfxSkipChars skipChars;
|
||||
|
||||
const void* textPtr = aTextBuffer;
|
||||
bool anySmallcapsStyle = false;
|
||||
|
@ -1987,7 +1987,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
|
||||
TextRunMappedFlow* newFlow = &userData->mMappedFlows[i];
|
||||
newFlow->mStartFrame = mappedFlow->mStartFrame;
|
||||
newFlow->mDOMOffsetToBeforeTransformOffset = builder.GetCharCount() -
|
||||
newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
|
||||
mappedFlow->mStartFrame->GetContentOffset();
|
||||
newFlow->mContentLength = contentLength;
|
||||
|
||||
|
@ -2003,7 +2003,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
char16_t* bufStart = static_cast<char16_t*>(aTextBuffer);
|
||||
char16_t* bufEnd = nsTextFrameUtils::TransformText(
|
||||
frag->Get2b() + contentStart, contentLength, bufStart,
|
||||
compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
aTextBuffer = bufEnd;
|
||||
currentTransformedTextOffset = bufEnd - static_cast<const char16_t*>(textPtr);
|
||||
} else {
|
||||
|
@ -2018,7 +2018,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
}
|
||||
uint8_t* end = nsTextFrameUtils::TransformText(
|
||||
reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
|
||||
bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
aTextBuffer = ExpandBuffer(static_cast<char16_t*>(aTextBuffer),
|
||||
tempBuf.Elements(), end - tempBuf.Elements());
|
||||
currentTransformedTextOffset =
|
||||
|
@ -2027,7 +2027,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
uint8_t* bufStart = static_cast<uint8_t*>(aTextBuffer);
|
||||
uint8_t* end = nsTextFrameUtils::TransformText(
|
||||
reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
|
||||
bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
aTextBuffer = end;
|
||||
currentTransformedTextOffset = end - static_cast<const uint8_t*>(textPtr);
|
||||
}
|
||||
|
@ -2035,12 +2035,6 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
textFlags |= analysisFlags;
|
||||
}
|
||||
|
||||
// Check for out-of-memory in gfxSkipCharsBuilder
|
||||
if (!builder.IsOK()) {
|
||||
DestroyUserData(userDataToDestroy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* finalUserData;
|
||||
if (userData == &dummyData) {
|
||||
textFlags |= nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW;
|
||||
|
@ -2093,8 +2087,6 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
textFlags |= gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX;
|
||||
}
|
||||
|
||||
gfxSkipChars skipChars;
|
||||
skipChars.TakeFrom(&builder);
|
||||
// Convert linebreak coordinates to transformed string offsets
|
||||
NS_ASSERTION(nextBreakIndex == mLineBreakBeforeFrames.Length(),
|
||||
"Didn't find all the frames to break-before...");
|
||||
|
@ -2235,7 +2227,7 @@ BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
|
|||
return false;
|
||||
}
|
||||
|
||||
gfxSkipCharsBuilder builder;
|
||||
gfxSkipChars skipChars;
|
||||
|
||||
nsAutoTArray<int32_t,50> textBreakPoints;
|
||||
TextRunUserData dummyData;
|
||||
|
@ -2280,7 +2272,7 @@ BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
|
|||
|
||||
TextRunMappedFlow* newFlow = &userData->mMappedFlows[i];
|
||||
newFlow->mStartFrame = mappedFlow->mStartFrame;
|
||||
newFlow->mDOMOffsetToBeforeTransformOffset = builder.GetCharCount() -
|
||||
newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
|
||||
mappedFlow->mStartFrame->GetContentOffset();
|
||||
newFlow->mContentLength = contentLength;
|
||||
|
||||
|
@ -2296,7 +2288,7 @@ BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
|
|||
char16_t* bufStart = static_cast<char16_t*>(textPtr);
|
||||
char16_t* bufEnd = nsTextFrameUtils::TransformText(
|
||||
frag->Get2b() + contentStart, contentLength, bufStart,
|
||||
compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
textPtr = bufEnd;
|
||||
} else {
|
||||
if (mDoubleByteText) {
|
||||
|
@ -2310,14 +2302,14 @@ BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
|
|||
}
|
||||
uint8_t* end = nsTextFrameUtils::TransformText(
|
||||
reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
|
||||
bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
textPtr = ExpandBuffer(static_cast<char16_t*>(textPtr),
|
||||
tempBuf.Elements(), end - tempBuf.Elements());
|
||||
} else {
|
||||
uint8_t* bufStart = static_cast<uint8_t*>(textPtr);
|
||||
uint8_t* end = nsTextFrameUtils::TransformText(
|
||||
reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
|
||||
bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
|
||||
bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
|
||||
textPtr = end;
|
||||
}
|
||||
}
|
||||
|
@ -8331,7 +8323,7 @@ nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
|
|||
uint32_t aSkippedMaxLength)
|
||||
{
|
||||
// The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient...
|
||||
gfxSkipCharsBuilder skipCharsBuilder;
|
||||
gfxSkipChars skipChars;
|
||||
nsTextFrame* textFrame;
|
||||
const nsTextFragment* textFrag = mContent->GetText();
|
||||
uint32_t keptCharsLength = 0;
|
||||
|
@ -8360,7 +8352,7 @@ nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
|
|||
TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, false);
|
||||
int32_t startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset;
|
||||
if (startOfLineSkipChars > 0) {
|
||||
skipCharsBuilder.SkipChars(startOfLineSkipChars);
|
||||
skipChars.SkipChars(startOfLineSkipChars);
|
||||
iter.SetOriginalOffset(trimmedContentOffsets.mStart);
|
||||
}
|
||||
|
||||
|
@ -8370,10 +8362,10 @@ nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
|
|||
keptCharsLength < aSkippedMaxLength) {
|
||||
// For each original char from content text
|
||||
if (iter.IsOriginalCharSkipped() || ++validCharsLength <= aSkippedStartOffset) {
|
||||
skipCharsBuilder.SkipChar();
|
||||
skipChars.SkipChar();
|
||||
} else {
|
||||
++keptCharsLength;
|
||||
skipCharsBuilder.KeepChar();
|
||||
skipChars.KeepChar();
|
||||
if (aAppendToString) {
|
||||
aAppendToString->Append(
|
||||
TransformChar(textStyle, textFrame->mTextRun, iter.GetSkippedOffset(),
|
||||
|
@ -8388,10 +8380,10 @@ nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
|
|||
}
|
||||
|
||||
if (aSkipChars) {
|
||||
aSkipChars->TakeFrom(&skipCharsBuilder); // Copy skipChars into aSkipChars
|
||||
aSkipChars->TakeFrom(&skipChars); // Copy skipChars into aSkipChars
|
||||
if (aSkipIter) {
|
||||
// Caller must provide both pointers in order to retrieve a gfxSkipCharsIterator,
|
||||
// because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipCars.
|
||||
// because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipChars.
|
||||
*aSkipIter = gfxSkipCharsIterator(*aSkipChars, GetContentLength());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ nsTextFrameUtils::TransformText(const char16_t* aText, uint32_t aLength,
|
|||
char16_t* aOutput,
|
||||
CompressionMode aCompression,
|
||||
uint8_t* aIncomingFlags,
|
||||
gfxSkipCharsBuilder* aSkipChars,
|
||||
gfxSkipChars* aSkipChars,
|
||||
uint32_t* aAnalysisFlags)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
@ -140,7 +140,7 @@ nsTextFrameUtils::TransformText(const uint8_t* aText, uint32_t aLength,
|
|||
uint8_t* aOutput,
|
||||
CompressionMode aCompression,
|
||||
uint8_t* aIncomingFlags,
|
||||
gfxSkipCharsBuilder* aSkipChars,
|
||||
gfxSkipChars* aSkipChars,
|
||||
uint32_t* aAnalysisFlags)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
|
|
@ -100,14 +100,14 @@ public:
|
|||
char16_t* aOutput,
|
||||
CompressionMode aCompression,
|
||||
uint8_t * aIncomingFlags,
|
||||
gfxSkipCharsBuilder* aSkipChars,
|
||||
gfxSkipChars* aSkipChars,
|
||||
uint32_t* aAnalysisFlags);
|
||||
|
||||
static uint8_t* TransformText(const uint8_t* aText, uint32_t aLength,
|
||||
uint8_t* aOutput,
|
||||
CompressionMode aCompression,
|
||||
uint8_t * aIncomingFlags,
|
||||
gfxSkipCharsBuilder* aSkipChars,
|
||||
gfxSkipChars* aSkipChars,
|
||||
uint32_t* aAnalysisFlags);
|
||||
|
||||
static void
|
||||
|
|
Загрузка…
Ссылка в новой задаче