/* -*- 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) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Scott Collins (original author) */ #ifndef nsAWritableString_h___ #define nsAWritableString_h___ // See also... #ifndef nsAReadableString_h___ #include "nsAReadableString.h" #endif template struct nsWritableFragment { CharT* mStart; CharT* mEnd; void* 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; nsWritingIterator( nsWritableFragment& aFragment, CharT* aStartingPosition, basic_nsAWritableString& aOwningString ) : mFragment(aFragment), mPosition(aStartingPosition), mOwningString(&aOwningString) { // nothing else to do here } public: nsWritingIterator() { } // nsWritingIterator( const nsWritingIterator& ); // auto-generated copy-constructor OK // nsWritingIterator& operator=( const nsWritingIterator& ); // auto-generated copy-assignment operator OK inline void normalize_forward(); inline void normalize_backward(); pointer get() const { return mPosition; } reference operator*() const { return *get(); } #if 0 // An iterator really deserves this, but some compilers (notably IBM VisualAge for OS/2) // don't like this when |CharT| is a type without members. pointer operator->() const { return get(); } #endif 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& advance( difference_type n ) { while ( n > 0 ) { difference_type one_hop = NS_MIN(n, size_forward()); NS_ASSERTION(one_hop>0, "Infinite loop: can't advance a writing iterator beyond the end of a string"); // perhaps I should |break| if |!one_hop|? mPosition += one_hop; normalize_forward(); n -= one_hop; } while ( n < 0 ) { normalize_backward(); difference_type one_hop = NS_MAX(n, -size_backward()); NS_ASSERTION(one_hop<0, "Infinite loop: can't advance (backward) a writing iterator beyond the end of a string"); // perhaps I should |break| if |!one_hop|? mPosition += one_hop; n -= one_hop; } return *this; } /** * Really don't want to call these two operations |+=| and |-=|. * Would prefer a single function, e.g., |advance|, which doesn't imply a constant time operation. * * We'll get rid of these as soon as we can. */ nsWritingIterator& operator+=( difference_type n ) // deprecated { return advance(n); } nsWritingIterator& operator-=( difference_type n ) // deprecated { return advance(-n); } 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::move(mPosition, s, n); advance( difference_type(n) ); return n; } }; #if 0 template nsWritingIterator& nsWritingIterator::advance( difference_type n ) { while ( n > 0 ) { difference_type one_hop = NS_MIN(n, size_forward()); NS_ASSERTION(one_hop>0, "Infinite loop: can't advance a writing iterator beyond the end of a string"); // perhaps I should |break| if |!one_hop|? mPosition += one_hop; normalize_forward(); n -= one_hop; } while ( n < 0 ) { normalize_backward(); difference_type one_hop = NS_MAX(n, -size_backward()); NS_ASSERTION(one_hop<0, "Infinite loop: can't advance (backward) a writing iterator beyond the end of a string"); // perhaps I should |break| if |!one_hop|? mPosition += one_hop; n -= one_hop; } return *this; } #endif /* 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 CharT char_type; typedef PRUint32 size_type; typedef PRUint32 index_type; typedef nsWritingIterator iterator; // basic_nsAWritableString(); // auto-generated default constructor OK (we're abstract anyway) // basic_nsAWritableString( const basic_nsAWritableString& ); // auto-generated copy-constructor OK (again, only because we're abstract) // ~basic_nsAWritableString(); // auto-generated destructor OK // see below for copy-assignment operator virtual CharT* GetWritableFragment( nsWritableFragment&, nsFragmentRequest, PRUint32 = 0 ) = 0; /** * Note: measure -- should the |BeginWriting| and |EndWriting| be |inline|? */ nsWritingIterator& BeginWriting( nsWritingIterator& aResult ) { aResult.mOwningString = this; GetWritableFragment(aResult.mFragment, kFirstFragment); aResult.mPosition = aResult.mFragment.mStart; aResult.normalize_forward(); return aResult; } // deprecated nsWritingIterator BeginWriting() { nsWritingIterator result; return BeginWriting(result); // copies (since I return a value, not a reference) } nsWritingIterator& EndWriting( nsWritingIterator& aResult ) { aResult.mOwningString = this; GetWritableFragment(aResult.mFragment, kLastFragment); aResult.mPosition = aResult.mFragment.mEnd; // must not |normalize_backward| as that would likely invalidate tests like |while ( first != last )| return aResult; } // deprecated nsWritingIterator EndWriting() { nsWritingIterator result; return EndWriting(result); // copies (since I return a value, not a reference) } /** * |SetCapacity| is not required to do anything; however, it can be used * as a hint to the implementation to reduce allocations. * |SetCapacity(0)| is a suggestion to discard all associated storage. */ virtual void SetCapacity( PRUint32 ) { } /** * |SetLength| is used in two ways: * 1) to |Cut| a suffix of the string; * 2) to prepare to |Append| or move characters around. * * External callers are not allowed to use |SetLength| is this latter capacity. * Should this really be a public operation? * Additionally, your implementation of |SetLength| need not satisfy (2) if and only if you * override the |do_...| routines to not need this facility. * * This distinction makes me think the two different uses should be split into * two distinct functions. */ 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 ) { AssignFromReadable(aReadable); } void Assign( const nsPromiseReadable& aReadable ) { AssignFromPromise(aReadable); } 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); } // copy-assignment operator. I must define my own if I don't want the compiler to make me one basic_nsAWritableString& operator=( const basic_nsAWritableString& aWritable ) { Assign(aWritable); return *this; } 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 ) { AppendFromReadable(aReadable); } void Append( const nsPromiseReadable& aReadable ) { AppendFromPromise(aReadable); } 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; } /** * The following index based routines need to be recast with iterators. */ // // |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 ) { InsertFromReadable(aReadable, atPosition); } void Insert( const nsPromiseReadable& aReadable, PRUint32 atPosition ) { InsertFromPromise(aReadable, 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 ) { ReplaceFromReadable(cutStart, cutLength, aReadable); } void Replace( PRUint32 cutStart, PRUint32 cutLength, const nsPromiseReadable& aReadable ) { ReplaceFromPromise(cutStart, cutLength, aReadable); } 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 ); protected: void AssignFromReadable( const basic_nsAReadableString& ); void AssignFromPromise( const basic_nsAReadableString& ); virtual void do_AssignFromReadable( const basic_nsAReadableString& ); virtual void do_AssignFromElementPtr( const CharT* ); virtual void do_AssignFromElementPtrLength( const CharT*, PRUint32 ); virtual void do_AssignFromElement( CharT ); void AppendFromReadable( const basic_nsAReadableString& ); void AppendFromPromise( const basic_nsAReadableString& ); virtual void do_AppendFromReadable( const basic_nsAReadableString& ); virtual void do_AppendFromElementPtr( const CharT* ); virtual void do_AppendFromElementPtrLength( const CharT*, PRUint32 ); virtual void do_AppendFromElement( CharT ); void InsertFromReadable( const basic_nsAReadableString&, PRUint32 ); void InsertFromPromise( const basic_nsAReadableString&, PRUint32 ); virtual void do_InsertFromReadable( const basic_nsAReadableString&, PRUint32 ); virtual void do_InsertFromElementPtr( const CharT*, PRUint32 ); virtual void do_InsertFromElementPtrLength( const CharT*, PRUint32, PRUint32 ); virtual void do_InsertFromElement( CharT, PRUint32 ); void ReplaceFromReadable( PRUint32, PRUint32, const basic_nsAReadableString& ); void ReplaceFromPromise( PRUint32, PRUint32, const basic_nsAReadableString& ); virtual void do_ReplaceFromReadable( PRUint32, PRUint32, const basic_nsAReadableString& ); }; // // |nsWritingIterator|s // template inline void nsWritingIterator::normalize_forward() { while ( mPosition == mFragment.mEnd && mOwningString->GetWritableFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } template inline void nsWritingIterator::normalize_backward() { while ( mPosition == mFragment.mStart && mOwningString->GetWritableFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } template inline PRBool operator==( const nsWritingIterator& lhs, const nsWritingIterator& rhs ) { return lhs.get() == rhs.get(); } template inline PRBool operator!=( const nsWritingIterator& lhs, const nsWritingIterator& rhs ) { return lhs.get() != rhs.get(); } // // |Assign()| // template void basic_nsAWritableString::AssignFromReadable( const basic_nsAReadableString& rhs ) { if ( NS_STATIC_CAST(const basic_nsAReadableString*, this) != &rhs ) do_AssignFromReadable(rhs); // else, self-assign is a no-op } template void basic_nsAWritableString::AssignFromPromise( const basic_nsAReadableString& 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.Promises(*this) ) do_AssignFromReadable(aReadable); else { PRUint32 length = aReadable.Length(); CharT* buffer = new CharT[length]; if ( buffer ) { // Note: not exception safe. We need something to manage temporary buffers like this nsReadingIterator fromBegin, fromEnd; CharT* toBegin = buffer; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin); do_AssignFromElementPtrLength(buffer, length); delete buffer; } // else assert? } } template void basic_nsAWritableString::do_AssignFromReadable( const basic_nsAReadableString& aReadable ) { SetLength(0); 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 nsReadingIterator fromBegin, fromEnd; nsWritingIterator toBegin; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin)); } 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::AppendFromReadable( const basic_nsAReadableString& aReadable ) { if ( NS_STATIC_CAST(const basic_nsAReadableString*, this) != &aReadable ) do_AppendFromReadable(aReadable); else AppendFromPromise(aReadable); } template void basic_nsAWritableString::AppendFromPromise( const basic_nsAReadableString& aReadable ) { if ( !aReadable.Promises(*this) ) do_AppendFromReadable(aReadable); else { PRUint32 length = aReadable.Length(); CharT* buffer = new CharT[length]; if ( buffer ) { nsReadingIterator fromBegin, fromEnd; CharT* toBegin = buffer; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin); do_AppendFromElementPtrLength(buffer, length); delete buffer; } // else assert? } } template void basic_nsAWritableString::do_AppendFromReadable( const basic_nsAReadableString& aReadable ) { PRUint32 oldLength = this->Length(); SetLength(oldLength + aReadable.Length()); nsReadingIterator fromBegin, fromEnd; nsWritingIterator toBegin; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), BeginWriting(toBegin).advance( PRInt32(oldLength) ) ); } 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 void basic_nsAWritableString::do_AppendFromElement( CharT aChar ) { do_AppendFromReadable(basic_nsLiteralChar(aChar)); } // // |Insert()| // template void basic_nsAWritableString::InsertFromReadable( const basic_nsAReadableString& aReadable, PRUint32 atPosition ) { if ( NS_STATIC_CAST(const basic_nsAReadableString*, this) != &aReadable ) do_InsertFromReadable(aReadable, atPosition); else InsertFromPromise(aReadable, atPosition); } template void basic_nsAWritableString::InsertFromPromise( const basic_nsAReadableString& aReadable, PRUint32 atPosition ) { if ( !aReadable.Promises(*this) ) do_InsertFromReadable(aReadable, atPosition); else { PRUint32 length = aReadable.Length(); CharT* buffer = new CharT[length]; if ( buffer ) { nsReadingIterator fromBegin, fromEnd; CharT* toBegin = buffer; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin); do_InsertFromElementPtrLength(buffer, atPosition, length); delete buffer; } // else assert } } template void basic_nsAWritableString::do_InsertFromReadable( const basic_nsAReadableString& aReadable, PRUint32 atPosition ) { PRUint32 oldLength = this->Length(); SetLength(oldLength + aReadable.Length()); nsReadingIterator fromBegin, fromEnd; nsWritingIterator 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))); } 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; nsReadingIterator fromBegin, fromEnd; nsWritingIterator toBegin; if ( cutEnd < myLength ) copy_string(this->BeginReading(fromBegin).advance(PRInt32(cutEnd)), this->EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart))); SetLength(myLength-cutLength); } // // |Replace()| // template void basic_nsAWritableString::ReplaceFromReadable( PRUint32 cutStart, PRUint32 cutLength, const basic_nsAReadableString& aReplacement ) { if ( NS_STATIC_CAST(const basic_nsAReadableString*, this) != &aReplacement ) do_ReplaceFromReadable(cutStart, cutLength, aReplacement); else ReplaceFromPromise(cutStart, cutLength, aReplacement); } template void basic_nsAWritableString::ReplaceFromPromise( PRUint32 cutStart, PRUint32 cutLength, const basic_nsAReadableString& aReadable ) { if ( !aReadable.Promises(*this) ) do_ReplaceFromReadable(cutStart, cutLength, aReadable); else { PRUint32 length = aReadable.Length(); CharT* buffer = new CharT[length]; if ( buffer ) { nsReadingIterator fromBegin, fromEnd; CharT* toBegin = buffer; copy_string(aReadable.BeginReading(fromBegin), aReadable.EndReading(fromEnd), toBegin); do_ReplaceFromReadable(cutStart, cutLength, basic_nsLiteralString(buffer, length)); delete buffer; } // else assert? } } 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; nsReadingIterator fromBegin, fromEnd; nsWritingIterator 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)), BeginWriting(toBegin).advance(PRInt32(replacementEnd))); copy_string(aReplacement.BeginReading(fromBegin), aReplacement.EndReading(fromEnd), BeginWriting(toBegin).advance(PRInt32(cutStart))); } template PRUint32 basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 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(); } template inline PRUint32 basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const { return Mid(aResult, 0, aLengthToCopy); } template PRUint32 basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const { PRUint32 myLength = Length(); aLengthToCopy = NS_MIN(myLength, aLengthToCopy); return Mid(aResult, myLength-aLengthToCopy, aLengthToCopy); } // // Types // typedef basic_nsAWritableString nsAWritableString; typedef basic_nsAWritableString nsAWritableCString; #endif // !defined(nsAWritableString_h___)