File: System\Xml\Core\XmlTextReaderImplAsync.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)

using System;
using System.IO;
using System.Text;
using System.Security;
using System.Threading;
using System.Xml.Schema;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
 
using System.Threading.Tasks;
 
#if SILVERLIGHT
using System.Reflection;
#endif
 
#if SILVERLIGHT
using BufferBuilder=System.Xml.BufferBuilder;
#else 
using BufferBuilder = System.Text.StringBuilder;
#endif
 
namespace System.Xml {
 
    internal partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver {
 
        private void CheckAsyncCall() {
            if (!useAsync) {
                throw new InvalidOperationException(Res.GetString(Res.Xml_ReaderAsyncNotSetException));
            }
        }
 
        public override Task<string> GetValueAsync() {
            CheckAsyncCall();
            if (parsingFunction >= ParsingFunction.PartialTextValue) {
                return _GetValueAsync();
            }
            return Task.FromResult(curNode.StringValue);
        }
 
        private async Task<string> _GetValueAsync() {
 
            if (parsingFunction >= ParsingFunction.PartialTextValue) {
                if (parsingFunction == ParsingFunction.PartialTextValue) {
                    await FinishPartialValueAsync().ConfigureAwait(false);
                    parsingFunction = nextParsingFunction;
                }
                else {
                    await FinishOtherValueIteratorAsync().ConfigureAwait(false);
                }
            }
            return curNode.StringValue;
        }
 
        private Task FinishInitAsync() {
            switch (laterInitParam.initType) {
                case InitInputType.UriString:
                    return FinishInitUriStringAsync();
                case InitInputType.Stream:
                    return FinishInitStreamAsync();
                case InitInputType.TextReader:
                    return FinishInitTextReaderAsync();
                default:
                    //should never hit here
                    Debug.Assert(false, "Invalid InitInputType");
                    return AsyncHelper.DoneTask;
            }
        }
 
        
        private async Task FinishInitUriStringAsync() {
 
            Stream stream = (Stream)(await laterInitParam.inputUriResolver.GetEntityAsync(laterInitParam.inputbaseUri, string.Empty, typeof(Stream)).ConfigureAwait(false));
            
            if (stream == null) {
                throw new XmlException(Res.Xml_CannotResolveUrl, laterInitParam.inputUriStr);
            }
 
            Encoding enc = null;
            // get Encoding from XmlParserContext
            if (laterInitParam.inputContext != null) {
                enc = laterInitParam.inputContext.Encoding;
            }
 
            try {
                // init ParsingState
                await InitStreamInputAsync(laterInitParam.inputbaseUri, reportedBaseUri, stream, null, 0, enc).ConfigureAwait(false);
 
                reportedEncoding = ps.encoding;
 
                // parse DTD
                if (laterInitParam.inputContext != null && laterInitParam.inputContext.HasDtdInfo) {
                    await ProcessDtdFromParserContextAsync(laterInitParam.inputContext).ConfigureAwait(false);
                }
            }
            catch {
                stream.Close();
                throw;
            }
            laterInitParam = null;
        }
 
 
        private async Task FinishInitStreamAsync() {
 
            Encoding enc = null;
 
            // get Encoding from XmlParserContext
            if (laterInitParam.inputContext != null) {
                enc = laterInitParam.inputContext.Encoding;
            }
 
            // init ParsingState
            await InitStreamInputAsync(laterInitParam.inputbaseUri, reportedBaseUri, laterInitParam.inputStream, laterInitParam.inputBytes, laterInitParam.inputByteCount, enc).ConfigureAwait(false);
 
            reportedEncoding = ps.encoding;
 
            // parse DTD
            if (laterInitParam.inputContext != null && laterInitParam.inputContext.HasDtdInfo) {
                await ProcessDtdFromParserContextAsync(laterInitParam.inputContext).ConfigureAwait(false);
            }
            laterInitParam = null;
        }
 
        private async Task FinishInitTextReaderAsync() {
 
             // init ParsingState
            await InitTextReaderInputAsync(reportedBaseUri, laterInitParam.inputTextReader).ConfigureAwait(false);
 
            reportedEncoding = ps.encoding;
 
            // parse DTD
            if (laterInitParam.inputContext != null && laterInitParam.inputContext.HasDtdInfo) {
                await ProcessDtdFromParserContextAsync(laterInitParam.inputContext).ConfigureAwait(false);
            }
 
            laterInitParam = null;
        }
 
        // Reads next node from the input data
        public override Task<bool> ReadAsync() {
            CheckAsyncCall();
 
            if (laterInitParam != null) {
                return FinishInitAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
            }
 
            for (; ; ) {
                switch (parsingFunction) {
                    case ParsingFunction.ElementContent:
                        return ParseElementContentAsync();
                    case ParsingFunction.DocumentContent:
                        return ParseDocumentContentAsync();
#if !SILVERLIGHT // Needed only for XmlTextReader
                    //XmlTextReader can't execute Async method.
                    case ParsingFunction.OpenUrl:
                        Debug.Assert(false);
                        break;
#endif
                    case ParsingFunction.SwitchToInteractive:
                        Debug.Assert(!ps.appendMode);
                        readState = ReadState.Interactive;
                        parsingFunction = nextParsingFunction;
                        continue;
                    case ParsingFunction.SwitchToInteractiveXmlDecl:
                        return ReadAsync_SwitchToInteractiveXmlDecl();
                    case ParsingFunction.ResetAttributesRootLevel:
                        ResetAttributes();
                        curNode = nodes[index];
                        parsingFunction = (index == 0) ? ParsingFunction.DocumentContent : ParsingFunction.ElementContent;
                        continue;
                    case ParsingFunction.MoveToElementContent:
                        ResetAttributes();
                        index++;
                        curNode = AddNode(index, index);
                        parsingFunction = ParsingFunction.ElementContent;
                        continue;
                    case ParsingFunction.PopElementContext:
                        PopElementContext();
                        parsingFunction = nextParsingFunction;
                        Debug.Assert(parsingFunction == ParsingFunction.ElementContent ||
                                      parsingFunction == ParsingFunction.DocumentContent);
                        continue;
                    case ParsingFunction.PopEmptyElementContext:
                        curNode = nodes[index];
                        Debug.Assert(curNode.type == XmlNodeType.Element);
                        curNode.IsEmptyElement = false;
                        ResetAttributes();
                        PopElementContext();
                        parsingFunction = nextParsingFunction;
                        continue;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                    case ParsingFunction.EntityReference:
                        parsingFunction = nextParsingFunction;
                        return ParseEntityReferenceAsync().ReturnTaskBoolWhenFinish(true);
                    case ParsingFunction.ReportEndEntity:
                        SetupEndEntityNodeInContent();
                        parsingFunction = nextParsingFunction;
                        return AsyncHelper.DoneTaskTrue;
                    case ParsingFunction.AfterResolveEntityInContent:
                        curNode = AddNode(index, index);
                        reportedEncoding = ps.encoding;
                        reportedBaseUri = ps.baseUriStr;
                        parsingFunction = nextParsingFunction;
                        continue;
                    case ParsingFunction.AfterResolveEmptyEntityInContent:
                        curNode = AddNode(index, index);
                        curNode.SetValueNode(XmlNodeType.Text, string.Empty);
                        curNode.SetLineInfo(ps.lineNo, ps.LinePos);
                        reportedEncoding = ps.encoding;
                        reportedBaseUri = ps.baseUriStr;
                        parsingFunction = nextParsingFunction;
                        return AsyncHelper.DoneTaskTrue;
#endif
                    case ParsingFunction.InReadAttributeValue:
                        FinishAttributeValueIterator();
                        curNode = nodes[index];
                        continue;
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
                    case ParsingFunction.InIncrementalRead:
                        FinishIncrementalRead();
                        return AsyncHelper.DoneTaskTrue;
                    case ParsingFunction.FragmentAttribute:
                        return Task.FromResult( ParseFragmentAttribute() );
                    case ParsingFunction.XmlDeclarationFragment:
                        ParseXmlDeclarationFragment();
                        parsingFunction = ParsingFunction.GoToEof;
                        return AsyncHelper.DoneTaskTrue;
#endif
                    case ParsingFunction.GoToEof:
                        OnEof();
                        return AsyncHelper.DoneTaskFalse;
                    case ParsingFunction.Error:
                    case ParsingFunction.Eof:
                    case ParsingFunction.ReaderClosed:
                        return AsyncHelper.DoneTaskFalse;
                    case ParsingFunction.NoData:
                        ThrowWithoutLineInfo(Res.Xml_MissingRoot);
                        return AsyncHelper.DoneTaskFalse;
                    case ParsingFunction.PartialTextValue:
                        return SkipPartialTextValueAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
                    case ParsingFunction.InReadValueChunk:
                        return FinishReadValueChunkAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
                    case ParsingFunction.InReadContentAsBinary:
                        return FinishReadContentAsBinaryAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
                    case ParsingFunction.InReadElementContentAsBinary:
                        return FinishReadElementContentAsBinaryAsync().CallBoolTaskFuncWhenFinish(ReadAsync);
                    default:
                        Debug.Assert(false);
                        break;
                }
            }
        }
 
        private Task<bool> ReadAsync_SwitchToInteractiveXmlDecl() {
            readState = ReadState.Interactive;
            parsingFunction = nextParsingFunction;
            Task<bool> task = ParseXmlDeclarationAsync(false);
            if (task.IsSuccess()) {
                return ReadAsync_SwitchToInteractiveXmlDecl_Helper(task.Result);
            }
            else {
                return _ReadAsync_SwitchToInteractiveXmlDecl(task);
            }
        }
 
        private async Task<bool> _ReadAsync_SwitchToInteractiveXmlDecl(Task<bool> task) {
            bool result = await task.ConfigureAwait(false);
            return await ReadAsync_SwitchToInteractiveXmlDecl_Helper(result).ConfigureAwait(false);
        }
 
        private Task<bool> ReadAsync_SwitchToInteractiveXmlDecl_Helper(bool finish) {
            if (finish) {
                reportedEncoding = ps.encoding;
                return AsyncHelper.DoneTaskTrue;
            }
            else {
                reportedEncoding = ps.encoding;
                return ReadAsync();
            }
        }
 
 
        // Skips the current node. If on element, skips to the end tag of the element.
        public override async Task SkipAsync() {
            CheckAsyncCall();
            if ( readState != ReadState.Interactive )
                return;
 
            if ( InAttributeValueIterator ) {
                FinishAttributeValueIterator();
                curNode = nodes[index];
            }
            else {
                switch ( parsingFunction ) {
                    case ParsingFunction.InReadAttributeValue:
                        Debug.Assert( false );
                        break;
#if !SILVERLIGHT // Needed only for XmlTextReader (ReadChars, ReadBase64, ReadBinHex)
                    case ParsingFunction.InIncrementalRead:
                        FinishIncrementalRead();
                        break;
#endif
                    case ParsingFunction.PartialTextValue:
                        await SkipPartialTextValueAsync().ConfigureAwait(false);
                        break;
                    case ParsingFunction.InReadValueChunk:
                        await FinishReadValueChunkAsync().ConfigureAwait(false);
                        break;
                    case ParsingFunction.InReadContentAsBinary:
                        await FinishReadContentAsBinaryAsync().ConfigureAwait(false);
                        break;
                    case ParsingFunction.InReadElementContentAsBinary:
                        await FinishReadElementContentAsBinaryAsync().ConfigureAwait(false);
                        break;
                }
            }
 
            switch ( curNode.type ) {
                // skip subtree
                case XmlNodeType.Element:
                    if ( curNode.IsEmptyElement ) {
                        break;
                    }
                    int initialDepth = index;
                    parsingMode = ParsingMode.SkipContent;
                    // skip content
                    while ( await outerReader.ReadAsync().ConfigureAwait(false) && index > initialDepth ) ;
                    Debug.Assert( curNode.type == XmlNodeType.EndElement );
                    Debug.Assert( parsingFunction != ParsingFunction.Eof );
                    parsingMode = ParsingMode.Full;
                    break;
                case XmlNodeType.Attribute:
                    outerReader.MoveToElement();
                    goto case XmlNodeType.Element;
            }
            // move to following sibling node
            await outerReader.ReadAsync().ConfigureAwait(false);
            return;
        }
 
        private async Task<int> ReadContentAsBase64_AsyncHelper(Task<bool> task, byte[] buffer, int index, int count) {
            await task.ConfigureAwait(false);
            if (!task.Result) {
                return 0;
            }
            else {
                // setup base64 decoder
                InitBase64Decoder();
 
                // read binary data
                return await ReadContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false);
            }
        }
 
        // Reads and concatenates content nodes, base64-decodes the results and copies the decoded bytes into the provided buffer
        public override Task< int > ReadContentAsBase64Async( byte[] buffer, int index, int count ) {
            CheckAsyncCall();
            // check arguments
            if ( buffer == null ) {
                throw new ArgumentNullException( "buffer" );
            }
            if ( count < 0 ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
            if ( index < 0 ) {
                throw new ArgumentOutOfRangeException( "index" );
            }
            if ( buffer.Length - index < count ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
 
            // if not the first call to ReadContentAsBase64 
            if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
                // and if we have a correct decoder
                if ( incReadDecoder == base64Decoder ) {
                    // read more binary data
                    return ReadContentAsBinaryAsync( buffer, index, count );
                }
            }
            // first call of ReadContentAsBase64 -> initialize (move to first text child (for elements) and initialize incremental read state)
            else {
                if ( readState != ReadState.Interactive ) {
                    return AsyncHelper.DoneTaskZero;
                }
                if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
                    throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
                }
                if ( !XmlReader.CanReadContentAs( curNode.type ) ) {
                    throw CreateReadContentAsException( "ReadContentAsBase64" );
                }
 
                Task<bool> task = InitReadContentAsBinaryAsync();
                if (task.IsSuccess()) {
                    if (!task.Result) {
                        return AsyncHelper.DoneTaskZero;
                    }
                }
                else {
                    return ReadContentAsBase64_AsyncHelper(task, buffer, index, count);
                }
            }
    
            // setup base64 decoder
            InitBase64Decoder();
 
            // read binary data
            return ReadContentAsBinaryAsync( buffer, index, count );
        }
 
        // Reads and concatenates content nodes, binhex-decodes the results and copies the decoded bytes into the provided buffer
        public override async Task< int > ReadContentAsBinHexAsync( byte[] buffer, int index, int count ) {
            CheckAsyncCall();
            // check arguments
            if ( buffer == null ) {
                throw new ArgumentNullException( "buffer" );
            }
            if ( count < 0 ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
            if ( index < 0 ) {
                throw new ArgumentOutOfRangeException( "index" );
            }
            if ( buffer.Length - index < count ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
 
            // if not the first call to ReadContentAsBinHex 
            if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
                // and if we have a correct decoder
                if ( incReadDecoder == binHexDecoder ) {
                    // read more binary data
                    return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false);
                }
            }
            // first call of ReadContentAsBinHex -> initialize (move to first text child (for elements) and initialize incremental read state)
            else {
                if ( readState != ReadState.Interactive ) {
                    return 0;
                }
                if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
                    throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
                }
                if ( !XmlReader.CanReadContentAs( curNode.type ) ) {
                    throw CreateReadContentAsException( "ReadContentAsBinHex" );
                }
 
                if (!await InitReadContentAsBinaryAsync().ConfigureAwait(false)) {
                    return 0;
                }
            }
    
            // setup binhex decoder (when in first ReadContentAsBinHex call or when mixed with ReadContentAsBase64)
            InitBinHexDecoder();
 
            // read binary data
            return await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false);
        }
 
        private async Task<int> ReadElementContentAsBase64Async_Helper(Task<bool> task, byte[] buffer, int index, int count) {
            await task.ConfigureAwait(false);
            if (!task.Result) {
                return 0;
            }
            else {
                // setup base64 decoder
                InitBase64Decoder();
 
                // read binary data
                return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false);
            }
        }
 
        // Reads and concatenates content of an element, base64-decodes the results and copies the decoded bytes into the provided buffer
        public override Task< int > ReadElementContentAsBase64Async( byte[] buffer, int index, int count ) {
            CheckAsyncCall();
            // check arguments
            if ( buffer == null ) {
                throw new ArgumentNullException( "buffer" );
            }
            if ( count < 0 ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
            if ( index < 0 ) {
                throw new ArgumentOutOfRangeException( "index" );
            }
            if ( buffer.Length - index < count ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
 
            // if not the first call to ReadContentAsBase64 
            if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
                // and if we have a correct decoder
                if ( incReadDecoder == base64Decoder ) {
                    // read more binary data
                    return ReadElementContentAsBinaryAsync(buffer, index, count);
                }
            }
            // first call of ReadElementContentAsBase64 -> initialize 
            else {
                if ( readState != ReadState.Interactive ) {
                    return AsyncHelper.DoneTaskZero;
                }
                if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
                    throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
                }
                if ( curNode.type != XmlNodeType.Element ) {
                    throw CreateReadElementContentAsException( "ReadElementContentAsBinHex" );
                }
 
                Task<bool> task = InitReadElementContentAsBinaryAsync();
                if (task.IsSuccess()) {
                    if (!task.Result) {
                        return AsyncHelper.DoneTaskZero;
                    }
                }
                else {
                    return ReadElementContentAsBase64Async_Helper(task, buffer, index, count);
                }
            }
    
            // setup base64 decoder
            InitBase64Decoder();
 
            // read binary data
            return ReadElementContentAsBinaryAsync(buffer, index, count);
        }
 
        // Reads and concatenates content of an element, binhex-decodes the results and copies the decoded bytes into the provided buffer
        public override async Task< int > ReadElementContentAsBinHexAsync( byte[] buffer, int index, int count ) {
            CheckAsyncCall();
            // check arguments
            if ( buffer == null ) {
                throw new ArgumentNullException( "buffer" );
            }
            if ( count < 0 ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
            if ( index < 0 ) {
                throw new ArgumentOutOfRangeException( "index" );
            }
            if ( buffer.Length - index < count ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
 
            // if not the first call to ReadContentAsBinHex 
            if ( parsingFunction == ParsingFunction.InReadElementContentAsBinary ) {
                // and if we have a correct decoder
                if ( incReadDecoder == binHexDecoder ) {
                    // read more binary data
                    return await ReadElementContentAsBinaryAsync(buffer, index, count).ConfigureAwait(false);
                }
            }
            // first call of ReadContentAsBinHex -> initialize
            else {
                if ( readState != ReadState.Interactive ) {
                    return 0;
                }
                if ( parsingFunction == ParsingFunction.InReadContentAsBinary ) {
                    throw new InvalidOperationException( Res.GetString( Res.Xml_MixingBinaryContentMethods ) );
                }
                if ( curNode.type != XmlNodeType.Element ) {
                    throw CreateReadElementContentAsException( "ReadElementContentAsBinHex" );
                }
                if (!await InitReadElementContentAsBinaryAsync().ConfigureAwait(false)) {
                    return 0;
                }
                
            }
    
            // setup binhex decoder (when in first ReadContentAsBinHex call or when mixed with ReadContentAsBase64)
            InitBinHexDecoder();
 
            // read binary data
            return await ReadElementContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false);
        }
 
        // Iterates over Value property and copies it into the provided buffer
        public override async Task< int > ReadValueChunkAsync( char[] buffer, int index, int count ) {
            CheckAsyncCall();
            // throw on elements
            if ( !XmlReader.HasValueInternal( curNode.type ) ) {
                throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidReadValueChunk, curNode.type ) ) ;
            }
            // check arguments
            if ( buffer == null ) {
                throw new ArgumentNullException( "buffer" );
            }
            if ( count < 0 ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
            if ( index < 0 ) {
                throw new ArgumentOutOfRangeException( "index" );
            }
            if ( buffer.Length - index < count ) {
                throw new ArgumentOutOfRangeException( "count" );
            }
 
            // first call of ReadValueChunk -> initialize incremental read state
            if ( parsingFunction != ParsingFunction.InReadValueChunk ) {
                if ( readState != ReadState.Interactive ) {
                    return 0;
                }
                if ( parsingFunction == ParsingFunction.PartialTextValue ) {
                    incReadState = IncrementalReadState.ReadValueChunk_OnPartialValue;
                }
                else {
                    incReadState = IncrementalReadState.ReadValueChunk_OnCachedValue;
                    nextNextParsingFunction = nextParsingFunction;
                    nextParsingFunction = parsingFunction;
                }
                parsingFunction = ParsingFunction.InReadValueChunk;
                readValueOffset = 0;
            }
 
            if ( count == 0 ) {
                return 0;
            }
 
            // read what is already cached in curNode
            int readCount = 0;
            int read = curNode.CopyTo( readValueOffset, buffer, index + readCount, count - readCount );
            readCount += read;
            readValueOffset += read;
 
            if ( readCount == count ) {
                // take care of surrogate pairs spanning between buffers
                char ch = buffer[index + count - 1];
                if ( XmlCharType.IsHighSurrogate(ch) ) {
                    readCount--;
                    readValueOffset--;
                    if ( readCount == 0 ) {
                        Throw( Res.Xml_NotEnoughSpaceForSurrogatePair );
                    }
                }
                return readCount;
            }
 
            // if on partial value, read the rest of it
            if ( incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue ) {
                curNode.SetValue( string.Empty );
 
                // read next chunk of text
                bool endOfValue = false;
                int startPos = 0;
                int endPos = 0;
                while ( readCount < count && !endOfValue ) {
                    int orChars = 0;
 
                    var tuple_0 = await ParseTextAsync(orChars).ConfigureAwait(false);
                    startPos = tuple_0.Item1;
                    endPos = tuple_0.Item2;
                    orChars = tuple_0.Item3;
 
                    endOfValue = tuple_0.Item4;
 
                    int copyCount = count - readCount;
                    if ( copyCount > endPos - startPos ) {
                        copyCount = endPos - startPos;
                    }
                    BlockCopyChars( ps.chars, startPos, buffer, ( index + readCount ), copyCount ); 
 
                    readCount += copyCount;
                    startPos += copyCount;
                }
 
                incReadState = endOfValue ? IncrementalReadState.ReadValueChunk_OnCachedValue : IncrementalReadState.ReadValueChunk_OnPartialValue;
 
                if ( readCount == count ) {
                    char ch = buffer[index + count - 1];
                    if ( XmlCharType.IsHighSurrogate(ch) ) {
                        readCount--;
                        startPos--;
                        if ( readCount == 0 ) {
                            Throw( Res.Xml_NotEnoughSpaceForSurrogatePair );
                        }
                    }
                }
 
                readValueOffset = 0;
                curNode.SetValue( ps.chars, startPos, endPos - startPos );
            }
            return readCount;
        }
 
        internal Task< int > DtdParserProxy_ReadDataAsync() {
            CheckAsyncCall();
            return this.ReadDataAsync();
        }
 
        internal async Task< int > DtdParserProxy_ParseNumericCharRefAsync( BufferBuilder internalSubsetBuilder ) {
            CheckAsyncCall();
 
            var tuple_1 = await this.ParseNumericCharRefAsync( true,  internalSubsetBuilder).ConfigureAwait(false);
            return tuple_1.Item2;
 
        }
 
        internal Task< int > DtdParserProxy_ParseNamedCharRefAsync( bool expand, BufferBuilder internalSubsetBuilder ) {
            CheckAsyncCall();
            return this.ParseNamedCharRefAsync( expand, internalSubsetBuilder );
        }
 
        internal async Task DtdParserProxy_ParsePIAsync( BufferBuilder sb ) {
            CheckAsyncCall();
            if ( sb == null ) {
                ParsingMode pm = parsingMode;
                parsingMode = ParsingMode.SkipNode;
                await ParsePIAsync( null ).ConfigureAwait(false);
                parsingMode = pm;
            }
            else {
                await ParsePIAsync( sb ).ConfigureAwait(false);
            }
        }
        
        internal async Task DtdParserProxy_ParseCommentAsync( BufferBuilder sb ) {
            CheckAsyncCall();
            Debug.Assert( parsingMode == ParsingMode.Full );
 
            try {
                if ( sb == null ) {
                    ParsingMode savedParsingMode = parsingMode;
                    parsingMode = ParsingMode.SkipNode;
                    await ParseCDataOrCommentAsync( XmlNodeType.Comment ).ConfigureAwait(false);
                    parsingMode = savedParsingMode;
                }
                else {
                    NodeData originalCurNode = curNode;
 
                    curNode = AddNode( index + attrCount + 1, index );
                    await ParseCDataOrCommentAsync( XmlNodeType.Comment ).ConfigureAwait(false);
                    curNode.CopyTo( 0, sb );
 
                    curNode = originalCurNode;
                }
            }
            catch ( XmlException e ) {
#if !SILVERLIGHT
                if ( e.ResString == Res.Xml_UnexpectedEOF && ps.entity != null ) {
                    SendValidationEvent( XmlSeverityType.Error, Res.Sch_ParEntityRefNesting, null, ps.LineNo, ps.LinePos );
                }   
                else {
                    throw;
                }
#else 
                throw e;
#endif
            }
        }
 
        internal async Task< Tuple<int, bool> > DtdParserProxy_PushEntityAsync(IDtdEntityInfo entity) {
            CheckAsyncCall();
            int entityId;
 
            bool retValue;
            if ( entity.IsExternal ) {
                if ( IsResolverNull ) {
                    entityId = -1;
 
                    return new Tuple<int, bool>(entityId, false);
 
                }
                retValue = await PushExternalEntityAsync( entity ).ConfigureAwait(false);
            }
            else {
                PushInternalEntity( entity );
                retValue = true;
            }
            entityId = ps.entityId;
 
            return new Tuple<int, bool>(entityId, retValue);
 
        }
 
        // SxS: The caller did not provide any SxS sensitive name or resource. No resource is being exposed either. 
        // It is OK to suppress SxS warning.
#if !SILVERLIGHT
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
#endif
        internal async Task< bool > DtdParserProxy_PushExternalSubsetAsync( string systemId, string publicId ) {
            CheckAsyncCall();
            Debug.Assert( parsingStatesStackTop == -1 );
            Debug.Assert( ( systemId != null && systemId.Length > 0 ) || ( publicId != null && publicId.Length > 0 ) );
 
            if (IsResolverNull) {
                return false;
            }
 
            // Resolve base URI
            if (ps.baseUri == null && !string.IsNullOrEmpty(ps.baseUriStr)) {
                ps.baseUri = xmlResolver.ResolveUri(null, ps.baseUriStr);
            }
            await PushExternalEntityOrSubsetAsync( publicId,  systemId, ps.baseUri,  null ).ConfigureAwait(false);
 
            ps.entity = null;
            ps.entityId = 0;
 
            Debug.Assert( ps.appendMode );
            int initialPos = ps.charPos;
            if ( v1Compat ) {
                await EatWhitespacesAsync( null ).ConfigureAwait(false);
            }
            if ( !await ParseXmlDeclarationAsync( true ).ConfigureAwait(false) ) {
                ps.charPos = initialPos;
            }
 
            return true;        
        }
 
        private Task InitStreamInputAsync( Uri baseUri, Stream stream, Encoding encoding ) {
            Debug.Assert( baseUri != null );
            return InitStreamInputAsync( baseUri, baseUri.ToString(), stream, null, 0, encoding );
        }
 
#if !SILVERLIGHT
        private Task InitStreamInputAsync( Uri baseUri, string baseUriStr, Stream stream, Encoding encoding ) {
            return InitStreamInputAsync( baseUri, baseUriStr, stream, null, 0, encoding );
        }
#endif
 
        private async Task InitStreamInputAsync( Uri baseUri, string baseUriStr, Stream stream, byte[] bytes, int byteCount, Encoding encoding ) {
            Debug.Assert( ps.charPos == 0 && ps.charsUsed == 0 && ps.textReader == null );
            Debug.Assert( baseUriStr != null );
            Debug.Assert( baseUri == null || ( baseUri.ToString().Equals( baseUriStr ) ) );
 
            ps.stream = stream;
            ps.baseUri = baseUri;
            ps.baseUriStr = baseUriStr;
 
            // take over the byte buffer allocated in XmlReader.Create, if available
            int bufferSize;
            if ( bytes != null ) {
                ps.bytes = bytes;
                ps.bytesUsed = byteCount;
                bufferSize = ps.bytes.Length;
            }
            else {
                // allocate the byte buffer 
 
                if (laterInitParam != null && laterInitParam.useAsync) {
                    bufferSize = AsyncBufferSize;
                }
                else {
                    bufferSize = XmlReader.CalcBufferSize(stream);
                }
 
                if ( ps.bytes == null || ps.bytes.Length < bufferSize ) {
                    ps.bytes = new byte[ bufferSize ];
                }
            }
 
            // allocate char buffer
            if ( ps.chars == null || ps.chars.Length < bufferSize + 1 ) {
                ps.chars = new char[ bufferSize + 1 ];
            }
 
            // make sure we have at least 4 bytes to detect the encoding (no preamble of System.Text supported encoding is longer than 4 bytes)
            ps.bytePos = 0;
            while ( ps.bytesUsed < 4 && ps.bytes.Length - ps.bytesUsed > 0 ) {
                int read = await stream.ReadAsync( ps.bytes, ps.bytesUsed, ps.bytes.Length - ps.bytesUsed ).ConfigureAwait(false);
                if ( read == 0 ) {
                    ps.isStreamEof = true;
                    break;
                }
                ps.bytesUsed += read;
            } 
 
            // detect & setup encoding
            if ( encoding == null ) {
                encoding = DetectEncoding();
            }
            SetupEncoding( encoding );
 
            // eat preamble 
            byte[] preamble = ps.encoding.GetPreamble();
            int preambleLen = preamble.Length;
            int i;
            for ( i = 0; i < preambleLen && i < ps.bytesUsed; i++ ) {
                if ( ps.bytes[i] != preamble[i] ) {
                    break;
                }
            }
            if ( i == preambleLen ) {
                ps.bytePos = preambleLen; 
            }
 
            documentStartBytePos = ps.bytePos;
 
            ps.eolNormalized = !normalize;
 
            // decode first characters
            ps.appendMode = true;
            await ReadDataAsync().ConfigureAwait(false);
        }
        private Task InitTextReaderInputAsync( string baseUriStr, TextReader input ) {
            return InitTextReaderInputAsync( baseUriStr, null, input );
        }
 
        private Task InitTextReaderInputAsync( string baseUriStr, Uri baseUri, TextReader input ) {
            Debug.Assert( ps.charPos == 0 && ps.charsUsed == 0 && ps.stream == null );
            Debug.Assert( baseUriStr != null );
 
            ps.textReader = input;
            ps.baseUriStr = baseUriStr;
            ps.baseUri = baseUri;
            
            if ( ps.chars == null ) {
                int bufferSize;
#if !ASYNC
                bufferSize = XmlReader.DefaultBufferSize;
#else
                if (laterInitParam != null && laterInitParam.useAsync) {
                    bufferSize = XmlReader.AsyncBufferSize;
                }
                else {
                    bufferSize = XmlReader.DefaultBufferSize;
                }
#endif
                ps.chars = new char[bufferSize + 1];
            }
 
            ps.encoding = Encoding.Unicode;
            ps.eolNormalized = !normalize;
 
            // read first characters
            ps.appendMode = true;
            return ReadDataAsync();
        }
 
        private Task ProcessDtdFromParserContextAsync(XmlParserContext context) {
            Debug.Assert( context != null && context.HasDtdInfo );
 
            switch ( dtdProcessing ) {
                case DtdProcessing.Prohibit:
                    ThrowWithoutLineInfo( Res.Xml_DtdIsProhibitedEx );
                    break;
                case DtdProcessing.Ignore:
                    // do nothing
                    break;
                case DtdProcessing.Parse:
                    return ParseDtdFromParserContextAsync();
 
                default:
                    Debug.Assert( false, "Unhandled DtdProcessing enumeration value." );
                    break;
            }
 
            return AsyncHelper.DoneTask;
 
        }
 
        // Switches the reader's encoding
        private Task SwitchEncodingAsync( Encoding newEncoding ) {
#if SILVERLIGHT 
            if ( ( newEncoding.WebName != ps.encoding.WebName || ps.decoder is SafeAsciiDecoder ) ) {
#else 
            if ( ( newEncoding.WebName != ps.encoding.WebName || ps.decoder is SafeAsciiDecoder ) && !afterResetState) {
#endif
                Debug.Assert( ps.stream != null );
                UnDecodeChars();
                ps.appendMode = false;
                SetupEncoding( newEncoding );
                return ReadDataAsync();
            }
 
            return AsyncHelper.DoneTask;
 
        }
 
        private Task SwitchEncodingToUTF8Async() {
            return SwitchEncodingAsync( new UTF8Encoding( true, true ) );
        }
 
        // Reads more data to the character buffer, discarding already parsed chars / decoded bytes.
        async Task< int > ReadDataAsync() {
            // Append Mode:  Append new bytes and characters to the buffers, do not rewrite them. Allocate new buffers
            //               if the current ones are full
            // Rewrite Mode: Reuse the buffers. If there is less than half of the char buffer left for new data, move 
            //               the characters that has not been parsed yet to the front of the buffer. Same for bytes.
 
            if ( ps.isEof ) {
                return 0;
            }
 
            int charsRead;
            if ( ps.appendMode ) {
                // the character buffer is full -> allocate a new one
                if ( ps.charsUsed == ps.chars.Length - 1 ) {
                    // invalidate node values kept in buffer - applies to attribute values only
                    for ( int i = 0; i < attrCount; i++ ) {
                        nodes[index + i + 1].OnBufferInvalidated();
                    }
 
                    char[] newChars = new char[ ps.chars.Length * 2 ];
                    BlockCopyChars( ps.chars, 0, newChars, 0, ps.chars.Length );
                    ps.chars = newChars;
                }
 
                if ( ps.stream != null ) {
                    // the byte buffer is full -> allocate a new one
                    if ( ps.bytesUsed - ps.bytePos < MaxByteSequenceLen ) {
                        if ( ps.bytes.Length - ps.bytesUsed < MaxByteSequenceLen ) {
                            byte[] newBytes = new byte[ ps.bytes.Length * 2 ];
                            BlockCopy( ps.bytes, 0, newBytes, 0, ps.bytesUsed );
                            ps.bytes = newBytes;
                        }
                    }
                }
 
                charsRead = ps.chars.Length - ps.charsUsed - 1;
                if ( charsRead > ApproxXmlDeclLength ) {
                    charsRead = ApproxXmlDeclLength;
                }
            }
            else {
                int charsLen = ps.chars.Length;
                if ( charsLen - ps.charsUsed <= charsLen/2 ) {
                    // invalidate node values kept in buffer - applies to attribute values only
                    for ( int i = 0; i < attrCount; i++ ) {
                        nodes[index + i + 1].OnBufferInvalidated();
                    }
 
                    // move unparsed characters to front, unless the whole buffer contains unparsed characters
                    int copyCharsCount = ps.charsUsed - ps.charPos;
                    if ( copyCharsCount < charsLen - 1 ) {
                        ps.lineStartPos = ps.lineStartPos - ps.charPos;
                        if ( copyCharsCount > 0 ) {
                            BlockCopyChars( ps.chars, ps.charPos, ps.chars, 0, copyCharsCount );
                        }
                        ps.charPos = 0;
                        ps.charsUsed = copyCharsCount;
                    }
                    else {
                        char[] newChars = new char[ ps.chars.Length * 2 ];
                        BlockCopyChars( ps.chars, 0, newChars, 0, ps.chars.Length );
                        ps.chars = newChars;
                    }
                }
 
                if ( ps.stream != null ) {
                    // move undecoded bytes to the front to make some space in the byte buffer
                    int bytesLeft = ps.bytesUsed - ps.bytePos;
                    if ( bytesLeft <= MaxBytesToMove  ) {
                        if ( bytesLeft == 0 ) {
                            ps.bytesUsed = 0;
                        }
                        else {
                            BlockCopy( ps.bytes, ps.bytePos, ps.bytes, 0, bytesLeft );
                            ps.bytesUsed = bytesLeft;
                        }
                        ps.bytePos = 0;
                    }
                }
                charsRead = ps.chars.Length - ps.charsUsed - 1;
            }
 
            if ( ps.stream != null ) {
                if ( !ps.isStreamEof ) {
                    // read new bytes
                    if ( ps.bytePos == ps.bytesUsed && ps.bytes.Length - ps.bytesUsed > 0 ) {
                        int read = await ps.stream.ReadAsync( ps.bytes, ps.bytesUsed, ps.bytes.Length - ps.bytesUsed ).ConfigureAwait(false);
                        if ( read == 0 ) {
                            ps.isStreamEof = true;
                        }
                        ps.bytesUsed += read;
                    }
                }
 
                int originalBytePos = ps.bytePos;
 
                // decode chars
                charsRead = GetChars( charsRead );
                if ( charsRead == 0 && ps.bytePos != originalBytePos ) {
                    // GetChars consumed some bytes but it was not enough bytes to form a character -> try again
                    return await ReadDataAsync().ConfigureAwait(false);
                }
            }
            else if ( ps.textReader != null ) {
                // read chars
                charsRead = await ps.textReader.ReadAsync( ps.chars, ps.charsUsed, ps.chars.Length - ps.charsUsed - 1 ).ConfigureAwait(false);  
                ps.charsUsed += charsRead;
            }
            else {
                charsRead = 0;
            }
 
            RegisterConsumedCharacters(charsRead, InEntity);
 
            if ( charsRead == 0 ) {
                Debug.Assert ( ps.charsUsed < ps.chars.Length );
                ps.isEof = true;
            }
            ps.chars[ ps.charsUsed ] = (char)0;
            return charsRead;
        }
 
        // Parses the xml or text declaration and switched encoding if needed
        private async Task< bool > ParseXmlDeclarationAsync( bool isTextDecl ) {
            while ( ps.charsUsed - ps.charPos < 6 ) {  // minimum "<?xml "
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    goto NoXmlDecl;
                }
            }
 
            if ( !XmlConvert.StrEqual( ps.chars, ps.charPos, 5, XmlDeclarationBegining ) ||
                 xmlCharType.IsNameSingleChar( ps.chars[ps.charPos + 5] ) 
#if XML10_FIFTH_EDITION
                 || xmlCharType.IsNCNameHighSurrogateChar( ps.chars[ps.charPos + 5] ) 
#endif
                ) {
                goto NoXmlDecl;
            }
 
            if ( !isTextDecl ) {
                curNode.SetLineInfo( ps.LineNo, ps.LinePos + 2 );
                curNode.SetNamedNode( XmlNodeType.XmlDeclaration, Xml );
            }
            ps.charPos += 5;
 
            // parsing of text declarations cannot change global stringBuidler or curNode as we may be in the middle of a text node
            Debug.Assert( stringBuilder.Length == 0 || isTextDecl );
            BufferBuilder sb = isTextDecl ? new BufferBuilder() : stringBuilder;
 
            // parse version, encoding & standalone attributes
            int xmlDeclState = 0;   // <?xml (0) version='1.0' (1) encoding='__' (2) standalone='__' (3) ?>
            Encoding encoding = null;
 
            for (;;) {
                int originalSbLen = sb.Length;
                int wsCount = await EatWhitespacesAsync( xmlDeclState == 0 ? null : sb ).ConfigureAwait(false);
 
                // end of xml declaration
                if ( ps.chars[ps.charPos] == '?' ) {
                    sb.Length = originalSbLen;
 
                    if ( ps.chars[ps.charPos + 1] == '>' ) {
                        if ( xmlDeclState == 0 ) {
                            Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
                        }
 
                        ps.charPos += 2;
                        if ( !isTextDecl ) {
                            curNode.SetValue( sb.ToString() );
                            sb.Length = 0;
 
                            nextParsingFunction = parsingFunction;
                            parsingFunction = ParsingFunction.ResetAttributesRootLevel;
                        }
 
                        // switch to encoding specified in xml declaration
                        if ( encoding == null ) {
                            if ( isTextDecl ) {
                                Throw( Res.Xml_InvalidTextDecl );
                            }
#if !SILVERLIGHT // Needed only for XmlTextReader
                            if ( afterResetState ) {
                                // check for invalid encoding switches to default encoding
                                string encodingName = ps.encoding.WebName;
                                if ( encodingName != "utf-8" && encodingName != "utf-16" &&
                                     encodingName != "utf-16BE" && !( ps.encoding is Ucs4Encoding ) ) {
                                    Throw( Res.Xml_EncodingSwitchAfterResetState, ( ps.encoding.GetByteCount( "A" ) == 1 ) ? "UTF-8" : "UTF-16" );
                                }
                            }
#endif
                            if ( ps.decoder is SafeAsciiDecoder ) {
                                await SwitchEncodingToUTF8Async().ConfigureAwait(false);
                            }
                        }
                        else {
                            await SwitchEncodingAsync( encoding ).ConfigureAwait(false);
                        }
                        ps.appendMode = false; 
                        return true;
                    }
                    else if ( ps.charPos + 1 == ps.charsUsed ) {
                        goto ReadData;
                    }
                    else {
                        ThrowUnexpectedToken( "'>'" );
                    }
                }
 
                if ( wsCount == 0 && xmlDeclState != 0 ) {
                    ThrowUnexpectedToken( "?>" );
                }
    
                // read attribute name            
                int nameEndPos = await ParseNameAsync().ConfigureAwait(false);
 
                NodeData attr = null;
                switch ( ps.chars[ps.charPos] ) {
                    case 'v':
                        if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "version" ) && xmlDeclState == 0 ) {
                            if ( !isTextDecl ) {
                                attr = AddAttributeNoChecks( "version", 1 );
                            }
                            break;
                        }
                        goto default;
                    case 'e':
                        if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "encoding" ) && 
                            ( xmlDeclState == 1 || ( isTextDecl && xmlDeclState == 0 ) ) ) {
                            if ( !isTextDecl ) {
                                attr = AddAttributeNoChecks( "encoding", 1 );
                            }
                            xmlDeclState = 1;
                            break;
                        }
                        goto default;
                    case 's':
                        if ( XmlConvert.StrEqual( ps.chars, ps.charPos, nameEndPos - ps.charPos, "standalone" ) &&
                             ( xmlDeclState == 1 || xmlDeclState == 2 ) && !isTextDecl ) {
                            if ( !isTextDecl ) {
                                attr = AddAttributeNoChecks( "standalone", 1 );
                            }
                            xmlDeclState = 2;
                            break;
                        }
                        goto default;
                    default:
                        Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
                        break;
                }
                if ( !isTextDecl ) {
                    attr.SetLineInfo( ps.LineNo, ps.LinePos );
                }
                sb.Append( ps.chars, ps.charPos, nameEndPos - ps.charPos );
                ps.charPos = nameEndPos;
 
                // parse equals and quote char; 
                if ( ps.chars[ps.charPos] != '=' ) {
                    await EatWhitespacesAsync( sb ).ConfigureAwait(false);
                    if ( ps.chars[ps.charPos] != '=' ) {
                        ThrowUnexpectedToken( "=" );
                    }
                }
                sb.Append( '=' );
                ps.charPos++;
 
                char quoteChar = ps.chars[ps.charPos];
                if ( quoteChar != '"' && quoteChar != '\'' ) {
                    await EatWhitespacesAsync( sb ).ConfigureAwait(false);
                    quoteChar = ps.chars[ps.charPos];
                    if ( quoteChar != '"' && quoteChar != '\'' ) {
                        ThrowUnexpectedToken( "\"", "'" );
                    }
                }
                sb.Append( quoteChar );
                ps.charPos++;
                if ( !isTextDecl ) {
                    attr.quoteChar = quoteChar;
                    attr.SetLineInfo2( ps.LineNo, ps.LinePos );
                }
 
                // parse attribute value
                int pos = ps.charPos;
                char[] chars;
            Continue:
                chars = ps.chars;
 
