1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
8 using System.Runtime.Serialization;
11 using System.Threading;
13 abstract class XmlStreamNodeWriter : XmlNodeWriter
19 const int bufferLength = 512;
20 const int maxEntityLength = 32;
21 const int maxBytesPerChar = 3;
24 AsyncEventArgs<object> flushBufferState;
25 static UTF8Encoding UTF8Encoding = new UTF8Encoding(false, true);
26 static AsyncCallback onFlushBufferComplete;
27 static AsyncEventArgsCallback onGetFlushComplete;
29 protected XmlStreamNodeWriter()
31 this.buffer = new byte[bufferLength];
32 encoding = XmlStreamNodeWriter.UTF8Encoding;
35 protected void SetOutput(Stream stream, bool ownsStream, Encoding encoding)
38 this.ownsStream = ownsStream;
43 this.encoding = encoding;
47 // Getting/Setting the Stream exists for fragmenting
60 // StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes
61 public byte[] StreamBuffer
68 public int BufferOffset
80 return (int)stream.Position + offset;
84 protected byte[] GetBuffer(int count, out int offset)
86 Fx.Assert(count >= 0 && count <= bufferLength, "");
87 int bufferOffset = this.offset;
88 if (bufferOffset + count <= bufferLength)
90 offset = bufferOffset;
98 Fx.Assert(offset + count <= bufferLength, "");
99 for (int i = 0; i < count; i++)
101 buffer[offset + i] = (byte)'<';
107 internal AsyncCompletionResult GetBufferAsync(GetBufferAsyncEventArgs getBufferState)
109 Fx.Assert(getBufferState != null, "GetBufferAsyncEventArgs cannot be null.");
110 int count = getBufferState.Arguments.Count;
111 Fx.Assert(count >= 0 && count <= bufferLength, String.Empty);
114 int bufferOffset = this.offset;
115 if (bufferOffset + count <= bufferLength)
117 finalOffset = bufferOffset;
121 if (onGetFlushComplete == null)
123 onGetFlushComplete = new AsyncEventArgsCallback(GetBufferFlushComplete);
125 if (flushBufferState == null)
127 this.flushBufferState = new AsyncEventArgs<object>();
130 this.flushBufferState.Set(onGetFlushComplete, getBufferState, this);
131 if (FlushBufferAsync(this.flushBufferState) == AsyncCompletionResult.Completed)
134 this.flushBufferState.Complete(true);
138 return AsyncCompletionResult.Queued;
142 Fx.Assert(finalOffset + count <= bufferLength, "");
143 for (int i = 0; i < count; i++)
145 buffer[finalOffset + i] = (byte)'<';
148 //return the buffer and finalOffset;
149 getBufferState.Result = getBufferState.Result ?? new GetBufferEventResult();
150 getBufferState.Result.Buffer = this.buffer;
151 getBufferState.Result.Offset = finalOffset;
152 return AsyncCompletionResult.Completed;
155 static void GetBufferFlushComplete(IAsyncEventArgs completionState)
157 XmlStreamNodeWriter thisPtr = (XmlStreamNodeWriter)completionState.AsyncState;
158 GetBufferAsyncEventArgs getBufferState = (GetBufferAsyncEventArgs)thisPtr.flushBufferState.Arguments;
159 getBufferState.Result = getBufferState.Result ?? new GetBufferEventResult();
160 getBufferState.Result.Buffer = thisPtr.buffer;
161 getBufferState.Result.Offset = 0;
162 getBufferState.Complete(false, completionState.Exception);
165 AsyncCompletionResult FlushBufferAsync(AsyncEventArgs<object> state)
167 if (Interlocked.CompareExchange(ref this.hasPendingWrite, 1, 0) != 0)
169 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.FlushBufferAlreadyInUse)));
172 if (this.offset != 0)
174 if (onFlushBufferComplete == null)
176 onFlushBufferComplete = new AsyncCallback(OnFlushBufferCompete);
179 IAsyncResult result = stream.BeginWrite(buffer, 0, this.offset, onFlushBufferComplete, this);
180 if (!result.CompletedSynchronously)
182 return AsyncCompletionResult.Queued;
185 stream.EndWrite(result);
189 if (Interlocked.CompareExchange(ref this.hasPendingWrite, 0, 1) != 1)
191 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
194 return AsyncCompletionResult.Completed;
197 static void OnFlushBufferCompete(IAsyncResult result)
199 if (result.CompletedSynchronously)
204 XmlStreamNodeWriter thisPtr = (XmlStreamNodeWriter)result.AsyncState;
205 Exception completionException = null;
208 thisPtr.stream.EndWrite(result);
210 if (Interlocked.CompareExchange(ref thisPtr.hasPendingWrite, 0, 1) != 1)
212 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
222 completionException = ex;
225 thisPtr.flushBufferState.Complete(false, completionException);
228 protected IAsyncResult BeginGetBuffer(int count, AsyncCallback callback, object state)
230 Fx.Assert(count >= 0 && count <= bufferLength, "");
231 return new GetBufferAsyncResult(count, this, callback, state);
234 protected byte[] EndGetBuffer(IAsyncResult result, out int offset)
236 return GetBufferAsyncResult.End(result, out offset);
239 class GetBufferAsyncResult : AsyncResult
241 XmlStreamNodeWriter writer;
244 static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
246 public GetBufferAsyncResult(int count, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
247 : base(callback, state)
250 this.writer = writer;
251 int bufferOffset = writer.offset;
253 bool completeSelf = false;
255 if (bufferOffset + count <= bufferLength)
257 this.offset = bufferOffset;
262 IAsyncResult result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onComplete), this);
263 completeSelf = SyncContinue(result);
272 static bool OnComplete(IAsyncResult result)
274 GetBufferAsyncResult thisPtr = (GetBufferAsyncResult)result.AsyncState;
275 return thisPtr.HandleFlushBuffer(result);
278 bool HandleFlushBuffer(IAsyncResult result)
280 writer.EndFlushBuffer(result);
284 Fx.Assert(this.offset + this.count <= bufferLength, "");
285 for (int i = 0; i < this.count; i++)
287 writer.buffer[this.offset + i] = (byte)'<';
293 public static byte[] End(IAsyncResult result, out int offset)
295 GetBufferAsyncResult thisPtr = AsyncResult.End<GetBufferAsyncResult>(result);
297 offset = thisPtr.offset;
298 return thisPtr.writer.buffer;
302 protected void Advance(int count)
304 Fx.Assert(offset + count <= bufferLength, "");
310 if (offset >= bufferLength)
316 protected void WriteByte(byte b)
319 buffer[offset++] = b;
322 protected void WriteByte(char ch)
324 Fx.Assert(ch < 0x80, "");
328 protected void WriteBytes(byte b1, byte b2)
330 byte[] buffer = this.buffer;
331 int offset = this.offset;
332 if (offset + 1 >= bufferLength)
337 buffer[offset + 0] = b1;
338 buffer[offset + 1] = b2;
342 protected void WriteBytes(char ch1, char ch2)
344 Fx.Assert(ch1 < 0x80 && ch2 < 0x80, "");
345 WriteBytes((byte)ch1, (byte)ch2);
348 public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount)
350 if (byteCount < bufferLength)
353 byte[] buffer = GetBuffer(byteCount, out offset);
354 Buffer.BlockCopy(byteBuffer, byteOffset, buffer, offset, byteCount);
360 stream.Write(byteBuffer, byteOffset, byteCount);
364 public IAsyncResult BeginWriteBytes(byte[] byteBuffer, int byteOffset, int byteCount, AsyncCallback callback, object state)
366 return new WriteBytesAsyncResult(byteBuffer, byteOffset, byteCount, this, callback, state);
369 public void EndWriteBytes(IAsyncResult result)
371 WriteBytesAsyncResult.End(result);
374 class WriteBytesAsyncResult : AsyncResult
376 static AsyncCompletion onHandleGetBufferComplete = new AsyncCompletion(OnHandleGetBufferComplete);
377 static AsyncCompletion onHandleFlushBufferComplete = new AsyncCompletion(OnHandleFlushBufferComplete);
378 static AsyncCompletion onHandleWrite = new AsyncCompletion(OnHandleWrite);
383 XmlStreamNodeWriter writer;
385 public WriteBytesAsyncResult(byte[] byteBuffer, int byteOffset, int byteCount, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
386 : base(callback, state)
388 this.byteBuffer = byteBuffer;
389 this.byteOffset = byteOffset;
390 this.byteCount = byteCount;
391 this.writer = writer;
393 bool completeSelf = false;
395 if (byteCount < bufferLength)
397 completeSelf = HandleGetBuffer(null);
401 completeSelf = HandleFlushBuffer(null);
410 static bool OnHandleGetBufferComplete(IAsyncResult result)
412 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
413 return thisPtr.HandleGetBuffer(result);
416 static bool OnHandleFlushBufferComplete(IAsyncResult result)
418 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
419 return thisPtr.HandleFlushBuffer(result);
422 static bool OnHandleWrite(IAsyncResult result)
424 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
425 return thisPtr.HandleWrite(result);
428 bool HandleGetBuffer(IAsyncResult result)
432 result = writer.BeginGetBuffer(this.byteCount, PrepareAsyncCompletion(onHandleGetBufferComplete), this);
433 if (!result.CompletedSynchronously)
440 byte[] buffer = writer.EndGetBuffer(result, out offset);
442 Buffer.BlockCopy(this.byteBuffer, this.byteOffset, buffer, offset, this.byteCount);
443 writer.Advance(this.byteCount);
448 bool HandleFlushBuffer(IAsyncResult result)
452 result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onHandleFlushBufferComplete), this);
453 if (!result.CompletedSynchronously)
459 writer.EndFlushBuffer(result);
460 return HandleWrite(null);
463 bool HandleWrite(IAsyncResult result)
467 result = writer.stream.BeginWrite(this.byteBuffer, this.byteOffset, this.byteCount, PrepareAsyncCompletion(onHandleWrite), this);
468 if (!result.CompletedSynchronously)
474 writer.stream.EndWrite(result);
478 public static void End(IAsyncResult result)
480 AsyncResult.End<WriteBytesAsyncResult>(result);
484 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
486 unsafe protected void UnsafeWriteBytes(byte* bytes, int byteCount)
489 byte[] buffer = this.buffer;
490 while (byteCount > bufferLength)
492 for (int i = 0; i < bufferLength; i++)
493 buffer[i] = bytes[i];
494 stream.Write(buffer, 0, bufferLength);
495 bytes += bufferLength;
496 byteCount -= bufferLength;
501 for (int i = 0; i < byteCount; i++)
502 buffer[i] = bytes[i];
503 stream.Write(buffer, 0, byteCount);
507 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
508 Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
509 [SecuritySafeCritical]
510 unsafe protected void WriteUTF8Char(int ch)
516 else if (ch <= char.MaxValue)
518 char* chars = stackalloc char[1];
520 UnsafeWriteUTF8Chars(chars, 1);
524 SurrogateChar surrogateChar = new SurrogateChar(ch);
525 char* chars = stackalloc char[2];
526 chars[0] = surrogateChar.HighChar;
527 chars[1] = surrogateChar.LowChar;
528 UnsafeWriteUTF8Chars(chars, 2);
532 protected void WriteUTF8Chars(byte[] chars, int charOffset, int charCount)
534 if (charCount < bufferLength)
537 byte[] buffer = GetBuffer(charCount, out offset);
538 Buffer.BlockCopy(chars, charOffset, buffer, offset, charCount);
544 stream.Write(chars, charOffset, charCount);
548 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
549 Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
550 [SecuritySafeCritical]
551 unsafe protected void WriteUTF8Chars(string value)
553 int count = value.Length;
556 fixed (char* chars = value)
558 UnsafeWriteUTF8Chars(chars, count);
563 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
565 unsafe protected void UnsafeWriteUTF8Chars(char* chars, int charCount)
567 const int charChunkSize = bufferLength / maxBytesPerChar;
568 while (charCount > charChunkSize)
571 int chunkSize = charChunkSize;
572 if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
574 byte[] buffer = GetBuffer(chunkSize * maxBytesPerChar, out offset);
575 Advance(UnsafeGetUTF8Chars(chars, chunkSize, buffer, offset));
576 charCount -= chunkSize;
582 byte[] buffer = GetBuffer(charCount * maxBytesPerChar, out offset);
583 Advance(UnsafeGetUTF8Chars(chars, charCount, buffer, offset));
587 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
589 unsafe protected void UnsafeWriteUnicodeChars(char* chars, int charCount)
591 const int charChunkSize = bufferLength / 2;
592 while (charCount > charChunkSize)
595 int chunkSize = charChunkSize;
596 if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
598 byte[] buffer = GetBuffer(chunkSize * 2, out offset);
599 Advance(UnsafeGetUnicodeChars(chars, chunkSize, buffer, offset));
600 charCount -= chunkSize;
606 byte[] buffer = GetBuffer(charCount * 2, out offset);
607 Advance(UnsafeGetUnicodeChars(chars, charCount, buffer, offset));
611 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
613 unsafe protected int UnsafeGetUnicodeChars(char* chars, int charCount, byte[] buffer, int offset)
615 char* charsMax = chars + charCount;
616 while (chars < charsMax)
618 char value = *chars++;
619 buffer[offset++] = (byte)value;
621 buffer[offset++] = (byte)value;
623 return charCount * 2;
626 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
628 unsafe protected int UnsafeGetUTF8Length(char* chars, int charCount)
630 char* charsMax = chars + charCount;
631 while (chars < charsMax)
639 if (chars == charsMax)
642 return (int)(chars - (charsMax - charCount)) + encoding.GetByteCount(chars, (int)(charsMax - chars));
645 [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
647 unsafe protected int UnsafeGetUTF8Chars(char* chars, int charCount, byte[] buffer, int offset)
651 fixed (byte* _bytes = &buffer[offset])
653 byte* bytes = _bytes;
654 byte* bytesMax = &bytes[buffer.Length - offset];
655 char* charsMax = &chars[charCount];
659 while (chars < charsMax)
670 if (chars >= charsMax)
673 char* charsStart = chars;
674 while (chars < charsMax && *chars >= 0x80)
679 bytes += encoding.GetBytes(charsStart, (int)(chars - charsStart), bytes, (int)(bytesMax - bytes));
681 if (chars >= charsMax)
685 return (int)(bytes - _bytes);
691 protected virtual void FlushBuffer()
695 stream.Write(buffer, 0, offset);
700 protected virtual IAsyncResult BeginFlushBuffer(AsyncCallback callback, object state)
702 return new FlushBufferAsyncResult(this, callback, state);
705 protected virtual void EndFlushBuffer(IAsyncResult result)
707 FlushBufferAsyncResult.End(result);
710 class FlushBufferAsyncResult : AsyncResult
712 static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
713 XmlStreamNodeWriter writer;
715 public FlushBufferAsyncResult(XmlStreamNodeWriter writer, AsyncCallback callback, object state)
716 : base(callback, state)
718 this.writer = writer;
719 bool completeSelf = true;
721 if (writer.offset != 0)
723 completeSelf = HandleFlushBuffer(null);
732 static bool OnComplete(IAsyncResult result)
734 FlushBufferAsyncResult thisPtr = (FlushBufferAsyncResult)result.AsyncState;
735 return thisPtr.HandleFlushBuffer(result);
738 bool HandleFlushBuffer(IAsyncResult result)
742 result = this.writer.stream.BeginWrite(writer.buffer, 0, writer.offset, PrepareAsyncCompletion(onComplete), this);
743 if (!result.CompletedSynchronously)
749 this.writer.stream.EndWrite(result);
750 this.writer.offset = 0;
755 public static void End(IAsyncResult result)
757 AsyncResult.End<FlushBufferAsyncResult>(result);
761 public override void Flush()
767 public override void Close()
779 internal class GetBufferArgs
781 public int Count { get; set; }
784 internal class GetBufferEventResult
786 internal byte[] Buffer { get; set; }
787 internal int Offset { get; set; }
790 internal class GetBufferAsyncEventArgs : AsyncEventArgs<GetBufferArgs, GetBufferEventResult>