parser/htmlparser/src/nsScannerString.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial