File: System\Xml\BinaryXml\XmlBinaryReader.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Schema;
 
namespace System.Xml
{
    internal sealed partial class XmlSqlBinaryReader : XmlReader, IXmlNamespaceResolver
    {
        private static volatile Type?[] s_tokenTypeMap = null!;
 
        private static ReadOnlySpan<byte> XsdKatmaiTimeScaleToValueLengthMap => // 8
        [
            // length scale
            3, // 0
            3, // 1
            3, // 2
            4, // 3
            4, // 4
            5, // 5
            5, // 6
            5, // 7
        ];
 
        private enum ScanState
        {
            Doc = 0,
            XmlText = 1,
            Attr = 2,
            AttrVal = 3,
            AttrValPseudoValue = 4,
            Init = 5,
            Error = 6,
            EOF = 7,
            Closed = 8
        }
 
        private static readonly ReadState[] s_scanState2ReadState = {
            ReadState.Interactive,
            ReadState.Interactive,
            ReadState.Interactive,
            ReadState.Interactive,
            ReadState.Interactive,
            ReadState.Initial,
            ReadState.Error,
            ReadState.EndOfFile,
            ReadState.Closed
        };
 
        // Note: also used by XmlBinaryWriter
        internal struct QName : IEquatable<QName>
        {
            public string prefix;
            public string localname;
            public string namespaceUri;
 
            public QName(string prefix, string lname, string nsUri)
            {
                this.prefix = prefix; this.localname = lname; this.namespaceUri = nsUri;
            }
            public void Set(string prefix, string lname, string nsUri)
            {
                this.prefix = prefix; this.localname = lname; this.namespaceUri = nsUri;
            }
 
            public void Clear()
            {
                this.prefix = this.localname = this.namespaceUri = string.Empty;
            }
 
            public bool MatchNs(string lname, string nsUri)
            {
                return lname == this.localname && nsUri == this.namespaceUri;
            }
 
            public bool MatchPrefix(string prefix, string lname)
            {
                return lname == this.localname && prefix == this.prefix;
            }
 
            public void CheckPrefixNS(string prefix, string namespaceUri)
            {
                if (this.prefix == prefix && this.namespaceUri != namespaceUri)
                    throw new XmlException(SR.XmlBinary_NoRemapPrefix, new string[] { prefix, this.namespaceUri, namespaceUri });
            }
 
            public int GetNSHashCode() =>
                HashCode.Combine(this.namespaceUri, this.localname);
 
            public override int GetHashCode() =>
                this.prefix.GetHashCode() ^ this.localname.GetHashCode();
 
            public override bool Equals([NotNullWhen(true)] object? other) =>
                other is QName qname && Equals(qname);
 
            public bool Equals(QName other) =>
                prefix == other.prefix &&
                localname == other.localname &&
                namespaceUri == other.namespaceUri;
 
            public static bool operator ==(QName a, QName b) => a.Equals(b);
 
            public static bool operator !=(QName a, QName b) => !a.Equals(b);
 
            public override string ToString() =>
                prefix.Length == 0 ? localname : $"{this.prefix}:{this.localname}";
        };
 
        private struct ElemInfo
        {
            public QName name;
            public string? xmlLang;
            public XmlSpace xmlSpace;
            public bool xmlspacePreserve;
            public NamespaceDecl? nsdecls;
 
            public void Set(QName name, bool xmlspacePreserve)
            {
                this.name = name;
                this.xmlLang = null;
                this.xmlSpace = XmlSpace.None;
                this.xmlspacePreserve = xmlspacePreserve;
            }
            public NamespaceDecl? Clear()
            {
                NamespaceDecl? nsdecls = this.nsdecls;
                this.nsdecls = null;
                return nsdecls;
            }
        };
 
        private struct AttrInfo
        {
            public QName name;
            public string? val;
            public int contentPos;
            public int hashCode;
            public int prevHash;
 
            public void Set(QName n, string v)
            {
                this.name = n;
                this.val = v;
                this.contentPos = 0;
                this.hashCode = 0;
                this.prevHash = 0;
            }
 
            public void Set(QName n, int pos)
            {
                this.name = n;
                this.val = null;
                this.contentPos = pos;
                this.hashCode = 0;
                this.prevHash = 0;
            }
 
            public void GetLocalnameAndNamespaceUri(out string localname, out string namespaceUri)
            {
                localname = this.name.localname;
                namespaceUri = this.name.namespaceUri;
            }
 
            public int GetLocalnameAndNamespaceUriAndHash(out string localname, out string namespaceUri)
            {
                localname = this.name.localname;
                namespaceUri = this.name.namespaceUri;
                return this.hashCode = this.name.GetNSHashCode();
            }
 
            public bool MatchNS(string localname, string namespaceUri)
            {
                return this.name.MatchNs(localname, namespaceUri);
            }
 
            public bool MatchHashNS(int hash, string localname, string namespaceUri)
            {
                return this.hashCode == hash && this.name.MatchNs(localname, namespaceUri);
            }
 
            public void AdjustPosition(int adj)
            {
                if (this.contentPos != 0)
                    this.contentPos += adj;
            }
        }
 
        private sealed class NamespaceDecl
        {
            public string prefix;
            public string uri;
            public NamespaceDecl? scopeLink;
            public NamespaceDecl? prevLink;
            public int scope;
            public bool implied;
 
            public NamespaceDecl(string prefix, string nsuri,
                                NamespaceDecl? nextInScope, NamespaceDecl? prevDecl,
                                int scope, bool implied)
            {
                this.prefix = prefix;
                this.uri = nsuri;
                this.scopeLink = nextInScope;
                this.prevLink = prevDecl;
                this.scope = scope;
                this.implied = implied;
            }
        }
 
        // symbol and qname tables
        private struct SymbolTables
        {
            public string[] symtable;
            public int symCount;
            public QName[] qnametable;
            public int qnameCount;
            public void Init()
            {
                this.symtable = new string[64];
                this.qnametable = new QName[16];
                this.symtable[0] = string.Empty;
                this.symCount = 1;
                this.qnameCount = 1;
            }
        }
 
        private sealed class NestedBinXml
        {
            public SymbolTables symbolTables;
            public int docState;
            public NestedBinXml? next;
            public NestedBinXml(SymbolTables symbolTables, int docState, NestedBinXml? next)
            {
                this.symbolTables = symbolTables;
                this.docState = docState;
                this.next = next;
            }
        }
 
        // input data
        private Stream _inStrm;
        private byte[] _data;
        private int _pos;
        private int _mark;
        private int _end;
        private bool _eof;
        private bool _sniffed;
        private bool _isEmpty; // short-tag element start tag
 
        private int _docState; // 0=>auto, 1=>doc/pre-dtd, 2=>doc/pre-elem, 3=>doc/instance -1=>doc/post-elem, 9=>frag
 
        // symbol and qname tables
        private SymbolTables _symbolTables;
 
        private readonly XmlNameTable _xnt;
        private readonly bool _xntFromSettings;
        private readonly string _xml;
        private readonly string _xmlns;
        private readonly string _nsxmlns;
 
        // base uri...
        private readonly string _baseUri;
 
        // current parse state
        private ScanState _state;
        private XmlNodeType _nodetype;
        private BinXmlToken _token;
        // current attribute
        private int _attrIndex;
        // index of current qname
        private QName _qnameOther;
        // saved qname of element (for MoveToElement)
        private QName _qnameElement;
        private XmlNodeType _parentNodeType; // use for MoveToElement()
        // stack of current open element tags
        private ElemInfo[] _elementStack;
        private int _elemDepth;
        // current attributes
        private AttrInfo[] _attributes;
        private int[] _attrHashTbl;
        private int _attrCount;
        private int _posAfterAttrs;
        // xml:space
        private bool _xmlspacePreserve;
        // position/parse info for current typed token
        private int _tokLen;
        private int _tokDataPos;
        private bool _hasTypedValue;
        private System.Type _valueType;
        // if it is a simple string value, we cache it
        private string? _stringValue;
        // hashtable of current namespaces
        private readonly Dictionary<string, NamespaceDecl> _namespaces;
        //Hashtable namespaces;
        // linked list of pushed nametables (to support nested binary-xml documents)
        private NestedBinXml? _prevNameInfo;
        // XmlTextReader to handle embedded text blocks
        private XmlTextReaderImpl? _textXmlReader;
        // close input flag
        private readonly bool _closeInput;
 
        private readonly bool _checkCharacters;
        private readonly bool _ignoreWhitespace;
        private readonly bool _ignorePIs;
        private readonly bool _ignoreComments;
        private readonly DtdProcessing _dtdProcessing;
 
        // current version of the protocol
        private byte _version;
 
        public XmlSqlBinaryReader(Stream stream, byte[] data, int len, string baseUri, bool closeInput, XmlReaderSettings settings)
        {
            _xnt = settings.NameTable!;
            if (_xnt == null)
            {
                _xnt = new NameTable();
                _xntFromSettings = false;
            }
            else
            {
                _xntFromSettings = true;
            }
 
            _xml = _xnt.Add("xml");
            _xmlns = _xnt.Add("xmlns");
            _nsxmlns = _xnt.Add(XmlReservedNs.NsXmlNs);
            _baseUri = baseUri;
            _state = ScanState.Init;
            _nodetype = XmlNodeType.None;
            _token = BinXmlToken.Error;
            _elementStack = new ElemInfo[16];
            _attributes = new AttrInfo[8];
            _attrHashTbl = new int[8];
            _symbolTables.Init();
            _qnameOther.Clear();
            _qnameElement.Clear();
            _xmlspacePreserve = false;
            _namespaces = new Dictionary<string, NamespaceDecl>();
            AddInitNamespace(string.Empty, string.Empty);
            AddInitNamespace(_xml, _xnt.Add(XmlReservedNs.NsXml));
            AddInitNamespace(_xmlns, _nsxmlns);
            _valueType = typeof(string);
            // init buffer position, etc
            _inStrm = stream;
            if (data != null)
            {
                Debug.Assert(len >= 2 && (data[0] == 0xdf && data[1] == 0xff));
                _data = data;
                _end = len;
                _pos = 2;
                _sniffed = true;
            }
            else
            {
                _data = new byte[XmlReader.DefaultBufferSize];
                _end = stream.Read(_data, 0, XmlReader.DefaultBufferSize);
                _pos = 0;
                _sniffed = false;
            }
 
            _mark = -1;
            _eof = (0 == _end);
            _closeInput = closeInput;
            switch (settings.ConformanceLevel)
            {
                case ConformanceLevel.Auto:
                    _docState = 0; break;
                case ConformanceLevel.Fragment:
                    _docState = 9; break;
                case ConformanceLevel.Document:
                    _docState = 1; break;
            }
            _checkCharacters = settings.CheckCharacters;
            _dtdProcessing = settings.DtdProcessing;
            _ignoreWhitespace = settings.IgnoreWhitespace;
            _ignorePIs = settings.IgnoreProcessingInstructions;
            _ignoreComments = settings.IgnoreComments;
 
            s_tokenTypeMap ??= GenerateTokenTypeMap();
        }
 
        public override XmlReaderSettings Settings
        {
            get
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                if (_xntFromSettings)
                {
                    settings.NameTable = _xnt;
                }
                // 0=>auto, 1=>doc/pre-dtd, 2=>doc/pre-elem, 3=>doc/instance -1=>doc/post-elem, 9=>frag
                settings.ConformanceLevel = _docState switch
                {
                    0 => ConformanceLevel.Auto,
                    9 => ConformanceLevel.Fragment,
                    _ => ConformanceLevel.Document,
                };
                settings.CheckCharacters = _checkCharacters;
                settings.IgnoreWhitespace = _ignoreWhitespace;
                settings.IgnoreProcessingInstructions = _ignorePIs;
                settings.IgnoreComments = _ignoreComments;
                settings.DtdProcessing = _dtdProcessing;
                settings.CloseInput = _closeInput;
 
                settings.ReadOnly = true;
                return settings;
            }
        }
 
        public override XmlNodeType NodeType
        {
            get
            {
                return _nodetype;
            }
        }
 
        public override string LocalName
        {
            get
            {
                return _qnameOther.localname;
            }
        }
 
        public override string NamespaceURI
        {
            get
            {
                return _qnameOther.namespaceUri;
            }
        }
 
        public override string Prefix
        {
            get
            {
                return _qnameOther.prefix;
            }
        }
 
        public override bool HasValue
        {
            get
            {
                if (ScanState.XmlText == _state)
                {
                    Debug.Assert(_textXmlReader != null);
                    return _textXmlReader.HasValue;
                }
                else
                    return XmlReader.HasValueInternal(_nodetype);
            }
        }
 
        public override string Value
        {
            get
            {
                if (null != _stringValue)
                    return _stringValue;
                switch (_state)
                {
                    case ScanState.Doc:
                        switch (_nodetype)
                        {
                            case XmlNodeType.DocumentType:
                            case XmlNodeType.ProcessingInstruction:
                            case XmlNodeType.Comment:
                                return _stringValue = GetString(_tokDataPos, _tokLen);
 
                            case XmlNodeType.CDATA:
                                return _stringValue = CDATAValue();
 
                            case XmlNodeType.XmlDeclaration:
                                return _stringValue = XmlDeclValue();
 
                            case XmlNodeType.Text:
                            case XmlNodeType.Whitespace:
                            case XmlNodeType.SignificantWhitespace:
                                return _stringValue = ValueAsString(_token);
                        }
                        break;
 
                    case ScanState.XmlText:
                        {
                            Debug.Assert(_textXmlReader != null);
                            return _textXmlReader.Value;
                        }
 
                    case ScanState.Attr:
                    case ScanState.AttrValPseudoValue:
                        return _stringValue = GetAttributeText(_attrIndex - 1);
 
                    case ScanState.AttrVal:
                        return _stringValue = ValueAsString(_token);
                }
                return string.Empty;
            }
        }
 
