/************************************************************************************* * * MODULES NOTES: * * 1. See nsStringImpl.h * * Contributor(s): * Rick Gessner * * History: * * 02.29.2000: Original files (rickg) * 03.02.2000: Flesh out the interface to be compatible with old library (rickg) * 03.03.2000: Finished mapping original nsStr functionality to template form (rickg) * 03.10.2000: Fixed off-by-1 error in StripChar() (rickg) * 03.20.2000: Ran regressions by comparing operations against original nsString (rickg) * * To Do: * * 1. Use shared buffer for empty strings * 2. Test refcounting * 3. Implement copy semantics between autostring and nsCSubsumeString to ensure * that actual character copying occurs if necessary. * *************************************************************************************/ #ifndef NS_BUFFERMANAGER_ #define NS_BUFFERMANAGER_ #include "nsStringValue.h" #define NS_MEMORY_ERROR 1000 // uncomment the following line to caught nsString char* casting problem //#define DEBUG_ILLEGAL_CAST_UP //#define DEBUG_ILLEGAL_CAST_DOWN #if defined(DEBUG_ILLEGAL_CAST_UP) || defined(DEBUG_ILLEGAL_CAST_DOWN) static PRBool track_illegal = PR_TRUE; static PRBool track_latin1 = PR_TRUE; #ifdef XP_UNIX #include "nsTraceRefcnt.h" class CTraceFile { public: CTraceFile() { mFile = fopen("nsStringTrace.txt" , "a+"); } ~CTraceFile() { fflush(mFile); fclose(mFile); } void ReportCastUp(const char* data, const char* msg) { if(mFile) { fprintf(mFile, "ERRORTEXT= %s\n", msg); fprintf(mFile, "BEGINDATA\n"); const char* s=data; while(*s) { if(*s & 0x80) { fprintf(mFile, "[%2X]", (char)*s); } else { fprintf(mFile, "%c", *s); } s++; } fprintf(mFile, "\n"); fprintf(mFile, "ENDDATA\n"); fprintf(mFile, "BEGINSTACK\n"); nsTraceRefcnt::WalkTheStack(mFile); fprintf(mFile, "\n"); fprintf(mFile, "ENDSTACK\n"); fflush(mFile); } } void ReportCastDown(const PRUnichar* data, const char* msg) { if(mFile) { fprintf(mFile, "ERRORTEXT=%s\n", msg); fprintf(mFile, "BEGINDATA\n"); const PRUnichar* s=data; while(*s) { if(*s & 0xFF80) { fprintf(mFile, "\\u%X", *s); } else { fprintf(mFile, "%c", *s); } s++; } fprintf(mFile, "\n"); fprintf(mFile, "ENDDATA\n"); fprintf(mFile, "BEGINSTACK\n"); nsTraceRefcnt::WalkTheStack(mFile); fprintf(mFile, "\n"); fprintf(mFile, "ENDSTACK\n"); fflush(mFile); } } private: FILE* mFile; }; static CTraceFile gTrace; #define TRACE_ILLEGAL_CAST_UP(c, s, m) if(!(c)) gTrace.ReportCastUp(s,m); #define TRACE_ILLEGAL_CAST_DOWN(c, s, m) if(!(c)) gTrace.ReportCastDown(s,m); #else // XP_UNIX #define TRACE_ILLEGAL_CAST_UP(c, s, m) NS_ASSERTION((c), (m)) #define TRACE_ILLEGAL_CAST_DOWN(c, s, m) NS_ASSERTION((c), (m)) #endif //XP_UNIX #endif /************************************************************************************* * * These methods offer the low level buffer manipulation routines used throughout * the string hierarchy. T1 and T2 will be types of stringvalues. (rickg) * *************************************************************************************/ template inline void SVAddNullTerminator( nsStringValueImpl& aDest) { if(aDest.mBuffer) { aDest.mBuffer[aDest.mLength]=0; } } template inline CharType ToLower(CharType aChar) { if((aChar>='A') && (aChar<='Z')) aChar+=32; return aChar; } static const PRUnichar gCommonEmptyBuffer[1] = {0}; static nsresult gStringAcquiredMemory = NS_OK; template inline nsresult SVAlloc( nsStringValueImpl& aDest, PRUint32 aCount) { nsresult result=NS_OK; //we're given the acount value in charunits; now scale up to next multiple. aDest.mCapacity=kDefaultStringSize; while(aDest.mCapacity inline nsresult SVFree( nsStringValueImpl& aDest){ aDest.mRefCount--; if((aDest.mBuffer) && (!aDest.mRefCount)){ delete [] aDest.mBuffer; // nsAllocator::Free(aDest.mBuffer); aDest.mBuffer=0; } return NS_OK; } /** * Realloc memory from given stringvalueimpl * * @update rickg 03.02.2000 * @return memory error (usually returns NS_OK) */ template inline nsresult SVRealloc(nsStringValueImpl&aDest,PRUint32 aCount){ nsStringValueImpl temp(aDest); nsresult result=SVAlloc(temp,aCount); if(NS_SUCCEEDED(result)) { if(aDest.mBuffer) { if(0255) { NS_ASSERTION( (aSource[i] < 256), "data in U+0100-U+FFFF will be lost"); if(track_illegal) { if(track_latin1) { if(aSource[i] & 0xFF80) illegal = PR_TRUE; } else { if(aSource[i] & 0xFF00) illegal = PR_TRUE; } // track_latin1 } // track_illegal break; } } #endif while(aSource inline nsresult SVDelete( nsStringValueImpl& aDest,PRUint32 anOffset,PRInt32 aCount=-1){ nsresult result=NS_OK; PRInt32 theLength =(aCount<0) ? aDest.mLength : aCount; CharType* root_cp = aDest.mBuffer; CharType* null_cp = root_cp+aDest.mLength; CharType* offset_cp = root_cp+anOffset; //dst1 if(offset_cp inline void SVTruncate( nsStringValueImpl& aDest,PRUint32 anOffset=0) { if(anOffset inline nsresult SVAppend( nsStringValueImpl& aDest,const nsStringValueImpl& aSource,PRInt32 aCount=-1,PRUint32 aSrcOffset=0){ nsresult result=NS_OK; if(aSrcOffset aDest.mCapacity) { result=SVRealloc(aDest,aDest.mLength+theLength); } if(NS_SUCCEEDED(result)) { //now append new chars, starting at offset CharType1 *to_cp = aDest.mBuffer+aDest.mLength; const CharType2 *from_cp = aSource.mBuffer+aSrcOffset; SVCopyBuffer_(to_cp,from_cp,theLength); aDest.mLength+=theLength; aDest.mBuffer[aDest.mLength]=0; } } } return result; } /** * This method is used to append content to the given stringvalue from an nsAReadableString * * @update rickg 03.01.2000 * @param aDest is the stringvalue to be modified * @param aSource is the readablestring to be copied from * @param anOffset tells us where in source to start copying * @param aCount tells us the (max) # of chars to copy */ template inline nsresult SVAppendReadable( nsStringValueImpl& aDest,const nsAReadableString& aSource,PRInt32 aCount,PRUint32 anSrcOffset){ nsresult result=NS_OK; /* //I expect a pattern similar to this that could work on any readable string nsAReadableStringIterator theFirst=aSource.First(); nsAReadableStringIterator theLast=aSource.Last(); CharType1 *theDest=aDest.mBuffer; while(theFirst inline nsresult SVAssign(nsStringValueImpl& aDest,const nsStringValueImpl& aSource,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) { nsresult result=NS_OK; if((void*)aDest.mBuffer!=(void*)aSource.mBuffer){ SVTruncate(aDest,0); SVAppend(aDest,aSource,aCount,aSrcOffset); } return result; } /** * These methods are used to insert content from source string to the dest stringvalue * * @update rickg 03.01.2000 * @param aDest is the stringvalue to be modified * @param aDestOffset tells us where in dest to start insertion * @param aSource is the buffer to be copied from * @param aSrcOffset tells us where in source to start copying * @param aCount tells us the (max) # of chars to insert */ template inline nsresult SVInsert( nsStringValueImpl& aDest,PRUint32 aDestOffset,const nsStringValueImpl& aSource,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) { nsresult result=NS_OK; //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) { nsStringValueImpl theTempStr; result=SVAlloc(theTempStr,aDest.mLength+theLength); if(NS_SUCCEEDED(result)) { CharType1 *to_cp=theTempStr.mBuffer; memcpy(to_cp,aDest.mBuffer,aDestOffset*sizeof(CharType1)); //copy front of existing string... to_cp+=aDestOffset; SVCopyBuffer_(to_cp,from_src,theLength); //now insert the new data... PRUint32 theRemains=aDest.mLength-aDestOffset; if(theRemains) { CharType1 *from_dst=to_cp; to_cp+=theLength; memcpy(to_cp,from_dst,theRemains*sizeof(CharType1)); } SVFree(aDest); aDest=theTempStr; theTempStr.mBuffer=0; //make sure to null this out so that you don't lose the buffer you just stole... } } else { //if you're here, it's because we have enough room to insert the new chars into the buffer //that we already possess. CharType1* root_cp = aDest.mBuffer; CharType1* end_cp = root_cp+aDest.mLength; CharType1* offset_cp= root_cp+aDestOffset; CharType1* max_cp = offset_cp+aCount; memmove(max_cp,offset_cp,sizeof(CharType1)*(end_cp-offset_cp)); SVCopyBuffer_(offset_cp,from_src,theLength); //finally, make sure to update the string length... aDest.mLength+=theLength; aDest.mBuffer[aDest.mLength]=0; } }//if //else nothing to do! } else { SVAppend(aDest,aSource,aCount,0); } } else { SVAppend(aDest,aSource,aCount,0); } } return result; } /** * This method compares characters strings of two types, and ignores case * * @update rickg 03.01.2000 * @param aLeft * @param anEnd * @param aTarget * @return aDestaSource=1 */ template inline PRInt32 SVCompareCase_(const CharType1* aLeft,const CharType1* anEnd,const CharType2* aTarget){ while(aLeftaSource=1 */ inline PRInt32 SVCompare_(const char* aLeft,const char* anEnd,const char* aTarget){ size_t theCount=anEnd-aLeft; return memcmp(aLeft,aTarget,theCount); } /** * This method compares characters strings of two types (case sensitive) * * @update rickg 03.01.2000 * @param aLeft * @param anEnd * @param aTarget * @return aDestaSource=1 */ template inline PRInt32 SVCompare_(const CharType1* aLeft,const CharType1* anEnd,const CharType2* aTarget){ while(aLeftaSource=1 */ template inline PRInt32 SVCompare(const nsStringValueImpl& aDest,const nsStringValueImpl& aSource,PRBool aIgnoreCase, PRUint32 aCount){ PRInt32 result=0; if((void*)aDest.mBuffer!=(void*)aSource.mBuffer) { if(aCount) { PRUint32 minlen=(aSource.mLength inline PRInt32 SVCompareChars(const nsStringValueImpl& aLHS,const nsStringValueImpl& aRHS,PRBool aIgnoreCase,PRInt32 aCount) { PRInt32 result=0; if(aLHS.mBuffer!=aRHS.mBuffer) { if(!aIgnoreCase) { PRInt32 aMax = (aLHS.mLength inline PRInt32 SVFindOrig(const nsStringValueImpl& aDest,const nsStringValueImpl& aTarget,PRBool aIgnoreCase,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) { PRUint32 minlen=(aTarget.mLength inline PRInt32 SVFind(const nsStringValueImpl& aDest,const nsStringValueImpl& aTarget,PRBool aIgnoreCase,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) { PRUint32 minlen=(aTarget.mLength PRInt32 SVFindChar(const nsStringValueImpl& aDest,PRUnichar aChar,PRBool aIgnoreCase,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) { if(aSrcOffset<0) aSrcOffset=0; if(aCount<0) aCount = (PRInt32)aDest.mLength; size_t theCharSize=sizeof(CharType); if((2==theCharSize) || (aChar<256)) { if((0 inline PRInt32 SVFindCharInSet(const nsStringValueImpl& aDest,const nsStringValueImpl& aSet,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 anOffset) { if(anOffset<0) anOffset=0; if(aCount<0) aCount = (PRInt32)aDest.mLength; if((0 inline PRInt32 SVRFind(const nsStringValueImpl& aDest,const nsStringValueImpl& aTarget,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 aSrcOffset) { if(aSrcOffset<0) aSrcOffset=(PRInt32)aDest.mLength-1; if(aCount<0) aCount = aSrcOffset; size_t theCharSize=sizeof(CharType1); if((0 PRInt32 SVRFindChar(const nsStringValueImpl& aDest,PRUnichar aChar,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 aSrcOffset) { if(aSrcOffset<0) aSrcOffset=(PRInt32)aDest.mLength-1; if(aCount<0) aCount = aSrcOffset; size_t theCharSize=sizeof(CharType); if((2==theCharSize) || (aChar<256)) { if((0 inline PRInt32 SVRFindCharInSet(const nsStringValueImpl& aDest,const nsStringValueImpl& aSet,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 aSrcOffset) { if(aSrcOffset<0) aSrcOffset=(PRInt32)aDest.mLength-1; if(aCount<0) aCount = aSrcOffset; if((0 nsresult SVTrim(nsStringValueImpl& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing){ nsresult result=NS_OK; if(0 theSet((char*)aSet); if(aEliminateTrailing) { while(root_cp nsresult SVStripCharsInSet(nsStringValueImpl& aDest,const char* aSet,PRInt32 aCount,PRInt32 anOffset) { if((0 theSet((char*)aSet); while(left_cp nsresult SVStripChar( nsStringValueImpl& aDest, CharType aChar,PRInt32 aCount,PRInt32 anOffset) { nsresult result=NS_OK; if(anOffset<(PRInt32)aDest.mLength){ CharType *root_cp=aDest.mBuffer; CharType *theCP=root_cp+anOffset; CharType *end_cp=root_cp+aDest.mLength; CharType *theWriteCP=theCP; while(theCP nsresult SVReplace( nsStringValueImpl &aDest, const nsStringValueImpl &aTarget, const nsStringValueImpl &aNewValue, PRInt32 aRepCount, PRUint32 anOffset) { nsresult result=NS_OK; if((aTarget.mLength) && (aNewValue.mLength)) { nsStringValueImpl theTempString; PRUint32 thePrevOffset=0; PRInt32 thePos=SVFind(aDest,aTarget,PR_FALSE,aDest.mLength,thePrevOffset); while(kNotFound nsresult SVReplaceCharsInSet(nsStringValueImpl& aDest,const char* aSet,PRUnichar aNewChar,PRInt32 aCount,PRUint32 anOffset) { if((0 theSet((char*)aSet); while(offset_cp nsresult SVCompressSet(nsStringValueImpl& aDest,const char* aSet, PRUnichar aChar,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE){ nsresult result=NS_OK; if(0 theSet((char*)aSet); //this code converts /n, /t, /r into normal space ' '; //it also compresses runs of whitespace down to a single char... PRUint32 aSetLen=strlen(aSet); if(aSetLen) { while (root_cp < end_cp) { CharType theChar = *root_cp++; *to_cp++=theChar; //always copy this char if((theChar<256) && (kNotFound void SVToLowerCase(nsStringValueImpl &aString) { CharType *theCP=aString.mBuffer; CharType *end_cp=theCP+aString.mLength; while(theCP= 'A') && (theChar <= 'Z')) { *theCP = 'a' + (theChar - 'A'); } theCP++; } } template void SVToUpperCase(nsStringValueImpl &aString) { CharType *theCP=aString.mBuffer; CharType *end_cp=theCP+aString.mLength; while(theCP= 'a') && (theChar<= 'z')) { *theCP= 'A' + (theChar - 'a'); } theCP++; } } template PRInt32 SVCountChar(const nsStringValueImpl &aString,PRUnichar aChar) { PRInt32 result=0; const CharType* cp=aString.mBuffer; const CharType* end_cp=cp+aString.mLength; while(cp void SVDebugDump(const nsStringValueImpl &aString) { CharType* cp=aString.mBuffer; CharType* end_cp=cp+aString.mLength; char theBuffer[256]={0}; char* outp=theBuffer; char* outendp=&theBuffer[255]; while(cp PRInt32 SVToInteger(const nsStringValueImpl &aString,PRInt32* anErrorCode,PRUint32 aRadix=kAutoDetect) { CharType* cp=aString.mBuffer; 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... CharType* endcp=cp+aString.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; } return result; } #endif