//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

namespace System.Runtime.Serialization.Json
{
    using System.Globalization;
    using System.IO;
    using System.ServiceModel;
    using System.Text;
    using System.Runtime.Serialization;
    using System.Collections.Generic;
    using System.Xml;

    class XmlJsonReader : XmlBaseReader, IXmlJsonReaderInitializer
    {
        const int MaxTextChunk = 2048;

        static byte[] charType = new byte[256]
            {
                CharType.None, //   0 (.) 
                CharType.None, //   1 (.) 
                CharType.None, //   2 (.) 
                CharType.None, //   3 (.) 
                CharType.None, //   4 (.) 
                CharType.None, //   5 (.) 
                CharType.None, //   6 (.) 
                CharType.None, //   7 (.) 
                CharType.None, //   8 (.) 
                CharType.None, //   9 (.) 
                CharType.None, //   A (.) 
                CharType.None, //   B (.) 
                CharType.None, //   C (.) 
                CharType.None, //   D (.) 
                CharType.None, //   E (.) 
                CharType.None, //   F (.) 
                CharType.None, //  10 (.) 
                CharType.None, //  11 (.) 
                CharType.None, //  12 (.) 
                CharType.None, //  13 (.) 
                CharType.None, //  14 (.) 
                CharType.None, //  15 (.) 
                CharType.None, //  16 (.) 
                CharType.None, //  17 (.) 
                CharType.None, //  18 (.) 
                CharType.None, //  19 (.) 
                CharType.None, //  1A (.) 
                CharType.None, //  1B (.) 
                CharType.None, //  1C (.) 
                CharType.None, //  1D (.) 
                CharType.None, //  1E (.) 
                CharType.None, //  1F (.) 
                CharType.None, //  20 ( ) 
                CharType.None, //  21 (!) 
                CharType.None, //  22 (") 
                CharType.None, //  23 (#) 
                CharType.None, //  24 ($) 
                CharType.None, //  25 (%) 
                CharType.None, //  26 (&) 
                CharType.None, //  27 (') 
                CharType.None, //  28 (() 
                CharType.None, //  29 ()) 
                CharType.None, //  2A (*) 
                CharType.None, //  2B (+) 
                CharType.None, //  2C (,) 
                CharType.None | CharType.Name, //  2D (-) 
                CharType.None | CharType.Name, //  2E (.) 
                CharType.None, //  2F (/) 
                CharType.None | CharType.Name, //  30 (0) 
                CharType.None | CharType.Name, //  31 (1) 
                CharType.None | CharType.Name, //  32 (2) 
                CharType.None | CharType.Name, //  33 (3) 
                CharType.None | CharType.Name, //  34 (4) 
                CharType.None | CharType.Name, //  35 (5) 
                CharType.None | CharType.Name, //  36 (6) 
                CharType.None | CharType.Name, //  37 (7) 
                CharType.None | CharType.Name, //  38 (8) 
                CharType.None | CharType.Name, //  39 (9) 
                CharType.None, //  3A (:) 
                CharType.None, //  3B (;) 
                CharType.None, //  3C (<) 
                CharType.None, //  3D (=) 
                CharType.None, //  3E (>) 
                CharType.None, //  3F (?) 
                CharType.None, //  40 (@) 
                CharType.None | CharType.FirstName | CharType.Name, //  41 (A) 
                CharType.None | CharType.FirstName | CharType.Name, //  42 (B) 
                CharType.None | CharType.FirstName | CharType.Name, //  43 (C) 
                CharType.None | CharType.FirstName | CharType.Name, //  44 (D) 
                CharType.None | CharType.FirstName | CharType.Name, //  45 (E) 
                CharType.None | CharType.FirstName | CharType.Name, //  46 (F) 
                CharType.None | CharType.FirstName | CharType.Name, //  47 (G) 
                CharType.None | CharType.FirstName | CharType.Name, //  48 (H) 
                CharType.None | CharType.FirstName | CharType.Name, //  49 (I) 
                CharType.None | CharType.FirstName | CharType.Name, //  4A (J) 
                CharType.None | CharType.FirstName | CharType.Name, //  4B (K) 
                CharType.None | CharType.FirstName | CharType.Name, //  4C (L) 
                CharType.None | CharType.FirstName | CharType.Name, //  4D (M) 
                CharType.None | CharType.FirstName | CharType.Name, //  4E (N) 
                CharType.None | CharType.FirstName | CharType.Name, //  4F (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  50 (P) 
                CharType.None | CharType.FirstName | CharType.Name, //  51 (Q) 
                CharType.None | CharType.FirstName | CharType.Name, //  52 (R) 
                CharType.None | CharType.FirstName | CharType.Name, //  53 (S) 
                CharType.None | CharType.FirstName | CharType.Name, //  54 (T) 
                CharType.None | CharType.FirstName | CharType.Name, //  55 (U) 
                CharType.None | CharType.FirstName | CharType.Name, //  56 (V) 
                CharType.None | CharType.FirstName | CharType.Name, //  57 (W) 
                CharType.None | CharType.FirstName | CharType.Name, //  58 (X) 
                CharType.None | CharType.FirstName | CharType.Name, //  59 (Y) 
                CharType.None | CharType.FirstName | CharType.Name, //  5A (Z) 
                CharType.None, //  5B ([) 
                CharType.None, //  5C (\) 
                CharType.None, //  5D (]) 
                CharType.None, //  5E (^) 
                CharType.None | CharType.FirstName | CharType.Name, //  5F (_) 
                CharType.None, //  60 (`) 
                CharType.None | CharType.FirstName | CharType.Name, //  61 (a) 
                CharType.None | CharType.FirstName | CharType.Name, //  62 (b) 
                CharType.None | CharType.FirstName | CharType.Name, //  63 (c) 
                CharType.None | CharType.FirstName | CharType.Name, //  64 (d) 
                CharType.None | CharType.FirstName | CharType.Name, //  65 (e) 
                CharType.None | CharType.FirstName | CharType.Name, //  66 (f) 
                CharType.None | CharType.FirstName | CharType.Name, //  67 (g) 
                CharType.None | CharType.FirstName | CharType.Name, //  68 (h) 
                CharType.None | CharType.FirstName | CharType.Name, //  69 (i) 
                CharType.None | CharType.FirstName | CharType.Name, //  6A (j) 
                CharType.None | CharType.FirstName | CharType.Name, //  6B (k) 
                CharType.None | CharType.FirstName | CharType.Name, //  6C (l) 
                CharType.None | CharType.FirstName | CharType.Name, //  6D (m) 
                CharType.None | CharType.FirstName | CharType.Name, //  6E (n) 
                CharType.None | CharType.FirstName | CharType.Name, //  6F (o) 
                CharType.None | CharType.FirstName | CharType.Name, //  70 (p) 
                CharType.None | CharType.FirstName | CharType.Name, //  71 (q) 
                CharType.None | CharType.FirstName | CharType.Name, //  72 (r) 
                CharType.None | CharType.FirstName | CharType.Name, //  73 (s) 
                CharType.None | CharType.FirstName | CharType.Name, //  74 (t) 
                CharType.None | CharType.FirstName | CharType.Name, //  75 (u) 
                CharType.None | CharType.FirstName | CharType.Name, //  76 (v) 
                CharType.None | CharType.FirstName | CharType.Name, //  77 (w) 
                CharType.None | CharType.FirstName | CharType.Name, //  78 (x) 
                CharType.None | CharType.FirstName | CharType.Name, //  79 (y) 
                CharType.None | CharType.FirstName | CharType.Name, //  7A (z) 
                CharType.None, //  7B ({) 
                CharType.None, //  7C (|) 
                CharType.None, //  7D (}) 
                CharType.None, //  7E (~) 
                CharType.None, //  7F (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  80 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  81 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  82 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  83 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  84 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  85 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  86 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  87 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  88 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  89 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8A (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8B (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8C (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8D (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8E (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  8F (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  90 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  91 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  92 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  93 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  94 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  95 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  96 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  97 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  98 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  99 (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9A (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9B (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9C (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9D (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9E (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  9F (.) 
                CharType.None | CharType.FirstName | CharType.Name, //  A0 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A1 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A2 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A3 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A4 () 
                CharType.None | CharType.FirstName | CharType.Name, //  A5 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A6 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  A7 () 
                CharType.None | CharType.FirstName | CharType.Name, //  A8 (") 
                CharType.None | CharType.FirstName | CharType.Name, //  A9 (c) 
                CharType.None | CharType.FirstName | CharType.Name, //  AA (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  AB (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  AC (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  AD (-) 
                CharType.None | CharType.FirstName | CharType.Name, //  AE (r) 
                CharType.None | CharType.FirstName | CharType.Name, //  AF (_) 
                CharType.None | CharType.FirstName | CharType.Name, //  B0 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  B1 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  B2 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  B3 (3) 
                CharType.None | CharType.FirstName | CharType.Name, //  B4 (') 
                CharType.None | CharType.FirstName | CharType.Name, //  B5 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  B6 () 
                CharType.None | CharType.FirstName | CharType.Name, //  B7 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  B8 (,) 
                CharType.None | CharType.FirstName | CharType.Name, //  B9 (1) 
                CharType.None | CharType.FirstName | CharType.Name, //  BA (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  BB (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  BC (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  BD (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  BE (_) 
                CharType.None | CharType.FirstName | CharType.Name, //  BF (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  C0 (A) 
                CharType.None | CharType.FirstName | CharType.Name, //  C1 (A) 
                CharType.None | CharType.FirstName | CharType.Name, //  C2 (A) 
                CharType.None | CharType.FirstName | CharType.Name, //  C3 (A) 
                CharType.None | CharType.FirstName | CharType.Name, //  C4 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  C5 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  C6 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  C7 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  C8 (E) 
                CharType.None | CharType.FirstName | CharType.Name, //  C9 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  CA (E) 
                CharType.None | CharType.FirstName | CharType.Name, //  CB (E) 
                CharType.None | CharType.FirstName | CharType.Name, //  CC (I) 
                CharType.None | CharType.FirstName | CharType.Name, //  CD (I) 
                CharType.None | CharType.FirstName | CharType.Name, //  CE (I) 
                CharType.None | CharType.FirstName | CharType.Name, //  CF (I) 
                CharType.None | CharType.FirstName | CharType.Name, //  D0 (D) 
                CharType.None | CharType.FirstName | CharType.Name, //  D1 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  D2 (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  D3 (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  D4 (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  D5 (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  D6 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  D7 (x) 
                CharType.None | CharType.FirstName | CharType.Name, //  D8 (O) 
                CharType.None | CharType.FirstName | CharType.Name, //  D9 (U) 
                CharType.None | CharType.FirstName | CharType.Name, //  DA (U) 
                CharType.None | CharType.FirstName | CharType.Name, //  DB (U) 
                CharType.None | CharType.FirstName | CharType.Name, //  DC (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  DD (Y) 
                CharType.None | CharType.FirstName | CharType.Name, //  DE (_) 
                CharType.None | CharType.FirstName | CharType.Name, //  DF (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E0 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E1 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E2 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E3 (a) 
                CharType.None | CharType.FirstName | CharType.Name, //  E4 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E5 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E6 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E7 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E8 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  E9 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  EA (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  EB (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  EC (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  ED (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  EE (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  EF (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F0 (d) 
                CharType.None | CharType.FirstName | CharType.Name, //  F1 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F2 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F3 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F4 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F5 (o) 
                CharType.None | CharType.FirstName | CharType.Name, //  F6 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F7 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  F8 (o) 
                CharType.None | CharType.FirstName | CharType.Name, //  F9 (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  FA (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  FB (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  FC (�) 
                CharType.None | CharType.FirstName | CharType.Name, //  FD (y) 
                CharType.None | CharType.FirstName | CharType.Name, //  FE (_) 
                CharType.None | CharType.FirstName | CharType.Name, //  FF (�) 
            };
        bool buffered;
        byte[] charactersToSkipOnNextRead;
        JsonComplexTextMode complexTextMode = JsonComplexTextMode.None;
        bool expectingFirstElementInNonPrimitiveChild;
        int maxBytesPerRead;
        OnXmlDictionaryReaderClose onReaderClose;
        bool readServerTypeElement = false;
        int scopeDepth = 0;
        JsonNodeType[] scopes;

