/* -*- 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.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include #include #include #include #include "nsString.h" #include "nsCRT.h" #include "nsDebug.h" #include "prprf.h" #include "prdtoa.h" #include "nsISizeOfHandler.h" #include "nsStr.h" #include "nsUnicharUtilCIID.h" #include "nsIServiceManager.h" #include "nsICaseConversion.h" const PRInt32 kGrowthDelta = 8; PRUnichar gBadChar = 0; const char* kOutOfBoundsError = "Error: out of bounds"; const char* kNullPointerError = "Error: unexpected null ptr"; const char* kFoolMsg = "Error: Some fool overwrote the shared buffer."; PRUnichar kCommonEmptyBuffer[100]; //shared by all strings; NEVER WRITE HERE!!! #ifdef RICKG_DEBUG PRBool nsString1::mSelfTested = PR_FALSE; #endif #define NOT_USED 0xfffd static PRUint16 PA_HackTable[] = { NOT_USED, NOT_USED, 0x201a, /* SINGLE LOW-9 QUOTATION MARK */ 0x0192, /* LATIN SMALL LETTER F WITH HOOK */ 0x201e, /* DOUBLE LOW-9 QUOTATION MARK */ 0x2026, /* HORIZONTAL ELLIPSIS */ 0x2020, /* DAGGER */ 0x2021, /* DOUBLE DAGGER */ 0x02c6, /* MODIFIER LETTER CIRCUMFLEX ACCENT */ 0x2030, /* PER MILLE SIGN */ 0x0160, /* LATIN CAPITAL LETTER S WITH CARON */ 0x2039, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ 0x0152, /* LATIN CAPITAL LIGATURE OE */ NOT_USED, NOT_USED, NOT_USED, NOT_USED, 0x2018, /* LEFT SINGLE QUOTATION MARK */ 0x2019, /* RIGHT SINGLE QUOTATION MARK */ 0x201c, /* LEFT DOUBLE QUOTATION MARK */ 0x201d, /* RIGHT DOUBLE QUOTATION MARK */ 0x2022, /* BULLET */ 0x2013, /* EN DASH */ 0x2014, /* EM DASH */ 0x02dc, /* SMALL TILDE */ 0x2122, /* TRADE MARK SIGN */ 0x0161, /* LATIN SMALL LETTER S WITH CARON */ 0x203a, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ 0x0153, /* LATIN SMALL LIGATURE OE */ NOT_USED, NOT_USED, 0x0178 /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ }; static PRUnichar gToUCS2[256]; class CTableConstructor { public: CTableConstructor(){ PRUnichar* cp = gToUCS2; PRInt32 i; for (i = 0; i < 256; i++) { *cp++ = PRUnichar(i); } cp = gToUCS2; for (i = 0; i < 32; i++) { cp[0x80 + i] = PA_HackTable[i]; } } }; static CTableConstructor gTableConstructor; //---- XPCOM code to connect with UnicharUtil class HandleCaseConversionShutdown2 : public nsIShutdownListener { public : NS_IMETHOD OnShutdown(const nsCID& cid, nsISupports* service); HandleCaseConversionShutdown2(void) { NS_INIT_REFCNT(); } virtual ~HandleCaseConversionShutdown2(void) {} NS_DECL_ISUPPORTS }; static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID); static NS_DEFINE_IID(kICaseConversionIID, NS_ICASECONVERSION_IID); static nsICaseConversion * gCaseConv = NULL; static NS_DEFINE_IID(kIShutdownListenerIID, NS_ISHUTDOWNLISTENER_IID); NS_IMPL_ISUPPORTS(HandleCaseConversionShutdown2, kIShutdownListenerIID); nsresult HandleCaseConversionShutdown2::OnShutdown(const nsCID& cid, nsISupports* service) { if (cid.Equals(kUnicharUtilCID)) { NS_ASSERTION(service == gCaseConv, "wrong service!"); gCaseConv->Release(); gCaseConv = NULL; } return NS_OK; } static HandleCaseConversionShutdown2* gListener = NULL; static void StartUpCaseConversion() { nsresult err; if ( NULL == gListener ) { gListener = new HandleCaseConversionShutdown2(); gListener->AddRef(); } err = nsServiceManager::GetService(kUnicharUtilCID, kICaseConversionIID, (nsISupports**) &gCaseConv, gListener); } static void CheckCaseConversion() { if(NULL == gCaseConv ) StartUpCaseConversion(); // NS_ASSERTION( gCaseConv != NULL , "cannot obtain UnicharUtil"); } /*********************************************************************** IMPLEMENTATION NOTES: Man I hate writing string classes. You'd think after about a qintrillion lines of code have been written, that no poor soul would ever have to do this again. Sigh. ***********************************************************************/ /** * Default constructor. Note that we actually allocate a small buffer * to begin with. This is because the "philosophy" of the string class * was to allow developers direct access to the underlying buffer for * performance reasons. */ nsString1::nsString1() { NS_ASSERTION(kCommonEmptyBuffer[0]==0,kFoolMsg); mLength = mCapacity = 0; mStr = kCommonEmptyBuffer; #ifdef RICKG_DEBUG if(!mSelfTested) { mSelfTested=PR_TRUE; SelfTest(); } #endif } /** * This constructor accepts an isolatin string * @update gess7/30/98 * @param aCString is a ptr to a 1-byte cstr */ nsString1::nsString1(const char* aCString) { mLength=mCapacity=0; mStr = kCommonEmptyBuffer; if(aCString) { PRInt32 len=strlen(aCString); EnsureCapacityFor(len); this->SetString(aCString,len); } } /** * This is our copy constructor * @update gess7/30/98 * @param reference to another nsString1 */ nsString1::nsString1(const nsString1 &aString) { mLength=mCapacity=0; mStr = kCommonEmptyBuffer; if(aString.mLength) { EnsureCapacityFor(aString.mLength); this->SetString(aString.mStr,aString.mLength); } } /** * Constructor from a unicode string * @update gess7/30/98 * @param anicodestr pts to a unicode string */ nsString1::nsString1(const PRUnichar* aUnicodeStr) { mLength=mCapacity=0; mStr = kCommonEmptyBuffer; PRInt32 len=(aUnicodeStr) ? nsCRT::strlen(aUnicodeStr) : 0; if(len>0) { EnsureCapacityFor(len); this->SetString(aUnicodeStr,len); } } /** * Destructor * @update gess7/30/98 */ nsString1::~nsString1() { if(mStr && (mStr!=kCommonEmptyBuffer)) delete [] mStr; mStr=0; mCapacity=mLength=0; } /** * This method truncates this string to given length. * * @update gess 7/27/98 * @param anIndex -- new length of string * @return nada */ void nsString1::Truncate(PRInt32 anIndex) { if((anIndex>-1) && (anIndexAdd(sizeof(*this)); aHandler->Add(mCapacity * sizeof(chartype)); } /** * Determine whether or not the characters in this * string are in sorted order. * * @update gess 8/25/98 * @return TRUE if ordered. */ PRBool nsString1::IsOrdered(void) const { PRBool result=PR_TRUE; if(mLength>1) { PRInt32 theIndex; for(theIndex=1;theIndexmStr[theIndex]) { result=PR_FALSE; break; } } } return result; } /** * This method gets called when the internal buffer needs * to grow to a given size. * @update gess 3/30/98 * @param aNewLength -- new capacity of string * @return void */ void nsString1::EnsureCapacityFor(PRInt32 aNewLength) { PRInt32 newCapacity; if (mCapacity > 64) { // When the string starts getting large, double the capacity as we // grow. newCapacity = mCapacity * 2; if (newCapacity < aNewLength) { newCapacity = mCapacity + aNewLength; } } else { // When the string is small, keep it's capacity a multiple of // kGrowthDelta PRInt32 size =aNewLength+kGrowthDelta; newCapacity=size-(size % kGrowthDelta); } if(mCapacity 0) { nsCRT::memcpy(temp, mStr, mLength * sizeof(chartype) + sizeof(chartype)); } if(mStr && (mStr!=kCommonEmptyBuffer)) delete [] mStr; mStr = temp; mStr[mLength]=0; } } /** * Call this method if you want string to report a shorter length. * @update gess7/30/98 * @param aLength -- contains new length for mStr * @return */ void nsString1::SetLength(PRInt32 aLength) { if(aLength>mLength) { EnsureCapacityFor(aLength); nsCRT::zero(mStr + mLength, (aLength - mLength) * sizeof(chartype)); } if((aLength>=0) && (aLengthToLower(mStr, mStr, mLength); if( NS_SUCCEEDED(err)) return; } // I18N code end // somehow UnicharUtil return failed, fallback to the old ascii only code chartype* cp = mStr; chartype* end = cp + mLength; while (cp < end) { chartype ch = *cp; if ((ch >= 'A') && (ch <= 'Z')) { *cp = 'a' + (ch - 'A'); } cp++; } } /** * Converts all chars in internal string to upper * @update gess 7/27/98 */ void nsString1::ToUpperCase() { // I18N code begin CheckCaseConversion(); if(gCaseConv) { nsresult err = gCaseConv->ToUpper(mStr, mStr, mLength); if( NS_SUCCEEDED(err)) return; } // I18N code end // somehow UnicharUtil return failed, fallback to the old ascii only code chartype* cp = mStr; chartype* end = cp + mLength; while (cp < end) { chartype ch = *cp; if ((ch >= 'a') && (ch <= 'z')) { *cp = 'A' + (ch - 'a'); } cp++; } } /** * Converts all chars in given string to UCS2 */ void nsString1::ToUCS2(PRInt32 aStartOffset){ if(aStartOffsetToLower(mStr, aOut.mStr, mLength); (*(aOut.mStr+mLength)) = 0; if( NS_SUCCEEDED(err)) return; } // I18N code end // somehow UnicharUtil return failed, fallback to the old ascii only code chartype* to = aOut.mStr; chartype* from = mStr; chartype* end = from + mLength; while (from < end) { chartype ch = *from++; if ((ch >= 'A') && (ch <= 'Z')) { ch = 'a' + (ch - 'A'); } *to++ = ch; } *to = 0; } /** * Converts chars in this to lowercase, and * stores them in a given output string * @update gess 7/27/98 * @param aOut is a string to contain result */ void nsString1::ToUpperCase(nsString1& aOut) const { aOut.EnsureCapacityFor(mLength); aOut.mLength = mLength; // I18N code begin CheckCaseConversion(); if(gCaseConv) { nsresult err = gCaseConv->ToUpper(mStr, aOut.mStr, mLength); (*(aOut.mStr+mLength)) = 0; if( NS_SUCCEEDED(err)) return; } // I18N code end // somehow UnicharUtil return failed, fallback to the old ascii only code chartype* to = aOut.mStr; chartype* from = mStr; chartype* end = from + mLength; while (from < end) { chartype ch = *from++; if ((ch >= 'a') && (ch <= 'z')) { ch = 'A' + (ch - 'a'); } *to++ = ch; } *to = 0; } /** * Creates a duplicate clone (ptr) of this string. * @update gess 7/27/98 * @return ptr to clone of this string */ nsString1* nsString1::ToNewString() const { return new nsString1(mStr); } /** * Creates an aCString clone of this string * NOTE: Call delete[] when you're done with this copy. NOT free! * @update gess 7/27/98 * @return ptr to new aCString string */ char* nsString1::ToNewCString() const { char* rv = new char[mLength + 1]; return ToCString(rv,mLength+1); } /** * Creates an unichar clone of this string * @update gess 7/27/98 * @return ptr to new unichar string */ PRUnichar* nsString1::ToNewUnicode() const { PRInt32 len = mLength; chartype* rv = new chartype[len + 1]; chartype* to = rv; chartype* from = mStr; while (--len >= 0) { *to++ = *from++; } *to++ = 0; return rv; } /** * Copies contents of this onto given string. * @update gess 7/27/98 * @param aString to hold copy of this * @return nada. */ void nsString1::Copy(nsString1& aString) const { aString.mLength = 0; aString.Append(mStr, mLength); } /** * * @update gess 7/27/98 * @param * @return */ char* nsString1::ToCString(char* aBuf, PRInt32 aBufLength) const { aBufLength--; // leave room for the \0 PRInt32 len = (mLength > aBufLength) ? aBufLength : mLength; char* to = aBuf; chartype* from = mStr; while (--len >= 0) { *to++ = char(*from++); } *to++ = '\0'; return aBuf; } /** * Perform string to float conversion. * @update gess 7/27/98 * @param aErrorCode will contain error if one occurs * @return float rep of string value */ float nsString1::ToFloat(PRInt32* aErrorCode) const { char buf[40]; 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 numeric string to int conversion with given radix. * @update gess 10/01/98 * @param aErrorCode will contain error if one occurs * @param aRadix tells us what base to expect the string in. * @return int rep of string value; aErrorCode gets set too: NS_OK, NS_ERROR_ILLEGAL_VALUE */ PRInt32 nsString1::ToInteger(PRInt32* aErrorCode,PRInt32 aRadix) const { PRInt32 result = 0; PRInt32 decPt=Find(PRUnichar('.'),0); PRUnichar* cp = (-1==decPt) ? mStr + mLength-1 : mStr+decPt-1; char digit=0; PRUnichar theChar; // PRInt32 theShift=0; PRInt32 theMult=1; *aErrorCode = (0= mStr) { theChar = *cp; if((theChar>='0') && (theChar<='9')){ break; } else if((theChar>='a') && (theChar<='f')) { break; } else if((theChar>='A') && (theChar<='F')) { break; } cp--; } //now iterate the numeric chars and build our result while(cp>=mStr) { theChar=*cp--; if((theChar>='0') && (theChar<='9')){ digit=theChar-'0'; } else if((theChar>='a') && (theChar<='f')) { digit=(theChar-'a')+10; } else if((theChar>='A') && (theChar<='F')) { digit=(theChar-'A')+10; } else if('-'==theChar) { result=-result; break; } else if(('+'==theChar) || (' '==theChar) || ('#'==theChar)) { //stop in a good state if you see this... break; } else if((('x'==theChar) || ('X'==theChar)) && (16==aRadix)) { //stop in a good state. break; } else{ *aErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } result+=digit*theMult; theMult*=aRadix; } return result; } /** * assign given PRUnichar* to this string * @update gess 7/27/98 * @param PRUnichar: buffer to be assigned to this * @return this */ nsString1& nsString1::SetString(const PRUnichar* aStr,PRInt32 aLength) { if((0 == aLength) || (nsnull == aStr)) { mLength=0; if (nsnull != mStr) { mStr[0]=0; } } else { PRInt32 len=(aLength<0) ? nsCRT::strlen(aStr) : aLength; if(mCapacity<=len) EnsureCapacityFor(len); nsCRT::memcpy(mStr,aStr,len*sizeof(chartype)); mLength=len; mStr[mLength]=0; } return *this; } /** * assign given char* to this string * @update gess 7/27/98 * @param aCString: buffer to be assigned to this * @return this */ nsString1& nsString1::SetString(const char* aCString,PRInt32 aLength) { if(aCString!=0) { PRInt32 len=(aLength<0) ? nsCRT::strlen(aCString) : aLength; if(mCapacity<=len) EnsureCapacityFor(len); const unsigned char* from = (const unsigned char*) aCString; const unsigned char* end = from + len; PRUnichar* dst = mStr; while (from < end) { *dst++ = PRUnichar(*from++); } mLength=len; mStr[mLength]=0; } else { mLength=0; //This little bit of code handles the case mStr[0]=0; //where some blockhead hands us a null string } return *this; } /** * assign given char* to this string * @update gess 7/27/98 * @param aCString: buffer to be assigned to this * @return this */ nsString1& nsString1::SetString(const nsString1& aString) { if(mCapacity<=aString.mLength) EnsureCapacityFor(aString.mLength); PRUnichar* from = (PRUnichar*) aString.mStr; PRUnichar* end = from + aString.mLength; PRUnichar* dst = (PRUnichar*) mStr; while(fromSetString(aStr); } /** * assign given string to this one * @update gess 7/27/98 * @param aString: string to be added to this * @return this */ nsString1& nsString1::operator=(const nsString1& aString) { return this->SetString(aString); } /** * assign given char* to this string * @update gess 7/27/98 * @param aCString: buffer to be assigned to this * @return this */ nsString1& nsString1::operator=(const char* aCString) { return SetString(aCString); } /** * assign given char to this string * @update gess 7/27/98 * @param aChar: char to be assignd to this * @return this */ nsString1& nsString1::operator=(char aChar) { return this->operator=(PRUnichar(aChar)); } /** * assign given char to this string * @update gess 7/27/98 * @param aChar: char to be assignd to this * @return this */ nsString1& nsString1::operator=(PRUnichar aChar) { if(mCapacity<1) EnsureCapacityFor(kGrowthDelta); mStr[0]=aChar; mLength=1; mStr[mLength]=0; return *this; } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::Append(const nsString1& aString,PRInt32 aLength) { return Append(aString.mStr,aString.mLength); } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::Append(const char* aCString,PRInt32 aLength) { if(aCString!=0) { PRInt32 len=(aLength<0) ? strlen(aCString) : aLength; if(mLength+len >= mCapacity) { EnsureCapacityFor(mLength+len); } const unsigned char* from = (const unsigned char*) aCString; const unsigned char* end = from + len; PRUnichar* to = mStr + mLength; while (from < end) { *to++ = PRUnichar(*from++); } mLength+=len; mStr[mLength]=0; } return *this; } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::Append(char aChar) { return Append(PRUnichar(aChar)); } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::Append(const PRUnichar* aString,PRInt32 aLength) { if(aString!=0) { PRInt32 len=(aLength<0) ? nsCRT::strlen(aString) : aLength; if(mLength+len >= mCapacity) { EnsureCapacityFor(mLength+len); } if(len>0) nsCRT::memcpy(&mStr[mLength],aString,len*sizeof(chartype)); mLength+=len; mStr[mLength]=0; } return *this; } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::Append(PRUnichar aChar) { if(mLength < mCapacity) { mStr[mLength++]=aChar; // the new string len < capacity, so just copy mStr[mLength]=0; } else { // The new string exceeds our capacity EnsureCapacityFor(mLength+1); mStr[mLength++]=aChar; mStr[mLength]=0; } return *this; } /** * append given string to this string * @update gess 7/27/98 * @param aString : string to be appended to this * @return this */ nsString1& nsString1::operator+=(const nsString1 &aString) { return this->Append(aString.mStr,aString.mLength); } /** * append given buffer to this string * @update gess 7/27/98 * @param aCString: buffer to be appended to this * @return this */ nsString1& nsString1::operator+=(const char* aCString) { return Append(aCString); } /** * append given buffer to this string * @update gess 7/27/98 * @param aBuffer: buffer to be appended to this * @return this */ nsString1& nsString1::operator+=(const PRUnichar* aBuffer) { return Append(aBuffer); } /** * append given char to this string * @update gess 7/27/98 * @param aChar: char to be appended to this * @return this */ nsString1& nsString1::operator+=(PRUnichar aChar) { return Append(aChar); } /** * * @update gess 7/27/98 * @param * @return */ nsString1& nsString1::Append(PRInt32 aInteger,PRInt32 aRadix) { char* fmt = "%d"; if (8 == aRadix) { fmt = "%o"; } else if (16 == aRadix) { fmt = "%x"; } char buf[40]; PR_snprintf(buf, sizeof(buf), fmt, aInteger); Append(buf); return *this; } /** * * @update gess 7/27/98 * @param * @return */ nsString1& nsString1::Append(float aFloat){ char buf[40]; PR_snprintf(buf, sizeof(buf), "%g", aFloat); Append(buf); return *this; } /* * Copies n characters from this string to given string, * starting at the leftmost offset. * * * @update gess 4/1/98 * @param aCopy -- Receiving string * @param aCount -- number of chars to copy * @return number of chars copied */ PRInt32 nsString1::Left(nsString1& aCopy,PRInt32 aCount) const { return Mid(aCopy,0,aCount); } /* * Copies n characters from this string to given string, * starting at the given offset. * * * @update gess 4/1/98 * @param aCopy -- Receiving string * @param aCount -- number of chars to copy * @param anOffset -- position where copying begins * @return number of chars copied */ PRInt32 nsString1::Mid(nsString1& aCopy,PRInt32 anOffset,PRInt32 aCount) const { aCopy.Truncate(); if(anOffsetaCopy.mLength) ? aCopy.mLength : aCount; //don't try to copy more than you are given if (aCount < 0) aCount = aCopy.mLength; if(0<=anOffset) { if(aCount>0) { //1st optimization: If you're inserting at end, then simply append! if(anOffset>=mLength){ Append(aCopy,aCopy.mLength); return aCopy.mLength; } if(mLength+aCount >= mCapacity) { EnsureCapacityFor(mLength+aCount); } PRUnichar* last = mStr + mLength; PRUnichar* first = mStr + anOffset-1; PRUnichar* next = mStr + mLength + aCount; //Copy rightmost chars, up to offset+aCount... while(first=mCapacity) { EnsureCapacityFor(mLength+1); } PRUnichar* last = mStr + mLength; PRUnichar* first = mStr + anOffset-1; PRUnichar* next = mStr + mLength + 1; //Copy rightmost chars, up to offset+aCount... while(first= '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 nsString1::IsSpace(PRUnichar aChar) { // XXX i18n if ((aChar == ' ') || (aChar == '\r') || (aChar == '\n') || (aChar == '\t')) { return PR_TRUE; } return PR_FALSE; } /** * 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 nsString1::IsDigit(PRUnichar aChar) { // XXX i18n return PRBool((aChar >= '0') && (aChar <= '9')); } /** * This method trims characters found in aTrimSet from * either end of the underlying string. * * @update gess 3/31/98 * @param aTrimSet -- contains chars to be trimmed from * both ends * @return this */ nsString1& nsString1::Trim(const char* aTrimSet, PRBool aEliminateLeading, PRBool aEliminateTrailing) { PRUnichar* from = mStr; PRUnichar* end = mStr + mLength-1; PRUnichar* to = mStr; //begin by find the first char not in aTrimSet if(aEliminateLeading) { while (from < end) { PRUnichar ch = *from; if(!strchr(aTrimSet,char(ch))) { break; } from++; } } //Now, find last char not in aTrimSet if(aEliminateTrailing) { while(end> from) { PRUnichar ch = *end; if(!strchr(aTrimSet,char(ch))) { break; } end--; } } //now rewrite your string without unwanted //leading or trailing characters. if (from != to) { while (from <= end) { *to++ = *from++; } } else { to = ++end; } *to = '\0'; mLength = to - mStr; return *this; } /** * 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 */ nsString1& nsString1::CompressWhitespace( PRBool aEliminateLeading, PRBool aEliminateTrailing) { Trim(" \r\n\t",aEliminateLeading,aEliminateTrailing); PRUnichar* from = mStr; PRUnichar* end = mStr + mLength; PRUnichar* to = from; //this code converts /n, /t, /r into normal space ' '; //it also eliminates runs of whitespace... while (from < end) { PRUnichar ch = *from++; if (IsSpace(ch)) { *to++ = ' '; while (from < end) { ch = *from++; if (!IsSpace(ch)) { *to++ = ch; break; } } } else { *to++ = ch; } } *to = '\0'; mLength = to - mStr; return *this; } /** * This method strips whitespace throughout the string * * @update gess 7/27/98 * @return this */ nsString1& nsString1::StripWhitespace() { Trim(" \r\n\t"); return StripChars("\r\t\n"); } /** * This method is used to replace all occurances of the * given source char with the given dest char * * @param * @return *this */ nsString1& nsString1::ReplaceChar(PRUnichar aSourceChar, PRUnichar aDestChar) { PRUnichar* from = mStr; PRUnichar* end = mStr + mLength; while (from < end) { PRUnichar ch = *from; if(ch==aSourceChar) { *from = aDestChar; } from++; } return *this; } /** * Search for given character within this string. * This method does so by using a binary search, * so your string HAD BETTER BE ORDERED! * * @update gess 3/25/98 * @param aChar is the unicode char to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsString1::BinarySearch(PRUnichar aChar) const { PRInt32 low=0; PRInt32 high=mLength-1; while (low <= high) { int middle = (low + high) >> 1; if (mStr[middle]==aChar) return middle; if (mStr[middle]>aChar) high = middle - 1; else low = middle + 1; } return kNotFound; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param aCStringBuf - charstr to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsString1::Find(const char* aCStringBuf) const{ NS_ASSERTION(0!=aCStringBuf,kNullPointerError); PRInt32 result=kNotFound; if(aCStringBuf) { PRInt32 len=strlen(aCStringBuf); if((00;i--){ char* pos=strchr(anAsciiSet,char(mStr[i])); if(pos) return i; } } return kNotFound; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString1::RFindCharInSet(nsString1& aSet,PRInt32 anOffset) const{ if(aSet.Length()) { for(PRInt32 i=mLength-1;i>0;i--){ PRInt32 pos=aSet.Find(mStr[i]); if(kNotFound!=pos) return i; } } return kNotFound; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString1::RFind(const PRUnichar* aString,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aString,kNullPointerError); if(aString) { PRInt32 len=nsCRT::strlen(aString); if((len) && (len<=mLength)) { //only enter if abuffer length is <= mStr length. for(PRInt32 offset=mLength-len;offset>=0;offset--) { PRInt32 result=0; if(aIgnoreCase) result=nsCRT::strncasecmp(&mStr[offset],aString,len); else result=nsCRT::strncmp(&mStr[offset],aString,len); if(0==result) return offset; //in this case, 0 means they match } } } return kNotFound; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString1::RFind(const nsString1& aString,PRBool aIgnoreCase) const{ PRInt32 len=aString.mLength; if((len) && (len<=mLength)) { //only enter if abuffer length is <= mStr length. for(PRInt32 offset=mLength-len;offset>=0;offset--) { PRInt32 result=0; if(aIgnoreCase) result=nsCRT::strncasecmp(&mStr[offset],aString.mStr,len); else result=nsCRT::strncmp(&mStr[offset],aString.mStr,len); if(0==result) return offset; //in this case, 0 means they match } } return kNotFound; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString1::RFind(const char* anAsciiSet,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=anAsciiSet,kNullPointerError); if(anAsciiSet) { PRInt32 len=strlen(anAsciiSet); if((len) && (len<=mLength)) { //only enter if abuffer length is <= mStr length. for(PRInt32 offset=mLength-len;offset>=0;offset--) { PRInt32 result=0; if(aIgnoreCase) result=nsCRT::strncasecmp(&mStr[offset],anAsciiSet,len); else result=nsCRT::strncmp(&mStr[offset],anAsciiSet,len); if(0==result) return offset; //in this case, 0 means they match } } } return kNotFound; } /** * Scans this string backwards for first occurance of * the given char. * * @update gess 3/25/98 * @param * @return offset of char in string, or -1 (kNotFound) */ PRInt32 nsString1::RFind(PRUnichar aChar,PRBool aIgnoreCase) const{ chartype uc=nsCRT::ToUpper(aChar); for(PRInt32 offset=mLength-1;offset>=0;offset--) if(aIgnoreCase) { if(nsCRT::ToUpper(mStr[offset])==uc) return offset; } else if(mStr[offset]==aChar) return offset; //in this case, 0 means they match return kNotFound; } /************************************************************** COMPARISON METHODS... **************************************************************/ /** * Compares given cstring to this string. * @update gess 7/27/98 * @param aCString pts to a cstring * @param aIgnoreCase tells us how to treat case * @return -1,0,1 */ PRInt32 nsString1::Compare(const char *aCString,PRBool aIgnoreCase,PRInt32 aLength) const { NS_ASSERTION(0!=aCString,kNullPointerError); if(-1!=aLength) { //if you're given a length, use it to determine the max # of bytes to compare. //In some cases, this can speed up the string comparison. int maxlen=(aLength(const nsString1 &S) const {return PRBool(Compare(S)>0);} PRBool nsString1::operator>(const char *s) const {return PRBool(Compare(s)>0);} PRBool nsString1::operator>(const PRUnichar *s) const {return PRBool(Compare(s)>0);} PRBool nsString1::operator<=(const nsString1 &S) const {return PRBool(Compare(S)<=0);} PRBool nsString1::operator<=(const char *s) const {return PRBool(Compare(s)<=0);} PRBool nsString1::operator<=(const PRUnichar *s) const {return PRBool(Compare(s)<=0);} PRBool nsString1::operator>=(const nsString1 &S) const {return PRBool(Compare(S)>=0);} PRBool nsString1::operator>=(const char *s) const {return PRBool(Compare(s)>=0);} PRBool nsString1::operator>=(const PRUnichar *s) const {return PRBool(Compare(s)>=0);} /** * Compare this to given string; note that we compare full strings here. * * @update gess 7/27/98 * @param aString is the other nsString1 to be compared to * @return TRUE if equal */ PRBool nsString1::Equals(const nsString1& aString) const { if(aString.mLength==mLength) { PRInt32 result=nsCRT::strcmp(mStr,aString.mStr); return PRBool(0==result); } return PR_FALSE; } /** * Compare this to given string; 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 7/27/98 * @param aCString -- Cstr to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString1::Equals(const char* aCString,PRInt32 aLength) const{ NS_ASSERTION(0!=aCString,kNullPointerError); if((aLength>0) && (aLength!=mLength)) return PR_FALSE; PRInt32 result=nsCRT::strcmp(mStr,aCString); return PRBool(0==result); } /** * Compare this to given atom * @update gess 7/27/98 * @param aAtom -- atom to compare to this * @return TRUE if equal */ PRBool nsString1::Equals(const nsIAtom* aAtom) const { NS_ASSERTION(0!=aAtom,kNullPointerError); PRInt32 result=nsCRT::strcmp(mStr,aAtom->GetUnicode()); return PRBool(0==result); } /** * Compare given strings * @update gess 7/27/98 * @param s1 -- first string to be compared * @param s2 -- second string to be compared * @return TRUE if equal */ PRBool nsString1::Equals(const PRUnichar* s1, const PRUnichar* s2) const { NS_ASSERTION(0!=s1,kNullPointerError); NS_ASSERTION(0!=s2,kNullPointerError); PRBool result=PR_FALSE; if((s1) && (s2)){ PRInt32 cmp=nsCRT::strcmp(s1,s2); result=PRBool(0==cmp); } return result; } /** * Compares all chars in both strings w/o regard to case * @update gess 7/27/98 * @param * @return */ PRBool nsString1::EqualsIgnoreCase(const nsString1& aString) const{ if(aString.mLength==mLength) { PRInt32 result=nsCRT::strcasecmp(mStr,aString.mStr); return PRBool(0==result); } return PR_FALSE; } /** * * @update gess 7/27/98 * @param * @return */ PRBool nsString1::EqualsIgnoreCase(const nsIAtom *aAtom) const{ NS_ASSERTION(0!=aAtom,kNullPointerError); PRBool result=PR_FALSE; if(aAtom){ PRInt32 cmp=nsCRT::strcasecmp(mStr,aAtom->GetUnicode()); result=PRBool(0==cmp); } return result; } /** * Compares given unicode string to this w/o regard to case * @update gess 7/27/98 * @param s1 is the unicode string to be compared with this * @param aLength is the length of s1, not # of bytes to compare * @return true if full length of both strings are equal (modulo case) */ PRBool nsString1::EqualsIgnoreCase(const PRUnichar* s1, const PRUnichar* s2) const { NS_ASSERTION(0!=s1,kNullPointerError); NS_ASSERTION(0!=s2,kNullPointerError); PRBool result=PR_FALSE; if((s1) && (s2)){ PRInt32 cmp=nsCRT::strcasecmp(s1,s2); result=PRBool(0==cmp); } return result; } /** * Compare this to given string w/o regard to case; 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 7/27/98 * @param aCString -- Cstr to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString1::EqualsIgnoreCase(const char* aCString,PRInt32 aLength) const { NS_ASSERTION(0!=aCString,kNullPointerError); if((aLength>0) && (aLength!=mLength)) return PR_FALSE; PRInt32 cmp=nsCRT::strcasecmp(mStr,aCString); return PRBool(0==cmp); } /** * * @update gess 7/27/98 * @param * @return */ void nsString1::DebugDump(ostream& aStream) const { for(int i=0;i mCapacity) { PRInt32 size = mCapacity * 2; if (size < aNewLength) { size = mCapacity + aNewLength; } mCapacity=size; chartype* temp = new chartype[mCapacity+1]; if (mLength > 0) { nsCRT::memcpy(temp, mStr, mLength * sizeof(chartype) + sizeof(chartype)); } if ((mStr != mBuf) && (0 != mStr)) { delete [] mStr; } mStr = temp; } } void nsAutoString1::SizeOf(nsISizeOfHandler* aHandler) const { aHandler->Add(sizeof(*this)); if (mStr != mBuf) { aHandler->Add(mCapacity * sizeof(chartype)); } } /** * * * @update gess 3/31/98 * @param * @return */ void nsAutoString1::SelfTest(){ nsAutoString1 xas("Hello there"); xas.Append("this string exceeds the max size"); xas.DebugDump(cout); } /** * * @update gess8/8/98 * @param * @return */ ostream& operator<<(ostream& os,nsAutoString1& aString){ const PRUnichar* uc=aString.GetUnicode(); int len=aString.Length(); for(int i=0;i= PRInt32(sizeof(buf))) { cp = aString.ToNewCString(); } else { aString.ToCString(cp, aLength + 1); } if(aLength >0) ::fwrite(cp, 1, aLength, out); if (cp != buf) { delete [] cp; } return (int) aLength; } /** * * @update gess 7/27/98 * @param * @return */ void nsString1::SelfTest(void) { #ifdef RICKG_DEBUG mSelfTested=PR_TRUE; { nsAutoString1 a("foobar"); nsAutoString1 b("foo"); nsAutoString1 c(".5111"); nsAutoString1 d(" 5"); PRInt32 result=a.Compare(b); PRInt32 result2=result; result=c.ToInteger(&result2); result=d.ToInteger(&result2); result2=result; } static const char* kConstructorError = kConstructorError; static const char* kComparisonError = "Comparision error!"; static const char* kEqualsError = "Equals error!"; nsAutoString1 as("Hello there"); as.SelfTest(); static const char* temp="hello"; //first, let's test the constructors... nsString1 empty; empty=""; empty="xxx"; empty=""; nsString1 a(temp); nsString1* a_=new nsString1(a); //test copy constructor nsString1 b("world!"); //verify destructor... delete a_; a_=0; //Let's verify the Length() method... NS_ASSERTION(5==a.Length(),"Error: constructor probably bad!"); //********************************************** //Let's check out the ACCESSORS... //********************************************** const chartype* p1=a.GetUnicode(); for(int i=0;itemp3.Length(),kConstructorError); //should be char longer nsString1* es1=temp2.ToNewString(); //this should make us a new string char* es2=temp2.ToNewCString(); for(i=0;itemp8.Compare(temp9),kComparisonError); NS_ASSERTION(0temp8,kComparisonError); NS_ASSERTION(temp9>aaaa,kComparisonError); NS_ASSERTION(temp8<=temp8,kComparisonError); NS_ASSERTION(temp8<=temp9,kComparisonError); NS_ASSERTION(temp8<=bbbb,kComparisonError); NS_ASSERTION(temp9>=temp9,kComparisonError); NS_ASSERTION(temp9>=temp8,kComparisonError); NS_ASSERTION(temp9>=aaaa,kComparisonError); NS_ASSERTION(temp8.Equals(temp8),kEqualsError); NS_ASSERTION(temp8.Equals(aaaa),kEqualsError); nsString1 temp10(temp8); temp10.ToUpperCase(); NS_ASSERTION(temp8.EqualsIgnoreCase(temp10),kEqualsError); NS_ASSERTION(temp8.EqualsIgnoreCase("AAAA"),kEqualsError); //********************************************** //Now let's test a few string MANIPULATORS... //********************************************** nsAutoString1 ab("ab"); nsString1 abcde("cde"); abcde.Insert(ab,0,2); nsAutoString1 xxx("xxx"); abcde.Insert(xxx,2,3); temp2.ToUpperCase(); for(i=0;i