| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- //------------------------------------------------------------
- // 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>
- {
- }
- }
- }
|