        public override int Depth
        {
            get
            {
                int adj = 0;
                switch (_state)
                {
                    case ScanState.Doc:
                        if (_nodetype == XmlNodeType.Element
                            || _nodetype == XmlNodeType.EndElement)
                            adj = -1;
                        break;
 
                    case ScanState.XmlText:
                        Debug.Assert(_textXmlReader != null);
                        adj = _textXmlReader.Depth;
                        break;
 
                    case ScanState.Attr:
                        if (_parentNodeType != XmlNodeType.Element)
                            adj = 1;
                        break;
                    case ScanState.AttrVal:
                    case ScanState.AttrValPseudoValue:
                        if (_parentNodeType != XmlNodeType.Element)
                            adj = 1;
                        adj += 1;
                        break;
                    default:
                        return 0;
                }
                return _elemDepth + adj;
            }
        }
 
        public override string BaseURI
        {
            get
            {
                return _baseUri;
            }
        }
 
        public override bool IsEmptyElement
        {
            get
            {
                switch (_state)
                {
                    case ScanState.Doc:
                    case ScanState.XmlText:
                        return _isEmpty;
                    default:
                        return false;
                }
            }
        }
 
        public override XmlSpace XmlSpace
        {
            get
            {
                if (ScanState.XmlText != _state)
                {
                    for (int i = _elemDepth; i >= 0; i--)
                    {
                        XmlSpace xs = _elementStack[i].xmlSpace;
                        if (xs != XmlSpace.None)
                            return xs;
                    }
                    return XmlSpace.None;
                }
                else
                {
                    Debug.Assert(_textXmlReader != null);
                    return _textXmlReader.XmlSpace;
                }
            }
        }
 
        public override string XmlLang
        {
            get
            {
                if (ScanState.XmlText != _state)
                {
                    for (int i = _elemDepth; i >= 0; i--)
                    {
                        string? xl = _elementStack[i].xmlLang;
                        if (null != xl)
                            return xl;
                    }
                    return string.Empty;
                }
                else
                {
                    Debug.Assert(_textXmlReader != null);
                    return _textXmlReader.XmlLang;
                }
            }
        }
 
        public override System.Type ValueType
        {
            get
            {
                return _valueType;
            }
        }
 
        public override int AttributeCount
        {
            get
            {
                switch (_state)
                {
                    case ScanState.Doc:
                    // for compatibility with XmlTextReader
                    //  we return the attribute count for the element
                    //  when positioned on an attribute under that
                    //  element...
                    case ScanState.Attr:
                    case ScanState.AttrVal:
                    case ScanState.AttrValPseudoValue:
                        return _attrCount;
                    case ScanState.XmlText:
                        {
                            Debug.Assert(_textXmlReader != null);
                            return _textXmlReader.AttributeCount;
                        }
                    default:
                        return 0;
                }
            }
        }
 
