forked from mirrors/gecko-dev
		
	There's no use case for stateful comparators, so they can be just plain function pointers. This is used in some hot places like CSS selector matching. Differential Revision: https://phabricator.services.mozilla.com/D77084
		
			
				
	
	
		
			578 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			578 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include "nsScannerString.h"
 | 
						|
#include "mozilla/CheckedInt.h"
 | 
						|
 | 
						|
/**
 | 
						|
 * nsScannerBufferList
 | 
						|
 */
 | 
						|
 | 
						|
#define MAX_CAPACITY \
 | 
						|
  ((UINT32_MAX / sizeof(char16_t)) - (sizeof(Buffer) + sizeof(char16_t)))
 | 
						|
 | 
						|
nsScannerBufferList::Buffer* nsScannerBufferList::AllocBufferFromString(
 | 
						|
    const nsAString& aString) {
 | 
						|
  uint32_t len = aString.Length();
 | 
						|
  Buffer* buf = AllocBuffer(len);
 | 
						|
 | 
						|
  if (buf) {
 | 
						|
    nsAString::const_iterator source;
 | 
						|
    aString.BeginReading(source);
 | 
						|
    nsCharTraits<char16_t>::copy(buf->DataStart(), source.get(), len);
 | 
						|
  }
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
nsScannerBufferList::Buffer* nsScannerBufferList::AllocBuffer(
 | 
						|
    uint32_t capacity) {
 | 
						|
  if (capacity > MAX_CAPACITY) return nullptr;
 | 
						|
 | 
						|
  void* ptr = malloc(sizeof(Buffer) + (capacity + 1) * sizeof(char16_t));
 | 
						|
  if (!ptr) return nullptr;
 | 
						|
 | 
						|
  Buffer* buf = new (ptr) Buffer();
 | 
						|
 | 
						|
  buf->mUsageCount = 0;
 | 
						|
  buf->mDataEnd = buf->DataStart() + capacity;
 | 
						|
 | 
						|
  // XXX null terminate.  this shouldn't be required, but we do it because
 | 
						|
  // nsScanner erroneously thinks it can dereference DataEnd :-(
 | 
						|
  *buf->mDataEnd = char16_t(0);
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerBufferList::ReleaseAll() {
 | 
						|
  while (!mBuffers.isEmpty()) {
 | 
						|
    Buffer* node = mBuffers.popFirst();
 | 
						|
    // printf(">>> freeing buffer @%p\n", node);
 | 
						|
    free(node);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerBufferList::SplitBuffer(const Position& pos) {
 | 
						|
  // splitting to the right keeps the work string and any extant token
 | 
						|
  // pointing to and holding a reference count on the same buffer.
 | 
						|
 | 
						|
  Buffer* bufferToSplit = pos.mBuffer;
 | 
						|
  NS_ASSERTION(bufferToSplit, "null pointer");
 | 
						|
 | 
						|
  uint32_t splitOffset = pos.mPosition - bufferToSplit->DataStart();
 | 
						|
  NS_ASSERTION(pos.mPosition >= bufferToSplit->DataStart() &&
 | 
						|
                   splitOffset <= bufferToSplit->DataLength(),
 | 
						|
               "split offset is outside buffer");
 | 
						|
 | 
						|
  uint32_t len = bufferToSplit->DataLength() - splitOffset;
 | 
						|
  Buffer* new_buffer = AllocBuffer(len);
 | 
						|
  if (new_buffer) {
 | 
						|
    nsCharTraits<char16_t>::copy(new_buffer->DataStart(),
 | 
						|
                                 bufferToSplit->DataStart() + splitOffset, len);
 | 
						|
    InsertAfter(new_buffer, bufferToSplit);
 | 
						|
    bufferToSplit->SetDataLength(splitOffset);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerBufferList::DiscardUnreferencedPrefix(Buffer* aBuf) {
 | 
						|
  if (aBuf == Head()) {
 | 
						|
    while (!mBuffers.isEmpty() && !Head()->IsInUse()) {
 | 
						|
      Buffer* buffer = Head();
 | 
						|
      buffer->remove();
 | 
						|
      free(buffer);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
size_t nsScannerBufferList::Position::Distance(const Position& aStart,
 | 
						|
                                               const Position& aEnd) {
 | 
						|
  size_t result = 0;
 | 
						|
  if (aStart.mBuffer == aEnd.mBuffer) {
 | 
						|
    result = aEnd.mPosition - aStart.mPosition;
 | 
						|
  } else {
 | 
						|
    result = aStart.mBuffer->DataEnd() - aStart.mPosition;
 | 
						|
    for (Buffer* b = aStart.mBuffer->Next(); b != aEnd.mBuffer; b = b->Next())
 | 
						|
      result += b->DataLength();
 | 
						|
    result += aEnd.mPosition - aEnd.mBuffer->DataStart();
 | 
						|
  }
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * nsScannerSubstring
 | 
						|
 */
 | 
						|
 | 
						|
nsScannerSubstring::nsScannerSubstring()
 | 
						|
    : mStart(nullptr, nullptr),
 | 
						|
      mEnd(nullptr, nullptr),
 | 
						|
      mBufferList(nullptr),
 | 
						|
      mLength(0),
 | 
						|
      mIsDirty(true) {}
 | 
						|
 | 
						|
nsScannerSubstring::nsScannerSubstring(const nsAString& s)
 | 
						|
    : mBufferList(nullptr), mIsDirty(true) {
 | 
						|
  Rebind(s);
 | 
						|
}
 | 
						|
 | 
						|
nsScannerSubstring::~nsScannerSubstring() {
 | 
						|
  release_ownership_of_buffer_list();
 | 
						|
}
 | 
						|
 | 
						|
int32_t nsScannerSubstring::CountChar(char16_t c) const {
 | 
						|
  /*
 | 
						|
    re-write this to use a counting sink
 | 
						|
   */
 | 
						|
 | 
						|
  size_type result = 0;
 | 
						|
  size_type lengthToExamine = Length();
 | 
						|
 | 
						|
  nsScannerIterator iter;
 | 
						|
  for (BeginReading(iter);;) {
 | 
						|
    int32_t lengthToExamineInThisFragment = iter.size_forward();
 | 
						|
    const char16_t* fromBegin = iter.get();
 | 
						|
    result += size_type(
 | 
						|
        NS_COUNT(fromBegin, fromBegin + lengthToExamineInThisFragment, c));
 | 
						|
    if (!(lengthToExamine -= lengthToExamineInThisFragment)) return result;
 | 
						|
    iter.advance(lengthToExamineInThisFragment);
 | 
						|
  }
 | 
						|
  // never reached; quiets warnings
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerSubstring::Rebind(const nsScannerSubstring& aString,
 | 
						|
                                const nsScannerIterator& aStart,
 | 
						|
                                const nsScannerIterator& aEnd) {
 | 
						|
  // allow for the case where &aString == this
 | 
						|
 | 
						|
  aString.acquire_ownership_of_buffer_list();
 | 
						|
  release_ownership_of_buffer_list();
 | 
						|
 | 
						|
  mStart = aStart;
 | 
						|
  mEnd = aEnd;
 | 
						|
  mBufferList = aString.mBufferList;
 | 
						|
  mLength = Distance(aStart, aEnd);
 | 
						|
  mIsDirty = true;
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerSubstring::Rebind(const nsAString& aString) {
 | 
						|
  release_ownership_of_buffer_list();
 | 
						|
 | 
						|
  mBufferList = new nsScannerBufferList(AllocBufferFromString(aString));
 | 
						|
  mIsDirty = true;
 | 
						|
 | 
						|
  init_range_from_buffer_list();
 | 
						|
  acquire_ownership_of_buffer_list();
 | 
						|
}
 | 
						|
 | 
						|
const nsAString& nsScannerSubstring::AsString() const {
 | 
						|
  if (mIsDirty) {
 | 
						|
    nsScannerSubstring* mutable_this = const_cast<nsScannerSubstring*>(this);
 | 
						|
 | 
						|
    if (mStart.mBuffer == mEnd.mBuffer) {
 | 
						|
      // We only have a single fragment to deal with, so just return it
 | 
						|
      // as a substring.
 | 
						|
      mutable_this->mFlattenedRep.Rebind(mStart.mPosition, mEnd.mPosition);
 | 
						|
    } else {
 | 
						|
      // Otherwise, we need to copy the data into a flattened buffer.
 | 
						|
      nsScannerIterator start, end;
 | 
						|
      CopyUnicodeTo(BeginReading(start), EndReading(end),
 | 
						|
                    mutable_this->mFlattenedRep);
 | 
						|
    }
 | 
						|
 | 
						|
    mutable_this->mIsDirty = false;
 | 
						|
  }
 | 
						|
 | 
						|
  return mFlattenedRep;
 | 
						|
}
 | 
						|
 | 
						|
nsScannerIterator& nsScannerSubstring::BeginReading(
 | 
						|
    nsScannerIterator& iter) const {
 | 
						|
  iter.mOwner = this;
 | 
						|
 | 
						|
  iter.mFragment.mBuffer = mStart.mBuffer;
 | 
						|
  iter.mFragment.mFragmentStart = mStart.mPosition;
 | 
						|
  if (mStart.mBuffer == mEnd.mBuffer)
 | 
						|
    iter.mFragment.mFragmentEnd = mEnd.mPosition;
 | 
						|
  else
 | 
						|
    iter.mFragment.mFragmentEnd = mStart.mBuffer->DataEnd();
 | 
						|
 | 
						|
  iter.mPosition = mStart.mPosition;
 | 
						|
  iter.normalize_forward();
 | 
						|
  return iter;
 | 
						|
}
 | 
						|
 | 
						|
nsScannerIterator& nsScannerSubstring::EndReading(
 | 
						|
    nsScannerIterator& iter) const {
 | 
						|
  iter.mOwner = this;
 | 
						|
 | 
						|
  iter.mFragment.mBuffer = mEnd.mBuffer;
 | 
						|
  iter.mFragment.mFragmentEnd = mEnd.mPosition;
 | 
						|
  if (mStart.mBuffer == mEnd.mBuffer)
 | 
						|
    iter.mFragment.mFragmentStart = mStart.mPosition;
 | 
						|
  else
 | 
						|
    iter.mFragment.mFragmentStart = mEnd.mBuffer->DataStart();
 | 
						|
 | 
						|
  iter.mPosition = mEnd.mPosition;
 | 
						|
  // must not |normalize_backward| as that would likely invalidate tests like
 | 
						|
  // |while ( first != last )|
 | 
						|
  return iter;
 | 
						|
}
 | 
						|
 | 
						|
bool nsScannerSubstring::GetNextFragment(nsScannerFragment& frag) const {
 | 
						|
  // check to see if we are at the end of the buffer list
 | 
						|
  if (frag.mBuffer == mEnd.mBuffer) return false;
 | 
						|
 | 
						|
  frag.mBuffer = frag.mBuffer->getNext();
 | 
						|
 | 
						|
  if (frag.mBuffer == mStart.mBuffer)
 | 
						|
    frag.mFragmentStart = mStart.mPosition;
 | 
						|
  else
 | 
						|
    frag.mFragmentStart = frag.mBuffer->DataStart();
 | 
						|
 | 
						|
  if (frag.mBuffer == mEnd.mBuffer)
 | 
						|
    frag.mFragmentEnd = mEnd.mPosition;
 | 
						|
  else
 | 
						|
    frag.mFragmentEnd = frag.mBuffer->DataEnd();
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool nsScannerSubstring::GetPrevFragment(nsScannerFragment& frag) const {
 | 
						|
  // check to see if we are at the beginning of the buffer list
 | 
						|
  if (frag.mBuffer == mStart.mBuffer) return false;
 | 
						|
 | 
						|
  frag.mBuffer = frag.mBuffer->getPrevious();
 | 
						|
 | 
						|
  if (frag.mBuffer == mStart.mBuffer)
 | 
						|
    frag.mFragmentStart = mStart.mPosition;
 | 
						|
  else
 | 
						|
    frag.mFragmentStart = frag.mBuffer->DataStart();
 | 
						|
 | 
						|
  if (frag.mBuffer == mEnd.mBuffer)
 | 
						|
    frag.mFragmentEnd = mEnd.mPosition;
 | 
						|
  else
 | 
						|
    frag.mFragmentEnd = frag.mBuffer->DataEnd();
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * nsScannerString
 | 
						|
 */
 | 
						|
 | 
						|
nsScannerString::nsScannerString(Buffer* aBuf) {
 | 
						|
  mBufferList = new nsScannerBufferList(aBuf);
 | 
						|
 | 
						|
  init_range_from_buffer_list();
 | 
						|
  acquire_ownership_of_buffer_list();
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerString::AppendBuffer(Buffer* aBuf) {
 | 
						|
  mBufferList->Append(aBuf);
 | 
						|
  mLength += aBuf->DataLength();
 | 
						|
 | 
						|
  mEnd.mBuffer = aBuf;
 | 
						|
  mEnd.mPosition = aBuf->DataEnd();
 | 
						|
 | 
						|
  mIsDirty = true;
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerString::DiscardPrefix(const nsScannerIterator& aIter) {
 | 
						|
  Position old_start(mStart);
 | 
						|
  mStart = aIter;
 | 
						|
  mLength -= Position::Distance(old_start, mStart);
 | 
						|
 | 
						|
  mStart.mBuffer->IncrementUsageCount();
 | 
						|
  old_start.mBuffer->DecrementUsageCount();
 | 
						|
 | 
						|
  mBufferList->DiscardUnreferencedPrefix(old_start.mBuffer);
 | 
						|
 | 
						|
  mIsDirty = true;
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerString::UngetReadable(const nsAString& aReadable,
 | 
						|
                                    const nsScannerIterator& aInsertPoint)
 | 
						|
/*
 | 
						|
 * Warning: this routine manipulates the shared buffer list in an
 | 
						|
 * unexpected way.  The original design did not really allow for
 | 
						|
 * insertions, but this call promises that if called for a point after the
 | 
						|
 * end of all extant token strings, that no token string or the work string
 | 
						|
 * will be invalidated.
 | 
						|
 *
 | 
						|
 * This routine is protected because it is the responsibility of the
 | 
						|
 * derived class to keep those promises.
 | 
						|
 */
 | 
						|
{
 | 
						|
  Position insertPos(aInsertPoint);
 | 
						|
 | 
						|
  mBufferList->SplitBuffer(insertPos);
 | 
						|
  // splitting to the right keeps the work string and any extant token
 | 
						|
  // pointing to and holding a reference count on the same buffer
 | 
						|
 | 
						|
  Buffer* new_buffer = AllocBufferFromString(aReadable);
 | 
						|
  // make a new buffer with all the data to insert...
 | 
						|
  // ALERT: we may have empty space to re-use in the split buffer,
 | 
						|
  // measure the cost of this and decide if we should do the work to fill
 | 
						|
  // it
 | 
						|
 | 
						|
  Buffer* buffer_to_split = insertPos.mBuffer;
 | 
						|
  mBufferList->InsertAfter(new_buffer, buffer_to_split);
 | 
						|
  mLength += aReadable.Length();
 | 
						|
 | 
						|
  mEnd.mBuffer = mBufferList->Tail();
 | 
						|
  mEnd.mPosition = mEnd.mBuffer->DataEnd();
 | 
						|
 | 
						|
  mIsDirty = true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * nsScannerSharedSubstring
 | 
						|
 */
 | 
						|
 | 
						|
void nsScannerSharedSubstring::Rebind(const nsScannerIterator& aStart,
 | 
						|
                                      const nsScannerIterator& aEnd) {
 | 
						|
  // If the start and end positions are inside the same buffer, we must
 | 
						|
  // acquire ownership of the buffer.  If not, we can optimize by not holding
 | 
						|
  // onto it.
 | 
						|
 | 
						|
  Buffer* buffer = const_cast<Buffer*>(aStart.buffer());
 | 
						|
  bool sameBuffer = buffer == aEnd.buffer();
 | 
						|
 | 
						|
  nsScannerBufferList* bufferList;
 | 
						|
 | 
						|
  if (sameBuffer) {
 | 
						|
    bufferList = aStart.mOwner->mBufferList;
 | 
						|
    bufferList->AddRef();
 | 
						|
    buffer->IncrementUsageCount();
 | 
						|
  }
 | 
						|
 | 
						|
  if (mBufferList) ReleaseBuffer();
 | 
						|
 | 
						|
  if (sameBuffer) {
 | 
						|
    mBuffer = buffer;
 | 
						|
    mBufferList = bufferList;
 | 
						|
    mString.Rebind(aStart.mPosition, aEnd.mPosition);
 | 
						|
  } else {
 | 
						|
    mBuffer = nullptr;
 | 
						|
    mBufferList = nullptr;
 | 
						|
    CopyUnicodeTo(aStart, aEnd, mString);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerSharedSubstring::ReleaseBuffer() {
 | 
						|
  NS_ASSERTION(mBufferList, "Should only be called with non-null mBufferList");
 | 
						|
  mBuffer->DecrementUsageCount();
 | 
						|
  mBufferList->DiscardUnreferencedPrefix(mBuffer);
 | 
						|
  mBufferList->Release();
 | 
						|
}
 | 
						|
 | 
						|
void nsScannerSharedSubstring::MakeMutable() {
 | 
						|
  nsString temp(mString);  // this will force a copy of the data
 | 
						|
  mString.Assign(temp);    // mString will now share the just-allocated buffer
 | 
						|
 | 
						|
  ReleaseBuffer();
 | 
						|
 | 
						|
  mBuffer = nullptr;
 | 
						|
  mBufferList = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * utils -- based on code from nsReadableUtils.cpp
 | 
						|
 */
 | 
						|
 | 
						|
// private helper function
 | 
						|
static inline nsAString::iterator& copy_multifragment_string(
 | 
						|
    nsScannerIterator& first, const nsScannerIterator& last,
 | 
						|
    nsAString::iterator& result) {
 | 
						|
  typedef nsCharSourceTraits<nsScannerIterator> source_traits;
 | 
						|
  typedef nsCharSinkTraits<nsAString::iterator> sink_traits;
 | 
						|
 | 
						|
  while (first != last) {
 | 
						|
    uint32_t distance = source_traits::readable_distance(first, last);
 | 
						|
    sink_traits::write(result, source_traits::read(first), distance);
 | 
						|
    NS_ASSERTION(distance > 0,
 | 
						|
                 "|copy_multifragment_string| will never terminate");
 | 
						|
    source_traits::advance(first, distance);
 | 
						|
  }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
bool CopyUnicodeTo(const nsScannerIterator& aSrcStart,
 | 
						|
                   const nsScannerIterator& aSrcEnd, nsAString& aDest) {
 | 
						|
  mozilla::CheckedInt<nsAString::size_type> distance(
 | 
						|
      Distance(aSrcStart, aSrcEnd));
 | 
						|
  if (!distance.isValid()) {
 | 
						|
    return false;  // overflow detected
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aDest.SetLength(distance.value(), mozilla::fallible)) {
 | 
						|
    aDest.Truncate();
 | 
						|
    return false;  // out of memory
 | 
						|
  }
 | 
						|
  auto writer = aDest.BeginWriting();
 | 
						|
  nsScannerIterator fromBegin(aSrcStart);
 | 
						|
 | 
						|
  copy_multifragment_string(fromBegin, aSrcEnd, writer);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool AppendUnicodeTo(const nsScannerIterator& aSrcStart,
 | 
						|
                     const nsScannerIterator& aSrcEnd,
 | 
						|
                     nsScannerSharedSubstring& aDest) {
 | 
						|
  // Check whether we can just create a dependent string.
 | 
						|
  if (aDest.str().IsEmpty()) {
 | 
						|
    // We can just make |aDest| point to the buffer.
 | 
						|
    // This will take care of copying if the buffer spans fragments.
 | 
						|
    aDest.Rebind(aSrcStart, aSrcEnd);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  // The dest string is not empty, so it can't be a dependent substring.
 | 
						|
  return AppendUnicodeTo(aSrcStart, aSrcEnd, aDest.writable());
 | 
						|
}
 | 
						|
 | 
						|
bool AppendUnicodeTo(const nsScannerIterator& aSrcStart,
 | 
						|
                     const nsScannerIterator& aSrcEnd, nsAString& aDest) {
 | 
						|
  const nsAString::size_type oldLength = aDest.Length();
 | 
						|
  mozilla::CheckedInt<nsAString::size_type> newLen(
 | 
						|
      Distance(aSrcStart, aSrcEnd));
 | 
						|
  newLen += oldLength;
 | 
						|
  if (!newLen.isValid()) {
 | 
						|
    return false;  // overflow detected
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aDest.SetLength(newLen.value(), mozilla::fallible))
 | 
						|
    return false;  // out of memory
 | 
						|
  auto writer = aDest.BeginWriting();
 | 
						|
  std::advance(writer, oldLength);
 | 
						|
  nsScannerIterator fromBegin(aSrcStart);
 | 
						|
 | 
						|
  copy_multifragment_string(fromBegin, aSrcEnd, writer);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool FindCharInReadable(char16_t aChar, nsScannerIterator& aSearchStart,
 | 
						|
                        const nsScannerIterator& aSearchEnd) {
 | 
						|
  while (aSearchStart != aSearchEnd) {
 | 
						|
    int32_t fragmentLength;
 | 
						|
    if (SameFragment(aSearchStart, aSearchEnd))
 | 
						|
      fragmentLength = aSearchEnd.get() - aSearchStart.get();
 | 
						|
    else
 | 
						|
      fragmentLength = aSearchStart.size_forward();
 | 
						|
 | 
						|
    const char16_t* charFoundAt =
 | 
						|
        nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
 | 
						|
    if (charFoundAt) {
 | 
						|
      aSearchStart.advance(charFoundAt - aSearchStart.get());
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    aSearchStart.advance(fragmentLength);
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool FindInReadable(const nsAString& aPattern, nsScannerIterator& aSearchStart,
 | 
						|
                    nsScannerIterator& aSearchEnd,
 | 
						|
                    const nsStringComparator compare) {
 | 
						|
  bool found_it = false;
 | 
						|
 | 
						|
  // only bother searching at all if we're given a non-empty range to search
 | 
						|
  if (aSearchStart != aSearchEnd) {
 | 
						|
    nsAString::const_iterator aPatternStart, aPatternEnd;
 | 
						|
    aPattern.BeginReading(aPatternStart);
 | 
						|
    aPattern.EndReading(aPatternEnd);
 | 
						|
 | 
						|
    // outer loop keeps searching till we find it or run out of string to search
 | 
						|
    while (!found_it) {
 | 
						|
      // fast inner loop (that's what it's called, not what it is) looks for a
 | 
						|
      // potential match
 | 
						|
      while (aSearchStart != aSearchEnd &&
 | 
						|
             compare(aPatternStart.get(), aSearchStart.get(), 1, 1))
 | 
						|
        ++aSearchStart;
 | 
						|
 | 
						|
      // if we broke out of the `fast' loop because we're out of string ...
 | 
						|
      // we're done: no match
 | 
						|
      if (aSearchStart == aSearchEnd) break;
 | 
						|
 | 
						|
      // otherwise, we're at a potential match, let's see if we really hit one
 | 
						|
      nsAString::const_iterator testPattern(aPatternStart);
 | 
						|
      nsScannerIterator testSearch(aSearchStart);
 | 
						|
 | 
						|
      // slow inner loop verifies the potential match (found by the `fast' loop)
 | 
						|
      // at the current position
 | 
						|
      for (;;) {
 | 
						|
        // we already compared the first character in the outer loop,
 | 
						|
        //  so we'll advance before the next comparison
 | 
						|
        ++testPattern;
 | 
						|
        ++testSearch;
 | 
						|
 | 
						|
        // if we verified all the way to the end of the pattern, then we found
 | 
						|
        // it!
 | 
						|
        if (testPattern == aPatternEnd) {
 | 
						|
          found_it = true;
 | 
						|
          aSearchEnd = testSearch;  // return the exact found range through the
 | 
						|
                                    // parameters
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        // if we got to end of the string we're searching before we hit the end
 | 
						|
        // of the
 | 
						|
        //  pattern, we'll never find what we're looking for
 | 
						|
        if (testSearch == aSearchEnd) {
 | 
						|
          aSearchStart = aSearchEnd;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        // else if we mismatched ... it's time to advance to the next search
 | 
						|
        // position
 | 
						|
        //  and get back into the `fast' loop
 | 
						|
        if (compare(testPattern.get(), testSearch.get(), 1, 1)) {
 | 
						|
          ++aSearchStart;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return found_it;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * This implementation is simple, but does too much work.
 | 
						|
 * It searches the entire string from left to right, and returns the last match
 | 
						|
 * found, if any. This implementation will be replaced when I get
 | 
						|
 * |reverse_iterator|s working.
 | 
						|
 */
 | 
						|
bool RFindInReadable(const nsAString& aPattern, nsScannerIterator& aSearchStart,
 | 
						|
                     nsScannerIterator& aSearchEnd,
 | 
						|
                     const nsStringComparator aComparator) {
 | 
						|
  bool found_it = false;
 | 
						|
 | 
						|
  nsScannerIterator savedSearchEnd(aSearchEnd);
 | 
						|
  nsScannerIterator searchStart(aSearchStart), searchEnd(aSearchEnd);
 | 
						|
 | 
						|
  while (searchStart != searchEnd) {
 | 
						|
    if (FindInReadable(aPattern, searchStart, searchEnd, aComparator)) {
 | 
						|
      found_it = true;
 | 
						|
 | 
						|
      // this is the best match so far, so remember it
 | 
						|
      aSearchStart = searchStart;
 | 
						|
      aSearchEnd = searchEnd;
 | 
						|
 | 
						|
      // ...and get ready to search some more
 | 
						|
      //  (it's tempting to set |searchStart=searchEnd| ... but that misses
 | 
						|
      //  overlapping patterns)
 | 
						|
      ++searchStart;
 | 
						|
      searchEnd = savedSearchEnd;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // if we never found it, return an empty range
 | 
						|
  if (!found_it) aSearchStart = aSearchEnd;
 | 
						|
 | 
						|
  return found_it;
 | 
						|
}
 |