e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
795 lines
26 KiB
C#
795 lines
26 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
namespace System.Xml
|
|
{
|
|
using System.IO;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Security;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
abstract class XmlStreamNodeWriter : XmlNodeWriter
|
|
{
|
|
Stream stream;
|
|
byte[] buffer;
|
|
int offset;
|
|
bool ownsStream;
|
|
const int bufferLength = 512;
|
|
const int maxEntityLength = 32;
|
|
const int maxBytesPerChar = 3;
|
|
Encoding encoding;
|
|
int hasPendingWrite;
|
|
AsyncEventArgs<object> flushBufferState;
|
|
static UTF8Encoding UTF8Encoding = new UTF8Encoding(false, true);
|
|
static AsyncCallback onFlushBufferComplete;
|
|
static AsyncEventArgsCallback onGetFlushComplete;
|
|
|
|
protected XmlStreamNodeWriter()
|
|
{
|
|
this.buffer = new byte[bufferLength];
|
|
encoding = XmlStreamNodeWriter.UTF8Encoding;
|
|
}
|
|
|
|
protected void SetOutput(Stream stream, bool ownsStream, Encoding encoding)
|
|
{
|
|
this.stream = stream;
|
|
this.ownsStream = ownsStream;
|
|
this.offset = 0;
|
|
|
|
if (encoding != null)
|
|
{
|
|
this.encoding = encoding;
|
|
}
|
|
}
|
|
|
|
// Getting/Setting the Stream exists for fragmenting
|
|
public Stream Stream
|
|
{
|
|
get
|
|
{
|
|
return stream;
|
|
}
|
|
set
|
|
{
|
|
stream = value;
|
|
}
|
|
}
|
|
|
|
// StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes
|
|
public byte[] StreamBuffer
|
|
{
|
|
get
|
|
{
|
|
return buffer;
|
|
}
|
|
}
|
|
public int BufferOffset
|
|
{
|
|
get
|
|
{
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
public int Position
|
|
{
|
|
get
|
|
{
|
|
return (int)stream.Position + offset;
|
|
}
|
|
}
|
|
|
|
protected byte[] GetBuffer(int count, out int offset)
|
|
{
|
|
Fx.Assert(count >= 0 && count <= bufferLength, "");
|
|
int bufferOffset = this.offset;
|
|
if (bufferOffset + count <= bufferLength)
|
|
{
|
|
offset = bufferOffset;
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
offset = 0;
|
|
}
|
|
#if DEBUG
|
|
Fx.Assert(offset + count <= bufferLength, "");
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
buffer[offset + i] = (byte)'<';
|
|
}
|
|
#endif
|
|
return buffer;
|
|
}
|
|
|
|
internal AsyncCompletionResult GetBufferAsync(GetBufferAsyncEventArgs getBufferState)
|
|
{
|
|
Fx.Assert(getBufferState != null, "GetBufferAsyncEventArgs cannot be null.");
|
|
int count = getBufferState.Arguments.Count;
|
|
Fx.Assert(count >= 0 && count <= bufferLength, String.Empty);
|
|
int finalOffset = 0;
|
|
|
|
int bufferOffset = this.offset;
|
|
if (bufferOffset + count <= bufferLength)
|
|
{
|
|
finalOffset = bufferOffset;
|
|
}
|
|
else
|
|
{
|
|
if (onGetFlushComplete == null)
|
|
{
|
|
onGetFlushComplete = new AsyncEventArgsCallback(GetBufferFlushComplete);
|
|
}
|
|
if (flushBufferState == null)
|
|
{
|
|
this.flushBufferState = new AsyncEventArgs<object>();
|
|
}
|
|
|
|
this.flushBufferState.Set(onGetFlushComplete, getBufferState, this);
|
|
if (FlushBufferAsync(this.flushBufferState) == AsyncCompletionResult.Completed)
|
|
{
|
|
finalOffset = 0;
|
|
this.flushBufferState.Complete(true);
|
|
}
|
|
else
|
|
{
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
}
|
|
#if DEBUG
|
|
Fx.Assert(finalOffset + count <= bufferLength, "");
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
buffer[finalOffset + i] = (byte)'<';
|
|
}
|
|
#endif
|
|
//return the buffer and finalOffset;
|
|
getBufferState.Result = getBufferState.Result ?? new GetBufferEventResult();
|
|
getBufferState.Result.Buffer = this.buffer;
|
|
getBufferState.Result.Offset = finalOffset;
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
static void GetBufferFlushComplete(IAsyncEventArgs completionState)
|
|
{
|
|
XmlStreamNodeWriter thisPtr = (XmlStreamNodeWriter)completionState.AsyncState;
|
|
GetBufferAsyncEventArgs getBufferState = (GetBufferAsyncEventArgs)thisPtr.flushBufferState.Arguments;
|
|
getBufferState.Result = getBufferState.Result ?? new GetBufferEventResult();
|
|
getBufferState.Result.Buffer = thisPtr.buffer;
|
|
getBufferState.Result.Offset = 0;
|
|
getBufferState.Complete(false, completionState.Exception);
|
|
}
|
|
|
|
AsyncCompletionResult FlushBufferAsync(AsyncEventArgs<object> state)
|
|
{
|
|
if (Interlocked.CompareExchange(ref this.hasPendingWrite, 1, 0) != 0)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.FlushBufferAlreadyInUse)));
|
|
}
|
|
|
|
if (this.offset != 0)
|
|
{
|
|
if (onFlushBufferComplete == null)
|
|
{
|
|
onFlushBufferComplete = new AsyncCallback(OnFlushBufferCompete);
|
|
}
|
|
|
|
IAsyncResult result = stream.BeginWrite(buffer, 0, this.offset, onFlushBufferComplete, this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return AsyncCompletionResult.Queued;
|
|
}
|
|
|
|
stream.EndWrite(result);
|
|
this.offset = 0;
|
|
}
|
|
|
|
if (Interlocked.CompareExchange(ref this.hasPendingWrite, 0, 1) != 1)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
|
|
}
|
|
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
static void OnFlushBufferCompete(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
|
|
XmlStreamNodeWriter thisPtr = (XmlStreamNodeWriter)result.AsyncState;
|
|
Exception completionException = null;
|
|
try
|
|
{
|
|
thisPtr.stream.EndWrite(result);
|
|
thisPtr.offset = 0;
|
|
if (Interlocked.CompareExchange(ref thisPtr.hasPendingWrite, 0, 1) != 1)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Fx.IsFatal(ex))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completionException = ex;
|
|
}
|
|
|
|
thisPtr.flushBufferState.Complete(false, completionException);
|
|
}
|
|
|
|
protected IAsyncResult BeginGetBuffer(int count, AsyncCallback callback, object state)
|
|
{
|
|
Fx.Assert(count >= 0 && count <= bufferLength, "");
|
|
return new GetBufferAsyncResult(count, this, callback, state);
|
|
}
|
|
|
|
protected byte[] EndGetBuffer(IAsyncResult result, out int offset)
|
|
{
|
|
return GetBufferAsyncResult.End(result, out offset);
|
|
}
|
|
|
|
class GetBufferAsyncResult : AsyncResult
|
|
{
|
|
XmlStreamNodeWriter writer;
|
|
int offset;
|
|
int count;
|
|
static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
|
|
|
|
public GetBufferAsyncResult(int count, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.count = count;
|
|
this.writer = writer;
|
|
int bufferOffset = writer.offset;
|
|
|
|
bool completeSelf = false;
|
|
|
|
if (bufferOffset + count <= bufferLength)
|
|
{
|
|
this.offset = bufferOffset;
|
|
completeSelf = true;
|
|
}
|
|
else
|
|
{
|
|
IAsyncResult result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onComplete), this);
|
|
completeSelf = SyncContinue(result);
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
static bool OnComplete(IAsyncResult result)
|
|
{
|
|
GetBufferAsyncResult thisPtr = (GetBufferAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleFlushBuffer(result);
|
|
}
|
|
|
|
bool HandleFlushBuffer(IAsyncResult result)
|
|
{
|
|
writer.EndFlushBuffer(result);
|
|
this.offset = 0;
|
|
|
|
#if DEBUG
|
|
Fx.Assert(this.offset + this.count <= bufferLength, "");
|
|
for (int i = 0; i < this.count; i++)
|
|
{
|
|
writer.buffer[this.offset + i] = (byte)'<';
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
public static byte[] End(IAsyncResult result, out int offset)
|
|
{
|
|
GetBufferAsyncResult thisPtr = AsyncResult.End<GetBufferAsyncResult>(result);
|
|
|
|
offset = thisPtr.offset;
|
|
return thisPtr.writer.buffer;
|
|
}
|
|
}
|
|
|
|
protected void Advance(int count)
|
|
{
|
|
Fx.Assert(offset + count <= bufferLength, "");
|
|
offset += count;
|
|
}
|
|
|
|
void EnsureByte()
|
|
{
|
|
if (offset >= bufferLength)
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
}
|
|
|
|
protected void WriteByte(byte b)
|
|
{
|
|
EnsureByte();
|
|
buffer[offset++] = b;
|
|
}
|
|
|
|
protected void WriteByte(char ch)
|
|
{
|
|
Fx.Assert(ch < 0x80, "");
|
|
WriteByte((byte)ch);
|
|
}
|
|
|
|
protected void WriteBytes(byte b1, byte b2)
|
|
{
|
|
byte[] buffer = this.buffer;
|
|
int offset = this.offset;
|
|
if (offset + 1 >= bufferLength)
|
|
{
|
|
FlushBuffer();
|
|
offset = 0;
|
|
}
|
|
buffer[offset + 0] = b1;
|
|
buffer[offset + 1] = b2;
|
|
this.offset += 2;
|
|
}
|
|
|
|
protected void WriteBytes(char ch1, char ch2)
|
|
{
|
|
Fx.Assert(ch1 < 0x80 && ch2 < 0x80, "");
|
|
WriteBytes((byte)ch1, (byte)ch2);
|
|
}
|
|
|
|
public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount)
|
|
{
|
|
if (byteCount < bufferLength)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(byteCount, out offset);
|
|
Buffer.BlockCopy(byteBuffer, byteOffset, buffer, offset, byteCount);
|
|
Advance(byteCount);
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
stream.Write(byteBuffer, byteOffset, byteCount);
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginWriteBytes(byte[] byteBuffer, int byteOffset, int byteCount, AsyncCallback callback, object state)
|
|
{
|
|
return new WriteBytesAsyncResult(byteBuffer, byteOffset, byteCount, this, callback, state);
|
|
}
|
|
|
|
public void EndWriteBytes(IAsyncResult result)
|
|
{
|
|
WriteBytesAsyncResult.End(result);
|
|
}
|
|
|
|
class WriteBytesAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCompletion onHandleGetBufferComplete = new AsyncCompletion(OnHandleGetBufferComplete);
|
|
static AsyncCompletion onHandleFlushBufferComplete = new AsyncCompletion(OnHandleFlushBufferComplete);
|
|
static AsyncCompletion onHandleWrite = new AsyncCompletion(OnHandleWrite);
|
|
|
|
byte[] byteBuffer;
|
|
int byteOffset;
|
|
int byteCount;
|
|
XmlStreamNodeWriter writer;
|
|
|
|
public WriteBytesAsyncResult(byte[] byteBuffer, int byteOffset, int byteCount, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.byteBuffer = byteBuffer;
|
|
this.byteOffset = byteOffset;
|
|
this.byteCount = byteCount;
|
|
this.writer = writer;
|
|
|
|
bool completeSelf = false;
|
|
|
|
if (byteCount < bufferLength)
|
|
{
|
|
completeSelf = HandleGetBuffer(null);
|
|
}
|
|
else
|
|
{
|
|
completeSelf = HandleFlushBuffer(null);
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
static bool OnHandleGetBufferComplete(IAsyncResult result)
|
|
{
|
|
WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleGetBuffer(result);
|
|
}
|
|
|
|
static bool OnHandleFlushBufferComplete(IAsyncResult result)
|
|
{
|
|
WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleFlushBuffer(result);
|
|
}
|
|
|
|
static bool OnHandleWrite(IAsyncResult result)
|
|
{
|
|
WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleWrite(result);
|
|
}
|
|
|
|
bool HandleGetBuffer(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginGetBuffer(this.byteCount, PrepareAsyncCompletion(onHandleGetBufferComplete), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int offset;
|
|
byte[] buffer = writer.EndGetBuffer(result, out offset);
|
|
|
|
Buffer.BlockCopy(this.byteBuffer, this.byteOffset, buffer, offset, this.byteCount);
|
|
writer.Advance(this.byteCount);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HandleFlushBuffer(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onHandleFlushBufferComplete), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
writer.EndFlushBuffer(result);
|
|
return HandleWrite(null);
|
|
}
|
|
|
|
bool HandleWrite(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = writer.stream.BeginWrite(this.byteBuffer, this.byteOffset, this.byteCount, PrepareAsyncCompletion(onHandleWrite), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
writer.stream.EndWrite(result);
|
|
return true;
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<WriteBytesAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected void UnsafeWriteBytes(byte* bytes, int byteCount)
|
|
{
|
|
FlushBuffer();
|
|
byte[] buffer = this.buffer;
|
|
while (byteCount > bufferLength)
|
|
{
|
|
for (int i = 0; i < bufferLength; i++)
|
|
buffer[i] = bytes[i];
|
|
stream.Write(buffer, 0, bufferLength);
|
|
bytes += bufferLength;
|
|
byteCount -= bufferLength;
|
|
}
|
|
|
|
if (byteCount > 0)
|
|
{
|
|
for (int i = 0; i < byteCount; i++)
|
|
buffer[i] = bytes[i];
|
|
stream.Write(buffer, 0, byteCount);
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
unsafe protected void WriteUTF8Char(int ch)
|
|
{
|
|
if (ch < 0x80)
|
|
{
|
|
WriteByte((byte)ch);
|
|
}
|
|
else if (ch <= char.MaxValue)
|
|
{
|
|
char* chars = stackalloc char[1];
|
|
chars[0] = (char)ch;
|
|
UnsafeWriteUTF8Chars(chars, 1);
|
|
}
|
|
else
|
|
{
|
|
SurrogateChar surrogateChar = new SurrogateChar(ch);
|
|
char* chars = stackalloc char[2];
|
|
chars[0] = surrogateChar.HighChar;
|
|
chars[1] = surrogateChar.LowChar;
|
|
UnsafeWriteUTF8Chars(chars, 2);
|
|
}
|
|
}
|
|
|
|
protected void WriteUTF8Chars(byte[] chars, int charOffset, int charCount)
|
|
{
|
|
if (charCount < bufferLength)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(charCount, out offset);
|
|
Buffer.BlockCopy(chars, charOffset, buffer, offset, charCount);
|
|
Advance(charCount);
|
|
}
|
|
else
|
|
{
|
|
FlushBuffer();
|
|
stream.Write(chars, charOffset, charCount);
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
unsafe protected void WriteUTF8Chars(string value)
|
|
{
|
|
int count = value.Length;
|
|
if (count > 0)
|
|
{
|
|
fixed (char* chars = value)
|
|
{
|
|
UnsafeWriteUTF8Chars(chars, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected void UnsafeWriteUTF8Chars(char* chars, int charCount)
|
|
{
|
|
const int charChunkSize = bufferLength / maxBytesPerChar;
|
|
while (charCount > charChunkSize)
|
|
{
|
|
int offset;
|
|
int chunkSize = charChunkSize;
|
|
if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
|
|
chunkSize--;
|
|
byte[] buffer = GetBuffer(chunkSize * maxBytesPerChar, out offset);
|
|
Advance(UnsafeGetUTF8Chars(chars, chunkSize, buffer, offset));
|
|
charCount -= chunkSize;
|
|
chars += chunkSize;
|
|
}
|
|
if (charCount > 0)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(charCount * maxBytesPerChar, out offset);
|
|
Advance(UnsafeGetUTF8Chars(chars, charCount, buffer, offset));
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected void UnsafeWriteUnicodeChars(char* chars, int charCount)
|
|
{
|
|
const int charChunkSize = bufferLength / 2;
|
|
while (charCount > charChunkSize)
|
|
{
|
|
int offset;
|
|
int chunkSize = charChunkSize;
|
|
if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
|
|
chunkSize--;
|
|
byte[] buffer = GetBuffer(chunkSize * 2, out offset);
|
|
Advance(UnsafeGetUnicodeChars(chars, chunkSize, buffer, offset));
|
|
charCount -= chunkSize;
|
|
chars += chunkSize;
|
|
}
|
|
if (charCount > 0)
|
|
{
|
|
int offset;
|
|
byte[] buffer = GetBuffer(charCount * 2, out offset);
|
|
Advance(UnsafeGetUnicodeChars(chars, charCount, buffer, offset));
|
|
}
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected int UnsafeGetUnicodeChars(char* chars, int charCount, byte[] buffer, int offset)
|
|
{
|
|
char* charsMax = chars + charCount;
|
|
while (chars < charsMax)
|
|
{
|
|
char value = *chars++;
|
|
buffer[offset++] = (byte)value;
|
|
value >>= 8;
|
|
buffer[offset++] = (byte)value;
|
|
}
|
|
return charCount * 2;
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected int UnsafeGetUTF8Length(char* chars, int charCount)
|
|
{
|
|
char* charsMax = chars + charCount;
|
|
while (chars < charsMax)
|
|
{
|
|
if (*chars >= 0x80)
|
|
break;
|
|
|
|
chars++;
|
|
}
|
|
|
|
if (chars == charsMax)
|
|
return charCount;
|
|
|
|
return (int)(chars - (charsMax - charCount)) + encoding.GetByteCount(chars, (int)(charsMax - chars));
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
|
|
[SecurityCritical]
|
|
unsafe protected int UnsafeGetUTF8Chars(char* chars, int charCount, byte[] buffer, int offset)
|
|
{
|
|
if (charCount > 0)
|
|
{
|
|
fixed (byte* _bytes = &buffer[offset])
|
|
{
|
|
byte* bytes = _bytes;
|
|
byte* bytesMax = &bytes[buffer.Length - offset];
|
|
char* charsMax = &chars[charCount];
|
|
|
|
while (true)
|
|
{
|
|
while (chars < charsMax)
|
|
{
|
|
char t = *chars;
|
|
if (t >= 0x80)
|
|
break;
|
|
|
|
*bytes = (byte)t;
|
|
bytes++;
|
|
chars++;
|
|
}
|
|
|
|
if (chars >= charsMax)
|
|
break;
|
|
|
|
char* charsStart = chars;
|
|
while (chars < charsMax && *chars >= 0x80)
|
|
{
|
|
chars++;
|
|
}
|
|
|
|
bytes += encoding.GetBytes(charsStart, (int)(chars - charsStart), bytes, (int)(bytesMax - bytes));
|
|
|
|
if (chars >= charsMax)
|
|
break;
|
|
}
|
|
|
|
return (int)(bytes - _bytes);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
protected virtual void FlushBuffer()
|
|
{
|
|
if (offset != 0)
|
|
{
|
|
stream.Write(buffer, 0, offset);
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
protected virtual IAsyncResult BeginFlushBuffer(AsyncCallback callback, object state)
|
|
{
|
|
return new FlushBufferAsyncResult(this, callback, state);
|
|
}
|
|
|
|
protected virtual void EndFlushBuffer(IAsyncResult result)
|
|
{
|
|
FlushBufferAsyncResult.End(result);
|
|
}
|
|
|
|
class FlushBufferAsyncResult : AsyncResult
|
|
{
|
|
static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
|
|
XmlStreamNodeWriter writer;
|
|
|
|
public FlushBufferAsyncResult(XmlStreamNodeWriter writer, AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.writer = writer;
|
|
bool completeSelf = true;
|
|
|
|
if (writer.offset != 0)
|
|
{
|
|
completeSelf = HandleFlushBuffer(null);
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
static bool OnComplete(IAsyncResult result)
|
|
{
|
|
FlushBufferAsyncResult thisPtr = (FlushBufferAsyncResult)result.AsyncState;
|
|
return thisPtr.HandleFlushBuffer(result);
|
|
}
|
|
|
|
bool HandleFlushBuffer(IAsyncResult result)
|
|
{
|
|
if (result == null)
|
|
{
|
|
result = this.writer.stream.BeginWrite(writer.buffer, 0, writer.offset, PrepareAsyncCompletion(onComplete), this);
|
|
if (!result.CompletedSynchronously)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this.writer.stream.EndWrite(result);
|
|
this.writer.offset = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void End(IAsyncResult result)
|
|
{
|
|
AsyncResult.End<FlushBufferAsyncResult>(result);
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
FlushBuffer();
|
|
stream.Flush();
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
if (stream != null)
|
|
{
|
|
if (ownsStream)
|
|
{
|
|
stream.Close();
|
|
}
|
|
stream = null;
|
|
}
|
|
}
|
|
|
|
internal class GetBufferArgs
|
|
{
|
|
public int Count { get; set; }
|
|
}
|
|
|
|
internal class GetBufferEventResult
|
|
{
|
|
internal byte[] Buffer { get; set; }
|
|
internal int Offset { get; set; }
|
|
}
|
|
|
|
internal class GetBufferAsyncEventArgs : AsyncEventArgs<GetBufferArgs, GetBufferEventResult>
|
|
{
|
|
}
|
|
}
|
|
}
|