#if SILVERLIGHT
                while (xmlCharType.IsAttributeValueChar(chars[pos])) {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
            unsafe {
                while (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0)) {
                    pos++;
                }
            }
#endif
 
                if ( ps.chars[pos] == quoteChar ) {
                    switch ( xmlDeclState ) {
                        // version
                        case 0:
#if XML10_FIFTH_EDITION
                            //  VersionNum ::= '1.' [0-9]+   (starting with XML Fifth Edition)
                            if ( pos - ps.charPos >= 3 && 
                                 ps.chars[ps.charPos] == '1' && 
                                 ps.chars[ps.charPos + 1] == '.' && 
                                 XmlCharType.IsOnlyDigits( ps.chars, ps.charPos + 2, pos - ps.charPos - 2 ) ) {
#else 
                            // VersionNum  ::=  '1.0'        (XML Fourth Edition and earlier)
                            if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "1.0" ) ) {
#endif
                                if ( !isTextDecl ) {
                                    attr.SetValue( ps.chars, ps.charPos, pos - ps.charPos );
                                }
                                xmlDeclState = 1;
                            }
                            else {
                                string badVersion = new string( ps.chars, ps.charPos, pos - ps.charPos );
                                Throw( Res.Xml_InvalidVersionNumber, badVersion );
                            }
                            break;
                        case 1:
                            string encName = new string( ps.chars, ps.charPos, pos - ps.charPos );
                            encoding = CheckEncoding( encName );
                            if ( !isTextDecl ) {
                                attr.SetValue( encName );
                            }
                            xmlDeclState = 2;
                            break;
                        case 2:
                            if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "yes" ) ) {
                                this.standalone = true;
                            }
                            else if ( XmlConvert.StrEqual( ps.chars, ps.charPos, pos - ps.charPos, "no" ) ) {
                                this.standalone = false;
                            }
                            else {
                                Debug.Assert( !isTextDecl );
                                Throw( Res.Xml_InvalidXmlDecl, ps.LineNo, ps.LinePos - 1 );
                            }
                            if ( !isTextDecl ) {
                                attr.SetValue( ps.chars, ps.charPos, pos - ps.charPos );
                            }
                            xmlDeclState = 3;
                            break;
                        default:
                            Debug.Assert( false );
                            break;
                    }
                    sb.Append( chars, ps.charPos, pos - ps.charPos );
                    sb.Append( quoteChar );
                    ps.charPos = pos + 1;
                    continue;
                }
                else if ( pos == ps.charsUsed ) {
                    if ( await ReadDataAsync().ConfigureAwait(false) != 0 ) {
                        goto Continue;
                    }
                    else {
                        Throw( Res.Xml_UnclosedQuote );
                    }
                }
                else {
                    Throw( isTextDecl ? Res.Xml_InvalidTextDecl : Res.Xml_InvalidXmlDecl );
                }
                            
            ReadData:
                if ( ps.isEof || await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    Throw( Res.Xml_UnexpectedEOF1 );
                }
            }
 
        NoXmlDecl:
            // no xml declaration
            if ( !isTextDecl ) {
                parsingFunction = nextParsingFunction;
            }
#if !SILVERLIGHT // Needed only for XmlTextReader
            if ( afterResetState ) {
                // check for invalid encoding switches to default encoding
                string encodingName = ps.encoding.WebName;
                if ( encodingName != "utf-8" && encodingName != "utf-16" &&
                    encodingName != "utf-16BE" && !( ps.encoding is Ucs4Encoding ) ) {
                    Throw( Res.Xml_EncodingSwitchAfterResetState, ( ps.encoding.GetByteCount( "A" ) == 1 ) ? "UTF-8" : "UTF-16" );
                }
            }
#endif
            if ( ps.decoder is SafeAsciiDecoder ) {
                await SwitchEncodingToUTF8Async().ConfigureAwait(false);
            }
            ps.appendMode = false;
            return false;
        }
 
      
 
        // Parses the document content, no async keyword for perf optimize
        private Task<bool> ParseDocumentContentAsync() {
            for (; ; ) {
                bool needMoreChars = false;
                int pos = ps.charPos;
                char[] chars = ps.chars;
 
                // some tag
                if (chars[pos] == '<') {
                    needMoreChars = true;
                    if (ps.charsUsed - pos < 4) // minimum  "<a/>"
                        return ParseDocumentContentAsync_ReadData(needMoreChars);
                    pos++;
                    switch (chars[pos]) {
                        // processing instruction
                        case '?':
                            ps.charPos = pos + 1;
                            return ParsePIAsync().ContinueBoolTaskFuncWhenFalse(ParseDocumentContentAsync);
                        case '!':
                            pos++;
                            if (ps.charsUsed - pos < 2) // minimum characters expected "--"
                                return ParseDocumentContentAsync_ReadData(needMoreChars);
                            // comment
                            if (chars[pos] == '-') {
                                if (chars[pos + 1] == '-') {
                                    ps.charPos = pos + 2;
                                    return ParseCommentAsync().ContinueBoolTaskFuncWhenFalse(ParseDocumentContentAsync);
                                }
                                else {
                                    ThrowUnexpectedToken(pos + 1, "-");
                                }
                            }
                            // CDATA section
                            else if (chars[pos] == '[') {
                                if (fragmentType != XmlNodeType.Document) {
                                    pos++;
                                    if (ps.charsUsed - pos < 6) {
                                        return ParseDocumentContentAsync_ReadData(needMoreChars);
                                    }
                                    if (XmlConvert.StrEqual(chars, pos, 6, "CDATA[")) {
                                        ps.charPos = pos + 6;
                                        return ParseCDataAsync().CallBoolTaskFuncWhenFinish(ParseDocumentContentAsync_CData);
                                    }
                                    else {
                                        ThrowUnexpectedToken(pos, "CDATA[");
                                    }
                                }
                                else {
                                    Throw(ps.charPos, Res.Xml_InvalidRootData);
                                }
                            }
                            // DOCTYPE declaration
                            else {
                                if (fragmentType == XmlNodeType.Document || fragmentType == XmlNodeType.None) {
                                    fragmentType = XmlNodeType.Document;
                                    ps.charPos = pos;
                                    return ParseDoctypeDeclAsync().ContinueBoolTaskFuncWhenFalse(ParseDocumentContentAsync);
                                }
                                else {
                                    if (ParseUnexpectedToken(pos) == "DOCTYPE") {
                                        Throw(Res.Xml_BadDTDLocation);
                                    }
                                    else {
                                        ThrowUnexpectedToken(pos, "<!--", "<[CDATA[");
                                    }
                                }
                            }
                            break;
                        case '/':
                            Throw(pos + 1, Res.Xml_UnexpectedEndTag);
                            break;
                        // document element start tag
                        default:
                            if (rootElementParsed) {
                                if (fragmentType == XmlNodeType.Document) {
                                    Throw(pos, Res.Xml_MultipleRoots);
                                }
                                if (fragmentType == XmlNodeType.None) {
                                    fragmentType = XmlNodeType.Element;
                                }
                            }
                            ps.charPos = pos;
                            rootElementParsed = true;
                            return ParseElementAsync().ReturnTaskBoolWhenFinish(true);
                    }
                }
                else if (chars[pos] == '&') {
                    return ParseDocumentContentAsync_ParseEntity();
                }
                // end of buffer
                else if (pos == ps.charsUsed || (v1Compat && chars[pos] == 0x0)) {
                    return ParseDocumentContentAsync_ReadData(needMoreChars);
                }
                // something else -> root level whitespaces
                else {
                    if (fragmentType == XmlNodeType.Document) {
                        return ParseRootLevelWhitespaceAsync().ContinueBoolTaskFuncWhenFalse(ParseDocumentContentAsync);
                    }
                    else {
                        return ParseDocumentContentAsync_WhiteSpace();
                    }
                }
 
                Debug.Assert(pos == ps.charsUsed && !ps.isEof);
            }
        }
 
        private Task<bool> ParseDocumentContentAsync_CData() {
            if (fragmentType == XmlNodeType.None) {
                fragmentType = XmlNodeType.Element;
            }
            return AsyncHelper.DoneTaskTrue;
        }
 
        private async Task<bool> ParseDocumentContentAsync_ParseEntity() {
 
            int pos = ps.charPos;
 
            if (fragmentType == XmlNodeType.Document) {
                Throw(pos, Res.Xml_InvalidRootData);
                return false;
            }
            else {
                if (fragmentType == XmlNodeType.None) {
                    fragmentType = XmlNodeType.Element;
                }
                
                var tuple_3 = await HandleEntityReferenceAsync(false, EntityExpandType.OnlyGeneral).ConfigureAwait(false);
 
                switch (tuple_3.Item2) {
 
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                    case EntityType.Unexpanded:
                        if (parsingFunction == ParsingFunction.EntityReference) {
                            parsingFunction = nextParsingFunction;
                        }
                        await ParseEntityReferenceAsync().ConfigureAwait(false);
                        return true;
#endif
                    case EntityType.CharacterDec:
                    case EntityType.CharacterHex:
                    case EntityType.CharacterNamed:
                        if (await ParseTextAsync().ConfigureAwait(false)) {
                            return true;
                        }
                        return await ParseDocumentContentAsync().ConfigureAwait(false);
                    default:
                        return await ParseDocumentContentAsync().ConfigureAwait(false);
                }
            }
 
        }
 
        private Task<bool> ParseDocumentContentAsync_WhiteSpace() {
            Task<bool> task = ParseTextAsync();
            if (task.IsSuccess()) {
                if (task.Result) {
                    if (fragmentType == XmlNodeType.None && curNode.type == XmlNodeType.Text) {
                        fragmentType = XmlNodeType.Element;
                    }
                    return AsyncHelper.DoneTaskTrue;
                }
                else {
                    return ParseDocumentContentAsync();
                }
            }
            else {
                return _ParseDocumentContentAsync_WhiteSpace(task);
            }
        }
 
        private async Task<bool> _ParseDocumentContentAsync_WhiteSpace(Task<bool> task) {
            if (await task.ConfigureAwait(false)) {
                if (fragmentType == XmlNodeType.None && curNode.type == XmlNodeType.Text) {
                    fragmentType = XmlNodeType.Element;
                }
                return true;
            }
            return await ParseDocumentContentAsync().ConfigureAwait(false);
        }
 
        private async Task<bool> ParseDocumentContentAsync_ReadData(bool needMoreChars) {
            // read new characters into the buffer
            if (await ReadDataAsync().ConfigureAwait(false) != 0) {
                return await ParseDocumentContentAsync().ConfigureAwait(false);
            }
            else {
                if (needMoreChars) {
                    Throw(Res.Xml_InvalidRootData);
                }
 
                if (InEntity) {
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                        HandleEntityEnd( true );
#else
                    if (HandleEntityEnd(true)) {
                        SetupEndEntityNodeInContent();
                        return true;
                    }
#endif
                    return await ParseDocumentContentAsync().ConfigureAwait(false);
                }
                Debug.Assert(index == 0);
 
                if (!rootElementParsed && fragmentType == XmlNodeType.Document) {
                    ThrowWithoutLineInfo(Res.Xml_MissingRoot);
                }
                if (fragmentType == XmlNodeType.None) {
                    fragmentType = rootElementParsed ? XmlNodeType.Document : XmlNodeType.Element;
                }
                OnEof();
                return false;
            }
        }
 
        
        // Parses element content
        private Task<bool> ParseElementContentAsync() {
            for (; ; ) {
                int pos = ps.charPos;
                char[] chars = ps.chars;
 
                switch (chars[pos]) {
                    // some tag
                    case '<':
                        switch (chars[pos + 1]) {
                            // processing instruction
                            case '?':
                                ps.charPos = pos + 2;
                                return ParsePIAsync().ContinueBoolTaskFuncWhenFalse(ParseElementContentAsync);
                            case '!':
                                pos += 2;
                                if (ps.charsUsed - pos < 2)
                                    return ParseElementContent_ReadData();
                                // comment
                                if (chars[pos] == '-') {
                                    if (chars[pos + 1] == '-') {
                                        ps.charPos = pos + 2;
                                        return ParseCommentAsync().ContinueBoolTaskFuncWhenFalse(ParseElementContentAsync);
                                    }
                                    else {
                                        ThrowUnexpectedToken(pos + 1, "-");
                                    }
                                }
                                // CDATA section
                                else if (chars[pos] == '[') {
                                    pos++;
                                    if (ps.charsUsed - pos < 6) {
                                        return ParseElementContent_ReadData();
                                    }
                                    if (XmlConvert.StrEqual(chars, pos, 6, "CDATA[")) {
                                        ps.charPos = pos + 6;
                                        return ParseCDataAsync().ReturnTaskBoolWhenFinish(true);
                                    }
                                    else {
                                        ThrowUnexpectedToken(pos, "CDATA[");
                                    }
                                }
                                else {
 
                                    if (ParseUnexpectedToken(pos) == "DOCTYPE") {
                                        Throw(Res.Xml_BadDTDLocation);
                                    }
                                    else {
                                        ThrowUnexpectedToken(pos, "<!--", "<[CDATA[");
                                    }
                                }
                                break;
                            // element end tag
                            case '/':
                                ps.charPos = pos + 2;
                                return ParseEndElementAsync().ReturnTaskBoolWhenFinish(true);
                            default:
                                // end of buffer
                                if (pos + 1 == ps.charsUsed) {
                                    return ParseElementContent_ReadData();
                                }
                                else {
                                    // element start tag
                                    ps.charPos = pos + 1;
                                    return ParseElementAsync().ReturnTaskBoolWhenFinish(true);
                                }
                        }
                        break;
                    case '&':
                        return ParseTextAsync().ContinueBoolTaskFuncWhenFalse(ParseElementContentAsync);
                    default:
                        // end of buffer
                        if (pos == ps.charsUsed) {
                            return ParseElementContent_ReadData();
                        }
                        else {
                            // text node, whitespace or entity reference
                            return ParseTextAsync().ContinueBoolTaskFuncWhenFalse(ParseElementContentAsync);
                        }
                }
            }
        }
 
        private async Task<bool> ParseElementContent_ReadData() {
            // read new characters into the buffer
            if (await ReadDataAsync().ConfigureAwait(false) == 0) {
                if (ps.charsUsed - ps.charPos != 0) {
                    ThrowUnclosedElements();
                }
                if (!InEntity) {
                    if (index == 0 && fragmentType != XmlNodeType.Document) {
                        OnEof();
                        return false;
                    }
                    ThrowUnclosedElements();
                }
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                    HandleEntityEnd( true );
#else
                if (HandleEntityEnd(true)) {
                    SetupEndEntityNodeInContent();
                    return true;
                }
#endif
            }
            return await ParseElementContentAsync().ConfigureAwait(false);
        }
 
        // Parses the element start tag
        private Task ParseElementAsync() {
            int pos = ps.charPos;
            char[] chars = ps.chars;
            int colonPos = -1;
 
            curNode.SetLineInfo(ps.LineNo, ps.LinePos);
 
            // PERF: we intentionally don't call ParseQName here to parse the element name unless a special 
        // case occurs (like end of buffer, invalid name char)
        ContinueStartName:
            // check element name start char
            unsafe {
#if SILVERLIGHT
                if ( xmlCharType.IsStartNCNameSingleChar( chars[pos] ) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                if ((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0) {
#endif
                    pos++;
                }
 
#if XML10_FIFTH_EDITION
                else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], chars[pos])) {
                    pos += 2;
                }
#endif
                else {
                    goto ParseQNameSlow;
                }
            }
 
        ContinueName:
            unsafe {
                // parse element name
                for (; ; ) {
#if SILVERLIGHT
                    if ( xmlCharType.IsNCNameSingleChar( chars[pos] ) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                    if (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0)) {
#endif
                        pos++;
                    }
 
#if XML10_FIFTH_EDITION
                    else if ( pos < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], chars[pos])) {
                        pos += 2;
                    }
