blob: 4409a99725c2fdbb5e342bb0c538d472b8431864 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_SKIP_CHARS_H
#define GFX_SKIP_CHARS_H
#include "prtypes.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "gfxTypes.h"
/*
* gfxSkipChars is a data structure representing a list of characters that
* have been skipped. The initial string is called the "original string"
* and after skipping some characters, the result is called the "skipped string".
* gfxSkipChars provides efficient ways to translate between offsets in the
* original string and the skipped string. It is used by textrun code to keep
* track of offsets before and after text transformations such as whitespace
* compression and control code deletion.
*/
/**
* 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 THEBES_API gfxSkipCharsBuilder {
public:
gfxSkipCharsBuilder() :
mCharCount(0), mRunCharCount(0), mRunSkipped(PR_FALSE), mInErrorState(PR_FALSE)
{}
void SkipChars(PRUint32 aChars) {
DoChars(aChars, PR_TRUE);
}
void KeepChars(PRUint32 aChars) {
DoChars(aChars, PR_FALSE);
}
void SkipChar() {
SkipChars(1);
}
void KeepChar() {
KeepChars(1);
}
void DoChars(PRUint32 aChars, PRBool aSkipped) {
if (aSkipped != mRunSkipped && aChars > 0) {
FlushRun();
}
NS_ASSERTION(mRunCharCount + aChars > mRunCharCount,
"Character count overflow");
mRunCharCount += aChars;
}
PRBool IsOK() { return !mInErrorState; }
PRUint32 GetCharCount() { return mCharCount + mRunCharCount; }
PRBool GetAllCharsKept() { return mBuffer.Length() == 0; }
friend class gfxSkipChars;
private:
typedef nsAutoTArray<PRUint8,256> Buffer;
/**
* Moves mRunCharCount/mRunSkipped to the buffer (updating mCharCount),
* sets mRunCharCount to zero and toggles mRunSkipped.
*/
void FlushRun();
Buffer mBuffer;
PRUint32 mCharCount;
PRUint32 mRunCharCount;
PRPackedBool mRunSkipped; // == mBuffer.Length()&1
PRPackedBool 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.
*
* A freshly-created gfxSkipChars means "all chars kept".
*/
class THEBES_API gfxSkipChars {
public:
gfxSkipChars() : mListLength(0), mCharCount(0) {}
void TakeFrom(gfxSkipChars* aSkipChars) {
mList = aSkipChars->mList.forget();
mListLength = aSkipChars->mListLength;
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 = nsnull;
mListLength = 0;
} else {
aSkipCharsBuilder->FlushRun();
mCharCount = aSkipCharsBuilder->mCharCount;
mList = new PRUint8[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 = PR_FALSE;
BuildShortcuts();
}
void SetAllKeep(PRUint32 aLength) {
mCharCount = aLength;
mList = nsnull;
mListLength = 0;
}
PRInt32 GetOriginalCharCount() const { return mCharCount; }
friend class gfxSkipCharsIterator;
private:
struct Shortcut {
PRUint32 mListPrefixLength;
PRUint32 mListPrefixCharCount;
PRUint32 mListPrefixKeepCharCount;
Shortcut() {}
Shortcut(PRUint32 aListPrefixLength, PRUint32 aListPrefixCharCount,
PRUint32 aListPrefixKeepCharCount) :
mListPrefixLength(aListPrefixLength),
mListPrefixCharCount(aListPrefixCharCount),
mListPrefixKeepCharCount(aListPrefixKeepCharCount) {}
};
void BuildShortcuts();
nsAutoArrayPtr<PRUint8> mList;
nsAutoArrayPtr<Shortcut> mShortcuts;
PRUint32 mListLength;
PRUint32 mCharCount;
};
/**
* A gfxSkipCharsIterator represents a position in the original string. It lets you
* map efficiently to and from positions in the string after skipped characters
* have been removed. You can also specify an offset that is added to all
* 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 THEBES_API gfxSkipCharsIterator {
public:
/**
* @param aOriginalStringToSkipCharsOffset add this to all incoming and
* outgoing original string offsets
*/
gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
PRInt32 aOriginalStringToSkipCharsOffset,
PRInt32 aOriginalStringOffset)
: mSkipChars(&aSkipChars),
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
SetOriginalOffset(aOriginalStringOffset);
}
gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
PRInt32 aOriginalStringToSkipCharsOffset = 0)
: mSkipChars(&aSkipChars),
mOriginalStringOffset(0), mSkippedStringOffset(0),
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
}
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)
{}
/**
* The empty constructor creates an object that is useless until it is assigned.
*/
gfxSkipCharsIterator() : mSkipChars(nsnull) {}
/**
* Return true if this iterator is properly initialized and usable.
*/
PRBool IsInitialized() { return mSkipChars != nsnull; }
/**
* 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(PRInt32 aOriginalStringOffset) {
SetOffsets(aOriginalStringOffset + mOriginalStringToSkipCharsOffset, PR_TRUE);
}
/**
* 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(PRUint32 aSkippedStringOffset) {
SetOffsets(aSkippedStringOffset, PR_FALSE);
}
PRUint32 ConvertOriginalToSkipped(PRInt32 aOriginalStringOffset) {
SetOriginalOffset(aOriginalStringOffset);
return GetSkippedOffset();
}
PRUint32 ConvertSkippedToOriginal(PRInt32 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
* to a number of characters all of which are either skipped or not, starting
* at this character. When the current position is at the end of the original
* string, we return PR_TRUE and *aRunLength is set to zero.
*/
PRBool IsOriginalCharSkipped(PRInt32* aRunLength = nsnull) const;
void AdvanceOriginal(PRInt32 aDelta) {
SetOffsets(mOriginalStringOffset + aDelta, PR_TRUE);
}
void AdvanceSkipped(PRInt32 aDelta) {
SetOffsets(mSkippedStringOffset + aDelta, PR_FALSE);
}
/**
* @return the offset within the original string
*/
PRInt32 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
* in the original string is a character that is skipped, then we return
* the position corresponding to the first non-skipped character in the
* original string after the current position, or the length of the skipped
* string if there is no such character.
*/
PRUint32 GetSkippedOffset() const { return mSkippedStringOffset; }
private:
void SetOffsets(PRUint32 aOffset, PRBool aInOriginalString);
const gfxSkipChars* mSkipChars;
PRInt32 mOriginalStringOffset;
PRUint32 mSkippedStringOffset;
PRUint32 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).
*/
PRUint32 mListPrefixLength;
PRUint32 mListPrefixCharCount;
PRUint32 mListPrefixKeepCharCount;
};
#endif /*GFX_SKIP_CHARS_H*/