private int IncrementalRead()
{
int charsDecoded = 0;
OuterContinue:
int charsLeft = _incReadLeftEndPos - _incReadLeftStartPos;
if (charsLeft > 0)
{
int count;
try
{
count = _incReadDecoder.Decode(_ps.chars, _incReadLeftStartPos, charsLeft);
}
catch (XmlException e)
{
ReThrow(e, (int)_incReadLineInfo.lineNo, (int)_incReadLineInfo.linePos);
return 0;
}
if (count < charsLeft)
{
_incReadLeftStartPos += count;
_incReadLineInfo.linePos += count; // we have never more then 1 line cached
return count;
}
else
{
_incReadLeftStartPos = 0;
_incReadLeftEndPos = 0;
_incReadLineInfo.linePos += count;
if (_incReadDecoder.IsFull)
{
return count;
}
}
}
int startPos = 0;
int pos = 0;
for (;;)
{
switch (_incReadState)
{
case IncrementalReadState.Text:
case IncrementalReadState.Attributes:
case IncrementalReadState.AttributeValue:
break;
case IncrementalReadState.PI:
if (ParsePIValue(out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 2, 2, "?>"));
_ps.charPos -= 2;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.Comment:
if (ParseCDataOrComment(XmlNodeType.Comment, out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 3, 3, "-->"));
_ps.charPos -= 3;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.CDATA:
if (ParseCDataOrComment(XmlNodeType.CDATA, out startPos, out pos))
{
Debug.Assert(XmlConvert.StrEqual(_ps.chars, _ps.charPos - 3, 3, "]]>"));
_ps.charPos -= 3;
_incReadState = IncrementalReadState.Text;
}
goto Append;
case IncrementalReadState.EndElement:
_parsingFunction = ParsingFunction.PopElementContext;
_nextParsingFunction = (_index > 0 || _fragmentType != XmlNodeType.Document) ? ParsingFunction.ElementContent
: ParsingFunction.DocumentContent;
_outerReader.Read();
_incReadState = IncrementalReadState.End;
goto case IncrementalReadState.End;
case IncrementalReadState.End:
return charsDecoded;
case IncrementalReadState.ReadData:
if (ReadData() == 0)
{
ThrowUnclosedElements();
}
_incReadState = IncrementalReadState.Text;
startPos = _ps.charPos;
pos = startPos;
break;
default:
Debug.Assert(false);
break;
}
Debug.Assert(_incReadState == IncrementalReadState.Text ||
_incReadState == IncrementalReadState.Attributes ||
_incReadState == IncrementalReadState.AttributeValue);
char[] chars = _ps.chars;
startPos = _ps.charPos;
pos = startPos;
for (;;)
{
_incReadLineInfo.Set(_ps.LineNo, _ps.LinePos);
char c;
unsafe
{
if (_incReadState == IncrementalReadState.Attributes)
{
while (_xmlCharType.IsAttributeValueChar(c = chars[pos]) && c != '/')
{
pos++;
}
}
else
{
while (_xmlCharType.IsAttributeValueChar(c = chars[pos]))
{
pos++;
}
}
}
if (chars[pos] == '&' || chars[pos] == (char)0x9)
{
pos++;
continue;
}
if (pos - startPos > 0)
{
goto AppendAndUpdateCharPos;
}
switch (chars[pos])
{
// eol
case (char)0xA:
pos++;
OnNewLine(pos);
continue;
case (char)0xD:
if (chars[pos + 1] == (char)0xA)
{
pos += 2;
}
else if (pos + 1 < _ps.charsUsed)
{
pos++;
}
else
{
goto ReadData;
}
OnNewLine(pos);
continue;
// some tag
case '<':
if (_incReadState != IncrementalReadState.Text)
{
pos++;
continue;
}
if (_ps.charsUsed - pos < 2)
{
goto ReadData;
}
switch (chars[pos + 1])
{
// pi
case '?':
pos += 2;
_incReadState = IncrementalReadState.PI;
goto AppendAndUpdateCharPos;
// comment
case '!':
if (_ps.charsUsed - pos < 4)
{
goto ReadData;
}
if (chars[pos + 2] == '-' && chars[pos + 3] == '-')
{
pos += 4;
_incReadState = IncrementalReadState.Comment;
goto AppendAndUpdateCharPos;
}
if (_ps.charsUsed - pos < 9)
{
goto ReadData;
}
if (XmlConvert.StrEqual(chars, pos + 2, 7, "[CDATA["))
{
pos += 9;
_incReadState = IncrementalReadState.CDATA;
goto AppendAndUpdateCharPos;
}
else
{
;//Throw( );
}
break;
// end tag
case '/':
{
Debug.Assert(_ps.charPos - pos == 0);
Debug.Assert(_ps.charPos - startPos == 0);
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName(true, 2, out colonPos);
if (XmlConvert.StrEqual(chars, _ps.charPos + 2, endPos - _ps.charPos - 2, _curNode.GetNameWPrefix(_nameTable)) &&
(_ps.chars[endPos] == '>' || _xmlCharType.IsWhiteSpace(_ps.chars[endPos])))
{
if (--_incReadDepth > 0)
{
pos = endPos + 1;
continue;
}
_ps.charPos = endPos;
if (_xmlCharType.IsWhiteSpace(_ps.chars[endPos]))
{
EatWhitespaces(null);
}
if (_ps.chars[_ps.charPos] != '>')
{
ThrowUnexpectedToken(">");
}
_ps.charPos++;
_incReadState = IncrementalReadState.EndElement;
goto OuterContinue;
}
else
{
pos = endPos;
startPos = _ps.charPos;
chars = _ps.chars;
continue;
}
}
// start tag
default:
{
Debug.Assert(_ps.charPos - pos == 0);
Debug.Assert(_ps.charPos - startPos == 0);
int colonPos;
// ParseQName can flush the buffer, so we need to update the startPos, pos and chars after calling it
int endPos = ParseQName(true, 1, out colonPos);
if (XmlConvert.StrEqual(_ps.chars, _ps.charPos + 1, endPos - _ps.charPos - 1, _curNode.localName) &&
(_ps.chars[endPos] == '>' || _ps.chars[endPos] == '/' || _xmlCharType.IsWhiteSpace(_ps.chars[endPos])))
{
_incReadDepth++;
_incReadState = IncrementalReadState.Attributes;
pos = endPos;
goto AppendAndUpdateCharPos;
}
pos = endPos;
startPos = _ps.charPos;
chars = _ps.chars;
continue;
}
}
break;
// end of start tag
case '/':
if (_incReadState == IncrementalReadState.Attributes)
{
if (_ps.charsUsed - pos < 2)
{
goto ReadData;
}
if (chars[pos + 1] == '>')
{
_incReadState = IncrementalReadState.Text;
_incReadDepth--;
}
}
pos++;
continue;
// end of start tag
case '>':
if (_incReadState == IncrementalReadState.Attributes)
{
_incReadState = IncrementalReadState.Text;
}
pos++;
continue;
case '"':
case '\'':
switch (_incReadState)
{
case IncrementalReadState.AttributeValue:
if (chars[pos] == _curNode.quoteChar)
{
_incReadState = IncrementalReadState.Attributes;
}
break;
case IncrementalReadState.Attributes:
_curNode.quoteChar = chars[pos];
_incReadState = IncrementalReadState.AttributeValue;
break;
}
pos++;
continue;
default:
// end of buffer
if (pos == _ps.charsUsed)
{
goto ReadData;
}
// surrogate chars or invalid chars are ignored
else
{
pos++;
continue;
}
}
}
ReadData:
_incReadState = IncrementalReadState.ReadData;
AppendAndUpdateCharPos:
_ps.charPos = pos;
Append:
// decode characters
int charsParsed = pos - startPos;
if (charsParsed > 0)
{
int count;
try
{
count = _incReadDecoder.Decode(_ps.chars, startPos, charsParsed);
}
catch (XmlException e)
{
ReThrow(e, (int)_incReadLineInfo.lineNo, (int)_incReadLineInfo.linePos);
return 0;
}
Debug.Assert(count == charsParsed || _incReadDecoder.IsFull, "Check if decoded consumed all characters unless it's full.");
charsDecoded += count;
if (_incReadDecoder.IsFull)
{
_incReadLeftStartPos = startPos + count;
_incReadLeftEndPos = pos;
_incReadLineInfo.linePos += count; // we have never more than 1 line cached
return charsDecoded;
}
}
}
}