#endif
                    else {
                        break;
                    }
                }
            }
 
            // colon -> save prefix end position and check next char if it's name start char
            if (chars[pos] == ':') {
                if (colonPos != -1) {
                    if (supportNamespaces) {
                        Throw(pos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
                    }
                    else {
                        pos++;
                        goto ContinueName;
                    }
                }
                else {
                    colonPos = pos;
                    pos++;
                    goto ContinueStartName;
                }
            }
            else if (pos + 1 < ps.charsUsed) {
                goto SetElement;
            }
 
        ParseQNameSlow:
            Task< Tuple<int,int> > parseQNameTask = ParseQNameAsync();
            return ParseElementAsync_ContinueWithSetElement(parseQNameTask);
 
        SetElement:
            return ParseElementAsync_SetElement(colonPos, pos);
        }
 
        private Task ParseElementAsync_ContinueWithSetElement(Task<Tuple<int,int>> task) {
            if (task.IsSuccess()) {
                var tuple_4 = task.Result;
                int colonPos = tuple_4.Item1;
                int pos = tuple_4.Item2;
                return ParseElementAsync_SetElement(colonPos, pos);
            }
            else { 
                return _ParseElementAsync_ContinueWithSetElement(task);
            }
        }
 
        private async Task _ParseElementAsync_ContinueWithSetElement(Task<Tuple<int, int>> task) {
            var tuple_4 = await task.ConfigureAwait(false);
            int colonPos = tuple_4.Item1;
            int pos = tuple_4.Item2;
            await ParseElementAsync_SetElement(colonPos, pos).ConfigureAwait(false); 
        }
 
        private Task ParseElementAsync_SetElement(int colonPos, int pos) {
            char[] chars = ps.chars;
 
            // push namespace context
            namespaceManager.PushScope();
 
            // init the NodeData class
            if (colonPos == -1 || !supportNamespaces) {
                curNode.SetNamedNode(XmlNodeType.Element,
                                      nameTable.Add(chars, ps.charPos, pos - ps.charPos));
            }
            else {
                int startPos = ps.charPos;
                int prefixLen = colonPos - startPos;
                if (prefixLen == lastPrefix.Length && XmlConvert.StrEqual(chars, startPos, prefixLen, lastPrefix)) {
                    curNode.SetNamedNode(XmlNodeType.Element,
                                          nameTable.Add(chars, colonPos + 1, pos - colonPos - 1),
                                          lastPrefix,
                                          null);
                }
                else {
                    curNode.SetNamedNode(XmlNodeType.Element,
                                          nameTable.Add(chars, colonPos + 1, pos - colonPos - 1),
                                          nameTable.Add(chars, ps.charPos, prefixLen),
                                          null);
                    lastPrefix = curNode.prefix;
                }
            }
 
            char ch = chars[pos];
            // white space after element name -> there are probably some attributes
            bool isWs;
 
#if SILVERLIGHT
            isWs = xmlCharType.IsWhiteSpace(ch);
#else // Optimization due to the lack of inlining when a method uses byte*
            unsafe {
                isWs = ((xmlCharType.charProperties[ch] & XmlCharType.fWhitespace) != 0);
            }
#endif
 
            ps.charPos = pos;
            if (isWs) {
                return ParseAttributesAsync();
            }
            // no attributes
            else {
                return ParseElementAsync_NoAttributes();
            }
 
        }
 
        private Task ParseElementAsync_NoAttributes() {
            int pos = ps.charPos;
            char[] chars = ps.chars;
            char ch = chars[pos];
             // non-empty element
            if (ch == '>') {
                ps.charPos = pos + 1;
                parsingFunction = ParsingFunction.MoveToElementContent;
            }
            // empty element
            else if (ch == '/') {
                if (pos + 1 == ps.charsUsed) {
                    ps.charPos = pos;
                    return ParseElementAsync_ReadData(pos);
                }
                if (chars[pos + 1] == '>') {
                    curNode.IsEmptyElement = true;
                    nextParsingFunction = parsingFunction;
                    parsingFunction = ParsingFunction.PopEmptyElementContext;
                    ps.charPos = pos + 2;
                }
                else {
                    ThrowUnexpectedToken(pos, ">");
                }
            }
            // something else after the element name
            else {
                Throw(pos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(chars, ps.charsUsed, pos));
            }
 
            // add default attributes & strip spaces in attributes with type other than CDATA
            if (addDefaultAttributesAndNormalize) {
                AddDefaultAttributesAndNormalize();
            }
 
            // lookup element namespace
            ElementNamespaceLookup();
 
            return AsyncHelper.DoneTask;
        }
 
        private async Task ParseElementAsync_ReadData(int pos) {
            if (await ReadDataAsync().ConfigureAwait(false) == 0) {
                Throw(pos, Res.Xml_UnexpectedEOF, ">");
            }
 
            await ParseElementAsync_NoAttributes().ConfigureAwait(false);
        }
 
        private Task ParseEndElementAsync() { 
            NodeData startTagNode = nodes[index - 1];
 
            int prefLen = startTagNode.prefix.Length;
            int locLen = startTagNode.localName.Length;
 
            if (ps.charsUsed - ps.charPos < prefLen + locLen + 1) {
                return _ParseEndElmentAsync();
            }
 
            return ParseEndElementAsync_CheckNameAndParse();
        }
 
        private async Task _ParseEndElmentAsync() {
            await ParseEndElmentAsync_PrepareData().ConfigureAwait(false);
            await ParseEndElementAsync_CheckNameAndParse().ConfigureAwait(false);
        }
 
        private async Task ParseEndElmentAsync_PrepareData() {
            // check if the end tag name equals start tag name
            NodeData startTagNode = nodes[index - 1];
 
            int prefLen = startTagNode.prefix.Length;
            int locLen = startTagNode.localName.Length;
 
            while (ps.charsUsed - ps.charPos < prefLen + locLen + 1) {
                if (await ReadDataAsync().ConfigureAwait(false) == 0) {
                    break;
                }
            }
 
        }
 
        private Task ParseEndElementAsync_CheckNameAndParse() {
            NodeData startTagNode = nodes[index - 1];
            int prefLen = startTagNode.prefix.Length;
            int locLen = startTagNode.localName.Length;
 
            int nameLen;
            char[] chars = ps.chars;
            if (startTagNode.prefix.Length == 0) {
                if (!XmlConvert.StrEqual(chars, ps.charPos, locLen, startTagNode.localName)) {
                    return ThrowTagMismatchAsync(startTagNode);
                }
                nameLen = locLen;
            }
            else {
                int colonPos = ps.charPos + prefLen;
                if (!XmlConvert.StrEqual(chars, ps.charPos, prefLen, startTagNode.prefix) ||
                        chars[colonPos] != ':' ||
                        !XmlConvert.StrEqual(chars, colonPos + 1, locLen, startTagNode.localName)) {
                    return ThrowTagMismatchAsync(startTagNode);
                }
                nameLen = locLen + prefLen + 1;
            }
            LineInfo endTagLineInfo = new LineInfo(ps.lineNo, ps.LinePos);
            return ParseEndElementAsync_Finish(nameLen, startTagNode, endTagLineInfo);
        }
 
        private enum ParseEndElementParseFunction {
            CheckEndTag,
            ReadData,
            Done
        }
 
        private ParseEndElementParseFunction parseEndElement_NextFunc;
 
        private Task ParseEndElementAsync_Finish(int nameLen, NodeData startTagNode, LineInfo endTagLineInfo) {
            Task task = ParseEndElementAsync_CheckEndTag(nameLen, startTagNode, endTagLineInfo);
            while (true) {
                if (!task.IsSuccess()) {
                    return ParseEndElementAsync_Finish(task, nameLen, startTagNode, endTagLineInfo);
                }
 
                switch (parseEndElement_NextFunc)
                {
                    case ParseEndElementParseFunction.CheckEndTag:
                        task = ParseEndElementAsync_CheckEndTag(nameLen, startTagNode, endTagLineInfo);
                        break;
                    case ParseEndElementParseFunction.ReadData:
                        task = ParseEndElementAsync_ReadData();
                        break;
                    case ParseEndElementParseFunction.Done:
                        return task;
                }
            }
        }
 
        private async Task ParseEndElementAsync_Finish(Task task, int nameLen, NodeData startTagNode, LineInfo endTagLineInfo) {
            
            while (true) {
                await task.ConfigureAwait(false);
                switch (parseEndElement_NextFunc) {
                    case ParseEndElementParseFunction.CheckEndTag:
                        task = ParseEndElementAsync_CheckEndTag(nameLen, startTagNode, endTagLineInfo);
                        break;
                    case ParseEndElementParseFunction.ReadData:
                        task = ParseEndElementAsync_ReadData();
                        break;
                    case ParseEndElementParseFunction.Done:
                        return;
                }
            }
        }
 
        private Task ParseEndElementAsync_CheckEndTag(int nameLen, NodeData startTagNode, LineInfo endTagLineInfo) {
 
            int pos;
            char[] chars;
            for (; ; ) {
                pos = ps.charPos + nameLen;
                chars = ps.chars;
 
                if (pos == ps.charsUsed) {
                    parseEndElement_NextFunc = ParseEndElementParseFunction.ReadData;
                    return AsyncHelper.DoneTask;
                }
 
                bool tagMismatch = false;
 
                unsafe {
#if SILVERLIGHT
                    if ( xmlCharType.IsNCNameSingleChar( chars[pos] ) ||
#else // Optimization due to the lack of inlining when a method uses byte*
                    if (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0) ||
#endif
 (chars[pos] == ':')
#if XML10_FIFTH_EDITION
                         || xmlCharType.IsNCNameHighSurrogateChar( chars[pos] ) 
#endif
) {
                        tagMismatch = true;
                    }
                }
                                
                if (tagMismatch) {
                    return ThrowTagMismatchAsync(startTagNode);
                }
                
                // eat whitespaces
                if (chars[pos] != '>') {
                    char tmpCh;
                    while (xmlCharType.IsWhiteSpace(tmpCh = chars[pos])) {
                        pos++;
                        switch (tmpCh) {
                            case (char)0xA:
                                OnNewLine(pos);
                                continue;
                            case (char)0xD:
                                if (chars[pos] == (char)0xA) {
                                    pos++;
                                }
                                else if (pos == ps.charsUsed && !ps.isEof) {
                                    break;
                                }
                                OnNewLine(pos);
                                continue;
                        }
                    }
                }
 
                if (chars[pos] == '>') {
                    break;
                }
                else if (pos == ps.charsUsed) {
                    parseEndElement_NextFunc = ParseEndElementParseFunction.ReadData;
                    return AsyncHelper.DoneTask;
                }
                else {
                    ThrowUnexpectedToken(pos, ">");
                }
 
                Debug.Assert(false, "We should never get to this point.");
            }
 
            Debug.Assert(index > 0);
            index--;
            curNode = nodes[index];
 
            // set the element data
            Debug.Assert(curNode == startTagNode);
            startTagNode.lineInfo = endTagLineInfo;
            startTagNode.type = XmlNodeType.EndElement;
            ps.charPos = pos + 1;
 
            // set next parsing function
            nextParsingFunction = (index > 0) ? parsingFunction : ParsingFunction.DocumentContent;
            parsingFunction = ParsingFunction.PopElementContext;
 
            parseEndElement_NextFunc = ParseEndElementParseFunction.Done;
            return AsyncHelper.DoneTask;
        }
 
        private async Task ParseEndElementAsync_ReadData() {
            if (await ReadDataAsync().ConfigureAwait(false) == 0) {
                ThrowUnclosedElements();
            }
            parseEndElement_NextFunc = ParseEndElementParseFunction.CheckEndTag;
            return;
        }
 
        private async Task ThrowTagMismatchAsync( NodeData startTag ) {
            if ( startTag.type == XmlNodeType.Element ) { 
                // parse the bad name
                int colonPos;
 
                var tuple_5 = await ParseQNameAsync().ConfigureAwait(false);
                colonPos = tuple_5.Item1;
 
                int endPos = tuple_5.Item2;
 
                string[] args = new string[4];
                args[0] = startTag.GetNameWPrefix( nameTable );
                args[1] = startTag.lineInfo.lineNo.ToString(CultureInfo.InvariantCulture);
                args[2] = startTag.lineInfo.linePos.ToString(CultureInfo.InvariantCulture);
                args[3] = new string( ps.chars, ps.charPos, endPos - ps.charPos );
                Throw( Res.Xml_TagMismatchEx, args );
            }
            else {
                Debug.Assert( startTag.type == XmlNodeType.EntityReference );
                Throw( Res.Xml_UnexpectedEndTag );
            }
        }
 
        // Reads the attributes
        private async Task ParseAttributesAsync() {
            int pos = ps.charPos;
            char[] chars = ps.chars;
            NodeData attr = null;
 
            Debug.Assert( attrCount == 0 );
 
            for (;;) {
                // eat whitespaces
                int lineNoDelta = 0;
                char tmpch0;
 
#if SILVERLIGHT
                {
                    while (xmlCharType.IsWhiteSpace(tmpch0 = chars[pos])) {
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[tmpch0 = chars[pos]] & XmlCharType.fWhitespace) != 0)) {
#endif
                        if ( tmpch0 == (char)0xA ) {
                            OnNewLine( pos + 1 );
                            lineNoDelta++;
                        }
                        else if ( tmpch0 == (char)0xD ) {
                            if ( chars[pos+1] == (char)0xA ) {
                                OnNewLine( pos + 2 );
                                lineNoDelta++;
                                pos++;
                            }
                            else if ( pos+1 != ps.charsUsed ) {
                                OnNewLine( pos + 1 );
                                lineNoDelta++;
                            }
                            else {
                                ps.charPos = pos;
                                goto ReadData;
                            }
                        }
                        pos++;
                    }
                }
 
                char tmpch1;
                int startNameCharSize = 0;
 
                unsafe {
#if SILVERLIGHT
                    if ( xmlCharType.IsStartNCNameSingleChar( tmpch1 = chars[pos]) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                    if ((xmlCharType.charProperties[tmpch1 = chars[pos]] & XmlCharType.fNCStartNameSC) != 0) {
#endif
                        startNameCharSize = 1;
                    }
#if XML10_FIFTH_EDITION
                    else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], tmpch1 ) ) {
                        startNameCharSize = 2;
                    }
#endif
                }
 
                if ( startNameCharSize == 0 ) {
                    // element end
                    if ( tmpch1 == '>' ) {
                        Debug.Assert( curNode.type == XmlNodeType.Element );
                        ps.charPos = pos + 1;
                        parsingFunction = ParsingFunction.MoveToElementContent;
                        goto End;
                    }
                    // empty element end
                    else if ( tmpch1 == '/' ) {
                        Debug.Assert( curNode.type == XmlNodeType.Element );
                        if ( pos+1 == ps.charsUsed ) {
                            goto ReadData;
                        }
                        if ( chars[pos+1] == '>' ) {
                            ps.charPos = pos + 2;
                            curNode.IsEmptyElement = true;
                            nextParsingFunction = parsingFunction;
                            parsingFunction = ParsingFunction.PopEmptyElementContext;
                            goto End;
                        }
                        else {
                            ThrowUnexpectedToken( pos + 1, ">" );
                        }
                    }
                    else if ( pos == ps.charsUsed ) {
                        goto ReadData;
                    }
                    else if ( tmpch1 != ':' || supportNamespaces ) {
                        Throw( pos, Res.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs( chars, ps.charsUsed, pos ) );
                    }
                }
 
                if ( pos == ps.charPos ) {
                    ThrowExpectingWhitespace(pos);
                }
                ps.charPos = pos;
 
                // save attribute name line position
                int attrNameLinePos = ps.LinePos;
 
#if DEBUG
                int attrNameLineNo = ps.LineNo;
#endif
 
                // parse attribute name
                int colonPos = -1;
    
                // PERF: we intentionally don't call ParseQName here to parse the element name unless a special 
                // case occurs (like end of buffer, invalid name char)
                pos += startNameCharSize; // start name char has already been checked
 
                // parse attribute name
            ContinueParseName:
                char tmpch2;
 
                unsafe {
                    for (;;) {
#if SILVERLIGHT
                        if ( xmlCharType.IsNCNameSingleChar( tmpch2 = chars[pos] ) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                        if (((xmlCharType.charProperties[tmpch2 = chars[pos]] & XmlCharType.fNCNameSC) != 0)) {
#endif
                            pos++;
                        }
#if XML10_FIFTH_EDITION
                        else if (pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar(chars[pos + 1], tmpch2)) {
                            pos += 2;
                        }
#endif
                        else {
                            break;
                        }
                    }
                }
 
                // colon -> save prefix end position and check next char if it's name start char
                if ( tmpch2 == ':' ) {
                    if ( colonPos != -1 ) {
                        if ( supportNamespaces ) {
                            Throw( pos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs( ':', '\0' ));
                        }
                        else {
                            pos++;
                            goto ContinueParseName;
                        }
                    }
                    else {
                        colonPos = pos;
                        pos++;
 
                        unsafe {
#if SILVERLIGHT
                            if ( xmlCharType.IsStartNCNameSingleChar( chars[pos] ) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                            if (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0)) {
#endif
                                pos++;
                                goto ContinueParseName;
                            }
#if XML10_FIFTH_EDITION
                            else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
                                pos += 2;
                                goto ContinueParseName;
                            }
#endif
                        }
 
                        // else fallback to full name parsing routine
 
                        var tuple_6 = await ParseQNameAsync().ConfigureAwait(false);
                        colonPos = tuple_6.Item1;
 
                        pos = tuple_6.Item2;
 
                        chars = ps.chars;
                    }
                }
                else if ( pos + 1 >= ps.charsUsed ) {
 
                    var tuple_7 = await ParseQNameAsync().ConfigureAwait(false);
                    colonPos = tuple_7.Item1;
 
                    pos = tuple_7.Item2;
 
                    chars = ps.chars;
                }
 
                attr = AddAttribute( pos, colonPos );
                attr.SetLineInfo( ps.LineNo, attrNameLinePos );
 
#if DEBUG
                Debug.Assert( attrNameLineNo == ps.LineNo );
#endif
 
                // parse equals and quote char; 
                if ( chars[pos] != '=' ) {
                    ps.charPos = pos;
                    await EatWhitespacesAsync( null ).ConfigureAwait(false);
                    pos = ps.charPos;
                    if ( chars[pos] != '=' ) {
                        ThrowUnexpectedToken( "=" );
                    }
                }
                pos++;
 
                char quoteChar = chars[pos];
                if ( quoteChar != '"' && quoteChar != '\'' ) {
                    ps.charPos = pos;
                    await EatWhitespacesAsync( null ).ConfigureAwait(false);
                    pos = ps.charPos;
                    quoteChar = chars[pos];
                    if ( quoteChar != '"' && quoteChar != '\'' ) {
                        ThrowUnexpectedToken( "\"", "'" );
                    }
                }
                pos++;
                ps.charPos = pos;
 
                attr.quoteChar = quoteChar;
                attr.SetLineInfo2( ps.LineNo, ps.LinePos );
 
                // parse attribute value
                char tmpch3;
#if SILVERLIGHT
                while (xmlCharType.IsAttributeValueChar(tmpch3 = chars[pos])) {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[tmpch3 = chars[pos]] & XmlCharType.fAttrValue) != 0)) {
                        pos++;
                    }
                }
#endif
                if ( tmpch3 == quoteChar ) {
#if DEBUG
#if !SILVERLIGHT
                    if ( normalize ) {
                        string val = new string( chars, ps.charPos, pos - ps.charPos );
                        Debug.Assert( val == XmlComplianceUtil.CDataNormalize( val ), "The attribute value is not CDATA normalized!" ); 
                    }
#endif
#endif
                    attr.SetValue( chars, ps.charPos, pos - ps.charPos );
                    pos++;
                    ps.charPos = pos;
                }
                else {
                    await ParseAttributeValueSlowAsync( pos, quoteChar, attr ).ConfigureAwait(false);
                    pos = ps.charPos;
                    chars = ps.chars;
                }
 
                // handle special attributes:
                if ( attr.prefix.Length == 0 ) {
                    // default namespace declaration
                    if ( Ref.Equal( attr.localName, XmlNs ) ) {
                        OnDefaultNamespaceDecl( attr );
                    }
                }
                else {
                    // prefixed namespace declaration
                    if ( Ref.Equal( attr.prefix, XmlNs ) ) {
                        OnNamespaceDecl( attr );
                    }
                    // xml: attribute
                    else if ( Ref.Equal( attr.prefix, Xml ) ) {
                        OnXmlReservedAttribute( attr );
                    }
                }
                continue;
 
            ReadData:
                ps.lineNo -= lineNoDelta;
                if ( await ReadDataAsync().ConfigureAwait(false) != 0 ) {
                    pos = ps.charPos;
                    chars = ps.chars;
                }
                else {
                    ThrowUnclosedElements();
                }
            }
 
        End:
            if ( addDefaultAttributesAndNormalize ) {
                AddDefaultAttributesAndNormalize();
            }
            // lookup namespaces: element
            ElementNamespaceLookup();
 
            // lookup namespaces: attributes
            if ( attrNeedNamespaceLookup ) {
                AttributeNamespaceLookup();
                attrNeedNamespaceLookup = false;
            }
 
            // check duplicate attributes
            if ( attrDuplWalkCount >= MaxAttrDuplWalkCount ) {
                AttributeDuplCheck();
            }
        }
 
        private async Task ParseAttributeValueSlowAsync( int curPos, char quoteChar, NodeData attr ) {
            int pos = curPos;
            char[] chars = ps.chars;
            int attributeBaseEntityId = ps.entityId;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
            int valueChunkStartPos = 0;
            LineInfo valueChunkLineInfo = new LineInfo(ps.lineNo, ps.LinePos);
            NodeData lastChunk = null;
#endif
 
            Debug.Assert( stringBuilder.Length == 0 );
 
            for (;;) {
                // parse the rest of the attribute value
#if SILVERLIGHT
                while (xmlCharType.IsAttributeValueChar(chars[pos])) {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fAttrValue) != 0)) {
                        pos++;
                    }
                }
