/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Original Author: * Rick Gessner * * Contributor(s): * Scott Collins */ #include #include #include #include "nsString.h" #include "nsDebug.h" #include "nsDeque.h" #ifndef RICKG_TESTBED #include "prdtoa.h" #include "nsISizeOfHandler.h" #endif static const char* kPossibleNull = "Error: possible unintended null in string"; static const char* kNullPointerError = "Error: unexpected null ptr"; static const char* kWhitespace="\b\t\r\n "; static void Subsume(nsStr& aDest,nsStr& aSource){ if(aSource.mStr && aSource.mLength) { if(aSource.mOwnsBuffer){ nsStr::Destroy(aDest); aDest.mStr=aSource.mStr; aDest.mLength=aSource.mLength; aDest.mCharSize=aSource.mCharSize; aDest.mCapacity=aSource.mCapacity; aDest.mOwnsBuffer=aSource.mOwnsBuffer; aSource.mOwnsBuffer=PR_FALSE; aSource.mStr=0; } else{ nsStr::StrAssign(aDest,aSource,0,aSource.mLength); } } else nsStr::Truncate(aDest,0); } /** * Default constructor. */ nsString::nsString() { Initialize(*this,eTwoByte); } #if 0 /** * This constructor accepts an ascii string * @update gess 1/4/99 * @param aCString is a ptr to a 1-byte cstr * @param aLength tells us how many chars to copy from given CString */ nsString::nsString(const char* aCString,PRInt32 aCount){ Initialize(*this,eTwoByte); AssignWithConversion(aCString,aCount); } #endif #ifdef NEW_STRING_APIS nsString::nsString(const PRUnichar* aString) { Initialize(*this,eTwoByte); Assign(aString); } #endif /** * This constructor accepts a unicode string * @update gess 1/4/99 * @param aString is a ptr to a unichar string * @param aLength tells us how many chars to copy from given aString */ nsString::nsString(const PRUnichar* aString,PRInt32 aCount) { Initialize(*this,eTwoByte); Assign(aString,aCount); } #if 0 /** * This constructor works for all other nsSTr derivatives * @update gess 1/4/99 * @param reference to another nsCString */ nsString::nsString(const nsStr &aString) { Initialize(*this,eTwoByte); StrAssign(*this,aString,0,aString.mLength); } #endif /** * This is our copy constructor * @update gess 1/4/99 * @param reference to another nsString */ nsString::nsString(const nsString& aString) { Initialize(*this,eTwoByte); StrAssign(*this,aString,0,aString.mLength); } /** * construct from subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ #if defined(AIX) || defined(XP_OS2_VACPP) nsString::nsString(const nsSubsumeStr& aSubsumeStr) { Initialize(*this,eTwoByte); nsSubsumeStr temp(aSubsumeStr); // a temp is needed for the AIX and VAC++ compilers Subsume(*this,temp); #else nsString::nsString(nsSubsumeStr& aSubsumeStr) { Initialize(*this,eTwoByte); Subsume(*this,aSubsumeStr); #endif /* AIX || XP_OS2_VACPP */ } /** * Destructor * Make sure we call nsStr::Destroy. */ nsString::~nsString() { nsStr::Destroy(*this); } #ifdef NEW_STRING_APIS const PRUnichar* nsString::GetReadableFragment( nsReadableFragment& aFragment, nsFragmentRequest aRequest, PRUint32 aOffset ) const { switch ( aRequest ) { case kFirstFragment: case kLastFragment: case kFragmentAt: aFragment.mEnd = (aFragment.mStart = mUStr) + mLength; return aFragment.mStart + aOffset; case kPrevFragment: case kNextFragment: default: return 0; } } PRUnichar* nsString::GetWritableFragment( nsWritableFragment& aFragment, nsFragmentRequest aRequest, PRUint32 aOffset ) { switch ( aRequest ) { case kFirstFragment: case kLastFragment: case kFragmentAt: aFragment.mEnd = (aFragment.mStart = mUStr) + mLength; return aFragment.mStart + aOffset; case kPrevFragment: case kNextFragment: default: return 0; } } nsString::nsString( const nsAReadableString& aReadable ) { Initialize(*this,eTwoByte); Assign(aReadable); } #endif void nsString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } } /** * This method truncates this string to given length. * * @update gess 01/04/99 * @param anIndex -- new length of string * @return nada */ void nsString::SetLength(PRUint32 anIndex) { if ( anIndex > mCapacity ) SetCapacity(anIndex); nsStr::Truncate(*this,anIndex); } /** * Call this method if you want to force the string to a certain capacity * @update gess 1/4/99 * @param aLength -- contains new length for mStr * @return */ void nsString::SetCapacity(PRUint32 aLength) { if(aLength) { if(aLength>mCapacity) { GrowCapacity(*this,aLength); } AddNullTerminator(*this); } } /********************************************************************** Accessor methods... *********************************************************************/ //static char gChar=0; /** * * @update gess1/4/99 * @return ptr to internal buffer (if 1-byte), otherwise NULL */ const char* nsString::GetBuffer(void) const { const char* result=(eOneByte==mCharSize) ? mStr : 0; return result; } /** * This method returns the internal unicode buffer. * Now that we've factored the string class, this should never * be able to return a 1 byte string. * * @update gess1/4/99 * @return ptr to internal (2-byte) buffer; */ const PRUnichar* nsString::GetUnicode(void) const { const PRUnichar* result=(eOneByte==mCharSize) ? 0 : mUStr; return result; } #ifndef NEW_STRING_APIS /** * Get nth character. */ PRUnichar nsString::operator[](PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } /** * Get nth character. */ PRUnichar nsString::CharAt(PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } /** * Get 1st character. */ PRUnichar nsString::First(void) const{ return GetCharAt(*this,0); } /** * Get last character. */ PRUnichar nsString::Last(void) const{ return GetCharAt(*this,mLength-1); } #endif // !defined(NEW_STRING_APIS) /** * set a char inside this string at given index * @param aChar is the char you want to write into this string * @param anIndex is the ofs where you want to write the given char * @return TRUE if successful */ PRBool nsString::SetCharAt(PRUnichar aChar,PRUint32 anIndex){ PRBool result=PR_FALSE; if(anIndex2)) { theFirstChar=First(); theLastChar=Last(); if(theFirstChar==theLastChar) { if(('\''==theFirstChar) || ('"'==theFirstChar)) { Cut(0,1); Truncate(mLength-1); theQuotesAreNeeded=PR_TRUE; } else theFirstChar=0; } } nsStr::Trim(*this,aTrimSet,aEliminateLeading,aEliminateTrailing); if(aIgnoreQuotes && theQuotesAreNeeded) { Insert(theFirstChar,0); Append(theLastChar); } } } /** * This method strips chars in given set from string. * You can control whether chars are yanked from * start and end of string as well. * * @update gess 3/31/98 * @param aEliminateLeading controls stripping of leading ws * @param aEliminateTrailing controls stripping of trailing ws * @return this */ void nsString::CompressSet(const char* aSet, PRUnichar aChar,PRBool aEliminateLeading,PRBool aEliminateTrailing){ if(aSet){ ReplaceChar(aSet,aChar); nsStr::CompressSet(*this,aSet,aEliminateLeading,aEliminateTrailing); } } /** * This method strips whitespace from string. * You can control whether whitespace is yanked from * start and end of string as well. * * @update gess 3/31/98 * @param aEliminateLeading controls stripping of leading ws * @param aEliminateTrailing controls stripping of trailing ws * @return this */ void nsString::CompressWhitespace( PRBool aEliminateLeading,PRBool aEliminateTrailing){ CompressSet(kWhitespace,' ',aEliminateLeading,aEliminateTrailing); } /********************************************************************** string conversion methods... *********************************************************************/ /** * Creates a duplicate clone (ptr) of this string. * @update gess 01/04/99 * @return ptr to clone of this string */ nsString* nsString::ToNewString() const { return new nsString(*this); } /** * Creates an ascii clone of this string * Note that calls to this method should be matched with calls to Recycle(). * @update gess 02/24/00 * @WARNING! Potential i18n issue here, since we're stepping down from 2byte chars to 1byte chars! * @return ptr to new ascii string */ char* nsString::ToNewCString() const { char* result = NS_STATIC_CAST(char*, nsMemory::Alloc(mLength + 1)); if (result) { CBufDescriptor desc(result, PR_TRUE, mLength + 1, 0); nsCAutoString temp(desc); temp.AssignWithConversion(*this); } return result; } /** * Creates an UTF8 clone of this string * Note that calls to this method should be matched with calls to Recycle(). * @update ftang 09/10/99 * @return ptr to new UTF8 string * http://www.cis.ohio-state.edu/htbin/rfc/rfc2279.html */ char* nsString::ToNewUTF8String() const { NS_ConvertUCS2toUTF8 temp(mUStr); char* result; if (temp.mOwnsBuffer) { // We allocated. Trick the string into not freeing its buffer to // avoid an extra allocation. result = temp.mStr; temp.mStr=0; temp.mOwnsBuffer = PR_FALSE; } else { // We didn't allocate a buffer, so we need to copy it out of the // nsCAutoString's storage. result = nsCRT::strdup(temp.mStr); } return result; } /** * Creates an ascii clone of this string * Note that calls to this method should be matched with calls to Recycle(). * @update gess 02/24/00 * @return ptr to new ascii string */ PRUnichar* nsString::ToNewUnicode() const { return nsCRT::strdup(mUStr); } /** * Copies contents of this string into he given buffer * Note that if you provide me a buffer that is smaller than the length of * this string, only the number of bytes that will fit are copied. * * @update gess 01/04/99 * @param aBuf * @param aBufLength -- size of your external buffer (including null) * @param anOffset -- THIS IS NOT USED AT THIS TIME! * @return */ char* nsString::ToCString(char* aBuf, PRUint32 aBufLength,PRUint32 anOffset) const{ if(aBuf) { // NS_ASSERTION(mLength<=aBufLength,"buffer smaller than string"); CBufDescriptor theDescr(aBuf,PR_TRUE,aBufLength,0); nsCAutoString temp(theDescr); nsStr::StrAssign(temp, *this, anOffset, aBufLength-1); temp.mStr=0; } return aBuf; } /** * Perform string to float conversion. * @update gess 01/04/99 * @param aErrorCode will contain error if one occurs * @return float rep of string value */ float nsString::ToFloat(PRInt32* aErrorCode) const { char buf[100]; if (mLength > PRInt32(sizeof(buf)-1)) { *aErrorCode = (PRInt32) NS_ERROR_ILLEGAL_VALUE; return 0.0f; } char* cp = ToCString(buf, sizeof(buf)); float f = (float) PR_strtod(cp, &cp); if (*cp != 0) { *aErrorCode = (PRInt32) NS_ERROR_ILLEGAL_VALUE; } *aErrorCode = (PRInt32) NS_OK; return f; } /** * Perform decimal numeric string to int conversion. * NOTE: In this version, we use the radix you give, even if it's wrong. * @update gess 02/14/00 * @param aErrorCode will contain error if one occurs * @param aRadix tells us what base to expect the given string in. kAutoDetect tells us to determine the radix. * @return int rep of string value */ PRInt32 nsString::ToInteger(PRInt32* anErrorCode,PRUint32 aRadix) const { PRUnichar* cp=mUStr; PRInt32 theRadix = (kAutoDetect==aRadix) ? 10 : aRadix; PRInt32 result=0; PRBool negate=PR_FALSE; PRUnichar theChar=0; *anErrorCode=NS_ERROR_ILLEGAL_VALUE; if(cp) { //begin by skipping over leading chars that shouldn't be part of the number... PRUnichar* endcp=cp+mLength; PRBool done=PR_FALSE; while((cp='A') && (theChar<='F')) { if(10==theRadix) { if(kAutoDetect==aRadix){ theRadix=16; cp=first; //backup result=0; } else { *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } } else { result = (theRadix * result) + ((theChar-'A')+10); } } else if((theChar>='a') && (theChar<='f')) { if(10==theRadix) { if(kAutoDetect==aRadix){ theRadix=16; cp=first; //backup result=0; } else { *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } } else { result = (theRadix * result) + ((theChar-'a')+10); } } else if(('X'==theChar) || ('x'==theChar) || ('#'==theChar) || ('+'==theChar)) { continue; } else { //we've encountered a char that's not a legal number or sign break; } } //while if(negate) result=-result; } //if } return result; } /********************************************************************** String manipulation methods... *********************************************************************/ #ifndef NEW_STRING_APIS /** * assign given nsStr (or derivative) to this one * @update gess 01/04/99 * @param aString: nsStr to be appended * @return this */ nsString& nsString::Assign(const nsStr& aString,PRInt32 aCount) { if(this!=&aString){ nsStr::Truncate(*this,0); if(aCount<0) aCount=aString.mLength; else aCount=MinInt(aCount,aString.mLength); StrAssign(*this,aString,0,aCount); } return *this; } #endif /** * assign given char* to this string * @update gess 01/04/99 * @param aCString: buffer to be assigned to this * @param aCount -- length of given buffer or -1 if you want me to compute length. * NOTE: IFF you pass -1 as aCount, then your buffer must be null terminated. * * @return this */ void nsString::AssignWithConversion(const char* aCString,PRInt32 aCount) { nsStr::Truncate(*this,0); if(aCString){ AppendWithConversion(aCString,aCount); } } #ifdef NEW_STRING_APIS void nsString::AssignWithConversion(const char* aCString) { nsStr::Truncate(*this,0); if(aCString){ AppendWithConversion(aCString); } } #endif #ifndef NEW_STRING_APIS /** * assign given unichar* to this string * @update gess 01/04/99 * @param aString: buffer to be assigned to this * @param aCount -- length of given buffer or -1 if you want me to compute length. * NOTE: IFF you pass -1 as aCount, then your buffer must be null terminated. * * @return this */ nsString& nsString::Assign(const PRUnichar* aString,PRInt32 aCount) { nsStr::Truncate(*this,0); if(aString){ Append(aString,aCount); } return *this; } #endif /** * assign given char to this string * @update gess 01/04/99 * @param aChar: char to be assignd to this * @return this */ void nsString::AssignWithConversion(char aChar) { nsStr::Truncate(*this,0); AppendWithConversion(aChar); } #ifndef NEW_STRING_APIS /** * assign given unichar to this string * @update gess 01/04/99 * @param aChar: char to be assignd to this * @return this */ nsString& nsString::Assign(PRUnichar aChar) { nsStr::Truncate(*this,0); return Append(aChar); } #endif #ifndef NEW_STRING_APIS /** * WARNING! THIS IS A VERY SPECIAL METHOD. * This method "steals" the contents of aSource and hands it to aDest. * Ordinarily a copy is made, but not in this version. * @update gess10/30/98 * @param * @return */ #if defined(AIX) || defined(XP_OS2_VACPP) nsString& nsString::operator=(const nsSubsumeStr& aSubsumeString) { nsSubsumeStr temp(aSubsumeString); // a temp is needed for the AIX and VAC++ compilers Subsume(*this,temp); #else nsString& nsString::operator=(nsSubsumeStr& aSubsumeString) { Subsume(*this,aSubsumeString); #endif // AIX || XP_OS2_VACPP return *this; } #endif /** * append given c-string to this string * @update gess 01/04/99 * @param aString : string to be appended to this * @param aCount -- length of given buffer or -1 if you want me to compute length. * NOTE: IFF you pass -1 as aCount, then your buffer must be null terminated. * * @return this */ void nsString::AppendWithConversion(const char* aCString,PRInt32 aCount) { if(aCString && aCount){ //if astring is null or count==0 there's nothing to do nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mStr=(char*)aCString; if(0=1) { PRInt32 theDiv=theInt/mask1; if((theDiv) || (!isfirst)) { buf[charpos++]="0123456789abcdef"[theDiv]; isfirst=PR_FALSE; } theInt-=theDiv*mask1; mask1/=aRadix; } AppendWithConversion(buf); } /** * Append the given float to this string * @update gess 01/04/99 * @param aFloat: * @return */ void nsString::AppendFloat(double aFloat){ char buf[40]; // *** XX UNCOMMENT THIS LINE //PR_snprintf(buf, sizeof(buf), "%g", aFloat); sprintf(buf,"%g",aFloat); AppendWithConversion(buf); } #ifndef NEW_STRING_APIS /** * append given string to this string; * @update gess 01/04/99 * @param aString : string to be appended to this * @return this */ #if 0 nsString& nsString::Append(const nsStr& aString,PRInt32 aCount) { if(aCount<0) aCount=aString.mLength; else aCount=MinInt(aCount,aString.mLength); if(0(const nsString& S) const {return PRBool(Compare(S)>0);} //PRBool nsString::operator>(const nsStr& S) const {return PRBool(Compare(S)>0);} //PRBool nsString::operator>(const char* s) const {return PRBool(Compare(s)>0);} PRBool nsString::operator>(const PRUnichar* s) const {return PRBool(Compare(nsAutoString(s))>0);} PRBool nsString::operator<=(const nsString& S) const {return PRBool(Compare(S)<=0);} //PRBool nsString::operator<=(const nsStr& S) const {return PRBool(Compare(S)<=0);} //PRBool nsString::operator<=(const char* s) const {return PRBool(Compare(s)<=0);} PRBool nsString::operator<=(const PRUnichar* s) const {return PRBool(Compare(nsAutoString(s))<=0);} PRBool nsString::operator>=(const nsString& S) const {return PRBool(Compare(S)>=0);} //PRBool nsString::operator>=(const nsStr& S) const {return PRBool(Compare(S)>=0);} //PRBool nsString::operator>=(const char* s) const {return PRBool(Compare(s)>=0);} PRBool nsString::operator>=(const PRUnichar* s) const {return PRBool(Compare(nsAutoString(s))>=0);} #endif // !defined(NEW_STRING_APIS) PRBool nsString::EqualsIgnoreCase(const nsString& aString) const { return EqualsWithConversion(aString,PR_TRUE); } PRBool nsString::EqualsIgnoreCase(const char* aString,PRInt32 aLength) const { return EqualsWithConversion(aString,PR_TRUE,aLength); } #ifndef NEW_STRING_APIS PRBool nsString::EqualsIgnoreCase(const PRUnichar* s1, const PRUnichar* s2) const { return Equals(s1,s2,PR_TRUE); } #endif /** * Compare this to given string; note that we compare full strings here. * * @update gess 01/04/99 * @param aString is the other nsString to be compared to * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsString::EqualsWithConversion(const nsString& aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=nsStr::StrCompare(*this,aString,aCount,aIgnoreCase); PRBool result=PRBool(0==theAnswer); return result; } /** * Compare this to given c-string; note that we compare full strings here. * * @param aString is the CString to be compared * @param aIgnorecase tells us whether to be case sensitive * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsString::EqualsWithConversion(const char* aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=CompareWithConversion(aString,aIgnoreCase,aCount); PRBool result=PRBool(0==theAnswer); return result; } /** * Compare this to given unicode string; note that we compare full strings here. * * @param aString is the U-String to be compared * @param aIgnorecase tells us whether to be case sensitive * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsString::EqualsWithConversion(const PRUnichar* aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=CompareWithConversion(aString,aIgnoreCase,aCount); PRBool result=PRBool(0==theAnswer); return result; } /** * Compare this to given atom; note that we compare full strings here. * The optional length argument just lets us know how long the given string is. * If you provide a length, it is compared to length of this string as an * optimization. * * @update gess 01/04/99 * @param aString -- unistring to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString::EqualsAtom(/*FIX: const */nsIAtom* aAtom,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aAtom,kNullPointerError); PRBool result=PR_FALSE; if(aAtom){ PRInt32 cmp=0; const PRUnichar* unicode; if (aAtom->GetUnicode(&unicode) != NS_OK || unicode == nsnull) return PR_FALSE; if (aIgnoreCase) cmp=nsCRT::strcasecmp(mUStr,unicode); else cmp=nsCRT::strcmp(mUStr,unicode); result=PRBool(0==cmp); } return result; } PRBool nsString::EqualsIgnoreCase(/*FIX: const */nsIAtom *aAtom) const { return EqualsAtom(aAtom,PR_TRUE); } #ifndef NEW_STRING_APIS /** * Compare this to given string; note that we compare full strings here. * * @update gess 01/04/99 * @param aString is the other nsString to be compared to * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsString::Equals(const nsStr& aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=nsStr::StrCompare(*this,aString,aCount,aIgnoreCase); PRBool result=PRBool(0==theAnswer); return result; } /** * Compare given strings * @update gess 7/27/98 * @param s1 -- first unichar string to be compared * @param s2 -- second unichar string to be compared * @return TRUE if equal */ PRBool nsString::Equals(const PRUnichar* s1, const PRUnichar* s2,PRBool aIgnoreCase) const { NS_ASSERTION(0!=s1,kNullPointerError); NS_ASSERTION(0!=s2,kNullPointerError); PRBool result=PR_FALSE; if((s1) && (s2)){ PRInt32 cmp=(aIgnoreCase) ? nsCRT::strcasecmp(s1,s2) : nsCRT::strcmp(s1,s2); result=PRBool(0==cmp); } return result; } #endif /** * Determine if given char in valid alpha range * * @update gess 3/31/98 * @param aChar is character to be tested * @return TRUE if in alpha range */ PRBool nsString::IsAlpha(PRUnichar aChar) { // XXX i18n if (((aChar >= 'A') && (aChar <= 'Z')) || ((aChar >= 'a') && (aChar <= 'z'))) { return PR_TRUE; } return PR_FALSE; } /** * Determine if given char is a valid space character * * @update gess 3/31/98 * @param aChar is character to be tested * @return TRUE if is valid space char */ PRBool nsString::IsSpace(PRUnichar aChar) { // XXX i18n if ((aChar == ' ') || (aChar == '\r') || (aChar == '\n') || (aChar == '\t')) { return PR_TRUE; } return PR_FALSE; } /** * Determine if given buffer contains plain ascii * * @param aBuffer -- if null, then we test *this, otherwise we test given buffer * @return TRUE if is all ascii chars, or if strlen==0 */ PRBool nsString::IsASCII(const PRUnichar* aBuffer) { if(!aBuffer) { if(eOneByte==mCharSize) { char* aByte = mStr; while(*aByte) { if(*aByte & 0x80) { // don't use (*aByte > 0x7F) since char is signed return PR_FALSE; } aByte++; } return PR_TRUE; } else { aBuffer=mUStr; // let the following code handle it } } if(aBuffer) { while(*aBuffer) { if(*aBuffer>0x007F){ return PR_FALSE; } aBuffer++; } } return PR_TRUE; } /** * Determine if given char is valid digit * * @update gess 3/31/98 * @param aChar is character to be tested * @return TRUE if char is a valid digit */ PRBool nsString::IsDigit(PRUnichar aChar) { // XXX i18n return PRBool((aChar >= '0') && (aChar <= '9')); } #ifndef RICKG_TESTBED /************************************************************** Define the string deallocator class... **************************************************************/ class nsStringDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* anObject) { nsString* aString= (nsString*)anObject; if(aString){ delete aString; } return 0; } }; /**************************************************************************** * This class, appropriately enough, creates and recycles nsString objects.. ****************************************************************************/ class nsStringRecycler { public: nsStringRecycler() : mDeque(0) { } ~nsStringRecycler() { nsStringDeallocator theDeallocator; mDeque.ForEach(theDeallocator); //now delete the strings } void Recycle(nsString* aString) { mDeque.Push(aString); } nsString* CreateString(void){ nsString* result=(nsString*)mDeque.Pop(); if(!result) result=new nsString(); return result; } nsDeque mDeque; }; static nsStringRecycler& GetRecycler(void); /** * * @update gess 01/04/99 * @param * @return */ nsStringRecycler& GetRecycler(void){ static nsStringRecycler gRecycler; return gRecycler; } #endif /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ nsString* nsString::CreateString(void){ nsString* result=0; #ifndef RICKG_TESTBED GetRecycler().CreateString(); #endif return result; } /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ void nsString::Recycle(nsString* aString){ #ifndef RICKG_TESTBED GetRecycler().Recycle(aString); #else delete aString; #endif } #if 0 /** * * @update gess8/8/98 * @param * @return */ ostream& operator<<(ostream& aStream,const nsString& aString){ if(eOneByte==aString.mCharSize) { aStream<= PRInt32(sizeof(buf))) { cp = aString.ToNewCString(); } else { aString.ToCString(cp, len + 1); } if(len>0) ::fwrite(cp, 1, len, out); if (cp != buf) { Recycle(cp); } return (int) len; } /** * Dumps the contents of the string to stdout * @update gess 11/15/99 */ void nsString::DebugDump(void) const { const char* theBuffer=mStr; nsCAutoString temp; if(eTwoByte==mCharSize) { nsStr::StrAssign(temp, *this, 0, mLength); theBuffer=temp.GetBuffer(); } if(theBuffer) { printf("\n%s",theBuffer); } } /*********************************************************************** IMPLEMENTATION NOTES: AUTOSTRING... ***********************************************************************/ /** * Default constructor * */ nsAutoString::nsAutoString() : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); } nsAutoString::nsAutoString(const PRUnichar* aString) : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * Copy construct from uni-string * @param aString is a ptr to a unistr * @param aLength tells us how many chars to copy from aString */ nsAutoString::nsAutoString(const PRUnichar* aString,PRInt32 aLength) : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aString,aLength); } nsAutoString::nsAutoString( const nsString& aString ) : nsString() { Initialize(*this, mBuffer, (sizeof(mBuffer)>>eTwoByte)-1, 0, eTwoByte, PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * constructor that uses external buffer * @param aBuffer describes the external buffer */ nsAutoString::nsAutoString(const CBufDescriptor& aBuffer) : nsString() { if(!aBuffer.mBuffer) { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); } else { Initialize(*this,aBuffer.mBuffer,aBuffer.mCapacity,aBuffer.mLength,aBuffer.mCharSize,!aBuffer.mStackBased); } if(!aBuffer.mIsConst) AddNullTerminator(*this); } NS_ConvertASCIItoUCS2::NS_ConvertASCIItoUCS2( const char* aCString, PRUint32 aLength ) { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); AppendWithConversion(aCString,aLength); } NS_ConvertASCIItoUCS2::NS_ConvertASCIItoUCS2( const char* aCString ) { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); AppendWithConversion(aCString); } NS_ConvertASCIItoUCS2::NS_ConvertASCIItoUCS2( char aChar ) { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); AppendWithConversion(aChar); } #if 0 #ifdef NEW_STRING_APIS NS_ConvertASCIItoUCS2::NS_ConvertASCIItoUCS2( const nsAReadableCString& ) { // ... } #else NS_ConvertASCIItoUCS2::NS_ConvertASCIItoUCS2( const nsCString& ) { // ... } #endif #endif void NS_ConvertUTF8toUCS2::Init( const char* aCString, PRUint32 aLength ) { // Handle null string by just leaving us as a brand-new // uninitialized nsAutoString. if (! aCString) return; // Compute space required: do this once so we don't incur multiple // allocations. This "optimization" is probably of dubious value... const char* p; PRUint32 count; for (p = aCString, count = 0; *p && count < aLength; ++count) { if ( 0 == (*p & 0x80) ) p += 1; // ASCII else if ( 0xC0 == (*p & 0xE0) ) p += 2; // 2 byte UTF8 else if ( 0xE0 == (*p & 0xF0) ) p += 3; // 3 byte UTF8 else if ( 0xF0 == (*p & 0xF8) ) p += 4; // 4 byte UTF8 else if ( 0xF8 == (*p & 0xFC) ) p += 5; // 5 byte UTF8 else if ( 0xFC == (*p & 0xFE) ) p += 6; else { NS_ERROR("not a UTF-8 string"); return; } } // Grow the buffer if we need to. if ((count * sizeof(PRUnichar)) >= sizeof(mBuffer)) SetCapacity(count + 1); // We'll write directly into the new string's buffer PRUnichar* out = mUStr; // Convert the characters. for (p = aCString, count = 0; *p && count < aLength; ++count) { char c = *p++; if( 0 == (0x80 & c)) { // ASCII *out++ = PRUnichar(c); continue; } PRUint32 ucs4; PRInt32 state = 0; if ( 0xC0 == (0xE0 & c) ) { // 2 bytes UTF8 ucs4 = (PRUint32(c) << 6) & 0x000007C0L; state = 1; } else if ( 0xE0 == (0xF0 & c) ) { // 3 bytes UTF8 ucs4 = (PRUint32(c) << 12) & 0x0000F000L; state = 2; } else if ( 0xF0 == (0xF8 & c) ) { // 4 bytes UTF8 ucs4 = (PRUint32(c) << 18) & 0x001F0000L; state = 3; } else if ( 0xF8 == (0xFC & c) ) { // 5 bytes UTF8 ucs4 = (PRUint32(c) << 24) & 0x03000000L; state = 4; } else if ( 0xFC == (0xFE & c) ) { // 6 bytes UTF8 ucs4 = (PRUint32(c) << 30) & 0x40000000L; state = 5; } else { NS_ERROR("not a UTF8 string"); break; } while (state--) { c = *p++; if ( 0x80 == (0xC0 & c) ) { PRInt32 shift = state * 6; ucs4 |= (PRUint32(c) & 0x3F) << shift; } else { NS_ERROR("not a UTF8 string"); goto done; // so we minimally clean up } } if (ucs4 >= 0x00010000) { if (ucs4 >= 0x001F0000) { *out++ = 0xFFFD; } else { ucs4 -= 0x00010000; *out++ = 0xD800 | (0x000003FF & (ucs4 >> 10)); *out++ = 0xDC00 | (0x000003FF & ucs4); } } else { if (0xfeff != ucs4) // ignore BOM *out++ = ucs4; } } done: *out = '\0'; // null terminate mLength = count; } #if 0 /** * Copy construct from ascii c-string * @param aCString is a ptr to a 1-byte cstr * @param aLength tells us how many chars to copy from aCString */ nsAutoString::nsAutoString(const char* aCString,PRInt32 aLength) : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aCString,aLength); } /** * Copy construct from an nsStr * @param */ nsAutoString::nsAutoString(const nsStr& aString) : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } #endif /** * Default copy constructor */ nsAutoString::nsAutoString(const nsAutoString& aString) : nsString() { Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * Copy construct from a unichar * @param */ nsAutoString::nsAutoString(PRUnichar aChar) : nsString(){ Initialize(*this,mBuffer,(sizeof(mBuffer)>>eTwoByte)-1,0,eTwoByte,PR_FALSE); AddNullTerminator(*this); Append(aChar); } /** * construct from a subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ #if defined(AIX) || defined(XP_OS2_VACPP) nsAutoString::nsAutoString(const nsSubsumeStr& aSubsumeStr) :nsString() { nsSubsumeStr temp(aSubsumeStr); // a temp is needed for the AIX and VAC++ compilers Subsume(*this,temp); #else nsAutoString::nsAutoString( nsSubsumeStr& aSubsumeStr) :nsString() { Subsume(*this,aSubsumeStr); #endif // AIX || XP_OS2_VACPP } /** * deconstruct the autstring * @param */ nsAutoString::~nsAutoString(){ } void nsAutoString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } } nsSubsumeStr::nsSubsumeStr() : nsString() { } nsSubsumeStr::nsSubsumeStr(nsStr& aString) : nsString() { ::Subsume(*this,aString); } nsSubsumeStr::nsSubsumeStr(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength) : nsString() { mUStr=aString; mCharSize=eTwoByte; mCapacity=mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength; mOwnsBuffer=assumeOwnership; } nsSubsumeStr::nsSubsumeStr(char* aString,PRBool assumeOwnership,PRInt32 aLength) : nsString() { mStr=aString; mCharSize=eOneByte; mCapacity=mLength=(-1==aLength) ? strlen(aString) : aLength; mOwnsBuffer=assumeOwnership; } int nsSubsumeStr::Subsume(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength) { mUStr=aString; mCharSize=eTwoByte; mCapacity=mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength; mOwnsBuffer=assumeOwnership; return 0; }