1052 lines
29 KiB
C++
1052 lines
29 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* 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 Mozilla.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications. Portions created by Netscape Communications are
|
|
* Copyright (C) 2001 by Netscape Communications. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Scott Collins <scc@mozilla.org> (original author)
|
|
*/
|
|
|
|
#include "nsAString.h"
|
|
#include "nsDependentSubstring.h"
|
|
#include "nsDependentString.h"
|
|
#include "nsCRT.h"
|
|
|
|
|
|
int
|
|
nsDefaultStringComparator::operator()( const char_type* lhs, const char_type* rhs, PRUint32 aLength ) const
|
|
{
|
|
return nsCharTraits<char_type>::compare(lhs, rhs, aLength);
|
|
}
|
|
|
|
int
|
|
nsDefaultStringComparator::operator()( char_type lhs, char_type rhs) const
|
|
{
|
|
return lhs - rhs;
|
|
}
|
|
|
|
NS_COM
|
|
int
|
|
Compare( const nsAString& lhs, const nsAString& rhs, const nsStringComparator& aComparator )
|
|
{
|
|
typedef nsAString::size_type size_type;
|
|
|
|
if ( &lhs == &rhs )
|
|
return 0;
|
|
|
|
size_type lLength = lhs.Length();
|
|
size_type rLength = rhs.Length();
|
|
size_type lengthToCompare = NS_MIN(lLength, rLength);
|
|
|
|
nsAString::const_iterator leftIter, rightIter;
|
|
lhs.BeginReading(leftIter);
|
|
rhs.BeginReading(rightIter);
|
|
|
|
for (;;)
|
|
{
|
|
size_type lengthAvailable = size_type( NS_MIN(leftIter.size_forward(), rightIter.size_forward()) );
|
|
|
|
if ( lengthAvailable > lengthToCompare )
|
|
lengthAvailable = lengthToCompare;
|
|
|
|
{
|
|
int result;
|
|
// Note: |result| should be declared in this |if| expression, but some compilers don't like that
|
|
if ( (result = aComparator(leftIter.get(), rightIter.get(), lengthAvailable)) != 0 )
|
|
return result;
|
|
}
|
|
|
|
if ( !(lengthToCompare -= lengthAvailable) )
|
|
break;
|
|
|
|
leftIter.advance( PRInt32(lengthAvailable) );
|
|
rightIter.advance( PRInt32(lengthAvailable) );
|
|
}
|
|
|
|
if ( lLength < rLength )
|
|
return -1;
|
|
else if ( rLength < lLength )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
const nsAString::shared_buffer_handle_type*
|
|
nsAString::GetSharedBufferHandle() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const nsAString::buffer_handle_type*
|
|
nsAString::GetFlatBufferHandle() const
|
|
{
|
|
return GetSharedBufferHandle();
|
|
}
|
|
|
|
const nsAString::buffer_handle_type*
|
|
nsAString::GetBufferHandle() const
|
|
{
|
|
return GetSharedBufferHandle();
|
|
}
|
|
|
|
PRUint32
|
|
nsAString::GetImplementationFlags() const
|
|
{
|
|
PRUint32 flags = 0;
|
|
const shared_buffer_handle_type* handle = GetSharedBufferHandle();
|
|
if ( handle )
|
|
flags = handle->GetImplementationFlags();
|
|
return flags;
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsAString::IsVoid() const
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void
|
|
nsAString::SetIsVoid( PRBool )
|
|
{
|
|
// |SetIsVoid| is ignored by default
|
|
}
|
|
|
|
PRBool
|
|
nsAString::Equals( const char_type* rhs, const nsStringComparator& aComparator ) const
|
|
{
|
|
return Equals(nsDependentString(rhs), aComparator);
|
|
}
|
|
|
|
|
|
|
|
nsAString::char_type
|
|
nsAString::First() const
|
|
{
|
|
NS_ASSERTION(Length()>0, "|First()| on an empty string");
|
|
|
|
const_iterator iter;
|
|
return *BeginReading(iter);
|
|
}
|
|
|
|
nsAString::char_type
|
|
nsAString::Last() const
|
|
{
|
|
NS_ASSERTION(Length()>0, "|Last()| on an empty string");
|
|
|
|
const_iterator iter;
|
|
|
|
if ( !IsEmpty() )
|
|
{
|
|
EndReading(iter);
|
|
iter.advance(-1);
|
|
}
|
|
|
|
return *iter; // Note: this has undefined results if |IsEmpty()|
|
|
}
|
|
|
|
nsAString::size_type
|
|
nsAString::CountChar( char_type c ) const
|
|
{
|
|
/*
|
|
re-write this to use a counting sink
|
|
*/
|
|
|
|
size_type result = 0;
|
|
size_type lengthToExamine = Length();
|
|
|
|
const_iterator iter;
|
|
for ( BeginReading(iter); ; )
|
|
{
|
|
PRInt32 lengthToExamineInThisFragment = iter.size_forward();
|
|
const char_type* 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;
|
|
}
|
|
|
|
nsAString::size_type
|
|
nsAString::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
|
|
{
|
|
// If we're just assigning our entire self, give |aResult| the opportunity to share
|
|
if ( aStartPos == 0 && aLengthToCopy >= Length() )
|
|
aResult = *this;
|
|
else
|
|
aResult = Substring(*this, aStartPos, aLengthToCopy);
|
|
|
|
return aResult.Length();
|
|
}
|
|
|
|
nsAString::size_type
|
|
nsAString::Right( self_type& aResult, size_type aLengthToCopy ) const
|
|
{
|
|
size_type myLength = Length();
|
|
aLengthToCopy = NS_MIN(myLength, aLengthToCopy);
|
|
return Mid(aResult, myLength-aLengthToCopy, aLengthToCopy);
|
|
}
|
|
|
|
PRInt32
|
|
nsAString::FindChar( char_type aChar, PRUint32 aOffset ) const
|
|
{
|
|
const_iterator iter, done_searching;
|
|
BeginReading(iter).advance( PRInt32(aOffset) );
|
|
EndReading(done_searching);
|
|
|
|
size_type lengthSearched = 0;
|
|
while ( iter != done_searching )
|
|
{
|
|
PRInt32 fragmentLength = iter.size_forward();
|
|
const char_type* charFoundAt = nsCharTraits<char_type>::find(iter.get(), fragmentLength, aChar);
|
|
if ( charFoundAt )
|
|
return lengthSearched + (charFoundAt-iter.get()) + aOffset;
|
|
|
|
lengthSearched += fragmentLength;
|
|
iter.advance(fragmentLength);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
//
|
|
// |Assign()|
|
|
//
|
|
|
|
void
|
|
nsAString::AssignFromReadable( const self_type& rhs )
|
|
{
|
|
if ( this != &rhs )
|
|
do_AssignFromReadable(rhs);
|
|
// else, self-assign is a no-op
|
|
}
|
|
|
|
void
|
|
nsAString::AssignFromPromise( const self_type& aReadable )
|
|
/*
|
|
...this function is only called when a promise that somehow references |this| is assigned _into_ |this|.
|
|
E.g.,
|
|
|
|
... writable& w ...
|
|
... readable& r ...
|
|
|
|
w = r + w;
|
|
|
|
In this example, you can see that unless the characters promised by |w| in |r+w| are resolved before
|
|
anything starts getting copied into |w|, there will be trouble. They will be overritten by the contents
|
|
of |r| before being retrieved to be appended.
|
|
|
|
We could have a really tricky solution where we tell the promise to resolve _just_ the data promised
|
|
by |this|, but this should be a rare case, since clients with more local knowledge will know that, e.g.,
|
|
in the case above, |Insert| could have special behavior with significantly better performance. Since
|
|
it's a rare case anyway, we should just do the simplest thing that could possibly work, resolve the
|
|
entire promise. If we measure and this turns out to show up on performance radar, we then have the
|
|
option to fix either the callers or this mechanism.
|
|
*/
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_AssignFromReadable(aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
// Note: not exception safe. We need something to manage temporary buffers like this
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_AssignFromElementPtrLength(buffer, length);
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAString::do_AssignFromReadable( const self_type& aReadable )
|
|
{
|
|
SetLength(0);
|
|
if ( !aReadable.IsEmpty() )
|
|
{
|
|
SetLength(aReadable.Length());
|
|
// first setting the length to |0| avoids copying characters only to be overwritten later
|
|
// in the case where the implementation decides to re-allocate
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAString::do_AssignFromElementPtr( const char_type* aPtr )
|
|
{
|
|
do_AssignFromReadable(nsDependentString(aPtr));
|
|
}
|
|
|
|
void
|
|
nsAString::do_AssignFromElementPtrLength( const char_type* aPtr, size_type aLength )
|
|
{
|
|
do_AssignFromReadable(Substring(aPtr, aPtr+aLength));
|
|
}
|
|
|
|
void
|
|
nsAString::do_AssignFromElement( char_type aChar )
|
|
{
|
|
do_AssignFromReadable(Substring(&aChar, &aChar+1));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Append()|
|
|
//
|
|
|
|
void
|
|
nsAString::AppendFromReadable( const self_type& aReadable )
|
|
{
|
|
if ( this != &aReadable )
|
|
do_AppendFromReadable(aReadable);
|
|
else
|
|
AppendFromPromise(aReadable);
|
|
}
|
|
|
|
void
|
|
nsAString::AppendFromPromise( const self_type& aReadable )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_AppendFromReadable(aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_AppendFromElementPtrLength(buffer, length);
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAString::do_AppendFromReadable( const self_type& aReadable )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
SetLength(oldLength + aReadable.Length());
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin).advance( PRInt32(oldLength) ) );
|
|
}
|
|
|
|
void
|
|
nsAString::do_AppendFromElementPtr( const char_type* aPtr )
|
|
{
|
|
do_AppendFromReadable(nsDependentString(aPtr));
|
|
}
|
|
|
|
void
|
|
nsAString::do_AppendFromElementPtrLength( const char_type* aPtr, size_type aLength )
|
|
{
|
|
do_AppendFromReadable(Substring(aPtr, aPtr+aLength));
|
|
}
|
|
|
|
void
|
|
nsAString::do_AppendFromElement( char_type aChar )
|
|
{
|
|
do_AppendFromReadable(Substring(&aChar, &aChar + 1));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Insert()|
|
|
//
|
|
|
|
void
|
|
nsAString::InsertFromReadable( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
if ( this != &aReadable )
|
|
do_InsertFromReadable(aReadable, atPosition);
|
|
else
|
|
InsertFromPromise(aReadable, atPosition);
|
|
}
|
|
|
|
void
|
|
nsAString::InsertFromPromise( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_InsertFromReadable(aReadable, atPosition);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_InsertFromElementPtrLength(buffer, atPosition, length);
|
|
delete buffer;
|
|
}
|
|
// else assert
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAString::do_InsertFromReadable( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
SetLength(oldLength + aReadable.Length());
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( atPosition < oldLength )
|
|
copy_string_backward(this->BeginReading(fromBegin).advance(PRInt32(atPosition)), this->BeginReading(fromEnd).advance(PRInt32(oldLength)), EndWriting(toBegin));
|
|
else
|
|
atPosition = oldLength;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(atPosition)));
|
|
}
|
|
|
|
void
|
|
nsAString::do_InsertFromElementPtr( const char_type* aPtr, index_type atPosition )
|
|
{
|
|
do_InsertFromReadable(nsDependentString(aPtr), atPosition);
|
|
}
|
|
|
|
void
|
|
nsAString::do_InsertFromElementPtrLength( const char_type* aPtr, index_type atPosition, size_type aLength )
|
|
{
|
|
do_InsertFromReadable(Substring(aPtr, aPtr+aLength), atPosition);
|
|
}
|
|
|
|
void
|
|
nsAString::do_InsertFromElement( char_type aChar, index_type atPosition )
|
|
{
|
|
do_InsertFromReadable(Substring(&aChar, &aChar+1), atPosition);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Cut()|
|
|
//
|
|
|
|
void
|
|
nsAString::Cut( index_type cutStart, size_type cutLength )
|
|
{
|
|
size_type myLength = this->Length();
|
|
cutLength = NS_MIN(cutLength, myLength-cutStart);
|
|
index_type cutEnd = cutStart + cutLength;
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( cutEnd < myLength )
|
|
copy_string(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart)));
|
|
SetLength(myLength-cutLength);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Replace()|
|
|
//
|
|
|
|
void
|
|
nsAString::ReplaceFromReadable( index_type cutStart, size_type cutLength, const self_type& aReplacement )
|
|
{
|
|
if ( this != &aReplacement )
|
|
do_ReplaceFromReadable(cutStart, cutLength, aReplacement);
|
|
else
|
|
ReplaceFromPromise(cutStart, cutLength, aReplacement);
|
|
}
|
|
|
|
void
|
|
nsAString::ReplaceFromPromise( index_type cutStart, size_type cutLength, const self_type& aReadable )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_ReplaceFromReadable(cutStart, cutLength, aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_ReplaceFromReadable(cutStart, cutLength, nsDependentString(buffer, length));
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAString::do_ReplaceFromReadable( index_type cutStart, size_type cutLength, const self_type& aReplacement )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
|
|
cutStart = NS_MIN(cutStart, oldLength);
|
|
cutLength = NS_MIN(cutLength, oldLength-cutStart);
|
|
index_type cutEnd = cutStart + cutLength;
|
|
|
|
size_type replacementLength = aReplacement.Length();
|
|
index_type replacementEnd = cutStart + replacementLength;
|
|
|
|
size_type newLength = oldLength - cutLength + replacementLength;
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( cutLength > replacementLength )
|
|
copy_string(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(replacementEnd)));
|
|
SetLength(newLength);
|
|
if ( cutLength < replacementLength )
|
|
copy_string_backward(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->BeginReading(fromEnd).advance(PRInt32(oldLength)), EndWriting(toBegin));
|
|
|
|
copy_string(aReplacement.BeginReading(fromBegin), aReplacement.EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart)));
|
|
}
|
|
|
|
|
|
int
|
|
nsDefaultCStringComparator::operator()( const char_type* lhs, const char_type* rhs, PRUint32 aLength ) const
|
|
{
|
|
return nsCharTraits<char_type>::compare(lhs, rhs, aLength);
|
|
}
|
|
|
|
PRBool
|
|
nsDefaultCStringComparator::operator()( char_type lhs, char_type rhs ) const
|
|
{
|
|
return lhs - rhs;
|
|
}
|
|
|
|
int
|
|
nsCaseInsensitiveCStringComparator::operator()( const char_type* lhs, const char_type* rhs, PRUint32 aLength ) const
|
|
{
|
|
return nsCRT::strncasecmp(lhs, rhs, aLength);
|
|
}
|
|
|
|
PRBool
|
|
nsCaseInsensitiveCStringComparator::operator()( char lhs, char rhs ) const
|
|
{
|
|
if (lhs == rhs) return 0;
|
|
|
|
lhs = nsCRT::ToLower(lhs);
|
|
rhs = nsCRT::ToLower(rhs);
|
|
|
|
return lhs - rhs;
|
|
}
|
|
|
|
NS_COM
|
|
int
|
|
Compare( const nsACString& lhs, const nsACString& rhs, const nsCStringComparator& aComparator )
|
|
{
|
|
typedef nsACString::size_type size_type;
|
|
|
|
if ( &lhs == &rhs )
|
|
return 0;
|
|
|
|
size_type lLength = lhs.Length();
|
|
size_type rLength = rhs.Length();
|
|
size_type lengthToCompare = NS_MIN(lLength, rLength);
|
|
|
|
nsACString::const_iterator leftIter, rightIter;
|
|
lhs.BeginReading(leftIter);
|
|
rhs.BeginReading(rightIter);
|
|
|
|
for (;;)
|
|
{
|
|
size_type lengthAvailable = size_type( NS_MIN(leftIter.size_forward(), rightIter.size_forward()) );
|
|
|
|
if ( lengthAvailable > lengthToCompare )
|
|
lengthAvailable = lengthToCompare;
|
|
|
|
{
|
|
int result;
|
|
// Note: |result| should be declared in this |if| expression, but some compilers don't like that
|
|
if ( (result = aComparator(leftIter.get(), rightIter.get(), lengthAvailable)) != 0 )
|
|
return result;
|
|
}
|
|
|
|
if ( !(lengthToCompare -= lengthAvailable) )
|
|
break;
|
|
|
|
leftIter.advance( PRInt32(lengthAvailable) );
|
|
rightIter.advance( PRInt32(lengthAvailable) );
|
|
}
|
|
|
|
if ( lLength < rLength )
|
|
return -1;
|
|
else if ( rLength < lLength )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
const nsACString::shared_buffer_handle_type*
|
|
nsACString::GetSharedBufferHandle() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const nsACString::buffer_handle_type*
|
|
nsACString::GetFlatBufferHandle() const
|
|
{
|
|
return GetSharedBufferHandle();
|
|
}
|
|
|
|
const nsACString::buffer_handle_type*
|
|
nsACString::GetBufferHandle() const
|
|
{
|
|
return GetSharedBufferHandle();
|
|
}
|
|
|
|
PRUint32
|
|
nsACString::GetImplementationFlags() const
|
|
{
|
|
PRUint32 flags = 0;
|
|
const shared_buffer_handle_type* handle = GetSharedBufferHandle();
|
|
if ( handle )
|
|
flags = handle->GetImplementationFlags();
|
|
return flags;
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsACString::IsVoid() const
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void
|
|
nsACString::SetIsVoid( PRBool )
|
|
{
|
|
// |SetIsVoid| is ignored by default
|
|
}
|
|
|
|
PRBool
|
|
nsACString::Equals( const char_type* rhs, const nsCStringComparator& aComparator ) const
|
|
{
|
|
return Equals(nsDependentCString(rhs), aComparator);
|
|
}
|
|
|
|
nsACString::char_type
|
|
nsACString::First() const
|
|
{
|
|
NS_ASSERTION(Length()>0, "|First()| on an empty string");
|
|
|
|
const_iterator iter;
|
|
return *BeginReading(iter);
|
|
}
|
|
|
|
nsACString::char_type
|
|
nsACString::Last() const
|
|
{
|
|
NS_ASSERTION(Length()>0, "|Last()| on an empty string");
|
|
|
|
const_iterator iter;
|
|
|
|
if ( !IsEmpty() )
|
|
{
|
|
EndReading(iter);
|
|
iter.advance(-1);
|
|
}
|
|
|
|
return *iter; // Note: this has undefined results if |IsEmpty()|
|
|
}
|
|
|
|
nsACString::size_type
|
|
nsACString::CountChar( char_type c ) const
|
|
{
|
|
/*
|
|
re-write this to use a counting sink
|
|
*/
|
|
|
|
size_type result = 0;
|
|
size_type lengthToExamine = Length();
|
|
|
|
const_iterator iter;
|
|
for ( BeginReading(iter); ; )
|
|
{
|
|
PRInt32 lengthToExamineInThisFragment = iter.size_forward();
|
|
const char_type* 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;
|
|
}
|
|
|
|
nsACString::size_type
|
|
nsACString::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
|
|
{
|
|
// If we're just assigning our entire self, give |aResult| the opportunity to share
|
|
if ( aStartPos == 0 && aLengthToCopy >= Length() )
|
|
aResult = *this;
|
|
else
|
|
aResult = Substring(*this, aStartPos, aLengthToCopy);
|
|
|
|
return aResult.Length();
|
|
}
|
|
|
|
nsACString::size_type
|
|
nsACString::Right( self_type& aResult, size_type aLengthToCopy ) const
|
|
{
|
|
size_type myLength = Length();
|
|
aLengthToCopy = NS_MIN(myLength, aLengthToCopy);
|
|
return Mid(aResult, myLength-aLengthToCopy, aLengthToCopy);
|
|
}
|
|
|
|
PRInt32
|
|
nsACString::FindChar( char_type aChar, PRUint32 aOffset ) const
|
|
{
|
|
const_iterator iter, done_searching;
|
|
BeginReading(iter).advance( PRInt32(aOffset) );
|
|
EndReading(done_searching);
|
|
|
|
size_type lengthSearched = 0;
|
|
while ( iter != done_searching )
|
|
{
|
|
PRInt32 fragmentLength = iter.size_forward();
|
|
const char_type* charFoundAt = nsCharTraits<char_type>::find(iter.get(), fragmentLength, aChar);
|
|
if ( charFoundAt )
|
|
return lengthSearched + (charFoundAt-iter.get()) + aOffset;
|
|
|
|
lengthSearched += fragmentLength;
|
|
iter.advance(fragmentLength);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
//
|
|
// |Assign()|
|
|
//
|
|
|
|
void
|
|
nsACString::AssignFromReadable( const self_type& rhs )
|
|
{
|
|
if ( this != &rhs )
|
|
do_AssignFromReadable(rhs);
|
|
// else, self-assign is a no-op
|
|
}
|
|
|
|
void
|
|
nsACString::AssignFromPromise( const self_type& aReadable )
|
|
/*
|
|
...this function is only called when a promise that somehow references |this| is assigned _into_ |this|.
|
|
E.g.,
|
|
|
|
... writable& w ...
|
|
... readable& r ...
|
|
|
|
w = r + w;
|
|
|
|
In this example, you can see that unless the characters promised by |w| in |r+w| are resolved before
|
|
anything starts getting copied into |w|, there will be trouble. They will be overritten by the contents
|
|
of |r| before being retrieved to be appended.
|
|
|
|
We could have a really tricky solution where we tell the promise to resolve _just_ the data promised
|
|
by |this|, but this should be a rare case, since clients with more local knowledge will know that, e.g.,
|
|
in the case above, |Insert| could have special behavior with significantly better performance. Since
|
|
it's a rare case anyway, we should just do the simplest thing that could possibly work, resolve the
|
|
entire promise. If we measure and this turns out to show up on performance radar, we then have the
|
|
option to fix either the callers or this mechanism.
|
|
*/
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_AssignFromReadable(aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
// Note: not exception safe. We need something to manage temporary buffers like this
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_AssignFromElementPtrLength(buffer, length);
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsACString::do_AssignFromReadable( const self_type& aReadable )
|
|
{
|
|
SetLength(0);
|
|
if ( !aReadable.IsEmpty() )
|
|
{
|
|
SetLength(aReadable.Length());
|
|
// first setting the length to |0| avoids copying characters only to be overwritten later
|
|
// in the case where the implementation decides to re-allocate
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsACString::do_AssignFromElementPtr( const char_type* aPtr )
|
|
{
|
|
do_AssignFromReadable(nsDependentCString(aPtr));
|
|
}
|
|
|
|
void
|
|
nsACString::do_AssignFromElementPtrLength( const char_type* aPtr, size_type aLength )
|
|
{
|
|
do_AssignFromReadable(Substring(aPtr, aPtr+aLength));
|
|
}
|
|
|
|
void
|
|
nsACString::do_AssignFromElement( char_type aChar )
|
|
{
|
|
do_AssignFromReadable(Substring(&aChar, &aChar+1));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Append()|
|
|
//
|
|
|
|
void
|
|
nsACString::AppendFromReadable( const self_type& aReadable )
|
|
{
|
|
if ( this != &aReadable )
|
|
do_AppendFromReadable(aReadable);
|
|
else
|
|
AppendFromPromise(aReadable);
|
|
}
|
|
|
|
void
|
|
nsACString::AppendFromPromise( const self_type& aReadable )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_AppendFromReadable(aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_AppendFromElementPtrLength(buffer, length);
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsACString::do_AppendFromReadable( const self_type& aReadable )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
SetLength(oldLength + aReadable.Length());
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin).advance( PRInt32(oldLength) ) );
|
|
}
|
|
|
|
void
|
|
nsACString::do_AppendFromElementPtr( const char_type* aPtr )
|
|
{
|
|
do_AppendFromReadable(nsDependentCString(aPtr));
|
|
}
|
|
|
|
void
|
|
nsACString::do_AppendFromElementPtrLength( const char_type* aPtr, size_type aLength )
|
|
{
|
|
do_AppendFromReadable(Substring(aPtr, aPtr+aLength));
|
|
}
|
|
|
|
void
|
|
nsACString::do_AppendFromElement( char_type aChar )
|
|
{
|
|
do_AppendFromReadable(Substring(&aChar, &aChar + 1));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Insert()|
|
|
//
|
|
|
|
void
|
|
nsACString::InsertFromReadable( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
if ( this != &aReadable )
|
|
do_InsertFromReadable(aReadable, atPosition);
|
|
else
|
|
InsertFromPromise(aReadable, atPosition);
|
|
}
|
|
|
|
void
|
|
nsACString::InsertFromPromise( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_InsertFromReadable(aReadable, atPosition);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_InsertFromElementPtrLength(buffer, atPosition, length);
|
|
delete buffer;
|
|
}
|
|
// else assert
|
|
}
|
|
}
|
|
|
|
void
|
|
nsACString::do_InsertFromReadable( const self_type& aReadable, index_type atPosition )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
SetLength(oldLength + aReadable.Length());
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( atPosition < oldLength )
|
|
copy_string_backward(this->BeginReading(fromBegin).advance(PRInt32(atPosition)), this->BeginReading(fromEnd).advance(PRInt32(oldLength)), EndWriting(toBegin));
|
|
else
|
|
atPosition = oldLength;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(atPosition)));
|
|
}
|
|
|
|
void
|
|
nsACString::do_InsertFromElementPtr( const char_type* aPtr, index_type atPosition )
|
|
{
|
|
do_InsertFromReadable(nsDependentCString(aPtr), atPosition);
|
|
}
|
|
|
|
void
|
|
nsACString::do_InsertFromElementPtrLength( const char_type* aPtr, index_type atPosition, size_type aLength )
|
|
{
|
|
do_InsertFromReadable(Substring(aPtr, aPtr+aLength), atPosition);
|
|
}
|
|
|
|
void
|
|
nsACString::do_InsertFromElement( char_type aChar, index_type atPosition )
|
|
{
|
|
do_InsertFromReadable(Substring(&aChar, &aChar+1), atPosition);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Cut()|
|
|
//
|
|
|
|
void
|
|
nsACString::Cut( index_type cutStart, size_type cutLength )
|
|
{
|
|
size_type myLength = this->Length();
|
|
cutLength = NS_MIN(cutLength, myLength-cutStart);
|
|
index_type cutEnd = cutStart + cutLength;
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( cutEnd < myLength )
|
|
copy_string(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart)));
|
|
SetLength(myLength-cutLength);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// |Replace()|
|
|
//
|
|
|
|
void
|
|
nsACString::ReplaceFromReadable( index_type cutStart, size_type cutLength, const self_type& aReplacement )
|
|
{
|
|
if ( this != &aReplacement )
|
|
do_ReplaceFromReadable(cutStart, cutLength, aReplacement);
|
|
else
|
|
ReplaceFromPromise(cutStart, cutLength, aReplacement);
|
|
}
|
|
|
|
void
|
|
nsACString::ReplaceFromPromise( index_type cutStart, size_type cutLength, const self_type& aReadable )
|
|
{
|
|
if ( !aReadable.IsDependentOn(*this) )
|
|
do_ReplaceFromReadable(cutStart, cutLength, aReadable);
|
|
else
|
|
{
|
|
size_type length = aReadable.Length();
|
|
char_type* buffer = new char_type[length];
|
|
if ( buffer )
|
|
{
|
|
const_iterator fromBegin, fromEnd;
|
|
char_type* toBegin = buffer;
|
|
copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin);
|
|
do_ReplaceFromReadable(cutStart, cutLength, nsDependentCString(buffer, length));
|
|
delete buffer;
|
|
}
|
|
// else assert?
|
|
}
|
|
}
|
|
|
|
void
|
|
nsACString::do_ReplaceFromReadable( index_type cutStart, size_type cutLength, const self_type& aReplacement )
|
|
{
|
|
size_type oldLength = this->Length();
|
|
|
|
cutStart = NS_MIN(cutStart, oldLength);
|
|
cutLength = NS_MIN(cutLength, oldLength-cutStart);
|
|
index_type cutEnd = cutStart + cutLength;
|
|
|
|
size_type replacementLength = aReplacement.Length();
|
|
index_type replacementEnd = cutStart + replacementLength;
|
|
|
|
size_type newLength = oldLength - cutLength + replacementLength;
|
|
|
|
const_iterator fromBegin, fromEnd;
|
|
iterator toBegin;
|
|
if ( cutLength > replacementLength )
|
|
copy_string(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(replacementEnd)));
|
|
SetLength(newLength);
|
|
if ( cutLength < replacementLength )
|
|
copy_string_backward(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->BeginReading(fromEnd).advance(PRInt32(oldLength)), EndWriting(toBegin));
|
|
|
|
copy_string(aReplacement.BeginReading(fromBegin), aReplacement.EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart)));
|
|
}
|
|
|