#endif
 
                if ( pos - ps.charPos > 0 ) {
                    stringBuilder.Append( chars, ps.charPos, pos - ps.charPos );
                    ps.charPos = pos;
                }
 
                if ( chars[pos] == quoteChar && attributeBaseEntityId == ps.entityId ) {
                    break;
                }
                else {
                    switch ( chars[pos] ) {
                        // eol
                        case (char)0xA:
                            pos++;
                            OnNewLine( pos );
                            if ( normalize ) {
                                stringBuilder.Append( (char)0x20 );  // CDATA normalization of 0xA
                                ps.charPos++;
                            }
                            continue;
                        case (char)0xD:
                            if ( chars[pos+1] == (char)0xA ) {
                                pos += 2;
                                if ( normalize ) {
                                    stringBuilder.Append( ps.eolNormalized ? "\u0020\u0020" : "\u0020" ); // CDATA normalization of 0xD 0xA
                                    ps.charPos = pos;
                                }
                            }
                            else if ( pos+1 < ps.charsUsed || ps.isEof ) { 
                                pos++;
                                if ( normalize ) {
                                    stringBuilder.Append( (char)0x20 );  // CDATA normalization of 0xD and 0xD 0xA
                                    ps.charPos = pos;
                                }
                            } 
                            else {
                                goto ReadData;
                            }
                            OnNewLine( pos );
                            continue;
                        // tab
                        case (char)0x9:
                            pos++;
                            if ( normalize ) {
                                stringBuilder.Append( (char)0x20 );  // CDATA normalization of 0x9
                                ps.charPos++;
                            }
                            continue;
                        case '"':
                        case '\'':
                        case '>':
                            pos++;
                            continue;
                        // attribute values cannot contain '<'
                        case '<':
                            Throw( pos, Res.Xml_BadAttributeChar, XmlException.BuildCharExceptionArgs( '<', '\0' ) );
                            break;
                        // entity referece
                        case '&':
                            if ( pos - ps.charPos > 0 ) {
                                stringBuilder.Append( chars, ps.charPos, pos - ps.charPos );
                            }
                            ps.charPos = pos;
 
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                            int enclosingEntityId = ps.entityId;
                            LineInfo entityLineInfo = new LineInfo( ps.lineNo, ps.LinePos + 1 );
#endif
 
                            var tuple_8 = await HandleEntityReferenceAsync( true,  EntityExpandType.All).ConfigureAwait(false);
                            pos = tuple_8.Item1;
 
                            switch ( tuple_8.Item2 ) {
 
                                case EntityType.CharacterDec:
                                case EntityType.CharacterHex:
                                case EntityType.CharacterNamed:
                                    break;
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                                case EntityType.Unexpanded:
                                    if ( parsingMode == ParsingMode.Full && ps.entityId == attributeBaseEntityId ) {
                                        // construct text value chunk
                                        int valueChunkLen = stringBuilder.Length - valueChunkStartPos;
                                        if ( valueChunkLen > 0 ) {
                                            NodeData textChunk = new NodeData();
                                            textChunk.lineInfo = valueChunkLineInfo;
                                            textChunk.depth = attr.depth + 1;
                                            textChunk.SetValueNode( XmlNodeType.Text, stringBuilder.ToString( valueChunkStartPos, valueChunkLen ) );
                                            AddAttributeChunkToList( attr, textChunk, ref lastChunk );
                                        }
 
                                        // parse entity name
                                        ps.charPos++;
                                        string entityName = await ParseEntityNameAsync().ConfigureAwait(false);
                                             
                                        // construct entity reference chunk
                                        NodeData entityChunk = new NodeData();
                                        entityChunk.lineInfo = entityLineInfo;
                                        entityChunk.depth = attr.depth + 1;
                                        entityChunk.SetNamedNode( XmlNodeType.EntityReference, entityName );
                                        AddAttributeChunkToList( attr, entityChunk, ref lastChunk );
 
                                        // append entity ref to the attribute value
                                        stringBuilder.Append( '&' );
                                        stringBuilder.Append( entityName );
                                        stringBuilder.Append( ';' );
 
                                        // update info for the next attribute value chunk
                                        valueChunkStartPos = stringBuilder.Length;
                                        valueChunkLineInfo.Set( ps.LineNo, ps.LinePos );
 
                                        fullAttrCleanup = true;
                                    }
                                    else {
                                        ps.charPos++;
                                        await ParseEntityNameAsync().ConfigureAwait(false);
                                    }
                                    pos = ps.charPos;
                                    break;
 
                                case EntityType.ExpandedInAttribute:
                                    if ( parsingMode == ParsingMode.Full && enclosingEntityId == attributeBaseEntityId  ) {
                                        
                                        // construct text value chunk
                                        int valueChunkLen = stringBuilder.Length - valueChunkStartPos;
                                        if ( valueChunkLen > 0 ) {
                                            NodeData textChunk = new NodeData();
                                            textChunk.lineInfo = valueChunkLineInfo;
                                            textChunk.depth = attr.depth + 1;
                                            textChunk.SetValueNode( XmlNodeType.Text, stringBuilder.ToString( valueChunkStartPos, valueChunkLen ) );
                                            AddAttributeChunkToList( attr, textChunk, ref lastChunk );
                                        }
 
                                        // construct entity reference chunk
                                        NodeData entityChunk = new NodeData();
                                        entityChunk.lineInfo = entityLineInfo;
                                        entityChunk.depth = attr.depth + 1;
                                        entityChunk.SetNamedNode( XmlNodeType.EntityReference, ps.entity.Name );
                                        AddAttributeChunkToList( attr, entityChunk, ref lastChunk );
 
                                        fullAttrCleanup = true;
 
                                        // Note: info for the next attribute value chunk will be updated once we
                                        // get out of the expanded entity
                                    }
                                    pos = ps.charPos;
                                    break;
#endif
                                default:
                                    pos = ps.charPos;
                                    break;
                            }
                            chars = ps.chars;
                            continue;
                        default:
                            // end of buffer
                            if ( pos == ps.charsUsed ) {
                                goto ReadData;
                            }
                            // surrogate chars
                            else { 
                                char ch = chars[pos];
                                if ( XmlCharType.IsHighSurrogate(ch) ) {
                                    if ( pos + 1 == ps.charsUsed ) {
                                        goto ReadData;
                                    }
                                    pos++;
                                    if ( XmlCharType.IsLowSurrogate( chars[pos] ) ) {
                                        pos++;
                                        continue;
                                    }
                                }
                                ThrowInvalidChar( chars, ps.charsUsed, pos );
                                break;
                            }
                    }
                }
            
            ReadData:
                // read new characters into the buffer
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    if ( ps.charsUsed - ps.charPos > 0 ) {
                        if ( ps.chars[ps.charPos] != (char)0xD ) {
                            Debug.Assert( false, "We should never get to this point." );
                            Throw( Res.Xml_UnexpectedEOF1 );
                        }
                        Debug.Assert( ps.isEof );
                    }
                    else {
                        if ( !InEntity ) {
                            if ( fragmentType == XmlNodeType.Attribute ) {
                                if ( attributeBaseEntityId != ps.entityId ) {
                                    Throw( Res.Xml_EntityRefNesting );
                                }
                                break;
                            }
                            Throw( Res.Xml_UnclosedQuote );
                        }
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                        HandleEntityEnd( true );
#else 
                        if ( HandleEntityEnd( true ) ) { // no EndEntity reporting while parsing attributes
                            Debug.Assert( false );
                            Throw( Res.Xml_InternalError );
                        }
                        // update info for the next attribute value chunk
                        if ( attributeBaseEntityId == ps.entityId ) {
                            valueChunkStartPos = stringBuilder.Length;
                            valueChunkLineInfo.Set( ps.LineNo, ps.LinePos );
                        }
#endif
                    }
                }
 
                pos = ps.charPos;
                chars = ps.chars;
            }
 
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
            if ( attr.nextAttrValueChunk != null ) {
                // construct last text value chunk
                int valueChunkLen = stringBuilder.Length - valueChunkStartPos;
                if ( valueChunkLen > 0 ) {
                    NodeData textChunk = new NodeData();
                    textChunk.lineInfo = valueChunkLineInfo;
                    textChunk.depth = attr.depth + 1;
                    textChunk.SetValueNode( XmlNodeType.Text, stringBuilder.ToString( valueChunkStartPos, valueChunkLen ) );
                    AddAttributeChunkToList( attr, textChunk, ref lastChunk );
                }
            }