        public override string? GetAttribute(string name, string? ns)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.GetAttribute(name, ns);
            }
            else
            {
                ArgumentNullException.ThrowIfNull(name);
                if (null == ns)
                    ns = string.Empty;
                int index = LocateAttribute(name, ns);
                if (-1 == index)
                    return null;
                return GetAttribute(index);
            }
        }
 
        public override string? GetAttribute(string name)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.GetAttribute(name);
            }
            else
            {
                int index = LocateAttribute(name);
                if (-1 == index)
                    return null;
                return GetAttribute(index);
            }
        }
 
        public override string GetAttribute(int i)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.GetAttribute(i);
            }
            else
            {
                ArgumentOutOfRangeException.ThrowIfNegative(i);
                ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(i, _attrCount);
                return GetAttributeText(i);
            }
        }
 
        public override bool MoveToAttribute(string name, string? ns)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return UpdateFromTextReader(_textXmlReader.MoveToAttribute(name, ns));
            }
            else
            {
                ArgumentNullException.ThrowIfNull(name);
                if (null == ns)
                    ns = string.Empty;
                int index = LocateAttribute(name, ns);
                if ((-1 != index) && (_state < ScanState.Init))
                {
                    PositionOnAttribute(index + 1);
                    return true;
                }
                return false;
            }
        }
 
        public override bool MoveToAttribute(string name)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return UpdateFromTextReader(_textXmlReader.MoveToAttribute(name));
            }
            else
            {
                int index = LocateAttribute(name);
                if ((-1 != index) && (_state < ScanState.Init))
                {
                    PositionOnAttribute(index + 1);
                    return true;
                }
                return false;
            }
        }
 
        public override void MoveToAttribute(int i)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                _textXmlReader.MoveToAttribute(i);
                UpdateFromTextReader(true);
            }
            else
            {
                ArgumentOutOfRangeException.ThrowIfNegative(i);
                ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(i, _attrCount);
                PositionOnAttribute(i + 1);
            }
        }
 
        public override bool MoveToFirstAttribute()
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return UpdateFromTextReader(_textXmlReader.MoveToFirstAttribute());
            }
            else
            {
                if (_attrCount == 0)
                    return false;
                // set up for walking attributes
                PositionOnAttribute(1);
                return true;
            }
        }
 
        public override bool MoveToNextAttribute()
        {
            switch (_state)
            {
                case ScanState.Doc:
                case ScanState.Attr:
                case ScanState.AttrVal:
                case ScanState.AttrValPseudoValue:
                    if (_attrIndex >= _attrCount)
                        return false;
                    PositionOnAttribute(++_attrIndex);
                    return true;
 
                case ScanState.XmlText:
                    Debug.Assert(_textXmlReader != null);
                    return UpdateFromTextReader(_textXmlReader.MoveToNextAttribute());
 
                default:
                    return false;
            }
        }
 
        public override bool MoveToElement()
        {
            switch (_state)
            {
                case ScanState.Attr:
                case ScanState.AttrVal:
                case ScanState.AttrValPseudoValue:
                    _attrIndex = 0;
                    _qnameOther = _qnameElement;
                    if (XmlNodeType.Element == _parentNodeType)
                        _token = BinXmlToken.Element;
                    else if (XmlNodeType.XmlDeclaration == _parentNodeType)
                        _token = BinXmlToken.XmlDecl;
                    else if (XmlNodeType.DocumentType == _parentNodeType)
                        _token = BinXmlToken.DocType;
                    else
                        Debug.Fail("Unexpected parent NodeType");
                    _nodetype = _parentNodeType;
                    _state = ScanState.Doc;
                    _pos = _posAfterAttrs;
                    _stringValue = null;
                    return true;
 
                case ScanState.XmlText:
                    Debug.Assert(_textXmlReader != null);
                    return UpdateFromTextReader(_textXmlReader.MoveToElement());
 
                default:
                    return false;
            }
        }
 
        public override bool EOF
        {
            get
            {
                return _state == ScanState.EOF;
            }
        }
 
        public override bool ReadAttributeValue()
        {
            _stringValue = null;
            switch (_state)
            {
                case ScanState.Attr:
                    if (null == _attributes[_attrIndex - 1].val)
                    {
                        _pos = _attributes[_attrIndex - 1].contentPos;
                        BinXmlToken tok = RescanNextToken();
                        if (BinXmlToken.Attr == tok || BinXmlToken.EndAttrs == tok)
                        {
                            return false;
                        }
                        _token = tok;
                        ReScanOverValue(tok);
                        _valueType = GetValueType(tok);
                        _state = ScanState.AttrVal;
                    }
                    else
                    {
                        _token = BinXmlToken.Error;
                        _valueType = typeof(string);
                        _state = ScanState.AttrValPseudoValue;
                    }
                    _qnameOther.Clear();
                    _nodetype = XmlNodeType.Text;
                    return true;
 
                case ScanState.AttrVal:
                    return false;
 
                case ScanState.XmlText:
                    Debug.Assert(_textXmlReader != null);
                    return UpdateFromTextReader(_textXmlReader.ReadAttributeValue());
 
                default:
                    return false;
            }
        }
 
        public override void Close()
        {
            _state = ScanState.Closed;
            _nodetype = XmlNodeType.None;
            _token = BinXmlToken.Error;
            _stringValue = null;
            if (null != _textXmlReader)
            {
                _textXmlReader.Close();
                _textXmlReader = null;
            }
 
            if (null != _inStrm && _closeInput)
                _inStrm.Dispose();
            _inStrm = null!;
            _pos = _end = 0;
        }
 
        public override XmlNameTable NameTable
        {
            get
            {
                return _xnt;
            }
        }
 
        public override string? LookupNamespace(string prefix)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.LookupNamespace(prefix);
            }
 
            NamespaceDecl? decl;
            if (prefix != null && _namespaces.TryGetValue(prefix, out decl))
            {
                Debug.Assert(decl != null);
                return decl.uri;
            }
            return null;
        }
 
        public override void ResolveEntity()
        {
            throw new NotSupportedException();
        }
 
        public override ReadState ReadState
        {
            get
            {
                return s_scanState2ReadState[(int)_state];
            }
        }
 
        public override bool Read()
        {
            try
            {
                switch (_state)
                {
                    case ScanState.Init:
                        return ReadInit(false);
 
                    case ScanState.Doc:
                        return ReadDoc();
 
                    case ScanState.XmlText:
                        Debug.Assert(_textXmlReader != null);
 
                        if (_textXmlReader.Read())
                        {
                            return UpdateFromTextReader(true);
                        }
 
                        _state = ScanState.Doc;
                        _nodetype = XmlNodeType.None;
                        _isEmpty = false;
                        goto case ScanState.Doc;
 
                    case ScanState.Attr:
                    case ScanState.AttrVal:
                    case ScanState.AttrValPseudoValue:
                        // clean up attribute stuff...
                        MoveToElement();
                        goto case ScanState.Doc;
 
                    default:
                        return false;
                }
            }
            catch (OverflowException e)
            {
                _state = ScanState.Error;
                throw new XmlException(e.Message, e);
            }
            catch
            {
                _state = ScanState.Error;
                throw;
            }
        }
 
        // Use default implementation of and ReadContentAsString and ReadElementContentAsString
        // (there is no benefit to providing a custom version)
        // public override bool ReadElementContentAsString( string localName, string namespaceURI )
        // public override bool ReadElementContentAsString()
        // public override bool ReadContentAsString()
 
        // Do setup work for ReadContentAsXXX methods
        // If ready for a typed value read, returns true, otherwise returns
        //  false to indicate caller should fall back to XmlReader.ReadContentAsXXX
        // Special-Case: returns true and positioned on Element or EndElem to force parse of empty-string
        private bool SetupContentAsXXX(string name)
        {
            if (!CanReadContentAs(this.NodeType))
            {
                throw CreateReadContentAsException(name);
            }
            switch (_state)
            {
                case ScanState.Doc:
                    if (this.NodeType == XmlNodeType.EndElement)
                        return true;
                    if (this.NodeType == XmlNodeType.ProcessingInstruction || this.NodeType == XmlNodeType.Comment)
                    {
                        while (Read() && (this.NodeType == XmlNodeType.ProcessingInstruction || this.NodeType == XmlNodeType.Comment))
                            ;
                        if (this.NodeType == XmlNodeType.EndElement)
                            return true;
                    }
                    if (_hasTypedValue)
                    {
                        return true;
                    }
                    break;
                case ScanState.Attr:
                    _pos = _attributes[_attrIndex - 1].contentPos;
                    BinXmlToken token = RescanNextToken();
                    if (BinXmlToken.Attr == token || BinXmlToken.EndAttrs == token)
                        break;
                    _token = token;
                    ReScanOverValue(token);
                    return true;
                case ScanState.AttrVal:
                    return true;
                default:
                    break;
            }
            return false;
        }
 
        private int FinishContentAsXXX(int origPos)
        {
            if (_state == ScanState.Doc)
            {
                // if we are already on a tag, then don't move
                if (this.NodeType != XmlNodeType.Element && this.NodeType != XmlNodeType.EndElement)
                {
                // advance over PIs and Comments
                Loop:
                    if (Read())
                    {
                        switch (this.NodeType)
                        {
                            case XmlNodeType.ProcessingInstruction:
                            case XmlNodeType.Comment:
                                goto Loop;
 
                            case XmlNodeType.Element:
                            case XmlNodeType.EndElement:
                                break;
 
                            default:
                                throw CreateNotSupportedException(SR.XmlBinary_ListsOfValuesNotSupported);
                        }
                    }
                }
                return _pos;
            }
            return origPos;
        }
 
        public override bool ReadContentAsBoolean()
        {
            int origPos = _pos;
            bool value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsBoolean"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.XSD_BOOLEAN:
                                value = 0 != _data[_tokDataPos];
                                break;
 
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Boolean"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToBoolean(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Boolean", e, null);
                    }
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsBoolean();
        }
 
        public override DateTime ReadContentAsDateTime()
        {
            int origPos = _pos;
            DateTime value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsDateTime"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                                value = ValueAsDateTime();
                                break;
 
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "DateTime"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToDateTime(string.Empty, XmlDateTimeSerializationMode.RoundtripKind);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "DateTime", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsDateTime();
        }
 
        public override double ReadContentAsDouble()
        {
            int origPos = _pos;
            double value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsDouble"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                                value = ValueAsDouble();
                                break;
 
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Double"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToDouble(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Double", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsDouble();
        }
 
        public override float ReadContentAsFloat()
        {
            int origPos = _pos;
            float value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsFloat"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                                value = checked(((float)ValueAsDouble()));
                                break;
 
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Float"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToSingle(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Float", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsFloat();
        }
 
        public override decimal ReadContentAsDecimal()
        {
            int origPos = _pos;
            decimal value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsDecimal"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                                value = ValueAsDecimal();
                                break;
 
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Decimal"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToDecimal(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Decimal", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsDecimal();
        }
 
        public override int ReadContentAsInt()
        {
            int origPos = _pos;
            int value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsInt"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                                value = checked((int)ValueAsLong());
                                break;
 
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Int32"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToInt32(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int32", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int32", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int32", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsInt();
        }
 
        public override long ReadContentAsLong()
        {
            int origPos = _pos;
            long value;
            try
            {
                if (SetupContentAsXXX("ReadContentAsLong"))
                {
                    try
                    {
                        switch (_token)
                        {
                            case BinXmlToken.SQL_BIT:
                            case BinXmlToken.SQL_TINYINT:
                            case BinXmlToken.SQL_SMALLINT:
                            case BinXmlToken.SQL_INT:
                            case BinXmlToken.SQL_BIGINT:
                            case BinXmlToken.SQL_MONEY:
                            case BinXmlToken.SQL_SMALLMONEY:
                            case BinXmlToken.SQL_DECIMAL:
                            case BinXmlToken.SQL_NUMERIC:
                            case BinXmlToken.XSD_DECIMAL:
                            case BinXmlToken.XSD_BYTE:
                            case BinXmlToken.XSD_UNSIGNEDSHORT:
                            case BinXmlToken.XSD_UNSIGNEDINT:
                            case BinXmlToken.XSD_UNSIGNEDLONG:
                                value = ValueAsLong();
                                break;
 
                            case BinXmlToken.SQL_REAL:
                            case BinXmlToken.SQL_FLOAT:
                            case BinXmlToken.SQL_DATETIME:
                            case BinXmlToken.SQL_SMALLDATETIME:
                            case BinXmlToken.SQL_UUID:
                            case BinXmlToken.SQL_VARBINARY:
                            case BinXmlToken.SQL_BINARY:
                            case BinXmlToken.SQL_IMAGE:
                            case BinXmlToken.SQL_UDT:
                            case BinXmlToken.XSD_KATMAI_DATE:
                            case BinXmlToken.XSD_KATMAI_DATETIME:
                            case BinXmlToken.XSD_KATMAI_TIME:
                            case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                            case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                            case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                            case BinXmlToken.XSD_BINHEX:
                            case BinXmlToken.XSD_BASE64:
                            case BinXmlToken.XSD_BOOLEAN:
                            case BinXmlToken.XSD_TIME:
                            case BinXmlToken.XSD_DATETIME:
                            case BinXmlToken.XSD_DATE:
                            case BinXmlToken.XSD_QNAME:
                                throw new InvalidCastException(SR.Format(SR.XmlBinary_CastNotSupported, _token, "Int64"));
 
                            case BinXmlToken.SQL_CHAR:
                            case BinXmlToken.SQL_VARCHAR:
                            case BinXmlToken.SQL_TEXT:
                            case BinXmlToken.SQL_NCHAR:
                            case BinXmlToken.SQL_NVARCHAR:
                            case BinXmlToken.SQL_NTEXT:
                                goto Fallback;
 
                            case BinXmlToken.Element:
                            case BinXmlToken.EndElem:
                                return XmlConvert.ToInt64(string.Empty);
 
                            default:
                                Debug.Fail("should never happen");
                                goto Fallback;
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int64", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int64", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Int64", e, null);
                    }
 
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
        Fallback:
            return base.ReadContentAsLong();
        }
 
        public override object ReadContentAsObject()
        {
            int origPos = _pos;
            try
            {
                if (SetupContentAsXXX("ReadContentAsObject"))
                {
                    object value;
                    try
                    {
                        if (this.NodeType == XmlNodeType.Element || this.NodeType == XmlNodeType.EndElement)
                            value = string.Empty;
                        else
                            value = this.ValueAsObject(_token, false);
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Object", e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Object", e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, "Object", e, null);
                    }
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
            //Fallback:
            return base.ReadContentAsObject();
        }
 
        public override object ReadContentAs(Type returnType, IXmlNamespaceResolver? namespaceResolver)
        {
            int origPos = _pos;
            try
            {
                if (SetupContentAsXXX("ReadContentAs"))
                {
                    object value;
                    try
                    {
                        if (this.NodeType == XmlNodeType.Element || this.NodeType == XmlNodeType.EndElement)
                        {
                            value = string.Empty;
                        }
                        else if (returnType == this.ValueType || returnType == typeof(object))
                        {
                            value = this.ValueAsObject(_token, false);
                        }
                        else
                        {
                            value = this.ValueAs(_token, returnType, namespaceResolver);
                        }
                    }
                    catch (InvalidCastException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, null);
                    }
                    catch (FormatException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, null);
                    }
                    catch (OverflowException e)
                    {
                        throw new XmlException(SR.Xml_ReadContentAsFormatException, returnType.ToString(), e, null);
                    }
                    origPos = FinishContentAsXXX(origPos);
                    return value;
                }
            }
            finally
            {
                _pos = origPos;
            }
            return base.ReadContentAs(returnType, namespaceResolver);
        }
 
        //////////
        // IXmlNamespaceResolver
 
        System.Collections.Generic.IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope(XmlNamespaceScope scope)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.GetNamespacesInScope(scope);
            }
            else
            {
                Dictionary<string, string> nstable = new Dictionary<string, string>();
                if (XmlNamespaceScope.Local == scope)
                {
                    // are we even inside an element? (depth==0 is where we have xml, and xmlns declared...)
                    if (_elemDepth > 0)
                    {
                        NamespaceDecl? nsdecl = _elementStack[_elemDepth].nsdecls;
                        while (null != nsdecl)
                        {
                            nstable.Add(nsdecl.prefix, nsdecl.uri);
                            nsdecl = nsdecl.scopeLink;
                        }
                    }
                }
                else
                {
                    foreach (NamespaceDecl nsdecl in _namespaces.Values)
                    {
                        // don't add predefined decls unless scope == all, then only add 'xml'
                        if (nsdecl.scope != -1 || (XmlNamespaceScope.All == scope && "xml" == nsdecl.prefix))
                        {
                            // xmlns="" only ever reported via scope==local
                            if (nsdecl.prefix.Length > 0 || nsdecl.uri.Length > 0)
                                nstable.Add(nsdecl.prefix, nsdecl.uri);
                        }
                    }
                }
                return nstable;
            }
        }
 
        string? IXmlNamespaceResolver.LookupPrefix(string namespaceName)
        {
            if (ScanState.XmlText == _state)
            {
                Debug.Assert(_textXmlReader != null);
                return _textXmlReader.LookupPrefix(namespaceName);
            }
            else
            {
                if (null == namespaceName)
                    return null;
 
                string? atomizedNamespaceName = _xnt.Get(namespaceName);
                if (null == atomizedNamespaceName)
                    return null;
 
                for (int i = _elemDepth; i >= 0; i--)
                {
                    NamespaceDecl? nsdecl = _elementStack[i].nsdecls;
                    while (null != nsdecl)
                    {
                        if ((object)nsdecl.uri == (object?)atomizedNamespaceName)
                            return nsdecl.prefix;
                        nsdecl = nsdecl.scopeLink;
                    }
                }
                return null;
            }
        }
 
        //////////
        // Internal implementation methods
 
        private void VerifyVersion(int requiredVersion, BinXmlToken token)
        {
            if (_version < requiredVersion)
            {
                throw CreateUnexpectedTokenException(token);
            }
        }
 
        private void AddInitNamespace(string prefix, string uri)
        {
            NamespaceDecl nsdecl = new NamespaceDecl(prefix, uri, _elementStack[0].nsdecls, null, -1, true);
            _elementStack[0].nsdecls = nsdecl;
            _namespaces.Add(prefix, nsdecl);
        }
 
        private void AddName()
        {
            string txt = ParseText();
            int symNum = _symbolTables.symCount++;
            string[] symtable = _symbolTables.symtable;
            if (symNum == symtable.Length)
            {
                string[] n = new string[checked(symNum * 2)];
                System.Array.Copy(symtable, n, symNum);
                _symbolTables.symtable = symtable = n;
            }
            symtable[symNum] = _xnt.Add(txt);
        }
 
        private void AddQName()
        {
            int nsUri = ReadNameRef();
            int prefix = ReadNameRef();
            int lname = ReadNameRef();
            int qnameNum = _symbolTables.qnameCount++;
            QName[] qnametable = _symbolTables.qnametable;
            if (qnameNum == qnametable.Length)
            {
                QName[] n = new QName[checked(qnameNum * 2)];
                System.Array.Copy(qnametable, n, qnameNum);
                _symbolTables.qnametable = qnametable = n;
            }
            string[] symtable = _symbolTables.symtable;
            string prefixStr = symtable[prefix];
            string lnameStr;
            string nsUriStr;
            // xmlns attributes are encodes differently...
            if (lname == 0)
            { // xmlns attribute
                // for some reason, sqlserver sometimes generates these...
                if (prefix == 0 && nsUri == 0)
                    return;
                // it is a real namespace decl, make sure it looks valid
                if (!prefixStr.StartsWith("xmlns", StringComparison.Ordinal))
                    goto BadDecl;
                if (5 < prefixStr.Length)
                {
                    if (6 == prefixStr.Length || ':' != prefixStr[5])
                        goto BadDecl;
                    lnameStr = _xnt.Add(prefixStr.Substring(6));
                    prefixStr = _xmlns;
                }
                else
                {
                    lnameStr = prefixStr;
                    prefixStr = string.Empty;
                }
                nsUriStr = _nsxmlns;
            }
            else
            {
                lnameStr = symtable[lname];
                nsUriStr = symtable[nsUri];
            }
            qnametable[qnameNum].Set(prefixStr, lnameStr, nsUriStr);
            return;
        BadDecl:
            throw new XmlException(SR.Xml_BadNamespaceDecl, (string[]?)null);
        }
 
        private void NameFlush()
        {
            _symbolTables.symCount = _symbolTables.qnameCount = 1;
            Array.Clear(_symbolTables.symtable, 1, _symbolTables.symtable.Length - 1);
            Array.Clear(_symbolTables.qnametable);
        }
 
        private void SkipExtn()
        {
            int cb = ParseMB32();
            checked { _pos += cb; }
            Fill(-1);
        }
 
        private int ReadQNameRef()
        {
            int nameNum = ParseMB32();
            if (nameNum < 0 || nameNum >= _symbolTables.qnameCount)
                throw new XmlException(SR.XmlBin_InvalidQNameID, string.Empty);
            return nameNum;
        }
 
        private int ReadNameRef()
        {
            int nameNum = ParseMB32();
            if (nameNum < 0 || nameNum >= _symbolTables.symCount)
                throw new XmlException(SR.XmlBin_InvalidQNameID, string.Empty);
            return nameNum;
        }
 
        // pull more data from input stream
        private bool FillAllowEOF()
        {
            if (_eof)
                return false;
            byte[] data = _data;
            int pos = _pos;
            int mark = _mark;
            int end = _end;
            if (mark == -1)
            {
                mark = pos;
            }
            if (mark >= 0 && mark < end)
            {
                Debug.Assert(_mark <= _end, "Mark should never be past End");
                Debug.Assert(_mark <= _pos, "Mark should never be after Pos");
                int cbKeep = end - mark;
                if (cbKeep > 7 * (data.Length / 8))
                {
                    // grow buffer
                    byte[] newdata = new byte[checked(data.Length * 2)];
                    System.Array.Copy(data, mark, newdata, 0, cbKeep);
                    _data = data = newdata;
                }
                else
                {
                    System.Array.Copy(data, mark, data, 0, cbKeep);
                }
                pos -= mark;
                end -= mark;
                _tokDataPos -= mark;
                for (int i = 0; i < _attrCount; i++)
                {
                    _attributes[i].AdjustPosition(-mark);
                    // make sure it is still a valid range
                    Debug.Assert((_attributes[i].contentPos >= 0) && (_attributes[i].contentPos <= (end)));
                }
                _pos = pos;
                _mark = 0;
            }
            else
            {
                Debug.Assert(_attrCount == 0);
                _pos -= end;
                _mark -= end;
                _tokDataPos -= end;
                end = 0;
            }
            int cbFill = data.Length - end;
            int cbRead = _inStrm.Read(data, end, cbFill);
            _end = end + cbRead;
            _eof = !(cbRead > 0);
            return (cbRead > 0);
        }
 
        // require must be < 1/8 buffer, or else Fill might not actually
        // grab that much data
        private void Fill_(int require)
        {
            Debug.Assert((_pos + require) >= _end);
            while (FillAllowEOF() && ((_pos + require) >= _end))
                ;
            if ((_pos + require) >= _end)
                throw CreateXmlException(SR.Xml_UnexpectedEOF1);
        }
 
        // inline the common case
        private void Fill(int require)
        {
            if ((_pos + require) >= _end)
                Fill_(require);
        }
 
        private byte ReadByte()
        {
            Fill(0);
            return _data[_pos++];
        }
        private ushort ReadUShort()
        {
            Fill(1);
            int pos = _pos; byte[] data = _data;
            ushort val = (ushort)(data[pos] + (data[pos + 1] << 8));
            _pos += 2;
            return val;
        }
 
        private int ParseMB32()
        {
            byte b = ReadByte();
            if (b > 127)
                return ParseMB32_(b);
            return b;
        }
 
        private int ParseMB32_(byte b)
        {
            uint u, t;
            u = (uint)b & (uint)0x7F;
            Debug.Assert(0 != (b & 0x80));
            b = ReadByte();
            t = (uint)b & (uint)0x7F;
            u += (t << 7);
            if (b > 127)
            {
                b = ReadByte();
                t = (uint)b & (uint)0x7F;
                u += (t << 14);
                if (b > 127)
                {
                    b = ReadByte();
                    t = (uint)b & (uint)0x7F;
                    u += (t << 21);
                    if (b > 127)
                    {
                        b = ReadByte();
                        // bottom 4 bits are all that are needed,
                        // but we are mapping to 'int', which only
                        // actually has space for 3 more bits.
                        t = (uint)b & (uint)0x07;
                        if (b > 7)
                            throw CreateXmlException(SR.XmlBinary_ValueTooBig);
                        u += (t << 28);
                    }
                }
            }
            return (int)u;
        }
 
        // this assumes that we have already ensured that all
        // necessary bytes are loaded in to the buffer
        private int ParseMB32(int pos)
        {
            uint u, t;
            byte[] data = _data;
            byte b = data[pos++];
            u = (uint)b & (uint)0x7F;
            if (b > 127)
            {
                b = data[pos++];
                t = (uint)b & (uint)0x7F;
                u += (t << 7);
                if (b > 127)
                {
                    b = data[pos++];
                    t = (uint)b & (uint)0x7F;
                    u += (t << 14);
                    if (b > 127)
                    {
                        b = data[pos++];
                        t = (uint)b & (uint)0x7F;
                        u += (t << 21);
                        if (b > 127)
                        {
                            b = data[pos++];
                            // last byte only has 4 significant digits
                            t = (uint)b & (uint)0x07;
                            if (b > 7)
                                throw CreateXmlException(SR.XmlBinary_ValueTooBig);
                            u += (t << 28);
                        }
                    }
                }
            }
            return (int)u;
        }
 
        // we don't actually support MB64, since we use int for
        // all our math anyway...
        private int ParseMB64()
        {
            byte b = ReadByte();
            if (b > 127)
                return ParseMB32_(b);
            return b;
        }
 
        private BinXmlToken PeekToken()
        {
            while ((_pos >= _end) && FillAllowEOF())
                ;
            if (_pos >= _end)
                return BinXmlToken.EOF;
            return (BinXmlToken)_data[_pos];
        }
 
        private BinXmlToken ReadToken()
        {
            while ((_pos >= _end) && FillAllowEOF())
                ;
            if (_pos >= _end)
                return BinXmlToken.EOF;
            return (BinXmlToken)_data[_pos++];
        }
 
        private BinXmlToken NextToken2(BinXmlToken token)
        {
            while (true)
            {
                switch (token)
                {
                    case BinXmlToken.Name:
                        AddName();
                        break;
                    case BinXmlToken.QName:
                        AddQName();
                        break;
                    case BinXmlToken.NmFlush:
                        NameFlush();
                        break;
                    case BinXmlToken.Extn:
                        SkipExtn();
                        break;
                    default:
                        return token;
                }
                token = ReadToken();
            }
        }
 
        private BinXmlToken NextToken1()
        {
            BinXmlToken token;
            int pos = _pos;
            if (pos >= _end)
                token = ReadToken();
            else
            {
                token = (BinXmlToken)_data[pos];
                _pos = pos + 1;
            }
            // BinXmlToken.Name = 0xF0
            // BinXmlToken.QName = 0xEF
            // BinXmlToken.Extn = 0xEA,
            // BinXmlToken.NmFlush = 0xE9,
            if (token >= BinXmlToken.NmFlush
                && token <= BinXmlToken.Name)
                return NextToken2(token);
            return token;
        }
 
        private BinXmlToken NextToken()
        {
            int pos = _pos;
            if (pos < _end)
            {
                BinXmlToken t = (BinXmlToken)_data[pos];
                if (!(t >= BinXmlToken.NmFlush && t <= BinXmlToken.Name))
                {
                    _pos = pos + 1;
                    return t;
                }
            }
            return NextToken1();
        }
 
        // peek next non-meta token
        private BinXmlToken PeekNextToken()
        {
            BinXmlToken token = NextToken();
            if (BinXmlToken.EOF != token)
                _pos--;
            return token;
        }
 
        // like NextToken() but meta-tokens are skipped (not reinterpreted)
        private BinXmlToken RescanNextToken()
        {
            BinXmlToken token;
            while (true)
            {
                token = ReadToken();
                switch (token)
                {
                    case BinXmlToken.Name:
                        {
                            int cb = ParseMB32();
                            checked { _pos += 2 * cb; }
                            break;
                        }
                    case BinXmlToken.QName:
                        ParseMB32();
                        ParseMB32();
                        ParseMB32();
                        break;
                    case BinXmlToken.Extn:
                        {
                            int cb = ParseMB32();
                            checked { _pos += cb; }
                            break;
                        }
                    case BinXmlToken.NmFlush:
                        break;
                    default:
                        return token;
                }
            }
        }
 
        private string ParseText()
        {
            int oldmark = _mark;
            try
            {
                if (oldmark < 0)
                    _mark = _pos;
                int cch, pos;
                cch = ScanText(out pos);
                return GetString(pos, cch);
            }
            finally
            {
                if (oldmark < 0)
                    _mark = -1;
            }
        }
 
        private int ScanText(out int start)
        {
            int cch = ParseMB32();
            int oldmark = _mark;
            int begin = _pos;
            checked { _pos += cch * 2; } // cch = num utf-16 chars
            if (_pos > _end)
                Fill(-1);
            // Fill call might have moved buffer
            start = begin - (oldmark - _mark);
            return cch;
        }
 
        private string GetString(int pos, int cch)
        {
            Debug.Assert(pos >= 0 && cch >= 0);
            if (checked(pos + (cch * sizeof(char))) > _end)
                throw new XmlException(SR.Xml_UnexpectedEOF1, (string[]?)null);
            if (cch == 0)
                return string.Empty;
 
            return string.Create(cch, (_data, pos), static (dstChars, state) =>
            {
                // bitblt source bytes directly into the destination char span
                // n.b. source buffer assumed to be well-formed UTF-16 machine endian
 
                int cch = dstChars.Length;
                ReadOnlySpan<byte> srcBytes = state._data.AsSpan(state.pos, checked(cch * sizeof(char)));
                Span<byte> dstBytes = MemoryMarshal.AsBytes(dstChars);
                srcBytes.CopyTo(dstBytes);
            });
        }
 
        private string GetAttributeText(int i)
        {
            string? val = _attributes[i].val;
 
            if (null != val)
                return val;
            else
            {
                int origPos = _pos;
                try
                {
                    _pos = _attributes[i].contentPos;
                    BinXmlToken token = RescanNextToken();
                    if (BinXmlToken.Attr == token || BinXmlToken.EndAttrs == token)
                    {
                        return "";
                    }
                    _token = token;
                    ReScanOverValue(token);
                    return ValueAsString(token);
                }
                finally
                {
                    _pos = origPos;
                }
            }
        }
 
        private int LocateAttribute(string name, string ns)
        {
            for (int i = 0; i < _attrCount; i++)
            {
                if (_attributes[i].name.MatchNs(name, ns))
                    return i;
            }
 
            return -1;
        }
 
        private int LocateAttribute(string name)
        {
            string prefix, lname;
            ValidateNames.SplitQName(name, out prefix, out lname);
 
            for (int i = 0; i < _attrCount; i++)
            {
                if (_attributes[i].name.MatchPrefix(prefix, lname))
                    return i;
            }
 
            return -1;
        }
 
        private void PositionOnAttribute(int i)
        {
            // save element's qname
            _attrIndex = i;
            _qnameOther = _attributes[i - 1].name;
            if (_state == ScanState.Doc)
            {
                _parentNodeType = _nodetype;
            }
            _token = BinXmlToken.Attr;
            _nodetype = XmlNodeType.Attribute;
            _state = ScanState.Attr;
            _valueType = typeof(object);
            _stringValue = null;
        }
 
        private void GrowElements()
        {
            int newcount = _elementStack.Length * 2;
            ElemInfo[] n = new ElemInfo[newcount];
 
            System.Array.Copy(_elementStack, n, _elementStack.Length);
            _elementStack = n;
        }
 
        private void GrowAttributes()
        {
            int newcount = _attributes.Length * 2;
            AttrInfo[] n = new AttrInfo[newcount];
 
            System.Array.Copy(_attributes, n, _attrCount);
            _attributes = n;
        }
 
        private void ClearAttributes()
        {
            if (_attrCount != 0)
                _attrCount = 0;
        }
 
        private void PushNamespace(string prefix, string ns, bool implied)
        {
            if (prefix == "xml")
                return;
            int elemDepth = _elemDepth;
            NamespaceDecl? curDecl;
            _namespaces.TryGetValue(prefix, out curDecl);
            if (null != curDecl)
            {
                if (curDecl.uri == ns)
                {
                    // if we see the nsdecl after we saw the first reference in this scope
                    // fix up 'implied' flag
                    if (!implied && curDecl.implied
                        && (curDecl.scope == elemDepth))
                    {
                        curDecl.implied = false;
                    }
                    return;
                }
                // check that this doesn't conflict
                _qnameElement.CheckPrefixNS(prefix, ns);
                if (prefix.Length != 0)
                {
                    for (int i = 0; i < _attrCount; i++)
                    {
                        if (_attributes[i].name.prefix.Length != 0)
                            _attributes[i].name.CheckPrefixNS(prefix, ns);
                    }
                }
            }
            // actually add ns decl
            NamespaceDecl decl = new NamespaceDecl(prefix, ns,
                _elementStack[elemDepth].nsdecls,
                curDecl, elemDepth, implied);
            _elementStack[elemDepth].nsdecls = decl;
            _namespaces[prefix] = decl;
        }
 
        private void PopNamespaces(NamespaceDecl? firstInScopeChain)
        {
            NamespaceDecl? decl = firstInScopeChain;
            while (null != decl)
            {
                if (null == decl.prevLink)
                    _namespaces.Remove(decl.prefix);
                else
                    _namespaces[decl.prefix] = decl.prevLink;
                NamespaceDecl? next = decl.scopeLink;
                // unlink chains for better gc behaviour
                decl.prevLink = null;
                decl.scopeLink = null;
                decl = next;
            }
        }
 
        private void GenerateImpliedXmlnsAttrs()
        {
            QName name;
            NamespaceDecl? decl = _elementStack[_elemDepth].nsdecls;
            while (null != decl)
            {
                if (decl.implied)
                {
                    if (_attrCount == _attributes.Length)
                        GrowAttributes();
                    if (decl.prefix.Length == 0)
                        name = new QName(string.Empty, _xmlns, _nsxmlns);
                    else
                        name = new QName(_xmlns, _xnt.Add(decl.prefix), _nsxmlns);
                    _attributes[_attrCount].Set(name, decl.uri);
                    _attrCount++;
                }
                decl = decl.scopeLink;
            }
        }
 
        private bool ReadInit(bool skipXmlDecl)
        {
            string? err;
            if (!_sniffed)
            {
                // check magic header
                ushort magic = ReadUShort();
                if (magic != 0xFFDF)
                {
                    err = SR.XmlBinary_InvalidSignature;
                    goto Error;
                }
            }
 
            // check protocol version
            _version = ReadByte();
            if (_version != 0x1 && _version != 0x2)
            {
                err = SR.XmlBinary_InvalidProtocolVersion;
                goto Error;
            }
 
            // check encoding marker, 1200 == utf16
            if (1200 != ReadUShort())
            {
                err = SR.XmlBinary_UnsupportedCodePage;
                goto Error;
            }
 
            _state = ScanState.Doc;
            if (BinXmlToken.XmlDecl == PeekToken())
            {
                _pos++;
                _attributes[0].Set(new QName(string.Empty, _xnt.Add("version"), string.Empty), ParseText());
                _attrCount = 1;
                if (BinXmlToken.Encoding == PeekToken())
                {
                    _pos++;
                    _attributes[1].Set(new QName(string.Empty, _xnt.Add("encoding"), string.Empty), ParseText());
                    _attrCount++;
                }
 
                byte standalone = ReadByte();
                switch (standalone)
                {
                    case 0:
                        break;
                    case 1:
                    case 2:
                        _attributes[_attrCount].Set(new QName(string.Empty, _xnt.Add("standalone"), string.Empty), (standalone == 1) ? "yes" : "no");
                        _attrCount++;
                        break;
                    default:
                        err = SR.XmlBinary_InvalidStandalone;
                        goto Error;
                }
                if (!skipXmlDecl)
                {
                    QName xmlDeclQName = new QName(string.Empty, _xnt.Add("xml"), string.Empty);
                    _qnameOther = _qnameElement = xmlDeclQName;
                    _nodetype = XmlNodeType.XmlDeclaration;
                    _posAfterAttrs = _pos;
                    return true;
                }
                // else ReadDoc will clear the attributes for us
            }
            return ReadDoc();
 
        Error:
            _state = ScanState.Error;
            throw new XmlException(err, (string[]?)null);
        }
 
        private void ScanAttributes()
        {
            BinXmlToken token;
            int xmlspace = -1;
            int xmllang = -1;
 
            _mark = _pos;
            string? curDeclPrefix = null;
            bool lastWasValue = false;
 
            while (BinXmlToken.EndAttrs != (token = NextToken()))
            {
                if (BinXmlToken.Attr == token)
                {
                    // watch out for nsdecl with no actual content
                    if (null != curDeclPrefix)
                    {
                        PushNamespace(curDeclPrefix, string.Empty, false);
                        curDeclPrefix = null;
                    }
                    // do we need to grow the array?
                    if (_attrCount == _attributes.Length)
                        GrowAttributes();
                    // note: ParseMB32 _must_ happen _before_ we grab this.pos...
                    QName n = _symbolTables.qnametable[ReadQNameRef()];
                    _attributes[_attrCount].Set(n, (int)_pos);
                    if (n.prefix == "xml")
                    {
                        if (n.localname == "lang")
                        {
                            xmllang = _attrCount;
                        }
                        else if (n.localname == "space")
                        {
                            xmlspace = _attrCount;
                        }
                    }
                    else if (Ref.Equal(n.namespaceUri, _nsxmlns))
                    {
                        // push namespace when we get the value
                        curDeclPrefix = n.localname;
                        if (curDeclPrefix == "xmlns")
                            curDeclPrefix = string.Empty;
                    }
                    else if (n.prefix.Length != 0)
                    {
                        if (n.namespaceUri.Length == 0)
                            throw new XmlException(SR.Xml_PrefixForEmptyNs, string.Empty);
                        this.PushNamespace(n.prefix, n.namespaceUri, true);
                    }
                    else if (n.namespaceUri.Length != 0)
                    {
                        throw CreateXmlException(SR.XmlBinary_AttrWithNsNoPrefix, n.localname, n.namespaceUri);
                    }
                    _attrCount++;
                    lastWasValue = false;
                }
                else
                {
                    // first scan over token to make sure it is a value token
                    ScanOverValue(token, true, true);
                    // don't allow lists of values
                    if (lastWasValue)
                    {
                        throw CreateNotSupportedException(SR.XmlBinary_ListsOfValuesNotSupported);
                    }
 
                    // if char checking is on, we need to scan text values to
                    // validate that they don't use invalid CharData, so we
                    // might as well store the saved string for quick attr value access
                    string? val = _stringValue;
 
                    if (null != val)
                    {
                        _attributes[_attrCount - 1].val = val;
                        _stringValue = null;
                    }
 
                    // namespace decls can only have text values, and should only
                    // have a single value, so we just grab it here...
                    if (null != curDeclPrefix)
                    {
                        string nsuri = _xnt.Add(ValueAsString(token));
                        PushNamespace(curDeclPrefix, nsuri, false);
                        curDeclPrefix = null;
                    }
 
                    lastWasValue = true;
                }
            }
 
            if (xmlspace != -1)
            {
                string val = GetAttributeText(xmlspace);
                XmlSpace xs = XmlSpace.None;
                if (val == "preserve")
                    xs = XmlSpace.Preserve;
                else if (val == "default")
                    xs = XmlSpace.Default;
                _elementStack[_elemDepth].xmlSpace = xs;
                _xmlspacePreserve = (XmlSpace.Preserve == xs);
            }
            if (xmllang != -1)
            {
                _elementStack[_elemDepth].xmlLang = GetAttributeText(xmllang);
            }
 
            if (_attrCount < 200)
                SimpleCheckForDuplicateAttributes();
            else
                HashCheckForDuplicateAttributes();
        }
 
        private void SimpleCheckForDuplicateAttributes()
        {
            for (int i = 0; i < _attrCount; i++)
            {
                string localname, namespaceUri;
                _attributes[i].GetLocalnameAndNamespaceUri(out localname, out namespaceUri);
                for (int j = i + 1; j < _attrCount; j++)
                {
                    if (_attributes[j].MatchNS(localname, namespaceUri))
                        throw new XmlException(SR.Xml_DupAttributeName, _attributes[i].name.ToString());
                }
            }
        }
 
        private void HashCheckForDuplicateAttributes()
        {
            int tblSize = 256;
            while (tblSize < _attrCount)
                tblSize = checked(tblSize * 2);
            if (_attrHashTbl.Length < tblSize)
                _attrHashTbl = new int[tblSize];
            for (int i = 0; i < _attrCount; i++)
            {
                string localname, namespaceUri;
                int hash = _attributes[i].GetLocalnameAndNamespaceUriAndHash(out localname, out namespaceUri);
                int index = hash & (tblSize - 1);
                int next = _attrHashTbl[index];
                _attrHashTbl[index] = i + 1;
                _attributes[i].prevHash = next;
                while (next != 0)
                {
                    next--;
                    if (_attributes[next].MatchHashNS(hash, localname, namespaceUri))
                    {
                        throw new XmlException(SR.Xml_DupAttributeName, _attributes[i].name.ToString());
                    }
                    next = _attributes[next].prevHash;
                }
            }
 
            Array.Clear(_attrHashTbl, 0, tblSize);
        }
 
        private string XmlDeclValue()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < _attrCount; i++)
            {
                if (i > 0)
                    sb.Append(' ');
                sb.Append(_attributes[i].name.localname);
                sb.Append("=\"");
                sb.Append(_attributes[i].val);
                sb.Append('"');
            }
            return sb.ToString();
        }
 
        private string CDATAValue()
        {
            Debug.Assert(_stringValue == null, "this.stringValue == null");
            Debug.Assert(_token == BinXmlToken.CData, "this.token == BinXmlToken.CData");
            string value = GetString(_tokDataPos, _tokLen);
            StringBuilder? sb = null;
            while (PeekToken() == BinXmlToken.CData)
            {
                _pos++; // skip over token byte
                sb ??= new StringBuilder(value.Length + value.Length / 2).Append(value);
                sb.Append(ParseText());
            }
            if (sb != null)
                value = sb.ToString();
            _stringValue = value;
            return value;
        }
 
        private void FinishCDATA()
        {
            while (true)
            {
                switch (PeekToken())
                {
                    case BinXmlToken.CData:
                        // skip
                        _pos++;
                        ScanText(out _);
                        // try again
                        break;
                    case BinXmlToken.EndCData:
                        // done... on to next token...
                        _pos++;
                        return;
                    default:
                        throw new XmlException(SR.XmlBin_MissingEndCDATA);
                }
            }
        }
 
        private void FinishEndElement()
        {
            NamespaceDecl? nsdecls = _elementStack[_elemDepth].Clear();
            this.PopNamespaces(nsdecls);
            _elemDepth--;
        }
 
        private bool ReadDoc()
        {
            switch (_nodetype)
            {
                case XmlNodeType.CDATA:
                    FinishCDATA();
                    break;
                case XmlNodeType.EndElement:
                    FinishEndElement();
                    break;
                case XmlNodeType.Element:
                    if (_isEmpty)
                    {
                        FinishEndElement();
                        _isEmpty = false;
                    }
                    break;
            }
 
        Read:
            // clear existing state
            _nodetype = XmlNodeType.None;
            _mark = -1;
            if (_qnameOther.localname.Length != 0)
                _qnameOther.Clear();
 
            ClearAttributes();
            _attrCount = 0;
            _valueType = typeof(string);
            _stringValue = null;
            _hasTypedValue = false;
 
            _token = NextToken();
            switch (_token)
            {
                case BinXmlToken.EOF:
                    if (_elemDepth > 0)
                        throw new XmlException(SR.Xml_UnexpectedEOF1, (string[]?)null);
                    _state = ScanState.EOF;
                    return false;
 
                case BinXmlToken.Element:
                    ImplReadElement();
                    break;
 
                case BinXmlToken.EndElem:
                    ImplReadEndElement();
                    break;
 
                case BinXmlToken.DocType:
                    ImplReadDoctype();
                    if (_dtdProcessing == DtdProcessing.Ignore)
                        goto Read;
                    // nested, don't report doctype
                    if (_prevNameInfo != null)
                        goto Read;
                    break;
 
                case BinXmlToken.PI:
                    ImplReadPI();
                    if (_ignorePIs)
                        goto Read;
                    break;
 
                case BinXmlToken.Comment:
                    ImplReadComment();
                    if (_ignoreComments)
                        goto Read;
                    break;
 
                case BinXmlToken.CData:
                    ImplReadCDATA();
                    break;
 
                case BinXmlToken.Nest:
                    ImplReadNest();
                    // parse first token in nested document
                    _sniffed = false;
                    return ReadInit(true);
 
                case BinXmlToken.EndNest:
                    if (null == _prevNameInfo)
                        goto default;
                    ImplReadEndNest();
                    return ReadDoc();
 
                case BinXmlToken.XmlText:
                    ImplReadXmlText();
                    break;
 
                // text values
                case BinXmlToken.SQL_BIT:
                case BinXmlToken.SQL_TINYINT:
                case BinXmlToken.SQL_SMALLINT:
                case BinXmlToken.SQL_INT:
                case BinXmlToken.SQL_BIGINT:
                case BinXmlToken.SQL_REAL:
                case BinXmlToken.SQL_FLOAT:
                case BinXmlToken.SQL_MONEY:
                case BinXmlToken.SQL_SMALLMONEY:
                case BinXmlToken.SQL_DATETIME:
                case BinXmlToken.SQL_SMALLDATETIME:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_UUID:
                case BinXmlToken.SQL_VARBINARY:
                case BinXmlToken.SQL_BINARY:
                case BinXmlToken.SQL_IMAGE:
                case BinXmlToken.SQL_UDT:
                case BinXmlToken.XSD_KATMAI_DATE:
                case BinXmlToken.XSD_KATMAI_DATETIME:
                case BinXmlToken.XSD_KATMAI_TIME:
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                case BinXmlToken.XSD_BINHEX:
                case BinXmlToken.XSD_BASE64:
                case BinXmlToken.SQL_CHAR:
                case BinXmlToken.SQL_VARCHAR:
                case BinXmlToken.SQL_TEXT:
                case BinXmlToken.SQL_NCHAR:
                case BinXmlToken.SQL_NVARCHAR:
                case BinXmlToken.SQL_NTEXT:
                case BinXmlToken.XSD_BOOLEAN:
                case BinXmlToken.XSD_TIME:
                case BinXmlToken.XSD_DATETIME:
                case BinXmlToken.XSD_DATE:
                case BinXmlToken.XSD_BYTE:
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                case BinXmlToken.XSD_UNSIGNEDINT:
                case BinXmlToken.XSD_UNSIGNEDLONG:
                case BinXmlToken.XSD_QNAME:
                    ImplReadData(_token);
                    if (XmlNodeType.Text == _nodetype)
                        CheckAllowContent();
                    else if (_ignoreWhitespace && !_xmlspacePreserve)
                        goto Read; // skip to next token
                    return true;
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
 
            return true;
        }
 
        private void ImplReadData(BinXmlToken tokenType)
        {
            Debug.Assert(_mark < 0);
            _mark = _pos;
 
            switch (tokenType)
            {
                case BinXmlToken.SQL_CHAR:
                case BinXmlToken.SQL_VARCHAR:
                case BinXmlToken.SQL_TEXT:
                case BinXmlToken.SQL_NCHAR:
                case BinXmlToken.SQL_NVARCHAR:
                case BinXmlToken.SQL_NTEXT:
                    _valueType = typeof(string);
                    _hasTypedValue = false;
                    break;
                default:
                    _valueType = GetValueType(_token);
                    _hasTypedValue = true;
                    break;
            }
 
            _nodetype = ScanOverValue(_token, false, true);
 
            // we don't support lists of values
            BinXmlToken tNext = PeekNextToken();
            switch (tNext)
            {
                case BinXmlToken.SQL_BIT:
                case BinXmlToken.SQL_TINYINT:
                case BinXmlToken.SQL_SMALLINT:
                case BinXmlToken.SQL_INT:
                case BinXmlToken.SQL_BIGINT:
                case BinXmlToken.SQL_REAL:
                case BinXmlToken.SQL_FLOAT:
                case BinXmlToken.SQL_MONEY:
                case BinXmlToken.SQL_SMALLMONEY:
                case BinXmlToken.SQL_DATETIME:
                case BinXmlToken.SQL_SMALLDATETIME:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_UUID:
                case BinXmlToken.SQL_VARBINARY:
                case BinXmlToken.SQL_BINARY:
                case BinXmlToken.SQL_IMAGE:
                case BinXmlToken.SQL_UDT:
                case BinXmlToken.XSD_KATMAI_DATE:
                case BinXmlToken.XSD_KATMAI_DATETIME:
                case BinXmlToken.XSD_KATMAI_TIME:
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                case BinXmlToken.XSD_BINHEX:
                case BinXmlToken.XSD_BASE64:
                case BinXmlToken.SQL_CHAR:
                case BinXmlToken.SQL_VARCHAR:
                case BinXmlToken.SQL_TEXT:
                case BinXmlToken.SQL_NCHAR:
                case BinXmlToken.SQL_NVARCHAR:
                case BinXmlToken.SQL_NTEXT:
                case BinXmlToken.XSD_BOOLEAN:
                case BinXmlToken.XSD_TIME:
                case BinXmlToken.XSD_DATETIME:
                case BinXmlToken.XSD_DATE:
                case BinXmlToken.XSD_BYTE:
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                case BinXmlToken.XSD_UNSIGNEDINT:
                case BinXmlToken.XSD_UNSIGNEDLONG:
                case BinXmlToken.XSD_QNAME:
                    throw CreateNotSupportedException(SR.XmlBinary_ListsOfValuesNotSupported);
                default:
                    break;
            }
        }
 
        private void ImplReadElement()
        {
            if (3 != _docState || 9 != _docState)
            {
                switch (_docState)
                {
                    case 0:
                        _docState = 9;
                        break;
                    case 1:
                    case 2:
                        _docState = 3;
                        break;
                    case -1:
                        throw CreateUnexpectedTokenException(_token);
                    default:
                        break;
                }
            }
            _elemDepth++;
            if (_elemDepth == _elementStack.Length)
                GrowElements();
            QName qname = _symbolTables.qnametable[ReadQNameRef()];
            _qnameOther = _qnameElement = qname;
            _elementStack[_elemDepth].Set(qname, _xmlspacePreserve);
            this.PushNamespace(qname.prefix, qname.namespaceUri, true);
            BinXmlToken t = PeekNextToken();
            if (BinXmlToken.Attr == t)
            {
                ScanAttributes();
                t = PeekNextToken();
            }
            GenerateImpliedXmlnsAttrs();
            if (BinXmlToken.EndElem == t)
            {
                NextToken(); // move over token...
                _isEmpty = true;
            }
            else if (BinXmlToken.SQL_NVARCHAR == t)
            {
                if (_mark < 0)
                    _mark = _pos;
                // skip over token byte
                _pos++;
                // is this a zero-length string?  if yes, skip it.
                // (It just indicates that this is _not_ an empty element)
                // Also make sure that the following token is an EndElem
                if (0 == ReadByte())
                {
                    if (BinXmlToken.EndElem != (BinXmlToken)ReadByte())
                    {
                        Debug.Assert(_pos >= 3);
                        _pos -= 3; // jump back to start of NVarChar token
                    }
                    else
                    {
                        Debug.Assert(_pos >= 1);
                        _pos -= 1; // jump back to EndElem token
                    }
                }
                else
                {
                    Debug.Assert(_pos >= 2);
                    _pos -= 2; // jump back to start of NVarChar token
                }
            }
            _nodetype = XmlNodeType.Element;
            _valueType = typeof(object);
            _posAfterAttrs = _pos;
        }
 
        private void ImplReadEndElement()
        {
            if (_elemDepth == 0)
                throw CreateXmlException(SR.Xml_UnexpectedEndTag);
            int index = _elemDepth;
            if (1 == index && 3 == _docState)
                _docState = -1;
            _qnameOther = _elementStack[index].name;
            _xmlspacePreserve = _elementStack[index].xmlspacePreserve;
            _nodetype = XmlNodeType.EndElement;
        }
 
        private void ImplReadDoctype()
        {
            if (_dtdProcessing == DtdProcessing.Prohibit)
                throw CreateXmlException(SR.Xml_DtdIsProhibited);
            // 0=>auto, 1=>doc/pre-dtd, 2=>doc/pre-elem, 3=>doc/instance -1=>doc/post-elem, 9=>frag
            switch (_docState)
            {
                case 0: // 0=>auto
                case 1: // 1=>doc/pre-dtd
                    break;
                case 9: // 9=>frag
                    throw CreateXmlException(SR.Xml_DtdNotAllowedInFragment);
                default: // 2=>doc/pre-elem, 3=>doc/instance -1=>doc/post-elem
                    throw CreateXmlException(SR.Xml_BadDTDLocation);
            }
            _docState = 2;
            _qnameOther.localname = ParseText();
            if (BinXmlToken.System == PeekToken())
            {
                _pos++;
                _attributes[_attrCount++].Set(new QName(string.Empty, _xnt.Add("SYSTEM"), string.Empty), ParseText());
            }
            if (BinXmlToken.Public == PeekToken())
            {
                _pos++;
                _attributes[_attrCount++].Set(new QName(string.Empty, _xnt.Add("PUBLIC"), string.Empty), ParseText());
            }
            if (BinXmlToken.Subset == PeekToken())
            {
                _pos++;
                _mark = _pos;
                _tokLen = ScanText(out _tokDataPos);
            }
            else
            {
                _tokLen = _tokDataPos = 0;
            }
            _nodetype = XmlNodeType.DocumentType;
            _posAfterAttrs = _pos;
        }
 
        private void ImplReadPI()
        {
            _qnameOther.localname = _symbolTables.symtable[ReadNameRef()];
            _mark = _pos;
            _tokLen = ScanText(out _tokDataPos);
            _nodetype = XmlNodeType.ProcessingInstruction;
        }
 
        private void ImplReadComment()
        {
            _nodetype = XmlNodeType.Comment;
            _mark = _pos;
            _tokLen = ScanText(out _tokDataPos);
        }
 
        private void ImplReadCDATA()
        {
            CheckAllowContent();
            _nodetype = XmlNodeType.CDATA;
            _mark = _pos;
            _tokLen = ScanText(out _tokDataPos);
        }
 
        private void ImplReadNest()
        {
            CheckAllowContent();
            // push current nametables
            _prevNameInfo = new NestedBinXml(_symbolTables, _docState, _prevNameInfo);
            _symbolTables.Init();
            _docState = 0; // auto
        }
 
        private void ImplReadEndNest()
        {
            Debug.Assert(_prevNameInfo != null);
            NestedBinXml nested = _prevNameInfo;
            _symbolTables = nested.symbolTables;
            _docState = nested.docState;
            _prevNameInfo = nested.next;
        }
 
        private void ImplReadXmlText()
        {
            CheckAllowContent();
            string xmltext = ParseText();
            XmlNamespaceManager xnm = new XmlNamespaceManager(_xnt);
            foreach (NamespaceDecl decl in _namespaces.Values)
            {
                if (decl.scope > 0)
                {
#if DEBUG
                    if ((object)decl.prefix != (object?)this._xnt.Get(decl.prefix))
                        throw new Exception("Prefix not interned: \'" + decl.prefix + "\'");
                    if ((object)decl.uri != (object?)this._xnt.Get(decl.uri))
                        throw new Exception("Uri not interned: \'" + decl.uri + "\'");
#endif
                    xnm.AddNamespace(decl.prefix, decl.uri);
                }
            }
            XmlReaderSettings settings = this.Settings;
            settings.ReadOnly = false;
            settings.NameTable = _xnt;
            settings.DtdProcessing = DtdProcessing.Prohibit;
            if (0 != _elemDepth)
            {
                settings.ConformanceLevel = ConformanceLevel.Fragment;
            }
            settings.ReadOnly = true;
            XmlParserContext xpc = new XmlParserContext(_xnt, xnm, this.XmlLang, this.XmlSpace);
            _textXmlReader = new XmlTextReaderImpl(xmltext, xpc, settings);
            if (!_textXmlReader.Read()
                || ((_textXmlReader.NodeType == XmlNodeType.XmlDeclaration)
                    && !_textXmlReader.Read()))
            {
                _state = ScanState.Doc;
                ReadDoc();
            }
            else
            {
                _state = ScanState.XmlText;
                UpdateFromTextReader();
            }
        }
 
        private void UpdateFromTextReader()
        {
            Debug.Assert(_textXmlReader != null);
            XmlReader r = _textXmlReader;
            _nodetype = r.NodeType;
            _qnameOther.prefix = r.Prefix;
            _qnameOther.localname = r.LocalName;
            _qnameOther.namespaceUri = r.NamespaceURI;
            _valueType = r.ValueType;
            _isEmpty = r.IsEmptyElement;
        }
 
        private bool UpdateFromTextReader(bool needUpdate)
        {
            if (needUpdate)
                UpdateFromTextReader();
            return needUpdate;
        }
 
        private void CheckAllowContent()
        {
            switch (_docState)
            {
                case 0: // auto
                    _docState = 9;
                    break;
                case 9: // conformance = fragment
                case 3:
                    break;
                default:
                    throw CreateXmlException(SR.Xml_InvalidRootData);
            }
        }
 
        private static Type?[] GenerateTokenTypeMap()
        {
            Type?[] map = new Type[256];
            map[(int)BinXmlToken.XSD_BOOLEAN] = typeof(bool);
            map[(int)BinXmlToken.SQL_TINYINT] = typeof(byte);
            map[(int)BinXmlToken.XSD_BYTE] = typeof(sbyte);
            map[(int)BinXmlToken.SQL_SMALLINT] = typeof(short);
            map[(int)BinXmlToken.XSD_UNSIGNEDSHORT] = typeof(ushort);
            map[(int)BinXmlToken.XSD_UNSIGNEDINT] = typeof(uint);
            map[(int)BinXmlToken.SQL_REAL] = typeof(float);
            map[(int)BinXmlToken.SQL_FLOAT] = typeof(double);
            map[(int)BinXmlToken.SQL_BIGINT] = typeof(long);
            map[(int)BinXmlToken.XSD_UNSIGNEDLONG] = typeof(ulong);
            map[(int)BinXmlToken.XSD_QNAME] = typeof(XmlQualifiedName);
            Type TypeOfInt32 = typeof(int);
            map[(int)BinXmlToken.SQL_BIT] = TypeOfInt32;
            map[(int)BinXmlToken.SQL_INT] = TypeOfInt32;
            Type TypeOfDecimal = typeof(decimal);
            map[(int)BinXmlToken.SQL_SMALLMONEY] = TypeOfDecimal;
            map[(int)BinXmlToken.SQL_MONEY] = TypeOfDecimal;
            map[(int)BinXmlToken.SQL_DECIMAL] = TypeOfDecimal;
            map[(int)BinXmlToken.SQL_NUMERIC] = TypeOfDecimal;
            map[(int)BinXmlToken.XSD_DECIMAL] = TypeOfDecimal;
            Type TypeOfDateTime = typeof(System.DateTime);
            map[(int)BinXmlToken.SQL_SMALLDATETIME] = TypeOfDateTime;
            map[(int)BinXmlToken.SQL_DATETIME] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_TIME] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_DATETIME] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_DATE] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_KATMAI_DATE] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_KATMAI_DATETIME] = TypeOfDateTime;
            map[(int)BinXmlToken.XSD_KATMAI_TIME] = TypeOfDateTime;
            Type TypeOfDateTimeOffset = typeof(System.DateTimeOffset);
            map[(int)BinXmlToken.XSD_KATMAI_DATEOFFSET] = TypeOfDateTimeOffset;
            map[(int)BinXmlToken.XSD_KATMAI_DATETIMEOFFSET] = TypeOfDateTimeOffset;
            map[(int)BinXmlToken.XSD_KATMAI_TIMEOFFSET] = TypeOfDateTimeOffset;
            Type TypeOfByteArray = typeof(byte[]);
            map[(int)BinXmlToken.SQL_VARBINARY] = TypeOfByteArray;
            map[(int)BinXmlToken.SQL_BINARY] = TypeOfByteArray;
            map[(int)BinXmlToken.SQL_IMAGE] = TypeOfByteArray;
            map[(int)BinXmlToken.SQL_UDT] = TypeOfByteArray;
            map[(int)BinXmlToken.XSD_BINHEX] = TypeOfByteArray;
            map[(int)BinXmlToken.XSD_BASE64] = TypeOfByteArray;
            Type TypeOfString = typeof(string);
            map[(int)BinXmlToken.SQL_CHAR] = TypeOfString;
            map[(int)BinXmlToken.SQL_VARCHAR] = TypeOfString;
            map[(int)BinXmlToken.SQL_TEXT] = TypeOfString;
            map[(int)BinXmlToken.SQL_NCHAR] = TypeOfString;
            map[(int)BinXmlToken.SQL_NVARCHAR] = TypeOfString;
            map[(int)BinXmlToken.SQL_NTEXT] = TypeOfString;
            map[(int)BinXmlToken.SQL_UUID] = TypeOfString;
            return map;
        }
 
        private System.Type GetValueType(BinXmlToken token)
        {
            Type? t = s_tokenTypeMap[(int)token];
 
            if (t == null)
                throw CreateUnexpectedTokenException(token);
 
            return t;
        }
 
        // helper method...
        private void ReScanOverValue(BinXmlToken token)
        {
            ScanOverValue(token, true, false);
        }
 
        private XmlNodeType ScanOverValue(BinXmlToken token, bool attr, bool checkChars)
        {
            if (token == BinXmlToken.SQL_NVARCHAR)
            {
                if (_mark < 0)
                    _mark = _pos;
                _tokLen = ParseMB32();
                _tokDataPos = _pos;
                checked { _pos += _tokLen * 2; }
                Fill(-1);
                // check chars (if this is the first pass and settings.CheckCharacters was set)
                if (checkChars && _checkCharacters)
                {
                    // check for invalid chardata
                    return CheckText(attr);
                }
                else if (!attr)
                { // attribute values are always reported as Text
                    // check for whitespace-only text
                    return CheckTextIsWS();
                }
                else
                {
                    return XmlNodeType.Text;
                }
            }
            else
            {
                return ScanOverAnyValue(token, attr, checkChars);
            }
        }
 
        private XmlNodeType ScanOverAnyValue(BinXmlToken token, bool attr, bool checkChars)
        {
            if (_mark < 0)
                _mark = _pos;
            checked
            {
                switch (token)
                {
                    case BinXmlToken.SQL_BIT:
                    case BinXmlToken.SQL_TINYINT:
                    case BinXmlToken.XSD_BOOLEAN:
                    case BinXmlToken.XSD_BYTE:
                        _tokDataPos = _pos;
                        _tokLen = 1;
                        _pos += 1;
                        break;
 
                    case BinXmlToken.SQL_SMALLINT:
                    case BinXmlToken.XSD_UNSIGNEDSHORT:
                        _tokDataPos = _pos;
                        _tokLen = 2;
                        _pos += 2;
                        break;
 
                    case BinXmlToken.SQL_INT:
                    case BinXmlToken.XSD_UNSIGNEDINT:
                    case BinXmlToken.SQL_REAL:
                    case BinXmlToken.SQL_SMALLMONEY:
                    case BinXmlToken.SQL_SMALLDATETIME:
                        _tokDataPos = _pos;
                        _tokLen = 4;
                        _pos += 4;
                        break;
 
                    case BinXmlToken.SQL_BIGINT:
                    case BinXmlToken.XSD_UNSIGNEDLONG:
                    case BinXmlToken.SQL_FLOAT:
                    case BinXmlToken.SQL_MONEY:
                    case BinXmlToken.SQL_DATETIME:
                    case BinXmlToken.XSD_TIME:
                    case BinXmlToken.XSD_DATETIME:
                    case BinXmlToken.XSD_DATE:
                        _tokDataPos = _pos;
                        _tokLen = 8;
                        _pos += 8;
                        break;
 
                    case BinXmlToken.SQL_UUID:
                        _tokDataPos = _pos;
                        _tokLen = 16;
                        _pos += 16;
                        break;
 
                    case BinXmlToken.SQL_DECIMAL:
                    case BinXmlToken.SQL_NUMERIC:
                    case BinXmlToken.XSD_DECIMAL:
                        _tokDataPos = _pos;
                        _tokLen = ParseMB64();
                        _pos += _tokLen;
                        break;
 
                    case BinXmlToken.SQL_VARBINARY:
                    case BinXmlToken.SQL_BINARY:
                    case BinXmlToken.SQL_IMAGE:
                    case BinXmlToken.SQL_UDT:
                    case BinXmlToken.XSD_BINHEX:
                    case BinXmlToken.XSD_BASE64:
                        _tokLen = ParseMB64();
                        _tokDataPos = _pos;
                        _pos += _tokLen;
                        break;
 
                    case BinXmlToken.SQL_CHAR:
                    case BinXmlToken.SQL_VARCHAR:
                    case BinXmlToken.SQL_TEXT:
                        _tokLen = ParseMB64();
                        _tokDataPos = _pos;
                        _pos += _tokLen;
                        if (checkChars && _checkCharacters)
                        {
                            // check for invalid chardata
                            Fill(-1);
                            string val = ValueAsString(token);
                            XmlConvert.VerifyCharData(val, ExceptionType.ArgumentException, ExceptionType.XmlException);
                            _stringValue = val;
                        }
                        break;
 
                    case BinXmlToken.SQL_NVARCHAR:
                    case BinXmlToken.SQL_NCHAR:
                    case BinXmlToken.SQL_NTEXT:
                        return ScanOverValue(BinXmlToken.SQL_NVARCHAR, attr, checkChars);
 
                    case BinXmlToken.XSD_QNAME:
                        _tokDataPos = _pos;
                        ParseMB32();
                        break;
 
                    case BinXmlToken.XSD_KATMAI_DATE:
                    case BinXmlToken.XSD_KATMAI_DATETIME:
                    case BinXmlToken.XSD_KATMAI_TIME:
                    case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                    case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                    case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                        VerifyVersion(2, token);
                        _tokDataPos = _pos;
                        _tokLen = GetXsdKatmaiTokenLength(token);
                        _pos += _tokLen;
                        break;
 
                    default:
                        throw CreateUnexpectedTokenException(token);
                }
            }
            Fill(-1);
            return XmlNodeType.Text;
        }
 
        private XmlNodeType CheckText(bool attr)
        {
            Debug.Assert(_checkCharacters, "this.checkCharacters");
            // grab local copy (perf)
 
            // Get the bytes for the current token. _tokDataPos is the beginning position,
            // and _pos has advanced to the next token (1 past the end of this token).
            ReadOnlySpan<byte> data = _data.AsSpan(_tokDataPos, _pos - _tokDataPos);
            Debug.Assert(data.Length % 2 == 0, "Data size should not be odd");
 
            if (!attr)
            {
                // scan if this is whitespace
                while (true)
                {
                    if (!BinaryPrimitives.TryReadUInt16LittleEndian(data, out ushort value))
                        return _xmlspacePreserve ? XmlNodeType.SignificantWhitespace : XmlNodeType.Whitespace;
                    if (value > byte.MaxValue || !XmlCharType.IsWhiteSpace((char)value))
                        break;
                    data = data.Slice(2); // we consumed one ANSI whitespace char
                }
            }
 
            while (true)
            {
                char ch;
                while (true)
                {
                    if (!BinaryPrimitives.TryReadUInt16LittleEndian(data, out ushort value))
                        return XmlNodeType.Text;
                    data = data.Slice(2); // we consumed one char (possibly a high surrogate)
                    ch = (char)value;
                    if (!XmlCharType.IsCharData(ch))
                        break;
                }
 
                if (!XmlCharType.IsHighSurrogate(ch))
                {
                    throw XmlConvert.CreateInvalidCharException(ch, '\0', ExceptionType.XmlException);
                }
                else
                {
                    if (!BinaryPrimitives.TryReadUInt16LittleEndian(data, out ushort lowSurr))
                    {
                        throw CreateXmlException(SR.Xml_InvalidSurrogateMissingLowChar);
                    }
                    if (!XmlCharType.IsLowSurrogate((char)lowSurr))
                    {
                        throw XmlConvert.CreateInvalidSurrogatePairException(ch, (char)lowSurr);
                    }
                    data = data.Slice(2); //consumed a low surrogate char
                }
            }
        }
 
        private XmlNodeType CheckTextIsWS()
        {
            Debug.Assert(!_checkCharacters, "!this.checkCharacters");
            byte[] data = _data;
            // assert that size is an even number
            Debug.Assert(0 == ((_pos - _tokDataPos) & 1), "Data size should not be odd");
            for (int pos = _tokDataPos; pos < _pos; pos += 2)
            {
                if (0 != data[pos + 1])
                    goto NonWSText;
                switch (data[pos])
                {
                    case 0x09: // tab
                    case 0x0A: // nl
                    case 0x0D: // cr
                    case 0x20: // space
                        break;
                    default:
                        goto NonWSText;
                }
            }
            if (_xmlspacePreserve)
                return XmlNodeType.SignificantWhitespace;
            return XmlNodeType.Whitespace;
        NonWSText:
            return XmlNodeType.Text;
        }
 
        private void CheckValueTokenBounds()
        {
            if ((_end - _tokDataPos) < _tokLen)
                throw CreateXmlException(SR.Xml_UnexpectedEOF1);
        }
 
        private int GetXsdKatmaiTokenLength(BinXmlToken token)
        {
            byte scale;
            switch (token)
            {
                case BinXmlToken.XSD_KATMAI_DATE:
                    // SQL Katmai type DATE = date(3b)
                    return 3;
                case BinXmlToken.XSD_KATMAI_TIME:
                case BinXmlToken.XSD_KATMAI_DATETIME:
                    // SQL Katmai type DATETIME2 = scale(1b) + time(3-5b) + date(3b)
                    Fill(0);
                    scale = _data[_pos];
                    return 4 + XsdKatmaiTimeScaleToValueLength(scale);
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                    // SQL Katmai type DATETIMEOFFSET = scale(1b) + time(3-5b) + date(3b) + zone(2b)
                    Fill(0);
                    scale = _data[_pos];
                    return 6 + XsdKatmaiTimeScaleToValueLength(scale);
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private static int XsdKatmaiTimeScaleToValueLength(byte scale)
        {
            if (scale > 7)
            {
                throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
            }
            return XsdKatmaiTimeScaleToValueLengthMap[scale];
        }
 
        private long ValueAsLong()
        {
            CheckValueTokenBounds();
            switch (_token)
            {
                case BinXmlToken.SQL_BIT:
                case BinXmlToken.SQL_TINYINT:
                    {
                        byte v = _data[_tokDataPos];
                        return v;
                    }
 
                case BinXmlToken.XSD_BYTE:
                    {
                        sbyte v = unchecked((sbyte)_data[_tokDataPos]);
                        return v;
                    }
 
                case BinXmlToken.SQL_SMALLINT:
                    return GetInt16(_tokDataPos);
 
                case BinXmlToken.SQL_INT:
                    return GetInt32(_tokDataPos);
 
                case BinXmlToken.SQL_BIGINT:
                    return GetInt64(_tokDataPos);
 
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                    return GetUInt16(_tokDataPos);
 
                case BinXmlToken.XSD_UNSIGNEDINT:
                    return GetUInt32(_tokDataPos);
 
                case BinXmlToken.XSD_UNSIGNEDLONG:
                    {
                        ulong v = GetUInt64(_tokDataPos);
                        return checked((long)v);
                    }
 
                case BinXmlToken.SQL_REAL:
                case BinXmlToken.SQL_FLOAT:
                    {
                        double v = ValueAsDouble();
                        return (long)v;
                    }
 
                case BinXmlToken.SQL_MONEY:
                case BinXmlToken.SQL_SMALLMONEY:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                case BinXmlToken.XSD_DECIMAL:
                    {
                        decimal v = ValueAsDecimal();
                        return (long)v;
                    }
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private ulong ValueAsULong()
        {
            if (BinXmlToken.XSD_UNSIGNEDLONG == _token)
            {
                CheckValueTokenBounds();
                return GetUInt64(_tokDataPos);
            }
            else
            {
                throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private decimal ValueAsDecimal()
        {
            CheckValueTokenBounds();
            switch (_token)
            {
                case BinXmlToken.SQL_BIT:
                case BinXmlToken.SQL_TINYINT:
                case BinXmlToken.SQL_SMALLINT:
                case BinXmlToken.SQL_INT:
                case BinXmlToken.SQL_BIGINT:
                case BinXmlToken.XSD_BYTE:
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                case BinXmlToken.XSD_UNSIGNEDINT:
                    return new decimal(ValueAsLong());
 
                case BinXmlToken.XSD_UNSIGNEDLONG:
                    return new decimal(ValueAsULong());
 
                case BinXmlToken.SQL_REAL:
                    return new decimal(GetSingle(_tokDataPos));
 
                case BinXmlToken.SQL_FLOAT:
                    return new decimal(GetDouble(_tokDataPos));
 
                case BinXmlToken.SQL_SMALLMONEY:
                    {
                        BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt32(_tokDataPos));
                        return v.ToDecimal();
                    }
                case BinXmlToken.SQL_MONEY:
                    {
                        BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt64(_tokDataPos));
                        return v.ToDecimal();
                    }
 
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                    {
                        BinXmlSqlDecimal v = new BinXmlSqlDecimal(_data, _tokDataPos, _token == BinXmlToken.XSD_DECIMAL);
                        return v.ToDecimal();
                    }
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private double ValueAsDouble()
        {
            CheckValueTokenBounds();
            switch (_token)
            {
                case BinXmlToken.SQL_BIT:
                case BinXmlToken.SQL_TINYINT:
                case BinXmlToken.SQL_SMALLINT:
                case BinXmlToken.SQL_INT:
                case BinXmlToken.SQL_BIGINT:
                case BinXmlToken.XSD_BYTE:
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                case BinXmlToken.XSD_UNSIGNEDINT:
                    return (double)ValueAsLong();
 
                case BinXmlToken.XSD_UNSIGNEDLONG:
                    return (double)ValueAsULong();
 
                case BinXmlToken.SQL_REAL:
                    return GetSingle(_tokDataPos);
 
                case BinXmlToken.SQL_FLOAT:
                    return GetDouble(_tokDataPos);
 
                case BinXmlToken.SQL_SMALLMONEY:
                case BinXmlToken.SQL_MONEY:
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                    return (double)ValueAsDecimal();
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private DateTime ValueAsDateTime()
        {
            CheckValueTokenBounds();
            switch (_token)
            {
                case BinXmlToken.SQL_DATETIME:
                    {
                        int pos = _tokDataPos;
                        int dateticks; uint timeticks;
                        dateticks = GetInt32(pos);
                        timeticks = GetUInt32(pos + 4);
                        return BinXmlDateTime.SqlDateTimeToDateTime(dateticks, timeticks);
                    }
 
                case BinXmlToken.SQL_SMALLDATETIME:
                    {
                        int pos = _tokDataPos;
                        short dateticks; ushort timeticks;
                        dateticks = GetInt16(pos);
                        timeticks = GetUInt16(pos + 2);
                        return BinXmlDateTime.SqlSmallDateTimeToDateTime(dateticks, timeticks);
                    }
 
                case BinXmlToken.XSD_TIME:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdTimeToDateTime(time);
                    }
 
                case BinXmlToken.XSD_DATE:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdDateToDateTime(time);
                    }
 
                case BinXmlToken.XSD_DATETIME:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdDateTimeToDateTime(time);
                    }
 
                case BinXmlToken.XSD_KATMAI_DATE:
                    return BinXmlDateTime.XsdKatmaiDateToDateTime(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATETIME:
                    return BinXmlDateTime.XsdKatmaiDateTimeToDateTime(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_TIME:
                    return BinXmlDateTime.XsdKatmaiTimeToDateTime(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                    return BinXmlDateTime.XsdKatmaiDateOffsetToDateTime(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                    return BinXmlDateTime.XsdKatmaiDateTimeOffsetToDateTime(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                    return BinXmlDateTime.XsdKatmaiTimeOffsetToDateTime(_data, _tokDataPos);
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private DateTimeOffset ValueAsDateTimeOffset()
        {
            CheckValueTokenBounds();
            return _token switch
            {
                BinXmlToken.XSD_KATMAI_DATEOFFSET => BinXmlDateTime.XsdKatmaiDateOffsetToDateTimeOffset(_data, _tokDataPos),
                BinXmlToken.XSD_KATMAI_DATETIMEOFFSET => BinXmlDateTime.XsdKatmaiDateTimeOffsetToDateTimeOffset(_data, _tokDataPos),
                BinXmlToken.XSD_KATMAI_TIMEOFFSET => BinXmlDateTime.XsdKatmaiTimeOffsetToDateTimeOffset(_data, _tokDataPos),
                _ => throw CreateUnexpectedTokenException(_token),
            };
        }
 
 
        private string ValueAsDateTimeString()
        {
            CheckValueTokenBounds();
            switch (_token)
            {
                case BinXmlToken.SQL_DATETIME:
                    {
                        int pos = _tokDataPos;
                        int dateticks; uint timeticks;
                        dateticks = GetInt32(pos);
                        timeticks = GetUInt32(pos + 4);
                        return BinXmlDateTime.SqlDateTimeToString(dateticks, timeticks);
                    }
 
                case BinXmlToken.SQL_SMALLDATETIME:
                    {
                        int pos = _tokDataPos;
                        short dateticks; ushort timeticks;
                        dateticks = GetInt16(pos);
                        timeticks = GetUInt16(pos + 2);
                        return BinXmlDateTime.SqlSmallDateTimeToString(dateticks, timeticks);
                    }
 
                case BinXmlToken.XSD_TIME:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdTimeToString(time);
                    }
 
                case BinXmlToken.XSD_DATE:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdDateToString(time);
                    }
 
                case BinXmlToken.XSD_DATETIME:
                    {
                        long time = GetInt64(_tokDataPos);
                        return BinXmlDateTime.XsdDateTimeToString(time);
                    }
 
                case BinXmlToken.XSD_KATMAI_DATE:
                    return BinXmlDateTime.XsdKatmaiDateToString(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATETIME:
                    return BinXmlDateTime.XsdKatmaiDateTimeToString(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_TIME:
                    return BinXmlDateTime.XsdKatmaiTimeToString(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                    return BinXmlDateTime.XsdKatmaiDateOffsetToString(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                    return BinXmlDateTime.XsdKatmaiDateTimeOffsetToString(_data, _tokDataPos);
 
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                    return BinXmlDateTime.XsdKatmaiTimeOffsetToString(_data, _tokDataPos);
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private string ValueAsString(BinXmlToken token)
        {
            try
            {
                CheckValueTokenBounds();
                switch (token)
                {
                    case BinXmlToken.SQL_NCHAR:
                    case BinXmlToken.SQL_NVARCHAR:
                    case BinXmlToken.SQL_NTEXT:
                        return GetString(_tokDataPos, _tokLen);
 
                    case BinXmlToken.XSD_BOOLEAN:
                        {
                            if (0 == _data[_tokDataPos])
                                return "false";
                            else
                                return "true";
                        }
 
                    case BinXmlToken.SQL_BIT:
                    case BinXmlToken.SQL_TINYINT:
                    case BinXmlToken.SQL_SMALLINT:
                    case BinXmlToken.SQL_INT:
                    case BinXmlToken.SQL_BIGINT:
                    case BinXmlToken.XSD_BYTE:
                    case BinXmlToken.XSD_UNSIGNEDSHORT:
                    case BinXmlToken.XSD_UNSIGNEDINT:
                        return ValueAsLong().ToString(CultureInfo.InvariantCulture);
 
                    case BinXmlToken.XSD_UNSIGNEDLONG:
                        return ValueAsULong().ToString(CultureInfo.InvariantCulture);
 
                    case BinXmlToken.SQL_REAL:
                        return XmlConvert.ToString(GetSingle(_tokDataPos));
 
                    case BinXmlToken.SQL_FLOAT:
                        return XmlConvert.ToString(GetDouble(_tokDataPos));
 
                    case BinXmlToken.SQL_UUID:
                        {
                            int a; short b, c;
                            int pos = _tokDataPos;
                            a = GetInt32(pos);
                            b = GetInt16(pos + 4);
                            c = GetInt16(pos + 6);
                            Guid v = new Guid(a, b, c, _data[pos + 8], _data[pos + 9], _data[pos + 10], _data[pos + 11], _data[pos + 12], _data[pos + 13], _data[pos + 14], _data[pos + 15]);
                            return v.ToString();
                        }
 
                    case BinXmlToken.SQL_SMALLMONEY:
                        {
                            BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt32(_tokDataPos));
                            return v.ToString();
                        }
                    case BinXmlToken.SQL_MONEY:
                        {
                            BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt64(_tokDataPos));
                            return v.ToString();
                        }
 
                    case BinXmlToken.XSD_DECIMAL:
                    case BinXmlToken.SQL_DECIMAL:
                    case BinXmlToken.SQL_NUMERIC:
                        {
                            BinXmlSqlDecimal v = new BinXmlSqlDecimal(_data, _tokDataPos, token == BinXmlToken.XSD_DECIMAL);
                            return v.ToString();
                        }
 
                    case BinXmlToken.SQL_CHAR:
                    case BinXmlToken.SQL_VARCHAR:
                    case BinXmlToken.SQL_TEXT:
                        {
                            int pos = _tokDataPos;
                            int codepage = GetInt32(pos);
                            Encoding enc = System.Text.Encoding.GetEncoding(codepage);
                            return enc.GetString(_data, pos + 4, _tokLen - 4);
                        }
 
                    case BinXmlToken.SQL_VARBINARY:
                    case BinXmlToken.SQL_BINARY:
                    case BinXmlToken.SQL_IMAGE:
                    case BinXmlToken.SQL_UDT:
                    case BinXmlToken.XSD_BASE64:
                        {
                            return Convert.ToBase64String(_data, _tokDataPos, _tokLen);
                        }
 
                    case BinXmlToken.XSD_BINHEX:
                        return BinHexEncoder.Encode(_data, _tokDataPos, _tokLen);
 
                    case BinXmlToken.SQL_DATETIME:
                    case BinXmlToken.SQL_SMALLDATETIME:
                    case BinXmlToken.XSD_TIME:
                    case BinXmlToken.XSD_DATE:
                    case BinXmlToken.XSD_DATETIME:
                    case BinXmlToken.XSD_KATMAI_DATE:
                    case BinXmlToken.XSD_KATMAI_DATETIME:
                    case BinXmlToken.XSD_KATMAI_TIME:
                    case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                    case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                    case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                        return ValueAsDateTimeString();
 
                    case BinXmlToken.XSD_QNAME:
                        {
                            int nameNum = ParseMB32(_tokDataPos);
                            if (nameNum < 0 || nameNum >= _symbolTables.qnameCount)
                                throw new XmlException(SR.XmlBin_InvalidQNameID, string.Empty);
                            QName qname = _symbolTables.qnametable[nameNum];
                            if (qname.prefix.Length == 0)
                                return qname.localname;
                            else
                                return $"{qname.prefix}:{qname.localname}";
                        }
 
                    default:
                        throw CreateUnexpectedTokenException(_token);
                }
            }
            catch
            {
                _state = ScanState.Error;
                throw;
            }
        }
 
        private object ValueAsObject(BinXmlToken token, bool returnInternalTypes)
        {
            CheckValueTokenBounds();
            switch (token)
            {
                case BinXmlToken.SQL_NCHAR:
                case BinXmlToken.SQL_NVARCHAR:
                case BinXmlToken.SQL_NTEXT:
                    return GetString(_tokDataPos, _tokLen);
 
                case BinXmlToken.XSD_BOOLEAN:
                    return (0 != _data[_tokDataPos]);
 
                case BinXmlToken.SQL_BIT:
                    return (int)_data[_tokDataPos];
 
                case BinXmlToken.SQL_TINYINT:
                    return _data[_tokDataPos];
 
                case BinXmlToken.SQL_SMALLINT:
                    return GetInt16(_tokDataPos);
 
                case BinXmlToken.SQL_INT:
                    return GetInt32(_tokDataPos);
 
                case BinXmlToken.SQL_BIGINT:
                    return GetInt64(_tokDataPos);
 
                case BinXmlToken.XSD_BYTE:
                    {
                        sbyte v = unchecked((sbyte)_data[_tokDataPos]);
                        return v;
                    }
 
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                    return GetUInt16(_tokDataPos);
 
                case BinXmlToken.XSD_UNSIGNEDINT:
                    return GetUInt32(_tokDataPos);
 
                case BinXmlToken.XSD_UNSIGNEDLONG:
                    return GetUInt64(_tokDataPos);
 
                case BinXmlToken.SQL_REAL:
                    return GetSingle(_tokDataPos);
 
                case BinXmlToken.SQL_FLOAT:
                    return GetDouble(_tokDataPos);
 
                case BinXmlToken.SQL_UUID:
                    {
                        int a; short b, c;
                        int pos = _tokDataPos;
                        a = GetInt32(pos);
                        b = GetInt16(pos + 4);
                        c = GetInt16(pos + 6);
                        Guid v = new Guid(a, b, c, _data[pos + 8], _data[pos + 9], _data[pos + 10], _data[pos + 11], _data[pos + 12], _data[pos + 13], _data[pos + 14], _data[pos + 15]);
                        return v.ToString();
                    }
 
                case BinXmlToken.SQL_SMALLMONEY:
                    {
                        BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt32(_tokDataPos));
                        if (returnInternalTypes)
                            return v;
                        else
                            return v.ToDecimal();
                    }
 
                case BinXmlToken.SQL_MONEY:
                    {
                        BinXmlSqlMoney v = new BinXmlSqlMoney(GetInt64(_tokDataPos));
                        if (returnInternalTypes)
                            return v;
                        else
                            return v.ToDecimal();
                    }
 
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                    {
                        BinXmlSqlDecimal v = new BinXmlSqlDecimal(_data, _tokDataPos, token == BinXmlToken.XSD_DECIMAL);
                        if (returnInternalTypes)
                            return v;
                        else
                            return v.ToDecimal();
                    }
 
                case BinXmlToken.SQL_CHAR:
                case BinXmlToken.SQL_VARCHAR:
                case BinXmlToken.SQL_TEXT:
                    {
                        int pos = _tokDataPos;
                        int codepage = GetInt32(pos);
                        Encoding enc = System.Text.Encoding.GetEncoding(codepage);
                        return enc.GetString(_data, pos + 4, _tokLen - 4);
                    }
 
                case BinXmlToken.SQL_VARBINARY:
                case BinXmlToken.SQL_BINARY:
                case BinXmlToken.SQL_IMAGE:
                case BinXmlToken.SQL_UDT:
                case BinXmlToken.XSD_BASE64:
                case BinXmlToken.XSD_BINHEX:
                    {
                        byte[] data = new byte[_tokLen];
                        Array.Copy(_data, _tokDataPos, data, 0, _tokLen);
                        return data;
                    }
 
                case BinXmlToken.SQL_DATETIME:
                case BinXmlToken.SQL_SMALLDATETIME:
                case BinXmlToken.XSD_TIME:
                case BinXmlToken.XSD_DATE:
                case BinXmlToken.XSD_DATETIME:
                case BinXmlToken.XSD_KATMAI_DATE:
                case BinXmlToken.XSD_KATMAI_DATETIME:
                case BinXmlToken.XSD_KATMAI_TIME:
                    return ValueAsDateTime();
 
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                    return ValueAsDateTimeOffset();
 
                case BinXmlToken.XSD_QNAME:
                    {
                        int nameNum = ParseMB32(_tokDataPos);
                        if (nameNum < 0 || nameNum >= _symbolTables.qnameCount)
                            throw new XmlException(SR.XmlBin_InvalidQNameID, string.Empty);
                        QName qname = _symbolTables.qnametable[nameNum];
                        return new XmlQualifiedName(qname.localname, qname.namespaceUri);
                    }
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
        }
 
        private static XmlValueConverter GetValueConverter(XmlTypeCode typeCode)
        {
            XmlSchemaSimpleType xsst = DatatypeImplementation.GetSimpleTypeFromTypeCode(typeCode);
            return xsst.ValueConverter;
        }
 
        private object ValueAs(BinXmlToken token, Type returnType, IXmlNamespaceResolver? namespaceResolver)
        {
            object value;
            CheckValueTokenBounds();
            switch (token)
            {
                case BinXmlToken.SQL_NCHAR:
                case BinXmlToken.SQL_NVARCHAR:
                case BinXmlToken.SQL_NTEXT:
                    value = GetValueConverter(XmlTypeCode.UntypedAtomic).ChangeType(
                        GetString(_tokDataPos, _tokLen),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_BOOLEAN:
                    value = GetValueConverter(XmlTypeCode.Boolean).ChangeType(
                        (0 != _data[_tokDataPos]),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_BIT:
                    value = GetValueConverter(XmlTypeCode.NonNegativeInteger).ChangeType(
                        (int)_data[_tokDataPos],
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_TINYINT:
                    value = GetValueConverter(XmlTypeCode.UnsignedByte).ChangeType(
                        _data[_tokDataPos],
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_SMALLINT:
                    {
                        int v = GetInt16(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.Short).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.SQL_INT:
                    {
                        int v = GetInt32(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.Int).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.SQL_BIGINT:
                    {
                        long v = GetInt64(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.Long).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.XSD_BYTE:
                    {
                        value = GetValueConverter(XmlTypeCode.Byte).ChangeType(
                            (int)unchecked((sbyte)_data[_tokDataPos]),
                            returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.XSD_UNSIGNEDSHORT:
                    {
                        int v = GetUInt16(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.UnsignedShort).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.XSD_UNSIGNEDINT:
                    {
                        long v = GetUInt32(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.UnsignedInt).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.XSD_UNSIGNEDLONG:
                    {
                        decimal v = (decimal)GetUInt64(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.UnsignedLong).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.SQL_REAL:
                    {
                        float v = GetSingle(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.Float).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.SQL_FLOAT:
                    {
                        double v = GetDouble(_tokDataPos);
                        value = GetValueConverter(XmlTypeCode.Double).ChangeType(
                            v, returnType, namespaceResolver);
                        break;
                    }
                case BinXmlToken.SQL_UUID:
                    value = GetValueConverter(XmlTypeCode.String).ChangeType(
                        this.ValueAsString(token), returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_SMALLMONEY:
                    value = GetValueConverter(XmlTypeCode.Decimal).ChangeType(
                        (new BinXmlSqlMoney(GetInt32(_tokDataPos))).ToDecimal(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_MONEY:
                    value = GetValueConverter(XmlTypeCode.Decimal).ChangeType(
                        (new BinXmlSqlMoney(GetInt64(_tokDataPos))).ToDecimal(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_DECIMAL:
                case BinXmlToken.SQL_DECIMAL:
                case BinXmlToken.SQL_NUMERIC:
                    value = GetValueConverter(XmlTypeCode.Decimal).ChangeType(
                        (new BinXmlSqlDecimal(_data, _tokDataPos, token == BinXmlToken.XSD_DECIMAL)).ToDecimal(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.SQL_CHAR:
                case BinXmlToken.SQL_VARCHAR:
                case BinXmlToken.SQL_TEXT:
                    {
                        int pos = _tokDataPos;
                        int codepage = GetInt32(pos);
                        Encoding enc = System.Text.Encoding.GetEncoding(codepage);
                        value = GetValueConverter(XmlTypeCode.UntypedAtomic).ChangeType(
                            enc.GetString(_data, pos + 4, _tokLen - 4),
                            returnType, namespaceResolver);
                        break;
                    }
 
                case BinXmlToken.SQL_VARBINARY:
                case BinXmlToken.SQL_BINARY:
                case BinXmlToken.SQL_IMAGE:
                case BinXmlToken.SQL_UDT:
                case BinXmlToken.XSD_BASE64:
                case BinXmlToken.XSD_BINHEX:
                    {
                        byte[] data = new byte[_tokLen];
                        Array.Copy(_data, _tokDataPos, data, 0, _tokLen);
                        value = GetValueConverter(token == BinXmlToken.XSD_BINHEX ? XmlTypeCode.HexBinary : XmlTypeCode.Base64Binary).ChangeType(
                            data, returnType, namespaceResolver);
                        break;
                    }
 
                case BinXmlToken.SQL_DATETIME:
                case BinXmlToken.SQL_SMALLDATETIME:
                case BinXmlToken.XSD_DATETIME:
                case BinXmlToken.XSD_KATMAI_DATE:
                case BinXmlToken.XSD_KATMAI_DATETIME:
                case BinXmlToken.XSD_KATMAI_TIME:
                    value = GetValueConverter(XmlTypeCode.DateTime).ChangeType(
                        ValueAsDateTime(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_KATMAI_DATEOFFSET:
                case BinXmlToken.XSD_KATMAI_DATETIMEOFFSET:
                case BinXmlToken.XSD_KATMAI_TIMEOFFSET:
                    value = GetValueConverter(XmlTypeCode.DateTime).ChangeType(
                        ValueAsDateTimeOffset(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_TIME:
                    value = GetValueConverter(XmlTypeCode.Time).ChangeType(
                        ValueAsDateTime(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_DATE:
                    value = GetValueConverter(XmlTypeCode.Date).ChangeType(
                        ValueAsDateTime(),
                        returnType, namespaceResolver);
                    break;
 
                case BinXmlToken.XSD_QNAME:
                    {
                        int nameNum = ParseMB32(_tokDataPos);
                        if (nameNum < 0 || nameNum >= _symbolTables.qnameCount)
                            throw new XmlException(SR.XmlBin_InvalidQNameID, string.Empty);
                        QName qname = _symbolTables.qnametable[nameNum];
                        value = GetValueConverter(XmlTypeCode.QName).ChangeType(
                            new XmlQualifiedName(qname.localname, qname.namespaceUri),
                            returnType, namespaceResolver);
                        break;
                    }
 
                default:
                    throw CreateUnexpectedTokenException(_token);
            }
            return value;
        }
 
        private short GetInt16(int pos) => BinaryPrimitives.ReadInt16LittleEndian(_data.AsSpan(pos));
 
        private ushort GetUInt16(int pos) => BinaryPrimitives.ReadUInt16LittleEndian(_data.AsSpan(pos));
 
        private int GetInt32(int pos) => BinaryPrimitives.ReadInt32LittleEndian(_data.AsSpan(pos));
 
        private uint GetUInt32(int pos) => BinaryPrimitives.ReadUInt32LittleEndian(_data.AsSpan(pos));
 
        private long GetInt64(int pos) => BinaryPrimitives.ReadInt64LittleEndian(_data.AsSpan(pos));
 
        private ulong GetUInt64(int pos) => BinaryPrimitives.ReadUInt64LittleEndian(_data.AsSpan(pos));
 
        private float GetSingle(int offset) => BinaryPrimitives.ReadSingleLittleEndian(_data.AsSpan(offset));
 
        private double GetDouble(int offset) => BinaryPrimitives.ReadDoubleLittleEndian(_data.AsSpan(offset));
 
        private XmlException CreateUnexpectedTokenException(BinXmlToken token)
        {
            System.Diagnostics.Debug.WriteLine($"Unhandled token: {token}");
            return CreateXmlException(SR.XmlBinary_UnexpectedToken);
        }
 
        private XmlException CreateXmlException(string res)
        {
            _state = ScanState.Error;
            return new XmlException(res, (string[]?)null);
        }
 
        private XmlException CreateXmlException(string res, string arg1, string arg2)
        {
            _state = ScanState.Error;
            return new XmlException(res, new string[] { arg1, arg2 });
        }
 
        private NotSupportedException CreateNotSupportedException(string res)
        {
            _state = ScanState.Error;
            return new NotSupportedException(res);
        }
    }
}