94b2861243
Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
1720 lines
72 KiB
C#
1720 lines
72 KiB
C#
//------------------------------------------------------------
|
||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
//------------------------------------------------------------
|
||
|
||
namespace System.Runtime.Serialization.Json
|
||
{
|
||
using System.Globalization;
|
||
using System.IO;
|
||
#if !MONO
|
||
using System.ServiceModel;
|
||
#endif
|
||
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 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // A1 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // A2 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // A3 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // A4 ()
|
||
CharType.None | CharType.FirstName | CharType.Name, // A5 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // A6 (<28>)
|
||
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 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // AB (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // AC (<28>)
|
||
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 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B1 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B2 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B3 (3)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B4 (')
|
||
CharType.None | CharType.FirstName | CharType.Name, // B5 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B6 ()
|
||
CharType.None | CharType.FirstName | CharType.Name, // B7 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B8 (,)
|
||
CharType.None | CharType.FirstName | CharType.Name, // B9 (1)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BA (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BB (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BC (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BD (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BE (_)
|
||
CharType.None | CharType.FirstName | CharType.Name, // BF (<28>)
|
||
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 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // C5 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // C6 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // C7 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // C8 (E)
|
||
CharType.None | CharType.FirstName | CharType.Name, // C9 (<28>)
|
||
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 (<28>)
|
||
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 (<28>)
|
||
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 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // DD (Y)
|
||
CharType.None | CharType.FirstName | CharType.Name, // DE (_)
|
||
CharType.None | CharType.FirstName | CharType.Name, // DF (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E0 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E1 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E2 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E3 (a)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E4 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E5 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E6 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E7 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E8 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // E9 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // EA (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // EB (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // EC (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // ED (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // EE (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // EF (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F0 (d)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F1 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F2 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F3 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F4 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F5 (o)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F6 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F7 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F8 (o)
|
||
CharType.None | CharType.FirstName | CharType.Name, // F9 (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FA (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FB (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FC (<28>)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FD (y)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FE (_)
|
||
CharType.None | CharType.FirstName | CharType.Name, // FF (<28>)
|
||
};
|
||
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;
|
||
}
|
||
}
|
||
}
|