From 6164595e54431fec0daeae0b7bec5fdf590092ef Mon Sep 17 00:00:00 2001 From: "dp%netscape.com" Date: Thu, 7 Mar 2002 15:43:12 +0000 Subject: [PATCH] (again) bug 116023 Parser recycling buffers. Protects all cases of usage of mCurrentPosition without checking mSlidingBuffer. r=harishd, sr=sfraser, a=asa --- htmlparser/src/nsParserModule.cpp | 3 + htmlparser/src/nsScanner.cpp | 136 +++++++++++++++++------ htmlparser/src/nsScanner.h | 24 +++- parser/htmlparser/src/nsParserModule.cpp | 3 + parser/htmlparser/src/nsScanner.cpp | 136 +++++++++++++++++------ parser/htmlparser/src/nsScanner.h | 24 +++- 6 files changed, 252 insertions(+), 74 deletions(-) diff --git a/htmlparser/src/nsParserModule.cpp b/htmlparser/src/nsParserModule.cpp index 83f7cecbb6de..4df5f75a3770 100644 --- a/htmlparser/src/nsParserModule.cpp +++ b/htmlparser/src/nsParserModule.cpp @@ -53,6 +53,7 @@ //#include "nsTextTokenizer.h" #include "nsElementTable.h" #include "nsParserService.h" +#include "nsScanner.h" #ifdef NS_DEBUG #include "nsLoggingSink.h" @@ -118,6 +119,8 @@ Shutdown(nsIModule* aSelf) DeleteElementTable(); CNewlineToken::FreeNewline(); gInitialized = PR_FALSE; + delete nsScanner::gAllocator; + nsScanner::gAllocator = nsnull; } } diff --git a/htmlparser/src/nsScanner.cpp b/htmlparser/src/nsScanner.cpp index d6b171d78494..345d1a8e1a1d 100644 --- a/htmlparser/src/nsScanner.cpp +++ b/htmlparser/src/nsScanner.cpp @@ -46,9 +46,48 @@ #include "nsFileSpec.h" #include "nsReadableUtils.h" -nsScannerString::nsScannerString(PRUnichar* aStorageStart, - PRUnichar* aDataEnd, - PRUnichar* aStorageEnd) : nsSlidingString(aStorageStart, aDataEnd, aStorageEnd) +nsRecyclingAllocator* nsScanner::gAllocator = nsnull; + +static void nsScannerRecycle(void *aPtr, void *aClientData) +{ + nsScanner::gAllocator->Free(aPtr); +} + +static void *nsScannerAllocate(PRUint32 size) +{ + return nsScanner::gAllocator->Malloc(size); +} + +/** + * Helper funciton to convert raw 8bit char * to unicode + * + * aStr : input 8bit string + * aLen : length of input string aStr + * aDest : Destination unicode buffer. Null terminated. + * aDestLength : size of destination buffer. Must be atleast aLen+1. + * + * return 0 if conversion succeeded + * returns -1, if there is not enough space in destination buffer, + */ +static int RawToUnicode(const char *aStr, PRInt32 aLen, PRUnichar *aDest, PRUint32 aDestLength) +{ + // Alert if someone if passing null buffers to us + NS_ASSERTION(aStr && aDest, "Null buffers"); + + // Error checking + if (!aStr || !aDest || (PRInt32)aDestLength < aLen+1) + return -1; + + while (*aStr) + *aDest++ = (PRUnichar) (unsigned char) *aStr++; + *aDest = 0; + return 0; +} + + +nsScannerString::nsScannerString(PRUnichar* aStorageStart, PRUnichar* aDataEnd, + PRUnichar* aStorageEnd, nsFreeProc *aFreeProc) + : nsSlidingString(aStorageStart, aDataEnd, aStorageEnd, aFreeProc) { } @@ -107,14 +146,17 @@ MOZ_DECL_CTOR_COUNTER(nsScanner) nsScanner::nsScanner(const nsAString& anHTMLString, const nsString& aCharset, PRInt32 aSource) { MOZ_COUNT_CTOR(nsScanner); - - PRUnichar* buffer = ToNewUnicode(anHTMLString); - mTotalRead = anHTMLString.Length(); + InitAllocator(); mSlidingBuffer = nsnull; + mTotalRead = anHTMLString.Length(); + if (mTotalRead) { + // Append to buffer will take care of initializing mCurrentPosition + // and mMarkPosition. If we didnt get here, mCurrentPosition and + // mMarkPosition would be uninitialized and hence we protect using these + // variables with if (mSlidingBuffer) checks all over the code. + AppendToBuffer(anHTMLString); + } mCountRemaining = 0; - AppendToBuffer(buffer, buffer+mTotalRead, buffer+mTotalRead); - mSlidingBuffer->BeginReading(mCurrentPosition); - mMarkPosition = mCurrentPosition; mIncremental=PR_FALSE; mOwnsStream=PR_FALSE; mInputStream=0; @@ -137,7 +179,7 @@ nsScanner::nsScanner(nsString& aFilename,PRBool aCreateStream, const nsString& a mFilename(aFilename) { MOZ_COUNT_CTOR(nsScanner); - + InitAllocator(); mSlidingBuffer = nsnull; mIncremental=PR_TRUE; mCountRemaining = 0; @@ -166,7 +208,7 @@ nsScanner::nsScanner(const nsAString& aFilename,nsInputStream& aStream,const nsS mFilename(aFilename) { MOZ_COUNT_CTOR(nsScanner); - + InitAllocator(); mSlidingBuffer = nsnull; mIncremental=PR_FALSE; mCountRemaining = 0; @@ -262,8 +304,10 @@ nsScanner::~nsScanner() { * @return */ void nsScanner::RewindToMark(void){ - mCountRemaining += (Distance(mMarkPosition, mCurrentPosition)); - mCurrentPosition = mMarkPosition; + if (mSlidingBuffer) { + mCountRemaining += (Distance(mMarkPosition, mCurrentPosition)); + mCurrentPosition = mMarkPosition; + } } @@ -293,9 +337,15 @@ void nsScanner::Mark() { */ PRBool nsScanner::UngetReadable(const nsAReadableString& aBuffer) { - mSlidingBuffer->UngetReadable(aBuffer,mCurrentPosition); - mSlidingBuffer->BeginReading(mCurrentPosition); // Insertion invalidated our iterators - mSlidingBuffer->EndReading(mEndPosition); + if (!mSlidingBuffer) { + // This happens to be the first buffer. Just append it. + AppendToBuffer(aBuffer); + } + else { + mSlidingBuffer->UngetReadable(aBuffer,mCurrentPosition); + mSlidingBuffer->BeginReading(mCurrentPosition); // Insertion invalidated our iterators + mSlidingBuffer->EndReading(mEndPosition); + } mTotalRead += aBuffer.Length(); return PR_TRUE; @@ -309,12 +359,8 @@ PRBool nsScanner::UngetReadable(const nsAReadableString& aBuffer) { * @return error code */ nsresult nsScanner::Append(const nsAReadableString& aBuffer) { - - PRUnichar* buffer = ToNewUnicode(aBuffer); - PRUint32 bufLen = aBuffer.Length(); - mTotalRead += bufLen; - - AppendToBuffer(buffer, buffer+bufLen, buffer+bufLen); + AppendToBuffer(aBuffer); + mTotalRead += aBuffer.Length(); return NS_OK; } @@ -332,8 +378,8 @@ nsresult nsScanner::Append(const char* aBuffer, PRUint32 aLen){ if(mUnicodeDecoder) { PRInt32 unicharBufLen = 0; mUnicodeDecoder->GetMaxLength(aBuffer, aLen, &unicharBufLen); - start = unichars = (PRUnichar*)nsMemory::Alloc((unicharBufLen+1) * sizeof(PRUnichar)); - NS_ENSURE_TRUE(unichars,NS_ERROR_OUT_OF_MEMORY); + start = unichars = (PRUnichar*) nsScannerAllocate((unicharBufLen+1) * sizeof(PRUnichar)); + NS_ENSURE_TRUE(unichars, NS_ERROR_OUT_OF_MEMORY); PRInt32 totalChars = 0; PRInt32 unicharLength = unicharBufLen; @@ -375,9 +421,7 @@ nsresult nsScanner::Append(const char* aBuffer, PRUint32 aLen){ res = NS_OK; } else { - nsDependentCString str(aBuffer, aLen); - unichars = ToNewUnicode(str); - AppendToBuffer(unichars, unichars+aLen, unichars+aLen); + AppendToBuffer(aBuffer, aLen); mTotalRead+=aLen; } @@ -411,23 +455,18 @@ nsresult nsScanner::FillBuffer(void) { } else { PRInt32 numread=0; - char* buf = new char[kBufsize+1]; + char buf[kBufsize+1]; buf[kBufsize]=0; if(mInputStream) { numread = mInputStream->read(buf, kBufsize); - if (0 == numread) { - delete [] buf; + if (0 == numread) return kEOF; - } } if((0& aPosition) { + NS_ASSERTION(mSlidingBuffer, "No data yet. CurrentPosition in invalid"); aPosition = mCurrentPosition; } void nsScanner::EndReading(nsReadingIterator& aPosition) { + NS_ASSERTION(mSlidingBuffer, "No data yet. EndPosition in invalid"); aPosition = mEndPosition; } @@ -1392,12 +1433,32 @@ void nsScanner::ReplaceCharacter(nsReadingIterator& aPosition, } } +void nsScanner::AppendToBuffer(const nsAString &aData) +{ + PRUint32 len = aData.Length(); + PRUnichar* buffer = (PRUnichar *) nsScannerAllocate(len * sizeof(PRUnichar)); + if (buffer) { + CopyUnicodeTo(aData, 0, buffer, len); + AppendToBuffer(buffer, buffer+len, buffer+len); + } +} + +void nsScanner::AppendToBuffer(const char *aData, PRUint32 aLen) +{ + PRUnichar *buffer = (PRUnichar*) nsScannerAllocate((aLen+1) * sizeof(PRUnichar)); + if (buffer) + { + RawToUnicode(aData, aLen, buffer, aLen+1); + AppendToBuffer(buffer, buffer+aLen, buffer+aLen); + } +} + void nsScanner::AppendToBuffer(PRUnichar* aStorageStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd) { if (!mSlidingBuffer) { - mSlidingBuffer = new nsScannerString(aStorageStart, aDataEnd, aStorageEnd); + mSlidingBuffer = new nsScannerString(aStorageStart, aDataEnd, aStorageEnd, nsScannerRecycle); mSlidingBuffer->BeginReading(mCurrentPosition); mMarkPosition = mCurrentPosition; mSlidingBuffer->EndReading(mEndPosition); @@ -1422,6 +1483,9 @@ void nsScanner::AppendToBuffer(PRUnichar* aStorageStart, * @return nada */ void nsScanner::CopyUnusedData(nsString& aCopyBuffer) { + if (!mSlidingBuffer) + return; + nsReadingIterator start, end; start = mCurrentPosition; end = mEndPosition; diff --git a/htmlparser/src/nsScanner.h b/htmlparser/src/nsScanner.h index 923d01e7bf5b..ce15a1d02ba4 100644 --- a/htmlparser/src/nsScanner.h +++ b/htmlparser/src/nsScanner.h @@ -58,12 +58,17 @@ #include "nsIUnicodeDecoder.h" #include "nsFileStream.h" #include "nsSlidingString.h" +#include "nsRecyclingAllocator.h" + +// Recycling memory used by scanner +#define NS_SCANNER_BUCKETS 10 class nsScannerString : public nsSlidingString { public: nsScannerString(PRUnichar* aStorageStart, PRUnichar* aDataEnd, - PRUnichar* aStorageEnd); + PRUnichar* aStorageEnd, + nsFreeProc* aFreeProc); virtual void UngetReadable(const nsAReadableString& aReadable, const nsReadingIterator& aCurrentPosition) { InsertReadable(aReadable,aCurrentPosition); } virtual void ReplaceCharacter(nsReadingIterator& aPosition, @@ -382,6 +387,19 @@ class nsScanner { PRUnichar* aDataEnd, PRUnichar* aStorageEnd); + /** + * These versions of AppendToBuffer allocate from the recyclingallocator, + * convert to unicode if necessary and then add the buffer into the sliding + * string + */ + void AppendToBuffer(const nsAString &aData); + void AppendToBuffer(const char* aData, PRUint32 aLen); + + static void InitAllocator() { + if (!gAllocator) + gAllocator = new nsRecyclingAllocator(NS_SCANNER_BUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "parser"); + } + nsInputStream* mInputStream; nsScannerString* mSlidingBuffer; nsReadingIterator mCurrentPosition; // The position we will next read from in the scanner buffer @@ -397,6 +415,10 @@ class nsScanner { nsString mCharset; nsIUnicodeDecoder *mUnicodeDecoder; PRInt32 mNewlinesSkipped; + +public: + // Allocator to recycle memory used by scanner + static nsRecyclingAllocator *gAllocator; }; #endif diff --git a/parser/htmlparser/src/nsParserModule.cpp b/parser/htmlparser/src/nsParserModule.cpp index 83f7cecbb6de..4df5f75a3770 100644 --- a/parser/htmlparser/src/nsParserModule.cpp +++ b/parser/htmlparser/src/nsParserModule.cpp @@ -53,6 +53,7 @@ //#include "nsTextTokenizer.h" #include "nsElementTable.h" #include "nsParserService.h" +#include "nsScanner.h" #ifdef NS_DEBUG #include "nsLoggingSink.h" @@ -118,6 +119,8 @@ Shutdown(nsIModule* aSelf) DeleteElementTable(); CNewlineToken::FreeNewline(); gInitialized = PR_FALSE; + delete nsScanner::gAllocator; + nsScanner::gAllocator = nsnull; } } diff --git a/parser/htmlparser/src/nsScanner.cpp b/parser/htmlparser/src/nsScanner.cpp index d6b171d78494..345d1a8e1a1d 100644 --- a/parser/htmlparser/src/nsScanner.cpp +++ b/parser/htmlparser/src/nsScanner.cpp @@ -46,9 +46,48 @@ #include "nsFileSpec.h" #include "nsReadableUtils.h" -nsScannerString::nsScannerString(PRUnichar* aStorageStart, - PRUnichar* aDataEnd, - PRUnichar* aStorageEnd) : nsSlidingString(aStorageStart, aDataEnd, aStorageEnd) +nsRecyclingAllocator* nsScanner::gAllocator = nsnull; + +static void nsScannerRecycle(void *aPtr, void *aClientData) +{ + nsScanner::gAllocator->Free(aPtr); +} + +static void *nsScannerAllocate(PRUint32 size) +{ + return nsScanner::gAllocator->Malloc(size); +} + +/** + * Helper funciton to convert raw 8bit char * to unicode + * + * aStr : input 8bit string + * aLen : length of input string aStr + * aDest : Destination unicode buffer. Null terminated. + * aDestLength : size of destination buffer. Must be atleast aLen+1. + * + * return 0 if conversion succeeded + * returns -1, if there is not enough space in destination buffer, + */ +static int RawToUnicode(const char *aStr, PRInt32 aLen, PRUnichar *aDest, PRUint32 aDestLength) +{ + // Alert if someone if passing null buffers to us + NS_ASSERTION(aStr && aDest, "Null buffers"); + + // Error checking + if (!aStr || !aDest || (PRInt32)aDestLength < aLen+1) + return -1; + + while (*aStr) + *aDest++ = (PRUnichar) (unsigned char) *aStr++; + *aDest = 0; + return 0; +} + + +nsScannerString::nsScannerString(PRUnichar* aStorageStart, PRUnichar* aDataEnd, + PRUnichar* aStorageEnd, nsFreeProc *aFreeProc) + : nsSlidingString(aStorageStart, aDataEnd, aStorageEnd, aFreeProc) { } @@ -107,14 +146,17 @@ MOZ_DECL_CTOR_COUNTER(nsScanner) nsScanner::nsScanner(const nsAString& anHTMLString, const nsString& aCharset, PRInt32 aSource) { MOZ_COUNT_CTOR(nsScanner); - - PRUnichar* buffer = ToNewUnicode(anHTMLString); - mTotalRead = anHTMLString.Length(); + InitAllocator(); mSlidingBuffer = nsnull; + mTotalRead = anHTMLString.Length(); + if (mTotalRead) { + // Append to buffer will take care of initializing mCurrentPosition + // and mMarkPosition. If we didnt get here, mCurrentPosition and + // mMarkPosition would be uninitialized and hence we protect using these + // variables with if (mSlidingBuffer) checks all over the code. + AppendToBuffer(anHTMLString); + } mCountRemaining = 0; - AppendToBuffer(buffer, buffer+mTotalRead, buffer+mTotalRead); - mSlidingBuffer->BeginReading(mCurrentPosition); - mMarkPosition = mCurrentPosition; mIncremental=PR_FALSE; mOwnsStream=PR_FALSE; mInputStream=0; @@ -137,7 +179,7 @@ nsScanner::nsScanner(nsString& aFilename,PRBool aCreateStream, const nsString& a mFilename(aFilename) { MOZ_COUNT_CTOR(nsScanner); - + InitAllocator(); mSlidingBuffer = nsnull; mIncremental=PR_TRUE; mCountRemaining = 0; @@ -166,7 +208,7 @@ nsScanner::nsScanner(const nsAString& aFilename,nsInputStream& aStream,const nsS mFilename(aFilename) { MOZ_COUNT_CTOR(nsScanner); - + InitAllocator(); mSlidingBuffer = nsnull; mIncremental=PR_FALSE; mCountRemaining = 0; @@ -262,8 +304,10 @@ nsScanner::~nsScanner() { * @return */ void nsScanner::RewindToMark(void){ - mCountRemaining += (Distance(mMarkPosition, mCurrentPosition)); - mCurrentPosition = mMarkPosition; + if (mSlidingBuffer) { + mCountRemaining += (Distance(mMarkPosition, mCurrentPosition)); + mCurrentPosition = mMarkPosition; + } } @@ -293,9 +337,15 @@ void nsScanner::Mark() { */ PRBool nsScanner::UngetReadable(const nsAReadableString& aBuffer) { - mSlidingBuffer->UngetReadable(aBuffer,mCurrentPosition); - mSlidingBuffer->BeginReading(mCurrentPosition); // Insertion invalidated our iterators - mSlidingBuffer->EndReading(mEndPosition); + if (!mSlidingBuffer) { + // This happens to be the first buffer. Just append it. + AppendToBuffer(aBuffer); + } + else { + mSlidingBuffer->UngetReadable(aBuffer,mCurrentPosition); + mSlidingBuffer->BeginReading(mCurrentPosition); // Insertion invalidated our iterators + mSlidingBuffer->EndReading(mEndPosition); + } mTotalRead += aBuffer.Length(); return PR_TRUE; @@ -309,12 +359,8 @@ PRBool nsScanner::UngetReadable(const nsAReadableString& aBuffer) { * @return error code */ nsresult nsScanner::Append(const nsAReadableString& aBuffer) { - - PRUnichar* buffer = ToNewUnicode(aBuffer); - PRUint32 bufLen = aBuffer.Length(); - mTotalRead += bufLen; - - AppendToBuffer(buffer, buffer+bufLen, buffer+bufLen); + AppendToBuffer(aBuffer); + mTotalRead += aBuffer.Length(); return NS_OK; } @@ -332,8 +378,8 @@ nsresult nsScanner::Append(const char* aBuffer, PRUint32 aLen){ if(mUnicodeDecoder) { PRInt32 unicharBufLen = 0; mUnicodeDecoder->GetMaxLength(aBuffer, aLen, &unicharBufLen); - start = unichars = (PRUnichar*)nsMemory::Alloc((unicharBufLen+1) * sizeof(PRUnichar)); - NS_ENSURE_TRUE(unichars,NS_ERROR_OUT_OF_MEMORY); + start = unichars = (PRUnichar*) nsScannerAllocate((unicharBufLen+1) * sizeof(PRUnichar)); + NS_ENSURE_TRUE(unichars, NS_ERROR_OUT_OF_MEMORY); PRInt32 totalChars = 0; PRInt32 unicharLength = unicharBufLen; @@ -375,9 +421,7 @@ nsresult nsScanner::Append(const char* aBuffer, PRUint32 aLen){ res = NS_OK; } else { - nsDependentCString str(aBuffer, aLen); - unichars = ToNewUnicode(str); - AppendToBuffer(unichars, unichars+aLen, unichars+aLen); + AppendToBuffer(aBuffer, aLen); mTotalRead+=aLen; } @@ -411,23 +455,18 @@ nsresult nsScanner::FillBuffer(void) { } else { PRInt32 numread=0; - char* buf = new char[kBufsize+1]; + char buf[kBufsize+1]; buf[kBufsize]=0; if(mInputStream) { numread = mInputStream->read(buf, kBufsize); - if (0 == numread) { - delete [] buf; + if (0 == numread) return kEOF; - } } if((0& aPosition) { + NS_ASSERTION(mSlidingBuffer, "No data yet. CurrentPosition in invalid"); aPosition = mCurrentPosition; } void nsScanner::EndReading(nsReadingIterator& aPosition) { + NS_ASSERTION(mSlidingBuffer, "No data yet. EndPosition in invalid"); aPosition = mEndPosition; } @@ -1392,12 +1433,32 @@ void nsScanner::ReplaceCharacter(nsReadingIterator& aPosition, } } +void nsScanner::AppendToBuffer(const nsAString &aData) +{ + PRUint32 len = aData.Length(); + PRUnichar* buffer = (PRUnichar *) nsScannerAllocate(len * sizeof(PRUnichar)); + if (buffer) { + CopyUnicodeTo(aData, 0, buffer, len); + AppendToBuffer(buffer, buffer+len, buffer+len); + } +} + +void nsScanner::AppendToBuffer(const char *aData, PRUint32 aLen) +{ + PRUnichar *buffer = (PRUnichar*) nsScannerAllocate((aLen+1) * sizeof(PRUnichar)); + if (buffer) + { + RawToUnicode(aData, aLen, buffer, aLen+1); + AppendToBuffer(buffer, buffer+aLen, buffer+aLen); + } +} + void nsScanner::AppendToBuffer(PRUnichar* aStorageStart, PRUnichar* aDataEnd, PRUnichar* aStorageEnd) { if (!mSlidingBuffer) { - mSlidingBuffer = new nsScannerString(aStorageStart, aDataEnd, aStorageEnd); + mSlidingBuffer = new nsScannerString(aStorageStart, aDataEnd, aStorageEnd, nsScannerRecycle); mSlidingBuffer->BeginReading(mCurrentPosition); mMarkPosition = mCurrentPosition; mSlidingBuffer->EndReading(mEndPosition); @@ -1422,6 +1483,9 @@ void nsScanner::AppendToBuffer(PRUnichar* aStorageStart, * @return nada */ void nsScanner::CopyUnusedData(nsString& aCopyBuffer) { + if (!mSlidingBuffer) + return; + nsReadingIterator start, end; start = mCurrentPosition; end = mEndPosition; diff --git a/parser/htmlparser/src/nsScanner.h b/parser/htmlparser/src/nsScanner.h index 923d01e7bf5b..ce15a1d02ba4 100644 --- a/parser/htmlparser/src/nsScanner.h +++ b/parser/htmlparser/src/nsScanner.h @@ -58,12 +58,17 @@ #include "nsIUnicodeDecoder.h" #include "nsFileStream.h" #include "nsSlidingString.h" +#include "nsRecyclingAllocator.h" + +// Recycling memory used by scanner +#define NS_SCANNER_BUCKETS 10 class nsScannerString : public nsSlidingString { public: nsScannerString(PRUnichar* aStorageStart, PRUnichar* aDataEnd, - PRUnichar* aStorageEnd); + PRUnichar* aStorageEnd, + nsFreeProc* aFreeProc); virtual void UngetReadable(const nsAReadableString& aReadable, const nsReadingIterator& aCurrentPosition) { InsertReadable(aReadable,aCurrentPosition); } virtual void ReplaceCharacter(nsReadingIterator& aPosition, @@ -382,6 +387,19 @@ class nsScanner { PRUnichar* aDataEnd, PRUnichar* aStorageEnd); + /** + * These versions of AppendToBuffer allocate from the recyclingallocator, + * convert to unicode if necessary and then add the buffer into the sliding + * string + */ + void AppendToBuffer(const nsAString &aData); + void AppendToBuffer(const char* aData, PRUint32 aLen); + + static void InitAllocator() { + if (!gAllocator) + gAllocator = new nsRecyclingAllocator(NS_SCANNER_BUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "parser"); + } + nsInputStream* mInputStream; nsScannerString* mSlidingBuffer; nsReadingIterator mCurrentPosition; // The position we will next read from in the scanner buffer @@ -397,6 +415,10 @@ class nsScanner { nsString mCharset; nsIUnicodeDecoder *mUnicodeDecoder; PRInt32 mNewlinesSkipped; + +public: + // Allocator to recycle memory used by scanner + static nsRecyclingAllocator *gAllocator; }; #endif