#endif
 
            ps.charPos = pos + 1;
 
            attr.SetValue( stringBuilder.ToString() );
            stringBuilder.Length = 0;
        }
 
        private Task<bool> ParseTextAsync() {
            int startPos;
            int endPos;
            int orChars = 0;
 
            // skip over the text if not in full parsing mode
            if (parsingMode != ParsingMode.Full) {
                return _ParseTextAsync(null);
            }
 
            curNode.SetLineInfo(ps.LineNo, ps.LinePos);
            Debug.Assert(stringBuilder.Length == 0);
 
            // the whole value is in buffer
 
            Task<Tuple<int,int,int,bool>> parseTextTask = ParseTextAsync(orChars);
            bool fullValue = false;
            if (!parseTextTask.IsSuccess()) {
                return _ParseTextAsync(parseTextTask);
            }
            else {
                var tuple_10 = parseTextTask.Result;
                startPos = tuple_10.Item1;
                endPos = tuple_10.Item2;
                orChars = tuple_10.Item3;
                fullValue = tuple_10.Item4;
            }
 
            if (fullValue) {
                if (endPos - startPos == 0) {
                    return ParseTextAsync_IgnoreNode();
                }
                XmlNodeType nodeType = GetTextNodeType(orChars);
                if (nodeType == XmlNodeType.None) {
                    return ParseTextAsync_IgnoreNode();
                }
                Debug.Assert(endPos - startPos > 0);
                curNode.SetValueNode(nodeType, ps.chars, startPos, endPos - startPos);
                return AsyncHelper.DoneTaskTrue;
            }
            // only piece of the value was returned
            else {
                return _ParseTextAsync(parseTextTask);
            }
 
        }
 
        // Parses text or white space node.
        // Returns true if a node has been parsed and its data set to curNode. 
        // Returns false when a white space has been parsed and ignored (according to current whitespace handling) or when parsing mode is not Full.
        // Also returns false if there is no text to be parsed.
        private async Task< bool > _ParseTextAsync(Task<Tuple<int,int,int,bool>> parseTask) {
            int startPos;
            int endPos;
            int orChars = 0;
 
            if (parseTask != null)
                goto Parse;
 
            // skip over the text if not in full parsing mode
            if ( parsingMode != ParsingMode.Full ) {
 
                Tuple<int,int,int,bool> tuple_9;
                do {
                    tuple_9 = await ParseTextAsync(orChars).ConfigureAwait(false);
                    startPos = tuple_9.Item1;
                    endPos = tuple_9.Item2;
                    orChars = tuple_9.Item3;
    
                } while ( !tuple_9.Item4 );
 
                goto IgnoredNode;
            }
 
            curNode.SetLineInfo( ps.LineNo, ps.LinePos ); 
            Debug.Assert( stringBuilder.Length == 0 );
            
            parseTask = ParseTextAsync(orChars);
 
        Parse:
            var tuple_10 = await parseTask.ConfigureAwait(false);
            startPos = tuple_10.Item1;
            endPos = tuple_10.Item2;
            orChars = tuple_10.Item3;
 
            if ( tuple_10.Item4 ) {
 
                if ( endPos - startPos == 0 ) {
                    goto IgnoredNode;
                }
                XmlNodeType nodeType = GetTextNodeType( orChars );
                if ( nodeType == XmlNodeType.None ) {
                    goto IgnoredNode;
                }
                Debug.Assert( endPos - startPos > 0 );
                curNode.SetValueNode( nodeType, ps.chars, startPos, endPos - startPos );
                return true;
            }
            // only piece of the value was returned
            else {
                // V1 compatibility mode -> cache the whole value
                if ( v1Compat ) {
 
                    Tuple<int,int,int,bool> tuple_11;
 
                    do {
                        if ( endPos - startPos > 0 ) {
                            stringBuilder.Append( ps.chars, startPos, endPos - startPos );
                        }
 
                        tuple_11 = await ParseTextAsync(orChars).ConfigureAwait(false);
                        startPos = tuple_11.Item1;
                        endPos = tuple_11.Item2;
                        orChars = tuple_11.Item3;
    
                    } while ( !tuple_11.Item4 );
 
                    if ( endPos - startPos > 0 ) {
                        stringBuilder.Append( ps.chars, startPos, endPos - startPos );
                    }
 
                    Debug.Assert( stringBuilder.Length > 0 );
 
                    XmlNodeType nodeType = GetTextNodeType( orChars );
                    if ( nodeType == XmlNodeType.None ) {
                        stringBuilder.Length = 0;
                        goto IgnoredNode;
                    }
 
                    curNode.SetValueNode( nodeType, stringBuilder.ToString() );
                    stringBuilder.Length = 0;
                    return true;
                }
                // V2 reader -> do not cache the whole value yet, read only up to 4kB to decide whether the value is a whitespace
                else {
                    bool fullValue = false;
 
                    // if it's a partial text value, not a whitespace -> return
                    if ( orChars > 0x20 ) {
                        Debug.Assert( endPos - startPos > 0 );
                        curNode.SetValueNode( XmlNodeType.Text, ps.chars, startPos, endPos - startPos );
                        nextParsingFunction = parsingFunction;
                        parsingFunction = ParsingFunction.PartialTextValue;
                        return true;
                    }
 
                    // partial whitespace -> read more data (up to 4kB) to decide if it is a whitespace or a text node
                    if ( endPos - startPos > 0 ) {
                        stringBuilder.Append( ps.chars, startPos, endPos - startPos );
                    }
                    do {
 
                        var tuple_12 = await ParseTextAsync(orChars).ConfigureAwait(false);
                        startPos = tuple_12.Item1;
                        endPos = tuple_12.Item2;
                        orChars = tuple_12.Item3;
 
                        fullValue = tuple_12.Item4;
 
                        if ( endPos - startPos > 0 ) {
                            stringBuilder.Append( ps.chars, startPos, endPos - startPos );
                        }
                    } while ( !fullValue && orChars <= 0x20 && stringBuilder.Length < MinWhitespaceLookahedCount );
 
                    // determine the value node type
                    XmlNodeType nodeType = ( stringBuilder.Length < MinWhitespaceLookahedCount ) ? GetTextNodeType( orChars ) : XmlNodeType.Text;
                    if ( nodeType == XmlNodeType.None ) {
                        // ignored whitespace -> skip over the rest of the value unless we already read it all
                        stringBuilder.Length = 0;
                        if ( !fullValue ) {
 
                            Tuple<int,int,int,bool> tuple_13;
                            do {
                                tuple_13 = await ParseTextAsync(orChars).ConfigureAwait(false);
                                startPos = tuple_13.Item1;
                                endPos = tuple_13.Item2;
                                orChars = tuple_13.Item3;
    
                            } while ( !tuple_13.Item4 );
 
                        }
                        goto IgnoredNode;
                    }
                    // set value to curNode
                    curNode.SetValueNode( nodeType, stringBuilder.ToString() );
                    stringBuilder.Length = 0;
 
                    // change parsing state if the full value was not parsed
                    if ( !fullValue ) {
                        nextParsingFunction = parsingFunction;
                        parsingFunction = ParsingFunction.PartialTextValue;
                    }
                    return true;
                }
            }
 
        IgnoredNode:
            return await ParseTextAsync_IgnoreNode().ConfigureAwait(false);
        }
        
        private Task<bool> ParseTextAsync_IgnoreNode() {
 
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
 
            // ignored whitespace at the end of manually resolved entity
            if (parsingFunction == ParsingFunction.ReportEndEntity) {
                SetupEndEntityNodeInContent();
                parsingFunction = nextParsingFunction;
                return AsyncHelper.DoneTaskTrue;
            }
            else if (parsingFunction == ParsingFunction.EntityReference) {
                parsingFunction = nextNextParsingFunction;
                return ParseEntityReferenceAsync().ReturnTaskBoolWhenFinish(true);
            }
#endif
            return AsyncHelper.DoneTaskFalse;
        }
 
        // Parses a chunk of text starting at ps.charPos. 
        //   startPos .... start position of the text chunk that has been parsed (can differ from ps.charPos before the call)
        //   endPos ...... end position of the text chunk that has been parsed (can differ from ps.charPos after the call)
        //   ourOrChars .. all parsed character bigger or equal to 0x20 or-ed (|) into a single int. It can be used for whitespace detection 
        //                 (the text has a non-whitespace character if outOrChars > 0x20).
        // Returns true when the whole value has been parsed. Return false when it needs to be called again to get a next chunk of value.
 
        
        private class ParseTextState { 
            public int outOrChars;
            public char[] chars;
            public int pos;
            public int rcount;
            public int rpos;
            public int orChars;
            public char c;
 
            public ParseTextState(int outOrChars, char[] chars, int pos, int rcount, int rpos, int orChars, char c) {
                this.outOrChars = outOrChars;
                this.chars = chars;
                this.pos = pos;
                this.rcount = rcount;
                this.rpos = rpos;
                this.orChars = orChars;
                this.c = c;
            }
        }
 
        private enum ParseTextFunction {
            ParseText,
            Entity,
            Surrogate,
            ReadData,
            NoValue,
            PartialValue,
        }
 
        private ParseTextFunction parseText_NextFunction;
 
        private ParseTextState lastParseTextState;
 
        private Task<Tuple<int, int, int, bool>> parseText_dummyTask = Task.FromResult(new Tuple<int, int, int, bool>(0,0,0,false));
 
        //To avoid stackoverflow like ParseText->ParseEntity->ParText->..., use a loop and parsing function to implement such call.
        private Task<Tuple<int, int, int, bool>> ParseTextAsync(int outOrChars) {
            Task<Tuple<int, int, int, bool>> task = ParseTextAsync(outOrChars, ps.chars, ps.charPos, 0, -1, outOrChars, (char)0);
            while (true)
            {
                if (!task.IsSuccess()) {
                    return ParseTextAsync_AsyncFunc(task);
                }
 
                outOrChars = lastParseTextState.outOrChars;
                char[] chars = lastParseTextState.chars;
                int pos = lastParseTextState.pos;
                int rcount = lastParseTextState.rcount;
                int rpos = lastParseTextState.rpos;
                int orChars = lastParseTextState.orChars;
                char c = lastParseTextState.c;
 
                switch (parseText_NextFunction)
                {
                    case ParseTextFunction.ParseText:
                        task = ParseTextAsync(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.Entity:
                        task = ParseTextAsync_ParseEntity(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.ReadData:
                        task = ParseTextAsync_ReadData(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.Surrogate:
                        task = ParseTextAsync_Surrogate(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.NoValue:
                        return ParseTextAsync_NoValue(outOrChars, pos);
                    case ParseTextFunction.PartialValue:
                        return ParseTextAsync_PartialValue(pos, rcount, rpos, orChars, c);
                }
            }
        }
 
        private async Task<Tuple<int, int, int, bool>> ParseTextAsync_AsyncFunc(Task<Tuple<int, int, int, bool>> task) {
 
            while (true) {
 
                await task.ConfigureAwait(false);
 
                int outOrChars = lastParseTextState.outOrChars;
                char[] chars = lastParseTextState.chars;
                int pos = lastParseTextState.pos;
                int rcount = lastParseTextState.rcount;
                int rpos = lastParseTextState.rpos;
                int orChars = lastParseTextState.orChars;
                char c = lastParseTextState.c;
 
                switch (parseText_NextFunction) {
                    case ParseTextFunction.ParseText:
                        task = ParseTextAsync(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.Entity:
                        task = ParseTextAsync_ParseEntity(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.ReadData:
                        task = ParseTextAsync_ReadData(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.Surrogate:
                        task = ParseTextAsync_Surrogate(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        break;
                    case ParseTextFunction.NoValue:
                        return await ParseTextAsync_NoValue(outOrChars, pos).ConfigureAwait(false);
                    case ParseTextFunction.PartialValue:
                        return await ParseTextAsync_PartialValue(pos, rcount, rpos, orChars, c).ConfigureAwait(false);
                }
            }
        }
 
        private Task<Tuple<int, int, int, bool>> ParseTextAsync(int outOrChars, char[] chars, int pos, int rcount, int rpos, int orChars, char c) {
            
            for (; ; ) {
                // parse text content
#if SILVERLIGHT
                while (xmlCharType.IsTextChar(c = chars[pos])) {
                    orChars |= (int)c;
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[c = chars[pos]] & XmlCharType.fText) != 0)) {
                        orChars |= (int)c;
                        pos++;
                    }
                }
#endif
                switch (c) {
                    case (char)0x9:
                        pos++;
                        continue;
                    // eol
                    case (char)0xA:
                        pos++;
                        OnNewLine(pos);
                        continue;
                    case (char)0xD:
                        if (chars[pos + 1] == (char)0xA) {
                            if (!ps.eolNormalized && parsingMode == ParsingMode.Full) {
                                if (pos - ps.charPos > 0) {
                                    if (rcount == 0) {
                                        rcount = 1;
                                        rpos = pos;
                                    }
                                    else {
                                        ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
                                        rpos = pos - rcount;
                                        rcount++;
                                    }
                                }
                                else {
                                    ps.charPos++;
                                }
                            }
                            pos += 2;
                        }
                        else if (pos + 1 < ps.charsUsed || ps.isEof) {
                            if (!ps.eolNormalized) {
                                chars[pos] = (char)0xA;             // EOL normalization of 0xD
                            }
                            pos++;
                        }
                        else {
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.ReadData;
                            return parseText_dummyTask;
                        }
                        OnNewLine(pos);
                        continue;
                    // some tag 
                    case '<':
                        lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        parseText_NextFunction = ParseTextFunction.PartialValue;
                        return parseText_dummyTask;
                    // entity reference
                    case '&':
                        lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        parseText_NextFunction = ParseTextFunction.Entity;
                        return parseText_dummyTask;
                    case ']':
                        if (ps.charsUsed - pos < 3 && !ps.isEof) {
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.ReadData;
                            return parseText_dummyTask;
                        }
                        if (chars[pos + 1] == ']' && chars[pos + 2] == '>') {
                            Throw(pos, Res.Xml_CDATAEndInText);
                        }
                        orChars |= ']';
                        pos++;
                        continue;
                    default:
                        // end of buffer
                        if (pos == ps.charsUsed) {
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.ReadData;
                            return parseText_dummyTask;
                        }
                        // surrogate chars
                        else {
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.Surrogate;
                            return parseText_dummyTask;
                        } 
                }
            }
        }
 
        private async Task<Tuple<int, int, int, bool>> ParseTextAsync_ParseEntity(int outOrChars, char[] chars, int pos, int rcount, int rpos, int orChars, char c) {
            // try to parse char entity inline
            int charRefEndPos, charCount;
            EntityType entityType;
            if ((charRefEndPos = ParseCharRefInline(pos, out charCount, out entityType)) > 0) {
                if (rcount > 0) {
                    ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
                }
                rpos = pos - rcount;
                rcount += (charRefEndPos - pos - charCount);
                pos = charRefEndPos;
 
                if (!xmlCharType.IsWhiteSpace(chars[charRefEndPos - charCount]) ||
                     (v1Compat && entityType == EntityType.CharacterDec)) {
                    orChars |= 0xFF;
                }
            }
            else {
                if (pos > ps.charPos) {
                    lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                    parseText_NextFunction = ParseTextFunction.PartialValue;
                    return parseText_dummyTask.Result;
                }
 
                var tuple_14 = await HandleEntityReferenceAsync(false, EntityExpandType.All).ConfigureAwait(false);
                pos = tuple_14.Item1;
 
                switch (tuple_14.Item2) {
 
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                    case EntityType.Unexpanded:
                        // make sure we will report EntityReference after the text node
                        nextParsingFunction = parsingFunction;
                        parsingFunction = ParsingFunction.EntityReference;
                        // end the value (returns nothing)
                        lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                        parseText_NextFunction = ParseTextFunction.NoValue;
                        return parseText_dummyTask.Result;
#endif
                    case EntityType.CharacterDec:
                        if (!v1Compat) {
                            goto case EntityType.CharacterHex;
                        }
                        orChars |= 0xFF;
                        break;
                    case EntityType.CharacterHex:
                    case EntityType.CharacterNamed:
                        if (!xmlCharType.IsWhiteSpace(ps.chars[pos - 1])) {
                            orChars |= 0xFF;
                        }
                        break;
                    default:
                        pos = ps.charPos;
                        break;
                }
                chars = ps.chars;
            }
 
            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
            parseText_NextFunction = ParseTextFunction.ParseText;
            return parseText_dummyTask.Result;
        }
        
        private async Task<Tuple<int, int, int, bool>> ParseTextAsync_Surrogate(int outOrChars, char[] chars, int pos, int rcount, int rpos, int orChars, char c) {
            char ch = chars[pos];
            if (XmlCharType.IsHighSurrogate(ch)) {
                if (pos + 1 == ps.charsUsed) {
                    lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                    parseText_NextFunction = ParseTextFunction.ReadData;
                    return parseText_dummyTask.Result;
                }
                pos++;
                if (XmlCharType.IsLowSurrogate(chars[pos])) {
                    pos++;
                    orChars |= ch;
                    lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                    parseText_NextFunction = ParseTextFunction.ParseText;
                    return parseText_dummyTask.Result;
                }
            }
            int offset = pos - ps.charPos;
            if (await ZeroEndingStreamAsync(pos).ConfigureAwait(false)) {
                chars = ps.chars;
                pos = ps.charPos + offset;
                lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                parseText_NextFunction = ParseTextFunction.PartialValue;
                return parseText_dummyTask.Result;
            }
            else {
                ThrowInvalidChar(ps.chars, ps.charsUsed, ps.charPos + offset);
            }
            //should never hit here
            throw new Exception();
        }
        
        private async Task<Tuple<int, int, int, bool>> ParseTextAsync_ReadData(int outOrChars, char[] chars, int pos, int rcount, int rpos, int orChars, char c)
        {
            if (pos > ps.charPos) {
                    lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                    parseText_NextFunction = ParseTextFunction.PartialValue;
                    return parseText_dummyTask.Result;
                }
                // read new characters into the buffer 
                if (await ReadDataAsync().ConfigureAwait(false) == 0) {
                    if (ps.charsUsed - ps.charPos > 0) {
                        if (ps.chars[ps.charPos] != (char)0xD && ps.chars[ps.charPos] != ']') {
                            Throw(Res.Xml_UnexpectedEOF1);
                        }
                        Debug.Assert(ps.isEof);
                    }
                    else {
                        if (!InEntity) {
                            // end the value (returns nothing)
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.NoValue;
                            return parseText_dummyTask.Result;
                        }
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                        HandleEntityEnd( true );
#else
                        if (HandleEntityEnd(true)) {
                            // report EndEntity after the text node
                            nextParsingFunction = parsingFunction;
                            parsingFunction = ParsingFunction.ReportEndEntity;
                            // end the value (returns nothing)
                            lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                            parseText_NextFunction = ParseTextFunction.NoValue;
                            return parseText_dummyTask.Result;
                        }
#endif
                    }
                }
                pos = ps.charPos;
                chars = ps.chars;
                lastParseTextState = new ParseTextState(outOrChars, chars, pos, rcount, rpos, orChars, c);
                parseText_NextFunction = ParseTextFunction.ParseText;
                return parseText_dummyTask.Result;
        }
 
        private Task<Tuple<int, int, int, bool>> ParseTextAsync_NoValue(int outOrChars, int pos) {
            return Task.FromResult(new Tuple<int, int, int, bool>(pos, pos, outOrChars, true));
        }
 
        private Task<Tuple<int, int, int, bool>> ParseTextAsync_PartialValue(int pos, int rcount, int rpos, int orChars, char c) {
            if (parsingMode == ParsingMode.Full && rcount > 0) {
                ShiftBuffer(rpos + rcount, rpos, pos - rpos - rcount);
            }
            int startPos = ps.charPos;
            int endPos = pos - rcount;
            ps.charPos = pos;
            int outOrChars = orChars;
 
            return Task.FromResult(new Tuple<int, int, int, bool>(startPos, endPos, outOrChars, c == '<'));
        }
 
        
        // When in ParsingState.PartialTextValue, this method parses and caches the rest of the value and stores it in curNode.
        async Task FinishPartialValueAsync() {
            Debug.Assert( stringBuilder.Length == 0 );
            Debug.Assert( parsingFunction == ParsingFunction.PartialTextValue ||
                          ( parsingFunction == ParsingFunction.InReadValueChunk && incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue ) );
 
            curNode.CopyTo( readValueOffset, stringBuilder );
 
            int startPos;
            int endPos;
            int orChars = 0;
 
            var tuple_15 = await ParseTextAsync(orChars).ConfigureAwait(false);
            startPos = tuple_15.Item1;
            endPos = tuple_15.Item2;
            orChars = tuple_15.Item3;
 
            while ( !tuple_15.Item4 ) {
 
                stringBuilder.Append( ps.chars, startPos, endPos - startPos );
 
                tuple_15 = await ParseTextAsync(orChars).ConfigureAwait(false);
                startPos = tuple_15.Item1;
                endPos = tuple_15.Item2;
                orChars = tuple_15.Item3;
    
            }
            stringBuilder.Append( ps.chars, startPos, endPos - startPos );
 
            Debug.Assert( stringBuilder.Length > 0 );
            curNode.SetValue( stringBuilder.ToString() );
            stringBuilder.Length = 0;
        }
 
        async Task FinishOtherValueIteratorAsync() {
            switch ( parsingFunction ) {
                case ParsingFunction.InReadAttributeValue:
                    // do nothing, correct value is already in curNode
                    break;
                case ParsingFunction.InReadValueChunk:
                    if ( incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue ) {
                        await FinishPartialValueAsync().ConfigureAwait(false);
                        incReadState = IncrementalReadState.ReadValueChunk_OnCachedValue;
                    }
                    else {
                        if ( readValueOffset > 0 ) {
                            curNode.SetValue( curNode.StringValue.Substring( readValueOffset ) );
                            readValueOffset = 0;
                        }
                    }
                    break;
                case ParsingFunction.InReadContentAsBinary:
                case ParsingFunction.InReadElementContentAsBinary:
                    switch ( incReadState ) {
                        case IncrementalReadState.ReadContentAsBinary_OnPartialValue:
                            await FinishPartialValueAsync().ConfigureAwait(false);
                            incReadState = IncrementalReadState.ReadContentAsBinary_OnCachedValue;
                            break;
                        case IncrementalReadState.ReadContentAsBinary_OnCachedValue:
                            if ( readValueOffset > 0 ) {
                                curNode.SetValue( curNode.StringValue.Substring( readValueOffset ) );
                                readValueOffset = 0;
                            }
                            break;
                        case IncrementalReadState.ReadContentAsBinary_End:
                            curNode.SetValue( string.Empty );
                            break;
                    }
                    break;
            }
        }
 
        // When in ParsingState.PartialTextValue, this method skips over the rest of the partial value.
        [MethodImplAttribute(MethodImplOptions.NoInlining)]
        async Task SkipPartialTextValueAsync() {
            Debug.Assert( parsingFunction == ParsingFunction.PartialTextValue || parsingFunction == ParsingFunction.InReadValueChunk || 
                          parsingFunction == ParsingFunction.InReadContentAsBinary || parsingFunction == ParsingFunction.InReadElementContentAsBinary );
            int startPos;
            int endPos;
            int orChars = 0;
 
            parsingFunction = nextParsingFunction;
 
            Tuple<int,int,int,bool> tuple_16;
            do {
                tuple_16 = await ParseTextAsync(orChars).ConfigureAwait(false);
                startPos = tuple_16.Item1;
                endPos = tuple_16.Item2;
                orChars = tuple_16.Item3;
    
            } while ( !tuple_16.Item4 );
 
        }
 
        Task FinishReadValueChunkAsync() {
            Debug.Assert( parsingFunction == ParsingFunction.InReadValueChunk );
 
            readValueOffset = 0;
            if ( incReadState == IncrementalReadState.ReadValueChunk_OnPartialValue ) {
                Debug.Assert( ( index > 0 ) ? nextParsingFunction == ParsingFunction.ElementContent : nextParsingFunction == ParsingFunction.DocumentContent );
                return SkipPartialTextValueAsync();
            }
            else {
                parsingFunction = nextParsingFunction;
                nextParsingFunction = nextNextParsingFunction;
 
                return AsyncHelper.DoneTask;
 
            }
        }
 
        async Task FinishReadContentAsBinaryAsync() {
            Debug.Assert( parsingFunction == ParsingFunction.InReadContentAsBinary || parsingFunction == ParsingFunction.InReadElementContentAsBinary );
 
            readValueOffset = 0;
            if ( incReadState == IncrementalReadState.ReadContentAsBinary_OnPartialValue ) {
                Debug.Assert( ( index > 0 ) ? nextParsingFunction == ParsingFunction.ElementContent : nextParsingFunction == ParsingFunction.DocumentContent );
                await SkipPartialTextValueAsync().ConfigureAwait(false);
            }
            else {
                parsingFunction = nextParsingFunction;
                nextParsingFunction = nextNextParsingFunction;
            }
            if ( incReadState != IncrementalReadState.ReadContentAsBinary_End ) {
                while ( await MoveToNextContentNodeAsync( true ).ConfigureAwait(false) );
            }
        }
 
        async Task FinishReadElementContentAsBinaryAsync() {
            await FinishReadContentAsBinaryAsync().ConfigureAwait(false);
 
            if ( curNode.type != XmlNodeType.EndElement ) {
                Throw( Res.Xml_InvalidNodeType, curNode.type.ToString() );
            }
            // move off the end element
            await outerReader.ReadAsync().ConfigureAwait(false);
        }
 
        private async Task< bool > ParseRootLevelWhitespaceAsync() {
            Debug.Assert( stringBuilder.Length == 0 );
 
            XmlNodeType nodeType = GetWhitespaceType();
 
            if ( nodeType == XmlNodeType.None ) {
                await EatWhitespacesAsync( null ).ConfigureAwait(false);
                if ( ps.chars[ps.charPos] == '<' || ps.charsUsed - ps.charPos == 0 || await ZeroEndingStreamAsync( ps.charPos ).ConfigureAwait(false) ) {
                    return false;
                }
            }
            else {
                curNode.SetLineInfo( ps.LineNo, ps.LinePos ); 
                await EatWhitespacesAsync( stringBuilder ).ConfigureAwait(false);
                if ( ps.chars[ps.charPos] == '<' || ps.charsUsed - ps.charPos == 0 || await ZeroEndingStreamAsync( ps.charPos ).ConfigureAwait(false) ) {
                    if ( stringBuilder.Length > 0 ) {
                        curNode.SetValueNode( nodeType, stringBuilder.ToString() );
                        stringBuilder.Length = 0;
                        return true;
                    }
                    return false;
                }
            }
 
            if ( xmlCharType.IsCharData( ps.chars[ps.charPos] ) ) {
                Throw( Res.Xml_InvalidRootData );
            }
            else {
                ThrowInvalidChar( ps.chars, ps.charsUsed, ps.charPos );
            }
            return false;
        }
 
#if !SILVERLIGHT
        private async Task ParseEntityReferenceAsync() {
            Debug.Assert( ps.chars[ps.charPos] == '&' );
            ps.charPos++;
 
            curNode.SetLineInfo( ps.LineNo, ps.LinePos );
            curNode.SetNamedNode( XmlNodeType.EntityReference, await ParseEntityNameAsync().ConfigureAwait(false) );
        }
#endif
        
        private async Task< Tuple<int, EntityType> > HandleEntityReferenceAsync(bool isInAttributeValue, EntityExpandType expandType) {
            int charRefEndPos;
 
            Debug.Assert( ps.chars[ps.charPos] == '&' );
 
            if ( ps.charPos + 1 == ps.charsUsed ) {
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    Throw( Res.Xml_UnexpectedEOF1 );
                }
            }
            
            // numeric characters reference
            if ( ps.chars[ps.charPos+1] == '#' ) {
                EntityType entityType;
 
                var tuple_17 = await ParseNumericCharRefAsync( expandType != EntityExpandType.OnlyGeneral,  null).ConfigureAwait(false);
                entityType = tuple_17.Item1;
 
                charRefEndPos = tuple_17.Item2;
 
                Debug.Assert( entityType == EntityType.CharacterDec || entityType == EntityType.CharacterHex );
 
                return new Tuple<int, EntityType>(charRefEndPos, entityType);
 
            }
            // named reference
            else {
                // named character reference
                charRefEndPos = await ParseNamedCharRefAsync( expandType != EntityExpandType.OnlyGeneral, null ).ConfigureAwait(false);
                if ( charRefEndPos >= 0 ) {
 
                    return new Tuple<int, EntityType>(charRefEndPos, EntityType.CharacterNamed);
 
                }
 
                // general entity reference
#if !SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                // NOTE: XmlValidatingReader compatibility mode: expand all entities in attribute values
                // general entity reference
                if ( expandType == EntityExpandType.OnlyCharacter ||
                     ( entityHandling != EntityHandling.ExpandEntities &&
                       ( !isInAttributeValue || !validatingReaderCompatFlag ) ) ) {
 
                    return new Tuple<int, EntityType>(charRefEndPos, EntityType.Unexpanded);
 
                }
#endif
                int endPos;
 
                ps.charPos++;
                int savedLinePos = ps.LinePos;
                try {
                    endPos = await ParseNameAsync().ConfigureAwait(false);
                }
                catch ( XmlException ) {
                    Throw( Res.Xml_ErrorParsingEntityName, ps.LineNo, savedLinePos );
 
                    return new Tuple<int, EntityType>(charRefEndPos, EntityType.Skipped);
 
                }
 
                // check ';'
                if ( ps.chars[endPos] != ';' ) {
                    ThrowUnexpectedToken( endPos, ";" );
                }
 
                int entityLinePos = ps.LinePos;
                string entityName = nameTable.Add( ps.chars, ps.charPos, endPos - ps.charPos );
                ps.charPos = endPos + 1;
                charRefEndPos = -1;
 
                EntityType entType = await HandleGeneralEntityReferenceAsync( entityName, isInAttributeValue, false, entityLinePos ).ConfigureAwait(false);
                reportedBaseUri = ps.baseUriStr;
                reportedEncoding = ps.encoding;
 
                return new Tuple<int, EntityType>(charRefEndPos, entType);
 
            }
        }
 
        // returns true == continue parsing
        // return false == unexpanded external entity, stop parsing and return
        private async Task< EntityType > HandleGeneralEntityReferenceAsync( string name, bool isInAttributeValue, bool pushFakeEntityIfNullResolver, int entityStartLinePos ) {
            IDtdEntityInfo entity = null;
 
            if ( dtdInfo == null && fragmentParserContext != null && fragmentParserContext.HasDtdInfo && dtdProcessing == DtdProcessing.Parse ) {
                await ParseDtdFromParserContextAsync().ConfigureAwait(false);
            }
 
            if ( dtdInfo == null || 
                 ( ( entity = dtdInfo.LookupEntity( name) ) == null ) ) {
#if !SILVERLIGHT // Needed only for XmlTextReader (when used from XmlDocument)
                if ( disableUndeclaredEntityCheck ) {
                    SchemaEntity schemaEntity = new SchemaEntity( new XmlQualifiedName( name ), false );
                    schemaEntity.Text = string.Empty;
                    entity = schemaEntity;
                }
                else
#endif
                Throw( Res.Xml_UndeclaredEntity, name, ps.LineNo, entityStartLinePos );
            }
 
            if ( entity.IsUnparsedEntity ) {
#if !SILVERLIGHT // Needed only for XmlTextReader (when used from XmlDocument)
                if ( disableUndeclaredEntityCheck ) {
                    SchemaEntity schemaEntity = new SchemaEntity( new XmlQualifiedName( name ), false );
                    schemaEntity.Text = string.Empty;
                    entity = schemaEntity;
                }
                else
#endif
                Throw( Res.Xml_UnparsedEntityRef, name, ps.LineNo, entityStartLinePos ); 
            }
 
            if ( standalone && entity.IsDeclaredInExternal ) {
                Throw( Res.Xml_ExternalEntityInStandAloneDocument, entity.Name, ps.LineNo, entityStartLinePos );
            }
 
            if ( entity.IsExternal ) {
                if ( isInAttributeValue ) {
                    Throw( Res.Xml_ExternalEntityInAttValue, name, ps.LineNo, entityStartLinePos );
                    return EntityType.Skipped;
                }
 
                if ( parsingMode == ParsingMode.SkipContent ) {
                    return EntityType.Skipped;
                }
 
                if (IsResolverNull) {
                    if ( pushFakeEntityIfNullResolver ) {
                        await PushExternalEntityAsync( entity ).ConfigureAwait(false);
                        curNode.entityId = ps.entityId;
                        return EntityType.FakeExpanded;
                    }
                    return EntityType.Skipped;
                }
                else {
                    await PushExternalEntityAsync( entity ).ConfigureAwait(false);
                    curNode.entityId = ps.entityId;
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                    return EntityType.Expanded; 
#else 
                    return (isInAttributeValue && validatingReaderCompatFlag) ? EntityType.ExpandedInAttribute : EntityType.Expanded;
#endif
                }
            }
            else {
                if ( parsingMode == ParsingMode.SkipContent ) {
                    return EntityType.Skipped;
                }
 
                PushInternalEntity( entity );
 
                curNode.entityId = ps.entityId;
#if SILVERLIGHT // Needed only for XmlTextReader (reporting of entities)
                return EntityType.Expanded;
#else 
                return ( isInAttributeValue && validatingReaderCompatFlag ) ? EntityType.ExpandedInAttribute : EntityType.Expanded;
#endif
            }
        }
 
        private Task< bool > ParsePIAsync() {
            return ParsePIAsync( null );
        }
 
        // Parses processing instruction; if piInDtdStringBuilder != null, the processing instruction is in DTD and
        // it will be saved in the passed string builder (target, whitespace & value).
        private async Task< bool > ParsePIAsync( BufferBuilder piInDtdStringBuilder ) {
            if ( parsingMode == ParsingMode.Full ) {
                curNode.SetLineInfo( ps.LineNo, ps.LinePos );
            }
 
            Debug.Assert( stringBuilder.Length == 0 );
 
            // parse target name
            int nameEndPos = await ParseNameAsync().ConfigureAwait(false);
            string target = nameTable.Add( ps.chars, ps.charPos, nameEndPos - ps.charPos );
 
            if ( string.Compare( target, "xml", StringComparison.OrdinalIgnoreCase ) == 0 ) {
                Throw( target.Equals( "xml" ) ? Res.Xml_XmlDeclNotFirst : Res.Xml_InvalidPIName, target );
            }
            ps.charPos = nameEndPos;
 
            if ( piInDtdStringBuilder == null ) {
                if ( !ignorePIs && parsingMode == ParsingMode.Full ) {
                    curNode.SetNamedNode( XmlNodeType.ProcessingInstruction, target );
                }
            }
            else {
                piInDtdStringBuilder.Append( target );
            }
 
            // check mandatory whitespace
            char ch = ps.chars[ps.charPos];
            Debug.Assert( ps.charPos < ps.charsUsed );
            if ( await EatWhitespacesAsync( piInDtdStringBuilder ).ConfigureAwait(false) == 0 ) {
                if ( ps.charsUsed - ps.charPos < 2 ) {
                    await ReadDataAsync().ConfigureAwait(false);
                }
                if ( ch != '?' || ps.chars[ps.charPos+1] != '>' ) {
                    Throw( Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs( ps.chars, ps.charsUsed, ps.charPos ) );
                }
            }
 
            // scan processing instruction value
            int startPos, endPos;
 
            var tuple_18 = await ParsePIValueAsync().ConfigureAwait(false);
            startPos = tuple_18.Item1;
            endPos = tuple_18.Item2;
 
            if ( tuple_18.Item3 ) {
 
                if ( piInDtdStringBuilder == null ) {
                    if ( ignorePIs ) {
                        return false;
                    }
                    if ( parsingMode == ParsingMode.Full ) {
                        curNode.SetValue( ps.chars, startPos, endPos - startPos );
                    }
                }
                else {
                    piInDtdStringBuilder.Append( ps.chars, startPos, endPos - startPos );
                }
            }
            else {
                BufferBuilder sb;
                if ( piInDtdStringBuilder == null ) {
                    if ( ignorePIs || parsingMode != ParsingMode.Full ) {
 
                        Tuple<int,int,bool> tuple_19;
                        do {
                            tuple_19 = await ParsePIValueAsync().ConfigureAwait(false);
                            startPos = tuple_19.Item1;
                            endPos = tuple_19.Item2;
    
                        } while ( !tuple_19.Item3 );
 
                        return false;
                    }
                    sb = stringBuilder;
                    Debug.Assert( stringBuilder.Length == 0 );
                }
                else {
                    sb = piInDtdStringBuilder;
                }
 
                Tuple<int,int,bool> tuple_20;
 
                do {
                    sb.Append( ps.chars, startPos, endPos - startPos );
 
                    tuple_20 = await ParsePIValueAsync().ConfigureAwait(false);
                    startPos = tuple_20.Item1;
                    endPos = tuple_20.Item2;
    
                } while ( !tuple_20.Item3 );
 
                sb.Append( ps.chars, startPos, endPos - startPos );
 
                if ( piInDtdStringBuilder == null ) {
                    curNode.SetValue( stringBuilder.ToString() );
                    stringBuilder.Length = 0;
                }
            }
            return true;
        }
 
        private async Task< Tuple<int, int, bool> > ParsePIValueAsync() {
            int outStartPos;
            int outEndPos;
 
            // read new characters into the buffer
            if ( ps.charsUsed - ps.charPos < 2 ) {
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    Throw( ps.charsUsed, Res.Xml_UnexpectedEOF, "PI" );
                }
            }
 
            int pos = ps.charPos;
            char[] chars = ps.chars;
            int rcount = 0;
            int rpos = -1;
            
            for (;;) {
 
                char tmpch;
 
#if SILVERLIGHT
                while (xmlCharType.IsTextChar(tmpch = chars[pos]) &&
                    tmpch != '?') {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[tmpch = chars[pos]] & XmlCharType.fText) != 0) &&
                        tmpch != '?') {
                        pos++;
                    }
                }
#endif
 
                switch ( chars[pos] ) {
                    // possibly end of PI
                    case '?':
                        if ( chars[pos+1] == '>' ) {
                            if ( rcount > 0 ) {
                                Debug.Assert( !ps.eolNormalized );
                                ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                                outEndPos = pos - rcount;
                            }
                            else {
                                outEndPos = pos;
                            }
                            outStartPos = ps.charPos;
                            ps.charPos = pos + 2;
 
                            return new Tuple<int, int, bool>(outStartPos, outEndPos, true);
 
                        }
                        else if ( pos+1 == ps.charsUsed ) {
                            goto ReturnPartial;
                        }
                        else {
                            pos++;
                            continue;
                        }
                    // eol
                    case (char)0xA:
                        pos++;
                        OnNewLine( pos );
                        continue;
                    case (char)0xD:
                        if ( chars[pos+1] == (char)0xA ) {
                            if ( !ps.eolNormalized && parsingMode == ParsingMode.Full ) {
                                // EOL normalization of 0xD 0xA
                                if ( pos - ps.charPos > 0 ) {
                                    if ( rcount == 0 ) { 
                                        rcount = 1;
                                        rpos = pos;
                                    }
                                    else {
                                        ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                                        rpos = pos - rcount;
                                        rcount++;
                                    }
                                }
                                else {
                                    ps.charPos++;
                                }
                            }
                            pos += 2;
                        }
                        else if ( pos+1 < ps.charsUsed || ps.isEof ) {
                            if ( !ps.eolNormalized ) {
                                chars[pos] = (char)0xA;             // EOL normalization of 0xD
                            }
                            pos++;
                        }
                        else {
                            goto ReturnPartial;
                        }
                        OnNewLine( pos );
                        continue;
                    case '<':
                    case '&':
                    case ']':
                    case (char)0x9:
                        pos++;
                        continue;
                    default:
                        // end of buffer
                        if ( pos == ps.charsUsed ) {
                            goto ReturnPartial;
                        }
                        // surrogate characters
                        else {
                            char ch = chars[pos];
                            if ( XmlCharType.IsHighSurrogate(ch) ) {
                                if ( pos + 1 == ps.charsUsed ) {
                                    goto ReturnPartial;
                                }
                                pos++;
                                if ( XmlCharType.IsLowSurrogate( chars[pos] ) ) {
                                    pos++;
                                    continue;
                                }
                            }
                            ThrowInvalidChar( chars, ps.charsUsed, pos );
                            break;
                        }
                }
                
            }
            
        ReturnPartial:
            if ( rcount > 0 ) {
                ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                outEndPos = pos - rcount;
            }
            else {
                outEndPos = pos;
            }
            outStartPos = ps.charPos;
            ps.charPos = pos;
 
            return new Tuple<int, int, bool>(outStartPos, outEndPos, false);
 
        }
 
        private async Task< bool > ParseCommentAsync() {
            if ( ignoreComments ) {
                ParsingMode oldParsingMode = parsingMode;
                parsingMode = ParsingMode.SkipNode;
                await ParseCDataOrCommentAsync( XmlNodeType.Comment ).ConfigureAwait(false);
                parsingMode = oldParsingMode;
                return false;
            }
            else {
                await ParseCDataOrCommentAsync( XmlNodeType.Comment ).ConfigureAwait(false);
                return true;
            }
        }
 
        private Task ParseCDataAsync() {
            return ParseCDataOrCommentAsync( XmlNodeType.CDATA );
        }
 
        // Parses CDATA section or comment
        private async Task ParseCDataOrCommentAsync( XmlNodeType type ) {
            int startPos, endPos;
 
            if ( parsingMode == ParsingMode.Full ) {
                curNode.SetLineInfo( ps.LineNo, ps.LinePos );
                Debug.Assert( stringBuilder.Length == 0 );
 
                var tuple_21 = await ParseCDataOrCommentTupleAsync( type).ConfigureAwait(false);
                startPos = tuple_21.Item1;
                endPos = tuple_21.Item2;
 
                if ( tuple_21.Item3 ) {
 
                    curNode.SetValueNode( type, ps.chars, startPos, endPos - startPos );
                }
                else {
 
                    Tuple<int, int, bool> tuple_22;
 
                    do {
                        stringBuilder.Append( ps.chars, startPos, endPos - startPos );
 
                        tuple_22 = await ParseCDataOrCommentTupleAsync( type).ConfigureAwait(false);
                        startPos = tuple_22.Item1;
                        endPos = tuple_22.Item2;
    
                    } while ( !tuple_22.Item3 );
 
                    stringBuilder.Append( ps.chars, startPos, endPos - startPos );
                    curNode.SetValueNode( type, stringBuilder.ToString() );
                    stringBuilder.Length = 0;
                }
            }
            else {
 
                Tuple<int,int,bool> tuple_23;
                do {
                    tuple_23 = await ParseCDataOrCommentTupleAsync( type).ConfigureAwait(false);
                    startPos = tuple_23.Item1;
                    endPos = tuple_23.Item2;
    
                } while ( !tuple_23.Item3 ) ;
 
            }
        }
 
        // Parses a chunk of CDATA section or comment. Returns true when the end of CDATA or comment was reached.
 
        private async Task< Tuple<int, int, bool> > ParseCDataOrCommentTupleAsync(XmlNodeType type) {
            int outStartPos;
            int outEndPos;
 
            if ( ps.charsUsed - ps.charPos < 3 ) {
                // read new characters into the buffer
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    Throw( Res.Xml_UnexpectedEOF, ( type == XmlNodeType.Comment ) ? "Comment" : "CDATA" );
                }
            }
 
            int pos = ps.charPos;
            char[] chars = ps.chars;
            int rcount = 0;
            int rpos = -1;
            char stopChar = ( type == XmlNodeType.Comment ) ? '-' : ']';
            
            for (;;) {
 
                char tmpch;
#if SILVERLIGHT
                while (xmlCharType.IsTextChar(tmpch = chars[pos]) &&
                    tmpch != stopChar) {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[tmpch = chars[pos]] & XmlCharType.fText) != 0) &&
                        tmpch != stopChar) {
                        pos++;
                    }
                }
