1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Novell code.
16 : *
17 : * The Initial Developer of the Original Code is Novell.
18 : * Portions created by the Initial Developer are Copyright (C) 2006
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Robert O'Callahan <robert@ocallahan.org>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #ifndef GFX_SKIP_CHARS_H
39 : #define GFX_SKIP_CHARS_H
40 :
41 : #include "prtypes.h"
42 : #include "nsAutoPtr.h"
43 : #include "nsTArray.h"
44 : #include "gfxTypes.h"
45 :
46 : /*
47 : * gfxSkipChars is a data structure representing a list of characters that
48 : * have been skipped. The initial string is called the "original string"
49 : * and after skipping some characters, the result is called the "skipped string".
50 : * gfxSkipChars provides efficient ways to translate between offsets in the
51 : * original string and the skipped string. It is used by textrun code to keep
52 : * track of offsets before and after text transformations such as whitespace
53 : * compression and control code deletion.
54 : */
55 :
56 : /**
57 : * gfxSkipCharsBuilder is a helper class that accumulates a list of (skip, keep)
58 : * commands and can eventually be used to construct a real gfxSkipChars.
59 : * gfxSkipCharsBuilder objects are quite large so don't keep these around.
60 : * On the positive side, the Skip/KeepChar(s) methods are very efficient,
61 : * especially when you have runs of all-kept or all-skipped characters.
62 : *
63 : * mBuffer is an array of bytes; even numbered bytes represent characters kept,
64 : * odd numbered bytes represent characters skipped. After those characters
65 : * are accounted for, we have mRunCharCount characters which are kept or
66 : * skipped depending on the value of mRunSkipped.
67 : *
68 : * mCharCount is the sum of counts of all skipped and kept characters, i.e.,
69 : * the length of the original string.
70 : */
71 : class THEBES_API gfxSkipCharsBuilder {
72 : public:
73 : gfxSkipCharsBuilder() :
74 : mCharCount(0), mRunCharCount(0), mRunSkipped(false), mInErrorState(false)
75 : {}
76 :
77 : void SkipChars(PRUint32 aChars) {
78 : DoChars(aChars, true);
79 : }
80 : void KeepChars(PRUint32 aChars) {
81 : DoChars(aChars, false);
82 : }
83 : void SkipChar() {
84 : SkipChars(1);
85 : }
86 : void KeepChar() {
87 : KeepChars(1);
88 : }
89 : void DoChars(PRUint32 aChars, bool aSkipped) {
90 : if (aSkipped != mRunSkipped && aChars > 0) {
91 : FlushRun();
92 : }
93 : NS_ASSERTION(mRunCharCount + aChars > mRunCharCount,
94 : "Character count overflow");
95 : mRunCharCount += aChars;
96 : }
97 :
98 : bool IsOK() { return !mInErrorState; }
99 :
100 : PRUint32 GetCharCount() { return mCharCount + mRunCharCount; }
101 : bool GetAllCharsKept() { return mBuffer.Length() == 0; }
102 :
103 : friend class gfxSkipChars;
104 :
105 : private:
106 : typedef AutoFallibleTArray<PRUint8,256> Buffer;
107 :
108 : /**
109 : * Moves mRunCharCount/mRunSkipped to the buffer (updating mCharCount),
110 : * sets mRunCharCount to zero and toggles mRunSkipped.
111 : */
112 : void FlushRun();
113 :
114 : Buffer mBuffer;
115 : PRUint32 mCharCount;
116 : PRUint32 mRunCharCount;
117 : bool mRunSkipped; // == mBuffer.Length()&1
118 : bool mInErrorState;
119 : };
120 :
121 : /**
122 : * The gfxSkipChars list is represented as a list of bytes of the form
123 : * [chars to keep, chars to skip, chars to keep, chars to skip, ...]
124 : * In the special case where all chars are to be kept, the list is length
125 : * zero.
126 : *
127 : * A freshly-created gfxSkipChars means "all chars kept".
128 : */
129 0 : class THEBES_API gfxSkipChars {
130 : public:
131 0 : gfxSkipChars() : mListLength(0), mCharCount(0) {}
132 :
133 0 : void TakeFrom(gfxSkipChars* aSkipChars) {
134 0 : mList = aSkipChars->mList.forget();
135 0 : mListLength = aSkipChars->mListLength;
136 0 : mCharCount = aSkipChars->mCharCount;
137 0 : aSkipChars->mCharCount = 0;
138 0 : aSkipChars->mListLength = 0;
139 0 : BuildShortcuts();
140 0 : }
141 :
142 : void TakeFrom(gfxSkipCharsBuilder* aSkipCharsBuilder) {
143 : if (!aSkipCharsBuilder->mBuffer.Length()) {
144 : NS_ASSERTION(!aSkipCharsBuilder->mRunSkipped, "out of sync");
145 : // all characters kept
146 : mCharCount = aSkipCharsBuilder->mRunCharCount;
147 : mList = nsnull;
148 : mListLength = 0;
149 : } else {
150 : aSkipCharsBuilder->FlushRun();
151 : mCharCount = aSkipCharsBuilder->mCharCount;
152 : mList = new PRUint8[aSkipCharsBuilder->mBuffer.Length()];
153 : if (!mList) {
154 : mListLength = 0;
155 : } else {
156 : mListLength = aSkipCharsBuilder->mBuffer.Length();
157 : memcpy(mList, aSkipCharsBuilder->mBuffer.Elements(), mListLength);
158 : }
159 : }
160 : aSkipCharsBuilder->mBuffer.Clear();
161 : aSkipCharsBuilder->mCharCount = 0;
162 : aSkipCharsBuilder->mRunCharCount = 0;
163 : aSkipCharsBuilder->mRunSkipped = false;
164 : BuildShortcuts();
165 : }
166 :
167 : void SetAllKeep(PRUint32 aLength) {
168 : mCharCount = aLength;
169 : mList = nsnull;
170 : mListLength = 0;
171 : }
172 :
173 : PRInt32 GetOriginalCharCount() const { return mCharCount; }
174 :
175 : friend class gfxSkipCharsIterator;
176 :
177 : private:
178 : struct Shortcut {
179 : PRUint32 mListPrefixLength;
180 : PRUint32 mListPrefixCharCount;
181 : PRUint32 mListPrefixKeepCharCount;
182 :
183 0 : Shortcut() {}
184 0 : Shortcut(PRUint32 aListPrefixLength, PRUint32 aListPrefixCharCount,
185 : PRUint32 aListPrefixKeepCharCount) :
186 : mListPrefixLength(aListPrefixLength),
187 : mListPrefixCharCount(aListPrefixCharCount),
188 0 : mListPrefixKeepCharCount(aListPrefixKeepCharCount) {}
189 : };
190 :
191 : void BuildShortcuts();
192 :
193 : nsAutoArrayPtr<PRUint8> mList;
194 : nsAutoArrayPtr<Shortcut> mShortcuts;
195 : PRUint32 mListLength;
196 : PRUint32 mCharCount;
197 : };
198 :
199 : /**
200 : * A gfxSkipCharsIterator represents a position in the original string. It lets you
201 : * map efficiently to and from positions in the string after skipped characters
202 : * have been removed. You can also specify an offset that is added to all
203 : * incoming original string offsets and subtracted from all outgoing original
204 : * string offsets --- useful when the gfxSkipChars corresponds to something
205 : * offset from the original DOM coordinates, which it often does for gfxTextRuns.
206 : *
207 : * The current positions (in both the original and skipped strings) are
208 : * always constrained to be >= 0 and <= the string length. When the position
209 : * is equal to the string length, it is at the end of the string. The current
210 : * positions do not include any aOriginalStringToSkipCharsOffset.
211 : *
212 : * When the position in the original string corresponds to a skipped character,
213 : * the skipped-characters offset is the offset of the next unskipped character,
214 : * or the skipped-characters string length if there is no next unskipped character.
215 : */
216 : class THEBES_API gfxSkipCharsIterator {
217 : public:
218 : /**
219 : * @param aOriginalStringToSkipCharsOffset add this to all incoming and
220 : * outgoing original string offsets
221 : */
222 : gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
223 : PRInt32 aOriginalStringToSkipCharsOffset,
224 : PRInt32 aOriginalStringOffset)
225 : : mSkipChars(&aSkipChars),
226 : mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
227 : mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
228 : SetOriginalOffset(aOriginalStringOffset);
229 : }
230 :
231 : gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
232 : PRInt32 aOriginalStringToSkipCharsOffset = 0)
233 : : mSkipChars(&aSkipChars),
234 : mOriginalStringOffset(0), mSkippedStringOffset(0),
235 : mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
236 : mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
237 : }
238 :
239 : gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
240 : : mSkipChars(aIterator.mSkipChars),
241 : mOriginalStringOffset(aIterator.mOriginalStringOffset),
242 : mSkippedStringOffset(aIterator.mSkippedStringOffset),
243 : mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset),
244 : mListPrefixLength(aIterator.mListPrefixLength),
245 : mListPrefixCharCount(aIterator.mListPrefixCharCount),
246 : mListPrefixKeepCharCount(aIterator.mListPrefixKeepCharCount)
247 : {}
248 :
249 : /**
250 : * The empty constructor creates an object that is useless until it is assigned.
251 : */
252 : gfxSkipCharsIterator() : mSkipChars(nsnull) {}
253 :
254 : /**
255 : * Return true if this iterator is properly initialized and usable.
256 : */
257 : bool IsInitialized() { return mSkipChars != nsnull; }
258 :
259 : /**
260 : * Set the iterator to aOriginalStringOffset in the original string.
261 : * This can efficiently move forward or backward from the current position.
262 : * aOriginalStringOffset is clamped to [0,originalStringLength].
263 : */
264 : void SetOriginalOffset(PRInt32 aOriginalStringOffset) {
265 : SetOffsets(aOriginalStringOffset + mOriginalStringToSkipCharsOffset, true);
266 : }
267 :
268 : /**
269 : * Set the iterator to aSkippedStringOffset in the skipped string.
270 : * This can efficiently move forward or backward from the current position.
271 : * aSkippedStringOffset is clamped to [0,skippedStringLength].
272 : */
273 : void SetSkippedOffset(PRUint32 aSkippedStringOffset) {
274 : SetOffsets(aSkippedStringOffset, false);
275 : }
276 :
277 : PRUint32 ConvertOriginalToSkipped(PRInt32 aOriginalStringOffset) {
278 : SetOriginalOffset(aOriginalStringOffset);
279 : return GetSkippedOffset();
280 : }
281 : PRUint32 ConvertSkippedToOriginal(PRInt32 aSkippedStringOffset) {
282 : SetSkippedOffset(aSkippedStringOffset);
283 : return GetOriginalOffset();
284 : }
285 :
286 : /**
287 : * Test if the character at the current position in the original string
288 : * is skipped or not. If aRunLength is non-null, then *aRunLength is set
289 : * to a number of characters all of which are either skipped or not, starting
290 : * at this character. When the current position is at the end of the original
291 : * string, we return true and *aRunLength is set to zero.
292 : */
293 : bool IsOriginalCharSkipped(PRInt32* aRunLength = nsnull) const;
294 :
295 : void AdvanceOriginal(PRInt32 aDelta) {
296 : SetOffsets(mOriginalStringOffset + aDelta, true);
297 : }
298 : void AdvanceSkipped(PRInt32 aDelta) {
299 : SetOffsets(mSkippedStringOffset + aDelta, false);
300 : }
301 :
302 : /**
303 : * @return the offset within the original string
304 : */
305 : PRInt32 GetOriginalOffset() const {
306 : return mOriginalStringOffset - mOriginalStringToSkipCharsOffset;
307 : }
308 : /**
309 : * @return the offset within the skipped string corresponding to the
310 : * current position in the original string. If the current position
311 : * in the original string is a character that is skipped, then we return
312 : * the position corresponding to the first non-skipped character in the
313 : * original string after the current position, or the length of the skipped
314 : * string if there is no such character.
315 : */
316 : PRUint32 GetSkippedOffset() const { return mSkippedStringOffset; }
317 :
318 : PRInt32 GetOriginalEnd() const {
319 : return mSkipChars->GetOriginalCharCount() -
320 : mOriginalStringToSkipCharsOffset;
321 : }
322 :
323 : private:
324 : void SetOffsets(PRUint32 aOffset, bool aInOriginalString);
325 :
326 : const gfxSkipChars* mSkipChars;
327 : PRInt32 mOriginalStringOffset;
328 : PRUint32 mSkippedStringOffset;
329 :
330 : // This offset is added to map from "skipped+unskipped characters in
331 : // the original DOM string" character space to "skipped+unskipped
332 : // characters in the textrun's gfxSkipChars" character space
333 : PRInt32 mOriginalStringToSkipCharsOffset;
334 :
335 : /*
336 : * This is used to speed up cursor-style traversal. The invariant is that
337 : * the first mListPrefixLength bytes of mSkipChars.mList sum to
338 : * mListPrefixCharCount, and the even-indexed bytes in that prefix sum to
339 : * mListPrefixKeepCharCount.
340 : * Also, 0 <= mListPrefixLength < mSkipChars.mListLength, or else
341 : * mSkipChars.mListLength is zero.
342 : * Also, mListPrefixCharCount <= mOriginalStringOffset (and therefore
343 : * mListPrefixKeepCharCount < mSkippedStringOffset).
344 : */
345 : PRUint32 mListPrefixLength;
346 : PRUint32 mListPrefixCharCount;
347 : PRUint32 mListPrefixKeepCharCount;
348 : };
349 :
350 : #endif /*GFX_SKIP_CHARS_H*/
|