1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is IBM Corporation.
19 : * Portions created by IBM Corporation are Copyright (C) 2003
20 : * IBM Corporation. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <stdlib.h>
40 : #include "nsScannerString.h"
41 :
42 :
43 : /**
44 : * nsScannerBufferList
45 : */
46 :
47 : #define MAX_CAPACITY ((PR_UINT32_MAX / sizeof(PRUnichar)) - \
48 : (sizeof(Buffer) + sizeof(PRUnichar)))
49 :
50 : nsScannerBufferList::Buffer*
51 292 : nsScannerBufferList::AllocBufferFromString( const nsAString& aString )
52 : {
53 292 : PRUint32 len = aString.Length();
54 :
55 292 : if (len > MAX_CAPACITY)
56 0 : return nsnull;
57 :
58 292 : Buffer* buf = (Buffer*) malloc(sizeof(Buffer) + (len + 1) * sizeof(PRUnichar));
59 292 : if (buf)
60 : {
61 : // leave PRCList members of Buffer uninitialized
62 :
63 292 : buf->mUsageCount = 0;
64 292 : buf->mDataEnd = buf->DataStart() + len;
65 :
66 292 : nsAString::const_iterator source;
67 292 : aString.BeginReading(source);
68 292 : nsCharTraits<PRUnichar>::copy(buf->DataStart(), source.get(), len);
69 :
70 : // XXX null terminate. this shouldn't be required, but we do it because
71 : // nsScanner erroneously thinks it can dereference DataEnd :-(
72 292 : *buf->mDataEnd = PRUnichar(0);
73 : }
74 292 : return buf;
75 : }
76 :
77 : nsScannerBufferList::Buffer*
78 3491 : nsScannerBufferList::AllocBuffer( PRUint32 capacity )
79 : {
80 3491 : if (capacity > MAX_CAPACITY)
81 0 : return nsnull;
82 :
83 3491 : Buffer* buf = (Buffer*) malloc(sizeof(Buffer) + (capacity + 1) * sizeof(PRUnichar));
84 3491 : if (buf)
85 : {
86 : // leave PRCList members of Buffer uninitialized
87 :
88 3491 : buf->mUsageCount = 0;
89 3491 : buf->mDataEnd = buf->DataStart() + capacity;
90 :
91 : // XXX null terminate. this shouldn't be required, but we do it because
92 : // nsScanner erroneously thinks it can dereference DataEnd :-(
93 3491 : *buf->mDataEnd = PRUnichar(0);
94 : }
95 3491 : return buf;
96 : }
97 :
98 : void
99 3576 : nsScannerBufferList::ReleaseAll()
100 : {
101 7166 : while (!PR_CLIST_IS_EMPTY(&mBuffers))
102 : {
103 14 : PRCList* node = PR_LIST_HEAD(&mBuffers);
104 14 : PR_REMOVE_LINK(node);
105 : //printf(">>> freeing buffer @%p\n", node);
106 14 : free(static_cast<Buffer*>(node));
107 : }
108 3576 : }
109 :
110 : void
111 0 : nsScannerBufferList::SplitBuffer( const Position& pos )
112 : {
113 : // splitting to the right keeps the work string and any extant token
114 : // pointing to and holding a reference count on the same buffer.
115 :
116 0 : Buffer* bufferToSplit = pos.mBuffer;
117 0 : NS_ASSERTION(bufferToSplit, "null pointer");
118 :
119 0 : PRUint32 splitOffset = pos.mPosition - bufferToSplit->DataStart();
120 0 : NS_ASSERTION(pos.mPosition >= bufferToSplit->DataStart() &&
121 : splitOffset <= bufferToSplit->DataLength(),
122 : "split offset is outside buffer");
123 :
124 0 : PRUint32 len = bufferToSplit->DataLength() - splitOffset;
125 0 : Buffer* new_buffer = AllocBuffer(len);
126 0 : if (new_buffer)
127 : {
128 : nsCharTraits<PRUnichar>::copy(new_buffer->DataStart(),
129 0 : bufferToSplit->DataStart() + splitOffset,
130 0 : len);
131 0 : InsertAfter(new_buffer, bufferToSplit);
132 0 : bufferToSplit->SetDataLength(splitOffset);
133 : }
134 0 : }
135 :
136 : void
137 29025 : nsScannerBufferList::DiscardUnreferencedPrefix( Buffer* aBuf )
138 : {
139 29025 : if (aBuf == Head())
140 : {
141 58767 : while (!PR_CLIST_IS_EMPTY(&mBuffers) && !Head()->IsInUse())
142 : {
143 3769 : Buffer* buffer = Head();
144 3769 : PR_REMOVE_LINK(buffer);
145 3769 : free(buffer);
146 : }
147 : }
148 29025 : }
149 :
150 : size_t
151 73875 : nsScannerBufferList::Position::Distance( const Position& aStart, const Position& aEnd )
152 : {
153 73875 : size_t result = 0;
154 73875 : if (aStart.mBuffer == aEnd.mBuffer)
155 : {
156 73587 : result = aEnd.mPosition - aStart.mPosition;
157 : }
158 : else
159 : {
160 288 : result = aStart.mBuffer->DataEnd() - aStart.mPosition;
161 346 : for (Buffer* b = aStart.mBuffer->Next(); b != aEnd.mBuffer; b = b->Next())
162 58 : result += b->DataLength();
163 288 : result += aEnd.mPosition - aEnd.mBuffer->DataStart();
164 : }
165 73875 : return result;
166 : }
167 :
168 :
169 : /**
170 : * nsScannerSubstring
171 : */
172 :
173 4376 : nsScannerSubstring::nsScannerSubstring()
174 : : mStart(nsnull, nsnull)
175 : , mEnd(nsnull, nsnull)
176 : , mBufferList(nsnull)
177 : , mLength(0)
178 4376 : , mIsDirty(true)
179 : {
180 4376 : }
181 :
182 263 : nsScannerSubstring::nsScannerSubstring( const nsAString& s )
183 : : mBufferList(nsnull)
184 263 : , mIsDirty(true)
185 : {
186 263 : Rebind(s);
187 263 : }
188 :
189 9278 : nsScannerSubstring::~nsScannerSubstring()
190 : {
191 4639 : release_ownership_of_buffer_list();
192 4639 : }
193 :
194 : PRInt32
195 25 : nsScannerSubstring::CountChar( PRUnichar c ) const
196 : {
197 : /*
198 : re-write this to use a counting sink
199 : */
200 :
201 25 : size_type result = 0;
202 25 : size_type lengthToExamine = Length();
203 :
204 25 : nsScannerIterator iter;
205 25 : for ( BeginReading(iter); ; )
206 : {
207 25 : PRInt32 lengthToExamineInThisFragment = iter.size_forward();
208 25 : const PRUnichar* fromBegin = iter.get();
209 25 : result += size_type(NS_COUNT(fromBegin, fromBegin+lengthToExamineInThisFragment, c));
210 25 : if ( !(lengthToExamine -= lengthToExamineInThisFragment) )
211 25 : return result;
212 0 : iter.advance(lengthToExamineInThisFragment);
213 : }
214 : // never reached; quiets warnings
215 : return 0;
216 : }
217 :
218 : void
219 1063 : nsScannerSubstring::Rebind( const nsScannerSubstring& aString,
220 : const nsScannerIterator& aStart,
221 : const nsScannerIterator& aEnd )
222 : {
223 : // allow for the case where &aString == this
224 :
225 1063 : aString.acquire_ownership_of_buffer_list();
226 1063 : release_ownership_of_buffer_list();
227 :
228 1063 : mStart = aStart;
229 1063 : mEnd = aEnd;
230 1063 : mBufferList = aString.mBufferList;
231 1063 : mLength = Distance(aStart, aEnd);
232 1063 : mIsDirty = true;
233 1063 : }
234 :
235 : void
236 288 : nsScannerSubstring::Rebind( const nsAString& aString )
237 : {
238 288 : release_ownership_of_buffer_list();
239 :
240 576 : mBufferList = new nsScannerBufferList(AllocBufferFromString(aString));
241 288 : mIsDirty = true;
242 :
243 288 : init_range_from_buffer_list();
244 288 : acquire_ownership_of_buffer_list();
245 288 : }
246 :
247 : const nsSubstring&
248 2105 : nsScannerSubstring::AsString() const
249 : {
250 2105 : if (mIsDirty)
251 : {
252 1038 : nsScannerSubstring* mutable_this = const_cast<nsScannerSubstring*>(this);
253 :
254 1038 : if (mStart.mBuffer == mEnd.mBuffer) {
255 : // We only have a single fragment to deal with, so just return it
256 : // as a substring.
257 1038 : mutable_this->mFlattenedRep.Rebind(mStart.mPosition, mEnd.mPosition);
258 : } else {
259 : // Otherwise, we need to copy the data into a flattened buffer.
260 0 : nsScannerIterator start, end;
261 0 : CopyUnicodeTo(BeginReading(start), EndReading(end), mutable_this->mFlattenedRep);
262 : }
263 :
264 1038 : mutable_this->mIsDirty = false;
265 : }
266 :
267 2105 : return mFlattenedRep;
268 : }
269 :
270 : nsScannerIterator&
271 34642 : nsScannerSubstring::BeginReading( nsScannerIterator& iter ) const
272 : {
273 34642 : iter.mOwner = this;
274 :
275 34642 : iter.mFragment.mBuffer = mStart.mBuffer;
276 34642 : iter.mFragment.mFragmentStart = mStart.mPosition;
277 34642 : if (mStart.mBuffer == mEnd.mBuffer)
278 32381 : iter.mFragment.mFragmentEnd = mEnd.mPosition;
279 : else
280 2261 : iter.mFragment.mFragmentEnd = mStart.mBuffer->DataEnd();
281 :
282 34642 : iter.mPosition = mStart.mPosition;
283 34642 : iter.normalize_forward();
284 34642 : return iter;
285 : }
286 :
287 : nsScannerIterator&
288 3495 : nsScannerSubstring::EndReading( nsScannerIterator& iter ) const
289 : {
290 3495 : iter.mOwner = this;
291 :
292 3495 : iter.mFragment.mBuffer = mEnd.mBuffer;
293 3495 : iter.mFragment.mFragmentEnd = mEnd.mPosition;
294 3495 : if (mStart.mBuffer == mEnd.mBuffer)
295 3288 : iter.mFragment.mFragmentStart = mStart.mPosition;
296 : else
297 207 : iter.mFragment.mFragmentStart = mEnd.mBuffer->DataStart();
298 :
299 3495 : iter.mPosition = mEnd.mPosition;
300 : // must not |normalize_backward| as that would likely invalidate tests like |while ( first != last )|
301 3495 : return iter;
302 : }
303 :
304 : bool
305 30267 : nsScannerSubstring::GetNextFragment( nsScannerFragment& frag ) const
306 : {
307 : // check to see if we are at the end of the buffer list
308 30267 : if (frag.mBuffer == mEnd.mBuffer)
309 29838 : return false;
310 :
311 429 : frag.mBuffer = static_cast<const Buffer*>(PR_NEXT_LINK(frag.mBuffer));
312 :
313 429 : if (frag.mBuffer == mStart.mBuffer)
314 0 : frag.mFragmentStart = mStart.mPosition;
315 : else
316 429 : frag.mFragmentStart = frag.mBuffer->DataStart();
317 :
318 429 : if (frag.mBuffer == mEnd.mBuffer)
319 359 : frag.mFragmentEnd = mEnd.mPosition;
320 : else
321 70 : frag.mFragmentEnd = frag.mBuffer->DataEnd();
322 :
323 429 : return true;
324 : }
325 :
326 : bool
327 14 : nsScannerSubstring::GetPrevFragment( nsScannerFragment& frag ) const
328 : {
329 : // check to see if we are at the beginning of the buffer list
330 14 : if (frag.mBuffer == mStart.mBuffer)
331 0 : return false;
332 :
333 14 : frag.mBuffer = static_cast<const Buffer*>(PR_PREV_LINK(frag.mBuffer));
334 :
335 14 : if (frag.mBuffer == mStart.mBuffer)
336 2 : frag.mFragmentStart = mStart.mPosition;
337 : else
338 12 : frag.mFragmentStart = frag.mBuffer->DataStart();
339 :
340 14 : if (frag.mBuffer == mEnd.mBuffer)
341 0 : frag.mFragmentEnd = mEnd.mPosition;
342 : else
343 14 : frag.mFragmentEnd = frag.mBuffer->DataEnd();
344 :
345 14 : return true;
346 : }
347 :
348 :
349 : /**
350 : * nsScannerString
351 : */
352 :
353 3288 : nsScannerString::nsScannerString( Buffer* aBuf )
354 : {
355 3288 : mBufferList = new nsScannerBufferList(aBuf);
356 :
357 3288 : init_range_from_buffer_list();
358 3288 : acquire_ownership_of_buffer_list();
359 3288 : }
360 :
361 : void
362 207 : nsScannerString::AppendBuffer( Buffer* aBuf )
363 : {
364 207 : mBufferList->Append(aBuf);
365 207 : mLength += aBuf->DataLength();
366 :
367 207 : mEnd.mBuffer = aBuf;
368 207 : mEnd.mPosition = aBuf->DataEnd();
369 :
370 207 : mIsDirty = true;
371 207 : }
372 :
373 : void
374 22227 : nsScannerString::DiscardPrefix( const nsScannerIterator& aIter )
375 : {
376 22227 : Position old_start(mStart);
377 22227 : mStart = aIter;
378 22227 : mLength -= Position::Distance(old_start, mStart);
379 :
380 22227 : mStart.mBuffer->IncrementUsageCount();
381 22227 : old_start.mBuffer->DecrementUsageCount();
382 :
383 22227 : mBufferList->DiscardUnreferencedPrefix(old_start.mBuffer);
384 :
385 22227 : mIsDirty = true;
386 22227 : }
387 :
388 : void
389 0 : nsScannerString::UngetReadable( const nsAString& aReadable, const nsScannerIterator& aInsertPoint )
390 : /*
391 : * Warning: this routine manipulates the shared buffer list in an unexpected way.
392 : * The original design did not really allow for insertions, but this call promises
393 : * that if called for a point after the end of all extant token strings, that no token string
394 : * or the work string will be invalidated.
395 : *
396 : * This routine is protected because it is the responsibility of the derived class to keep those promises.
397 : */
398 : {
399 0 : Position insertPos(aInsertPoint);
400 :
401 0 : mBufferList->SplitBuffer(insertPos);
402 : // splitting to the right keeps the work string and any extant token pointing to and
403 : // holding a reference count on the same buffer
404 :
405 0 : Buffer* new_buffer = AllocBufferFromString(aReadable);
406 : // make a new buffer with all the data to insert...
407 : // BULLSHIT ALERT: we may have empty space to re-use in the split buffer, measure the cost
408 : // of this and decide if we should do the work to fill it
409 :
410 0 : Buffer* buffer_to_split = insertPos.mBuffer;
411 0 : mBufferList->InsertAfter(new_buffer, buffer_to_split);
412 0 : mLength += aReadable.Length();
413 :
414 0 : mEnd.mBuffer = mBufferList->Tail();
415 0 : mEnd.mPosition = mEnd.mBuffer->DataEnd();
416 :
417 0 : mIsDirty = true;
418 0 : }
419 :
420 : void
421 0 : nsScannerString::ReplaceCharacter(nsScannerIterator& aPosition, PRUnichar aChar)
422 : {
423 : // XXX Casting a const to non-const. Unless the base class
424 : // provides support for writing iterators, this is the best
425 : // that can be done.
426 0 : PRUnichar* pos = const_cast<PRUnichar*>(aPosition.get());
427 0 : *pos = aChar;
428 :
429 0 : mIsDirty = true;
430 0 : }
431 :
432 :
433 : /**
434 : * nsScannerSharedSubstring
435 : */
436 :
437 : void
438 2176 : nsScannerSharedSubstring::Rebind(const nsScannerIterator &aStart,
439 : const nsScannerIterator &aEnd)
440 : {
441 : // If the start and end positions are inside the same buffer, we must
442 : // acquire ownership of the buffer. If not, we can optimize by not holding
443 : // onto it.
444 :
445 2176 : Buffer *buffer = const_cast<Buffer*>(aStart.buffer());
446 2176 : bool sameBuffer = buffer == aEnd.buffer();
447 :
448 : nsScannerBufferList *bufferList;
449 :
450 2176 : if (sameBuffer) {
451 2159 : bufferList = aStart.mOwner->mBufferList;
452 2159 : bufferList->AddRef();
453 2159 : buffer->IncrementUsageCount();
454 : }
455 :
456 2176 : if (mBufferList)
457 0 : ReleaseBuffer();
458 :
459 2176 : if (sameBuffer) {
460 2159 : mBuffer = buffer;
461 2159 : mBufferList = bufferList;
462 2159 : mString.Rebind(aStart.mPosition, aEnd.mPosition);
463 : } else {
464 17 : mBuffer = nsnull;
465 17 : mBufferList = nsnull;
466 17 : CopyUnicodeTo(aStart, aEnd, mString);
467 : }
468 2176 : }
469 :
470 : void
471 2159 : nsScannerSharedSubstring::ReleaseBuffer()
472 : {
473 2159 : NS_ASSERTION(mBufferList, "Should only be called with non-null mBufferList");
474 2159 : mBuffer->DecrementUsageCount();
475 2159 : mBufferList->DiscardUnreferencedPrefix(mBuffer);
476 2159 : mBufferList->Release();
477 2159 : }
478 :
479 : void
480 11 : nsScannerSharedSubstring::MakeMutable()
481 : {
482 22 : nsString temp(mString); // this will force a copy of the data
483 11 : mString.Assign(temp); // mString will now share the just-allocated buffer
484 :
485 11 : ReleaseBuffer();
486 :
487 11 : mBuffer = nsnull;
488 11 : mBufferList = nsnull;
489 11 : }
490 :
491 : /**
492 : * utils -- based on code from nsReadableUtils.cpp
493 : */
494 :
495 : // private helper function
496 : static inline
497 : nsAString::iterator&
498 6827 : copy_multifragment_string( nsScannerIterator& first, const nsScannerIterator& last, nsAString::iterator& result )
499 : {
500 : typedef nsCharSourceTraits<nsScannerIterator> source_traits;
501 : typedef nsCharSinkTraits<nsAString::iterator> sink_traits;
502 :
503 17672 : while ( first != last )
504 : {
505 4018 : PRUint32 distance = source_traits::readable_distance(first, last);
506 4018 : sink_traits::write(result, source_traits::read(first), distance);
507 4018 : NS_ASSERTION(distance > 0, "|copy_multifragment_string| will never terminate");
508 4018 : source_traits::advance(first, distance);
509 : }
510 :
511 6827 : return result;
512 : }
513 :
514 : void
515 6803 : CopyUnicodeTo( const nsScannerIterator& aSrcStart,
516 : const nsScannerIterator& aSrcEnd,
517 : nsAString& aDest )
518 : {
519 6803 : nsAString::iterator writer;
520 6803 : if (!EnsureStringLength(aDest, Distance(aSrcStart, aSrcEnd))) {
521 0 : aDest.Truncate();
522 0 : return; // out of memory
523 : }
524 6803 : aDest.BeginWriting(writer);
525 6803 : nsScannerIterator fromBegin(aSrcStart);
526 :
527 6803 : copy_multifragment_string(fromBegin, aSrcEnd, writer);
528 : }
529 :
530 : void
531 2187 : AppendUnicodeTo( const nsScannerIterator& aSrcStart,
532 : const nsScannerIterator& aSrcEnd,
533 : nsScannerSharedSubstring& aDest )
534 : {
535 : // Check whether we can just create a dependent string.
536 2187 : if (aDest.str().IsEmpty()) {
537 : // We can just make |aDest| point to the buffer.
538 : // This will take care of copying if the buffer spans fragments.
539 2176 : aDest.Rebind(aSrcStart, aSrcEnd);
540 : } else {
541 : // The dest string is not empty, so it can't be a dependent substring.
542 11 : AppendUnicodeTo(aSrcStart, aSrcEnd, aDest.writable());
543 : }
544 2187 : }
545 :
546 : void
547 24 : AppendUnicodeTo( const nsScannerIterator& aSrcStart,
548 : const nsScannerIterator& aSrcEnd,
549 : nsAString& aDest )
550 : {
551 24 : nsAString::iterator writer;
552 24 : PRUint32 oldLength = aDest.Length();
553 24 : if (!EnsureStringLength(aDest, oldLength + Distance(aSrcStart, aSrcEnd)))
554 0 : return; // out of memory
555 24 : aDest.BeginWriting(writer).advance(oldLength);
556 24 : nsScannerIterator fromBegin(aSrcStart);
557 :
558 24 : copy_multifragment_string(fromBegin, aSrcEnd, writer);
559 : }
560 :
561 : bool
562 25 : FindCharInReadable( PRUnichar aChar,
563 : nsScannerIterator& aSearchStart,
564 : const nsScannerIterator& aSearchEnd )
565 : {
566 50 : while ( aSearchStart != aSearchEnd )
567 : {
568 : PRInt32 fragmentLength;
569 25 : if ( SameFragment(aSearchStart, aSearchEnd) )
570 0 : fragmentLength = aSearchEnd.get() - aSearchStart.get();
571 : else
572 25 : fragmentLength = aSearchStart.size_forward();
573 :
574 25 : const PRUnichar* charFoundAt = nsCharTraits<PRUnichar>::find(aSearchStart.get(), fragmentLength, aChar);
575 25 : if ( charFoundAt ) {
576 25 : aSearchStart.advance( charFoundAt - aSearchStart.get() );
577 25 : return true;
578 : }
579 :
580 0 : aSearchStart.advance(fragmentLength);
581 : }
582 :
583 0 : return false;
584 : }
585 :
586 : bool
587 25 : FindInReadable( const nsAString& aPattern,
588 : nsScannerIterator& aSearchStart,
589 : nsScannerIterator& aSearchEnd,
590 : const nsStringComparator& compare )
591 : {
592 25 : bool found_it = false;
593 :
594 : // only bother searching at all if we're given a non-empty range to search
595 25 : if ( aSearchStart != aSearchEnd )
596 : {
597 25 : nsAString::const_iterator aPatternStart, aPatternEnd;
598 25 : aPattern.BeginReading(aPatternStart);
599 25 : aPattern.EndReading(aPatternEnd);
600 :
601 : // outer loop keeps searching till we find it or run out of string to search
602 25 : while ( !found_it )
603 : {
604 : // fast inner loop (that's what it's called, not what it is) looks for a potential match
605 75 : while ( aSearchStart != aSearchEnd &&
606 25 : compare(aPatternStart.get(), aSearchStart.get(), 1, 1) )
607 0 : ++aSearchStart;
608 :
609 : // if we broke out of the `fast' loop because we're out of string ... we're done: no match
610 25 : if ( aSearchStart == aSearchEnd )
611 0 : break;
612 :
613 : // otherwise, we're at a potential match, let's see if we really hit one
614 25 : nsAString::const_iterator testPattern(aPatternStart);
615 25 : nsScannerIterator testSearch(aSearchStart);
616 :
617 : // slow inner loop verifies the potential match (found by the `fast' loop) at the current position
618 150 : for(;;)
619 : {
620 : // we already compared the first character in the outer loop,
621 : // so we'll advance before the next comparison
622 175 : ++testPattern;
623 175 : ++testSearch;
624 :
625 : // if we verified all the way to the end of the pattern, then we found it!
626 175 : if ( testPattern == aPatternEnd )
627 : {
628 25 : found_it = true;
629 25 : aSearchEnd = testSearch; // return the exact found range through the parameters
630 25 : break;
631 : }
632 :
633 : // if we got to end of the string we're searching before we hit the end of the
634 : // pattern, we'll never find what we're looking for
635 150 : if ( testSearch == aSearchEnd )
636 : {
637 0 : aSearchStart = aSearchEnd;
638 0 : break;
639 : }
640 :
641 : // else if we mismatched ... it's time to advance to the next search position
642 : // and get back into the `fast' loop
643 150 : if ( compare(testPattern.get(), testSearch.get(), 1, 1) )
644 : {
645 0 : ++aSearchStart;
646 0 : break;
647 : }
648 : }
649 : }
650 : }
651 :
652 25 : return found_it;
653 : }
654 :
655 : /**
656 : * This implementation is simple, but does too much work.
657 : * It searches the entire string from left to right, and returns the last match found, if any.
658 : * This implementation will be replaced when I get |reverse_iterator|s working.
659 : */
660 : bool
661 0 : RFindInReadable( const nsAString& aPattern,
662 : nsScannerIterator& aSearchStart,
663 : nsScannerIterator& aSearchEnd,
664 : const nsStringComparator& aComparator )
665 : {
666 0 : bool found_it = false;
667 :
668 0 : nsScannerIterator savedSearchEnd(aSearchEnd);
669 0 : nsScannerIterator searchStart(aSearchStart), searchEnd(aSearchEnd);
670 :
671 0 : while ( searchStart != searchEnd )
672 : {
673 0 : if ( FindInReadable(aPattern, searchStart, searchEnd, aComparator) )
674 : {
675 0 : found_it = true;
676 :
677 : // this is the best match so far, so remember it
678 0 : aSearchStart = searchStart;
679 0 : aSearchEnd = searchEnd;
680 :
681 : // ...and get ready to search some more
682 : // (it's tempting to set |searchStart=searchEnd| ... but that misses overlapping patterns)
683 0 : ++searchStart;
684 0 : searchEnd = savedSearchEnd;
685 : }
686 : }
687 :
688 : // if we never found it, return an empty range
689 0 : if ( !found_it )
690 0 : aSearchStart = aSearchEnd;
691 :
692 0 : return found_it;
693 : }
|