From 7ac3541035dbf97fbfc599fbfaa92a73b8dc08e6 Mon Sep 17 00:00:00 2001 From: "scc%netscape.com" Date: Sun, 12 Mar 2000 01:28:16 +0000 Subject: [PATCH] Incremental changes. Reorganized code; implemented more factored methods. Moved string owner from fragment to iterator. Renamed the |const| version of |GetFragment| to |GetConstFragment| so we didn't mix overloading with overriding; eliminating need for some uses of |using| ... may want to do the same thing with |Begin| and |End|. These files are not part of the build. --- string/public/nsAReadableString.h | 552 ++++++++++++++++-------- string/public/nsAWritableString.h | 77 +++- xpcom/ds/nsAReadableString.h | 552 ++++++++++++++++-------- xpcom/ds/nsAWritableString.h | 77 +++- xpcom/string/public/nsAReadableString.h | 552 ++++++++++++++++-------- xpcom/string/public/nsAWritableString.h | 77 +++- 6 files changed, 1311 insertions(+), 576 deletions(-) diff --git a/string/public/nsAReadableString.h b/string/public/nsAReadableString.h index 7c4a27857ba..3996229b551 100644 --- a/string/public/nsAReadableString.h +++ b/string/public/nsAReadableString.h @@ -54,30 +54,21 @@ */ -#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ - template \ - inline \ - PRBool \ - operator comp( T1 lhs, T2 rhs ) \ - { \ - return PRBool(Compare(lhs, rhs) comp 0); \ - } - -#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) - -#define NS_DEF_STRING_COMPARISONS(T) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) - template class basic_nsAWritableString; // ...because we sometimes use them as `out' params +template class basic_nsLiteralString; + // ...because we sometimes use them as in params to force the conversion of |CharT*|s + + + + + + + // + // nsAReadable[C]String + // + template class basic_nsAReadableString /* @@ -86,18 +77,14 @@ class basic_nsAReadableString { protected: - struct ConstFragment { - const CharT* mStart; - const CharT* mEnd; + const CharT* mStart; + const CharT* mEnd; + PRUint32 mFragmentIdentifier; - const basic_nsAReadableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - ConstFragment( const basic_nsAReadableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + ConstFragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } @@ -106,23 +93,32 @@ class basic_nsAReadableString public: enum FragmentRequest { kPrevFragment, kFirstFragment, kLastFragment, kNextFragment, kFragmentAt }; - // Damn! Had to make |GetFragment| public because the compilers suck. Should be protected. - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; + // Damn! Had to make |GetConstFragment| public because the compilers suck. Should be protected. + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; friend class ConstIterator; class ConstIterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAReadableString; ConstFragment mFragment; const CharT* mPosition; + const basic_nsAReadableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -130,12 +126,16 @@ class basic_nsAReadableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - ConstIterator( const ConstFragment& aFragment, const CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + ConstIterator( const ConstFragment& aFragment, + const CharT* aStartingPosition, + const basic_nsAReadableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -207,29 +207,40 @@ class basic_nsAReadableString ConstIterator Begin( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, aOffset); + return ConstIterator(fragment, startPos, *this); } ConstIterator End( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); + return ConstIterator(fragment, startPos, *this); } public: virtual ~basic_nsAReadableString() { } + // ...yes, I expect to be sub-classed. virtual PRUint32 Length() const = 0; - PRBool IsEmpty() const { return Length()==0; } - // PRBool IsOrdered() const; + PRBool + IsEmpty() const + { + return Length() == 0; + } + /* + RickG says the following three routines, |IsUnicode()|, |GetBuffer()|, and |GetUnicode()| + shouldn't be implemented because they're wrong access. I agree. Callers who really need + this access should use the iterators instead. We'll use these to ease the transition to + |nsAReadable...|, and then remove them as soon as possible. + */ + PRBool IsUnicode() const { return PR_FALSE; } // ...but note specialization for |PRUnichar|, below @@ -237,31 +248,19 @@ class basic_nsAReadableString const PRUnichar* GetUnicode() const { return 0; } // ...but note specializations for |char| and |PRUnichar|, below - // CharT operator[]( PRUint32 ) const; - // CharT CharAt( PRUint32 ) const; - // CharT First() const; - // CharT Last() const; - // void ToLowerCase( basic_nsAWritableString& ) const; - // void ToUpperCase( basic_nsAWritableString& ) const; - // PRUint32 CountChar( char_type ) const; + CharT CharAt( PRUint32 ) const; + CharT operator[]( PRUint32 ) const; + CharT First() const; + CharT Last() const; - // nsString* ToNewString() const; NO! The right way to say this is - // new nsString( fromAReadableString ) - - // char* ToNewCString() const; - // char* ToNewUTF8String() const; - // PRUnichar* ToNewUnicode() const; - // char* ToCString( char*, PRUint32, PRUint32 ) const; - // double ToFLoat( PRInt32* aErrorCode ) const; - // long ToInteger( PRInt32* aErrorCode, PRUint32 aRadix ); + PRUint32 CountChar( CharT ) const; PRUint32 Left( basic_nsAWritableString&, PRUint32 ) const; PRUint32 Mid( basic_nsAWritableString&, PRUint32, PRUint32 ) const; PRUint32 Right( basic_nsAWritableString&, PRUint32 ) const; - // PRUint32 BinarySearch( CharT ) const; // Find( ... ) const; // FindChar( ... ) const; // FindCharInSet( ... ) const; @@ -270,24 +269,66 @@ class basic_nsAReadableString // RFindCharInSet( ... ) const; int Compare( const basic_nsAReadableString& rhs ) const; - // int Compare( const CharT*, const CharT* ) const; + int Compare( const basic_nsLiteralString& rhs ) const; - // Equals + + // |Equals()| is a synonym for |Compare()| + + PRBool + Equals( const basic_nsAReadableString& rhs ) const + { + return Compare(rhs) == 0; + } + + PRBool + Equals( const basic_nsLiteralString& rhs ) const + { + return Compare(rhs) == 0; + } + + + /* + Shouldn't be implemented because they're i18n sensitive. + Let's leave them in |nsString| for now. + */ + + // ToLowerCase + // ToUpperCase // EqualsIgnoreCase - // IsASCII // IsSpace // IsAlpha // IsDigit + // ToFloat + // ToInteger + // char* ToNewCString() const; + // char* ToNewUTF8String() const; + // PRUnichar* ToNewUnicode() const; + // char* ToCString( char*, PRUint32, PRUint32 ) const; /* - Normally you wouldn't declare these as members... - - ...explanation to come... + Shouldn't be implemented because it's wrong duplication. + Let's leave it in |nsString| for now. */ + // nsString* ToNewString() const; + // NO! The right way to say this is |new nsString( fromAReadableString )| + + + /* + Shouldn't be implemented because they're not generally applicable. + Let's leave them in |nsString| for now. + */ + + // IsOrdered + // BinarySearch + + + + // Comparison operators are all synonyms for |Compare()| + PRBool operator!=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)!=0; } PRBool operator< ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)< 0; } PRBool operator<=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)<=0; } @@ -296,6 +337,28 @@ class basic_nsAReadableString PRBool operator> ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)> 0; } }; +#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ + template \ + inline \ + PRBool \ + operator comp( T1 lhs, T2 rhs ) \ + { \ + return PRBool(Compare(lhs, rhs) comp 0); \ + } + +#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) + +#define NS_DEF_STRING_COMPARISONS(T) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) + + NS_DEF_STRING_COMPARISONS(basic_nsAReadableString) @@ -308,17 +371,6 @@ basic_nsAReadableString::IsUnicode() const return PR_TRUE; } -NS_SPECIALIZE_TEMPLATE -inline -const PRUnichar* -basic_nsAReadableString::GetUnicode() const - // DEPRECATED: use the iterators instead - { - ConstFragment fragment; - GetFragment(fragment, kFirstFragment); - return fragment.mStart; - } - NS_SPECIALIZE_TEMPLATE inline const char* @@ -326,21 +378,163 @@ basic_nsAReadableString::GetBuffer() const // DEPRECATED: use the iterators instead { ConstFragment fragment; - GetFragment(fragment, kFirstFragment); + GetConstFragment(fragment, kFirstFragment); + return fragment.mStart; + } + +NS_SPECIALIZE_TEMPLATE +inline +const PRUnichar* +basic_nsAReadableString::GetUnicode() const + // DEPRECATED: use the iterators instead + { + ConstFragment fragment; + GetConstFragment(fragment, kFirstFragment); return fragment.mStart; } + /* + Note: the following four functions, |CharAt|, |operator[]|, |First|, and |Last|, are implemented + in the simplest reasonable scheme; by calling |GetConstFragment| and resolving the pointer it + returns. The alternative is to force at least one of these methods to be |virtual|. The ideal + candidate for that change would be |CharAt|. + + This is something to measure in the context of how string classes are actually used. In practice, + do people extract a character at a time in performance critical places? If so, can they use + iterators instead? If they must extract single characters, _and_ they can't use iterators, _and_ + it happens enough to notice, then we'll take the hit and make |CharAt| virtual. + */ + +template +inline +CharT +basic_nsAReadableString::CharAt( PRUint32 aIndex ) const + { + // ??? Is |CharAt()| supposed to be the 'safe' version? + ConstFragment fragment; + return *GetConstFragment(fragment, kFragmentAt, aIndex); + } + +template +inline +CharT +basic_nsAReadableString::operator[]( PRUint32 aIndex ) const + { + return CharAt(aIndex); + } + +template +inline +CharT +basic_nsAReadableString::First() const + { + return CharAt(0); + } + +template +inline +CharT +basic_nsAReadableString::Last() const + { + return CharAt(Length()-1); + } + +template +PRUint32 +basic_nsAReadableString::CountChar( CharT c ) const + { + return count(Begin(), End(), c); + } + + + /* + Note: |Left()|, |Mid()|, and |Right()| could be modified to notice when they degenerate into copying the + entire string, and call |Assign()| instead. This would be a win when the underlying implementation of + both strings could do buffer sharing. This is _definitely_ something that should be measured before + being implemented. + */ + +template +PRUint32 +basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, 0, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, aStartPos, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + PRUint32 myLength = Length(); + aLengthToCopy = min(myLength, aLengthToCopy); + aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); + return aResult.Length(); + } + + + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const + { + return ::Compare(*this, rhs); + } + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsLiteralString& rhs ) const + { + return ::Compare(*this, rhs); + } + + + + + + + // + // nsLiteral[C]String + // + template class basic_nsLiteralString : public basic_nsAReadableString + /* + ...this class wraps a constant literal string and lets it act like an |nsAReadable...|. + + Use it like this: + + SomeFunctionTakingACString( nsLiteralCString("Hello, World!") ); + + With some tweaking, I think I can make this work as well... + + SomeStringFunc( nsLiteralString( L"Hello, World!" ) ); + + This class just holds a pointer. If you don't supply the length, it must calculate it. + No copying or allocations are performed. + + |const basic_nsLiteralString&| appears frequently in interfaces because it + allows the automatic conversion of a |CharT*|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: @@ -368,37 +562,89 @@ class basic_nsLiteralString NS_DEF_STRING_COMPARISONS(basic_nsLiteralString) +template +const CharT* +basic_nsLiteralString::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const + { + switch ( aRequest ) + { + case kFirstFragment: + case kLastFragment: + case kFragmentAt: + aFragment.mStart = mStart; + aFragment.mEnd = mEnd; + return mStart + aOffset; + + case kPrevFragment: + case kNextFragment: + default: + return 0; + } + } + +template +PRUint32 +basic_nsLiteralString::Length() const + { + return PRUint32(mEnd - mStart); + } + + + + + + + // + // nsPromiseConcatenation + // + template class nsPromiseConcatenation : public basic_nsAReadableString /* - ...not unlike RickG's original |nsSubsumeString| in _intent_. + NOT FOR USE BY HUMANS + + Instances of this class only exist as anonymous temporary results from |operator+()|. + This is the machinery that makes string concatenation efficient. No allocations or + character copies are required unless and until a final assignment is made. It works + its magic by overriding and forwarding calls to |GetConstFragment()|. + + Note: |nsPromiseConcatenation| imposes some limits on string concatenation with |operator+()|. + - no more than 33 strings, e.g., |s1 + s2 + s3 + ... s32 + s33| + - left to right evaluation is required ... do not use parentheses to override this + + In practice, neither of these is onerous. Parentheses do not change the semantics of the + concatenation, only the order in which the result is assembled ... so there's no reason + for a user to need to control it. Too many strings summed together can easily be worked + around with an intermediate assignment. I wouldn't have the parentheses limitation if I + assigned the identifier mask starting at the top, the first time anybody called + |GetConstFragment()|. */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; static const int kLeftString = 0; static const int kRightString = 1; int - current_string( const ConstFragment& aFragment ) const + GetCurrentStringFromFragment( const ConstFragment& aFragment ) const { return (aFragment.mFragmentIdentifier & mFragmentIdentifierMask) ? kRightString : kLeftString; } int - use_left_string( ConstFragment& aFragment ) const + SetLeftStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier &= ~mFragmentIdentifierMask; return kLeftString; } int - use_right_string( ConstFragment& aFragment ) const + SetRightStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier |= mFragmentIdentifierMask; return kRightString; @@ -437,35 +683,37 @@ nsPromiseConcatenation::Length() const template const CharT* -nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseConcatenation::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { const int kLeftString = 0; const int kRightString = 1; int whichString; + // based on the request, pick which string we will forward the |GetConstFragment()| call into + switch ( aRequest ) { case kPrevFragment: case kNextFragment: - whichString = current_string(aFragment); + whichString = GetCurrentStringFromFragment(aFragment); break; case kFirstFragment: - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); break; case kLastFragment: - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); break; case kFragmentAt: PRUint32 leftLength = mStrings[kLeftString]->Length(); if ( aPosition < leftLength ) - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); else { - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); aPosition -= leftLength; } break; @@ -477,7 +725,7 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe do { done = true; - result = mStrings[whichString]->GetFragment(aFragment, aRequest, aPosition); + result = mStrings[whichString]->GetConstFragment(aFragment, aRequest, aPosition); if ( !result ) { @@ -485,12 +733,12 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe if ( aRequest == kNextFragment && whichString == kLeftString ) { aRequest = kFirstFragment; - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); } else if ( aRequest == kPrevFragment && whichString == kRightString ) { aRequest = kLastFragment; - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); } else done = true; @@ -508,15 +756,31 @@ nsPromiseConcatenation::operator+( const basic_nsAReadableString& } + + + + + // + // nsPromiseSubstring + // + template class nsPromiseSubstring : public basic_nsAReadableString + /* + NOT FOR USE BY HUMANS (mostly) + + ...not unlike |nsPromiseConcatenation|. Instances of this class exist only as anonymous + temporary results from |Substring()|. Like |nsPromiseConcatenation|, this class only + holds a pointer, no string data of its own. It does its magic by overriding and forwarding + calls to |GetConstFragment()|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: nsPromiseSubstring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aLength ) @@ -546,8 +810,11 @@ nsPromiseSubstring::Length() const template const CharT* -nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseSubstring::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { + // Offset any request for a specific position (First, Last, At) by our + // substrings startpos within the owning string + if ( aRequest == kFirstFragment ) { aPosition = mStartPos; @@ -555,15 +822,24 @@ nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentReques } else if ( aRequest == kLastFragment ) { - aPosition = mLength + mStartPos; + aPosition = mStartPos + mLength; aRequest = kFragmentAt; } else if ( aRequest == kFragmentAt ) aPosition += mStartPos; - return mString.GetFragment(aFragment, aRequest, aPosition); + return mString.GetConstFragment(aFragment, aRequest, aPosition); } + + + + + + // + // Global functions + // + template nsPromiseSubstring Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aSubstringLength ) @@ -572,61 +848,6 @@ Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PR } -template -const CharT* -basic_nsLiteralString::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const - { - switch ( aRequest ) - { - case kFirstFragment: - case kLastFragment: - case kFragmentAt: - aFragment.mStart = mStart; - aFragment.mEnd = mEnd; - return mStart + aOffset; - - case kPrevFragment: - case kNextFragment: - default: - return 0; - } - } - -template -PRUint32 -basic_nsLiteralString::Length() const - { - return PRUint32(mEnd - mStart); - } - - - -template -PRUint32 -basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, 0, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, aStartPos, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - PRUint32 myLength = Length(); - aLengthToCopy = min(myLength, aLengthToCopy); - aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); - return aResult.Length(); - } - template int Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableString& rhs ) @@ -634,17 +855,11 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin /* If this turns out to be too slow (after measurement), there are two important modifications 1) chunky iterators - 2) use char_traits::compare + 2) and then possibly use |char_traits::compare| */ PRUint32 lLength = lhs.Length(); PRUint32 rLength = rhs.Length(); - int result = 0; - if ( lLength < rLength ) - result = -1; - else if ( lLength > rLength ) - result = 1; - PRUint32 lengthToCompare = min(lLength, rLength); typedef typename basic_nsAReadableString::ConstIterator ConstIterator; @@ -663,10 +878,16 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin ++rPos; } - return result; + if ( lLength < rLength ) + return -1; + else if ( rLength < lLength ) + return 1; + else + return 0; } template +inline int Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) { @@ -674,20 +895,13 @@ Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) } template +inline int Compare( const CharT* lhs, const basic_nsAReadableString& rhs ) { return Compare(basic_nsLiteralString(lhs), rhs); } -template -inline -int -basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const - { - return ::Compare(*this, rhs); - } - /* diff --git a/string/public/nsAWritableString.h b/string/public/nsAWritableString.h index e2a535e8bb0..4cda230a73a 100644 --- a/string/public/nsAWritableString.h +++ b/string/public/nsAWritableString.h @@ -51,38 +51,43 @@ class basic_nsAWritableString struct Fragment { - CharT* mStart; - CharT* mEnd; + CharT* mStart; + CharT* mEnd; + PRUint32 mFragmentIdentifier; - basic_nsAWritableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - Fragment( basic_nsAWritableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + Fragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } }; public: - using basic_nsAReadableString::GetFragment; virtual CharT* GetFragment( Fragment&, FragmentRequest, PRUint32 = 0 ) = 0; friend class Iterator; class Iterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAWritableString; Fragment mFragment; CharT* mPosition; + basic_nsAWritableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -90,12 +95,16 @@ class basic_nsAWritableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - Iterator( Fragment& aFragment, CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + Iterator( Fragment& aFragment, + CharT* aStartingPosition, + basic_nsAWritableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -146,40 +155,55 @@ class basic_nsAWritableString } PRBool - operator==( const ConstIterator& rhs ) + operator==( const Iterator& rhs ) { return mPosition == rhs.mPosition; } PRBool - operator!=( const ConstIterator& rhs ) + operator!=( const Iterator& rhs ) { return mPosition != rhs.mPosition; } }; public: + +#ifdef HAVE_CPP_USING using basic_nsAReadableString::Begin; + using basic_nsAReadableString::End; +#else + basic_nsAReadableString::ConstIterator + Begin( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::Begin(aOffset); + } + + basic_nsAReadableString::ConstIterator + End( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::End(aOffset); + } +#endif Iterator Begin( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - using basic_nsAReadableString::End; Iterator End( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - // virtual void Splice( ... ); + virtual void Splice(); virtual void SetCapacity( PRUint32 ) = 0; virtual void SetLength( PRUint32 ) = 0; @@ -195,8 +219,8 @@ class basic_nsAWritableString // virtual PRBool SetCharAt( char_type, index_type ) = 0; - void ToLowerCase(); - void ToUpperCase(); + // void ToLowerCase(); + // void ToUpperCase(); // void StripChars( const CharT* aSet ); // void StripChar( ... ); @@ -246,9 +270,16 @@ class basic_nsAWritableString NS_DEF_STRING_COMPARISONS(basic_nsAWritableString) +template +void +basic_nsAWritableString::Splice() + { + } + template void basic_nsAWritableString::Assign( const basic_nsAReadableString& rhs ) + // Default implementation. Derived classes may be able to do something smarter... { SetLength(rhs.Length()); std::copy(rhs.Begin(), rhs.End(), Begin()); diff --git a/xpcom/ds/nsAReadableString.h b/xpcom/ds/nsAReadableString.h index 7c4a27857ba..3996229b551 100644 --- a/xpcom/ds/nsAReadableString.h +++ b/xpcom/ds/nsAReadableString.h @@ -54,30 +54,21 @@ */ -#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ - template \ - inline \ - PRBool \ - operator comp( T1 lhs, T2 rhs ) \ - { \ - return PRBool(Compare(lhs, rhs) comp 0); \ - } - -#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) - -#define NS_DEF_STRING_COMPARISONS(T) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) - template class basic_nsAWritableString; // ...because we sometimes use them as `out' params +template class basic_nsLiteralString; + // ...because we sometimes use them as in params to force the conversion of |CharT*|s + + + + + + + // + // nsAReadable[C]String + // + template class basic_nsAReadableString /* @@ -86,18 +77,14 @@ class basic_nsAReadableString { protected: - struct ConstFragment { - const CharT* mStart; - const CharT* mEnd; + const CharT* mStart; + const CharT* mEnd; + PRUint32 mFragmentIdentifier; - const basic_nsAReadableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - ConstFragment( const basic_nsAReadableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + ConstFragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } @@ -106,23 +93,32 @@ class basic_nsAReadableString public: enum FragmentRequest { kPrevFragment, kFirstFragment, kLastFragment, kNextFragment, kFragmentAt }; - // Damn! Had to make |GetFragment| public because the compilers suck. Should be protected. - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; + // Damn! Had to make |GetConstFragment| public because the compilers suck. Should be protected. + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; friend class ConstIterator; class ConstIterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAReadableString; ConstFragment mFragment; const CharT* mPosition; + const basic_nsAReadableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -130,12 +126,16 @@ class basic_nsAReadableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - ConstIterator( const ConstFragment& aFragment, const CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + ConstIterator( const ConstFragment& aFragment, + const CharT* aStartingPosition, + const basic_nsAReadableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -207,29 +207,40 @@ class basic_nsAReadableString ConstIterator Begin( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, aOffset); + return ConstIterator(fragment, startPos, *this); } ConstIterator End( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); + return ConstIterator(fragment, startPos, *this); } public: virtual ~basic_nsAReadableString() { } + // ...yes, I expect to be sub-classed. virtual PRUint32 Length() const = 0; - PRBool IsEmpty() const { return Length()==0; } - // PRBool IsOrdered() const; + PRBool + IsEmpty() const + { + return Length() == 0; + } + /* + RickG says the following three routines, |IsUnicode()|, |GetBuffer()|, and |GetUnicode()| + shouldn't be implemented because they're wrong access. I agree. Callers who really need + this access should use the iterators instead. We'll use these to ease the transition to + |nsAReadable...|, and then remove them as soon as possible. + */ + PRBool IsUnicode() const { return PR_FALSE; } // ...but note specialization for |PRUnichar|, below @@ -237,31 +248,19 @@ class basic_nsAReadableString const PRUnichar* GetUnicode() const { return 0; } // ...but note specializations for |char| and |PRUnichar|, below - // CharT operator[]( PRUint32 ) const; - // CharT CharAt( PRUint32 ) const; - // CharT First() const; - // CharT Last() const; - // void ToLowerCase( basic_nsAWritableString& ) const; - // void ToUpperCase( basic_nsAWritableString& ) const; - // PRUint32 CountChar( char_type ) const; + CharT CharAt( PRUint32 ) const; + CharT operator[]( PRUint32 ) const; + CharT First() const; + CharT Last() const; - // nsString* ToNewString() const; NO! The right way to say this is - // new nsString( fromAReadableString ) - - // char* ToNewCString() const; - // char* ToNewUTF8String() const; - // PRUnichar* ToNewUnicode() const; - // char* ToCString( char*, PRUint32, PRUint32 ) const; - // double ToFLoat( PRInt32* aErrorCode ) const; - // long ToInteger( PRInt32* aErrorCode, PRUint32 aRadix ); + PRUint32 CountChar( CharT ) const; PRUint32 Left( basic_nsAWritableString&, PRUint32 ) const; PRUint32 Mid( basic_nsAWritableString&, PRUint32, PRUint32 ) const; PRUint32 Right( basic_nsAWritableString&, PRUint32 ) const; - // PRUint32 BinarySearch( CharT ) const; // Find( ... ) const; // FindChar( ... ) const; // FindCharInSet( ... ) const; @@ -270,24 +269,66 @@ class basic_nsAReadableString // RFindCharInSet( ... ) const; int Compare( const basic_nsAReadableString& rhs ) const; - // int Compare( const CharT*, const CharT* ) const; + int Compare( const basic_nsLiteralString& rhs ) const; - // Equals + + // |Equals()| is a synonym for |Compare()| + + PRBool + Equals( const basic_nsAReadableString& rhs ) const + { + return Compare(rhs) == 0; + } + + PRBool + Equals( const basic_nsLiteralString& rhs ) const + { + return Compare(rhs) == 0; + } + + + /* + Shouldn't be implemented because they're i18n sensitive. + Let's leave them in |nsString| for now. + */ + + // ToLowerCase + // ToUpperCase // EqualsIgnoreCase - // IsASCII // IsSpace // IsAlpha // IsDigit + // ToFloat + // ToInteger + // char* ToNewCString() const; + // char* ToNewUTF8String() const; + // PRUnichar* ToNewUnicode() const; + // char* ToCString( char*, PRUint32, PRUint32 ) const; /* - Normally you wouldn't declare these as members... - - ...explanation to come... + Shouldn't be implemented because it's wrong duplication. + Let's leave it in |nsString| for now. */ + // nsString* ToNewString() const; + // NO! The right way to say this is |new nsString( fromAReadableString )| + + + /* + Shouldn't be implemented because they're not generally applicable. + Let's leave them in |nsString| for now. + */ + + // IsOrdered + // BinarySearch + + + + // Comparison operators are all synonyms for |Compare()| + PRBool operator!=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)!=0; } PRBool operator< ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)< 0; } PRBool operator<=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)<=0; } @@ -296,6 +337,28 @@ class basic_nsAReadableString PRBool operator> ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)> 0; } }; +#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ + template \ + inline \ + PRBool \ + operator comp( T1 lhs, T2 rhs ) \ + { \ + return PRBool(Compare(lhs, rhs) comp 0); \ + } + +#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) + +#define NS_DEF_STRING_COMPARISONS(T) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) + + NS_DEF_STRING_COMPARISONS(basic_nsAReadableString) @@ -308,17 +371,6 @@ basic_nsAReadableString::IsUnicode() const return PR_TRUE; } -NS_SPECIALIZE_TEMPLATE -inline -const PRUnichar* -basic_nsAReadableString::GetUnicode() const - // DEPRECATED: use the iterators instead - { - ConstFragment fragment; - GetFragment(fragment, kFirstFragment); - return fragment.mStart; - } - NS_SPECIALIZE_TEMPLATE inline const char* @@ -326,21 +378,163 @@ basic_nsAReadableString::GetBuffer() const // DEPRECATED: use the iterators instead { ConstFragment fragment; - GetFragment(fragment, kFirstFragment); + GetConstFragment(fragment, kFirstFragment); + return fragment.mStart; + } + +NS_SPECIALIZE_TEMPLATE +inline +const PRUnichar* +basic_nsAReadableString::GetUnicode() const + // DEPRECATED: use the iterators instead + { + ConstFragment fragment; + GetConstFragment(fragment, kFirstFragment); return fragment.mStart; } + /* + Note: the following four functions, |CharAt|, |operator[]|, |First|, and |Last|, are implemented + in the simplest reasonable scheme; by calling |GetConstFragment| and resolving the pointer it + returns. The alternative is to force at least one of these methods to be |virtual|. The ideal + candidate for that change would be |CharAt|. + + This is something to measure in the context of how string classes are actually used. In practice, + do people extract a character at a time in performance critical places? If so, can they use + iterators instead? If they must extract single characters, _and_ they can't use iterators, _and_ + it happens enough to notice, then we'll take the hit and make |CharAt| virtual. + */ + +template +inline +CharT +basic_nsAReadableString::CharAt( PRUint32 aIndex ) const + { + // ??? Is |CharAt()| supposed to be the 'safe' version? + ConstFragment fragment; + return *GetConstFragment(fragment, kFragmentAt, aIndex); + } + +template +inline +CharT +basic_nsAReadableString::operator[]( PRUint32 aIndex ) const + { + return CharAt(aIndex); + } + +template +inline +CharT +basic_nsAReadableString::First() const + { + return CharAt(0); + } + +template +inline +CharT +basic_nsAReadableString::Last() const + { + return CharAt(Length()-1); + } + +template +PRUint32 +basic_nsAReadableString::CountChar( CharT c ) const + { + return count(Begin(), End(), c); + } + + + /* + Note: |Left()|, |Mid()|, and |Right()| could be modified to notice when they degenerate into copying the + entire string, and call |Assign()| instead. This would be a win when the underlying implementation of + both strings could do buffer sharing. This is _definitely_ something that should be measured before + being implemented. + */ + +template +PRUint32 +basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, 0, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, aStartPos, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + PRUint32 myLength = Length(); + aLengthToCopy = min(myLength, aLengthToCopy); + aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); + return aResult.Length(); + } + + + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const + { + return ::Compare(*this, rhs); + } + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsLiteralString& rhs ) const + { + return ::Compare(*this, rhs); + } + + + + + + + // + // nsLiteral[C]String + // + template class basic_nsLiteralString : public basic_nsAReadableString + /* + ...this class wraps a constant literal string and lets it act like an |nsAReadable...|. + + Use it like this: + + SomeFunctionTakingACString( nsLiteralCString("Hello, World!") ); + + With some tweaking, I think I can make this work as well... + + SomeStringFunc( nsLiteralString( L"Hello, World!" ) ); + + This class just holds a pointer. If you don't supply the length, it must calculate it. + No copying or allocations are performed. + + |const basic_nsLiteralString&| appears frequently in interfaces because it + allows the automatic conversion of a |CharT*|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: @@ -368,37 +562,89 @@ class basic_nsLiteralString NS_DEF_STRING_COMPARISONS(basic_nsLiteralString) +template +const CharT* +basic_nsLiteralString::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const + { + switch ( aRequest ) + { + case kFirstFragment: + case kLastFragment: + case kFragmentAt: + aFragment.mStart = mStart; + aFragment.mEnd = mEnd; + return mStart + aOffset; + + case kPrevFragment: + case kNextFragment: + default: + return 0; + } + } + +template +PRUint32 +basic_nsLiteralString::Length() const + { + return PRUint32(mEnd - mStart); + } + + + + + + + // + // nsPromiseConcatenation + // + template class nsPromiseConcatenation : public basic_nsAReadableString /* - ...not unlike RickG's original |nsSubsumeString| in _intent_. + NOT FOR USE BY HUMANS + + Instances of this class only exist as anonymous temporary results from |operator+()|. + This is the machinery that makes string concatenation efficient. No allocations or + character copies are required unless and until a final assignment is made. It works + its magic by overriding and forwarding calls to |GetConstFragment()|. + + Note: |nsPromiseConcatenation| imposes some limits on string concatenation with |operator+()|. + - no more than 33 strings, e.g., |s1 + s2 + s3 + ... s32 + s33| + - left to right evaluation is required ... do not use parentheses to override this + + In practice, neither of these is onerous. Parentheses do not change the semantics of the + concatenation, only the order in which the result is assembled ... so there's no reason + for a user to need to control it. Too many strings summed together can easily be worked + around with an intermediate assignment. I wouldn't have the parentheses limitation if I + assigned the identifier mask starting at the top, the first time anybody called + |GetConstFragment()|. */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; static const int kLeftString = 0; static const int kRightString = 1; int - current_string( const ConstFragment& aFragment ) const + GetCurrentStringFromFragment( const ConstFragment& aFragment ) const { return (aFragment.mFragmentIdentifier & mFragmentIdentifierMask) ? kRightString : kLeftString; } int - use_left_string( ConstFragment& aFragment ) const + SetLeftStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier &= ~mFragmentIdentifierMask; return kLeftString; } int - use_right_string( ConstFragment& aFragment ) const + SetRightStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier |= mFragmentIdentifierMask; return kRightString; @@ -437,35 +683,37 @@ nsPromiseConcatenation::Length() const template const CharT* -nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseConcatenation::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { const int kLeftString = 0; const int kRightString = 1; int whichString; + // based on the request, pick which string we will forward the |GetConstFragment()| call into + switch ( aRequest ) { case kPrevFragment: case kNextFragment: - whichString = current_string(aFragment); + whichString = GetCurrentStringFromFragment(aFragment); break; case kFirstFragment: - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); break; case kLastFragment: - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); break; case kFragmentAt: PRUint32 leftLength = mStrings[kLeftString]->Length(); if ( aPosition < leftLength ) - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); else { - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); aPosition -= leftLength; } break; @@ -477,7 +725,7 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe do { done = true; - result = mStrings[whichString]->GetFragment(aFragment, aRequest, aPosition); + result = mStrings[whichString]->GetConstFragment(aFragment, aRequest, aPosition); if ( !result ) { @@ -485,12 +733,12 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe if ( aRequest == kNextFragment && whichString == kLeftString ) { aRequest = kFirstFragment; - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); } else if ( aRequest == kPrevFragment && whichString == kRightString ) { aRequest = kLastFragment; - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); } else done = true; @@ -508,15 +756,31 @@ nsPromiseConcatenation::operator+( const basic_nsAReadableString& } + + + + + // + // nsPromiseSubstring + // + template class nsPromiseSubstring : public basic_nsAReadableString + /* + NOT FOR USE BY HUMANS (mostly) + + ...not unlike |nsPromiseConcatenation|. Instances of this class exist only as anonymous + temporary results from |Substring()|. Like |nsPromiseConcatenation|, this class only + holds a pointer, no string data of its own. It does its magic by overriding and forwarding + calls to |GetConstFragment()|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: nsPromiseSubstring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aLength ) @@ -546,8 +810,11 @@ nsPromiseSubstring::Length() const template const CharT* -nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseSubstring::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { + // Offset any request for a specific position (First, Last, At) by our + // substrings startpos within the owning string + if ( aRequest == kFirstFragment ) { aPosition = mStartPos; @@ -555,15 +822,24 @@ nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentReques } else if ( aRequest == kLastFragment ) { - aPosition = mLength + mStartPos; + aPosition = mStartPos + mLength; aRequest = kFragmentAt; } else if ( aRequest == kFragmentAt ) aPosition += mStartPos; - return mString.GetFragment(aFragment, aRequest, aPosition); + return mString.GetConstFragment(aFragment, aRequest, aPosition); } + + + + + + // + // Global functions + // + template nsPromiseSubstring Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aSubstringLength ) @@ -572,61 +848,6 @@ Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PR } -template -const CharT* -basic_nsLiteralString::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const - { - switch ( aRequest ) - { - case kFirstFragment: - case kLastFragment: - case kFragmentAt: - aFragment.mStart = mStart; - aFragment.mEnd = mEnd; - return mStart + aOffset; - - case kPrevFragment: - case kNextFragment: - default: - return 0; - } - } - -template -PRUint32 -basic_nsLiteralString::Length() const - { - return PRUint32(mEnd - mStart); - } - - - -template -PRUint32 -basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, 0, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, aStartPos, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - PRUint32 myLength = Length(); - aLengthToCopy = min(myLength, aLengthToCopy); - aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); - return aResult.Length(); - } - template int Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableString& rhs ) @@ -634,17 +855,11 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin /* If this turns out to be too slow (after measurement), there are two important modifications 1) chunky iterators - 2) use char_traits::compare + 2) and then possibly use |char_traits::compare| */ PRUint32 lLength = lhs.Length(); PRUint32 rLength = rhs.Length(); - int result = 0; - if ( lLength < rLength ) - result = -1; - else if ( lLength > rLength ) - result = 1; - PRUint32 lengthToCompare = min(lLength, rLength); typedef typename basic_nsAReadableString::ConstIterator ConstIterator; @@ -663,10 +878,16 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin ++rPos; } - return result; + if ( lLength < rLength ) + return -1; + else if ( rLength < lLength ) + return 1; + else + return 0; } template +inline int Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) { @@ -674,20 +895,13 @@ Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) } template +inline int Compare( const CharT* lhs, const basic_nsAReadableString& rhs ) { return Compare(basic_nsLiteralString(lhs), rhs); } -template -inline -int -basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const - { - return ::Compare(*this, rhs); - } - /* diff --git a/xpcom/ds/nsAWritableString.h b/xpcom/ds/nsAWritableString.h index e2a535e8bb0..4cda230a73a 100644 --- a/xpcom/ds/nsAWritableString.h +++ b/xpcom/ds/nsAWritableString.h @@ -51,38 +51,43 @@ class basic_nsAWritableString struct Fragment { - CharT* mStart; - CharT* mEnd; + CharT* mStart; + CharT* mEnd; + PRUint32 mFragmentIdentifier; - basic_nsAWritableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - Fragment( basic_nsAWritableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + Fragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } }; public: - using basic_nsAReadableString::GetFragment; virtual CharT* GetFragment( Fragment&, FragmentRequest, PRUint32 = 0 ) = 0; friend class Iterator; class Iterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAWritableString; Fragment mFragment; CharT* mPosition; + basic_nsAWritableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -90,12 +95,16 @@ class basic_nsAWritableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - Iterator( Fragment& aFragment, CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + Iterator( Fragment& aFragment, + CharT* aStartingPosition, + basic_nsAWritableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -146,40 +155,55 @@ class basic_nsAWritableString } PRBool - operator==( const ConstIterator& rhs ) + operator==( const Iterator& rhs ) { return mPosition == rhs.mPosition; } PRBool - operator!=( const ConstIterator& rhs ) + operator!=( const Iterator& rhs ) { return mPosition != rhs.mPosition; } }; public: + +#ifdef HAVE_CPP_USING using basic_nsAReadableString::Begin; + using basic_nsAReadableString::End; +#else + basic_nsAReadableString::ConstIterator + Begin( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::Begin(aOffset); + } + + basic_nsAReadableString::ConstIterator + End( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::End(aOffset); + } +#endif Iterator Begin( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - using basic_nsAReadableString::End; Iterator End( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - // virtual void Splice( ... ); + virtual void Splice(); virtual void SetCapacity( PRUint32 ) = 0; virtual void SetLength( PRUint32 ) = 0; @@ -195,8 +219,8 @@ class basic_nsAWritableString // virtual PRBool SetCharAt( char_type, index_type ) = 0; - void ToLowerCase(); - void ToUpperCase(); + // void ToLowerCase(); + // void ToUpperCase(); // void StripChars( const CharT* aSet ); // void StripChar( ... ); @@ -246,9 +270,16 @@ class basic_nsAWritableString NS_DEF_STRING_COMPARISONS(basic_nsAWritableString) +template +void +basic_nsAWritableString::Splice() + { + } + template void basic_nsAWritableString::Assign( const basic_nsAReadableString& rhs ) + // Default implementation. Derived classes may be able to do something smarter... { SetLength(rhs.Length()); std::copy(rhs.Begin(), rhs.End(), Begin()); diff --git a/xpcom/string/public/nsAReadableString.h b/xpcom/string/public/nsAReadableString.h index 7c4a27857ba..3996229b551 100644 --- a/xpcom/string/public/nsAReadableString.h +++ b/xpcom/string/public/nsAReadableString.h @@ -54,30 +54,21 @@ */ -#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ - template \ - inline \ - PRBool \ - operator comp( T1 lhs, T2 rhs ) \ - { \ - return PRBool(Compare(lhs, rhs) comp 0); \ - } - -#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ - NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) - -#define NS_DEF_STRING_COMPARISONS(T) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ - NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) - template class basic_nsAWritableString; // ...because we sometimes use them as `out' params +template class basic_nsLiteralString; + // ...because we sometimes use them as in params to force the conversion of |CharT*|s + + + + + + + // + // nsAReadable[C]String + // + template class basic_nsAReadableString /* @@ -86,18 +77,14 @@ class basic_nsAReadableString { protected: - struct ConstFragment { - const CharT* mStart; - const CharT* mEnd; + const CharT* mStart; + const CharT* mEnd; + PRUint32 mFragmentIdentifier; - const basic_nsAReadableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - ConstFragment( const basic_nsAReadableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + ConstFragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } @@ -106,23 +93,32 @@ class basic_nsAReadableString public: enum FragmentRequest { kPrevFragment, kFirstFragment, kLastFragment, kNextFragment, kFragmentAt }; - // Damn! Had to make |GetFragment| public because the compilers suck. Should be protected. - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; + // Damn! Had to make |GetConstFragment| public because the compilers suck. Should be protected. + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 = 0 ) const = 0; friend class ConstIterator; class ConstIterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAReadableString; ConstFragment mFragment; const CharT* mPosition; + const basic_nsAReadableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -130,12 +126,16 @@ class basic_nsAReadableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetConstFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - ConstIterator( const ConstFragment& aFragment, const CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + ConstIterator( const ConstFragment& aFragment, + const CharT* aStartingPosition, + const basic_nsAReadableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -207,29 +207,40 @@ class basic_nsAReadableString ConstIterator Begin( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, aOffset); + return ConstIterator(fragment, startPos, *this); } ConstIterator End( PRUint32 aOffset = 0 ) const { - ConstFragment fragment(this); - const CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return ConstIterator(fragment, startPos); + ConstFragment fragment; + const CharT* startPos = GetConstFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); + return ConstIterator(fragment, startPos, *this); } public: virtual ~basic_nsAReadableString() { } + // ...yes, I expect to be sub-classed. virtual PRUint32 Length() const = 0; - PRBool IsEmpty() const { return Length()==0; } - // PRBool IsOrdered() const; + PRBool + IsEmpty() const + { + return Length() == 0; + } + /* + RickG says the following three routines, |IsUnicode()|, |GetBuffer()|, and |GetUnicode()| + shouldn't be implemented because they're wrong access. I agree. Callers who really need + this access should use the iterators instead. We'll use these to ease the transition to + |nsAReadable...|, and then remove them as soon as possible. + */ + PRBool IsUnicode() const { return PR_FALSE; } // ...but note specialization for |PRUnichar|, below @@ -237,31 +248,19 @@ class basic_nsAReadableString const PRUnichar* GetUnicode() const { return 0; } // ...but note specializations for |char| and |PRUnichar|, below - // CharT operator[]( PRUint32 ) const; - // CharT CharAt( PRUint32 ) const; - // CharT First() const; - // CharT Last() const; - // void ToLowerCase( basic_nsAWritableString& ) const; - // void ToUpperCase( basic_nsAWritableString& ) const; - // PRUint32 CountChar( char_type ) const; + CharT CharAt( PRUint32 ) const; + CharT operator[]( PRUint32 ) const; + CharT First() const; + CharT Last() const; - // nsString* ToNewString() const; NO! The right way to say this is - // new nsString( fromAReadableString ) - - // char* ToNewCString() const; - // char* ToNewUTF8String() const; - // PRUnichar* ToNewUnicode() const; - // char* ToCString( char*, PRUint32, PRUint32 ) const; - // double ToFLoat( PRInt32* aErrorCode ) const; - // long ToInteger( PRInt32* aErrorCode, PRUint32 aRadix ); + PRUint32 CountChar( CharT ) const; PRUint32 Left( basic_nsAWritableString&, PRUint32 ) const; PRUint32 Mid( basic_nsAWritableString&, PRUint32, PRUint32 ) const; PRUint32 Right( basic_nsAWritableString&, PRUint32 ) const; - // PRUint32 BinarySearch( CharT ) const; // Find( ... ) const; // FindChar( ... ) const; // FindCharInSet( ... ) const; @@ -270,24 +269,66 @@ class basic_nsAReadableString // RFindCharInSet( ... ) const; int Compare( const basic_nsAReadableString& rhs ) const; - // int Compare( const CharT*, const CharT* ) const; + int Compare( const basic_nsLiteralString& rhs ) const; - // Equals + + // |Equals()| is a synonym for |Compare()| + + PRBool + Equals( const basic_nsAReadableString& rhs ) const + { + return Compare(rhs) == 0; + } + + PRBool + Equals( const basic_nsLiteralString& rhs ) const + { + return Compare(rhs) == 0; + } + + + /* + Shouldn't be implemented because they're i18n sensitive. + Let's leave them in |nsString| for now. + */ + + // ToLowerCase + // ToUpperCase // EqualsIgnoreCase - // IsASCII // IsSpace // IsAlpha // IsDigit + // ToFloat + // ToInteger + // char* ToNewCString() const; + // char* ToNewUTF8String() const; + // PRUnichar* ToNewUnicode() const; + // char* ToCString( char*, PRUint32, PRUint32 ) const; /* - Normally you wouldn't declare these as members... - - ...explanation to come... + Shouldn't be implemented because it's wrong duplication. + Let's leave it in |nsString| for now. */ + // nsString* ToNewString() const; + // NO! The right way to say this is |new nsString( fromAReadableString )| + + + /* + Shouldn't be implemented because they're not generally applicable. + Let's leave them in |nsString| for now. + */ + + // IsOrdered + // BinarySearch + + + + // Comparison operators are all synonyms for |Compare()| + PRBool operator!=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)!=0; } PRBool operator< ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)< 0; } PRBool operator<=( const basic_nsAReadableString& rhs ) const { return Compare(rhs)<=0; } @@ -296,6 +337,28 @@ class basic_nsAReadableString PRBool operator> ( const basic_nsAReadableString& rhs ) const { return Compare(rhs)> 0; } }; +#define NS_DEF_1_STRING_COMPARISON_OPERATOR(comp, T1, T2) \ + template \ + inline \ + PRBool \ + operator comp( T1 lhs, T2 rhs ) \ + { \ + return PRBool(Compare(lhs, rhs) comp 0); \ + } + +#define NS_DEF_STRING_COMPARISON_OPERATORS(T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(!=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(< , T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(<=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(==, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(>=, T1, T2) \ + NS_DEF_1_STRING_COMPARISON_OPERATOR(> , T1, T2) + +#define NS_DEF_STRING_COMPARISONS(T) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const T&, const CharT*) \ + NS_DEF_STRING_COMPARISON_OPERATORS(const CharT*, const T&) + + NS_DEF_STRING_COMPARISONS(basic_nsAReadableString) @@ -308,17 +371,6 @@ basic_nsAReadableString::IsUnicode() const return PR_TRUE; } -NS_SPECIALIZE_TEMPLATE -inline -const PRUnichar* -basic_nsAReadableString::GetUnicode() const - // DEPRECATED: use the iterators instead - { - ConstFragment fragment; - GetFragment(fragment, kFirstFragment); - return fragment.mStart; - } - NS_SPECIALIZE_TEMPLATE inline const char* @@ -326,21 +378,163 @@ basic_nsAReadableString::GetBuffer() const // DEPRECATED: use the iterators instead { ConstFragment fragment; - GetFragment(fragment, kFirstFragment); + GetConstFragment(fragment, kFirstFragment); + return fragment.mStart; + } + +NS_SPECIALIZE_TEMPLATE +inline +const PRUnichar* +basic_nsAReadableString::GetUnicode() const + // DEPRECATED: use the iterators instead + { + ConstFragment fragment; + GetConstFragment(fragment, kFirstFragment); return fragment.mStart; } + /* + Note: the following four functions, |CharAt|, |operator[]|, |First|, and |Last|, are implemented + in the simplest reasonable scheme; by calling |GetConstFragment| and resolving the pointer it + returns. The alternative is to force at least one of these methods to be |virtual|. The ideal + candidate for that change would be |CharAt|. + + This is something to measure in the context of how string classes are actually used. In practice, + do people extract a character at a time in performance critical places? If so, can they use + iterators instead? If they must extract single characters, _and_ they can't use iterators, _and_ + it happens enough to notice, then we'll take the hit and make |CharAt| virtual. + */ + +template +inline +CharT +basic_nsAReadableString::CharAt( PRUint32 aIndex ) const + { + // ??? Is |CharAt()| supposed to be the 'safe' version? + ConstFragment fragment; + return *GetConstFragment(fragment, kFragmentAt, aIndex); + } + +template +inline +CharT +basic_nsAReadableString::operator[]( PRUint32 aIndex ) const + { + return CharAt(aIndex); + } + +template +inline +CharT +basic_nsAReadableString::First() const + { + return CharAt(0); + } + +template +inline +CharT +basic_nsAReadableString::Last() const + { + return CharAt(Length()-1); + } + +template +PRUint32 +basic_nsAReadableString::CountChar( CharT c ) const + { + return count(Begin(), End(), c); + } + + + /* + Note: |Left()|, |Mid()|, and |Right()| could be modified to notice when they degenerate into copying the + entire string, and call |Assign()| instead. This would be a win when the underlying implementation of + both strings could do buffer sharing. This is _definitely_ something that should be measured before + being implemented. + */ + +template +PRUint32 +basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, 0, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const + { + aResult = Substring(*this, aStartPos, aLengthToCopy); + return aResult.Length(); + } + +template +PRUint32 +basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const + { + PRUint32 myLength = Length(); + aLengthToCopy = min(myLength, aLengthToCopy); + aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); + return aResult.Length(); + } + + + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const + { + return ::Compare(*this, rhs); + } + +template +inline +int +basic_nsAReadableString::Compare( const basic_nsLiteralString& rhs ) const + { + return ::Compare(*this, rhs); + } + + + + + + + // + // nsLiteral[C]String + // + template class basic_nsLiteralString : public basic_nsAReadableString + /* + ...this class wraps a constant literal string and lets it act like an |nsAReadable...|. + + Use it like this: + + SomeFunctionTakingACString( nsLiteralCString("Hello, World!") ); + + With some tweaking, I think I can make this work as well... + + SomeStringFunc( nsLiteralString( L"Hello, World!" ) ); + + This class just holds a pointer. If you don't supply the length, it must calculate it. + No copying or allocations are performed. + + |const basic_nsLiteralString&| appears frequently in interfaces because it + allows the automatic conversion of a |CharT*|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: @@ -368,37 +562,89 @@ class basic_nsLiteralString NS_DEF_STRING_COMPARISONS(basic_nsLiteralString) +template +const CharT* +basic_nsLiteralString::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const + { + switch ( aRequest ) + { + case kFirstFragment: + case kLastFragment: + case kFragmentAt: + aFragment.mStart = mStart; + aFragment.mEnd = mEnd; + return mStart + aOffset; + + case kPrevFragment: + case kNextFragment: + default: + return 0; + } + } + +template +PRUint32 +basic_nsLiteralString::Length() const + { + return PRUint32(mEnd - mStart); + } + + + + + + + // + // nsPromiseConcatenation + // + template class nsPromiseConcatenation : public basic_nsAReadableString /* - ...not unlike RickG's original |nsSubsumeString| in _intent_. + NOT FOR USE BY HUMANS + + Instances of this class only exist as anonymous temporary results from |operator+()|. + This is the machinery that makes string concatenation efficient. No allocations or + character copies are required unless and until a final assignment is made. It works + its magic by overriding and forwarding calls to |GetConstFragment()|. + + Note: |nsPromiseConcatenation| imposes some limits on string concatenation with |operator+()|. + - no more than 33 strings, e.g., |s1 + s2 + s3 + ... s32 + s33| + - left to right evaluation is required ... do not use parentheses to override this + + In practice, neither of these is onerous. Parentheses do not change the semantics of the + concatenation, only the order in which the result is assembled ... so there's no reason + for a user to need to control it. Too many strings summed together can easily be worked + around with an intermediate assignment. I wouldn't have the parentheses limitation if I + assigned the identifier mask starting at the top, the first time anybody called + |GetConstFragment()|. */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; static const int kLeftString = 0; static const int kRightString = 1; int - current_string( const ConstFragment& aFragment ) const + GetCurrentStringFromFragment( const ConstFragment& aFragment ) const { return (aFragment.mFragmentIdentifier & mFragmentIdentifierMask) ? kRightString : kLeftString; } int - use_left_string( ConstFragment& aFragment ) const + SetLeftStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier &= ~mFragmentIdentifierMask; return kLeftString; } int - use_right_string( ConstFragment& aFragment ) const + SetRightStringInFragment( ConstFragment& aFragment ) const { aFragment.mFragmentIdentifier |= mFragmentIdentifierMask; return kRightString; @@ -437,35 +683,37 @@ nsPromiseConcatenation::Length() const template const CharT* -nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseConcatenation::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { const int kLeftString = 0; const int kRightString = 1; int whichString; + // based on the request, pick which string we will forward the |GetConstFragment()| call into + switch ( aRequest ) { case kPrevFragment: case kNextFragment: - whichString = current_string(aFragment); + whichString = GetCurrentStringFromFragment(aFragment); break; case kFirstFragment: - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); break; case kLastFragment: - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); break; case kFragmentAt: PRUint32 leftLength = mStrings[kLeftString]->Length(); if ( aPosition < leftLength ) - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); else { - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); aPosition -= leftLength; } break; @@ -477,7 +725,7 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe do { done = true; - result = mStrings[whichString]->GetFragment(aFragment, aRequest, aPosition); + result = mStrings[whichString]->GetConstFragment(aFragment, aRequest, aPosition); if ( !result ) { @@ -485,12 +733,12 @@ nsPromiseConcatenation::GetFragment( ConstFragment& aFragment, FragmentRe if ( aRequest == kNextFragment && whichString == kLeftString ) { aRequest = kFirstFragment; - whichString = use_right_string(aFragment); + whichString = SetRightStringInFragment(aFragment); } else if ( aRequest == kPrevFragment && whichString == kRightString ) { aRequest = kLastFragment; - whichString = use_left_string(aFragment); + whichString = SetLeftStringInFragment(aFragment); } else done = true; @@ -508,15 +756,31 @@ nsPromiseConcatenation::operator+( const basic_nsAReadableString& } + + + + + // + // nsPromiseSubstring + // + template class nsPromiseSubstring : public basic_nsAReadableString + /* + NOT FOR USE BY HUMANS (mostly) + + ...not unlike |nsPromiseConcatenation|. Instances of this class exist only as anonymous + temporary results from |Substring()|. Like |nsPromiseConcatenation|, this class only + holds a pointer, no string data of its own. It does its magic by overriding and forwarding + calls to |GetConstFragment()|. + */ { typedef typename basic_nsAReadableString::FragmentRequest FragmentRequest; typedef typename basic_nsAWritableString::ConstFragment ConstFragment; protected: - virtual const CharT* GetFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; + virtual const CharT* GetConstFragment( ConstFragment&, FragmentRequest, PRUint32 ) const; public: nsPromiseSubstring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aLength ) @@ -546,8 +810,11 @@ nsPromiseSubstring::Length() const template const CharT* -nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const +nsPromiseSubstring::GetConstFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aPosition ) const { + // Offset any request for a specific position (First, Last, At) by our + // substrings startpos within the owning string + if ( aRequest == kFirstFragment ) { aPosition = mStartPos; @@ -555,15 +822,24 @@ nsPromiseSubstring::GetFragment( ConstFragment& aFragment, FragmentReques } else if ( aRequest == kLastFragment ) { - aPosition = mLength + mStartPos; + aPosition = mStartPos + mLength; aRequest = kFragmentAt; } else if ( aRequest == kFragmentAt ) aPosition += mStartPos; - return mString.GetFragment(aFragment, aRequest, aPosition); + return mString.GetConstFragment(aFragment, aRequest, aPosition); } + + + + + + // + // Global functions + // + template nsPromiseSubstring Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PRUint32 aSubstringLength ) @@ -572,61 +848,6 @@ Substring( const basic_nsAReadableString& aString, PRUint32 aStartPos, PR } -template -const CharT* -basic_nsLiteralString::GetFragment( ConstFragment& aFragment, FragmentRequest aRequest, PRUint32 aOffset ) const - { - switch ( aRequest ) - { - case kFirstFragment: - case kLastFragment: - case kFragmentAt: - aFragment.mStart = mStart; - aFragment.mEnd = mEnd; - return mStart + aOffset; - - case kPrevFragment: - case kNextFragment: - default: - return 0; - } - } - -template -PRUint32 -basic_nsLiteralString::Length() const - { - return PRUint32(mEnd - mStart); - } - - - -template -PRUint32 -basic_nsAReadableString::Left( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, 0, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Mid( basic_nsAWritableString& aResult, PRUint32 aStartPos, PRUint32 aLengthToCopy ) const - { - aResult = Substring(*this, aStartPos, aLengthToCopy); - return aResult.Length(); - } - -template -PRUint32 -basic_nsAReadableString::Right( basic_nsAWritableString& aResult, PRUint32 aLengthToCopy ) const - { - PRUint32 myLength = Length(); - aLengthToCopy = min(myLength, aLengthToCopy); - aResult = Substring(*this, myLength-aLengthToCopy, aLengthToCopy); - return aResult.Length(); - } - template int Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableString& rhs ) @@ -634,17 +855,11 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin /* If this turns out to be too slow (after measurement), there are two important modifications 1) chunky iterators - 2) use char_traits::compare + 2) and then possibly use |char_traits::compare| */ PRUint32 lLength = lhs.Length(); PRUint32 rLength = rhs.Length(); - int result = 0; - if ( lLength < rLength ) - result = -1; - else if ( lLength > rLength ) - result = 1; - PRUint32 lengthToCompare = min(lLength, rLength); typedef typename basic_nsAReadableString::ConstIterator ConstIterator; @@ -663,10 +878,16 @@ Compare( const basic_nsAReadableString& lhs, const basic_nsAReadableStrin ++rPos; } - return result; + if ( lLength < rLength ) + return -1; + else if ( rLength < lLength ) + return 1; + else + return 0; } template +inline int Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) { @@ -674,20 +895,13 @@ Compare( const basic_nsAReadableString& lhs, const CharT* rhs ) } template +inline int Compare( const CharT* lhs, const basic_nsAReadableString& rhs ) { return Compare(basic_nsLiteralString(lhs), rhs); } -template -inline -int -basic_nsAReadableString::Compare( const basic_nsAReadableString& rhs ) const - { - return ::Compare(*this, rhs); - } - /* diff --git a/xpcom/string/public/nsAWritableString.h b/xpcom/string/public/nsAWritableString.h index e2a535e8bb0..4cda230a73a 100644 --- a/xpcom/string/public/nsAWritableString.h +++ b/xpcom/string/public/nsAWritableString.h @@ -51,38 +51,43 @@ class basic_nsAWritableString struct Fragment { - CharT* mStart; - CharT* mEnd; + CharT* mStart; + CharT* mEnd; + PRUint32 mFragmentIdentifier; - basic_nsAWritableString* mOwningString; - PRUint32 mFragmentIdentifier; - - explicit - Fragment( basic_nsAWritableString* aOwner = 0 ) - : mStart(0), mEnd(0), mOwningString(aOwner), mFragmentIdentifier(0) + Fragment() + : mStart(0), mEnd(0), mFragmentIdentifier(0) { // nothing else to do here } }; public: - using basic_nsAReadableString::GetFragment; virtual CharT* GetFragment( Fragment&, FragmentRequest, PRUint32 = 0 ) = 0; friend class Iterator; class Iterator : public std::bidirectional_iterator_tag { + public: + typedef ptrdiff_t difference_type; + typedef CharT value_type; + typedef const CharT* pointer; + typedef const CharT& reference; + typedef bidirectional_iterator_tag iterator_category; + + private: friend class basic_nsAWritableString; Fragment mFragment; CharT* mPosition; + basic_nsAWritableString* mOwningString; void normalize_forward() { if ( mPosition == mFragment.mEnd ) - if ( mFragment.mOwningString->GetFragment(mFragment, kNextFragment) ) + if ( mOwningString->GetFragment(mFragment, kNextFragment) ) mPosition = mFragment.mStart; } @@ -90,12 +95,16 @@ class basic_nsAWritableString normalize_backward() { if ( mPosition == mFragment.mStart ) - if ( mFragment.mOwningString->GetFragment(mFragment, kPrevFragment) ) + if ( mOwningString->GetFragment(mFragment, kPrevFragment) ) mPosition = mFragment.mEnd; } - Iterator( Fragment& aFragment, CharT* aStartingPosition ) - : mFragment(aFragment), mPosition(aStartingPosition) + Iterator( Fragment& aFragment, + CharT* aStartingPosition, + basic_nsAWritableString& aOwningString ) + : mFragment(aFragment), + mPosition(aStartingPosition), + mOwningString(&aOwningString) { // nothing else to do here } @@ -146,40 +155,55 @@ class basic_nsAWritableString } PRBool - operator==( const ConstIterator& rhs ) + operator==( const Iterator& rhs ) { return mPosition == rhs.mPosition; } PRBool - operator!=( const ConstIterator& rhs ) + operator!=( const Iterator& rhs ) { return mPosition != rhs.mPosition; } }; public: + +#ifdef HAVE_CPP_USING using basic_nsAReadableString::Begin; + using basic_nsAReadableString::End; +#else + basic_nsAReadableString::ConstIterator + Begin( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::Begin(aOffset); + } + + basic_nsAReadableString::ConstIterator + End( PRUint32 aOffset = 0 ) const + { + return basic_nsAReadableString::End(aOffset); + } +#endif Iterator Begin( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, aOffset); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - using basic_nsAReadableString::End; Iterator End( PRUint32 aOffset = 0 ) { - Fragment fragment(this); + Fragment fragment; CharT* startPos = GetFragment(fragment, kFragmentAt, max(0U, Length()-aOffset)); - return Iterator(fragment, startPos); + return Iterator(fragment, startPos, *this); } - // virtual void Splice( ... ); + virtual void Splice(); virtual void SetCapacity( PRUint32 ) = 0; virtual void SetLength( PRUint32 ) = 0; @@ -195,8 +219,8 @@ class basic_nsAWritableString // virtual PRBool SetCharAt( char_type, index_type ) = 0; - void ToLowerCase(); - void ToUpperCase(); + // void ToLowerCase(); + // void ToUpperCase(); // void StripChars( const CharT* aSet ); // void StripChar( ... ); @@ -246,9 +270,16 @@ class basic_nsAWritableString NS_DEF_STRING_COMPARISONS(basic_nsAWritableString) +template +void +basic_nsAWritableString::Splice() + { + } + template void basic_nsAWritableString::Assign( const basic_nsAReadableString& rhs ) + // Default implementation. Derived classes may be able to do something smarter... { SetLength(rhs.Length()); std::copy(rhs.Begin(), rhs.End(), Begin());