/* -*- 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 "nsString2.h" #include "nsDebug.h" #include "nsCRT.h" #include "nsIAtom.h" #include "nsDeque.h" #ifndef RICKG_TESTBED #include "prdtoa.h" #include "nsISizeOfHandler.h" #endif static const char* kNullPointerError = "Error: unexpected null ptr"; static const char* kWhitespace="\b\t\r\n "; #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]; } } }; /*********************************************************************** 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. ***********************************************************************/ void Subsume(nsStr& aDest,nsStr& aSource){ if(aSource.mStr && aSource.mLength) { if(aSource.mOwnsBuffer){ 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::Assign(aDest,aSource,0,aSource.mLength,0); } } else nsStr::Truncate(aDest,0,0); } //#define RICKG_DEBUG #undef RICKG_DEBUG #ifdef RICKG_DEBUG /******************************************************** This class's only purpose in life is to test nsString2. ********************************************************/ class CStringTester { public: CStringTester(); }; #endif /** * 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. */ nsString2::nsString2(eCharSize aCharSize,nsIMemoryAgent* anAgent) : mAgent(anAgent) { #ifdef RICKG_DEBUG static CStringTester gStringTester; #endif nsStr::Initialize(*this,aCharSize); } /** * This constructor accepts an ascii string * @update gess 1/4/99 * @param aCString is a ptr to a 1-byte cstr */ nsString2::nsString2(const char* aCString,eCharSize aCharSize,nsIMemoryAgent* anAgent) : mAgent(anAgent) { nsStr::Initialize(*this,aCharSize); Assign(aCString); } /** * This constructor accepts an ascii string * @update gess 1/4/99 * @param aCString is a ptr to a 1-byte cstr */ nsString2::nsString2(const PRUnichar* aString,eCharSize aCharSize,nsIMemoryAgent* anAgent) : mAgent(anAgent) { nsStr::Initialize(*this,aCharSize); Assign(aString); } /** * This is our copy constructor * @update gess 1/4/99 * @param reference to another nsString2 */ nsString2::nsString2(const nsStr &aString,eCharSize aCharSize,nsIMemoryAgent* anAgent) : mAgent(anAgent) { nsStr::Initialize(*this,aCharSize); nsStr::Assign(*this,aString,0,aString.mLength,mAgent); } /** * This is our copy constructor * @update gess 1/4/99 * @param reference to another nsString2 */ nsString2::nsString2(const nsString2& aString) :mAgent(aString.mAgent) { nsStr::Initialize(*this,aString.mCharSize); nsStr::Assign(*this,aString,0,aString.mLength,mAgent); } /** * construct off a subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ nsString2::nsString2(nsSubsumeStr& aSubsumeStr) :mAgent(0) { Subsume(*this,aSubsumeStr); } /** * Destructor * Make sure we call nsStr::Destroy. */ nsString2::~nsString2() { nsStr::Destroy(*this,mAgent); } void nsString2::SizeOf(nsISizeOfHandler* aHandler) const { aHandler->Add(sizeof(*this)); aHandler->Add(mCapacity << mCharSize); } /** * This method truncates this string to given length. * * @update gess 01/04/99 * @param anIndex -- new length of string * @return nada */ void nsString2::Truncate(PRInt32 anIndex) { nsStr::Truncate(*this,anIndex,mAgent); } /** * Determine whether or not the characters in this * string are in sorted order. * * @update gess 8/25/98 * @return TRUE if ordered. */ PRBool nsString2::IsOrdered(void) const { PRBool result=PR_TRUE; if(mLength>1) { PRUint32 theIndex; PRUnichar c1=0; PRUnichar c2=GetCharAt(*this,0); for(theIndex=1;theIndexc2) { result=PR_FALSE; break; } } } return result; } /** * 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 nsString2::SetCapacity(PRUint32 aLength) { if(aLength>mLength) { GrowCapacity(*this,aLength,mAgent); } mLength=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* nsString2::GetBuffer(void) const { const char* result=(eOneByte==mCharSize) ? mStr : 0; return result; } /** * * @update gess1/4/99 * @return ptr to internal buffer (if 2-byte), otherwise NULL */ const PRUnichar* nsString2::GetUnicode(void) const { const PRUnichar* result=(eTwoByte==mCharSize) ? mUStr : 0; return result; } /** * Get nth character. */ PRUnichar nsString2::operator[](PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } PRUnichar nsString2::CharAt(PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } PRUnichar nsString2::First(void) const{ return GetCharAt(*this,0); } PRUnichar nsString2::Last(void) const{ return GetCharAt(*this,mLength-1); } PRBool nsString2::SetCharAt(PRUnichar aChar,PRUint32 anIndex){ PRBool result=PR_FALSE; if(anIndex 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. * NOTE: 1. This method mandates that the string is well formed. * 2. This method will return an error if the string you give contains chars outside the range for the specified 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 */ PRInt32 _ToInteger(nsString2& aString,PRInt32* anErrorCode,PRUint32 aRadix) { //copy chars to local buffer -- step down from 2 bytes to 1 if necessary... PRInt32 result=0; char* cp = aString.mStr + aString.mLength; PRInt32 theMult=1; *anErrorCode = NS_OK; //now iterate the numeric chars and build our result char theChar=0; char theDigit=0; while(--cp>=aString.mStr){ char theChar=*cp; if((theChar>='0') && (theChar<='9')){ theDigit=theChar-'0'; } else if((theChar>='A') && (theChar<='F')) { if(10==aRadix){ *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } theDigit=(theChar-'A')+10; } else if('-'==theChar) { result=-result; break; } else if(('+'==theChar) || (' '==theChar)) { //stop in a good state if you see this... break; } /* The following block can be replaced with the next block else if(('X'==theChar) || ('#'==theChar)) { if(10==aRadix) { *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; } break; } ---cut above here...*/ else { //we've encountered a char that's not a legal number or sign *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } result+=theDigit*theMult; theMult*=aRadix; } return result; } /** * Call this method to extract the rightmost numeric value from the given * 1-byte input string, and simultaneously determine the radix. * NOTE: This method mandates that the string is well formed. * Leading and trailing gunk should be removed, and the case upper. * @update gess 10/01/98 * @param anInputString contains orig string * @param anOutString contains numeric portion copy of input string * @param aRadix (an out parm) tells the caller what base we think the string is in. * @return non-zero error code if this string is non-numeric */ PRInt32 GetNumericSubstring(nsString2& aString,PRUint32& aRadix) { aString.ToUpperCase(); PRInt32 decPt=aString.FindChar(aString,'.',PR_TRUE,0); char* cp = (kNotFound==decPt) ? aString.mStr + aString.mLength-1 : aString.mStr+decPt-1; aRadix=10; //assume for starters... // Skip trailing non-numeric... while (cp >= aString.mStr) { if((*cp>='0') && (*cp<='9')){ break; } else if((*cp>='A') && (*cp<='F')) { aRadix=16; break; } cp--; } aString.Truncate(cp-aString.mStr+1); //ok, now scan through chars until you find the start of this number... //we delimit the number by the presence of: +,-,#,X // Skip trailing non-numeric... cp++; while (--cp >= aString.mStr) { if((*cp>='0') && (*cp<='9')){ continue; } else if((*cp>='A') && (*cp<='F')) { continue; } else if((*cp=='-') || (*cp=='+')){ break; } else { if(('#'==(*cp)) || ('X'==(*cp))) aRadix=16; cp++; //move back by one break; } } if(cp>aString.mStr) aString.Cut(0,cp-aString.mStr); PRInt32 result=(0==aString.mLength) ? NS_ERROR_ILLEGAL_VALUE : NS_OK; return result; } /** * 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 */ PRInt32 nsString2::ToInteger(PRInt32* anErrorCode) const { /******************************************************************************** If you called this version, it's because you don't know which radix your string is stored in (hex, dec, etc.). This method will attempt to figure that out for you, and then call toInteger(err,radix). NOTE: If you know your string is in a given radix, then it's better to call toInteger(err,radix) directly, because there are cases where this method cannot make the correct determination. For example, a string that contains "123" can not be differentiated (hex vs. dec). ********************************************************************************/ //copy chars to local buffer -- step down from 2 bytes to 1 if necessary... PRInt32 result=0; if(0> 1; PRUnichar theChar=GetCharAt(*this,middle); if (theChar==aChar) return middle; if (theChar>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 nsString2::Find(const char* aCString,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aCString,kNullPointerError); PRInt32 result=kNotFound; if(aCString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCString); temp.mStr=(char*)aCString; result=nsStr::FindSubstr(*this,temp,aIgnoreCase,0); } return result; } /** * 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 nsString2::Find(const PRUnichar* aString,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aString,kNullPointerError); PRInt32 result=kNotFound; if(aString) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aString); temp.mUStr=(PRUnichar*)aString; result=nsStr::FindSubstr(*this,temp,aIgnoreCase,0); } return result; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param nsString2 -- buffer to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsString2::Find(const nsStr& aString,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::FindSubstr(*this,aString,aIgnoreCase,0); return result; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param nsString2 -- buffer to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsString2::Find(const nsString2& aString,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::FindSubstr(*this,aString,aIgnoreCase,0); return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsString2::Find(PRUnichar aChar,PRUint32 anOffset,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::FindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::FindCharInSet(const char* aCStringSet,PRUint32 anOffset) const{ NS_ASSERTION(0!=aCStringSet,kNullPointerError); PRInt32 result=kNotFound; if(aCStringSet) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCStringSet); temp.mStr=(char*)aCStringSet; result=nsStr::FindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::FindCharInSet(const nsString2& aSet,PRUint32 anOffset) const{ PRInt32 result=nsStr::FindCharInSet(*this,aSet,PR_FALSE,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::RFindCharInSet(const char* aCStringSet,PRUint32 anOffset) const{ NS_ASSERTION(0!=aCStringSet,kNullPointerError); PRInt32 result=kNotFound; if(aCStringSet) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCStringSet); temp.mStr=(char*)aCStringSet; result=nsStr::RFindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::RFindCharInSet(const nsString2& aSet,PRUint32 anOffset) const{ PRInt32 result=nsStr::RFindCharInSet(*this,aSet,PR_FALSE,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::RFind(const nsStr& aString,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::RFindSubstr(*this,aString,aIgnoreCase,0); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::RFind(const nsString2& aString,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::RFindSubstr(*this,aString,aIgnoreCase,0); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsString2::RFind(const char* aString,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aString,kNullPointerError); PRInt32 result=kNotFound; if(aString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aString); temp.mStr=(char*)aString; result=nsStr::RFindSubstr(*this,temp,aIgnoreCase,0); } return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsString2::RFind(PRUnichar aChar,PRUint32 anOffset,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::RFindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /************************************************************** COMPARISON METHODS... **************************************************************/ /** * Compares given cstring to this string. * @update gess 01/04/99 * @param aCString pts to a cstring * @param aIgnoreCase tells us how to treat case * @return -1,0,1 */ PRInt32 nsString2::Compare(const nsString2& aString,PRBool aIgnoreCase,PRInt32 aLength) const { return nsStr::Compare(*this,aString,aLength,aIgnoreCase); } /** * Compares given cstring to this string. * @update gess 01/04/99 * @param aCString pts to a cstring * @param aIgnoreCase tells us how to treat case * @return -1,0,1 */ PRInt32 nsString2::Compare(const char *aCString,PRBool aIgnoreCase,PRInt32 aLength) const { NS_ASSERTION(0!=aCString,kNullPointerError); if(aCString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCString); temp.mStr=(char*)aCString; return nsStr::Compare(*this,temp,aLength,aIgnoreCase); } return 0; } /** * Compares given unistring to this string. * @update gess 01/04/99 * @param aString pts to a uni-string * @param aIgnoreCase tells us how to treat case * @return -1,0,1 */ PRInt32 nsString2::Compare(const PRUnichar* aString,PRBool aIgnoreCase,PRInt32 aLength) const { NS_ASSERTION(0!=aString,kNullPointerError); if(aString) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aString); temp.mUStr=(PRUnichar*)aString; return nsStr::Compare(*this,temp,aLength,aIgnoreCase); } return 0; } /** * LAST MODS: gess * * @param * @return */ PRInt32 nsString2::Compare(const nsStr& aString,PRBool aIgnoreCase,PRInt32 aLength) const { return nsStr::Compare(*this,aString,aLength,aIgnoreCase); } PRBool nsString2::operator==(const nsString2& S) const {return Equals(S);} PRBool nsString2::operator==(const nsStr& S) const {return Equals(S);} PRBool nsString2::operator==(const char* s) const {return Equals(s);} PRBool nsString2::operator==(const PRUnichar* s) const {return Equals(s);} PRBool nsString2::operator!=(const nsString2& S) const {return PRBool(Compare(S)!=0);} PRBool nsString2::operator!=(const nsStr& S) const {return PRBool(Compare(S)!=0);} PRBool nsString2::operator!=(const char* s) const {return PRBool(Compare(s)!=0);} PRBool nsString2::operator!=(const PRUnichar* s) const {return PRBool(Compare(s)!=0);} PRBool nsString2::operator<(const nsString2& S) const {return PRBool(Compare(S)<0);} PRBool nsString2::operator<(const nsStr& S) const {return PRBool(Compare(S)<0);} PRBool nsString2::operator<(const char* s) const {return PRBool(Compare(s)<0);} PRBool nsString2::operator<(const PRUnichar* s) const {return PRBool(Compare(s)<0);} PRBool nsString2::operator>(const nsString2& S) const {return PRBool(Compare(S)>0);} PRBool nsString2::operator>(const nsStr& S) const {return PRBool(Compare(S)>0);} PRBool nsString2::operator>(const char* s) const {return PRBool(Compare(s)>0);} PRBool nsString2::operator>(const PRUnichar* s) const {return PRBool(Compare(s)>0);} PRBool nsString2::operator<=(const nsString2& S) const {return PRBool(Compare(S)<=0);} PRBool nsString2::operator<=(const nsStr& S) const {return PRBool(Compare(S)<=0);} PRBool nsString2::operator<=(const char* s) const {return PRBool(Compare(s)<=0);} PRBool nsString2::operator<=(const PRUnichar* s) const {return PRBool(Compare(s)<=0);} PRBool nsString2::operator>=(const nsString2& S) const {return PRBool(Compare(S)>=0);} PRBool nsString2::operator>=(const nsStr& S) const {return PRBool(Compare(S)>=0);} PRBool nsString2::operator>=(const char* s) const {return PRBool(Compare(s)>=0);} PRBool nsString2::operator>=(const PRUnichar* s) const {return PRBool(Compare(s)>=0);} PRBool nsString2::EqualsIgnoreCase(const nsString2& aString) const { return Equals(aString,PR_TRUE); } PRBool nsString2::EqualsIgnoreCase(const char* aString,PRInt32 aLength) const { return Equals(aString,aLength,PR_TRUE); } PRBool nsString2::EqualsIgnoreCase(const nsIAtom *aAtom) const { return Equals(aAtom,PR_TRUE); } PRBool nsString2::EqualsIgnoreCase(const PRUnichar* s1, const PRUnichar* s2) const { return Equals(s1,s2,PR_TRUE); } /** * Compare this to given string; note that we compare full strings here. * * @update gess 01/04/99 * @param aString is the other nsString2 to be compared to * @return TRUE if equal */ PRBool nsString2::Equals(const nsString2& aString,PRBool aIgnoreCase) const { PRInt32 result=nsStr::Compare(*this,aString,MinInt(mLength,aString.mLength),aIgnoreCase); return PRBool(0==result); } /** * Compare this to given string; note that we compare full strings here. * * @update gess 01/04/99 * @param aString is the other nsString2 to be compared to * @return TRUE if equal */ PRBool nsString2::Equals(const nsStr& aString,PRBool aIgnoreCase) const { PRInt32 result=nsStr::Compare(*this,aString,MinInt(mLength,aString.mLength),aIgnoreCase); return PRBool(0==result); } PRBool nsString2::Equals(const char* aString,PRBool aIgnoreCase) const { if(aString) { return Equals(aString,nsCRT::strlen(aString),aIgnoreCase); } return 0; } /** * 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 01/04/99 * @param aCString -- Cstr to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString2::Equals(const char* aCString,PRUint32 aCount,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aCString,kNullPointerError); PRBool result=PR_FALSE; if(aCString) { PRInt32 theAnswer=Compare(aCString,aIgnoreCase,aCount); result=PRBool(0==theAnswer); } return result; } PRBool nsString2::Equals(const PRUnichar* aString,PRBool aIgnoreCase) const { NS_ASSERTION(0!=aString,kNullPointerError); PRBool result=PR_FALSE; if(aString) { result=Equals(aString,nsCRT::strlen(aString),aIgnoreCase); } return result; } /** * 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 01/04/99 * @param aString -- unistring to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString2::Equals(const PRUnichar* aString,PRUint32 aCount,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aString,kNullPointerError); PRBool result=PR_FALSE; if(aString){ PRInt32 theAnswer=Compare(aString,aIgnoreCase,aCount); result=PRBool(0==theAnswer); } return result; } /** * 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 01/04/99 * @param aString -- unistring to compare to this * @param aLength -- length of given string. * @return TRUE if equal */ PRBool nsString2::Equals(const nsIAtom* aAtom,PRBool aIgnoreCase) const{ NS_ASSERTION(0!=aAtom,kNullPointerError); PRBool result=PR_FALSE; if(aAtom){ PRInt32 cmp=nsCRT::strcasecmp(mUStr,aAtom->GetUnicode()); result=PRBool(0==cmp); } return 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 nsString2::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; } /** * 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 nsString2::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 nsString2::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 nsString2::IsDigit(PRUnichar aChar) { // XXX i18n return PRBool((aChar >= '0') && (aChar <= '9')); } /************************************************************** Define the string deallocator class... **************************************************************/ class nsStringDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* anObject) { static nsMemoryAgent theAgent; nsString2* aString= (nsString2*)anObject; if(aString){ aString->mAgent=&theAgent; delete aString; } return 0; } }; /**************************************************************************** * This class, appropriately enough, creates and recycles nsString2 objects.. ****************************************************************************/ class nsStringRecycler { public: nsStringRecycler() : mDeque(0) { } ~nsStringRecycler() { nsStringDeallocator theDeallocator; mDeque.ForEach(theDeallocator); //now delete the strings } void Recycle(nsString2* aString) { mDeque.Push(aString); } nsString2* CreateString(eCharSize aCharSize){ nsString2* result=(nsString2*)mDeque.Pop(); if(!result) result=new nsString2(aCharSize); return result; } nsDeque mDeque; }; void Subsume(nsStr& aDest,nsStr& aSource); nsStringRecycler& GetRecycler(void); /** * * @update gess 01/04/99 * @param * @return */ nsStringRecycler& GetRecycler(void){ static nsStringRecycler gRecycler; return gRecycler; } /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ nsString2* nsString2::CreateString(eCharSize aCharSize){ nsString2* result=GetRecycler().CreateString(aCharSize); return result; } /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ void nsString2::Recycle(nsString2* aString){ GetRecycler().Recycle(aString); } /** * * @update gess 01/04/99 * @param * @return */ void nsString2::DebugDump(ostream& aStream) const { for(PRUint32 i=0;i= PRInt32(sizeof(buf))) { cp = aString.ToNewCString(); } else { aString.ToCString(cp, len + 1); } if(len>0) ::fwrite(cp, 1, len, out); if (cp != buf) { delete[] cp; } return (int) len; } /*********************************************************************** IMPLEMENTATION NOTES: AUTOSTRING... ***********************************************************************/ /** * Special case constructor, that allows the consumer to provide * an underlying buffer for performance reasons. * @param aBuffer points to your buffer * @param aBufSize defines the size of your buffer * @param aCurrentLength tells us the current length of the buffer */ nsAutoString2::nsAutoString2(eCharSize aCharSize) : nsString2(aCharSize){ nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); mAgent=0; AddNullTerminator(*this); } /** * construct from external buffer and given string * @param anExtBuffer describes an external buffer * @param aCString is a ptr to a 1-byte cstr */ nsAutoString2::nsAutoString2(nsStr& aStr,const char* aCString) : nsString2(aStr.mCharSize) { nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aStr.mCharSize)-1,0,aStr.mCharSize,PR_FALSE); mAgent=0; AddNullTerminator(*this); Assign(aCString); } /** * Copy construct from ascii c-string * @param aCString is a ptr to a 1-byte cstr */ nsAutoString2::nsAutoString2(const char* aCString,eCharSize aCharSize) : nsString2(aCharSize) { nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); mAgent=0; AddNullTerminator(*this); Assign(aCString); } /** * Copy construct from ascii c-string * @param aCString is a ptr to a 1-byte cstr */ nsAutoString2::nsAutoString2(char* aCString,PRInt32 aCapacity,eCharSize aCharSize,PRBool assumeOwnership) : nsString2(aCharSize) { mAgent=0; if(assumeOwnership && aCString) { aCapacity = (-1==aCapacity) ? strlen(aCString) : aCapacity-1; nsStr::Initialize(*this,aCString,aCapacity,aCapacity,eOneByte,PR_TRUE); AddNullTerminator(*this); } else { nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aCString); } } /** * Copy construct from uni-string * @param aString is a ptr to a unistr */ nsAutoString2::nsAutoString2(const PRUnichar* aString,eCharSize aCharSize) : nsString2(aCharSize) { mAgent=0; nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aString); } /** * Copy construct from uni-string * @param aString is a ptr to a unistr */ nsAutoString2::nsAutoString2(PRUnichar* aString,PRInt32 aCapacity,eCharSize aCharSize,PRBool assumeOwnership) : nsString2(aCharSize) { mAgent=0; if(assumeOwnership && aString) { aCapacity = (-1==aCapacity) ? nsCRT::strlen(aString) : aCapacity-1; nsStr::Initialize(*this,(char*)aString,aCapacity,aCapacity,eTwoByte,PR_TRUE); AddNullTerminator(*this); } else { nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aString); } } /** * Copy construct from an nsString2 * @param */ nsAutoString2::nsAutoString2(const nsStr& aString,eCharSize aCharSize) : nsString2(aCharSize) { mAgent=0; nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aString); } /** * Copy construct from an nsString2 * @param */ nsAutoString2::nsAutoString2(const nsAutoString2& aString,eCharSize aCharSize) : nsString2(aCharSize) { mAgent=0; nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aString); } /** * Copy construct from an nsString2 * @param */ nsAutoString2::nsAutoString2(const nsString2& aString,eCharSize aCharSize) : nsString2(aCharSize){ mAgent=0; nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aString); } /** * Copy construct from an nsString2 * @param */ nsAutoString2::nsAutoString2(PRUnichar aChar,eCharSize aCharSize) : nsString2(aCharSize){ mAgent=0; nsStr::Initialize(*this,mBuffer,(sizeof(mBuffer)>>aCharSize)-1,0,aCharSize,PR_FALSE); AddNullTerminator(*this); Assign(aChar); } /** * construct from a subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ #ifdef AIX nsAutoString2::nsAutoString2(const nsSubsumeStr& aSubsumeStr) :nsString2(aSubsumeStr.mCharSize) { mAgent=0; nsSubsumeStr temp(aSubsumeStr); // a temp is needed for the AIX compiler Subsume(*this,temp); #else nsAutoString2::nsAutoString2( nsSubsumeStr& aSubsumeStr) :nsString2(aSubsumeStr.mCharSize) { mAgent=0; Subsume(*this,aSubsumeStr); #endif // AIX } /** * deconstruct the autstring * @param */ nsAutoString2::~nsAutoString2(){ // bool b=true; // mStr=0; } void nsAutoString2::SizeOf(nsISizeOfHandler* aHandler) const { aHandler->Add(sizeof(*this)); aHandler->Add(mCapacity << mCharSize); } nsSubsumeStr::nsSubsumeStr(nsString2& aString) : nsString2(aString.mCharSize) { Subsume(*this,aString); } nsSubsumeStr::nsSubsumeStr(nsStr& aString) : nsString2(aString.mCharSize) { Subsume(*this,aString); } nsSubsumeStr::nsSubsumeStr(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength) : nsString2(eTwoByte) { mUStr=aString; mCapacity=mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength-1; mOwnsBuffer=assumeOwnership; } nsSubsumeStr::nsSubsumeStr(char* aString,PRBool assumeOwnership,PRInt32 aLength) : nsString2(eOneByte) { mStr=aString; mCapacity=mLength=(-1==aLength) ? strlen(aString) : aLength-1; mOwnsBuffer=assumeOwnership; } /*********************************************************************** IMPLEMENTATION of nsCAutoString, which is a vanilla string class that only ever stores 1 byte character strings. Typically you'll use this class to hold a pointer to a char*, which comes from an nsString. ***********************************************************************/ nsCAutoString::nsCAutoString(const nsString2& aString) : nsAutoString(aString,eOneByte){ } nsCAutoString::operator const char*() const { return (const char*)mStr; } #ifdef RICKG_DEBUG /*********************************************************************** IMPLEMENTATION of CStringTester... ***********************************************************************/ CStringTester::CStringTester() { static const char* kConstructorError = kConstructorError; static const char* kComparisonError = "Comparision error!"; static const char* kEqualsError = "Equals error!"; eCharSize theSize=eTwoByte; //begin by testing the constructors... { { nsString2 theString0("foo",theSize); //watch it construct and destruct } //Let's test our autoCStrings... { nsString2 theString("hello"); nsCAutoString theCStr(theString); } { //this test makes sure that autostrings who assume ownership of a buffer, //don't also try to copy that buffer onto itself... (was a bug) char* theStr="hello rick"; nsAutoString2(theStr,5,eOneByte,PR_FALSE); } { nsString2 theString("hello"); nsString2 temp1=theString+" there!"; nsString2 temp2=theString+'!'; nsSubsumeStr temp3=theString+'?'; nsString2 temp4(temp3); temp1=temp3; nsAutoString2 temp5("hello"); nsSubsumeStr temp6(temp5); } nsString2 theString1(theSize); nsString2 theString("hello",theSize); nsString2 theString3(theString,theSize); nsStr& si=theString; nsString2 theString4(si,theSize); PRUint32 theLen=theString3.Length(); //and hey, why not do a few lexo-morphic tests... theString.ToUpperCase(); theString.ToLowerCase(); //while we're here, let's try truncation and setting the length. theString3.Truncate(3); theLen=theString3.Length(); theString.SetCapacity(3); const char* theBuffer=theString.GetBuffer(); const char* theOther=theBuffer; { nsString2 temp(" hello there rick ",theSize); temp.CompressWhitespace(); } nsString2 theString5(" hello there rick ",theSize); theString5.StripChars("reo"); theString5.Trim(" ",PR_TRUE,PR_FALSE); theString5.StripWhitespace(); nsString2* theString6=theString5.ToNewString(); nsString2::Recycle(theString6); char* str=theString5.ToNewCString(); delete [] str; char buffer[100]; theString5.ToCString(buffer,sizeof(buffer)-1); theOther=theString5.GetBuffer(); } //try a few numeric conversion routines... { nsString2 str1("10000",theSize); nsString2 str2("hello"); nsString2 str3; PRInt32 err; PRInt32 theInt=str1.ToInteger(&err); theInt=str2.ToInteger(&err); theInt=str3.ToInteger(&err); str1="100.100"; float theFloat=str1.ToFloat(&err); } //Now test the character accessor methods... { nsString2 theString("hello",theSize); PRUint32 len=theString.Length(); PRUnichar ch; for(PRUint32 i=0;itemp8.Compare(bbbb),kComparisonError); NS_ASSERTION(0>temp8.Compare(temp9),kComparisonError); NS_ASSERTION(0=temp8)),kComparisonError); NS_ASSERTION(((temp9>temp8) && (temp8<=temp9)),kComparisonError); NS_ASSERTION(temp9>aaaa,kComparisonError); NS_ASSERTION(temp8<=temp8,kComparisonError); NS_ASSERTION(temp8<=temp9,kComparisonError); NS_ASSERTION(temp8<=bbbb,kComparisonError); NS_ASSERTION(((temp9>=temp8) && (temp8=temp8,kComparisonError); NS_ASSERTION(temp9>=aaaa,kComparisonError); NS_ASSERTION(temp8.Equals(temp8),kEqualsError); NS_ASSERTION(temp8.Equals(aaaa),kEqualsError); nsString2 temp10(temp8); temp10.ToUpperCase(); NS_ASSERTION(temp8.Equals(temp10,PR_TRUE),kEqualsError); NS_ASSERTION(temp8.Equals("AAAA",PR_TRUE),kEqualsError); //********************************************** //Now let's test a few string MANIPULATORS... //********************************************** { nsAutoString2 ab("ab",theSize); nsString2 abcde("cde",theSize); nsString2 cut("abcdef",theSize); cut.Cut(7,10); //this is out of bounds, so ignore... cut.DebugDump(cout); cut.Cut(5,2); //cut last chars cut.DebugDump(cout); cut.Cut(1,1); //cut first char cut.DebugDump(cout); cut.Cut(2,1); //cut one from the middle cut.DebugDump(cout); cut="Hello there Rick"; cut.DebugDump(cout); cut="'\"abcdef\"'"; cut.Trim("'"); cut.DebugDump(cout); cut.Trim("\"",PR_TRUE,PR_FALSE); cut.DebugDump(cout); cut.Trim("\"",PR_FALSE,PR_TRUE); cut.DebugDump(cout); cut="abc def\n\n ghi"; cut.StripWhitespace(); cut.DebugDump(cout); cut="abc def\n\n ghi"; cut.CompressWhitespace(); cut.DebugDump(cout); } //********************************************** //Now let's test the SEARCHING operations... //********************************************** { nsString2 find1("abcdefghijk",theSize); nsString2 find2("ijk",theSize); PRInt32 pos=find1.Find("efg"); NS_ASSERTION(pos==4,"Error: Find routine"); pos=find1.Find("EFG",PR_TRUE); NS_ASSERTION(pos==4,"Error: Find routine"); pos=find1.Find('d'); NS_ASSERTION(pos==3,"Error: Find char routine"); pos=find1.Find(find2); NS_ASSERTION(pos==8,"Error: Find char routine"); pos=find1.FindCharInSet("12k"); NS_ASSERTION(pos==10,"Error: FindFirstInChar routine"); pos=find1.RFindCharInSet("12k"); NS_ASSERTION(pos==10,"Error: FindLastInChar routine"); pos=find1.RFind("efg"); NS_ASSERTION(pos==4,"Error: RFind routine"); pos=find1.RFind("xxx"); NS_ASSERTION(pos==-1,"Error: RFind routine"); //this should fail pos=find1.RFind(""); NS_ASSERTION(pos==-1,"Error: RFind routine"); //this too should fail. pos=find1.RFind('a'); NS_ASSERTION(pos==0,"Error: RFind routine"); pos=find1.BinarySearch('a'); pos=find1.BinarySearch('b'); pos=find1.BinarySearch('c'); pos=find1.BinarySearch('d'); pos=find1.BinarySearch('e'); pos=find1.BinarySearch('f'); pos=find1.BinarySearch('g'); pos=find1.BinarySearch('h'); pos=find1.BinarySearch('i'); pos=find1.BinarySearch('z'); } { //now let's try a memory allocation test... nsString2 temp; for(int i=0;i<100;i++){ temp+="hello "; } int x=5; } }; #endif //rickg_debug