gecko-dev/xpcom/ds/nsBufferManager.h

1556 lines
44 KiB
C++

/*************************************************************************************
*
* MODULES NOTES:
*
* 1. See nsStringImpl.h
*
* Contributor(s):
* Rick Gessner <rickg@netscape.com>
*
* 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 <class CharType>
inline void SVAddNullTerminator( nsStringValueImpl<CharType>& aDest) {
if(aDest.mBuffer) {
aDest.mBuffer[aDest.mLength]=0;
}
}
template <class CharType>
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 <class CharType>
inline nsresult SVAlloc( nsStringValueImpl<CharType>& 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<aCount){
aDest.mCapacity<<=1;
}
aDest.mBuffer = new CharType[1+aDest.mCapacity];
// PRUint32 theSize=(theNewCapacity<<aDest.mCharSize);
// aDest.mBuffer = (char*)nsMemory::Alloc(theSize);
if(aDest.mBuffer) {
aDest.mBuffer[0]=0;
gStringAcquiredMemory=PR_TRUE;
}
else {
gStringAcquiredMemory=PR_FALSE;
result=NS_MEMORY_ERROR;
}
return result;
}
/**
* Free memory from given stringvalueimpl
*
* @update rickg 03.02.2000
* @return memory error (usually returns NS_OK)
*/
template <class CharType>
inline nsresult SVFree( nsStringValueImpl<CharType>& aDest){
aDest.mRefCount--;
if((aDest.mBuffer) && (!aDest.mRefCount)){
delete [] aDest.mBuffer; // nsMemory::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 <class CharType>
inline nsresult SVRealloc(nsStringValueImpl<CharType>&aDest,PRUint32 aCount){
nsStringValueImpl<CharType> temp(aDest);
nsresult result=SVAlloc(temp,aCount);
if(NS_SUCCEEDED(result)) {
if(aDest.mBuffer) {
if(0<aDest.mLength) {
memcpy(temp.mBuffer,aDest.mBuffer,aDest.mLength*sizeof(CharType));
temp.mLength=aDest.mLength;
}
SVFree(aDest);
}
aDest=temp;
aDest.mBuffer[aDest.mLength]=0;
}
return result;
}
/**
* Retrieve last memory error
*
* @update rickg 03.02.2000
* @return memory error (usually returns NS_OK)
*/
static PRBool DidAcquireMemory(void) {
return gStringAcquiredMemory;
}
//----------------------------------------------------------------------------------------
/**
* This is the low-level buffer copying routine between 2 stringvalues.
*
* @update rickg 03.01.2000
* @param aDest is the destination stringvalue
* @param aSource is the source stringvalue (you must account for offset yourself in calling routine)
* @param aCount tells us how many chars to copy from aSource
*/
inline nsresult SVCopyBuffer_( char* aDest, const char* aSource, PRUint32 aCount) {
memcpy(aDest,aSource,aCount);
return NS_OK;
}
inline nsresult SVCopyBuffer_( PRUnichar* aDest, const PRUnichar* aSource, PRUint32 aCount) {
memcpy(aDest,aSource,aCount*sizeof(PRUnichar));
return NS_OK;
}
/**
* This is the low-level buffer copying routine between 2 stringvalues.
*
* @update rickg 03.01.2000
* @param aDest is the destination stringvalue
* @param aSource is the source stringvalue (you must account for offset yourself in calling routine)
* @param aCount tells us how many chars to copy from aSource
*/
inline nsresult SVCopyBuffer_( char* aDest, const PRUnichar* aSource, PRUint32 aCount) {
const PRUnichar* aLast=aSource+aCount;
#ifdef DEBUG_ILLEGAL_CAST_UP
int i=0;
for(i=0;i<aCount;i++){
if(aSource[i]>255) {
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<aLast) {
*aDest++=(char)*aSource++;
}
return NS_OK;
}
inline nsresult SVCopyBuffer_( PRUnichar* aDest, const char* aSource, PRUint32 aCount) {
const char* aLast=aSource+aCount;
while(aSource<aLast) {
*aDest++=(PRUnichar)*aSource++;
}
#ifdef DEBUG_ILLEGAL_CAST_UP
int i=0;
for(i=0;i<aCount;i++){
if(track_illegal && track_latin1 && ((aDest[i])& 0x80)) {
TRACE_ILLEGAL_CAST_UP((!illegal), aSource, "illegal cast up in copying 1-to-2 byte buffers.");
break;
}
}
#endif
return NS_OK;
}
//----------------------------------------------------------------------------------------
/**
* This method deletes chars from the given str.
* The given allocator may choose to resize the str as well.
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param aDestOffset tells us where in dest to start deleting
* @param aCount tells us the (max) # of chars to delete (-1 means delete the rest of the string starting at offset)
*/
template <class CharType>
inline nsresult SVDelete( nsStringValueImpl<CharType>& 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<null_cp) {
//Proceed only if the offset is in range
CharType* max_cp = offset_cp+aCount; //dst2
if(max_cp<null_cp) {
aDest.mLength-=(max_cp-offset_cp);
memmove(offset_cp,max_cp,(null_cp-max_cp)*sizeof(CharType));
}
else aDest.mLength=anOffset;
aDest.mBuffer[aDest.mLength]=0;
}
return result;
}
/**
* This method is used to truncate the given string.
* The given allocator may choose to resize the str as well (but it's not likely).
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param anOffset tells us where in dest to truncate
*/
template <class CharType>
inline void SVTruncate( nsStringValueImpl<CharType>& aDest,PRUint32 anOffset=0) {
if(anOffset<aDest.mLength){
aDest.mLength=anOffset;
aDest.mBuffer[aDest.mLength]=0;
}
}
/**
* These methods are used to append content to the given stringvalue
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param aSource is the buffer to be copied from
* @param anOffset tells us where in source to start copying
* @param aCount tells us the (max) # of chars to append
*/
template <class CharType1, class CharType2>
inline nsresult SVAppend( nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aSource,PRInt32 aCount=-1,PRUint32 aSrcOffset=0){
nsresult result=NS_OK;
if(aSrcOffset<aSource.mLength){
PRInt32 theRealLen=(aCount<0) ? aSource.mLength : (aCount<(PRInt32)aSource.mLength) ? aCount : aSource.mLength;
PRInt32 theLength=(aSrcOffset+theRealLen<(PRInt32)aSource.mLength) ? theRealLen : (aSource.mLength-aSrcOffset);
if(0<theLength){
PRBool isBigEnough=PR_TRUE;
if(aDest.mLength+theLength > 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 <class CharType1>
inline nsresult SVAppendReadable( nsStringValueImpl<CharType1>& 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<theLast) {
*theDest++=theFirst++;
}
*/
return result;
}
/**
* These methods are used to assign contents of a source string to dest string
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param aSource is the buffer 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 <class CharType1, class CharType2>
inline nsresult SVAssign(nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& 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 <class CharType1, class CharType2>
inline nsresult SVInsert( nsStringValueImpl<CharType1>& aDest,PRUint32 aDestOffset,const nsStringValueImpl<CharType2>& 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<aSource.mLength){
if(aDest.mLength){
if(aDestOffset<aDest.mLength){
PRUint32 theRealLen=(aCount<0) ? aSource.mLength : (aCount<(PRInt32)aSource.mLength) ? aCount : aSource.mLength;
PRUint32 theLength=(aSrcOffset+theRealLen<aSource.mLength) ? theRealLen : (aSource.mLength-aSrcOffset);
if(aSrcOffset<(PRInt32)aSource.mLength) {
//here's the only new case we have to handle.
//chars are really being inserted into our buffer...
CharType2 *from_src=aSource.mBuffer+aSrcOffset;
if(aDest.mLength+theLength > aDest.mCapacity) {
nsStringValueImpl<CharType1> 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 aDest<aSource=-1;aDest==aSource==0;aDest>aSource=1
*/
template <class CharType1,class CharType2>
inline PRInt32 SVCompareCase_(const CharType1* aLeft,const CharType1* anEnd,const CharType2* aTarget){
while(aLeft<anEnd){
PRUnichar theChar=(PRUnichar)ToLower(*aLeft++);
PRUnichar theTargetChar=(PRUnichar)ToLower(*aTarget++);
if(theChar<theTargetChar)
return -1;
else if(theTargetChar<theChar)
return 1;
} //while
return 0;
}
/**
* This method compares characters strings of two types (case sensitive)
*
* @update rickg 03.01.2000
* @param aLeft
* @param anEnd
* @param aTarget
* @return aDest<aSource=-1;aDest==aSource==0;aDest>aSource=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 aDest<aSource=-1;aDest==aSource==0;aDest>aSource=1
*/
template <class CharType1,class CharType2>
inline PRInt32 SVCompare_(const CharType1* aLeft,const CharType1* anEnd,const CharType2* aTarget){
while(aLeft<anEnd){
CharType1 theChar=*aLeft++;
CharType2 theTargetChar=*aTarget++;
if(theChar<theTargetChar)
return -1;
else if(theTargetChar<theChar)
return 1;
} //while
return 0;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSource
* @param aIgnoreCase
* @param aCount --- tells us how many iterations to make.
* @return aDest<aSource=-1;aDest==aSource==0;aDest>aSource=1
*/
template <class CharType1,class CharType2>
inline PRInt32 SVCompare(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aSource,PRBool aIgnoreCase, PRUint32 aCount){
PRInt32 result=0;
if((void*)aDest.mBuffer!=(void*)aSource.mBuffer) {
if(aCount) {
PRUint32 minlen=(aSource.mLength<aDest.mLength) ? aSource.mLength : aDest.mLength;
if(0==minlen) {
if ((aDest.mLength == 0) && (aSource.mLength == 0))
return 0;
if (aDest.mLength == 0)
return -1;
return 1;
}
PRUint32 theCount = (aCount<0) ? minlen: MinInt(aCount,minlen);
const CharType1* root_cp = aDest.mBuffer;
const CharType1* last_cp = root_cp+theCount;
const CharType1* null_cp = root_cp+aDest.mLength;
const CharType1* end_cp = (last_cp<null_cp) ? last_cp : null_cp;
const CharType2* target = aSource.mBuffer;
if(aIgnoreCase)
result = SVCompareCase_(root_cp,end_cp,target) ;
else result =SVCompare_(root_cp,end_cp,target);
if (0==result) {
if(-1==aCount) {
//Since the caller didn't give us a length to test, and minlen characters matched,
//we have to assume that the longer string is greater.
if (aDest.mLength != aSource.mLength) {
//we think they match, but we've only compared minlen characters.
//if the string lengths are different, then they don't really match.
result = (aDest.mLength<aSource.mLength) ? -1 : 1;
}
} //if
}
}
}
return result;
}
template <class CharType>
inline PRInt32 SVCompareChars(const nsStringValueImpl<CharType>& aLHS,const nsStringValueImpl<CharType>& aRHS,PRBool aIgnoreCase,PRInt32 aCount) {
PRInt32 result=0;
if(aLHS.mBuffer!=aRHS.mBuffer) {
if(!aIgnoreCase) {
PRInt32 aMax = (aLHS.mLength<aRHS.mLength) ? aLHS.mLength : aRHS.mLength;
if((0<aCount) && (aCount<aMax))
aMax=aCount;
result=memcmp(aLHS.mBuffer,aRHS.mBuffer,aMax*sizeof(CharType));
if(0==result) {
if(-1==aCount) {
if (aLHS.mLength != aRHS.mLength) {
result = (aLHS.mLength<aRHS.mLength) ? -1 : 1;
}
} //if
} //if
}
else result=SVCompare(aLHS,aRHS,aIgnoreCase,aCount);
}
return result;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aTarget
* @param aIgnoreCase
* @param aCount : tells us how many iterations to make, starting at offset
* @param aOffset: tells us where to start searching within aDest
* @return nothing
*/
template <class CharType1,class CharType2>
inline PRInt32 SVFindOrig(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aTarget,PRBool aIgnoreCase,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) {
PRUint32 minlen=(aTarget.mLength<aDest.mLength) ? aTarget.mLength : aDest.mLength;
aCount = (-1==aCount) ? aDest.mLength-minlen+1 : aCount;
if((0<aCount) && minlen) {
const CharType1 *root_cp = aDest.mBuffer;
const CharType1 *offset_cp = root_cp+aSrcOffset;
const CharType1 *null_cp = root_cp+aDest.mLength;
if(offset_cp<null_cp) {
PRInt32 cmp=0;
const CharType1* last_cp = offset_cp+aCount;
const CharType1* end_cp = (last_cp<null_cp) ? last_cp : null_cp;
if(aIgnoreCase) {
while(offset_cp<=end_cp) {
cmp = SVCompareCase_(offset_cp,offset_cp+minlen,aTarget.mBuffer) ;
if(0==cmp) {
return offset_cp-root_cp;
}
offset_cp++;
}
}
else {
while(offset_cp<=null_cp) {
cmp=SVCompare_(offset_cp,offset_cp+minlen,aTarget.mBuffer);
if(0==cmp) {
return offset_cp-root_cp;
}
offset_cp++;
}
}
}
}
return kNotFound;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aTarget
* @param aIgnoreCase
* @param aCount : tells us how many iterations to make, starting at offset
* @param aOffset: tells us where to start searching within aDest
* @return nothing
*/
template <class CharType1,class CharType2>
inline PRInt32 SVFind(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aTarget,PRBool aIgnoreCase,PRInt32 aCount=-1,PRUint32 aSrcOffset=0) {
PRUint32 minlen=(aTarget.mLength<aDest.mLength) ? aTarget.mLength : aDest.mLength;
aCount = (-1==aCount) ? aDest.mLength-minlen+1 : aCount;
if((0<aCount) && minlen) {
const CharType1* root_cp = aDest.mBuffer;
const CharType1* offset_cp = root_cp+aSrcOffset;
const CharType1* null_cp = root_cp+aDest.mLength;
if(offset_cp<null_cp) {
PRInt32 cmp=0;
const CharType1* last_cp = offset_cp+aCount;
const CharType1* end_cp = (last_cp<null_cp) ? last_cp : null_cp;
if(aIgnoreCase) {
while(offset_cp<=end_cp) {
cmp = SVCompareCase_(offset_cp,offset_cp+minlen,aTarget.mBuffer) ;
if(0==cmp) {
return offset_cp-root_cp;
}
offset_cp++;
}
}
else {
//XXX Hack alert. I'm only playing around here with a lookaside buffer until I can get around to implementing
// a NMP search suitable for unicode chars.
const CharType1* theLATable[256];
PRInt32 theLACount=0;
const CharType1 *leftLA=offset_cp;
const CharType1 *endLA=end_cp;
const CharType1 *lastLA=last_cp;
const CharType2 targetFirst=aTarget.mBuffer[0];
const CharType2 targetLast=aTarget.mBuffer[aTarget.mLength-1];
size_t countLA=aTarget.mLength-1;
while(leftLA<=endLA) {
if((*leftLA==targetFirst) && (*(leftLA+countLA)==targetLast)) {
theLATable[theLACount++]=leftLA;
}
leftLA++;
}
theLATable[theLACount]=0;
PRInt32 theLAIndex=0;
while(offset_cp=theLATable[theLAIndex++]) {
cmp =SVCompare_(offset_cp,offset_cp+minlen,aTarget.mBuffer);
if(0==cmp) {
return offset_cp-root_cp;
}
} //while
}
}
}
return kNotFound;
}
/**
* This searches for the given char withing the given string
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.02.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType>
PRInt32 SVFindChar(const nsStringValueImpl<CharType>& 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<aDest.mLength) && ((PRUint32)aSrcOffset<aDest.mLength)) {
//We'll only search if the given aChar is within the normal ascii a range,
//(Since this string is definitely within the ascii range).
if(0<aCount) {
const CharType* root_cp = aDest.mBuffer;
const CharType* offset_cp = root_cp+aSrcOffset;
const CharType* last_cp = offset_cp+aCount;
const CharType* max_cp = offset_cp+aDest.mLength;
const CharType* end_cp = (last_cp<max_cp) ? last_cp : max_cp;
if(aIgnoreCase) {
PRUnichar theChar=ToLower(aChar);
while(offset_cp<end_cp){
if(ToLower(*offset_cp)==theChar)
return offset_cp-root_cp;
++offset_cp;
}
}
else if (1==theCharSize){
PRInt32 theMax = end_cp-offset_cp;
if(0<theMax) {
CharType theChar=(CharType)aChar;
const char* result=(const char*)memchr(offset_cp, theChar, theMax);
if(result) {
return result-(char*)root_cp;
}
}
}
else {
while(offset_cp<end_cp){
if(*offset_cp==aChar)
return (offset_cp-root_cp);
++offset_cp;
} //while
} //else
}
} //if
}//if
return kNotFound;
}
/**
* This searches for the given char withing the given string
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.02.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType1,class CharType2>
inline PRInt32 SVFindCharInSet(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aSet,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 anOffset) {
if(anOffset<0)
anOffset=0;
if(aCount<0)
aCount = (PRInt32)aDest.mLength;
if((0<aCount) && (0<aSet.mLength)){
const CharType1* root_cp = aDest.mBuffer;
const CharType1* offset_cp = root_cp+anOffset;
const CharType1* last_cp = offset_cp+aCount;
const CharType1* nullcp = root_cp+aDest.mLength;
const CharType1* end_cp = (last_cp<nullcp) ? last_cp : nullcp;
PRInt32 cmp=0;
while(offset_cp<=end_cp) {
PRUnichar theChar=(PRUnichar)*offset_cp;
PRInt32 thePos=SVFindChar(aSet,theChar,aIgnoreCase,aSet.mLength,0);
if(kNotFound<thePos) {
return offset_cp-root_cp;
}
offset_cp++;
}
}
return kNotFound;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType1,class CharType2>
inline PRInt32 SVRFind(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& 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<aTarget.mLength) && (0<aDest.mLength) && ((PRUint32)aSrcOffset<aDest.mLength)) {
//We'll only search if the given aChar is within the normal ascii a range,
//(Since this string is definitely within the ascii range).
if(0<aCount) {
const CharType1* root_cp = aDest.mBuffer;
const CharType1* first_cp = root_cp+aSrcOffset;
const CharType1* last_cp = first_cp+aTarget.mLength;
const CharType1* min_cp = first_cp-aCount+1;
const CharType1* left_cp = (min_cp<root_cp) ? root_cp : min_cp;
PRInt32 cmp=0;
while(left_cp<=first_cp) {
if(aIgnoreCase)
cmp = SVCompareCase_(first_cp,last_cp,aTarget.mBuffer) ;
else cmp =SVCompare_(first_cp,last_cp,aTarget.mBuffer);
if(0==cmp) {
return first_cp-root_cp;
}
first_cp--;
last_cp--;
}
}
}
return kNotFound;
}
/**
* This methods cans the given buffer (in reverse) for the given char
*
* @update gess 02/17/00
* @param aDest is the buffer to be searched
* @param anOffset is the start pos to begin searching
* @param aChar is the target character we're looking for
* @param aIgnorecase tells us whether to use a case sensitive search
* @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
* @return index of pos if found, else -1 (kNotFound)
*/
template <class CharType>
PRInt32 SVRFindChar(const nsStringValueImpl<CharType>& 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<aDest.mLength) && ((PRUint32)aSrcOffset<aDest.mLength)) {
//We'll only search if the given aChar is within the normal ascii a range,
//(Since this string is definitely within the ascii range).
if(0<aCount) {
const CharType* root_cp = aDest.mBuffer;
const CharType* right_cp = root_cp+aSrcOffset;
const CharType* min_cp = right_cp-aCount+1;
const CharType* left_cp = (min_cp<root_cp) ? root_cp : min_cp;
if(aIgnoreCase) {
PRUnichar theChar=ToLower(aChar);
while(left_cp<=right_cp){
if(ToLower(*right_cp)==theChar)
return right_cp-root_cp;
--right_cp;
}
}
else {
CharType theChar=(CharType)aChar;
while(left_cp<=right_cp){
if((*right_cp)==theChar)
return right_cp-root_cp;
--right_cp;
}
}
}
}
}
return kNotFound;
}
/**
* This methods cans the given buffer (in reverse) for the given char
*
* @update gess 02/17/00
* @param aDest is the buffer to be searched
* @param anOffset is the start pos to begin searching
* @param aChar is the target character we're looking for
* @param aIgnorecase tells us whether to use a case sensitive search
* @param aCount tells us how many characters to iterate through (which may be different than aLength); -1 means use full length.
* @param aSrcOffset
* @return index of pos if found, else -1 (kNotFound)
*/
template <class CharType1,class CharType2>
inline PRInt32 SVRFindCharInSet(const nsStringValueImpl<CharType1>& aDest,const nsStringValueImpl<CharType2>& aSet,PRBool aIgnoreCase,PRInt32 aCount,PRInt32 aSrcOffset) {
if(aSrcOffset<0)
aSrcOffset=(PRInt32)aDest.mLength-1;
if(aCount<0)
aCount = aSrcOffset;
if((0<aDest.mLength) && ((PRUint32)aSrcOffset<aDest.mLength)) {
//We'll only search if the given aChar is within the normal ascii a range,
//(Since this string is definitely within the ascii range).
if(0<aCount) {
const CharType1* root_cp = aDest.mBuffer;
const CharType1* right_cp = root_cp+aSrcOffset;
while(root_cp<=--right_cp){
CharType1 theChar=(aIgnoreCase) ? ToLower(*right_cp) : *right_cp;
PRInt32 thePos=SVFindChar(aSet,theChar,aIgnoreCase,aSet.mLength,0);
if(kNotFound<thePos) {
return right_cp-root_cp;
}
}
}
}
return kNotFound;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType>
nsresult SVTrim(nsStringValueImpl<CharType>& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing){
nsresult result=NS_OK;
if(0<aDest.mLength) {
CharType *root_cp = aDest.mBuffer;
CharType *end_cp = root_cp+aDest.mLength;
nsStringValueImpl<char> theSet((char*)aSet);
if(aEliminateTrailing) {
while(root_cp<end_cp) {
CharType theChar=*(end_cp-1);
PRInt32 thePos=SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0);
if(kNotFound==thePos)
break;
--end_cp;
}
aDest.mLength=(end_cp-root_cp);
aDest.mBuffer[aDest.mLength]=0;
}
CharType *cp = root_cp-1;
if(aEliminateLeading) {
while(++cp<end_cp) {
CharType theChar=*cp;
PRInt32 thePos=SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0);
if(kNotFound==thePos)
break;
}
size_t theCount=cp-root_cp;
if(0<theCount) {
result=SVDelete(aDest,0,theCount);
}
}
}
return result;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType>
nsresult SVStripCharsInSet(nsStringValueImpl<CharType>& aDest,const char* aSet,PRInt32 aCount,PRInt32 anOffset) {
if((0<aDest.mLength) && aSet && (anOffset<(PRInt32)aDest.mLength)){
CharType *left_cp = aDest.mBuffer+anOffset;
CharType *theWriteCP = left_cp;
const CharType *last_cp= left_cp+aCount;
const CharType *max_cp = left_cp+aDest.mLength;
const CharType *end_cp = (last_cp<max_cp) ? last_cp : max_cp;
nsStringValueImpl<char> theSet((char*)aSet);
while(left_cp<end_cp){
CharType theChar=*left_cp;
PRInt32 thePos=SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0);
if(kNotFound==thePos) {
*theWriteCP++=*left_cp;
}
++left_cp;
} //while
*theWriteCP=0;
aDest.mLength-=(left_cp-theWriteCP);
} //if
return NS_OK;
}
/**
* This method removes a given char the underlying stringvalue
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param anOffset tells us where in source to start copying
* @param aCount tells us the (max) # of chars to copy
*/
template <class CharType>
nsresult SVStripChar( nsStringValueImpl<CharType>& 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<end_cp) {
if(*theCP!=aChar) {
*theWriteCP++=*theCP;
}
theCP++;
}
*theWriteCP=0;
aDest.mLength-=(theCP-theWriteCP);
}
return result;
}
/**
* This method removes a given char the underlying stringvalue
*
* @update rickg 03.01.2000
* @param aDest is the stringvalue to be modified
* @param anOffset tells us where in source to start copying
* @param aRepCount tells us the (max) # of iterations
*/
template <class CharType1,class CharType2>
nsresult SVReplace( nsStringValueImpl<CharType1> &aDest,
const nsStringValueImpl<CharType2> &aTarget,
const nsStringValueImpl<CharType2> &aNewValue,
PRInt32 aRepCount,
PRUint32 anOffset) {
nsresult result=NS_OK;
if((aTarget.mLength) && (aNewValue.mLength)) {
nsStringValueImpl<CharType1> theTempString;
PRUint32 thePrevOffset=0;
PRInt32 thePos=SVFind(aDest,aTarget,PR_FALSE,aDest.mLength,thePrevOffset);
while(kNotFound<thePos) {
if(0<thePos) {
SVAppend(theTempString,aDest,thePos-thePrevOffset,thePrevOffset);
}
SVAppend(theTempString,aNewValue,aNewValue.mLength,0);
thePrevOffset=thePos+aTarget.mLength;
thePos=SVFind(aDest,aTarget,PR_FALSE,aDest.mLength-thePrevOffset,thePrevOffset);
}
if(thePrevOffset<aDest.mLength) {
SVAppend(theTempString,aDest,aDest.mLength-thePrevOffset,thePrevOffset);
}
SVFree(aDest);
aDest=theTempString;
theTempString.mBuffer=0;
}
return result;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSet
* @param aNewChar
* @param aCount
* @param anoffset
* @return nothing
*/
template <class CharType>
nsresult SVReplaceCharsInSet(nsStringValueImpl<CharType>& aDest,const char* aSet,PRUnichar aNewChar,PRInt32 aCount,PRUint32 anOffset) {
if((0<aDest.mLength) && (anOffset<aDest.mLength)){
PRInt32 theCount = (-1==aCount) ? aDest.mLength : aCount;
CharType *root_cp=aDest.mBuffer;
CharType *offset_cp=root_cp+anOffset; //was theCP
CharType *last_cp=offset_cp+aCount;
CharType *null_cp=root_cp+theCount;
CharType *end_cp=(last_cp<null_cp) ? last_cp : null_cp;
nsStringValueImpl<char> theSet((char*)aSet);
while(offset_cp<end_cp){
CharType theChar=*offset_cp;
PRInt32 thePos=SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0);
if(kNotFound!=thePos) {
*offset_cp=(CharType)aNewChar;
}
++offset_cp;
} //while
} //if
return NS_OK;
}
/**
* This method removes characters from the given set from this string.
* NOTE: aSet is a char*, and it's length is computed using strlen, which assumes null termination.
*
* @update rickg 03.01.2000
* @param aDest
* @param aSet
* @param aEliminateLeading
* @param aEliminateTrailing
* @return nothing
*/
template <class CharType>
nsresult SVCompressSet(nsStringValueImpl<CharType>& aDest,const char* aSet, PRUnichar aChar,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE){
nsresult result=NS_OK;
if(0<aDest.mLength){
result=SVReplaceCharsInSet(aDest, aSet, aChar, aDest.mLength,0);
SVTrim(aDest,aSet,aEliminateLeading,aEliminateTrailing);
CharType *root_cp = aDest.mBuffer;
CharType *end_cp = root_cp + aDest.mLength;
CharType *to_cp = root_cp;
nsStringValueImpl<char> 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<SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0))){
while (root_cp <= end_cp) {
theChar = *root_cp++;
PRInt32 thePos=SVFindChar(theSet,theChar,PR_FALSE,theSet.mLength,0);
if(kNotFound==thePos){
*to_cp++ = theChar;
break;
}
}
}
} //while
*to_cp = 0;
aDest.mLength = to_cp - (CharType*)aDest.mBuffer;
}
}
return result;
}
template <class CharType>
void SVToLowerCase(nsStringValueImpl<CharType> &aString) {
CharType *theCP=aString.mBuffer;
CharType *end_cp=theCP+aString.mLength;
while(theCP<end_cp) {
CharType theChar=*theCP;
if ((theChar >= 'A') && (theChar <= 'Z')) {
*theCP = 'a' + (theChar - 'A');
}
theCP++;
}
}
template <class CharType>
void SVToUpperCase(nsStringValueImpl<CharType> &aString) {
CharType *theCP=aString.mBuffer;
CharType *end_cp=theCP+aString.mLength;
while(theCP<end_cp) {
CharType theChar=*theCP;
if ((theChar>= 'a') && (theChar<= 'z')) {
*theCP= 'A' + (theChar - 'a');
}
theCP++;
}
}
template <class CharType>
PRInt32 SVCountChar(const nsStringValueImpl<CharType> &aString,PRUnichar aChar) {
PRInt32 result=0;
const CharType* cp=aString.mBuffer;
const CharType* end_cp=cp+aString.mLength;
while(cp<end_cp) {
if(*cp==aChar) {
result++;
}
cp++;
}
return result;
}
//This will not work correctly for any unicode set other than ascii
template <class CharType>
void SVDebugDump(const nsStringValueImpl<CharType> &aString) {
CharType* cp=aString.mBuffer;
CharType* end_cp=cp+aString.mLength;
char theBuffer[256]={0};
char* outp=theBuffer;
char* outendp=&theBuffer[255];
while(cp<end_cp) {
if(outp==outendp) {
*outp=0; //write null
printf("%s",theBuffer);
outp=theBuffer;
cp--;
}
else {
*outp++=(char)*cp;
}
cp++;
} //while
*outp=0; //force a null
printf("%s",theBuffer);
}
/**
* Perform string to int conversion.
* @param aErrorCode will contain error if one occurs
* @param aRadix tells us which radix to assume; kAutoDetect tells us to determine the radix for you.
* @return int rep of string value, and possible (out) error code
*/
template <class CharType>
PRInt32 SVToInteger(const nsStringValueImpl<CharType> &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<endcp) && (!done)){
theChar=*cp;
switch(*cp++) {
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
theRadix=16;
done=PR_TRUE;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
done=PR_TRUE;
break;
case '-':
negate=PR_TRUE; //fall through...
break;
case 'X': case 'x':
theRadix=16;
break;
default:
break;
} //switch
}
theRadix = (kAutoDetect==aRadix) ? theRadix : aRadix;
*anErrorCode = NS_OK;
//now iterate the numeric chars and build our result
CharType* first=--cp; //in case we have to back up.
while(cp<endcp){
theChar=*cp++;
if(('0'<=theChar) && (theChar<='9')){
result = (theRadix * result) + (theChar-'0');
}
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((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