/* -*- 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. */ /****************************************************************************************** MODULE NOTES: This file contains the nsStr data structure. This general purpose buffer management class is used as the basis for our strings. It's benefits include: 1. An efficient set of library style functions for manipulating nsStrs 2. Support for 1 and 2 byte character strings (which can easily be increased to n) 3. Unicode awareness and interoperability. *******************************************************************************************/ #include "nsStr.h" #include "bufferRoutines.h" #include "stdio.h" //only used for printf #include "nsCRT.h" #include "nsDeque.h" //static const char* kCallFindChar = "For better performance, call FindChar() for targets whose length==1."; //static const char* kCallRFindChar = "For better performance, call RFindChar() for targets whose length==1."; static const PRUnichar gCommonEmptyBuffer[1] = {0}; /** * This method initializes all the members of the nsStr structure * * @update gess10/30/98 * @param * @return */ void nsStr::Initialize(nsStr& aDest,eCharSize aCharSize) { aDest.mStr=(char*)gCommonEmptyBuffer; aDest.mLength=0; aDest.mCapacity=0; aDest.mCharSize=aCharSize; aDest.mOwnsBuffer=0; } /** * This method initializes all the members of the nsStr structure * @update gess10/30/98 * @param * @return */ void nsStr::Initialize(nsStr& aDest,char* aCString,PRUint32 aCapacity,PRUint32 aLength,eCharSize aCharSize,PRBool aOwnsBuffer){ aDest.mStr=(aCString) ? aCString : (char*)gCommonEmptyBuffer; aDest.mLength=aLength; aDest.mCapacity=aCapacity; aDest.mCharSize=aCharSize; aDest.mOwnsBuffer=aOwnsBuffer; } /** * * @update gess10/30/98 * @param * @return */ nsIMemoryAgent* GetDefaultAgent(void){ static nsMemoryAgent gDefaultAgent; return (nsIMemoryAgent*)&gDefaultAgent; } /** * This member destroys the memory buffer owned by an nsStr object (if it actually owns it) * @update gess10/30/98 * @param * @return */ void nsStr::Destroy(nsStr& aDest,nsIMemoryAgent* anAgent) { if((aDest.mStr) && (aDest.mStr!=(char*)gCommonEmptyBuffer)) { if(!anAgent) anAgent=GetDefaultAgent(); if(anAgent) { anAgent->Free(aDest); } else{ printf("%s\n","Leak occured in nsStr."); } } } /** * This method gets called when the internal buffer needs * to grow to a given size. The original contents are not preserved. * @update gess 3/30/98 * @param aNewLength -- new capacity of string in charSize units * @return void */ PRBool nsStr::EnsureCapacity(nsStr& aString,PRUint32 aNewLength,nsIMemoryAgent* anAgent) { PRBool result=PR_TRUE; if(aNewLength>aString.mCapacity) { nsIMemoryAgent* theAgent=(anAgent) ? anAgent : GetDefaultAgent(); result=theAgent->Realloc(aString,aNewLength); if(aString.mStr) AddNullTerminator(aString); } return result; } /** * This method gets called when the internal buffer needs * to grow to a given size. The original contents ARE preserved. * @update gess 3/30/98 * @param aNewLength -- new capacity of string in charSize units * @return void */ PRBool nsStr::GrowCapacity(nsStr& aDest,PRUint32 aNewLength,nsIMemoryAgent* anAgent) { PRBool result=PR_TRUE; if(aNewLength>aDest.mCapacity) { nsStr theTempStr; nsStr::Initialize(theTempStr,aDest.mCharSize); nsIMemoryAgent* theAgent=(anAgent) ? anAgent : GetDefaultAgent(); result=EnsureCapacity(theTempStr,aNewLength,theAgent); if(result) { if(aDest.mLength) { Append(theTempStr,aDest,0,aDest.mLength,theAgent); } theAgent->Free(aDest); aDest.mStr = theTempStr.mStr; theTempStr.mStr=0; //make sure to null this out so that you don't lose the buffer you just stole... aDest.mLength=theTempStr.mLength; aDest.mCapacity=theTempStr.mCapacity; aDest.mOwnsBuffer=theTempStr.mOwnsBuffer; } } return result; } /** * Replaces the contents of aDest with aSource, up to aCount of chars. * @update gess10/30/98 * @param aDest is the nsStr that gets changed. * @param aSource is where chars are copied from * @param aCount is the number of chars copied from aSource */ void nsStr::Assign(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount,nsIMemoryAgent* anAgent){ if(&aDest!=&aSource){ Truncate(aDest,0,anAgent); Append(aDest,aSource,anOffset,aCount,anAgent); } } /** * This method appends the given nsStr to this one. Note that we have to * pay attention to the underlying char-size of both structs. * @update gess10/30/98 * @param aDest is the nsStr to be manipulated * @param aSource is where char are copied from * @aCount is the number of bytes to be copied */ void nsStr::Append(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount,nsIMemoryAgent* anAgent){ if(anOffset aDest.mCapacity) { isBigEnough=GrowCapacity(aDest,aDest.mLength+theLength,anAgent); } if(isBigEnough) { //now append new chars, starting at offset (*gCopyChars[aSource.mCharSize][aDest.mCharSize])(aDest.mStr,aDest.mLength,aSource.mStr,anOffset,theLength); aDest.mLength+=theLength; AddNullTerminator(aDest); } } } } /** * This method inserts up to "aCount" chars from a source nsStr into a dest nsStr. * @update gess10/30/98 * @param aDest is the nsStr that gets changed * @param aDestOffset is where in aDest the insertion is to occur * @param aSource is where chars are copied from * @param aSrcOffset is where in aSource chars are copied from * @param aCount is the number of chars from aSource to be inserted into aDest */ void nsStr::Insert( nsStr& aDest,PRUint32 aDestOffset,const nsStr& aSource,PRUint32 aSrcOffset,PRInt32 aCount,nsIMemoryAgent* anAgent){ //there are a few cases for insert: // 1. You're inserting chars into an empty string (assign) // 2. You're inserting onto the end of a string (append) // 3. You're inserting onto the 1..n-1 pos of a string (the hard case). if(0 aDest.mCapacity) { nsStr theTempStr; nsStr::Initialize(theTempStr,aDest.mCharSize); nsIMemoryAgent* theAgent=(anAgent) ? anAgent : GetDefaultAgent(); PRBool isBigEnough=EnsureCapacity(theTempStr,aDest.mLength+theLength,theAgent); //grow the temp buffer to the right size if(isBigEnough) { if(aDestOffset) { Append(theTempStr,aDest,0,aDestOffset,theAgent); //first copy leftmost data... } Append(theTempStr,aSource,0,aSource.mLength,theAgent); //next copy inserted (new) data PRUint32 theRemains=aDest.mLength-aDestOffset; if(theRemains) { Append(theTempStr,aDest,aDestOffset,theRemains,theAgent); //next copy rightmost data } theAgent->Free(aDest); aDest.mStr = theTempStr.mStr; theTempStr.mStr=0; //make sure to null this out so that you don't lose the buffer you just stole... aDest.mCapacity=theTempStr.mCapacity; aDest.mOwnsBuffer=theTempStr.mOwnsBuffer; } } else { //shift the chars right by theDelta... (*gShiftChars[aDest.mCharSize][KSHIFTRIGHT])(aDest.mStr,aDest.mLength,aDestOffset,theLength); //now insert new chars, starting at offset (*gCopyChars[aSource.mCharSize][aDest.mCharSize])(aDest.mStr,aDestOffset,aSource.mStr,aSrcOffset,theLength); } //finally, make sure to update the string length... aDest.mLength+=theLength; AddNullTerminator(aDest); }//if //else nothing to do! } else Append(aDest,aSource,0,aCount,anAgent); } else Append(aDest,aSource,0,aCount,anAgent); } } /** * This method deletes up to aCount chars from aDest * @update gess10/30/98 * @param aDest is the nsStr to be manipulated * @param aDestOffset is where in aDest deletion is to occur * @param aCount is the number of chars to be deleted in aDest */ void nsStr::Delete(nsStr& aDest,PRUint32 aDestOffset,PRUint32 aCount,nsIMemoryAgent* anAgent){ if(aDestOffset0) && aSet){ PRInt32 theIndex=-1; PRInt32 theMax=aDest.mLength; PRInt32 theSetLen=nsCRT::strlen(aSet); if(aEliminateLeading) { while(++theIndex<=theMax) { PRUnichar theChar=GetCharAt(aDest,theIndex); PRInt32 thePos=gFindChars[eOneByte](aSet,theSetLen,0,theChar,PR_FALSE); if(kNotFound==thePos) break; } if(00) { PRUnichar theChar=GetCharAt(aDest,theIndex); //read at end now... PRInt32 thePos=gFindChars[eOneByte](aSet,theSetLen,0,theChar,PR_FALSE); if(kNotFound=aTarget.mLength) && (aTarget.mLength>0) && (index>=0)){ PRInt32 theTargetMax=aTarget.mLength; while(index<=theMax) { PRInt32 theSubIndex=-1; PRBool matches=PR_TRUE; while((++theSubIndex=aTarget.mLength) && (aTarget.mLength>0) && (index>=0)){ nsStr theCopy; nsStr::Initialize(theCopy,eOneByte); nsStr::Assign(theCopy,aTarget,0,aTarget.mLength,0); if(aIgnoreCase){ nsStr::ChangeCase(theCopy,PR_FALSE); //force to lowercase } PRInt32 theTargetMax=theCopy.mLength; while(index>=0) { PRInt32 theSubIndex=-1; PRBool matches=PR_FALSE; if(index+theCopy.mLength<=aDest.mLength) { matches=PR_TRUE; while((++theSubIndex=0) { PRUnichar theChar=GetCharAt(aDest,index); thePos=gFindChars[aSet.mCharSize](aSet.mStr,aSet.mLength,0,theChar,aIgnoreCase); if(kNotFound!=thePos) return index; } //while } return kNotFound; } /** * Compare source and dest strings, up to an (optional max) number of chars * @param aDest is the first str to compare * @param aSource is the second str to compare * @param aCount -- if (-1), then we use length of longer string; if (0aSource=1 */ PRInt32 nsStr::Compare(const nsStr& aDest,const nsStr& aSource,PRInt32 aCount,PRBool aIgnoreCase) { PRInt32 result=0; if(aCount) { PRInt32 minlen=(aSource.mLength1) { mCapacity=aCapacity-1; mLength=(-1==aLength) ? strlen(aString) : aLength; if(mLength>PRInt32(mCapacity)) mLength=mCapacity; } } CBufDescriptor::CBufDescriptor(const char* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) { mBuffer=(char*)aString; mCharSize=eOneByte; mStackBased=aStackBased; mIsConst=PR_TRUE; mLength=mCapacity=0; if(aString && aCapacity>1) { mCapacity=aCapacity-1; mLength=(-1==aLength) ? strlen(aString) : aLength; if(mLength>PRInt32(mCapacity)) mLength=mCapacity; } } CBufDescriptor::CBufDescriptor(PRUnichar* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) { mBuffer=(char*)aString; mCharSize=eTwoByte; mStackBased=aStackBased; mLength=mCapacity=0; mIsConst=PR_FALSE; if(aString && aCapacity>1) { mCapacity=aCapacity-1; mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength; if(mLength>PRInt32(mCapacity)) mLength=mCapacity; } } CBufDescriptor::CBufDescriptor(const PRUnichar* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) { mBuffer=(char*)aString; mCharSize=eTwoByte; mStackBased=aStackBased; mLength=mCapacity=0; mIsConst=PR_TRUE; if(aString && aCapacity>1) { mCapacity=aCapacity-1; mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength; if(mLength>PRInt32(mCapacity)) mLength=mCapacity; } } //----------------------------------------------------------------------------------------