        enum JsonComplexTextMode
        {
            QuotedText,
            NumericalText,
            None
        };

        public override bool CanCanonicalize
        {
            get
            {
                return false;
            }
        }

        public override string Value
        {
            get
            {
                if (IsAttributeValue && !this.IsLocalName(JsonGlobals.typeString))
                {
                    return UnescapeJsonString(base.Value);
                }
                return base.Value;
            }
        }

        bool IsAttributeValue
        {
            get
            {
                return (this.Node.NodeType == XmlNodeType.Attribute || this.Node is XmlAttributeTextNode);
            }
        }

        bool IsReadingCollection
        {
            get
            {
                return ((scopeDepth > 0) && (scopes[scopeDepth] == JsonNodeType.Collection));
            }
        }

        bool IsReadingComplexText
        {
            get
            {
                return ((!this.Node.IsAtomicValue) &&
                    (this.Node.NodeType == XmlNodeType.Text));

            }
        }

        public override void Close()
        {
            base.Close();
            OnXmlDictionaryReaderClose onClose = this.onReaderClose;
            this.onReaderClose = null;
            ResetState();
            if (onClose != null)
            {
                try
                {
                    onClose(this);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }

                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
                }
            }
        }

        public override void EndCanonicalization()
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }

        public override string GetAttribute(int index)
        {
            return UnescapeJsonString(base.GetAttribute(index));
        }

        public override string GetAttribute(string localName, string namespaceUri)
        {
            if (localName != JsonGlobals.typeString)
            {
                return UnescapeJsonString(base.GetAttribute(localName, namespaceUri));
            }
            return base.GetAttribute(localName, namespaceUri);
        }
        public override string GetAttribute(string name)
        {
            if (name != JsonGlobals.typeString)
            {
                return UnescapeJsonString(base.GetAttribute(name));
            }
            return base.GetAttribute(name);
        }

        public override string GetAttribute(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
        {
            if (XmlDictionaryString.GetString(localName) != JsonGlobals.typeString)
            {
                return UnescapeJsonString(base.GetAttribute(localName, namespaceUri));
            }
            return base.GetAttribute(localName, namespaceUri);
        }

        public override bool Read()
        {
            if (this.Node.CanMoveToElement)
            {
                // If we're positioned on an attribute or attribute text on an empty element, we need to move back
                // to the element in order to get the correct setting of ExitScope
                MoveToElement();
            }

            if (this.Node.ReadState == ReadState.Closed)
            {
                return false;
            }
            if (this.Node.ExitScope)
            {
                ExitScope();
            }
            if (!buffered)
            {
                BufferReader.SetWindow(ElementNode.BufferOffset, this.maxBytesPerRead);
            }

            byte ch;

            // Skip whitespace before checking EOF
            // Complex text check necessary because whitespace could be part of really long
            //    quoted text that's read using multiple Read() calls.
            // This also ensures that we deal with the whitespace-only input case properly by not
            //    floating a root element at all.
            if (!IsReadingComplexText)
            {
                SkipWhitespaceInBufferReader();

                if (TryGetByte(out ch))
                {
                    if (charactersToSkipOnNextRead[0] == ch || charactersToSkipOnNextRead[1] == ch)
                    {
                        BufferReader.SkipByte();
                        charactersToSkipOnNextRead[0] = 0;
                        charactersToSkipOnNextRead[1] = 0;
                    }
                }

                SkipWhitespaceInBufferReader();

                if (TryGetByte(out ch))
                {
                    if (ch == JsonGlobals.EndCollectionByte && IsReadingCollection)
                    {
                        BufferReader.SkipByte();
                        SkipWhitespaceInBufferReader();
                        ExitJsonScope();
                    }
                }

                if (BufferReader.EndOfFile)
                {
                    if (scopeDepth > 0)
                    {
                        MoveToEndElement();
                        return true;
                    }
                    else
                    {
                        MoveToEndOfFile();
                        return false;
                    }
                }
            }

            ch = BufferReader.GetByte();

            if (scopeDepth == 0)
            {
                ReadNonExistentElementName(StringHandleConstStringType.Root);
            }
            else if (IsReadingComplexText)
            {
                switch (complexTextMode)
                {
                    case JsonComplexTextMode.NumericalText:
                        ReadNumericalText();
                        break;
                    case JsonComplexTextMode.QuotedText:
                        if (ch == (byte)'\\')
                        {
                            ReadEscapedCharacter(true); //  moveToText 
                        }
                        else
                        {
                            ReadQuotedText(true); //  moveToText 
                        }
                        break;
                    case JsonComplexTextMode.None:
                        XmlExceptionHelper.ThrowXmlException(this,
                            new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, (char)ch)));
                        break;
                }
            }
            else if (IsReadingCollection)
            {
                ReadNonExistentElementName(StringHandleConstStringType.Item);
            }
            else if (ch == JsonGlobals.EndCollectionByte)
            {
                BufferReader.SkipByte();
                MoveToEndElement();
                ExitJsonScope();
            }
            else if (ch == JsonGlobals.ObjectByte)
            {
                BufferReader.SkipByte();
                SkipWhitespaceInBufferReader();
                ch = (byte)BufferReader.GetByte();
                if (ch == JsonGlobals.EndObjectByte)
                {
                    BufferReader.SkipByte();
                    SkipWhitespaceInBufferReader();
                    if (TryGetByte(out ch))
                    {
                        if (ch == JsonGlobals.MemberSeparatorByte)
                        {
                            BufferReader.SkipByte();
                        }
                    }
                    else
                    {
                        charactersToSkipOnNextRead[0] = JsonGlobals.MemberSeparatorByte;
                    }
                    MoveToEndElement();
                }
                else
                {
                    EnterJsonScope(JsonNodeType.Object);
                    ParseStartElement();
                }
            }
            else if (ch == JsonGlobals.EndObjectByte)
            {
                BufferReader.SkipByte();
                if (expectingFirstElementInNonPrimitiveChild)
                {
                    SkipWhitespaceInBufferReader();
                    ch = BufferReader.GetByte();
                    if ((ch == JsonGlobals.MemberSeparatorByte) ||
                        (ch == JsonGlobals.EndObjectByte))
                    {
                        BufferReader.SkipByte();
                    }
                    else
                    {
                        XmlExceptionHelper.ThrowXmlException(this,
                            new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter,
                            (char)ch)));
                    }
                    expectingFirstElementInNonPrimitiveChild = false;
                }
                MoveToEndElement();
            }
            else if (ch == JsonGlobals.MemberSeparatorByte)
            {
                BufferReader.SkipByte();
                MoveToEndElement();
            }
            else if (ch == JsonGlobals.QuoteByte)
            {
                if (readServerTypeElement)
                {
                    readServerTypeElement = false;
                    EnterJsonScope(JsonNodeType.Object);
                    ParseStartElement();
                }
                else if (this.Node.NodeType == XmlNodeType.Element)
                {
                    if (expectingFirstElementInNonPrimitiveChild)
                    {
                        EnterJsonScope(JsonNodeType.Object);
                        ParseStartElement();
                    }
                    else
                    {
                        BufferReader.SkipByte();
                        ReadQuotedText(true); //  moveToText 
                    }
                }
                else if (this.Node.NodeType == XmlNodeType.EndElement)
                {
                    EnterJsonScope(JsonNodeType.Element);
                    ParseStartElement();
                }
                else
                {
                    XmlExceptionHelper.ThrowXmlException(this,
                        new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter,
                        JsonGlobals.QuoteChar)));
                }
            }
            else if (ch == (byte)'f')
            {
                int offset;
                byte[] buffer = BufferReader.GetBuffer(5, out offset);
                if (buffer[offset + 1] != (byte)'a' ||
                    buffer[offset + 2] != (byte)'l' ||
                    buffer[offset + 3] != (byte)'s' ||
                    buffer[offset + 4] != (byte)'e')
                {
                    XmlExceptionHelper.ThrowTokenExpected(this, "false", Encoding.UTF8.GetString(buffer, offset, 5));
                }
                BufferReader.Advance(5);

                if (TryGetByte(out ch))
                {
                    if (!IsWhitespace(ch) && ch != JsonGlobals.MemberSeparatorByte && ch != JsonGlobals.EndObjectChar && ch != JsonGlobals.EndCollectionByte)
                    {
                        XmlExceptionHelper.ThrowTokenExpected(this, "false", Encoding.UTF8.GetString(buffer, offset, 4) + (char)ch);
                    }
                }
                MoveToAtomicText().Value.SetValue(ValueHandleType.UTF8, offset, 5);
            }
            else if (ch == (byte)'t')
            {
                int offset;
                byte[] buffer = BufferReader.GetBuffer(4, out offset);
                if (buffer[offset + 1] != (byte)'r' ||
                    buffer[offset + 2] != (byte)'u' ||
                    buffer[offset + 3] != (byte)'e')
                {
                    XmlExceptionHelper.ThrowTokenExpected(this, "true", Encoding.UTF8.GetString(buffer, offset, 4));
                }
                BufferReader.Advance(4);

                if (TryGetByte(out ch))
                {
                    if (!IsWhitespace(ch) && ch != JsonGlobals.MemberSeparatorByte && ch != JsonGlobals.EndObjectChar && ch != JsonGlobals.EndCollectionByte)
                    {
                        XmlExceptionHelper.ThrowTokenExpected(this, "true", Encoding.UTF8.GetString(buffer, offset, 4) + (char)ch);
                    }
                }
                MoveToAtomicText().Value.SetValue(ValueHandleType.UTF8, offset, 4);
            }
            else if (ch == (byte)'n')
            {
                int offset;
                byte[] buffer = BufferReader.GetBuffer(4, out offset);
                if (buffer[offset + 1] != (byte)'u' ||
                    buffer[offset + 2] != (byte)'l' ||
                    buffer[offset + 3] != (byte)'l')
                {
                    XmlExceptionHelper.ThrowTokenExpected(this, "null", Encoding.UTF8.GetString(buffer, offset, 4));
                }
                BufferReader.Advance(4);
                SkipWhitespaceInBufferReader();

                if (TryGetByte(out ch))
                {
                    if (ch == JsonGlobals.MemberSeparatorByte || ch == JsonGlobals.EndObjectChar)
                    {
                        BufferReader.SkipByte();
                    }
                    else if (ch != JsonGlobals.EndCollectionByte)
                    {
                        XmlExceptionHelper.ThrowTokenExpected(this, "null", Encoding.UTF8.GetString(buffer, offset, 4) + (char)ch);
                    }
                }
                else
                {
                    charactersToSkipOnNextRead[0] = JsonGlobals.MemberSeparatorByte;
                    charactersToSkipOnNextRead[1] = JsonGlobals.EndObjectByte;
                }
                MoveToEndElement();
            }
            else if ((ch == (byte)'-') ||
                (((byte)'0' <= ch) && (ch <= (byte)'9')) ||
                (ch == (byte)'I') ||
                (ch == (byte)'N'))
            {
                ReadNumericalText();
            }
            else
            {
                XmlExceptionHelper.ThrowXmlException(this,
                    new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, (char)ch)));
            }

            return true;
        }

        public override decimal ReadContentAsDecimal()
        {
            string value = ReadContentAsString();
            try
            {
                return decimal.Parse(value, NumberStyles.Float, NumberFormatInfo.InvariantInfo);
            }
            catch (ArgumentException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
            }
            catch (FormatException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
            }
            catch (OverflowException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
            }
        }

        public override int ReadContentAsInt()
        {
            return ParseInt(ReadContentAsString(), NumberStyles.Float);
        }

        public override long ReadContentAsLong()
        {
            string value = ReadContentAsString();
            try
            {
                return long.Parse(value, NumberStyles.Float, NumberFormatInfo.InvariantInfo);
            }
            catch (ArgumentException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
            }
            catch (FormatException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
            }
            catch (OverflowException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
            }
        }

        public override int ReadValueAsBase64(byte[] buffer, int offset, int count)
        {
            if (IsAttributeValue)
            {
                if (buffer == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer"));
                }
                if (offset < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.ValueMustBeNonNegative)));
                }
                if (offset > buffer.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.OffsetExceedsBufferSize, buffer.Length)));
                }
                if (count < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.ValueMustBeNonNegative)));
                }
                if (count > buffer.Length - offset)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.SizeExceedsRemainingBufferSpace, buffer.Length - offset)));
                }

                return 0;
            }

            return base.ReadValueAsBase64(buffer, offset, count);
        }

        public override int ReadValueChunk(char[] chars, int offset, int count)
        {
            if (IsAttributeValue)
            {
                if (chars == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("chars"));
                }
                if (offset < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.ValueMustBeNonNegative)));
                }
                if (offset > chars.Length)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.OffsetExceedsBufferSize, chars.Length)));
                }
                if (count < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.ValueMustBeNonNegative)));
                }
                if (count > chars.Length - offset)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.SizeExceedsRemainingBufferSpace, chars.Length - offset)));
                }
                int actual;

                string value = UnescapeJsonString(this.Node.ValueAsString);
                actual = Math.Min(count, value.Length);
                if (actual > 0)
                {
                    value.CopyTo(0, chars, offset, actual);
                    if (this.Node.QNameType == QNameType.Xmlns)
                    {
                        this.Node.Namespace.Uri.SetValue(0, 0);
                    }
                    else
                    {
                        this.Node.Value.SetValue(ValueHandleType.UTF8, 0, 0);
                    }
                }
                return actual;
            }

            return base.ReadValueChunk(chars, offset, count);
        }

        public void SetInput(byte[] buffer, int offset, int count, Encoding encoding, XmlDictionaryReaderQuotas quotas,
            OnXmlDictionaryReaderClose onClose)
        {
            if (buffer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
            }
            if (offset < 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
            }
            if (offset > buffer.Length)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("offset",
                    SR.GetString(SR.JsonOffsetExceedsBufferSize, buffer.Length)));
            }
            if (count < 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
            }
            if (count > buffer.Length - offset)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("count",
                    SR.GetString(SR.JsonSizeExceedsRemainingBufferSpace,
                    buffer.Length - offset)));
            }
            MoveToInitial(quotas, onClose);

            ArraySegment<byte> seg = JsonEncodingStreamWrapper.ProcessBuffer(buffer, offset, count, encoding);
            BufferReader.SetBuffer(seg.Array, seg.Offset, seg.Count, null, null);
            this.buffered = true;
            ResetState();
        }

        public void SetInput(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas,
            OnXmlDictionaryReaderClose onClose)
        {
            if (stream == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
            }
            MoveToInitial(quotas, onClose);

            stream = new JsonEncodingStreamWrapper(stream, encoding, true);

            BufferReader.SetBuffer(stream, null, null);
            this.buffered = false;
            ResetState();
        }

        public override void StartCanonicalization(Stream stream, bool includeComments, string[] inclusivePrefixes)
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
        }

        internal static void CheckArray(Array array, int offset, int count)
        {
            if (array == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("array"));
            }
            if (offset < 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeNonNegative)));
            }
            if (offset > array.Length)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.OffsetExceedsBufferSize, array.Length)));
            }
            if (count < 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeNonNegative)));
            }
            if (count > array.Length - offset)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.SizeExceedsRemainingBufferSpace, array.Length - offset)));
            }
        }

        protected override XmlSigningNodeWriter CreateSigningNodeWriter()
        {
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.JsonMethodNotSupported, "CreateSigningNodeWriter")));
        }

        static int BreakText(byte[] buffer, int offset, int length)
        {
            // See if we might be breaking a utf8 sequence
            if (length > 0 && (buffer[offset + length - 1] & 0x80) == 0x80)
            {
                // Find the lead char of the utf8 sequence (0x11xxxxxx)
                int originalLength = length;
                do
                {
                    length--;
                } while (length > 0 && (buffer[offset + length] & 0xC0) != 0xC0);
                // Couldn't find the lead char
                if (length == 0)
                {
                    return originalLength; // Invalid utf8 sequence - can't break
                }
                // Count how many bytes follow the lead char
                byte b = (byte)(buffer[offset + length] << 2);
                int byteCount = 2;
                while ((b & 0x80) == 0x80)
                {
                    b = (byte)(b << 1);
                    byteCount++;
                    // There shouldn't be more than 3 bytes following the lead char
                    if (byteCount > 4)
                    {
                        return originalLength; // Invalid utf8 sequence - can't break
                    }
                }
                if (length + byteCount == originalLength)
                {
                    return originalLength; // sequence fits exactly
                }
                if (length == 0)
                {
                    return originalLength; // Quota too small to read a char
                }
            }
            return length;
        }

        static int ComputeNumericalTextLength(byte[] buffer, int offset, int offsetMax)
        {
            int beginOffset = offset;
            while (offset < offsetMax)
            {
                byte ch = buffer[offset];
                if (ch == JsonGlobals.MemberSeparatorByte || ch == JsonGlobals.EndObjectByte || ch == JsonGlobals.EndCollectionByte
                    || IsWhitespace(ch))
                {
                    break;
                }
                offset++;
            }
            return offset - beginOffset;
        }

        static int ComputeQuotedTextLengthUntilEndQuote(byte[] buffer, int offset, int offsetMax, out bool escaped)
        {
            // Assumes that for quoted text "someText", the first " has been consumed.
            // For original text "someText", buffer passed in is someText".
            // This method returns return 8 for someText" (s, o, m, e, T, e, x, t).
            int beginOffset = offset;
            escaped = false;

            while (offset < offsetMax)
            {
                byte ch = buffer[offset];
                if (ch < 0x20)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidCharacterEncountered, (char)ch)));
                }
                else if (ch == (byte)'\\' || ch == 0xEF)
                {
                    escaped = true;
                    break;
                }
                else if (ch == JsonGlobals.QuoteByte)
                {
                    break;
                }

                offset++;
            }

            return offset - beginOffset;
        }


        // From JSON spec:
        // ws = *(
        //    %x20 /              ; Space
        //    %x09 /              ; Horizontal tab
        //    %x0A /              ; Line feed or New line
        //    %x0D                ; Carriage return
        // )            
        static bool IsWhitespace(byte ch)
        {
            return ((ch == 0x20) || (ch == 0x09) || (ch == 0x0A) || (ch == 0x0D));
        }

        static char ParseChar(string value, NumberStyles style)
        {
            int intValue = ParseInt(value, style);
            try
            {
                return Convert.ToChar(intValue);
            }
            catch (OverflowException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "char", exception));
            }
        }

        static int ParseInt(string value, NumberStyles style)
        {
            try
            {
                return int.Parse(value, style, NumberFormatInfo.InvariantInfo);
            }
            catch (ArgumentException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
            }
            catch (FormatException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
            }
            catch (OverflowException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
            }
        }

        void BufferElement()
        {
            int elementOffset = BufferReader.Offset;
            const int byteCount = 128;
            bool done = false;
            byte quoteChar = 0;
            while (!done)
            {
                int offset;
                int offsetMax;
                byte[] buffer = BufferReader.GetBuffer(byteCount, out offset, out offsetMax);
                if (offset + byteCount != offsetMax)
                {
                    break;
                }
                for (int i = offset; i < offsetMax && !done; i++)
                {
                    byte b = buffer[i];
                    if (b == '\\')
                    {
                        i++;
                        if (i >= offsetMax)
                        {
                            break;
                        }
                    }
                    else if (quoteChar == 0)
                    {
                        if (b == (byte)'\'' || b == JsonGlobals.QuoteByte)
                        {
                            quoteChar = b;
                        }
                        if (b == JsonGlobals.NameValueSeparatorByte)
                        {
                            done = true;
                        }
                    }
                    else
                    {
                        if (b == quoteChar)
                        {
                            quoteChar = 0;
                        }
                    }
                }
                BufferReader.Advance(byteCount);
            }
            BufferReader.Offset = elementOffset;
        }

        void EnterJsonScope(JsonNodeType currentNodeType)
        {
            scopeDepth++;
            if (scopes == null)
            {
                scopes = new JsonNodeType[4];
            }
            else if (scopes.Length == scopeDepth)
            {
                JsonNodeType[] newScopes = new JsonNodeType[scopeDepth * 2];
                Array.Copy(scopes, newScopes, scopeDepth);
                scopes = newScopes;
            }
            scopes[scopeDepth] = currentNodeType;
        }

        JsonNodeType ExitJsonScope()
        {
            JsonNodeType nodeTypeToReturn = scopes[scopeDepth];
            scopes[scopeDepth] = JsonNodeType.None;
            scopeDepth--;
            return nodeTypeToReturn;
        }

        new void MoveToEndElement()
        {
            ExitJsonScope();
            base.MoveToEndElement();
        }

        void MoveToInitial(XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
        {
            MoveToInitial(quotas);
            this.maxBytesPerRead = quotas.MaxBytesPerRead;
            this.onReaderClose = onClose;
        }

        void ParseAndSetLocalName()
        {
            XmlElementNode elementNode = EnterScope();
            elementNode.NameOffset = BufferReader.Offset;

            do
            {
                if (BufferReader.GetByte() == '\\')
                {
                    ReadEscapedCharacter(false); //  moveToText 
                }
                else
                {
                    ReadQuotedText(false); //  moveToText 
                }
            } while (complexTextMode == JsonComplexTextMode.QuotedText);

            int actualOffset = BufferReader.Offset - 1; //  -1 to ignore " at end of local name 
            elementNode.LocalName.SetValue(elementNode.NameOffset, actualOffset - elementNode.NameOffset);
            elementNode.NameLength = actualOffset - elementNode.NameOffset;
            elementNode.Namespace.Uri.SetValue(elementNode.NameOffset, 0);
            elementNode.Prefix.SetValue(PrefixHandleType.Empty);
            elementNode.IsEmptyElement = false;
            elementNode.ExitScope = false;
            elementNode.BufferOffset = actualOffset;

            int currentCharacter = (int)BufferReader.GetByte(elementNode.NameOffset);
            if ((charType[currentCharacter] & CharType.FirstName) == 0)
            {
                SetJsonNameWithMapping(elementNode);
            }
            else
            {
                for (int i = 0, offset = elementNode.NameOffset; i < elementNode.NameLength; i++, offset++)
                {
                    currentCharacter = (int)BufferReader.GetByte(offset);
                    if ((charType[currentCharacter] & CharType.Name) == 0 || currentCharacter >= 0x80)
                    {
                        SetJsonNameWithMapping(elementNode);
                        break;
                    }
                }
            }
        }

        void ParseStartElement()
        {
            if (!buffered)
            {
                BufferElement();
            }

            expectingFirstElementInNonPrimitiveChild = false;

            byte ch = BufferReader.GetByte();
            if (ch == JsonGlobals.QuoteByte)
            {
                BufferReader.SkipByte();

                ParseAndSetLocalName();

                SkipWhitespaceInBufferReader();
                SkipExpectedByteInBufferReader(JsonGlobals.NameValueSeparatorByte);
                SkipWhitespaceInBufferReader();


                if (BufferReader.GetByte() == JsonGlobals.ObjectByte)
                {
                    BufferReader.SkipByte();
                    expectingFirstElementInNonPrimitiveChild = true;
                }
                ReadAttributes();
            }
            else
            {
                // " and } are the only two valid characters that may follow a {
                XmlExceptionHelper.ThrowTokenExpected(this, "\"", (char)ch);
            }
        }

        void ReadAttributes()
        {
            XmlAttributeNode attribute = AddAttribute();
            attribute.LocalName.SetConstantValue(StringHandleConstStringType.Type);
            attribute.Namespace.Uri.SetValue(0, 0);
            attribute.Prefix.SetValue(PrefixHandleType.Empty);

            SkipWhitespaceInBufferReader();
            byte nextByte = BufferReader.GetByte();
            switch (nextByte)
            {
                case JsonGlobals.QuoteByte:
                    if (!expectingFirstElementInNonPrimitiveChild)
                    {
                        attribute.Value.SetConstantValue(ValueHandleConstStringType.String);
                    }
                    else
                    {
                        attribute.Value.SetConstantValue(ValueHandleConstStringType.Object);
                        ReadServerTypeAttribute(true);
                    }
                    break;
                case (byte)'n':
                    attribute.Value.SetConstantValue(ValueHandleConstStringType.Null);
                    break;
                case (byte)'t':
                case (byte)'f':
                    attribute.Value.SetConstantValue(ValueHandleConstStringType.Boolean);
                    break;
                case JsonGlobals.ObjectByte:
                    attribute.Value.SetConstantValue(ValueHandleConstStringType.Object);
                    ReadServerTypeAttribute(false);
                    break;
                case JsonGlobals.EndObjectByte:
                    if (expectingFirstElementInNonPrimitiveChild)
                    {
                        attribute.Value.SetConstantValue(ValueHandleConstStringType.Object);
                    }
                    else
                    {
                        XmlExceptionHelper.ThrowXmlException(this,
                            new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, (char)nextByte)));
                    }
                    break;
                case JsonGlobals.CollectionByte:
                    attribute.Value.SetConstantValue(ValueHandleConstStringType.Array);
                    BufferReader.SkipByte();
                    EnterJsonScope(JsonNodeType.Collection);
                    break;
                default:
                    if (nextByte == '-' ||
                        (nextByte <= '9' && nextByte >= '0') ||
                        nextByte == 'N' ||
                        nextByte == 'I')
                    {
                        attribute.Value.SetConstantValue(ValueHandleConstStringType.Number);
                    }
                    else
                    {
                        XmlExceptionHelper.ThrowXmlException(this,
                            new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, (char)nextByte)));
                    }
                    break;
            }
        }

        void ReadEscapedCharacter(bool moveToText)
        {
            BufferReader.SkipByte();
            char ch = (char)BufferReader.GetByte();
            if (ch == 'u')
            {
                BufferReader.SkipByte();
                int offset;
                byte[] buffer = BufferReader.GetBuffer(5, out offset);
                string bufferAsString = Encoding.UTF8.GetString(buffer, offset, 4);
                BufferReader.Advance(4);
                int charValue = ParseChar(bufferAsString, NumberStyles.HexNumber);
                if (Char.IsHighSurrogate((char)charValue))
                {
                    byte nextByte = BufferReader.GetByte();
                    if (nextByte == (byte)'\\')
                    {
                        BufferReader.SkipByte();
                        SkipExpectedByteInBufferReader((byte)'u');
                        buffer = BufferReader.GetBuffer(5, out offset);
                        bufferAsString = Encoding.UTF8.GetString(buffer, offset, 4);
                        BufferReader.Advance(4);
                        char lowChar = ParseChar(bufferAsString, NumberStyles.HexNumber);
                        if (!Char.IsLowSurrogate(lowChar))
                        {
                            XmlExceptionHelper.ThrowXmlException(this,
                                new XmlException(System.Runtime.Serialization.SR.GetString(System.Runtime.Serialization.SR.XmlInvalidLowSurrogate, bufferAsString)));
                        }
                        charValue = new SurrogateChar(lowChar, (char)charValue).Char;
                    }
                }

                if (buffer[offset + 4] == JsonGlobals.QuoteByte)
                {
                    BufferReader.SkipByte();
                    if (moveToText)
                    {
                        MoveToAtomicText().Value.SetCharValue(charValue);
                    }
                    complexTextMode = JsonComplexTextMode.None;
                }
                else
                {
                    if (moveToText)
                    {
                        MoveToComplexText().Value.SetCharValue(charValue);
                    }
                    complexTextMode = JsonComplexTextMode.QuotedText;
                }
            }
            else
            {
                switch (ch)
                {
                    case 'b':
                        ch = '\b';
                        break;
                    case 'f':
                        ch = '\f';
                        break;
                    case 'n':
                        ch = '\n';
                        break;
                    case 'r':
                        ch = '\r';
                        break;
                    case 't':
                        ch = '\t';
                        break;
                    case '\"':
                    case '\\':
                    case '/':
                        // Do nothing. These are the actual unescaped values.
                        break;
                    default:
                        XmlExceptionHelper.ThrowXmlException(this,
                            new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, (char)ch)));
                        break;
                }
                BufferReader.SkipByte();
                if (BufferReader.GetByte() == JsonGlobals.QuoteByte)
                {
                    BufferReader.SkipByte();
                    if (moveToText)
                    {
                        MoveToAtomicText().Value.SetCharValue(ch);
                    }
                    complexTextMode = JsonComplexTextMode.None;
                }
                else
                {
                    if (moveToText)
                    {
                        MoveToComplexText().Value.SetCharValue(ch);
                    }
                    complexTextMode = JsonComplexTextMode.QuotedText;
                }
            }
        }

        void ReadNonExistentElementName(StringHandleConstStringType elementName)
        {
            EnterJsonScope(JsonNodeType.Object);
            XmlElementNode elementNode = EnterScope();
            elementNode.LocalName.SetConstantValue(elementName);
            elementNode.Namespace.Uri.SetValue(elementNode.NameOffset, 0);
            elementNode.Prefix.SetValue(PrefixHandleType.Empty);
            elementNode.BufferOffset = BufferReader.Offset;
            elementNode.IsEmptyElement = false;
            elementNode.ExitScope = false;
            ReadAttributes();
        }

        int ReadNonFFFE()
        {
            int off;
            byte[] buff = BufferReader.GetBuffer(3, out off);
            if (buff[off + 1] == 0xBF && (buff[off + 2] == 0xBE || buff[off + 2] == 0xBF))
            {
                XmlExceptionHelper.ThrowXmlException(this, new XmlException(SR.GetString(SR.JsonInvalidFFFE)));
            }
            return 3;
        }

        void ReadNumericalText()
        {
            byte[] buffer;
            int offset;
            int offsetMax;
            int length;

            if (buffered)
            {
                buffer = BufferReader.GetBuffer(out offset, out offsetMax);
                length = ComputeNumericalTextLength(buffer, offset, offsetMax);
            }
            else
            {
                buffer = BufferReader.GetBuffer(MaxTextChunk, out offset, out offsetMax);
                length = ComputeNumericalTextLength(buffer, offset, offsetMax);
                length = BreakText(buffer, offset, length);
            }
            BufferReader.Advance(length);

            if (offset <= offsetMax - length)
            {
                MoveToAtomicText().Value.SetValue(ValueHandleType.UTF8, offset, length);
                complexTextMode = JsonComplexTextMode.None;
            }
            else
            {
                MoveToComplexText().Value.SetValue(ValueHandleType.UTF8, offset, length);
                complexTextMode = JsonComplexTextMode.NumericalText;
            }
        }

        void ReadQuotedText(bool moveToText)
        {
            byte[] buffer;
            int offset;
            int offsetMax;
            int length;
            bool escaped;
            bool endReached;

            if (buffered)
            {
                buffer = BufferReader.GetBuffer(out offset, out offsetMax);
                length = ComputeQuotedTextLengthUntilEndQuote(buffer, offset, offsetMax, out escaped);
                endReached = offset < offsetMax - length;
            }
            else
            {
                buffer = BufferReader.GetBuffer(MaxTextChunk, out offset, out offsetMax);
                length = ComputeQuotedTextLengthUntilEndQuote(buffer, offset, offsetMax, out escaped);
                endReached = offset < offsetMax - length;
                length = BreakText(buffer, offset, length);
            }

            if (escaped && BufferReader.GetByte() == 0xEF)
            {
                offset = BufferReader.Offset;
                length = ReadNonFFFE();
            }

            BufferReader.Advance(length);

            if (!escaped && endReached)
            {
                if (moveToText)
                {
                    MoveToAtomicText().Value.SetValue(ValueHandleType.UTF8, offset, length);
                }
                SkipExpectedByteInBufferReader(JsonGlobals.QuoteByte);
                complexTextMode = JsonComplexTextMode.None;
            }
            else
            {
                if ((length == 0) && escaped)
                {
                    ReadEscapedCharacter(moveToText);
                }
                else
                {
                    if (moveToText)
                    {
                        MoveToComplexText().Value.SetValue(ValueHandleType.UTF8, offset, length);
                    }
                    complexTextMode = JsonComplexTextMode.QuotedText;
                }
            }
        }

        void ReadServerTypeAttribute(bool consumedObjectChar)
        {
            if (!consumedObjectChar)
            {
                SkipExpectedByteInBufferReader(JsonGlobals.ObjectByte);
                SkipWhitespaceInBufferReader();

                // we only allow " or } after {
                byte ch = BufferReader.GetByte();
                if (ch != JsonGlobals.QuoteByte && ch != JsonGlobals.EndObjectByte)
                {
                    XmlExceptionHelper.ThrowTokenExpected(this, "\"", (char)ch);
                }
            }
            else
            {
                SkipWhitespaceInBufferReader();
            }

            int offset;
            int offsetMax;
            byte[] buffer = BufferReader.GetBuffer(8, out offset, out offsetMax);
            if (offset + 8 <= offsetMax)
            {
                if (buffer[offset + 0] == (byte)'\"' &&
                    buffer[offset + 1] == (byte)'_' &&
                    buffer[offset + 2] == (byte)'_' &&
                    buffer[offset + 3] == (byte)'t' &&
                    buffer[offset + 4] == (byte)'y' &&
                    buffer[offset + 5] == (byte)'p' &&
                    buffer[offset + 6] == (byte)'e' &&
                    buffer[offset + 7] == (byte)'\"')
                {
                    XmlAttributeNode attribute = AddAttribute();

                    attribute.LocalName.SetValue(offset + 1, 6);
                    attribute.Namespace.Uri.SetValue(0, 0);
                    attribute.Prefix.SetValue(PrefixHandleType.Empty);
                    BufferReader.Advance(8);

                    if (!buffered)
                    {
                        BufferElement();
                    }

                    SkipWhitespaceInBufferReader();
                    SkipExpectedByteInBufferReader(JsonGlobals.NameValueSeparatorByte);
                    SkipWhitespaceInBufferReader();
                    SkipExpectedByteInBufferReader(JsonGlobals.QuoteByte);

                    buffer = BufferReader.GetBuffer(out offset, out offsetMax);

                    do
                    {
                        if (BufferReader.GetByte() == '\\')
                        {
                            ReadEscapedCharacter(false); //  moveToText 
                        }
                        else
                        {
                            ReadQuotedText(false); //  moveToText 
                        }
                    } while (complexTextMode == JsonComplexTextMode.QuotedText);

                    attribute.Value.SetValue(ValueHandleType.UTF8, offset, BufferReader.Offset - 1 - offset);

                    SkipWhitespaceInBufferReader();

                    if (BufferReader.GetByte() == JsonGlobals.MemberSeparatorByte)
                    {
                        BufferReader.SkipByte();
                        readServerTypeElement = true;
                    }
                }
            }
            if (BufferReader.GetByte() == JsonGlobals.EndObjectByte)
            {
                BufferReader.SkipByte();
                readServerTypeElement = false;
                expectingFirstElementInNonPrimitiveChild = false;
            }
            else
            {
                readServerTypeElement = true;
            }
        }

        void ResetState()
        {
            complexTextMode = JsonComplexTextMode.None;
            expectingFirstElementInNonPrimitiveChild = false;
            charactersToSkipOnNextRead = new byte[2];
            scopeDepth = 0;
            if ((scopes != null) && (scopes.Length > JsonGlobals.maxScopeSize))
            {
                scopes = null;
            }
        }

        void SetJsonNameWithMapping(XmlElementNode elementNode)
        {
            Namespace ns = AddNamespace();
            ns.Prefix.SetValue(PrefixHandleType.A);
            ns.Uri.SetConstantValue(StringHandleConstStringType.Item);
            AddXmlnsAttribute(ns);

            XmlAttributeNode attribute = AddAttribute();
            attribute.LocalName.SetConstantValue(StringHandleConstStringType.Item);
            attribute.Namespace.Uri.SetValue(0, 0);
            attribute.Prefix.SetValue(PrefixHandleType.Empty);
            attribute.Value.SetValue(ValueHandleType.UTF8, elementNode.NameOffset, elementNode.NameLength);

            elementNode.NameLength = 0;
            elementNode.Prefix.SetValue(PrefixHandleType.A);
            elementNode.LocalName.SetConstantValue(StringHandleConstStringType.Item);
            elementNode.Namespace = ns;
        }

        void SkipExpectedByteInBufferReader(byte characterToSkip)
        {
            if (BufferReader.GetByte() != characterToSkip)
            {
                XmlExceptionHelper.ThrowTokenExpected(this, ((char)characterToSkip).ToString(), (char)BufferReader.GetByte());
            }
            BufferReader.SkipByte();
        }

        void SkipWhitespaceInBufferReader()
        {
            byte ch;
            while (TryGetByte(out ch) && IsWhitespace(ch))
            {
                BufferReader.SkipByte();
            }
        }

        bool TryGetByte(out byte ch)
        {
            int offset, offsetMax;
            byte[] buffer = BufferReader.GetBuffer(1, out offset, out offsetMax);

            if (offset < offsetMax)
            {
                ch = buffer[offset];
                return true;
            }
            else
            {
                ch = (byte)'\0';
                return false;
            }
        }

        string UnescapeJsonString(string val)
        {
            if (val == null)
            {
                return null;
            }

            StringBuilder sb = null;
            int startIndex = 0, count = 0;
            for (int i = 0; i < val.Length; i++)
            {
                if (val[i] == '\\')
                {
                    i++;
                    if (sb == null)
                    {
                        sb = new StringBuilder();
                    }
                    sb.Append(val, startIndex, count);
                    Fx.Assert(i < val.Length, "Found that an '\' was the last character in a string. ReadServerTypeAttriute validates that the escape sequence is valid when it calls ReadQuotedText and ReadEscapedCharacter");
                    if (i >= val.Length)
                    {
                        XmlExceptionHelper.ThrowXmlException(this, new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, val[i])));
                    }
                    switch (val[i])
                    {
                        case '"':
                        case '\'':
                        case '/':
                        case '\\':
                            sb.Append(val[i]);
                            break;
                        case 'b':
                            sb.Append('\b');
                            break;
                        case 'f':
                            sb.Append('\f');
                            break;
                        case 'n':
                            sb.Append('\n');
                            break;
                        case 'r':
                            sb.Append('\r');
                            break;
                        case 't':
                            sb.Append('\t');
                            break;
                        case 'u':
                            if ((i + 3) >= val.Length)
                            {
                                XmlExceptionHelper.ThrowXmlException(this,
                                    new XmlException(SR.GetString(SR.JsonEncounteredUnexpectedCharacter, val[i])));
                            }
                            sb.Append(ParseChar(val.Substring(i + 1, 4), NumberStyles.HexNumber));
                            i += 4;
                            break;
                    }
                    startIndex = i + 1;
                    count = 0;
                }
                else
                {
                    count++;
                }
            }
            if (sb == null)
            {
                return val;
            }
            if (count > 0)
            {
                sb.Append(val, startIndex, count);
            }

            return sb.ToString();
        }

        static class CharType
        {
            public const byte FirstName = 0x01;
            public const byte Name = 0x02;
            public const byte None = 0x00;
        }
    }
}