#endif
 
                // posibbly end of comment or cdata section
                if ( chars[pos] == stopChar ) {
                    if ( chars[pos+1] == stopChar ) {
                        if ( chars[pos+2] == '>' ) {
                            if ( rcount > 0 ) {
                                Debug.Assert( !ps.eolNormalized );
                                ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                                outEndPos = pos - rcount;
                            }
                            else {
                                outEndPos = pos;
                            }
                            outStartPos = ps.charPos;
                            ps.charPos = pos + 3;
 
                            return new Tuple<int, int, bool>(outStartPos, outEndPos, true);
 
                        }
                        else if ( pos+2 == ps.charsUsed ) {
                            goto ReturnPartial;
                        }
                        else if ( type == XmlNodeType.Comment ) {
                            Throw( pos, Res.Xml_InvalidCommentChars );
                        }
                    }
                    else if ( pos+1 == ps.charsUsed ) {
                        goto ReturnPartial;
                    }
                    pos++;
                    continue;
                }
                else {
                    switch ( chars[pos] ) {
                    // eol
                    case (char)0xA:
                        pos++;
                        OnNewLine( pos );
                        continue;
                    case (char)0xD:
                        if ( chars[pos+1] == (char)0xA ) {
                            // EOL normalization of 0xD 0xA - shift the buffer
                            if ( !ps.eolNormalized && parsingMode == ParsingMode.Full ) {
                                if ( pos - ps.charPos > 0 ) {
                                    if ( rcount == 0 ) { 
                                        rcount = 1;
                                        rpos = pos;
                                    }
                                    else {
                                        ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                                        rpos = pos - rcount;
                                        rcount++;
                                    }
                                }
                                else {
                                    ps.charPos++;
                                }
                            }
                            pos += 2;
                        }
                        else if ( pos+1 < ps.charsUsed || ps.isEof ) {
                            if ( !ps.eolNormalized ) {
                                chars[pos] = (char)0xA;             // EOL normalization of 0xD
                            }
                            pos++;
                        }
                        else {
                            goto ReturnPartial;
                        }
                        OnNewLine( pos );
                        continue;
                    case '<':
                    case '&':
                    case ']':
                    case (char)0x9:
                        pos++;
                        continue;
                    default:
                        // end of buffer
                        if ( pos == ps.charsUsed ) {
                            goto ReturnPartial;
                        }
                        // surrogate characters
                        char ch = chars[pos];
                        if ( XmlCharType.IsHighSurrogate(ch) ) {
                            if ( pos + 1 == ps.charsUsed ) {
                                goto ReturnPartial;
                            }
                            pos++;
                            if ( XmlCharType.IsLowSurrogate( chars[pos] ) ) {
                                pos++;
                                continue;
                            }
                        }
                        ThrowInvalidChar( chars, ps.charsUsed, pos );
                        break;
                    }
                }
            
            ReturnPartial:
                if ( rcount > 0 ) {
                    ShiftBuffer( rpos + rcount, rpos, pos - rpos - rcount );
                    outEndPos = pos - rcount;
                }
                else {
                    outEndPos = pos;
                }
                outStartPos = ps.charPos;
 
                ps.charPos = pos;
 
                return new Tuple<int, int, bool>(outStartPos, outEndPos, false);
 
            }
        }
 
        // Parses DOCTYPE declaration
        private async Task< bool > ParseDoctypeDeclAsync() {
            if ( dtdProcessing == DtdProcessing.Prohibit ) {
                ThrowWithoutLineInfo( v1Compat ? Res.Xml_DtdIsProhibited : Res.Xml_DtdIsProhibitedEx );
            }
 
            // parse 'DOCTYPE'
            while ( ps.charsUsed - ps.charPos < 8 ) {
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    Throw( Res.Xml_UnexpectedEOF, "DOCTYPE" );
                }
            }
            if ( !XmlConvert.StrEqual( ps.chars, ps.charPos, 7, "DOCTYPE" ) ) {
                ThrowUnexpectedToken( ( !rootElementParsed && dtdInfo == null ) ? "DOCTYPE" : "<!--" );
            }
            if ( !xmlCharType.IsWhiteSpace( ps.chars[ps.charPos + 7] ) ) {
                ThrowExpectingWhitespace( ps.charPos + 7 );
            }
 
            if ( dtdInfo != null ) {
                Throw( ps.charPos - 2, Res.Xml_MultipleDTDsProvided );  // position just before <!DOCTYPE
            }
            if ( rootElementParsed ) {
                Throw( ps.charPos - 2, Res.Xml_DtdAfterRootElement );
            }
 
            ps.charPos += 8;
 
            await EatWhitespacesAsync( null ).ConfigureAwait(false);
 
            // Parse DTD
            if (dtdProcessing == DtdProcessing.Parse) {
                curNode.SetLineInfo(ps.LineNo, ps.LinePos);
 
                await ParseDtdAsync().ConfigureAwait(false);
 
                nextParsingFunction = parsingFunction;
                parsingFunction = ParsingFunction.ResetAttributesRootLevel;
                return true;
            }
            // Skip DTD
            else {
                Debug.Assert(dtdProcessing == DtdProcessing.Ignore);
 
                await SkipDtdAsync().ConfigureAwait(false);
                return false;
            }
        }
 
        private async Task ParseDtdAsync() {
            IDtdParser dtdParser = DtdParser.Create();
 
            dtdInfo = await dtdParser.ParseInternalDtdAsync(new DtdParserProxy(this), true).ConfigureAwait(false);
 
#if SILVERLIGHT // Needed only for XmlTextReader and XmlValidatingReader
            if (dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes) {
#else 
                if ( ( validatingReaderCompatFlag || !v1Compat ) && ( dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes ) ) {
#endif
                addDefaultAttributesAndNormalize = true;
            }
 
            curNode.SetNamedNode(XmlNodeType.DocumentType, dtdInfo.Name.ToString(), string.Empty, null);
            curNode.SetValue(dtdInfo.InternalDtdSubset);
        }
 
        private async Task SkipDtdAsync() {
            int colonPos;
 
            // parse dtd name
 
            var tuple_24 = await ParseQNameAsync().ConfigureAwait(false);
            colonPos = tuple_24.Item1;
 
            int pos = tuple_24.Item2;
 
            ps.charPos = pos;
 
            // check whitespace
            await EatWhitespacesAsync( null ).ConfigureAwait(false);
 
            // PUBLIC Id
            if ( ps.chars[ps.charPos] == 'P' ) {
                // make sure we have enough characters
                while ( ps.charsUsed - ps.charPos < 6 ) {
                    if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                        Throw( Res.Xml_UnexpectedEOF1 );
                    }
                }
                // check 'PUBLIC'
                if ( !XmlConvert.StrEqual( ps.chars, ps.charPos, 6, "PUBLIC" ) ) {
                    ThrowUnexpectedToken( "PUBLIC" );
                }   
                ps.charPos += 6;
 
                // check whitespace
                if ( await EatWhitespacesAsync( null ).ConfigureAwait(false) == 0 ) {
                    ThrowExpectingWhitespace( ps.charPos );
                }
 
                // parse PUBLIC value
                await SkipPublicOrSystemIdLiteralAsync().ConfigureAwait(false);
 
                // check whitespace
                if ( await EatWhitespacesAsync( null ).ConfigureAwait(false) == 0 ) {
                    ThrowExpectingWhitespace( ps.charPos );
                }
 
                // parse SYSTEM value
                await SkipPublicOrSystemIdLiteralAsync().ConfigureAwait(false);
 
                await EatWhitespacesAsync( null ).ConfigureAwait(false);
            }
            else if ( ps.chars[ps.charPos] == 'S' ) {
                // make sure we have enough characters
                while ( ps.charsUsed - ps.charPos < 6 ) {
                    if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                        Throw( Res.Xml_UnexpectedEOF1 );
                    }
                }
                // check 'SYSTEM'
                if ( !XmlConvert.StrEqual( ps.chars, ps.charPos, 6, "SYSTEM" ) ) {
                    ThrowUnexpectedToken( "SYSTEM" );
                }   
                ps.charPos += 6;
 
                // check whitespace
                if ( await EatWhitespacesAsync( null ).ConfigureAwait(false) == 0 ) {
                    ThrowExpectingWhitespace( ps.charPos );
                }
 
                // parse SYSTEM value
                await SkipPublicOrSystemIdLiteralAsync().ConfigureAwait(false);
 
                await EatWhitespacesAsync( null ).ConfigureAwait(false);
            }
            else if ( ps.chars[ps.charPos] != '[' && ps.chars[ps.charPos] != '>' ) {
                Throw(Res.Xml_ExpectExternalOrClose);
            }
 
            // internal DTD
            if ( ps.chars[ps.charPos] == '[' ) {
                ps.charPos++;
 
                await SkipUntilAsync( ']', true ).ConfigureAwait(false);
 
                await EatWhitespacesAsync( null ).ConfigureAwait(false);
                if ( ps.chars[ps.charPos] != '>' ) {
                    ThrowUnexpectedToken( ">" );
                }
            }
            else if ( ps.chars[ps.charPos] == '>' ) {
                curNode.SetValue( string.Empty );
            }
            else {
                Throw( Res.Xml_ExpectSubOrClose );
            }
            ps.charPos++;
        }
 
        Task SkipPublicOrSystemIdLiteralAsync() {
            // check quote char
            char quoteChar = ps.chars[ps.charPos];
            if ( quoteChar != '"' && quoteChar != '\'' ) {
                ThrowUnexpectedToken( "\"", "'" );
            }
 
            ps.charPos++;
            return SkipUntilAsync( quoteChar, false );
        }
 
        async Task SkipUntilAsync( char stopChar, bool recognizeLiterals ) {
            bool inLiteral = false;
            bool inComment = false;
            bool inPI = false;
            char literalQuote = '"';
 
            char[] chars = ps.chars;
            int pos = ps.charPos;
 
            for (; ; ) {
                char ch;
 
#if SILVERLIGHT
                while ( xmlCharType.IsAttributeValueChar( ch = chars[pos] ) && ch != stopChar && ch != '-' && ch != '?') {
                    pos++;
                }
#else // Optimization due to the lack of inlining when a method uses byte*
                unsafe {
                    while (((xmlCharType.charProperties[ch = chars[pos]] & XmlCharType.fAttrValue) != 0) && chars[pos] != stopChar && ch != '-' && ch != '?') {
                        pos++;
                    }
                }
#endif
 
                // closing stopChar outside of literal and ignore/include sections -> save value & return
                if ( ch == stopChar && !inLiteral ) {
                    ps.charPos = pos + 1;
                    return;
                }
 
                // handle the special character
                ps.charPos = pos;
                switch ( ch ) {
                    // 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 || ps.isEof ) { 
                            pos++;
                        } 
                        else {
                            goto ReadData;
                        }
                        OnNewLine( pos );
                        continue;
 
                    // comment, PI
                    case '<':
                        // processing instruction
                        if ( chars[pos + 1] == '?' ) {
                            if ( recognizeLiterals && !inLiteral && !inComment ) {
                                inPI = true;
                                pos += 2;
                                continue;
                            }
                        }
                        // comment
                        else if ( chars[pos + 1] == '!' ) {
                            if ( pos + 3 >= ps.charsUsed && !ps.isEof ) {
                                goto ReadData;
                            }
                            if ( chars[pos+2] == '-' && chars[pos+3] == '-' ) {
                                if ( recognizeLiterals && !inLiteral && !inPI ) {
                                    inComment = true;
                                    pos += 4;
                                    continue;
                                }
                            }
                        }
                        // need more data
                        else if ( pos + 1 >= ps.charsUsed && !ps.isEof ) {
                            goto ReadData;
                        }
                        pos++;
                        continue;
                    case '-':
                        // end of comment
                        if ( inComment ) {
                            if ( pos + 2 >= ps.charsUsed && !ps.isEof ) {
                                goto ReadData;
                            }
                            if ( chars[pos + 1] == '-' && chars[pos + 2] == '>' ) {
                                inComment = false;
                                pos += 2;
                                continue;
                            }
                        }
                        pos++;
                        continue;
 
                    case '?':
                        // end of processing instruction
                        if (inPI) {
                            if (pos + 1 >= ps.charsUsed && !ps.isEof) {
                                goto ReadData;
                            }
                            if (chars[pos + 1] == '>') {
                                inPI = false;
                                pos += 1;
                                continue;
                            }
                        }
                        pos++;
                        continue;
 
                    case (char)0x9:
                    case '>':
                    case ']':
                    case '&':
                        pos++;
                        continue;
                    case '"':
                    case '\'':
                        if ( inLiteral ) {
                            if ( literalQuote == ch ) {
                                inLiteral = false;
                            }
                        }
                        else {
                            if ( recognizeLiterals && !inComment && !inPI ) {
                                inLiteral = true;
                                literalQuote = ch;
                            }
                        }
                        pos++;
                        continue;
                    default:
                        // end of buffer
                        if ( pos == ps.charsUsed ) {
                            goto ReadData;
                        }
                        // surrogate chars
                        else { 
                            char tmpCh = chars[pos];
                            if ( XmlCharType.IsHighSurrogate( tmpCh ) ) {
                                if ( pos + 1 == ps.charsUsed ) {
                                    goto ReadData;
                                }
                                pos++;
                                if ( XmlCharType.IsLowSurrogate( chars[pos] ) ) {
                                    pos++;
                                    continue;
                                }
                            }
                            ThrowInvalidChar( chars, ps.charsUsed, pos );
                            break;
                        }
                }
            
            ReadData:
                // read new characters into the buffer
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    if ( ps.charsUsed - ps.charPos > 0 ) {
                        if ( ps.chars[ps.charPos] != (char)0xD ) {
                            Debug.Assert( false, "We should never get to this point." );
                            Throw( Res.Xml_UnexpectedEOF1 );
                        }
                        Debug.Assert( ps.isEof );
                    }
                    else {
                        Throw( Res.Xml_UnexpectedEOF1 );
                    }
                }
                chars = ps.chars;
                pos = ps.charPos;
            }
        }
 
        private async Task< int > EatWhitespacesAsync( BufferBuilder sb ) {
            int pos = ps.charPos;
            int wsCount = 0;
            char[] chars = ps.chars;
 
            for (;;) {
                for (;;) {
                    switch ( chars[pos] ) {
                        case (char)0xA:
                            pos++;
                            OnNewLine( pos );
                            continue;
                        case (char)0xD:
                            if ( chars[pos+1] == (char)0xA ) {
                                int tmp1 = pos - ps.charPos;
                                if ( sb != null && !ps.eolNormalized ) {
                                    if ( tmp1 > 0 ) {
                                        sb.Append( chars, ps.charPos, tmp1 );
                                        wsCount += tmp1;
                                    }
                                    ps.charPos = pos + 1;
                                }
                                pos += 2;
                            }
                            else if ( pos+1 < ps.charsUsed || ps.isEof ) {
                                if ( !ps.eolNormalized ) {
                                    chars[pos] = (char)0xA;             // EOL normalization of 0xD
                                }
                                pos++;
                            }
                            else {
                                goto ReadData;
                            }
                            OnNewLine( pos );
                            continue;
                        case (char)0x9:
                        case (char)0x20:
                            pos++;
                            continue;
                        default:
                            if ( pos == ps.charsUsed ) {
                                goto ReadData;
                            }
                            else {
                                int tmp2 = pos - ps.charPos;
                                if ( tmp2 > 0 ) {
                                    if ( sb != null  ) {
                                        sb.Append( ps.chars, ps.charPos, tmp2 );
                                    }
                                    ps.charPos = pos;
                                    wsCount += tmp2;
                                }
                                return wsCount;
                            }
                    }
                }
 
            ReadData:
                int tmp3 = pos - ps.charPos;
                if ( tmp3 > 0 ) {
                    if ( sb != null  ) {
                        sb.Append( ps.chars, ps.charPos, tmp3 );
                    }
                    ps.charPos = pos;
                    wsCount += tmp3;
                }
 
                if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                    if ( ps.charsUsed - ps.charPos == 0 ) {
                        return wsCount;
                    }
                    if ( ps.chars[ps.charPos] != (char)0xD ) {
                        Debug.Assert( false, "We should never get to this point." );
                        Throw( Res.Xml_UnexpectedEOF1 );
                    }
                    Debug.Assert( ps.isEof );
                }
                pos = ps.charPos;
                chars = ps.chars;
            }
        }
 
        // Parses numeric character entity reference (e.g. &#32; &#x20;).
        //      - replaces the last one or two character of the entity reference (';' and the character before) with the referenced 
        //        character or surrogates pair (if expand == true)
        //      - returns position of the end of the character reference, that is of the character next to the original ';'
        //      - if (expand == true) then ps.charPos is changed to point to the replaced character
 
        private async Task< Tuple<EntityType, int> > ParseNumericCharRefAsync(bool expand, BufferBuilder internalSubsetBuilder) {
            EntityType entityType;
 
            for (;;) {
                int newPos;
                int charCount;
                switch ( newPos = ParseNumericCharRefInline( ps.charPos, expand, internalSubsetBuilder, out charCount, out entityType ) ) {
                    case -2:
                        // read new characters in the buffer
                        if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                            Throw( Res.Xml_UnexpectedEOF );
                        }
                        Debug.Assert( ps.chars[ps.charPos] == '&' );
                        continue;
                    default:
                        if ( expand ) {
                            ps.charPos = newPos - charCount;
                        }
 
                        return new Tuple<EntityType, int>(entityType, newPos);
 
                }
            }
        }
 
        // Parses named character entity reference (&amp; &apos; &lt; &gt; &quot;).
        // Returns -1 if the reference is not a character entity reference.
        // Otherwise 
        //      - replaces the last character of the entity reference (';') with the referenced character (if expand == true)
        //      - returns position of the end of the character reference, that is of the character next to the original ';'
        //      - if (expand == true) then ps.charPos is changed to point to the replaced character
        private async Task< int > ParseNamedCharRefAsync( bool expand, BufferBuilder internalSubsetBuilder ) {
            for (;;) {
                int newPos;
                switch ( newPos = ParseNamedCharRefInline( ps.charPos, expand, internalSubsetBuilder ) ) {
                    case -1:
                        return -1;
                    case -2:
                        // read new characters in the buffer
                        if ( await ReadDataAsync().ConfigureAwait(false) == 0 ) {
                            return -1;
                        }
                        Debug.Assert( ps.chars[ps.charPos] == '&' );
                        continue;
                    default:
                        if ( expand ) {
                            ps.charPos = newPos - 1;
                        }
                        return newPos;
                }
            }
        }
 
        private async Task< int > ParseNameAsync() {
 
            var tuple_25 = await ParseQNameAsync( false,  0).ConfigureAwait(false);
            return tuple_25.Item2;
 
        }
 
        private Task< Tuple<int, int> > ParseQNameAsync() {
 
            return ParseQNameAsync( true,  0);
 
        }
 
        private async Task< Tuple<int, int> > ParseQNameAsync(bool isQName, int startOffset) {
            int colonPos;
 
            int colonOffset = -1;
            int pos = ps.charPos + startOffset;
 
        ContinueStartName:
            char[] chars = ps.chars;
 
            //a tmp flag, used to avoid await keyword in unsafe context.
            bool awaitReadDataInNameAsync = false;
            // start name char
            unsafe {
#if SILVERLIGHT
                if ( xmlCharType.IsStartNCNameSingleChar( chars[pos] ) ) {
#else // Optimization due to the lack of inlining when a method uses byte*
                if ((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCStartNameSC) != 0) {
#endif
                    pos++;
                }
 
#if XML10_FIFTH_EDITION
                else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
                    pos += 2;
                }
#endif
                else {
                    if (pos + 1 >= ps.charsUsed) {
                        awaitReadDataInNameAsync = true;
                    }
                    else if (chars[pos] != ':' || supportNamespaces) {
                        Throw(pos, Res.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs(chars, ps.charsUsed, pos));
                    }
                }
            }
 
            if (awaitReadDataInNameAsync) {
                var tuple_27 = await ReadDataInNameAsync(pos).ConfigureAwait(false);
                pos = tuple_27.Item1;
 
                if (tuple_27.Item2) {
                    goto ContinueStartName;
                }
                Throw(pos, Res.Xml_UnexpectedEOF, "Name");
            }
 
        ContinueName:
            // parse name
            unsafe {
                for (;;) {
#if SILVERLIGHT
                    if ( xmlCharType.IsNCNameSingleChar( chars[pos] )) {
#else // Optimization due to the lack of inlining when a method uses byte*
                    if (((xmlCharType.charProperties[chars[pos]] & XmlCharType.fNCNameSC) != 0)) {
#endif
                        pos++;
                    }
#if XML10_FIFTH_EDITION
                    else if ( pos + 1 < ps.charsUsed && xmlCharType.IsNCNameSurrogateChar( chars[pos + 1], chars[pos] ) ) {
                        pos += 2;
                    }
#endif
                    else {
                        break;
                    }
                }
            }
 
            // colon
            if ( chars[pos] == ':' ) {
                if ( supportNamespaces ) {
                    if ( colonOffset != -1 || !isQName ) {
                        Throw(pos, Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0'));
                    }
                    colonOffset = pos - ps.charPos;
                    pos++;
                    goto ContinueStartName;
                }
                else {
                    colonOffset = pos - ps.charPos;
                    pos++;
                    goto ContinueName;
                }
            }
            // end of buffer
            else if ( pos == ps.charsUsed 
#if XML10_FIFTH_EDITION
                || ( pos + 1 == ps.charsUsed && xmlCharType.IsNCNameHighSurrogateChar( chars[pos] ) ) 
#endif
                ) {
 
                var tuple_28 = await ReadDataInNameAsync(pos).ConfigureAwait(false);
                pos = tuple_28.Item1;
 
                if ( tuple_28.Item2 ) {
 
                    chars = ps.chars;
                    goto ContinueName;
                }
                Throw( pos, Res.Xml_UnexpectedEOF, "Name" );
            }
 
            // end of name
            colonPos = ( colonOffset == -1 ) ? -1 : ps.charPos + colonOffset;
 
            return new Tuple<int, int>(colonPos, pos);
 
        }
 
        private async Task< Tuple<int, bool> > ReadDataInNameAsync(int pos) {
 
            int offset = pos - ps.charPos;
            bool newDataRead = ( await ReadDataAsync().ConfigureAwait(false) != 0 );
            pos = ps.charPos + offset;
 
            return new Tuple<int, bool>(pos, newDataRead);
 
        }
 
#if !SILVERLIGHT
        private async Task< string > ParseEntityNameAsync() {
            int endPos;
            try {
                endPos = await ParseNameAsync().ConfigureAwait(false);
            }
            catch ( XmlException ) {
                Throw( Res.Xml_ErrorParsingEntityName );
                return null;
            }
 
            // check ';'
            if ( ps.chars[endPos] != ';' ) {
                Throw( Res.Xml_ErrorParsingEntityName );
            }
 
            string entityName = nameTable.Add( ps.chars, ps.charPos, endPos - ps.charPos );
            ps.charPos = endPos + 1;
            return entityName;
        }
#endif
 
        // This method resolves and opens an external DTD subset or an external entity based on its SYSTEM or PUBLIC ID.
        // SxS: This method may expose a name if a resource in baseUri (ref) parameter. 
#if !SILVERLIGHT
        [ResourceConsumption(ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.Machine)]
#endif
 
        private async Task PushExternalEntityOrSubsetAsync(string publicId, string systemId, Uri baseUri, string entityName) {
 
            Uri uri;
 
            // First try opening the external reference by PUBLIC Id
            if ( !string.IsNullOrEmpty( publicId ) ) {
                try {
                    uri = xmlResolver.ResolveUri(baseUri, publicId);
                    if ( await OpenAndPushAsync( uri ).ConfigureAwait(false) ) {
 
                        return;
 
                    }
                }
                catch ( Exception ) {
                    // Intentionally empty - ---- all exception related to PUBLIC ID and try opening the entity via the SYSTEM ID
                }
            }
 
            // Then try SYSTEM Id
            uri = xmlResolver.ResolveUri( baseUri, systemId );
            try {
                if ( await OpenAndPushAsync( uri ).ConfigureAwait(false) ) {
 
                    return;
 
                }
                // resolver returned null, throw exception outside this try-catch
            }
            catch ( Exception e ) {
                if ( v1Compat ) {
                    throw;
                }
                string innerMessage;
#if SILVERLIGHT // This is to remove the second "An error occured" from "An error has occurred while opening external entity 'bla.ent': An error occurred."
                innerMessage = string.Empty;
#else 
                innerMessage = e.Message;
#endif
                Throw( new XmlException( entityName == null ? Res.Xml_ErrorOpeningExternalDtd : Res.Xml_ErrorOpeningExternalEntity, new string[] { uri.ToString(), innerMessage }, e, 0, 0 ) );
            }
 
            if ( entityName == null ) {
                ThrowWithoutLineInfo( Res.Xml_CannotResolveExternalSubset, new string[] { ( publicId != null ? publicId : string.Empty ), systemId }, null );
            }
            else {
                Throw( dtdProcessing == DtdProcessing.Ignore ? Res.Xml_CannotResolveEntityDtdIgnored : Res.Xml_CannotResolveEntity, entityName );
            }
 
            return;
 
        }
 
        // This method opens the URI as a TextReader or Stream, pushes new ParsingStateState on the stack and calls InitStreamInput or InitTextReaderInput.
        // Returns:
        //    - true when everything went ok.
        //    - false when XmlResolver.GetEntity returned null
        // Propagates any exceptions from the XmlResolver indicating when the URI cannot be opened.
        private async Task< bool > OpenAndPushAsync( Uri uri ) {
            Debug.Assert( xmlResolver != null );
            
            // First try to get the data as a TextReader
            if ( xmlResolver.SupportsType( uri, typeof( TextReader ) ) ) {
                TextReader textReader = (TextReader) await xmlResolver.GetEntityAsync( uri, null, typeof( TextReader ) ).ConfigureAwait(false);
                if ( textReader == null ) {
                    return false;
                }
 
                PushParsingState();
                await InitTextReaderInputAsync( uri.ToString(), uri, textReader ).ConfigureAwait(false);
            }
            else {
                // Then try get it as a Stream
                Debug.Assert( xmlResolver.SupportsType( uri, typeof( Stream ) ), "Stream must always be a supported type in XmlResolver" );
 
                Stream stream = (Stream)await xmlResolver.GetEntityAsync(uri, null, typeof(Stream)).ConfigureAwait(false);
                if ( stream == null ) {
                    return false;
                }
 
                PushParsingState();
                await InitStreamInputAsync( uri, stream, null ).ConfigureAwait(false);
            }
            return true;
        }
 
        // returns true if real entity has been pushed, false if fake entity (=empty content entity)
        // SxS: The method neither takes any name of resource directly nor it exposes any resource to the caller. 
        // Entity info was created based on source document. It's OK to suppress the SxS warning
#if !SILVERLIGHT
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [ResourceExposure(ResourceScope.None)]
#endif
        private async Task< bool > PushExternalEntityAsync( IDtdEntityInfo entity ) {
            Debug.Assert( entity.IsExternal );
 
            if (!IsResolverNull) {
 
                Uri entityBaseUri = null;
                // Resolve base URI
                if (!string.IsNullOrEmpty(entity.BaseUriString)) {
                    entityBaseUri = xmlResolver.ResolveUri(null, entity.BaseUriString);
                }
                await PushExternalEntityOrSubsetAsync( entity.PublicId,  entity.SystemId, entityBaseUri,  entity.Name ).ConfigureAwait(false);
 
                RegisterEntity(entity);
 
                Debug.Assert( ps.appendMode );
                int initialPos = ps.charPos;
                if ( v1Compat ) {
                    await EatWhitespacesAsync( null ).ConfigureAwait(false);
                }
                if ( !await ParseXmlDeclarationAsync( true ).ConfigureAwait(false) ) {
                    ps.charPos = initialPos;
                }
                return true;
            }
            else {
                Encoding enc = ps.encoding;
 
                PushParsingState();
                InitStringInput( entity.SystemId, enc, string.Empty );
 
                RegisterEntity(entity);
 
                RegisterConsumedCharacters(0, true);
 
                return false;
            }
        }
 
        // This method is used to enable parsing of zero-terminated streams. The old XmlTextReader implementation used 
        // to parse such streams, we this one needs to do that as well. 
        // If the last characters decoded from the stream is 0 and the stream is in EOF state, this method will remove 
        // the character from the parsing buffer (decrements ps.charsUsed).
        // Note that this method calls ReadData() which may change the value of ps.chars and ps.charPos.
        private async Task< bool > ZeroEndingStreamAsync( int pos ) {
#if !SILVERLIGHT || FEATURE_NETCORE
            if ( v1Compat && pos == ps.charsUsed - 1 && ps.chars[pos] == (char)0 && await ReadDataAsync().ConfigureAwait(false) == 0 && ps.isStreamEof ) {
                ps.charsUsed--;
                return true;
            }
#endif
            return false;
        }
 
        private async Task ParseDtdFromParserContextAsync() {
            Debug.Assert( dtdInfo == null && fragmentParserContext != null && fragmentParserContext.HasDtdInfo );
 
            IDtdParser dtdParser = DtdParser.Create();
 
            // Parse DTD
            dtdInfo = await dtdParser.ParseFreeFloatingDtdAsync(fragmentParserContext.BaseURI, fragmentParserContext.DocTypeName, fragmentParserContext.PublicId,                                                     fragmentParserContext.SystemId, fragmentParserContext.InternalSubset, new DtdParserProxy( this ) ).ConfigureAwait(false);
 
#if SILVERLIGHT // Needed only for XmlTextReader or XmlValidatingReader
            if (dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes) {
#else 
            if ( ( validatingReaderCompatFlag || !v1Compat ) && ( dtdInfo.HasDefaultAttributes || dtdInfo.HasNonCDataAttributes ) ) {
#endif
                addDefaultAttributesAndNormalize = true;
            }
        }
 
        async Task< bool > InitReadContentAsBinaryAsync() {
            Debug.Assert( parsingFunction != ParsingFunction.InReadContentAsBinary );
 
            if ( parsingFunction == ParsingFunction.InReadValueChunk ) {
                throw new InvalidOperationException( Res.GetString( Res.Xml_MixingReadValueChunkWithBinary ) );
            }
            if ( parsingFunction == ParsingFunction.InIncrementalRead ) {
                throw new InvalidOperationException( Res.GetString( Res.Xml_MixingV1StreamingWithV2Binary ) );
            }
 
            if ( !XmlReader.IsTextualNode( curNode.type ) ) {
                if ( !await MoveToNextContentNodeAsync( false ).ConfigureAwait(false) ) {
                    return false;
                }
            }
 
            SetupReadContentAsBinaryState( ParsingFunction.InReadContentAsBinary );
            incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            return true;
        }
 
        async Task< bool > InitReadElementContentAsBinaryAsync() {
            Debug.Assert( parsingFunction != ParsingFunction.InReadElementContentAsBinary );
            Debug.Assert( curNode.type == XmlNodeType.Element );
 
            bool isEmpty = curNode.IsEmptyElement;
 
            // move to content or off the empty element
            await outerReader.ReadAsync().ConfigureAwait(false);
            if ( isEmpty ) {
                return false;
            }
 
            // make sure we are on a content node
            if ( !await MoveToNextContentNodeAsync( false ).ConfigureAwait(false) ) {
                if ( curNode.type != XmlNodeType.EndElement ) {
                    Throw( Res.Xml_InvalidNodeType, curNode.type.ToString() );
                }
                // move off end element
                await outerReader.ReadAsync().ConfigureAwait(false);
                return false;
            }
            SetupReadContentAsBinaryState( ParsingFunction.InReadElementContentAsBinary );
            incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            return true;
        }
 
        async Task< bool > MoveToNextContentNodeAsync( bool moveIfOnContentNode ) {
            do {
                switch ( curNode.type ) {
                    case XmlNodeType.Attribute:
                        return !moveIfOnContentNode;
                    case XmlNodeType.Text:
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.SignificantWhitespace:
                    case XmlNodeType.CDATA:
                        if ( !moveIfOnContentNode ) {
                            return true;
                        }
                        break;
                    case XmlNodeType.ProcessingInstruction:
                    case XmlNodeType.Comment:
                    case XmlNodeType.EndEntity:
                        // skip comments, pis and end entity nodes
                        break;
                    case XmlNodeType.EntityReference:
                        outerReader.ResolveEntity();
                        break;
                    default:
                        return false;
                }
                moveIfOnContentNode = false;
            } while ( await outerReader.ReadAsync().ConfigureAwait(false) );
            return false;
        }
 
        async Task< int > ReadContentAsBinaryAsync( byte[] buffer, int index, int count ) { 
            Debug.Assert( incReadDecoder != null );
 
            if ( incReadState == IncrementalReadState.ReadContentAsBinary_End ) {
                return 0;
            }
 
            incReadDecoder.SetNextOutputBuffer( buffer, index, count );
 
            for (;;) {
                // read what is already cached in curNode
                int charsRead = 0;
                try {
                    charsRead = curNode.CopyToBinary( incReadDecoder, readValueOffset );
                }
                // add line info to the exception
                catch ( XmlException e ) {
                    curNode.AdjustLineInfo( readValueOffset, ps.eolNormalized, ref incReadLineInfo );
                    ReThrow( e, incReadLineInfo.lineNo, incReadLineInfo.linePos );
                }
                readValueOffset += charsRead;
 
                if ( incReadDecoder.IsFull ) {
                    return incReadDecoder.DecodedCount;
                }
 
                // if on partial value, read the rest of it
                if ( incReadState == IncrementalReadState.ReadContentAsBinary_OnPartialValue ) {
                    curNode.SetValue( string.Empty );
 
                    // read next chunk of text
                    bool endOfValue = false;
                    int startPos = 0;
                    int endPos = 0;
                    while ( !incReadDecoder.IsFull && !endOfValue ) {
                        int orChars = 0;
 
                        // store current line info and parse more text
                        incReadLineInfo.Set( ps.LineNo, ps.LinePos );
 
                        var tuple_36 = await ParseTextAsync(orChars).ConfigureAwait(false);
                        startPos = tuple_36.Item1;
                        endPos = tuple_36.Item2;
                        orChars = tuple_36.Item3;
 
                        endOfValue = tuple_36.Item4;
 
                        try {
                            charsRead = incReadDecoder.Decode( ps.chars, startPos, endPos - startPos ); 
                        }
                        // add line info to the exception
                        catch ( XmlException e ) {
                            ReThrow( e, incReadLineInfo.lineNo, incReadLineInfo.linePos);
                        }
                        startPos += charsRead;
                    }
                    incReadState = endOfValue ? IncrementalReadState.ReadContentAsBinary_OnCachedValue : IncrementalReadState.ReadContentAsBinary_OnPartialValue;
                    readValueOffset = 0;
 
                    if ( incReadDecoder.IsFull ) {
                        curNode.SetValue( ps.chars, startPos, endPos - startPos );
                        // adjust line info for the chunk that has been already decoded
                        AdjustLineInfo( ps.chars, startPos - charsRead, startPos, ps.eolNormalized, ref incReadLineInfo );
                        curNode.SetLineInfo( incReadLineInfo.lineNo, incReadLineInfo.linePos );
                        return incReadDecoder.DecodedCount;
                    }
                }
 
                // reset to normal state so we can call Read() to move forward
                ParsingFunction tmp = parsingFunction;
                parsingFunction = nextParsingFunction;
                nextParsingFunction = nextNextParsingFunction;
 
                // move to next textual node in the element content; throw on sub elements
                if ( !await MoveToNextContentNodeAsync( true ).ConfigureAwait(false) ) {
                    SetupReadContentAsBinaryState( tmp );
                    incReadState = IncrementalReadState.ReadContentAsBinary_End;
                    return incReadDecoder.DecodedCount;
                }
                SetupReadContentAsBinaryState( tmp );
                incReadLineInfo.Set( curNode.LineNo, curNode.LinePos );
            }
        }
 
        async Task< int > ReadElementContentAsBinaryAsync( byte[] buffer, int index, int count ) { 
            if ( count == 0 ) {
                return 0;
            }
            int decoded = await ReadContentAsBinaryAsync( buffer, index, count ).ConfigureAwait(false);
            if ( decoded > 0 ) {
                return decoded;
            }
 
            // if 0 bytes returned check if we are on a closing EndElement, throw exception if not
            if ( curNode.type != XmlNodeType.EndElement ) {
                throw new XmlException( Res.Xml_InvalidNodeType, curNode.type.ToString(), this as IXmlLineInfo );
            }
 
            // reset state
            parsingFunction = nextParsingFunction;
            nextParsingFunction = nextNextParsingFunction;
            Debug.Assert( parsingFunction != ParsingFunction.InReadElementContentAsBinary );
 
            // move off the EndElement
            await outerReader.ReadAsync().ConfigureAwait(false);
            return 0;
        }
 
    }
}