Mozilla FireFox Gecko内核源代码解析
(3.nsScanner)
中科院计算技术研究所网络数据科学与工程研究中心
信息抽取小组
耿耘
前面我们介绍了nsParser,nsTokenizer,它们之上都需要调用nsScanner获取基本的字符串信息,这里我们来介绍一下这个nsScanner。
nsScanner是一个为了给nsParser以及一些上层服务提供支持的底层类,它是一个功能完善,十分经典的字符串扫描器。它能够接受一个字符流(内部字符流),并且提供一些经典的扫描器方法,如readUntil(),以及SkipWhitespace()之类。说白了,它主要就是提供了一个可控制的字符串获取器,对HTML流进行逐字符的解析。
首先我们来看一下nsScanner.h的源代码
#include "nsCOMPtr.h" #include "nsString.h" #include "nsIParser.h" #include "prtypes.h" #include "nsIUnicodeDecoder.h" #include "nsScannerString.h" classnsParser; classnsReadEndCondition { public: const PRUnichar *mChars; PRUnichar mFilter; //构造参数的显式转换,nsReadEndCondition = (PRUnichar) explicit nsReadEndCondition(constPRUnichar* aTerminateChars); private: //这种通过把相应的构造方法和运算符设置为私有变量的方法,可以防止从外部调用该方法,也就禁止了使用同类型变量对其进行拷贝,或使用=运算符对其进行赋值的操作 nsReadEndCondition(constnsReadEndCondition& aOther); // No copying void operator=(const nsReadEndCondition& aOther); // No assigning }; classnsScanner { public: /** *Use this constructor if you want i/o to be based on *a single string you hand in during construction. *This short cut was added for Javascript. * *@update ftang 3/02/99 *@param aCharset charset *@param aCharsetSource - wherethe charset info came from *@param aMode represents theparser mode (nav, other) *@return */ nsScanner(const nsAString&anHTMLString, const nsACString& aCharset,PRInt32 aSource); //构造方法nsScanner,这个构造方法可以让你的I/O基于一个固定的字符串 /** *Use this constructor if you want i/o to be based on *a file (therefore a stream) or just data you provide via Append(). * *@update ftang 3/02/99 *@param aCharset charset *@param aCharsetSource - wherethe charset info came from *@param aMode represents theparser mode (nav, other) *@return */ //构造方法,如果你希望你的I/O基于一个文件(实际上就是流),或者是基于使用Append(),即不断加入新字符的情况下,可以使用这个构造方法 nsScanner(nsString& aFilename,PRBool aCreateStream, const nsACString& aCharset, PRInt32 aSource); ~nsScanner(); //析构方法 /** *retrieve next char from internal input stream * *@update gess 3/25/98 *@param ch is the char to acceptnew value *@return error code reflectingread status */ //从输入的字符流中,获取下一个字节 nsresult GetChar(PRUnichar& ch); /** *peek ahead to consume next char from scanner's internal *input buffer * *@update gess 3/25/98 *@param ch is the char to acceptnew value *@return error code reflectingread status */ //从输入流中通过只读方式获取下一个字节 nsresult Peek(PRUnichar& ch, PRUint32 aOffset=0); //从输入流中通过只读方式获取下一个字节 nsresult Peek(nsAString& aStr, PRInt32 aNumChars, PRInt32 aOffset =0); /** *Skip over chars as long as they equal given char * *@update gess 3/25/98 *@param char to be skipped *@return error code */ //跳过接下来的字符,只要找到第一个字符和参数给定的字符不同为止 nsresult SkipOver(PRUnichar aSkipChar); /** *Consume characters until you run into space, a '<', a '>', or a'/'. * *@param aString - receives newdata from stream *@return error code */ //处理字符,直到遇到了一个’<’或者’>’,或者’/’为止 nsresult ReadTagIdentifier(nsScannerSharedSubstring& aString); /** *Consume characters until you run into a char that's not valid in an *entity name * *@param aString - receives newdata from stream *@return error code */ //持续处理字符,直到你遇到一个不为Entity的字符为止 nsresult ReadEntityIdentifier(nsString& aString); //下面这几个也是相应的特殊的读取字符方法,我们在后面的cpp文件中会具体分析 nsresult ReadNumber(nsString& aString,PRInt32 aBase); nsresult ReadWhitespace(nsScannerSharedSubstring& aString, PRInt32&aNewlinesSkipped, PRBool&aHaveCR); nsresult ReadWhitespace(nsScannerIterator& aStart, nsScannerIterator&aEnd, PRInt32&aNewlinesSkipped); /** *Consume characters until you find the terminal char * *@update gess 3/25/98 *@param aString receives new datafrom stream *@param aTerminal containsterminating char *@param addTerminal tells uswhether to append terminal to aString *@return error code */ //读取字符,直到碰到terminal字符为止 nsresult ReadUntil(nsAString& aString, PRUnichar aTerminal, PRBool addTerminal); /** *Consume characters until you find one contained in given *terminal set. * *@update gess 3/25/98 *@param aString receives new datafrom stream *@param aTermSet contains set ofterminating chars *@param addTerminal tells uswhether to append terminal to aString *@return error code */ //读取字符,直到遇到了aEndCondition其中之一为止 nsresult ReadUntil(nsAString& aString, const nsReadEndCondition& aEndCondition, PRBool addTerminal); nsresult ReadUntil(nsScannerSharedSubstring& aString, const nsReadEndCondition& aEndCondition, PRBool addTerminal); nsresult ReadUntil(nsScannerIterator& aStart, nsScannerIterator&aEnd, const nsReadEndCondition& aEndCondition, PRBool addTerminal); /** *Records current offset position in input stream. This allows us *to back up to this point if the need should arise, such as when *tokenization gets interrupted. * *@update gess 5/12/98 *@param *@return */ //记录下当前读取的位置,这允许我们将来需要的时候可以回溯到这个位置,比如分词操作被打断的时候 PRInt32 Mark(void); /** *Resets current offset position of input stream to marked position. *This allows us to back up to this point if the need should arise, *such as when tokenization gets interrupted. *NOTE: IT IS REALLY BAD FORM TO CALL RELEASE WITHOUT CALLING MARK FIRST! * *@update gess 5/12/98 *@param *@return */ //重设当前读取的位置到之前记录(Mark)的位置。调用这个方法允许我们在需要的时候回溯到该位置,比如分词过程被打断。 void RewindToMark(void); /** * * *@update harishd 01/12/99 *@param *@return */ //将未解析的字符串返回给aBuffer里 PRBool UngetReadable(constnsAString& aBuffer); /** * * *@update gess 5/13/98 *@param *@return */ //在解析器所要解析的字符串后面新增新的字符串 nsresult Append(const nsAString&aBuffer); /** * * *@update gess 5/21/98 *@param *@return */ //和上一个方法作用相同,只不过形参不一样 nsresult Append(const char* aBuffer, PRUint32 aLen, nsIRequest *aRequest); /** *Call this to copy bytes out of the scanner that have not yet beenconsumed *by the tokenization process. * *@update gess 5/12/98 *@param aCopyBuffer is where thescanner buffer will be copied to *@return nada */ //调用这个方法获取当前未被分词器分词的字符串 void CopyUnusedData(nsString&aCopyBuffer); /** *Retrieve the name of the file that the scanner is reading from. *In some cases, it's just a given name, because the scanner isn't *really reading from a file. * *@update gess 5/12/98 *@return */ //获取扫描器所扫描的文件名字,其实这只是个名字而已,不一定是文件名,因为扫描器实际上并不是从文件中进行读取的 nsString& GetFilename(void); //一个自我测试方法 static voidSelfTest(); /** *Use this setter to change the scanner's unicode decoder * *@update ftang 3/02/99 *@param aCharset a normalized(alias resolved) charset name *@param aCharsetSource- where thecharset info came from *@return */ //设置文档所使用的字符集 nsresult SetDocumentCharset(constnsACString& aCharset, PRInt32 aSource); //获取子字符串 voidBindSubstring(nsScannerSubstring& aSubstring, constnsScannerIterator& aStart, constnsScannerIterator& aEnd); //获取当前位置 voidCurrentPosition(nsScannerIterator& aPosition); //设置读取末尾 void EndReading(nsScannerIterator&aPosition); //设置当前位置 void SetPosition(nsScannerIterator&aPosition, PRBool aTruncate =PR_FALSE, PRBool aReverse =PR_FALSE); //替换该位置的字符 void ReplaceCharacter(nsScannerIterator&aPosition, PRUnichar aChar); /** * Internal method used to cause theinternal buffer to * be filled with data. * * @update gess4/3/98 */ //获取是否是mIncremental即单字节模式,如果是则是按字节进行读取 PRBool IsIncremental(void) {returnmIncremental;} //设置mIncremental模式 voidSetIncremental(PRBool anIncrValue) {mIncremental=anIncrValue;} /** * Return the position of the firstnon-whitespace * character. This is only reliablebefore consumers start * reading from this scanner. */ //获取从当前位置起第一个不为空白字符的位置。这只有在外部模块在开始从扫描器获取字符前有效 PRInt32 FirstNonWhitespacePosition() { return mFirstNonWhitespacePosition; } //设置该扫描器对应的解析器 void SetParser(nsParser*aParser) { mParser = aParser; } /** * Override replacement character used bynsIUnicodeDecoder. * Default behavior is that it usesnsIUnicodeDecoder's mapping. * * @param aReplacementCharacter thereplacement character *XML (expat) parser uses 0xffff */ //重设替换用的字符 voidOverrideReplacementCharacter(PRUnichar aReplacementCharacter); //下面我们看一下数据成员: protected: //将字符串附加到被解析的字符串上的函数 PRBool AppendToBuffer(nsScannerString::Buffer *, nsIRequest *aRequest,PRInt32 aErrorPos = -1); PRBool AppendToBuffer(constnsAString& aStr) { nsScannerString::Buffer* buf =nsScannerString::AllocBufferFromString(aStr); if (!buf) return PR_FALSE; AppendToBuffer(buf, nsnull); return PR_TRUE; } //带解析的字符串 nsScannerString*mSlidingBuffer; //解析器的当前解析位置 nsScannerIteratormCurrentPosition; // The position we will nextread from in the scanner buffer //解析器记录的位置 nsScannerIteratormMarkPosition; // The position last marked (we may rewind to here) //当前解析器字符串的结束位置 nsScannerIteratormEndPosition; // The current end of the scanner buffer //第一个检测出非法字符的位置 nsScannerIteratormFirstInvalidPosition; // The position of thefirst invalid character that was detected //文件名,其实并不是从文件读取 nsString mFilename; //scanner还未读取的字符串中的剩余字节 PRUint32 mCountRemaining; // The number of bytes still to be read // from the scanner buffer //是否是单字节式解析 PRPackedBool mIncremental; PRPackedBoolmHasInvalidCharacter; //是否有非法的字符 PRUnicharmReplacementCharacter; //替换用字符 PRInt32mFirstNonWhitespacePosition; //第一个非空白字符的位置 PRInt32 mCharsetSource; //字符集编号 nsCString mCharset; //字符集名称 nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder; //Unicode编码器 nsParser *mParser; //当前扫描器所对应的解析器 private: nsScanner &operator =(const nsScanner &); //Not implemented. |
以上就是nsScanner的头文件定义,其中的具体实现都在nsScanner.cpp文件中,下面我们来具体看看nsScanner.cpp中的具体实现:
//我们忽略开头的一堆include文件,直接看其有效代码 // We replace NUL characterswith this character. //我们会使用以下字符替换NUL字符 staticPRUnichar sInvalid = UCS2_REPLACEMENT_CHAR; nsReadEndCondition::nsReadEndCondition(const PRUnichar* aTerminateChars) : mChars(aTerminateChars), mFilter(PRUnichar(~0)) //All bits set { // Build filter that will be used to filter out characterswith // bits that none ofthe terminal chars have. This works very well // because terminal chars often have only the last 4-6 bitsset and // normal ascii letters have bit 7 set. Other letters haveeven higher // bits set.
// Calculate filter //注意到,这个构造方法主要是为了建立一个名为Filter的字符,该字符会用来过滤其他拥有它所没有的bit位的字符,但我们首先要先来构建这个Filter。据作者说,这个Filter的工作效果很好,因为大部分的terminal字符只有低位的第4至第6bit设置了,但是一般的ascii字符的第7位bit设置了。其他的字符可能会有更高位的bit被设置了。 //注意两个参数,aTerminateChars是存放terminate字符的数组,而mFilter字符各个bit位首先被置为了全1 //首先获取aTerminateChars数组的首地址 const PRUnichar *current = aTerminateChars; //获取该数组地一个字符,放到terminalChar里 PRUnichar terminalChar = *current; //当terminalChar不为空的时候,即遍历整个terminalChar数组 while (terminalChar) { mFilter &= ~terminalChar; //用terminalChar的反码和mFilter进行与运算,也就是说如果terminalChar的第N位为1的话,那么mFilter的第N位则会被至为0 ++current; //取下一个TerminateChars数组元素的地址 terminalChar = *current; //并且其赋值给terminalChar } } #ifdef__INCREMENTAL //如果设置了INCREMENTAL位 const int kBufsize=1; //设置缓冲区大小为1 #else const intkBufsize=64; //否则设置为64 #endif /** * Usethis constructor if you want i/o to be based on * asingle string you hand in during construction. * Thisshort cut was added for Javascript. * *@update gess 5/12/98 *@param aMode represents theparser mode (nav, other) *@return */ //如果你希望i/o基于一个在构造方法时传递过来的单独的字符串则可以使用这个方法进行构造。这个捷径主要是为了给Javascript提供支持而添加的 nsScanner::nsScanner(const nsAString& anHTMLString, const nsACString& aCharset, PRInt32 aSource) : mParser(nsnull) { MOZ_COUNT_CTOR(nsScanner); //以下设置几个变量的初始值 mSlidingBuffer = nsnull; mCountRemaining = 0; mFirstNonWhitespacePosition = -1; if (AppendToBuffer(anHTMLString)) { //将给定的字符串拷贝到当前要解析的buffer中 mSlidingBuffer->BeginReading(mCurrentPosition); //开始解析,需要注意的是,实际上并不是从mCurrentPosition位置开始解析,而是开始解析,并把位置赋值给mCurrentPosition } else { //其他情况下,说明将字符串拷贝给buffer失败了 /* XXX see hack below, re: bug 182067 */ memset(&mCurrentPosition, 0, sizeof(mCurrentPosition)); //貌似通过内存设置方式直接将其指针置空 mEndPosition = mCurrentPosition; //此时这两个值都应当为0 } mMarkPosition = mCurrentPosition; //记录一下当前的位置 mIncremental = PR_FALSE; //设置增量式解析位为FALSE mUnicodeDecoder = 0; //某个变量 mCharsetSource = kCharsetUninitialized; //字符集变量 mHasInvalidCharacter = PR_FALSE; //设置是否有非法字符 mReplacementCharacter = PRUnichar(0x0); //设置如果遇到非法字符的替换字符 } //设置扫描器所用的字符集 nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSource) { //字符集是有优先级的,首先应对其进行判断,低优先级的字符集一般是高优先级的字符集子集,设置的时候需要进行判断 if (aSource < mCharsetSource) // priority is lower the the current one , just return NS_OK; //如果新字符集的优先级较低,直接返回 nsICharsetAlias* calias = nsParser::GetCharsetAliasService(); //获取字符集服务 NS_ASSERTION(calias, "Must have thecharset alias service!"); //确保获取成功 nsresult res = NS_OK; if (!mCharset.IsEmpty()) //如果当前字符集不为空 { PRBool same; res = calias->Equals(aCharset, mCharset, &same); //判断一下新字符集和当前字符集是否是同一个字符集 if(NS_SUCCEEDED(res) && same) //如果两个字符集一样 { //则直接返回 return NS_OK; //no difference, don't change it } } //运行到此处,说明两个字符集不一样,且新字符集的优先级较高,需要进行替换 // different, need to change it nsCString charsetName; res = calias->GetPreferred(aCharset, charsetName); //获取字符集名称 if(NS_FAILED(res) && (mCharsetSource ==kCharsetUninitialized)) { //如果获取失败,且当前的字符集为空 // failed - unknown alias , fallback toISO-8859-1 mCharset.AssignLiteral("ISO-8859-1"); //那么默认使用ISO-8859-1字符集 } else { mCharset.Assign(charsetName); //获取成功,则直接使用该字符集 } mCharsetSource = aSource; //设置字符集源值 NS_ASSERTION(nsParser::GetCharsetConverterManager(), "Musthave the charset converter manager!"); //获取UnicodeDecoder res = nsParser::GetCharsetConverterManager()-> GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder)); if (NS_FAILED(res)) //如果获取UnicodeDecoder失败 { // GetUnicodeDecoderRaw can fail if thecharset has the .isXSSVulnerable // flag. Try to fallback to ISO-8859-1 mCharset.AssignLiteral("ISO-8859-1"); //则还是默认使用ISO-8859-1字符集 mCharsetSource = kCharsetFromWeakDocTypeDefault; //设置为最低优先级 res = nsParser::GetCharsetConverterManager()-> //获取相应的UnicodeDecoder GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder)); } //如果获取成功 if (NS_SUCCEEDED(res) && mUnicodeDecoder) { // We need to detect conversion error ofcharacter to support XML // encoding error. //我们需要对字符转换的错误进行监测,从而支持XML编码中的错误 mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal); //为UnicodeDecoder进行错误行为处理设置 } return res; //返回处理结果 } //下面是析构方法 /** *default destructor * *@update gess 3/25/98 *@param *@return */ nsScanner::~nsScanner() { if (mSlidingBuffer) { delete mSlidingBuffer; //删除当前未扫描的字符串 } MOZ_COUNT_DTOR(nsScanner); //用来进行日志记录的一个构件方法,如果没有特殊#Define NS_BUILD_REFCNT_LOGGING的话,这个方法一般为空,什么都不做 } /** *default destructor * *@update gess 3/25/98 *@param *@return */ nsScanner::~nsScanner() { if (mSlidingBuffer) { delete mSlidingBuffer; //删除当前未扫描的字符串 } MOZ_COUNT_DTOR(nsScanner); //用来进行日志记录的一个构件方法,如果没有特殊#Define NS_BUILD_REFCNT_LOGGING的话,这个方法一般为空,什么都不做 } //上面提到过了一个Mark的方法,用来提供回溯时的记录位,下面我们来看看回溯的Rewind方法,很简单的实现。 /** *Resets current offset position of input stream to marked position. * Thisallows us to back up to this point if the need should arise, * suchas when tokenization gets interrupted. * NOTE:IT IS REALLY BAD FORM TO CALL RELEASE WITHOUT CALLING MARK FIRST! * *@update gess 5/12/98 *@param *@return */ voidnsScanner::RewindToMark(void){ if (mSlidingBuffer) { //如果当前存在一个mSlidingBuffer mCountRemaining += (Distance(mMarkPosition, mCurrentPosition)); //修改剩余的字节数,需要加上Mark位置到当前位置的距离 mCurrentPosition = mMarkPosition; //设置当前位置为Mark的位置 } } //下面是在上面方法执行之前必须进行的Mark方法。 /** *Records current offset position in input stream. This allows us * toback up to this point if the need should arise, such as when *tokenization gets interrupted. * *@update gess 7/29/98 *@param *@return */ PRInt32 nsScanner::Mark() { PRInt32 distance = 0; //设置距离为0 if (mSlidingBuffer) { //如果当前字符串存在 nsScannerIteratoroldStart; //设置一个游标 mSlidingBuffer->BeginReading(oldStart); //用该游标记录下原始的起始位置 distance = Distance(oldStart, mCurrentPosition); mSlidingBuffer->DiscardPrefix(mCurrentPosition); //去掉当前位置mCurrentPosition之前的字符串 mSlidingBuffer->BeginReading(mCurrentPosition); //设置起始位置为mCurrentPosition mMarkPosition = mCurrentPosition; //设置mMarkPosition为mCurrentPosition } return distance; } //下面这个方法,主要是配合Parse中,将上一次解析时没有处理完的字符串,重新插入到当前扫描器中去的方法。/** * Insert data to our underlying input bufferas * if it were read from an input stream. * * @updateharishd 01/12/99 * @returnerror code */ PRBool nsScanner::UngetReadable(const nsAString& aBuffer) { if (!mSlidingBuffer) { //如果当前的解析字符串不存在 return PR_FALSE; //则返回 } mSlidingBuffer->UngetReadable(aBuffer,mCurrentPosition); //调用mSlidingBuffer的UngetReadable,将aBuffer插入到mCurrentPosition中去 //重新设置读取的起始位置和结束位置,因为插入操作会破坏我们原始的游标 mSlidingBuffer->BeginReading(mCurrentPosition); // Insertion invalidated our iterators mSlidingBuffer->EndReading(mEndPosition); //获取新插入字符串的长度 PRUint32 length = aBuffer.Length(); //在原始未解析的字符串长度上加上新插入字符串的长度 mCountRemaining += length; // Ref. bug 117441 return PR_TRUE; } //下面这几个Append方法主要是为了给普通的I/O提供服务,即将新到来的字符串附到原始待解析的字符串末尾端。 /** * Append data to our underlying input bufferas * if it were read from an input stream. * * @updategess4/3/98 * @returnerror code */ nsresult nsScanner::Append(const nsAString& aBuffer) { if (!AppendToBuffer(aBuffer)) //直接调用AppendToBuffer方法 return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } /** * * *@update gess 5/21/98 *@param *@return */ //对于C++中,字符型数组作为参数,除了其指针,一般都需要传递其长度 nsresult nsScanner::Append(const char* aBuffer,PRUint32 aLen, nsIRequest*aRequest) { nsresult res=NS_OK; PRUnichar *unichars, *start; if (mUnicodeDecoder) { PRInt32 unicharBufLen = 0; mUnicodeDecoder->GetMaxLength(aBuffer,aLen, &unicharBufLen); //就是unicharBufLen = aLen + 1 //申请一个新数组,长度为unicharBufLen + 1,因为C++中字符数组最后一位要放’\0’ nsScannerString::Buffer* buffer =nsScannerString::AllocBuffer(unicharBufLen + 1); NS_ENSURE_TRUE(buffer,NS_ERROR_OUT_OF_MEMORY); //指针指向字符数组的第一个字节 start = unichars = buffer->DataStart(); //初始化几个变量 PRInt32 totalChars = 0; PRInt32 unicharLength = unicharBufLen; PRInt32errorPos = -1; //下面这个循环主要就是对于那些非法的字符(即无法识别的字符)需要用之前设置好的Replacement字符进行替换。 do { PRInt32 srcLength = aLen; //设置一个变量,记录附加字符串的原始长度 //进行字符串转换,转换结果放到unichars中去,并在unicharLength中记录转换后的字符串长度 res = mUnicodeDecoder->Convert(aBuffer, &srcLength, unichars,&unicharLength); //将总字节数,加上unicharLength totalChars += unicharLength; // Continuationof failure case if(NS_FAILED(res)) { // if we failed, we consume one byte, replaceit with the replacement // character and try the conversion again. // This is only needed because some decodersdon't follow the // nsIUnicodeDecoder contract: they return afailure when *aDestLength // is 0rather than the correct NS_OK_UDEC_MOREOUTPUT.See bug 244177 //运行到这里,说明我们刚才的转换操作失败了,我们将错误字符替换为代替字符,并且再次尝试对其进行转换。 //这只有在有些解码器不符合nsIUnicodeDecoder规则的时候才会发生:他们当 DestLength为0的时候会返回一个失败值 if ((unichars + unicharLength) >=buffer->DataEnd()) { //如果超出了最大长度 NS_ERROR("Unexpected end of destinationbuffer"); //指针越界出错了 break; } if (mReplacementCharacter == 0x0 && errorPos== -1) { //如果替换字符为0,且出错位置为-1 errorPos = totalChars; //出错位置直接记录为整体字符串 } //进行字符替换 unichars[unicharLength++] = mReplacementCharacter == 0x0 ? mUnicodeDecoder->GetCharacterForUnMapped() : mReplacementCharacter; //重新设置字符串的起始位置和长度 unichars = unichars + unicharLength; unicharLength = unicharBufLen - (++totalChars); //重设UnicodeDecoder mUnicodeDecoder->Reset(); if(((PRUint32) (srcLength + 1)) >aLen) { //此处应当是出现了错误字符才会导致的情况 srcLength = aLen; } else { //一般情况下只需要进行加一操作,即按序解析下一个字符 ++srcLength; } aBuffer += srcLength; //将aBuffer向后移动srcLength个位置 aLen -= srcLength; //减少srcLength长度,即已经解析的字节数 } }while (NS_FAILED(res) && (aLen >0)); buffer->SetDataLength(totalChars); //设置buffer的数据长度为新的数据长度 // Don't propagate return code of unicodedecoder // since it doesn't reflect on our success orfailure // - Ref. bug 87110 res= NS_OK; if (!AppendToBuffer(buffer, aRequest,errorPos)) //使用转换好的字符串进行AppendToBuffer操作,这个操作在代码的最后会进行介绍 res = NS_ERROR_OUT_OF_MEMORY; } else { //其他情况下,说明Append操作失败 NS_WARNING("No decoder found."); res = NS_ERROR_FAILURE; //设置结果为错误值 } return res; //返回结果 } //看完了上面的一些方法,我们来看接下来的几个经典的Scanner所支持的操作:/** *retrieve next char from scanners internal input stream * *@update gess 3/25/98 *@param *@return error code reflectingread status */ //这个方法很简单,就是“取出”字符流中的第一个字符,并将aChar指向的它 nsresultnsScanner::GetChar(PRUnichar& aChar) { if (!mSlidingBuffer || mCurrentPosition ==mEndPosition) { //先判断一下当前解析是不是已经到结尾了,或者带解析的字符串本身就不存在 aChar = 0; return kEOF; //返回文件末尾,即空值 } aChar = *mCurrentPosition++; //设置aChar为当前的位置加一 --mCountRemaining; //减少剩余未解析的字节数 return NS_OK; } //下面这个方法和上面的方法不同,并不是“取出(Get)”,而是只读方式的“浏览(Peek)”,代码很简单 /** * peekahead to consume next char from scanner's internal * inputbuffer * *@update gess 3/25/98 *@param *@return */ //浏览从当前位置起,aOffset之后个位置的字符,并将aChar指向该字符 nsresult nsScanner::Peek(PRUnichar&aChar, PRUint32 aOffset) { aChar = 0; if (!mSlidingBuffer || mCurrentPosition ==mEndPosition) { //如果当前待解析的字符串不存在,或当前位置等于结束位置了 return kEOF; //返回文件末尾kEOF,即空 } if (aOffset > 0) { //如果aOffset大于零 if (mCountRemaining <= aOffset) //如果偏移位置超过了剩余字节的数量 return kEOF; //直接返回空(为啥不返回最后一个字节呢,合情合理) nsScannerIterator pos = mCurrentPosition; //获取当前位置 pos.advance(aOffset); //前进aOffset个位置 aChar=*pos; //用aChar指向该位置的字符 } else { aChar=*mCurrentPosition; //其他情况下,即aOffset为0或者为负值的情况下,直接指向当前位置 } return NS_OK; //返回正确结果 } //下面这个Peek方法,同样是浏览,但是一次获取的是若干个字符,并不是单个字符 //较前一个方法,多了一个aNumChars,用来记录取出字符的数量 nsresult nsScanner::Peek(nsAString&aStr, PRInt32 aNumChars, PRInt32 aOffset) { if (!mSlidingBuffer || mCurrentPosition ==mEndPosition) { //如果当前待解析的字符串为空,或当前位置已经是结束的位置了 return kEOF; //直接返回文件末尾值,即空值 } nsScannerIterator start, end; //设置两个游标start,end start = mCurrentPosition; //start游标指向当前位置 if ((PRInt32)mCountRemaining <= aOffset) { //如果偏移值超过了剩余字节的数量 return kEOF; //返回文件末尾值 } if (aOffset > 0) { //如果偏移值大于0 start.advance(aOffset); //游标start向前前进aOffset个位置 } if (mCountRemaining < PRUint32(aNumChars +aOffset)) { //这个和前面的Peek方法就不一样了,如果需要获取的字符串的末位置超出了文档结尾,则已文档末尾作为获取字符串的结束位置 end = mEndPosition; } else { //其他情况下,即正常情况 end = start; end.advance(aNumChars); //将end在start的基础上前进aNumChars个字节 } CopyUnicodeTo(start, end, aStr); //这样就直接可以获取start和end之间的字符串作为结果放到aStr中去了 return NS_OK; //返回正确值 } //下面这个方法,是让扫描器从当前开始,不断前进,直到遇到一个不为空字符类型(\0,\r,\n)的字符,其过程并不复杂且很好理解。 /** * Skipwhitespace on scanner input stream * *@update gess 3/25/98 *@param *@return error status */ nsresult nsScanner::SkipWhitespace(PRInt32&aNewlinesSkipped) { //aNewlinesSkipped中记录跳过的行数 if (!mSlidingBuffer) { //如果当前解析字符串为空 return kEOF; //返回文件末尾值,即空值 } PRUnichar theChar = 0; //设置一个字符变量 nsresult result = Peek(theChar); //获取当前位置的字符 if (NS_FAILED(result)) { //如果获取失败 return result; //则返回该结果 } nsScannerIterator current = mCurrentPosition; //获取当前位置的游标 //设置两个变量 PRBool done = PR_FALSE; PRBool skipped = PR_FALSE; //进行循环,直到文件末尾,或者遇到不为空字符的字符为止 while (!done && current != mEndPosition) { switch(theChar) { case '\n': case '\r':++aNewlinesSkipped; //遇到\r或者\n,则对参数中的变量+1 case ' ': case '\t': { skipped = PR_TRUE; //设置遇到了空字符并进行了跳过 PRUnichar thePrevChar = theChar; //用thePrevChar记录当前字节 theChar = (++current != mEndPosition) ? *current : '\0'; //如果到文件末尾了,那么直接将theChar写成’\0’ if ((thePrevChar == '\r' && theChar == '\n') || (thePrevChar == '\n' && theChar == '\r')) { theChar = (++current !=mEndPosition) ? *current : '\0'; // CRLF == LFCR => LF //如果遇到了’\r’和’\n’结合使用的情况,再多跳过一个字节 } } break; default: done = PR_TRUE; //其他情况下即遇到非空字符,则设置DONE为PR_TRUE以跳出循环 break; } } if (skipped) { //如果发生了跳过空字符 SetPosition(current); //设置当前位置为新位置 if (current == mEndPosition) { //如果当前位置为mEndPosition result = kEOF; //返回文件末尾值 } } return result; //返回结果 } //下面这个方法,是让扫描器从当前开始,不断前进,直到遇到一个不为参数所给出的aSkipChar为止。/** * Skipover chars as long as they equal given char * *@update gess 3/25/98 *@param *@return error code */ nsresult nsScanner::SkipOver(PRUnicharaSkipChar){ if (!mSlidingBuffer) { //如果当前待解析字符串为空 return kEOF; //返回空值 } //初始化两个变量 PRUnichar ch=0; nsresult result=NS_OK; while(NS_OK==result) { //循环进行字符跳过 result=Peek(ch); //获取当前位置的字符 if(NS_OK == result) { //如果获取成功 if(ch!=aSkipChar) { //如果当前位置字符和所给定字符不等 break; //跳出循环 } GetChar(ch); //调用前面的GetChar获取下一个字符 } else break; //其他情况下,即Peek()返回失败值,则退出循环 } //while return result; //返回结果 } //下面这个方法,是针对Tag进行的,会让扫描器从当前位置开始,一直读取,直到遇到了一个’<’,或’>’,或’/’字符,或者前面提到过的空格型字符为止。 /** *Consume characters until you run into space, a '<', a '>', or a'/'. * *@param aString - receives newdata from stream *@return error code */ nsresultnsScanner::ReadTagIdentifier(nsScannerSharedSubstring& aString) { if (!mSlidingBuffer) { //如果当前待解析的字符串为空 return kEOF; //返回文件末尾值,即空值 } //设置并初始化一些需要用的变量 PRUnichar theChar=0; nsresultresult=Peek(theChar); nsScannerIterator current, end; PRBoolfound=PR_FALSE;
current = mCurrentPosition; end = mEndPosition; // Loop until we find an illegal character. Everything isthen appended // later. //下面这个循环,不断地循环,直到找到一个非法的字符为止 while(current != end && !found) { theChar=*current; switch(theChar) { case '\n': case '\r': case ' ': case '\t': case '\v': case '\f': case '<': case '>': case '/': found = PR_TRUE; //找到非法字符,设置相应标示位 break; case '\0': ReplaceCharacter(current, sInvalid); //如果是空字符则使用特殊字符对其进行替换 break; default: break; } if (!found) { //如果没找到 ++current; //则将当前位置前进一个字符 } } SetPosition(current); //设置当前位置为新位置 if (current == end) { //如果当前已经到了文件末尾 result = kEOF; //返回文件末尾值 } //DoErrTest(aString); return result; //返回处理结果 } //下面这个方法,是让扫描器不断地读取字符,直到遇到了一个实体名为止。 /** *Consume characters until you run into a char that's not valid in an *entity name * *@param aString - receives newdata from stream *@return error code */ //和前面的方法基本相同,区别就是判断条件改为了:’_’,’-’,’.’或大小写字符及数字 nsresultnsScanner::ReadEntityIdentifier(nsString& aString) { if (!mSlidingBuffer) { //对待解析字符串进行判断 return kEOF; } //设置几个变量 PRUnichar theChar=0; nsresultresult=Peek(theChar); nsScannerIterator origin, current, end; PRBoolfound=PR_FALSE; //下面几个变量用来记录位置信息 origin = mCurrentPosition; current = mCurrentPosition; end = mEndPosition; while(current != end) { //循环遍历字符串,直到末尾或主动退出 theChar=*current; //获取当前位置的字符 if(theChar) { //如果字符存在 found=PR_FALSE; //设置是否找到位found默认值为PR_FALSE switch(theChar) { case '_': case '-': case '.': // Don't allow ':' in entity names. See bug 23791 found = PR_TRUE; break; default: found = ('a'<=theChar &&theChar<='z') || ('A'<=theChar&& theChar<='Z') || ('0'<=theChar&& theChar<='9'); break; } if(!found) { //这里不应当对是否前进了进行一下判断么?如果mCurrentPosition和current相等怎么办?他似乎默认当前一定能找到Entity字符 //如果该字符不是任何Entity字符,那么将CurrentPosition位置和当前位置之间的字符串Append到aString中去 AppendUnicodeTo(mCurrentPosition, current,aString); break; } } ++current; //将current前进一位 }
SetPosition(current); //将当前位置设置为新的位置 if (current == end) { //如果发现是文件末尾了 AppendUnicodeTo(origin, current, aString); //将原始的position和当前位置之间的这段字符串粘贴到aString中去 return kEOF; } //DoErrTest(aString); return result; //返回结果 } //下面这个ReadNumber方法和刚才那个方法几乎一样,只不过不是读取EntityName,而是读取数字,一旦遇到非数字就退出。 /** *Consume digits * *@param aString - should containdigits *@return error code */ nsresultnsScanner::ReadNumber(nsString& aString,PRInt32 aBase) { if (!mSlidingBuffer) { //对待解析字符串的存在进行判定 return kEOF; } //判断aBase值进行判断,判断是什么进制的,目前只支持10进制或16进制 NS_ASSERTION(aBase == 10 || aBase == 16,"basevalue not supported"); //设置一些变量 PRUnichar theChar=0; nsresultresult=Peek(theChar); nsScannerIterator origin, current, end; //下面这些变量用来记录位置信息 origin = mCurrentPosition; current = origin; end = mEndPosition; PRBool done = PR_FALSE; while(current != end) { //循环遍历字符串,直到字符串结尾或主动退出循环 theChar=*current; if(theChar) { //如果字符串存在 done = (theChar < '0' || theChar> '9') && ((aBase == 16)? (theChar < 'A' || theChar > 'F')&& (theChar < 'a' || theChar > 'f') :PR_TRUE); //判断是否是0~9,或16进制情况下的A~F if(done) { //如果找到 AppendUnicodeTo(origin, current, aString); //则将其粘附到aString末尾 break; //退出循环 } } ++current; } SetPosition(current); //同上一个方法一样,主要为进行一些特殊情况的收尾工作 if (current == end) { AppendUnicodeTo(origin, current, aString); return kEOF; } //DoErrTest(aString); return result; } //下面的方法ReadWhitespace是让扫描前从当前位置开始,一直读取,直到遇到非空白字符为止。 /** *Consume characters until you find the terminal char * *@update gess 3/25/98 *@param aString receives new datafrom stream *@param addTerminal tells uswhether to append terminal to aString *@return error code //注意其中aHaveCR参数和aNewlinesSkipped参数都是需要在本函数体内进行修改的外部变量 nsresultnsScanner::ReadWhitespace(nsScannerSharedSubstring& aString, PRInt32&aNewlinesSkipped, PRBool&aHaveCR) { //首先将aHaveCR默认设置为FALSE aHaveCR = PR_FALSE; if (!mSlidingBuffer) { //对待解析字符串的存在进行判定 return kEOF; } //申请一个字符类型变量,默认值为0 PRUnichar theChar = 0; nsresult result = Peek(theChar); //查看当前位置的字符
if (NS_FAILED(result)) { //如果查看失败 return result; //则返回失败结果 } //申请用来存放位置信息的游标 nsScannerIterator origin, current, end; PRBool done = PR_FALSE; //设置后面循环用到的变量 //记录几个位置信息 origin = mCurrentPosition; current = origin; end = mEndPosition; PRBool haveCR = PR_FALSE; //申请一个新的内部使用的haveCR变量,默认同样为FALSE while(!done && current != end) { switch(theChar) { case '\n': case '\r': { //如果是’\n’或’\r’的情况下 ++aNewlinesSkipped; //首先将aNewlinesSkipped加一,因为新的一行开始了 PRUnichar thePrevChar = theChar; //记录当前字符 theChar = (++current != end) ? *current : '\0'; //获取下一个字符,如果是文件末尾了则设置下一个字符为’\0’ if ((thePrevChar == '\r' && theChar == '\n') || //判断是否是\r\n同时出现 (thePrevChar == '\n' && theChar == '\r')) { //如果是,那么首先判断theChar之后是否是字符串末尾,如果是则设theChar为’\0’ theChar = (++current != end) ? *current: '\0'; // CRLF ==LFCR => LF //设置内部的haveCR为TRUE haveCR = PR_TRUE; } else if(thePrevChar == '\r') { //如果上一个字符为’\r’ // LoneCR becomes CRLF; callers should know to remove extra CRs AppendUnicodeTo(origin, current,aString); //拷贝字符串 aString.writable().Append(PRUnichar('\n')); //并且需要手动在其之后加上一个’\n’字符 origin = current; haveCR = PR_TRUE; } } break; case ' ': case '\t': theChar = (++current != end) ? *current : '\0'; //遇到其他类型的空白字符,则都需要对其是否为文件末尾做判断 break; default: done = PR_TRUE; //默认情况下即不为空白字符,那么设置done为TRUE AppendUnicodeTo(origin, current, aString); //并将目前已解析的这些字符串,注意是从orgin break; } } //下面这个方法和上面的ReadWhiteSpace大同小异 //XXXbz callers of this haveto manage their lone '\r' themselves if they want //it to work. Good thing they're all in view-source and itdeals. nsresultnsScanner::ReadWhitespace(nsScannerIterator& aStart, nsScannerIterator& aEnd, PRInt32&aNewlinesSkipped) { if (!mSlidingBuffer) { //首先对代解析字符串的存在进行判断 return kEOF; } PRUnichartheChar = 0; //申请一个新的变量 nsresult result = Peek(theChar); //获取当前位置的字符 if (NS_FAILED(result)) { //如果获取字符失败 return result; //返回失败结果 } nsScannerIterator origin, current, end; //三个用来记录位置的游标 PRBool done = PR_FALSE; //设置循环条件 //初始化这三个游标的位置 origin = mCurrentPosition; current = origin; end = mEndPosition; //循环查找字符,直到 while(!done && current != end) { switch(theChar) { case '\n': case '\r':++aNewlinesSkipped; //遇到\n或者\r就将新行数加一 case ' ': case '\t': { PRUnichar thePrevChar = theChar; theChar = (++current != end) ? *current : '\0'; if ((thePrevChar == '\r' && theChar == '\n') || //同时需要注意处理\n\r紧邻着同时出现的情况 (thePrevChar == '\n' && theChar == '\r')) { theChar = (++current != end) ?*current : '\0'; //CRLF == LFCR => LF } } break; default: done = PR_TRUE; //默认情况下就说明找到了非空格字符 aStart = origin; aEnd = current; break; } } SetPosition(current); //设置当前位置为新位置 if (current == end) { //判断是否已到达字符串末尾 aStart = origin; aEnd = current; result = kEOF; } return result; } //下面这个ReadUntil方法,将会不断地读取字符,直到遇到了一个在给定输入集中出现的字符为止,看过了以上几个方法,相信对于以下这个方法驾轻就熟地就能明白。 /** *Consume characters until you encounter one contained in given * inputset. * *@update gess 3/25/98 *@param aString will contain theresult of this method *@param aTerminalSet is anordered string that contains *the set of INVALID characters *@return error code */ //与以上几个方法不同,这里的参数中多了一个前面解析过的nsReadEndCondition,其中就包含了能够导致读取终止的特殊字符 nsresultnsScanner::ReadUntil(nsAString& aString, const nsReadEndCondition& aEndCondition, PRBooladdTerminal) { if (!mSlidingBuffer) { //判断待解析的字符串是否为空 return kEOF; } //设置两个游标 nsScannerIterator origin, current; //获取特殊字符 const PRUnichar* setstart = aEndCondition.mChars; const PRUnichar* setcurrent; //设置两个游标的位置 origin = mCurrentPosition; current = origin; PRUnichar theChar=0; nsresultresult=Peek(theChar); //获取当前位置的字符 if (NS_FAILED(result)) { //如果获取字符失败 return result; }
while (current != mEndPosition) { //循环,直到字符串末尾 theChar = *current; //获取当亲字符 if (theChar == '\0'){ //如果当前位置是空字符’\0’ ReplaceCharacter(current, sInvalid); //用替换字符对其进行替换 theChar = sInvalid; //并且获取替换后的字符 } // Filter out completely wrong characters // Check if all bits are in the required area if(!(theChar & aEndCondition.mFilter)){ //首先进行一下粗略的检查过滤,过滤掉大部分肯定是错误的字符,使用的就是前面aEndCondition构造方法中提到过的mFilter,通过位与的方法进行判断和处理,这主要应当是为了提高当aEndCondition很大时的处理效率 // They were. Do a thorough check. //如果到了这里,那么就说明它很有可能是aEndCondition中的字符之一 setcurrent = setstart; while (*setcurrent) { if (*setcurrent == theChar) { //判断是否是特殊字符 if(addTerminal) //参数传递过来的标示位,是否需要将该特殊字符也添加到读取结果字符串中 ++current; AppendUnicodeTo(origin, current, aString); //粘贴字符串 SetPosition(current); //设置当前位置为新位置 //DoErrTest(aString); return NS_OK; } ++setcurrent; //获取下一个aEndCondition中的字符 } } ++current; //比较源字符串中的下一个字符 } // If we are here, we didn't find any terminator in thestring and // current = mEndPosition //如果到达了这里,说明我们已经到达了字符串的末尾,并且没有遇到特殊字符 SetPosition(current); AppendUnicodeTo(origin, current, aString); return kEOF; } //下面这个ReadUntil()方法,和前面的方法唯一的区别就在于,其参数中使用的不是普通的String,而是nsScannerSharedSubString(),其他的和上面的方法一样,我们就不详细解析了,留给读者自己去理解。 nsresultnsScanner::ReadUntil(nsScannerSharedSubstring& aString, const nsReadEndCondition& aEndCondition, PRBooladdTerminal) { if (!mSlidingBuffer) { return kEOF; } nsScannerIterator origin, current; const PRUnichar* setstart = aEndCondition.mChars; const PRUnichar* setcurrent; origin = mCurrentPosition; current = origin; PRUnichar theChar=0; nsresult result=Peek(theChar); if (NS_FAILED(result)) { return result; } while (current != mEndPosition) { theChar = *current; if (theChar == '\0'){ ReplaceCharacter(current, sInvalid); theChar = sInvalid; } // Filter out completely wrong characters // Check if all bits are in the required area if(!(theChar &aEndCondition.mFilter)) { // They were. Do a thorough check. setcurrent = setstart; while (*setcurrent) { if (*setcurrent == theChar) { if(addTerminal) ++current; AppendUnicodeTo(origin, current, aString); SetPosition(current); //DoErrTest(aString); return NS_OK; } ++setcurrent; } } ++current; } // If we are here, we didn't find any terminatorin the string and // current = mEndPosition SetPosition(current); AppendUnicodeTo(origin, current, aString); return kEOF; } //下面这个仍然是ReadUntil()方法,和前面唯一的区别就在于参数中多了两个参数aStart和aEnd,我们需要在读取的时候对这两个函数外部变量进行赋值,其中aStart实际上就是mCurrentPosition(几乎没有变化?),aEnd标示了读取结束的位置,这个我们同样交给读者去理解。 /** *Consumes chars until you see the given terminalChar * *@update gess 3/25/98 *@param *@return error code */ nsresult nsScanner::ReadUntil(nsAString&aString, PRUnicharaTerminalChar, PRBooladdTerminal) { if (!mSlidingBuffer) { return kEOF; } nsScannerIterator origin, current; origin = mCurrentPosition; current = origin; PRUnichar theChar; nsresult result = Peek(theChar); if (NS_FAILED(result)) { return result; } while (current != mEndPosition) { theChar = *current; if (theChar == '\0'){ ReplaceCharacter(current, sInvalid); theChar = sInvalid; } if (aTerminalChar == theChar) { if(addTerminal) ++current; AppendUnicodeTo(origin, current, aString); SetPosition(current); return NS_OK; } ++current; } // If we are here, we didn't find any terminator in thestring and // current = mEndPosition AppendUnicodeTo(origin, current, aString); SetPosition(current); return kEOF; } //下面,是几个提供支持的简单的方法: voidnsScanner::BindSubstring(nsScannerSubstring& aSubstring, const nsScannerIterator& aStart, const nsScannerIterator& aEnd) { //获取mSlidingBuffer,赋值到aSubstring中去,同时要为其传递mStart,和mEnd,以及计算一些如length等参数 aSubstring.Rebind(*mSlidingBuffer, aStart, aEnd); } voidnsScanner::CurrentPosition(nsScannerIterator& aPosition) { //获取mCurrentPosition aPosition = mCurrentPosition; } voidnsScanner::EndReading(nsScannerIterator& aPosition) { //获取mEndPosition aPosition = mEndPosition; } //接下来的这个SetPosition()方法前面用到过很多次,主要是为了强制重新设置mCurrentPosition的,同时要做好一些相关的处理工作,如重新计算mCountRemaining,所剩未解析的字节数等。 voidnsScanner::SetPosition(nsScannerIterator& aPosition, PRBool aTerminate,PRBool aReverse) { if (mSlidingBuffer) { //首先对未解析字符串的存在进行判定 #ifdefDEBUG PRUint32 origRemaining = mCountRemaining; #endif if (aReverse) { //需要通过参数来判断新位置是在当前位置之前还是之后 //如果是在之前,则当前所剩字节需要加上偏移距离 mCountRemaining += (Distance(aPosition, mCurrentPosition)); } else { //反之就是在之后,需要减去偏移距离 mCountRemaining -= (Distance(mCurrentPosition, aPosition)); } //下面这个是为了小心而对上面操作所做的一个检测 NS_ASSERTION((mCountRemaining >= origRemaining && aReverse)|| (mCountRemaining <=origRemaining && !aReverse), "Improperuse of nsScanner::SetPosition. Make sure to set the" "aReverse parameter correctly"); //设置当前位置为新位置 mCurrentPosition = aPosition; if (aTerminate &&(mCurrentPosition == mEndPosition)) { //如果当前已经到了字符串结尾,并且相应的aTerminate标示位被设置为TRUE mMarkPosition = mCurrentPosition; //记录一下当前位置 mSlidingBuffer->DiscardPrefix(mCurrentPosition); //删除当前位置之前的所有字符 } } } //下面的方法是用来对非法字符等进行替换的方法。 voidnsScanner::ReplaceCharacter(nsScannerIterator& aPosition, PRUnicharaChar) { if (mSlidingBuffer) { //如果当前待解析字符串存在 mSlidingBuffer->ReplaceCharacter(aPosition, aChar); //直接对aPosition位置的字符进行替换 } } //下面这个AppendToBuffer是前面的那个Append所调用的,进行实际粘贴操作的方法。 PRBoolnsScanner::AppendToBuffer(nsScannerString::Buffer* aBuf, nsIRequest *aRequest, PRInt32aErrorPos) { //首先调用mParser的DataAdded通知Parser新的数据到达了 if (nsParser::sParserDataListeners && mParser&& NS_FAILED(mParser->DataAdded(Substring(aBuf->DataStart(), aBuf->DataEnd()), aRequest))) { // Don't actually append on failure. //如果失败了,那么直接将字符串至为空 return mSlidingBuffer != nsnull; } if (!mSlidingBuffer) { //如果字符串为空的情况下 mSlidingBuffer = newnsScannerString(aBuf); //使用aBuf初始化其为一个新的字符串 if (!mSlidingBuffer) //如果初始化失败 return PR_FALSE; //返回错误值,这里怎么不报那个NS_OUTOFMEMORY错误了? mSlidingBuffer->BeginReading(mCurrentPosition); //获取字符串的读取当前位置,即起始位置 mMarkPosition = mCurrentPosition; //标记一下当前位置 mSlidingBuffer->EndReading(mEndPosition); //获取结束位置 mCountRemaining = aBuf->DataLength(); //获取aBuf的长度 } else { mSlidingBuffer->AppendBuffer(aBuf); //其他情况下,说明当前待解析的字符串不为空,我们需要将aBuf放到该字符串之后 if (mCurrentPosition == mEndPosition) { //判断,如果当前位置是原始字符串的末尾 mSlidingBuffer->BeginReading(mCurrentPosition); //设置新的当前位置,因为我们新增了内容 } mSlidingBuffer->EndReading(mEndPosition); //设置新的结束位置,同样因为有新增内容 mCountRemaining += aBuf->DataLength(); //增加剩余字节的数量 } if (aErrorPos != -1 && !mHasInvalidCharacter){ //同时,要对非法字符进行处理,做判断,如果原始字符串中没有非法字符,而新增加的字符串中有非法字符 mHasInvalidCharacter = PR_TRUE; //那么我们需要设置新的非法字符标志位 mFirstInvalidPosition = mCurrentPosition; //改变新的非法字符位置 mFirstInvalidPosition.advance(aErrorPos); //设置该位置为当前位置前进aErrorPos偏移距离后的位置 } if (mFirstNonWhitespacePosition == -1) { //同时需要对第一个非空白字符标示位进行修改,判断,如果原始的字符串中全部都是空白字符 nsScannerIterator iter(mCurrentPosition); //那么设置两个游标iter和end nsScannerIterator end(mEndPosition); while (iter != end) { //循环遍历新增加的那段字符串 if (!nsCRT::IsAsciiSpace(*iter)) { //判断当前字符如果是非空字符 mFirstNonWhitespacePosition = Distance(mCurrentPosition, iter); //那么设置第一个非空白字符标示位为起始地址+当前偏移地址(此处我怎么觉得这么别扭?不过貌似没错) break; } ++iter; //将游标前进至下一个字符 } } return PR_TRUE; } //下面的方法,是在Parser中,以及一些nsScanner的调用体中经常用到的方法,该将未使用的字符串拷贝出去,以便在下一次解析过程中再进行使用,相当于对分步解析过程的支持。 /** * callthis to copy bytes out of the scanner that have not yet been consumed * bythe tokenization process. * * @update gess 5/12/98 *@param aCopyBuffer is where thescanner buffer will be copied to *@return nada */ voidnsScanner::CopyUnusedData(nsString& aCopyBuffer) { if (!mSlidingBuffer) { //首先得判断解析字符串是否存在 aCopyBuffer.Truncate(); //如果不存在或者为0值,直接清空并返回空值 return; } //设置两个游标 nsScannerIterator start, end; start = mCurrentPosition; end = mEndPosition; //将当前未解析的字节拷贝出去,终于知道为什么设置mCurrentPosition和mEndPosition了 CopyUnicodeTo(start, end, aCopyBuffer); } //之后还有几个无关紧要的小方法,读者自己看一看就明白了,特别简单。 /** *Retrieve the name of the file that the scanner is reading from. * Insome cases, it's just a given name, because the scanner isn't *really reading from a file. * *@update gess 5/12/98 *@return */ nsString& nsScanner::GetFilename(void) { //这个filename基本没用 return mFilename; } /** *Conduct self test. Actually, selftesting for this class *occurs in the parser selftest. * *@update gess 3/25/98 *@param *@return */ voidnsScanner::SelfTest(void) { //空方法,期待后来人编写 #ifdef_DEBUG #endif } voidnsScanner::OverrideReplacementCharacter(PRUnichar aReplacementCharacter) { //重设新的非法字符替换字符 mReplacementCharacter = aReplacementCharacter; if (mHasInvalidCharacter) { ReplaceCharacter(mFirstInvalidPosition, mReplacementCharacter); } } |