Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Xml / XmlStreamNodeWriter.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.Xml
5 {
6     using System.IO;
7     using System.Runtime;
8     using System.Runtime.Serialization;
9     using System.Security;
10     using System.Text;
11     using System.Threading;
12
13     abstract class XmlStreamNodeWriter : XmlNodeWriter
14     {
15         Stream stream;
16         byte[] buffer;
17         int offset;
18         bool ownsStream;
19         const int bufferLength = 512;
20         const int maxEntityLength = 32;
21         const int maxBytesPerChar = 3;
22         Encoding encoding;
23         int hasPendingWrite;
24         AsyncEventArgs<object> flushBufferState;
25         static UTF8Encoding UTF8Encoding = new UTF8Encoding(false, true);
26         static AsyncCallback onFlushBufferComplete;
27         static AsyncEventArgsCallback onGetFlushComplete;
28
29         protected XmlStreamNodeWriter()
30         {
31             this.buffer = new byte[bufferLength];
32             encoding = XmlStreamNodeWriter.UTF8Encoding;
33         }
34
35         protected void SetOutput(Stream stream, bool ownsStream, Encoding encoding)
36         {
37             this.stream = stream;
38             this.ownsStream = ownsStream;
39             this.offset = 0;
40
41             if (encoding != null)
42             {
43                 this.encoding = encoding;
44             }
45         }
46
47         // Getting/Setting the Stream exists for fragmenting
48         public Stream Stream
49         {
50             get
51             {
52                 return stream;
53             }
54             set
55             {
56                 stream = value;
57             }
58         }
59
60         // StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes
61         public byte[] StreamBuffer
62         {
63             get
64             {
65                 return buffer;
66             }
67         }
68         public int BufferOffset
69         {
70             get
71             {
72                 return offset;
73             }
74         }
75
76         public int Position
77         {
78             get
79             {
80                 return (int)stream.Position + offset;
81             }
82         }
83
84         protected byte[] GetBuffer(int count, out int offset)
85         {
86             Fx.Assert(count >= 0 && count <= bufferLength, "");
87             int bufferOffset = this.offset;
88             if (bufferOffset + count <= bufferLength)
89             {
90                 offset = bufferOffset;
91             }
92             else
93             {
94                 FlushBuffer();
95                 offset = 0;
96             }
97 #if DEBUG
98             Fx.Assert(offset + count <= bufferLength, "");
99             for (int i = 0; i < count; i++)
100             {
101                 buffer[offset + i] = (byte)'<';
102             }
103 #endif
104             return buffer;
105         }
106
107         internal AsyncCompletionResult GetBufferAsync(GetBufferAsyncEventArgs getBufferState)
108         {
109             Fx.Assert(getBufferState != null, "GetBufferAsyncEventArgs cannot be null.");
110             int count = getBufferState.Arguments.Count;
111             Fx.Assert(count >= 0 && count <= bufferLength, String.Empty);
112             int finalOffset = 0;
113
114             int bufferOffset = this.offset;
115             if (bufferOffset + count <= bufferLength)
116             {
117                 finalOffset = bufferOffset;
118             }
119             else
120             {
121                 if (onGetFlushComplete == null)
122                 {
123                     onGetFlushComplete = new AsyncEventArgsCallback(GetBufferFlushComplete);
124                 }
125                 if (flushBufferState == null)
126                 {
127                     this.flushBufferState = new AsyncEventArgs<object>();
128                 }
129
130                 this.flushBufferState.Set(onGetFlushComplete, getBufferState, this);
131                 if (FlushBufferAsync(this.flushBufferState) == AsyncCompletionResult.Completed)
132                 {
133                     finalOffset = 0;
134                     this.flushBufferState.Complete(true);
135                 }
136                 else
137                 {
138                     return AsyncCompletionResult.Queued;
139                 }
140             }
141 #if DEBUG
142             Fx.Assert(finalOffset + count <= bufferLength, "");
143             for (int i = 0; i < count; i++)
144             {
145                 buffer[finalOffset + i] = (byte)'<';
146             }
147 #endif
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;
153         }
154
155         static void GetBufferFlushComplete(IAsyncEventArgs completionState)
156         {
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);
163         }
164
165         AsyncCompletionResult FlushBufferAsync(AsyncEventArgs<object> state)
166         {
167             if (Interlocked.CompareExchange(ref this.hasPendingWrite, 1, 0) != 0)
168             {
169                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.FlushBufferAlreadyInUse)));
170             }
171
172             if (this.offset != 0)
173             {
174                 if (onFlushBufferComplete == null)
175                 {
176                     onFlushBufferComplete = new AsyncCallback(OnFlushBufferCompete);
177                 }
178
179                 IAsyncResult result = stream.BeginWrite(buffer, 0, this.offset, onFlushBufferComplete, this);
180                 if (!result.CompletedSynchronously)
181                 {
182                     return AsyncCompletionResult.Queued;
183                 }
184
185                 stream.EndWrite(result);
186                 this.offset = 0;
187             }
188
189             if (Interlocked.CompareExchange(ref this.hasPendingWrite, 0, 1) != 1)
190             {
191                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
192             }
193
194             return AsyncCompletionResult.Completed;
195         }
196
197         static void OnFlushBufferCompete(IAsyncResult result)
198         {
199             if (result.CompletedSynchronously)
200             {
201                 return;
202             }
203
204             XmlStreamNodeWriter thisPtr = (XmlStreamNodeWriter)result.AsyncState;
205             Exception completionException = null;
206             try
207             {
208                 thisPtr.stream.EndWrite(result);
209                 thisPtr.offset = 0;
210                 if (Interlocked.CompareExchange(ref thisPtr.hasPendingWrite, 0, 1) != 1)
211                 {
212                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(SR.NoAsyncWritePending)));
213                 }
214             }
215             catch (Exception ex)
216             {
217                 if (Fx.IsFatal(ex))
218                 {
219                     throw;
220                 }
221
222                 completionException = ex;
223             }
224
225             thisPtr.flushBufferState.Complete(false, completionException);
226         }
227
228         protected IAsyncResult BeginGetBuffer(int count, AsyncCallback callback, object state)
229         {
230             Fx.Assert(count >= 0 && count <= bufferLength, "");
231             return new GetBufferAsyncResult(count, this, callback, state);
232         }
233
234         protected byte[] EndGetBuffer(IAsyncResult result, out int offset)
235         {
236             return GetBufferAsyncResult.End(result, out offset);
237         }
238
239         class GetBufferAsyncResult : AsyncResult
240         {
241             XmlStreamNodeWriter writer;
242             int offset;
243             int count;
244             static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
245
246             public GetBufferAsyncResult(int count, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
247                 : base(callback, state)
248             {
249                 this.count = count;
250                 this.writer = writer;
251                 int bufferOffset = writer.offset;
252
253                 bool completeSelf = false;
254
255                 if (bufferOffset + count <= bufferLength)
256                 {
257                     this.offset = bufferOffset;
258                     completeSelf = true;
259                 }
260                 else
261                 {
262                     IAsyncResult result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onComplete), this);
263                     completeSelf = SyncContinue(result);
264                 }
265
266                 if (completeSelf)
267                 {
268                     this.Complete(true);
269                 }
270             }
271
272             static bool OnComplete(IAsyncResult result)
273             {
274                 GetBufferAsyncResult thisPtr = (GetBufferAsyncResult)result.AsyncState;
275                 return thisPtr.HandleFlushBuffer(result);
276             }
277
278             bool HandleFlushBuffer(IAsyncResult result)
279             {
280                 writer.EndFlushBuffer(result);
281                 this.offset = 0;
282
283 #if DEBUG
284                 Fx.Assert(this.offset + this.count <= bufferLength, "");
285                 for (int i = 0; i < this.count; i++)
286                 {
287                     writer.buffer[this.offset + i] = (byte)'<';
288                 }
289 #endif
290                 return true;
291             }
292
293             public static byte[] End(IAsyncResult result, out int offset)
294             {
295                 GetBufferAsyncResult thisPtr = AsyncResult.End<GetBufferAsyncResult>(result);
296
297                 offset = thisPtr.offset;
298                 return thisPtr.writer.buffer;
299             }
300         }
301
302         protected void Advance(int count)
303         {
304             Fx.Assert(offset + count <= bufferLength, "");
305             offset += count;
306         }
307
308         void EnsureByte()
309         {
310             if (offset >= bufferLength)
311             {
312                 FlushBuffer();
313             }
314         }
315
316         protected void WriteByte(byte b)
317         {
318             EnsureByte();
319             buffer[offset++] = b;
320         }
321
322         protected void WriteByte(char ch)
323         {
324             Fx.Assert(ch < 0x80, "");
325             WriteByte((byte)ch);
326         }
327
328         protected void WriteBytes(byte b1, byte b2)
329         {
330             byte[] buffer = this.buffer;
331             int offset = this.offset;
332             if (offset + 1 >= bufferLength)
333             {
334                 FlushBuffer();
335                 offset = 0;
336             }
337             buffer[offset + 0] = b1;
338             buffer[offset + 1] = b2;
339             this.offset += 2;
340         }
341
342         protected void WriteBytes(char ch1, char ch2)
343         {
344             Fx.Assert(ch1 < 0x80 && ch2 < 0x80, "");
345             WriteBytes((byte)ch1, (byte)ch2);
346         }
347
348         public void WriteBytes(byte[] byteBuffer, int byteOffset, int byteCount)
349         {
350             if (byteCount < bufferLength)
351             {
352                 int offset;
353                 byte[] buffer = GetBuffer(byteCount, out offset);
354                 Buffer.BlockCopy(byteBuffer, byteOffset, buffer, offset, byteCount);
355                 Advance(byteCount);
356             }
357             else
358             {
359                 FlushBuffer();
360                 stream.Write(byteBuffer, byteOffset, byteCount);
361             }
362         }
363
364         public IAsyncResult BeginWriteBytes(byte[] byteBuffer, int byteOffset, int byteCount, AsyncCallback callback, object state)
365         {
366             return new WriteBytesAsyncResult(byteBuffer, byteOffset, byteCount, this, callback, state);
367         }
368
369         public void EndWriteBytes(IAsyncResult result)
370         {
371             WriteBytesAsyncResult.End(result);
372         }
373
374         class WriteBytesAsyncResult : AsyncResult
375         {
376             static AsyncCompletion onHandleGetBufferComplete = new AsyncCompletion(OnHandleGetBufferComplete);
377             static AsyncCompletion onHandleFlushBufferComplete = new AsyncCompletion(OnHandleFlushBufferComplete);
378             static AsyncCompletion onHandleWrite = new AsyncCompletion(OnHandleWrite);
379
380             byte[] byteBuffer;
381             int byteOffset;
382             int byteCount;
383             XmlStreamNodeWriter writer;
384
385             public WriteBytesAsyncResult(byte[] byteBuffer, int byteOffset, int byteCount, XmlStreamNodeWriter writer, AsyncCallback callback, object state)
386                 : base(callback, state)
387             {
388                 this.byteBuffer = byteBuffer;
389                 this.byteOffset = byteOffset;
390                 this.byteCount = byteCount;
391                 this.writer = writer;
392
393                 bool completeSelf = false;
394
395                 if (byteCount < bufferLength)
396                 {
397                     completeSelf = HandleGetBuffer(null);
398                 }
399                 else
400                 {
401                     completeSelf = HandleFlushBuffer(null);
402                 }
403
404                 if (completeSelf)
405                 {
406                     this.Complete(true);
407                 }
408             }
409
410             static bool OnHandleGetBufferComplete(IAsyncResult result)
411             {
412                 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
413                 return thisPtr.HandleGetBuffer(result);
414             }
415
416             static bool OnHandleFlushBufferComplete(IAsyncResult result)
417             {
418                 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
419                 return thisPtr.HandleFlushBuffer(result);
420             }
421
422             static bool OnHandleWrite(IAsyncResult result)
423             {
424                 WriteBytesAsyncResult thisPtr = (WriteBytesAsyncResult)result.AsyncState;
425                 return thisPtr.HandleWrite(result);
426             }
427
428             bool HandleGetBuffer(IAsyncResult result)
429             {
430                 if (result == null)
431                 {
432                     result = writer.BeginGetBuffer(this.byteCount, PrepareAsyncCompletion(onHandleGetBufferComplete), this);
433                     if (!result.CompletedSynchronously)
434                     {
435                         return false;
436                     }
437                 }
438
439                 int offset;
440                 byte[] buffer = writer.EndGetBuffer(result, out offset);
441
442                 Buffer.BlockCopy(this.byteBuffer, this.byteOffset, buffer, offset, this.byteCount);
443                 writer.Advance(this.byteCount);
444
445                 return true;
446             }
447
448             bool HandleFlushBuffer(IAsyncResult result)
449             {
450                 if (result == null)
451                 {
452                     result = writer.BeginFlushBuffer(PrepareAsyncCompletion(onHandleFlushBufferComplete), this);
453                     if (!result.CompletedSynchronously)
454                     {
455                         return false;
456                     }
457                 }
458
459                 writer.EndFlushBuffer(result);
460                 return HandleWrite(null);
461             }
462
463             bool HandleWrite(IAsyncResult result)
464             {
465                 if (result == null)
466                 {
467                     result = writer.stream.BeginWrite(this.byteBuffer, this.byteOffset, this.byteCount, PrepareAsyncCompletion(onHandleWrite), this);
468                     if (!result.CompletedSynchronously)
469                     {
470                         return false;
471                     }
472                 }
473
474                 writer.stream.EndWrite(result);
475                 return true;
476             }
477
478             public static void End(IAsyncResult result)
479             {
480                 AsyncResult.End<WriteBytesAsyncResult>(result);
481             }
482         }
483
484         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
485         [SecurityCritical]
486         unsafe protected void UnsafeWriteBytes(byte* bytes, int byteCount)
487         {
488             FlushBuffer();
489             byte[] buffer = this.buffer;
490             while (byteCount > bufferLength)
491             {
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;
497             }
498
499             if (byteCount > 0)
500             {
501                 for (int i = 0; i < byteCount; i++)
502                     buffer[i] = bytes[i];
503                 stream.Write(buffer, 0, byteCount);
504             }
505         }
506
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)
511         {
512             if (ch < 0x80)
513             {
514                 WriteByte((byte)ch);
515             }
516             else if (ch <= char.MaxValue)
517             {
518                 char* chars = stackalloc char[1];
519                 chars[0] = (char)ch;
520                 UnsafeWriteUTF8Chars(chars, 1);
521             }
522             else
523             {
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);
529             }
530         }
531
532         protected void WriteUTF8Chars(byte[] chars, int charOffset, int charCount)
533         {
534             if (charCount < bufferLength)
535             {
536                 int offset;
537                 byte[] buffer = GetBuffer(charCount, out offset);
538                 Buffer.BlockCopy(chars, charOffset, buffer, offset, charCount);
539                 Advance(charCount);
540             }
541             else
542             {
543                 FlushBuffer();
544                 stream.Write(chars, charOffset, charCount);
545             }
546         }
547
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)
552         {
553             int count = value.Length;
554             if (count > 0)
555             {
556                 fixed (char* chars = value)
557                 {
558                     UnsafeWriteUTF8Chars(chars, count);
559                 }
560             }
561         }
562
563         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
564         [SecurityCritical]
565         unsafe protected void UnsafeWriteUTF8Chars(char* chars, int charCount)
566         {
567             const int charChunkSize = bufferLength / maxBytesPerChar;
568             while (charCount > charChunkSize)
569             {
570                 int offset;
571                 int chunkSize = charChunkSize;
572                 if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
573                     chunkSize--;
574                 byte[] buffer = GetBuffer(chunkSize * maxBytesPerChar, out offset);
575                 Advance(UnsafeGetUTF8Chars(chars, chunkSize, buffer, offset));
576                 charCount -= chunkSize;
577                 chars += chunkSize;
578             }
579             if (charCount > 0)
580             {
581                 int offset;
582                 byte[] buffer = GetBuffer(charCount * maxBytesPerChar, out offset);
583                 Advance(UnsafeGetUTF8Chars(chars, charCount, buffer, offset));
584             }
585         }
586
587         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
588         [SecurityCritical]
589         unsafe protected void UnsafeWriteUnicodeChars(char* chars, int charCount)
590         {
591             const int charChunkSize = bufferLength / 2;
592             while (charCount > charChunkSize)
593             {
594                 int offset;
595                 int chunkSize = charChunkSize;
596                 if ((int)(chars[chunkSize - 1] & 0xFC00) == 0xD800) // This is a high surrogate
597                     chunkSize--;
598                 byte[] buffer = GetBuffer(chunkSize * 2, out offset);
599                 Advance(UnsafeGetUnicodeChars(chars, chunkSize, buffer, offset));
600                 charCount -= chunkSize;
601                 chars += chunkSize;
602             }
603             if (charCount > 0)
604             {
605                 int offset;
606                 byte[] buffer = GetBuffer(charCount * 2, out offset);
607                 Advance(UnsafeGetUnicodeChars(chars, charCount, buffer, offset));
608             }
609         }
610
611         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
612         [SecurityCritical]
613         unsafe protected int UnsafeGetUnicodeChars(char* chars, int charCount, byte[] buffer, int offset)
614         {
615             char* charsMax = chars + charCount;
616             while (chars < charsMax)
617             {
618                 char value = *chars++;
619                 buffer[offset++] = (byte)value;
620                 value >>= 8;
621                 buffer[offset++] = (byte)value;
622             }
623             return charCount * 2;
624         }
625
626         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
627         [SecurityCritical]
628         unsafe protected int UnsafeGetUTF8Length(char* chars, int charCount)
629         {
630             char* charsMax = chars + charCount;
631             while (chars < charsMax)
632             {
633                 if (*chars >= 0x80)
634                     break;
635
636                 chars++;
637             }
638
639             if (chars == charsMax)
640                 return charCount;
641
642             return (int)(chars - (charsMax - charCount)) + encoding.GetByteCount(chars, (int)(charsMax - chars));
643         }
644
645         [Fx.Tag.SecurityNote(Critical = "Contains unsafe code. Caller needs to validate arguments.")]
646         [SecurityCritical]
647         unsafe protected int UnsafeGetUTF8Chars(char* chars, int charCount, byte[] buffer, int offset)
648         {
649             if (charCount > 0)
650             {
651                 fixed (byte* _bytes = &buffer[offset])
652                 {
653                     byte* bytes = _bytes;
654                     byte* bytesMax = &bytes[buffer.Length - offset];
655                     char* charsMax = &chars[charCount];
656
657                     while (true)
658                     {
659                         while (chars < charsMax)
660                         {
661                             char t = *chars;
662                             if (t >= 0x80)
663                                 break;
664
665                             *bytes = (byte)t;
666                             bytes++;
667                             chars++;
668                         }
669
670                         if (chars >= charsMax)
671                             break;
672
673                         char* charsStart = chars;
674                         while (chars < charsMax && *chars >= 0x80)
675                         {
676                             chars++;
677                         }
678
679                         bytes += encoding.GetBytes(charsStart, (int)(chars - charsStart), bytes, (int)(bytesMax - bytes));
680
681                         if (chars >= charsMax)
682                             break;
683                     }
684
685                     return (int)(bytes - _bytes);
686                 }
687             }
688             return 0;
689         }
690
691         protected virtual void FlushBuffer()
692         {
693             if (offset != 0)
694             {
695                 stream.Write(buffer, 0, offset);
696                 offset = 0;
697             }
698         }
699
700         protected virtual IAsyncResult BeginFlushBuffer(AsyncCallback callback, object state)
701         {
702             return new FlushBufferAsyncResult(this, callback, state);
703         }
704
705         protected virtual void EndFlushBuffer(IAsyncResult result)
706         {
707             FlushBufferAsyncResult.End(result);
708         }
709
710         class FlushBufferAsyncResult : AsyncResult
711         {
712             static AsyncCompletion onComplete = new AsyncCompletion(OnComplete);
713             XmlStreamNodeWriter writer;
714
715             public FlushBufferAsyncResult(XmlStreamNodeWriter writer, AsyncCallback callback, object state)
716                 : base(callback, state)
717             {
718                 this.writer = writer;
719                 bool completeSelf = true;
720
721                 if (writer.offset != 0)
722                 {
723                     completeSelf = HandleFlushBuffer(null);
724                 }
725
726                 if (completeSelf)
727                 {
728                     this.Complete(true);
729                 }
730             }
731
732             static bool OnComplete(IAsyncResult result)
733             {
734                 FlushBufferAsyncResult thisPtr = (FlushBufferAsyncResult)result.AsyncState;
735                 return thisPtr.HandleFlushBuffer(result);
736             }
737
738             bool HandleFlushBuffer(IAsyncResult result)
739             {
740                 if (result == null)
741                 {
742                     result = this.writer.stream.BeginWrite(writer.buffer, 0, writer.offset, PrepareAsyncCompletion(onComplete), this);
743                     if (!result.CompletedSynchronously)
744                     {
745                         return false;
746                     }
747                 }
748
749                 this.writer.stream.EndWrite(result);
750                 this.writer.offset = 0;
751
752                 return true;
753             }
754
755             public static void End(IAsyncResult result)
756             {
757                 AsyncResult.End<FlushBufferAsyncResult>(result);
758             }
759         }
760
761         public override void Flush()
762         {
763             FlushBuffer();
764             stream.Flush();
765         }
766
767         public override void Close()
768         {
769             if (stream != null)
770             {
771                 if (ownsStream)
772                 {
773                     stream.Close();
774                 }
775                 stream = null;
776             }
777         }
778
779         internal class GetBufferArgs
780         {
781             public int Count { get; set; }
782         }
783
784         internal class GetBufferEventResult
785         {
786             internal byte[] Buffer { get; set; }
787             internal int Offset { get; set; }
788         }
789
790         internal class GetBufferAsyncEventArgs : AsyncEventArgs<GetBufferArgs, GetBufferEventResult>
791         {
792         }
793     }
794 }