e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1266 lines
44 KiB
C#
1266 lines
44 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
namespace System.Xml
|
|
{
|
|
using System.IO;
|
|
using System.Runtime;
|
|
using System.Security;
|
|
using System.Text;
|
|
|
|
public interface IXmlTextWriterInitializer
|
|
{
|
|
void SetOutput(Stream stream, Encoding encoding, bool ownsStream);
|
|
}
|
|
|
|
class XmlUTF8TextWriter : XmlBaseWriter, IXmlTextWriterInitializer
|
|
{
|
|
XmlUTF8NodeWriter writer;
|
|
|
|
//Supports FastAsync APIs
|
|
internal override bool FastAsync
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void SetOutput(Stream stream, Encoding encoding, bool ownsStream)
|
|
{
|
|
if (stream == null)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
|
|
if (encoding == null)
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
|
|
if (encoding.WebName != Encoding.UTF8.WebName)
|
|
{
|
|
stream = new EncodingStreamWrapper(stream, encoding, true);
|
|
}
|
|
|
|
if (writer == null)
|
|
{
|
|
writer = new XmlUTF8NodeWriter();
|
|
}
|
|
writer.SetOutput(stream, ownsStream, encoding);
|
|
SetOutput(writer);
|
|
}
|
|
|
|
public override bool CanFragment
|
|
{
|
|
get
|
|
{
|
|
// Fragmenting only works for utf8
|
|
return writer.Encoding == null;
|
|
}
|
|
}
|
|
|
|
protected override XmlSigningNodeWriter CreateSigningNodeWriter()
|
|
{
|
|
return new XmlSigningNodeWriter(true);
|
|
}
|
|
}
|
|
|
|
class XmlUTF8NodeWriter : XmlStreamNodeWriter
|
|
{
|
|
byte[] entityChars;
|
|
bool[] isEscapedAttributeChar;
|
|
bool[] isEscapedElementChar;
|
|
bool inAttribute;
|
|
const int bufferLength = 512;
|
|
const int maxEntityLength = 32;
|
|
const int maxBytesPerChar = 3;
|
|
Encoding encoding;
|
|
char[] chars;
|
|
InternalWriteBase64TextAsyncWriter internalWriteBase64TextAsyncWriter;
|
|
|
|
static readonly byte[] startDecl =
|
|
{
|
|
(byte)'<', (byte)'?', (byte)'x', (byte)'m', (byte)'l', (byte)' ',
|
|
(byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n', (byte)'=', (byte)'"', (byte)'1', (byte)'.', (byte)'0', (byte)'"', (byte)' ',
|
|
(byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g', (byte)'=', (byte)'"',
|
|
};
|
|
static readonly byte[] endDecl =
|
|
{
|
|
(byte)'"', (byte)'?', (byte)'>'
|
|
};
|
|
static readonly byte[] utf8Decl =
|
|
{
|
|
(byte)'<', (byte)'?', (byte)'x', (byte)'m', (byte)'l', (byte)' ',
|
|
(byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n', (byte)'=', (byte)'"', (byte)'1', (byte)'.', (byte)'0', (byte)'"', (byte)' ',
|
|
(byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g', (byte)'=', (byte)'"', (byte)'u', (byte)'t', (byte)'f', (byte)'-', (byte)'8', (byte)'"',
|
|
(byte)'?', (byte)'>'
|
|
};
|
|
static readonly byte[] digits =
|
|
{
|
|
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
|
(byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F'
|
|
};
|
|
static readonly bool[] defaultIsEscapedAttributeChar = new bool[]
|
|
{
|
|
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
|
|
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
|
|
false, false, true, false, false, false, true, false, false, false, false, false, false, false, false, false, // '"', '&'
|
|
false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false // '<', '>'
|
|
};
|
|
static readonly bool[] defaultIsEscapedElementChar = new bool[]
|
|
{
|
|
true, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, // All but 0x09, 0x0A
|
|
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
|
|
false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, // '&'
|
|
false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false // '<', '>'
|
|
};
|
|
|
|
public XmlUTF8NodeWriter()
|
|
: this(defaultIsEscapedAttributeChar, defaultIsEscapedElementChar)
|
|
{
|
|
}
|
|
|
|
public XmlUTF8NodeWriter(bool[] isEscapedAttributeChar, bool[] isEscapedElementChar)
|
|
{
|
|
this.isEscapedAttributeChar = isEscapedAttributeChar;
|
|
this.isEscapedElementChar = isEscapedElementChar;
|
|
this.inAttribute = false;
|
|
}
|
|
|
|
new public void SetOutput(Stream stream, bool ownsStream, Encoding encoding)
|
|
{
|
|
Encoding utf8Encoding = null;
|
|
if (encoding != null && encoding.CodePage == Encoding.UTF8.CodePage)
|
|
{
|
|
utf8Encoding = encoding;
|
|
encoding = null;
|
|
}
|
|
base.SetOutput(stream, ownsStream, utf8Encoding);
|
|
this.encoding = encoding;
|
|
this.inAttribute = false;
|
|
}
|
|
|
|
public Encoding Encoding
|
|
{
|
|
get
|
|
{
|
|
return encoding;
|
|
}
|
|
}
|
|
|
|
byte[] GetCharEntityBuffer()
|
|
{
|
|
if (entityChars == null)
|
|
{
|
|
entityChars = new byte[maxEntityLength];
|
|
}
|
|
return entityChars;
|
|
}
|
|
|
|
char[] GetCharBuffer(int charCount)
|
|
{
|
|
if (charCount >= 256)
|
|
return new char[charCount];
|
|
if (chars == null || chars.Length < charCount)
|
|
chars = new char[charCount];
|
|
return chars;
|
|
}
|
|
|
|
public override void WriteDeclaration()
|
|
{
|
|
if (encoding == null)
|
|
{
|
|
WriteUTF8Chars(utf8Decl, 0, utf8Decl.Length);
|
|
}
|
|
else
|
|
{
|
|
WriteUTF8Chars(startDecl, 0, startDecl.Length);
|
|
if (encoding.WebName == Encoding.BigEndianUnicode.WebName)
|
|
WriteUTF8Chars("utf-16BE");
|
|
else
|
|
WriteUTF8Chars(encoding.WebName);
|
|
WriteUTF8Chars(endDecl, 0, endDecl.Length);
|
|
}
|
|
}
|
|
|
|
public override void WriteCData(string text)
|
|
{
|
|
byte[] buffer;
|
|
int offset;
|
|
|
|
buffer = GetBuffer(9, out offset);
|
|
buffer[offset + 0] = (byte)'<';
|
|
buffer[offset + 1] = (byte)'!';
|
|
buffer[offset + 2] = (byte)'[';
|
|
buffer[offset + 3] = (byte)'C';
|
|
buffer[offset + 4] = (byte)'D';
|
|
buffer[offset + 5] = (byte)'A';
|
|
buffer[offset + 6] = (byte)'T';
|
|
buffer[offset + 7] = (byte)'A';
|
|
buffer[offset + 8] = (byte)'[';
|
|
Advance(9);
|
|
|
|
WriteUTF8Chars(text);
|
|
|
|
buffer = GetBuffer(3, out offset);
|
|
buffer[offset + 0] = (byte)']';
|
|
buffer[offset + 1] = (byte)']';
|
|
buffer[offset + 2] = (byte)'>';
|
|
Advance(3);
|
|
}
|
|
|
|
void WriteStartComment()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(4, out offset);
|
|
buffer[offset + 0] = (byte)'<';
|
|
buffer[offset + 1] = (byte)'!';
|
|
buffer[offset + 2] = (byte)'-';
|
|
buffer[offset + 3] = (byte)'-';
|
|
Advance(4);
|
|
}
|
|
|
|
void WriteEndComment()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(3, out offset);
|
|
buffer[offset + 0] = (byte)'-';
|
|
buffer[offset + 1] = (byte)'-';
|
|
buffer[offset + 2] = (byte)'>';
|
|
Advance(3);
|
|
}
|
|
|
|
public override void WriteComment(string text)
|
|
{
|
|
WriteStartComment();
|
|
WriteUTF8Chars(text);
|
|
WriteEndComment();
|
|
}
|
|
|
|
public override void WriteStartElement(string prefix, string localName)
|
|
{
|
|
WriteByte('<');
|
|
if (prefix.Length != 0)
|
|
{
|
|
WritePrefix(prefix);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localName);
|
|
}
|
|
|
|
public override void WriteStartElement(string prefix, XmlDictionaryString localName)
|
|
{
|
|
WriteStartElement(prefix, localName.Value);
|
|
}
|
|
|
|
public override void WriteStartElement(byte[] prefixBuffer, int prefixOffset, int prefixLength, byte[] localNameBuffer, int localNameOffset, int localNameLength)
|
|
{
|
|
WriteByte('<');
|
|
if (prefixLength != 0)
|
|
{
|
|
WritePrefix(prefixBuffer, prefixOffset, prefixLength);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localNameBuffer, localNameOffset, localNameLength);
|
|
}
|
|
|
|
public override void WriteEndStartElement(bool isEmpty)
|
|
{
|
|
if (!isEmpty)
|
|
{
|
|
WriteByte('>');
|
|
}
|
|
else
|
|
{
|
|
WriteBytes('/', '>');
|
|
}
|
|
}
|
|
|
|
public override void WriteEndElement(string prefix, string localName)
|
|
{
|
|
WriteBytes('<', '/');
|
|
if (prefix.Length != 0)
|
|
{
|
|
WritePrefix(prefix);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localName);
|
|
WriteByte('>');
|
|
}
|
|
|
|
public override void WriteEndElement(byte[] prefixBuffer, int prefixOffset, int prefixLength, byte[] localNameBuffer, int localNameOffset, int localNameLength)
|
|
{
|
|
WriteBytes('<', '/');
|
|
if (prefixLength != 0)
|
|
{
|
|
WritePrefix(prefixBuffer, prefixOffset, prefixLength);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localNameBuffer, localNameOffset, localNameLength);
|
|
WriteByte('>');
|
|
}
|
|
|
|
void WriteStartXmlnsAttribute()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(6, out offset);
|
|
buffer[offset + 0] = (byte)' ';
|
|
buffer[offset + 1] = (byte)'x';
|
|
buffer[offset + 2] = (byte)'m';
|
|
buffer[offset + 3] = (byte)'l';
|
|
buffer[offset + 4] = (byte)'n';
|
|
buffer[offset + 5] = (byte)'s';
|
|
Advance(6);
|
|
inAttribute = true;
|
|
}
|
|
|
|
public override void WriteXmlnsAttribute(string prefix, string ns)
|
|
{
|
|
WriteStartXmlnsAttribute();
|
|
if (prefix.Length != 0)
|
|
{
|
|
WriteByte(':');
|
|
WritePrefix(prefix);
|
|
}
|
|
WriteBytes('=', '"');
|
|
WriteEscapedText(ns);
|
|
WriteEndAttribute();
|
|
}
|
|
|
|
public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString ns)
|
|
{
|
|
WriteXmlnsAttribute(prefix, ns.Value);
|
|
}
|
|
|
|
public override void WriteXmlnsAttribute(byte[] prefixBuffer, int prefixOffset, int prefixLength, byte[] nsBuffer, int nsOffset, int nsLength)
|
|
{
|
|
WriteStartXmlnsAttribute();
|
|
if (prefixLength != 0)
|
|
{
|
|
WriteByte(':');
|
|
WritePrefix(prefixBuffer, prefixOffset, prefixLength);
|
|
}
|
|
WriteBytes('=', '"');
|
|
WriteEscapedText(nsBuffer, nsOffset, nsLength);
|
|
WriteEndAttribute();
|
|
}
|
|
|
|
public override void WriteStartAttribute(string prefix, string localName)
|
|
{
|
|
WriteByte(' ');
|
|
if (prefix.Length != 0)
|
|
{
|
|
WritePrefix(prefix);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localName);
|
|
WriteBytes('=', '"');
|
|
inAttribute = true;
|
|
}
|
|
|
|
public override void WriteStartAttribute(string prefix, XmlDictionaryString localName)
|
|
{
|
|
WriteStartAttribute(prefix, localName.Value);
|
|
}
|
|
|
|
public override void WriteStartAttribute(byte[] prefixBuffer, int prefixOffset, int prefixLength, byte[] localNameBuffer, int localNameOffset, int localNameLength)
|
|
{
|
|
WriteByte(' ');
|
|
if (prefixLength != 0)
|
|
{
|
|
WritePrefix(prefixBuffer, prefixOffset, prefixLength);
|
|
WriteByte(':');
|
|
}
|
|
WriteLocalName(localNameBuffer, localNameOffset, localNameLength);
|
|
WriteBytes('=', '"');
|
|
inAttribute = true;
|
|
}
|
|
|
|
public override void WriteEndAttribute()
|
|
{
|
|
WriteByte('"');
|
|
inAttribute = false;
|
|
}
|
|
|
|
void WritePrefix(string prefix)
|
|
{
|
|
if (prefix.Length == 1)
|
|
{
|
|
WriteUTF8Char(prefix[0]);
|
|
}
|
|
else
|
|
{
|
|
WriteUTF8Chars(prefix);
|
|
}
|
|
}
|
|
|
|
void WritePrefix(byte[] prefixBuffer, int prefixOffset, int prefixLength)
|
|
{
|
|
if (prefixLength == 1)
|
|
{
|
|
WriteUTF8Char((char)prefixBuffer[prefixOffset]);
|
|
}
|
|
else
|
|
{
|
|
WriteUTF8Chars(prefixBuffer, prefixOffset, prefixLength);
|
|
}
|
|
}
|
|
|
|
void WriteLocalName(string localName)
|
|
{
|
|
WriteUTF8Chars(localName);
|
|
}
|
|
|
|
void WriteLocalName(byte[] localNameBuffer, int localNameOffset, int localNameLength)
|
|
{
|
|
WriteUTF8Chars(localNameBuffer, localNameOffset, localNameLength);
|
|
}
|
|
|
|
public override void WriteEscapedText(XmlDictionaryString s)
|
|
{
|
|
WriteEscapedText(s.Value);
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
unsafe public override void WriteEscapedText(string s)
|
|
{
|
|
int count = s.Length;
|
|
if (count > 0)
|
|
{
|
|
fixed (char* chars = s)
|
|
{
|
|
UnsafeWriteEscapedText(chars, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
unsafe public override void WriteEscapedText(char[] s, int offset, int count)
|
|
{
|
|
if (count > 0)
|
|
{
|
|
fixed (char* chars = &s[offset])
|
|
{
|
|
UnsafeWriteEscapedText(chars, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe void UnsafeWriteEscapedText(char* chars, int count)
|
|
{
|
|
bool[] isEscapedChar = (inAttribute ? isEscapedAttributeChar : isEscapedElementChar);
|
|
int isEscapedCharLength = isEscapedChar.Length;
|
|
int i = 0;
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
char ch = chars[j];
|
|
if (ch < isEscapedCharLength && isEscapedChar[ch] || ch >= 0xFFFE)
|
|
{
|
|
UnsafeWriteUTF8Chars(chars + i, j - i);
|
|
WriteCharEntity(ch);
|
|
i = j + 1;
|
|
}
|
|
}
|
|
UnsafeWriteUTF8Chars(chars + i, count - i);
|
|
}
|
|
|
|
public override void WriteEscapedText(byte[] chars, int offset, int count)
|
|
{
|
|
bool[] isEscapedChar = (inAttribute ? isEscapedAttributeChar : isEscapedElementChar);
|
|
int isEscapedCharLength = isEscapedChar.Length;
|
|
int i = 0;
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
byte ch = chars[offset + j];
|
|
if (ch < isEscapedCharLength && isEscapedChar[ch])
|
|
{
|
|
WriteUTF8Chars(chars, offset + i, j - i);
|
|
WriteCharEntity(ch);
|
|
i = j + 1;
|
|
}
|
|
else if (ch == 239 && offset + j + 2 < count)
|
|
{
|
|
// 0xFFFE and 0xFFFF must be written as char entities
|
|
// UTF8(239, 191, 190) = (char) 0xFFFE
|
|
// UTF8(239, 191, 191) = (char) 0xFFFF
|
|
byte ch2 = chars[offset + j + 1];
|
|
byte ch3 = chars[offset + j + 2];
|
|
if (ch2 == 191 && (ch3 == 190 || ch3 == 191))
|
|
{
|
|
WriteUTF8Chars(chars, offset + i, j - i);
|
|
WriteCharEntity(ch3 == 190 ? (char)0xFFFE : (char)0xFFFF);
|
|
i = j + 3;
|
|
}
|
|
}
|
|
}
|
|
WriteUTF8Chars(chars, offset + i, count - i);
|
|
}
|
|
|
|
public void WriteText(int ch)
|
|
{
|
|
WriteUTF8Char(ch);
|
|
}
|
|
|
|
public override void WriteText(byte[] chars, int offset, int count)
|
|
{
|
|
WriteUTF8Chars(chars, offset, count);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
unsafe public override void WriteText(char[] chars, int offset, int count)
|
|
{
|
|
if (count > 0)
|
|
{
|
|
fixed (char* pch = &chars[offset])
|
|
{
|
|
UnsafeWriteUTF8Chars(pch, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void WriteText(string value)
|
|
{
|
|
WriteUTF8Chars(value);
|
|
}
|
|
|
|
public override void WriteText(XmlDictionaryString value)
|
|
{
|
|
WriteUTF8Chars(value.Value);
|
|
}
|
|
|
|
public void WriteLessThanCharEntity()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(4, out offset);
|
|
buffer[offset + 0] = (byte)'&';
|
|
buffer[offset + 1] = (byte)'l';
|
|
buffer[offset + 2] = (byte)'t';
|
|
buffer[offset + 3] = (byte)';';
|
|
Advance(4);
|
|
}
|
|
|
|
public void WriteGreaterThanCharEntity()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(4, out offset);
|
|
buffer[offset + 0] = (byte)'&';
|
|
buffer[offset + 1] = (byte)'g';
|
|
buffer[offset + 2] = (byte)'t';
|
|
buffer[offset + 3] = (byte)';';
|
|
Advance(4);
|
|
}
|
|
|
|
public void WriteAmpersandCharEntity()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(5, out offset);
|
|
buffer[offset + 0] = (byte)'&';
|
|
buffer[offset + 1] = (byte)'a';
|
|
buffer[offset + 2] = (byte)'m';
|
|
buffer[offset + 3] = (byte)'p';
|
|
buffer[offset + 4] = (byte)';';
|
|
Advance(5);
|
|
}
|
|
|
|
public void WriteApostropheCharEntity()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(6, out offset);
|
|
buffer[offset + 0] = (byte)'&';
|
|
buffer[offset + 1] = (byte)'a';
|
|
buffer[offset + 2] = (byte)'p';
|
|
buffer[offset + 3] = (byte)'o';
|
|
buffer[offset + 4] = (byte)'s';
|
|
buffer[offset + 5] = (byte)';';
|
|
Advance(6);
|
|
}
|
|
|
|
public void WriteQuoteCharEntity()
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(6, out offset);
|
|
buffer[offset + 0] = (byte)'&';
|
|
buffer[offset + 1] = (byte)'q';
|
|
buffer[offset + 2] = (byte)'u';
|
|
buffer[offset + 3] = (byte)'o';
|
|
buffer[offset + 4] = (byte)'t';
|
|
buffer[offset + 5] = (byte)';';
|
|
Advance(6);
|
|
}
|
|
|
|
void WriteHexCharEntity(int ch)
|
|
{
|
|
byte[] chars = GetCharEntityBuffer();
|
|
int offset = maxEntityLength;
|
|
chars[--offset] = (byte)';';
|
|
offset -= ToBase16(chars, offset, (uint)ch);
|
|
chars[--offset] = (byte)'x';
|
|
chars[--offset] = (byte)'#';
|
|
chars[--offset] = (byte)'&';
|
|
WriteUTF8Chars(chars, offset, maxEntityLength - offset);
|
|
}
|
|
|
|
public override void WriteCharEntity(int ch)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case '<':
|
|
WriteLessThanCharEntity();
|
|
break;
|
|
case '>':
|
|
WriteGreaterThanCharEntity();
|
|
break;
|
|
case '&':
|
|
WriteAmpersandCharEntity();
|
|
break;
|
|
case '\'':
|
|
WriteApostropheCharEntity();
|
|
break;
|
|
case '"':
|
|
WriteQuoteCharEntity();
|
|
break;
|
|
default:
|
|
WriteHexCharEntity(ch);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int ToBase16(byte[] chars, int offset, uint value)
|
|
{
|
|
int count = 0;
|
|
do
|
|
{
|
|
count++;
|
|
chars[--offset] = digits[(int)(value & 0x0F)];
|
|
value /= 16;
|
|
}
|
|
while (value != 0);
|
|
return count;
|
|
}
|
|
|
|
public override void WriteBoolText(bool value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxBoolChars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteDecimalText(decimal value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxDecimalChars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteDoubleText(double value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxDoubleChars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteFloatText(float value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxFloatChars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteDateTimeText(DateTime value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxDateTimeChars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteUniqueIdText(UniqueId value)
|
|
{
|
|
if (value.IsGuid)
|
|
{
|
|
int charCount = value.CharArrayLength;
|
|
char[] chars = GetCharBuffer(charCount);
|
|
value.ToCharArray(chars, 0);
|
|
WriteText(chars, 0, charCount);
|
|
}
|
|
else
|
|
{
|
|
WriteEscapedText(value.ToString());
|
|
}
|
|
}
|
|
|
|
public override void WriteInt32Text(int value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxInt32Chars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteInt64Text(long value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxInt64Chars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteUInt64Text(ulong value)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(XmlConverter.MaxUInt64Chars, out offset);
|
|
Advance(XmlConverter.ToChars(value, buffer, offset));
|
|
}
|
|
|
|
public override void WriteGuidText(Guid value)
|
|
{
|
|
WriteText(value.ToString());
|
|
}
|
|
|
|
public override void WriteBase64Text(byte[] trailBytes, int trailByteCount, byte[] buffer, int offset, int count)
|
|
{
|
|
if (trailByteCount > 0)
|
|
{
|
|
InternalWriteBase64Text(trailBytes, 0, trailByteCount);
|
|
}
|
|
InternalWriteBase64Text(buffer, offset, count);
|
|
}
|
|
|
|
void InternalWriteBase64Text(byte[] buffer, int offset, int count)
|
|
{
|
|
Base64Encoding encoding = XmlConverter.Base64Encoding;
|
|
while (count >= 3)
|
|
{
|
|
int byteCount = Math.Min(bufferLength / 4 * 3, count - count % 3);
|
|
int charCount = byteCount / 3 * 4;
|
|
int charOffset;
|
|
byte[] chars = GetBuffer(charCount, out charOffset);
|
|
Advance(encoding.GetChars(buffer, offset, byteCount, chars, charOffset));
|
|
offset += byteCount;
|
|
count -= byteCount;
|
|
}
|
|
if (count > 0)
|
|
{
|
|
int charOffset;
|
|
byte[] chars = GetBuffer(4, out charOffset);
|
|
Advance(encoding.GetChars(buffer, offset, count, chars, charOffset));
|
|
}
|
|
}
|
|
|
|
internal override AsyncCompletionResult WriteBase64TextAsync(AsyncEventArgs<XmlNodeWriterWriteBase64TextArgs> xmlNodeWriterState)
|
|
{
|
|
if (internalWriteBase64TextAsyncWriter == null)
|
|
{
|
|
internalWriteBase64TextAsyncWriter = new InternalWriteBase64TextAsyncWriter(this);
|
|
}
|
|
|
|
return this.internalWriteBase64TextAsyncWriter.StartAsync(xmlNodeWriterState);
|
|
}
|
|
|
|
class InternalWriteBase64TextAsyncWriter
|
|
{
|
|
AsyncEventArgs<XmlNodeWriterWriteBase64TextArgs> nodeState;
|
|
AsyncEventArgs<XmlWriteBase64AsyncArguments> writerState;
|
|
XmlWriteBase64AsyncArguments writerArgs;
|
|
XmlUTF8NodeWriter writer;
|
|
GetBufferAsyncEventArgs getBufferState;
|
|
GetBufferArgs getBufferArgs;
|
|
static AsyncEventArgsCallback onTrailByteComplete = new AsyncEventArgsCallback(OnTrailBytesComplete);
|
|
static AsyncEventArgsCallback onWriteComplete = new AsyncEventArgsCallback(OnWriteComplete);
|
|
static AsyncEventArgsCallback onGetBufferComplete = new AsyncEventArgsCallback(OnGetBufferComplete);
|
|
|
|
public InternalWriteBase64TextAsyncWriter(XmlUTF8NodeWriter writer)
|
|
{
|
|
this.writer = writer;
|
|
this.writerState = new AsyncEventArgs<XmlWriteBase64AsyncArguments>();
|
|
this.writerArgs = new XmlWriteBase64AsyncArguments();
|
|
}
|
|
|
|
internal AsyncCompletionResult StartAsync(AsyncEventArgs<XmlNodeWriterWriteBase64TextArgs> xmlNodeWriterState)
|
|
{
|
|
Fx.Assert(xmlNodeWriterState != null, "xmlNodeWriterState cannot be null.");
|
|
Fx.Assert(this.nodeState == null, "nodeState is not null.");
|
|
|
|
this.nodeState = xmlNodeWriterState;
|
|
XmlNodeWriterWriteBase64TextArgs nodeWriterArgs = xmlNodeWriterState.Arguments;
|
|
|
|
if (nodeWriterArgs.TrailCount > 0)
|
|
{
|
|
this.writerArgs.Buffer = nodeWriterArgs.TrailBuffer;
|
|
this.writerArgs.Offset = 0;
|
|
this.writerArgs.Count = nodeWriterArgs.TrailCount;
|
|
|
|
this.writerState.Set(onTrailByteComplete, this.writerArgs, this);
|
|
if (this.InternalWriteBase64TextAsync(this.writerState) != AsyncCompletionResult.Completed)
|
|
{
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
this.writerState.Complete(true);
|
|
}
|
|
|
|
if (this.WriteBufferAsync() == AsyncCompletionResult.Completed)
|
|
{
|
|
this.nodeState = null;
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
|
|
static private void OnTrailBytesComplete(IAsyncEventArgs eventArgs)
|
|
{
|
|
InternalWriteBase64TextAsyncWriter thisPtr = (InternalWriteBase64TextAsyncWriter)eventArgs.AsyncState;
|
|
Exception completionException = null;
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
{
|
|
if (eventArgs.Exception != null)
|
|
{
|
|
completionException = eventArgs.Exception;
|
|
completeSelf = true;
|
|
}
|
|
else if (thisPtr.WriteBufferAsync() == AsyncCompletionResult.Completed)
|
|
{
|
|
completeSelf = true;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
if (Fx.IsFatal(exception))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completionException = exception;
|
|
completeSelf = true;
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
AsyncEventArgs<XmlNodeWriterWriteBase64TextArgs> state = thisPtr.nodeState;
|
|
thisPtr.nodeState = null;
|
|
state.Complete(false, eventArgs.Exception);
|
|
}
|
|
}
|
|
|
|
AsyncCompletionResult WriteBufferAsync()
|
|
{
|
|
this.writerArgs.Buffer = nodeState.Arguments.Buffer;
|
|
this.writerArgs.Offset = nodeState.Arguments.Offset;
|
|
this.writerArgs.Count = nodeState.Arguments.Count;
|
|
|
|
this.writerState.Set(onWriteComplete, this.writerArgs, this);
|
|
if (this.InternalWriteBase64TextAsync(this.writerState) == AsyncCompletionResult.Completed)
|
|
{
|
|
this.writerState.Complete(true);
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
|
|
static void OnWriteComplete(IAsyncEventArgs eventArgs)
|
|
{
|
|
InternalWriteBase64TextAsyncWriter thisPtr = (InternalWriteBase64TextAsyncWriter)eventArgs.AsyncState;
|
|
AsyncEventArgs<XmlNodeWriterWriteBase64TextArgs> state = thisPtr.nodeState;
|
|
thisPtr.nodeState = null;
|
|
state.Complete(false, eventArgs.Exception);
|
|
}
|
|
|
|
AsyncCompletionResult InternalWriteBase64TextAsync(AsyncEventArgs<XmlWriteBase64AsyncArguments> writerState)
|
|
{
|
|
GetBufferAsyncEventArgs bufferState = this.getBufferState;
|
|
GetBufferArgs bufferArgs = this.getBufferArgs;
|
|
XmlWriteBase64AsyncArguments writerArgs = writerState.Arguments;
|
|
|
|
if (bufferState == null)
|
|
{
|
|
// Need to initialize the cached getBufferState
|
|
// used to call GetBufferAsync() multiple times.
|
|
bufferState = new GetBufferAsyncEventArgs();
|
|
bufferArgs = new GetBufferArgs();
|
|
this.getBufferState = bufferState;
|
|
this.getBufferArgs = bufferArgs;
|
|
}
|
|
|
|
Base64Encoding encoding = XmlConverter.Base64Encoding;
|
|
|
|
while (writerArgs.Count >= 3)
|
|
{
|
|
int byteCount = Math.Min(bufferLength / 4 * 3, writerArgs.Count - writerArgs.Count % 3);
|
|
int charCount = byteCount / 3 * 4;
|
|
|
|
bufferArgs.Count = charCount;
|
|
bufferState.Set(onGetBufferComplete, bufferArgs, this);
|
|
if (writer.GetBufferAsync(bufferState) == AsyncCompletionResult.Completed)
|
|
{
|
|
GetBufferEventResult getbufferResult = bufferState.Result;
|
|
bufferState.Complete(true);
|
|
writer.Advance(encoding.GetChars(
|
|
writerArgs.Buffer,
|
|
writerArgs.Offset,
|
|
byteCount,
|
|
getbufferResult.Buffer,
|
|
getbufferResult.Offset));
|
|
writerArgs.Offset += byteCount;
|
|
writerArgs.Count -= byteCount;
|
|
}
|
|
else
|
|
{
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
}
|
|
|
|
if (writerArgs.Count > 0)
|
|
{
|
|
bufferArgs.Count = 4;
|
|
bufferState.Set(onGetBufferComplete, bufferArgs, this);
|
|
if (writer.GetBufferAsync(bufferState) == AsyncCompletionResult.Completed)
|
|
{
|
|
GetBufferEventResult getbufferResult = bufferState.Result;
|
|
bufferState.Complete(true);
|
|
writer.Advance(encoding.GetChars(
|
|
writerArgs.Buffer,
|
|
writerArgs.Offset,
|
|
writerArgs.Count,
|
|
getbufferResult.Buffer,
|
|
getbufferResult.Offset));
|
|
}
|
|
else
|
|
{
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
}
|
|
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
static void OnGetBufferComplete(IAsyncEventArgs state)
|
|
{
|
|
GetBufferEventResult result = ((GetBufferAsyncEventArgs)state).Result;
|
|
InternalWriteBase64TextAsyncWriter thisPtr = (InternalWriteBase64TextAsyncWriter)state.AsyncState;
|
|
XmlWriteBase64AsyncArguments writerArgs = thisPtr.writerState.Arguments;
|
|
|
|
Exception completionException = null;
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
{
|
|
if (state.Exception != null)
|
|
{
|
|
completionException = state.Exception;
|
|
completeSelf = true;
|
|
}
|
|
else
|
|
{
|
|
byte[] chars = result.Buffer;
|
|
int offset = result.Offset;
|
|
|
|
Base64Encoding encoding = XmlConverter.Base64Encoding;
|
|
int byteCount = Math.Min(bufferLength / 4 * 3, writerArgs.Count - writerArgs.Count % 3);
|
|
int charCount = byteCount / 3 * 4;
|
|
|
|
thisPtr.writer.Advance(encoding.GetChars(
|
|
writerArgs.Buffer,
|
|
writerArgs.Offset,
|
|
byteCount,
|
|
chars,
|
|
offset));
|
|
|
|
if (byteCount >= 3)
|
|
{
|
|
writerArgs.Offset += byteCount;
|
|
writerArgs.Count -= byteCount;
|
|
}
|
|
|
|
if (thisPtr.InternalWriteBase64TextAsync(thisPtr.writerState) == AsyncCompletionResult.Completed)
|
|
{
|
|
completeSelf = true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
if (Fx.IsFatal(exception))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completionException = exception;
|
|
completeSelf = true;
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
thisPtr.writerState.Complete(false, completionException);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override IAsyncResult BeginWriteBase64Text(byte[] trailBytes, int trailByteCount, byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
return new WriteBase64TextAsyncResult(trailBytes, trailByteCount, buffer, offset, count, this, callback, state);
|
|
}
|
|
|
|
public override void EndWriteBase64Text(IAsyncResult result)
|
|
{
|
|
WriteBase64TextAsyncResult.End(result);
|
|
}
|
|
|
|
IAsyncResult BeginInternalWriteBase64Text(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
return new InternalWriteBase64TextAsyncResult(buffer, offset, count, this, callback, state);
|
|
}
|
|
|
|
void EndInternalWriteBase64Text(IAsyncResult result)
|
|
{
|
|
InternalWriteBase64TextAsyncResult.End(result);
|
|
}
|
|
|
|
class WriteBase64TextAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCompletion onTrailBytesComplete = new AsyncCompletion(OnTrailBytesComplete);
|
|
static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
|
|
|
|
byte[] trailBytes;
|
|
int trailByteCount;
|
|
byte[] buffer;
|
|
int offset;
|
|
int count;
|
|
|
|
XmlUTF8NodeWriter writer;
|
|
|
|
public WriteBase64TextAsyncResult(byte[] trailBytes, int trailByteCount, byte[] buffer, int offset, int count, XmlUTF8NodeWriter writer, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.writer = writer;
|
|
this.trailBytes = trailBytes;
|
|
this.trailByteCount = trailByteCount;
|
|
this.buffer = buffer;
|
|
this.offset = offset;
|
|
this.count = count;
|
|
|
|
bool completeSelf = HandleWriteTrailBytes(null);
|
|
|
|
if (completeSelf)
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
static bool OnTrailBytesComplete(IAsyncResult result)
|
|
{
|
|
WriteBase64TextAsyncResult thisPtr = (WriteBase64TextAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleWriteTrailBytes(result);
|
|
}
|
|
|
|
static bool OnComplete(IAsyncResult result)
|
|
{
|
|
WriteBase64TextAsyncResult thisPtr = (WriteBase64TextAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleWriteBase64Text(result);
|
|
}
|
|
|
|
bool HandleWriteTrailBytes(IAsyncResult result)
|
|
{
|
|
if (this.trailByteCount > 0)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginInternalWriteBase64Text(this.trailBytes, 0, this.trailByteCount, PrepareAsyncCompletion(onTrailBytesComplete), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
writer.EndInternalWriteBase64Text(result);
|
|
}
|
|
|
|
return HandleWriteBase64Text(null);
|
|
}
|
|
|
|
bool HandleWriteBase64Text(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginInternalWriteBase64Text(this.buffer, this.offset, this.count, PrepareAsyncCompletion(onComplete), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
writer.EndInternalWriteBase64Text(result);
|
|
return true;
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<WriteBase64TextAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
class InternalWriteBase64TextAsyncResult : AsyncResult
|
|
{
|
|
byte[] buffer;
|
|
int offset;
|
|
int count;
|
|
Base64Encoding encoding;
|
|
|
|
XmlUTF8NodeWriter writer;
|
|
static AsyncCallback onWriteCharacters = Fx.ThunkCallback(OnWriteCharacters);
|
|
static AsyncCompletion onWriteTrailingCharacters = new AsyncCompletion(OnWriteTrailingCharacters);
|
|
|
|
public InternalWriteBase64TextAsyncResult(byte[] buffer, int offset, int count, XmlUTF8NodeWriter writer, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.buffer = buffer;
|
|
this.offset = offset;
|
|
this.count = count;
|
|
this.writer = writer;
|
|
this.encoding = XmlConverter.Base64Encoding;
|
|
|
|
bool completeSelf = ContinueWork();
|
|
|
|
if (completeSelf)
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
|
|
static bool OnWriteTrailingCharacters(IAsyncResult result)
|
|
{
|
|
InternalWriteBase64TextAsyncResult thisPtr = (InternalWriteBase64TextAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleWriteTrailingCharacters(result);
|
|
}
|
|
|
|
bool ContinueWork()
|
|
{
|
|
while (this.count >= 3)
|
|
{
|
|
if (HandleWriteCharacters(null))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// needs to jump async
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (count > 0)
|
|
{
|
|
return HandleWriteTrailingCharacters(null);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HandleWriteCharacters(IAsyncResult result)
|
|
{
|
|
int byteCount = Math.Min(bufferLength / 4 * 3, count - count % 3);
|
|
int charCount = byteCount / 3 * 4;
|
|
int charOffset;
|
|
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginGetBuffer(charCount, onWriteCharacters, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
byte[] chars = writer.EndGetBuffer(result, out charOffset);
|
|
|
|
writer.Advance(encoding.GetChars(this.buffer, this.offset, byteCount, chars, charOffset));
|
|
this.offset += byteCount;
|
|
this.count -= byteCount;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HandleWriteTrailingCharacters(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginGetBuffer(4, PrepareAsyncCompletion(onWriteTrailingCharacters), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int charOffset;
|
|
byte[] chars = writer.EndGetBuffer(result, out charOffset);
|
|
writer.Advance(encoding.GetChars(this.buffer, this.offset, this.count, chars, charOffset));
|
|
return true;
|
|
}
|
|
|
|
static void OnWriteCharacters(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InternalWriteBase64TextAsyncResult thisPtr = (InternalWriteBase64TextAsyncResult)result.AsyncState;
|
|
Exception completionException = null;
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
{
|
|
thisPtr.HandleWriteCharacters(result);
|
|
completeSelf = thisPtr.ContinueWork();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Fx.IsFatal(ex))
|
|
{
|
|
throw;
|
|
}
|
|
completeSelf = true;
|
|
completionException = ex;
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
thisPtr.Complete(false, completionException);
|
|
}
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<InternalWriteBase64TextAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
public override void WriteTimeSpanText(TimeSpan value)
|
|
{
|
|
WriteText(XmlConvert.ToString(value));
|
|
}
|
|
|
|
public override void WriteStartListText()
|
|
{
|
|
}
|
|
|
|
public override void WriteListSeparator()
|
|
{
|
|
WriteByte(' ');
|
|
}
|
|
|
|
public override void WriteEndListText()
|
|
{
|
|
}
|
|
|
|
public override void WriteQualifiedName(string prefix, XmlDictionaryString localName)
|
|
{
|
|
if (prefix.Length != 0)
|
|
{
|
|
WritePrefix(prefix);
|
|
WriteByte(':');
|
|
}
|
|
WriteText(localName);
|
|
}
|
|
}
|
|
}
|