/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape 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/NPL/ * * 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.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Original Author: * Scott Collins * * Contributor(s): */ #ifndef _nsAWritableString_h__ #define _nsAWritableString_h__ // See also... #include "nsAReadableString.h" template struct nsWritableFragment { CharT* mStart; CharT* mEnd; PRUint32 mFragmentIdentifier; nsWritableFragment() : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } }; template class basic_nsAWritableString; template class nsWritingIterator // : public bidirectional_iterator_tag { public: typedef ptrdiff_t difference_type; typedef CharT value_type; typedef CharT* pointer; typedef CharT& reference; // typedef bidirectional_iterator_tag iterator_category; private: friend class basic_nsAWritableString; nsWritableFragment mFragment; CharT* mPosition; basic_nsAWritableString* mOwningString; inline void normalize_forward(); inline void normalize_backward(); nsWritingIterator( nsWritableFragment& aFragment, CharT* aStartingPosition, basic_nsAWritableString& aOwningString ) : mFragment(aFragment), mPosition(aStartingPosition), mOwningString(&aOwningString) { // nothing else to do here } public: // nsWritingIterator( const nsWritingIterator& ); ...use default copy-constructor // nsWritingIterator& operator=( const nsWritingIterator& ); ...use default copy-assignment operator reference operator*() const { return *mPosition; } pointer operator->() const { return mPosition; } nsWritingIterator& operator++() { ++mPosition; normalize_forward(); return *this; } nsWritingIterator operator++( int ) { nsWritingIterator result(*this); ++mPosition; normalize_forward(); return result; } nsWritingIterator& operator--() { normalize_backward(); --mPosition; return *this; } nsWritingIterator operator--( int ) { nsWritingIterator result(*this); normalize_backward(); --mPosition; return result; } const nsWritableFragment& fragment() const { return mFragment; } difference_type size_forward() const { return mFragment.mEnd - mPosition; } difference_type size_backward() const { return mPosition - mFragment.mStart; } nsWritingIterator& operator+=( difference_type n ) { if ( n < 0 ) return operator-=(-n); while ( n ) { difference_type one_hop = NS_MIN(n, size_forward()); NS_ASSERTION(one_hop>0, "Infinite loop: can't advance a writable iterator beyond the end of a string"); mPosition += one_hop; normalize_forward(); n -= one_hop; } return *this; } nsWritingIterator& operator-=( difference_type n ) { if ( n < 0 ) return operator+=(-n); while ( n ) { difference_type one_hop = NS_MIN(n, size_backward()); NS_ASSERTION(one_hop>0, "Infinite loop: can't advance (backward) a writable iterator beyond the end of a string"); mPosition -= one_hop; normalize_backward(); n -= one_hop; } return *this; } PRUint32 write( const value_type* s, PRUint32 n ) { NS_ASSERTION(size_forward() > 0, "You can't |write| into an |nsWritingIterator| with no space!"); n = NS_MIN(n, PRUint32(size_forward())); nsCharTraits::copy(mPosition, s, n); operator+=( difference_type(n) ); return n; } }; /* This file defines the abstract interfaces |nsAWritableString| and |nsAWritableCString|. |nsAWritableString| is a string of |PRUnichar|s. |nsAWritableCString| (note the 'C') is a string of |char|s. */ template class basic_nsAWritableString : public basic_nsAReadableString /* ... */ { // friend class nsWritingIterator; public: typedef PRUint32 size_type; typedef PRUint32 index_type; typedef nsWritingIterator iterator; virtual CharT* GetWritableFragment( nsWritableFragment&, nsFragmentRequest, PRUint32 = 0 ) = 0; nsWritingIterator BeginWriting( PRUint32 aOffset = 0 ) { nsWritableFragment fragment; CharT* startPos = GetWritableFragment(fragment, kFragmentAt, aOffset); return nsWritingIterator(fragment, startPos, *this); } nsWritingIterator EndWriting( PRUint32 aOffset = 0 ) { nsWritableFragment fragment; CharT* startPos = GetWritableFragment(fragment, kFragmentAt, NS_MAX(0U, this->Length()-aOffset)); return nsWritingIterator(fragment, startPos, *this); } virtual void SetCapacity( PRUint32 ) = 0; virtual void SetLength( PRUint32 ) = 0; void Truncate( PRUint32 aNewLength=0 ) { NS_ASSERTION(aNewLength<=this->Length(), "Can't use |Truncate()| to make a string longer."); if ( aNewLength < this->Length() ) SetLength(aNewLength); } // PRBool SetCharAt( char_type, index_type ) = 0; // void ToLowerCase(); // void ToUpperCase(); // void StripChars( const CharT* aSet ); // void StripChar( ... ); // void StripWhitespace(); // void ReplaceChar( ... ); // void ReplaceSubstring( ... ); // void Trim( ... ); // void CompressSet( ... ); // void CompressWhitespace( ... ); // // |Assign()|, |operator=()| // void Assign( const basic_nsAReadableString& aReadable ) { do_AssignFromReadable(aReadable); } void Assign( const nsPromiseReadable& aReadable ) { aReadable.Promises(*this) ? AssignFromPromise(aReadable) : do_AssignFromReadable(aReadable); } // void Assign( const nsReadingIterator& aStart, const nsReadingIterator& aEnd ) { do_AssignFromIterators(aStart, aEnd); } void Assign( const CharT* aPtr ) { aPtr ? do_AssignFromElementPtr(aPtr) : SetLength(0); } void Assign( const CharT* aPtr, PRUint32 aLength ) { do_AssignFromElementPtrLength(aPtr, aLength); } void Assign( CharT aChar ) { do_AssignFromElement(aChar); } basic_nsAWritableString& operator=( const basic_nsAReadableString& aReadable ) { Assign(aReadable); return *this; } basic_nsAWritableString& operator=( const nsPromiseReadable& aReadable ) { Assign(aReadable); return *this; } basic_nsAWritableString& operator=( const CharT* aPtr ) { Assign(aPtr); return *this; } basic_nsAWritableString& operator=( CharT aChar ) { Assign(aChar); return *this; } // // |Append()|, |operator+=()| // void Append( const basic_nsAReadableString& aReadable ) { do_AppendFromReadable(aReadable); } void Append( const nsPromiseReadable& aReadable ) { aReadable.Promises(*this) ? AppendFromPromise(aReadable) : do_AppendFromReadable(aReadable); } // void Append( const nsReadingIterator& aStart, const nsReadingIterator& aEnd ) { do_AppendFromIterators(aStart, aEnd); } void Append( const CharT* aPtr ) { if (aPtr) do_AppendFromElementPtr(aPtr); } void Append( const CharT* aPtr, PRUint32 aLength ) { do_AppendFromElementPtrLength(aPtr, aLength); } void Append( CharT aChar ) { do_AppendFromElement(aChar); } basic_nsAWritableString& operator+=( const basic_nsAReadableString& aReadable ) { Append(aReadable); return *this; } basic_nsAWritableString& operator+=( const nsPromiseReadable& aReadable ) { Append(aReadable); return *this; } basic_nsAWritableString& operator+=( const CharT* aPtr ) { Append(aPtr); return *this; } basic_nsAWritableString& operator+=( CharT aChar ) { Append(aChar); return *this; } // // |Insert()| // Note: I would really like to move the |atPosition| parameter to the front of the argument list // void Insert( const basic_nsAReadableString& aReadable, PRUint32 atPosition ) { do_InsertFromReadable(aReadable, atPosition); } void Insert( const nsPromiseReadable& aReadable, PRUint32 atPosition ) { aReadable.Promises(*this) ? InsertFromPromise(aReadable, atPosition) : do_InsertFromReadable(aReadable, atPosition); } // void Insert( const nsReadingIterator& aStart, const nsReadingIterator& aEnd, PRUint32 atPosition ) { do_InsertFromIterators(aStart, aEnd, atPosition); } void Insert( const CharT* aPtr, PRUint32 atPosition ) { if (aPtr) do_InsertFromElementPtr(aPtr, atPosition); } void Insert( const CharT* aPtr, PRUint32 atPosition, PRUint32 aLength ) { do_InsertFromElementPtrLength(aPtr, atPosition, aLength); } void Insert( CharT aChar, PRUint32 atPosition ) { do_InsertFromElement(aChar, atPosition); } virtual void Cut( PRUint32 cutStart, PRUint32 cutLength ); void Replace( PRUint32 cutStart, PRUint32 cutLength, const basic_nsAReadableString& aReadable ) { do_ReplaceFromReadable(cutStart, cutLength, aReadable); } void Replace( PRUint32 cutStart, PRUint32 cutLength, const nsPromiseReadable& aReadable ) { aReadable.Promises(*this) ? ReplaceFromPromise(cutStart, cutLength, aReadable) : do_ReplaceFromReadable(cutStart, cutLength, aReadable); } // void Replace( PRUint32, PRUint32, const nsReadingIterator&, const nsReadingIterator& ); // void Replace( PRUint32, PRUint32, const CharT* ); // void Replace( PRUint32, PRUint32, const CharT*, PRUint32 ); // void Replace( PRUint32, PRUint32, CharT ); private: typedef typename nsCharTraits::incompatible_char_type incompatible_char_type; // NOT TO BE IMPLEMENTED void operator= ( incompatible_char_type ); void Assign ( incompatible_char_type ); void operator+= ( incompatible_char_type ); void Append ( incompatible_char_type ); void Insert ( incompatible_char_type, PRUint32 ); // void Replace ( PRUint32, PRUint32, incompatible_char_type ); protected: virtual void do_AssignFromReadable( const basic_nsAReadableString& ); void AssignFromPromise( const nsPromiseReadable& ); // virtual void do_AssignFromIterators( nsReadingIterator, nsReadingIterator ); virtual void do_AssignFromElementPtr( const CharT* ); virtual void do_AssignFromElementPtrLength( const CharT*, PRUint32 ); virtual void do_AssignFromElement( CharT ); virtual void do_AppendFromReadable( const basic_nsAReadableString& ); void AppendFromPromise( const nsPromiseReadable& ); // virtual void do_AppendFromIterators( nsReadingIterator, nsReadingIterator ); virtual void do_AppendFromElementPtr( const CharT* ); virtual void do_AppendFromElementPtrLength( const CharT*, PRUint32 ); virtual void do_AppendFromElement( CharT ); virtual void do_InsertFromReadable( const basic_nsAReadableString&, PRUint32 ); void InsertFromPromise( const nsPromiseReadable&, PRUint32 ); // virtual void do_InsertFromIterators( nsReadingIterator, nsReadingIterator, PRUint32 ); virtual void do_InsertFromElementPtr( const CharT*, PRUint32 ); virtual void do_InsertFromElementPtrLength( const CharT*, PRUint32, PRUint32 ); virtual void do_InsertFromElement( CharT, PRUint32 ); virtual void do_ReplaceFromReadable( PRUint32, PRUint32, const basic_nsAReadableString& ); void ReplaceFromPromise( PRUint32 cutStart, PRUint32 cutLength, const nsPromiseReadable& ); // virtual void do_ReplaceFromIterators( ... ); // virtual void do_ReplaceFromElementPtr( ... ); // virtual void do_ReplaceFromElementPtrLength( ... ); // virtual void do_ReplaceFromElement( ... ); }; // // |nsWritingIterator|s // template inline void nsWritingIterator::normalize_forward() { if ( mPosition == mFragment.mEnd ) if ( mOwningString->GetWritableFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } template inline void nsWritingIterator::normalize_backward() { if ( mPosition == mFragment.mStart ) if ( mOwningString->GetWritableFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } template inline PRBool operator==( const nsWritingIterator& lhs, const nsWritingIterator& rhs ) { return lhs.operator->() == rhs.operator->(); } template inline PRBool operator!=( const nsWritingIterator& lhs, const nsWritingIterator& rhs ) { return lhs.operator->() != rhs.operator->(); } // // |Assign()| // template void basic_nsAWritableString::do_AssignFromReadable( const basic_nsAReadableString& rhs ) { SetLength(rhs.Length()); copy_string(rhs.BeginReading(), rhs.EndReading(), BeginWriting()); } template void basic_nsAWritableString::AssignFromPromise( const nsPromiseReadable& 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. */ { PRUint32 length = aReadable.Length(); if ( CharT* buffer = new CharT[length] ) { // Note: not exception safe. We need something to manage temporary buffers like this copy_string(aReadable.BeginReading(), aReadable.EndReading(), buffer); do_AssignFromElementPtrLength(buffer, length); delete buffer; } // else assert? } #if 0 template void basic_nsAWritableString::do_AssignFromIterators( const nsReadingIterator& aStart, const nsReadingIterator& aEnd ) { SetLength(distance(aStart, aEnd)); copy_string(aStart, aEnd, BeginWriting()); } #endif template void basic_nsAWritableString::do_AssignFromElementPtr( const CharT* aPtr ) { do_AssignFromReadable(basic_nsLiteralString(aPtr)); } template void basic_nsAWritableString::do_AssignFromElementPtrLength( const CharT* aPtr, PRUint32 aLength ) { do_AssignFromReadable(basic_nsLiteralString(aPtr, aLength)); } template void basic_nsAWritableString::do_AssignFromElement( CharT aChar ) { do_AssignFromReadable(basic_nsLiteralChar(aChar)); } // // |Append()| // template void basic_nsAWritableString::do_AppendFromReadable( const basic_nsAReadableString& rhs ) { PRUint32 oldLength = this->Length(); SetLength(oldLength + rhs.Length()); copy_string(rhs.BeginReading(), rhs.EndReading(), BeginWriting(oldLength)); } template void basic_nsAWritableString::AppendFromPromise( const nsPromiseReadable& aReadable ) { PRUint32 length = aReadable.Length(); if ( CharT* buffer = new CharT[length] ) { copy_string(aReadable.BeginReading(), aReadable.EndReading(), buffer); do_AppendFromElementPtrLength(buffer, length); delete buffer; } } #if 0 template void basic_nsAWritableString::do_AppendFromIterators( const nsReadingIterator& aStart, const nsReadingIterator& aEnd ) { PRUint32 oldLength = this->Length(); SetLength(oldLength + distance(aStart, aEnd)); copy_string(aStart, aEnd, BeginWriting(oldLength)); } #endif template void basic_nsAWritableString::do_AppendFromElementPtr( const CharT* aChar ) { do_AppendFromReadable(basic_nsLiteralString(aChar)); } template void basic_nsAWritableString::do_AppendFromElementPtrLength( const CharT* aChar, PRUint32 aLength ) { do_AppendFromReadable(basic_nsLiteralString(aChar, aLength)); } template inline void basic_nsAWritableString::do_AppendFromElement( CharT aChar ) { PRUint32 oldLength = this->Length(); SetLength(oldLength+1); nsWritableFragment fragment; *GetWritableFragment(fragment, kFragmentAt, oldLength) = aChar; } // // |Insert()| // template void basic_nsAWritableString::do_InsertFromReadable( const basic_nsAReadableString& aReadable, PRUint32 atPosition ) { PRUint32 oldLength = this->Length(); SetLength(oldLength + aReadable.Length()); if ( atPosition < oldLength ) copy_string_backward(this->BeginReading(atPosition), this->BeginReading(oldLength), EndWriting()); else atPosition = oldLength; copy_string(aReadable.BeginReading(), aReadable.EndReading(), BeginWriting(atPosition)); } template void basic_nsAWritableString::InsertFromPromise( const nsPromiseReadable& aReadable, PRUint32 atPosition ) { PRUint32 length = aReadable.Length(); if ( CharT* buffer = new CharT[length] ) { copy_string(aReadable.BeginReading(), aReadable.EndReading(), buffer); do_InsertFromElementPtrLength(buffer, atPosition, length); delete buffer; } } template void basic_nsAWritableString::do_InsertFromElementPtr( const CharT* aPtr, PRUint32 atPosition ) { do_InsertFromReadable(basic_nsLiteralString(aPtr), atPosition); } template void basic_nsAWritableString::do_InsertFromElementPtrLength( const CharT* aPtr, PRUint32 atPosition, PRUint32 aLength ) { do_InsertFromReadable(basic_nsLiteralString(aPtr, aLength), atPosition); } template void basic_nsAWritableString::do_InsertFromElement( CharT aChar, PRUint32 atPosition ) { do_InsertFromReadable(basic_nsLiteralChar(aChar), atPosition); } // // |Cut()| // template void basic_nsAWritableString::Cut( PRUint32 cutStart, PRUint32 cutLength ) { PRUint32 myLength = this->Length(); cutLength = NS_MIN(cutLength, myLength-cutStart); PRUint32 cutEnd = cutStart + cutLength; if ( cutEnd < myLength ) copy_string(this->BeginReading(cutEnd), this->EndReading(), BeginWriting(cutStart)); SetLength(myLength-cutLength); } // // |Replace()| // template void basic_nsAWritableString::do_ReplaceFromReadable( PRUint32 cutStart, PRUint32 cutLength, const basic_nsAReadableString& aReplacement ) { PRUint32 oldLength = this->Length(); cutStart = NS_MIN(cutStart, oldLength); cutLength = NS_MIN(cutLength, oldLength-cutStart); PRUint32 cutEnd = cutStart + cutLength; PRUint32 replacementLength = aReplacement.Length(); PRUint32 replacementEnd = cutStart + replacementLength; PRUint32 newLength = oldLength - cutLength + replacementLength; if ( cutLength > replacementLength ) copy_string(this->BeginReading(cutEnd), this->EndReading(), BeginWriting(replacementEnd)); SetLength(newLength); if ( cutLength < replacementLength ) copy_string_backward(this->BeginReading(cutEnd), this->BeginReading(oldLength), BeginWriting(replacementEnd)); copy_string(aReplacement.BeginReading(), aReplacement.EndReading(), BeginWriting(cutStart)); } template void basic_nsAWritableString::ReplaceFromPromise( PRUint32 cutStart, PRUint32 cutLength, const nsPromiseReadable& aReadable ) { PRUint32 length = aReadable.Length(); if ( CharT* buffer = new CharT[length] ) { copy_string(aReadable.BeginReading(), aReadable.EndReading(), buffer); do_ReplaceFromReadable(cutStart, cutLength, basic_nsLiteralString(buffer, length)); delete buffer; } } // // Types // typedef basic_nsAWritableString nsAWritableString; typedef basic_nsAWritableString nsAWritableCString; #endif // !defined(_nsAWritableString_h__)