• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2008 Google Inc.  All rights reserved.
4 // https://developers.google.com/protocol-buffers/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32 
33 using Google.Protobuf.Collections;
34 using System;
35 using System.IO;
36 using System.Text;
37 
38 namespace Google.Protobuf
39 {
40     /// <summary>
41     /// Encodes and writes protocol message fields.
42     /// </summary>
43     /// <remarks>
44     /// <para>
45     /// This class is generally used by generated code to write appropriate
46     /// primitives to the stream. It effectively encapsulates the lowest
47     /// levels of protocol buffer format. Unlike some other implementations,
48     /// this does not include combined "write tag and value" methods. Generated
49     /// code knows the exact byte representations of the tags they're going to write,
50     /// so there's no need to re-encode them each time. Manually-written code calling
51     /// this class should just call one of the <c>WriteTag</c> overloads before each value.
52     /// </para>
53     /// <para>
54     /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField&lt;T&gt;</c>
55     /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.
56     /// </para>
57     /// </remarks>
58     public sealed partial class CodedOutputStream : IDisposable
59     {
60         // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
61         internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
62 
63         /// <summary>
64         /// The buffer size used by CreateInstance(Stream).
65         /// </summary>
66         public static readonly int DefaultBufferSize = 4096;
67 
68         private readonly bool leaveOpen;
69         private readonly byte[] buffer;
70         private readonly int limit;
71         private int position;
72         private readonly Stream output;
73 
74         #region Construction
75         /// <summary>
76         /// Creates a new CodedOutputStream that writes directly to the given
77         /// byte array. If more bytes are written than fit in the array,
78         /// OutOfSpaceException will be thrown.
79         /// </summary>
CodedOutputStream(byte[] flatArray)80         public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
81         {
82         }
83 
84         /// <summary>
85         /// Creates a new CodedOutputStream that writes directly to the given
86         /// byte array slice. If more bytes are written than fit in the array,
87         /// OutOfSpaceException will be thrown.
88         /// </summary>
CodedOutputStream(byte[] buffer, int offset, int length)89         private CodedOutputStream(byte[] buffer, int offset, int length)
90         {
91             this.output = null;
92             this.buffer = buffer;
93             this.position = offset;
94             this.limit = offset + length;
95             leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
96         }
97 
CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)98         private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
99         {
100             this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
101             this.buffer = buffer;
102             this.position = 0;
103             this.limit = buffer.Length;
104             this.leaveOpen = leaveOpen;
105         }
106 
107         /// <summary>
108         /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that
109         /// stream when the returned <c>CodedOutputStream</c> is disposed.
110         /// </summary>
111         /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
CodedOutputStream(Stream output)112         public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
113         {
114         }
115 
116         /// <summary>
117         /// Creates a new CodedOutputStream which write to the given stream and uses
118         /// the specified buffer size.
119         /// </summary>
120         /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
121         /// <param name="bufferSize">The size of buffer to use internally.</param>
CodedOutputStream(Stream output, int bufferSize)122         public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
123         {
124         }
125 
126         /// <summary>
127         /// Creates a new CodedOutputStream which write to the given stream.
128         /// </summary>
129         /// <param name="output">The stream to write to.</param>
130         /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
131         /// if <c>false</c>, the provided stream is disposed as well.</param>
CodedOutputStream(Stream output, bool leaveOpen)132         public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)
133         {
134         }
135 
136         /// <summary>
137         /// Creates a new CodedOutputStream which write to the given stream and uses
138         /// the specified buffer size.
139         /// </summary>
140         /// <param name="output">The stream to write to.</param>
141         /// <param name="bufferSize">The size of buffer to use internally.</param>
142         /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
143         /// if <c>false</c>, the provided stream is disposed as well.</param>
CodedOutputStream(Stream output, int bufferSize, bool leaveOpen)144         public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
145         {
146         }
147         #endregion
148 
149         /// <summary>
150         /// Returns the current position in the stream, or the position in the output buffer
151         /// </summary>
152         public long Position
153         {
154             get
155             {
156                 if (output != null)
157                 {
158                     return output.Position + position;
159                 }
160                 return position;
161             }
162         }
163 
164         #region Writing of values (not including tags)
165 
166         /// <summary>
167         /// Writes a double field value, without a tag, to the stream.
168         /// </summary>
169         /// <param name="value">The value to write</param>
WriteDouble(double value)170         public void WriteDouble(double value)
171         {
172             WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
173         }
174 
175         /// <summary>
176         /// Writes a float field value, without a tag, to the stream.
177         /// </summary>
178         /// <param name="value">The value to write</param>
WriteFloat(float value)179         public void WriteFloat(float value)
180         {
181             byte[] rawBytes = BitConverter.GetBytes(value);
182             if (!BitConverter.IsLittleEndian)
183             {
184                 ByteArray.Reverse(rawBytes);
185             }
186 
187             if (limit - position >= 4)
188             {
189                 buffer[position++] = rawBytes[0];
190                 buffer[position++] = rawBytes[1];
191                 buffer[position++] = rawBytes[2];
192                 buffer[position++] = rawBytes[3];
193             }
194             else
195             {
196                 WriteRawBytes(rawBytes, 0, 4);
197             }
198         }
199 
200         /// <summary>
201         /// Writes a uint64 field value, without a tag, to the stream.
202         /// </summary>
203         /// <param name="value">The value to write</param>
WriteUInt64(ulong value)204         public void WriteUInt64(ulong value)
205         {
206             WriteRawVarint64(value);
207         }
208 
209         /// <summary>
210         /// Writes an int64 field value, without a tag, to the stream.
211         /// </summary>
212         /// <param name="value">The value to write</param>
WriteInt64(long value)213         public void WriteInt64(long value)
214         {
215             WriteRawVarint64((ulong) value);
216         }
217 
218         /// <summary>
219         /// Writes an int32 field value, without a tag, to the stream.
220         /// </summary>
221         /// <param name="value">The value to write</param>
WriteInt32(int value)222         public void WriteInt32(int value)
223         {
224             if (value >= 0)
225             {
226                 WriteRawVarint32((uint) value);
227             }
228             else
229             {
230                 // Must sign-extend.
231                 WriteRawVarint64((ulong) value);
232             }
233         }
234 
235         /// <summary>
236         /// Writes a fixed64 field value, without a tag, to the stream.
237         /// </summary>
238         /// <param name="value">The value to write</param>
WriteFixed64(ulong value)239         public void WriteFixed64(ulong value)
240         {
241             WriteRawLittleEndian64(value);
242         }
243 
244         /// <summary>
245         /// Writes a fixed32 field value, without a tag, to the stream.
246         /// </summary>
247         /// <param name="value">The value to write</param>
WriteFixed32(uint value)248         public void WriteFixed32(uint value)
249         {
250             WriteRawLittleEndian32(value);
251         }
252 
253         /// <summary>
254         /// Writes a bool field value, without a tag, to the stream.
255         /// </summary>
256         /// <param name="value">The value to write</param>
WriteBool(bool value)257         public void WriteBool(bool value)
258         {
259             WriteRawByte(value ? (byte) 1 : (byte) 0);
260         }
261 
262         /// <summary>
263         /// Writes a string field value, without a tag, to the stream.
264         /// The data is length-prefixed.
265         /// </summary>
266         /// <param name="value">The value to write</param>
WriteString(string value)267         public void WriteString(string value)
268         {
269             // Optimise the case where we have enough space to write
270             // the string directly to the buffer, which should be common.
271             int length = Utf8Encoding.GetByteCount(value);
272             WriteLength(length);
273             if (limit - position >= length)
274             {
275                 if (length == value.Length) // Must be all ASCII...
276                 {
277                     for (int i = 0; i < length; i++)
278                     {
279                         buffer[position + i] = (byte)value[i];
280                     }
281                 }
282                 else
283                 {
284                     Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
285                 }
286                 position += length;
287             }
288             else
289             {
290                 byte[] bytes = Utf8Encoding.GetBytes(value);
291                 WriteRawBytes(bytes);
292             }
293         }
294 
295         /// <summary>
296         /// Writes a message, without a tag, to the stream.
297         /// The data is length-prefixed.
298         /// </summary>
299         /// <param name="value">The value to write</param>
WriteMessage(IMessage value)300         public void WriteMessage(IMessage value)
301         {
302             WriteLength(value.CalculateSize());
303             value.WriteTo(this);
304         }
305 
306         /// <summary>
307         /// Writes a group, without a tag, to the stream.
308         /// </summary>
309         /// <param name="value">The value to write</param>
WriteGroup(IMessage value)310         public void WriteGroup(IMessage value)
311         {
312             value.WriteTo(this);
313         }
314 
315         /// <summary>
316         /// Write a byte string, without a tag, to the stream.
317         /// The data is length-prefixed.
318         /// </summary>
319         /// <param name="value">The value to write</param>
WriteBytes(ByteString value)320         public void WriteBytes(ByteString value)
321         {
322             WriteLength(value.Length);
323             value.WriteRawBytesTo(this);
324         }
325 
326         /// <summary>
327         /// Writes a uint32 value, without a tag, to the stream.
328         /// </summary>
329         /// <param name="value">The value to write</param>
WriteUInt32(uint value)330         public void WriteUInt32(uint value)
331         {
332             WriteRawVarint32(value);
333         }
334 
335         /// <summary>
336         /// Writes an enum value, without a tag, to the stream.
337         /// </summary>
338         /// <param name="value">The value to write</param>
WriteEnum(int value)339         public void WriteEnum(int value)
340         {
341             WriteInt32(value);
342         }
343 
344         /// <summary>
345         /// Writes an sfixed32 value, without a tag, to the stream.
346         /// </summary>
347         /// <param name="value">The value to write.</param>
WriteSFixed32(int value)348         public void WriteSFixed32(int value)
349         {
350             WriteRawLittleEndian32((uint) value);
351         }
352 
353         /// <summary>
354         /// Writes an sfixed64 value, without a tag, to the stream.
355         /// </summary>
356         /// <param name="value">The value to write</param>
WriteSFixed64(long value)357         public void WriteSFixed64(long value)
358         {
359             WriteRawLittleEndian64((ulong) value);
360         }
361 
362         /// <summary>
363         /// Writes an sint32 value, without a tag, to the stream.
364         /// </summary>
365         /// <param name="value">The value to write</param>
WriteSInt32(int value)366         public void WriteSInt32(int value)
367         {
368             WriteRawVarint32(EncodeZigZag32(value));
369         }
370 
371         /// <summary>
372         /// Writes an sint64 value, without a tag, to the stream.
373         /// </summary>
374         /// <param name="value">The value to write</param>
WriteSInt64(long value)375         public void WriteSInt64(long value)
376         {
377             WriteRawVarint64(EncodeZigZag64(value));
378         }
379 
380         /// <summary>
381         /// Writes a length (in bytes) for length-delimited data.
382         /// </summary>
383         /// <remarks>
384         /// This method simply writes a rawint, but exists for clarity in calling code.
385         /// </remarks>
386         /// <param name="length">Length value, in bytes.</param>
WriteLength(int length)387         public void WriteLength(int length)
388         {
389             WriteRawVarint32((uint) length);
390         }
391 
392         #endregion
393 
394         #region Raw tag writing
395         /// <summary>
396         /// Encodes and writes a tag.
397         /// </summary>
398         /// <param name="fieldNumber">The number of the field to write the tag for</param>
399         /// <param name="type">The wire format type of the tag to write</param>
WriteTag(int fieldNumber, WireFormat.WireType type)400         public void WriteTag(int fieldNumber, WireFormat.WireType type)
401         {
402             WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
403         }
404 
405         /// <summary>
406         /// Writes an already-encoded tag.
407         /// </summary>
408         /// <param name="tag">The encoded tag</param>
WriteTag(uint tag)409         public void WriteTag(uint tag)
410         {
411             WriteRawVarint32(tag);
412         }
413 
414         /// <summary>
415         /// Writes the given single-byte tag directly to the stream.
416         /// </summary>
417         /// <param name="b1">The encoded tag</param>
WriteRawTag(byte b1)418         public void WriteRawTag(byte b1)
419         {
420             WriteRawByte(b1);
421         }
422 
423         /// <summary>
424         /// Writes the given two-byte tag directly to the stream.
425         /// </summary>
426         /// <param name="b1">The first byte of the encoded tag</param>
427         /// <param name="b2">The second byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2)428         public void WriteRawTag(byte b1, byte b2)
429         {
430             WriteRawByte(b1);
431             WriteRawByte(b2);
432         }
433 
434         /// <summary>
435         /// Writes the given three-byte tag directly to the stream.
436         /// </summary>
437         /// <param name="b1">The first byte of the encoded tag</param>
438         /// <param name="b2">The second byte of the encoded tag</param>
439         /// <param name="b3">The third byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3)440         public void WriteRawTag(byte b1, byte b2, byte b3)
441         {
442             WriteRawByte(b1);
443             WriteRawByte(b2);
444             WriteRawByte(b3);
445         }
446 
447         /// <summary>
448         /// Writes the given four-byte tag directly to the stream.
449         /// </summary>
450         /// <param name="b1">The first byte of the encoded tag</param>
451         /// <param name="b2">The second byte of the encoded tag</param>
452         /// <param name="b3">The third byte of the encoded tag</param>
453         /// <param name="b4">The fourth byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3, byte b4)454         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
455         {
456             WriteRawByte(b1);
457             WriteRawByte(b2);
458             WriteRawByte(b3);
459             WriteRawByte(b4);
460         }
461 
462         /// <summary>
463         /// Writes the given five-byte tag directly to the stream.
464         /// </summary>
465         /// <param name="b1">The first byte of the encoded tag</param>
466         /// <param name="b2">The second byte of the encoded tag</param>
467         /// <param name="b3">The third byte of the encoded tag</param>
468         /// <param name="b4">The fourth byte of the encoded tag</param>
469         /// <param name="b5">The fifth byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)470         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
471         {
472             WriteRawByte(b1);
473             WriteRawByte(b2);
474             WriteRawByte(b3);
475             WriteRawByte(b4);
476             WriteRawByte(b5);
477         }
478         #endregion
479 
480         #region Underlying writing primitives
481         /// <summary>
482         /// Writes a 32 bit value as a varint. The fast route is taken when
483         /// there's enough buffer space left to whizz through without checking
484         /// for each byte; otherwise, we resort to calling WriteRawByte each time.
485         /// </summary>
WriteRawVarint32(uint value)486         internal void WriteRawVarint32(uint value)
487         {
488             // Optimize for the common case of a single byte value
489             if (value < 128 && position < limit)
490             {
491                 buffer[position++] = (byte)value;
492                 return;
493             }
494 
495             while (value > 127 && position < limit)
496             {
497                 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
498                 value >>= 7;
499             }
500             while (value > 127)
501             {
502                 WriteRawByte((byte) ((value & 0x7F) | 0x80));
503                 value >>= 7;
504             }
505             if (position < limit)
506             {
507                 buffer[position++] = (byte) value;
508             }
509             else
510             {
511                 WriteRawByte((byte) value);
512             }
513         }
514 
WriteRawVarint64(ulong value)515         internal void WriteRawVarint64(ulong value)
516         {
517             while (value > 127 && position < limit)
518             {
519                 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
520                 value >>= 7;
521             }
522             while (value > 127)
523             {
524                 WriteRawByte((byte) ((value & 0x7F) | 0x80));
525                 value >>= 7;
526             }
527             if (position < limit)
528             {
529                 buffer[position++] = (byte) value;
530             }
531             else
532             {
533                 WriteRawByte((byte) value);
534             }
535         }
536 
WriteRawLittleEndian32(uint value)537         internal void WriteRawLittleEndian32(uint value)
538         {
539             if (position + 4 > limit)
540             {
541                 WriteRawByte((byte) value);
542                 WriteRawByte((byte) (value >> 8));
543                 WriteRawByte((byte) (value >> 16));
544                 WriteRawByte((byte) (value >> 24));
545             }
546             else
547             {
548                 buffer[position++] = ((byte) value);
549                 buffer[position++] = ((byte) (value >> 8));
550                 buffer[position++] = ((byte) (value >> 16));
551                 buffer[position++] = ((byte) (value >> 24));
552             }
553         }
554 
WriteRawLittleEndian64(ulong value)555         internal void WriteRawLittleEndian64(ulong value)
556         {
557             if (position + 8 > limit)
558             {
559                 WriteRawByte((byte) value);
560                 WriteRawByte((byte) (value >> 8));
561                 WriteRawByte((byte) (value >> 16));
562                 WriteRawByte((byte) (value >> 24));
563                 WriteRawByte((byte) (value >> 32));
564                 WriteRawByte((byte) (value >> 40));
565                 WriteRawByte((byte) (value >> 48));
566                 WriteRawByte((byte) (value >> 56));
567             }
568             else
569             {
570                 buffer[position++] = ((byte) value);
571                 buffer[position++] = ((byte) (value >> 8));
572                 buffer[position++] = ((byte) (value >> 16));
573                 buffer[position++] = ((byte) (value >> 24));
574                 buffer[position++] = ((byte) (value >> 32));
575                 buffer[position++] = ((byte) (value >> 40));
576                 buffer[position++] = ((byte) (value >> 48));
577                 buffer[position++] = ((byte) (value >> 56));
578             }
579         }
580 
WriteRawByte(byte value)581         internal void WriteRawByte(byte value)
582         {
583             if (position == limit)
584             {
585                 RefreshBuffer();
586             }
587 
588             buffer[position++] = value;
589         }
590 
WriteRawByte(uint value)591         internal void WriteRawByte(uint value)
592         {
593             WriteRawByte((byte) value);
594         }
595 
596         /// <summary>
597         /// Writes out an array of bytes.
598         /// </summary>
WriteRawBytes(byte[] value)599         internal void WriteRawBytes(byte[] value)
600         {
601             WriteRawBytes(value, 0, value.Length);
602         }
603 
604         /// <summary>
605         /// Writes out part of an array of bytes.
606         /// </summary>
WriteRawBytes(byte[] value, int offset, int length)607         internal void WriteRawBytes(byte[] value, int offset, int length)
608         {
609             if (limit - position >= length)
610             {
611                 ByteArray.Copy(value, offset, buffer, position, length);
612                 // We have room in the current buffer.
613                 position += length;
614             }
615             else
616             {
617                 // Write extends past current buffer.  Fill the rest of this buffer and
618                 // flush.
619                 int bytesWritten = limit - position;
620                 ByteArray.Copy(value, offset, buffer, position, bytesWritten);
621                 offset += bytesWritten;
622                 length -= bytesWritten;
623                 position = limit;
624                 RefreshBuffer();
625 
626                 // Now deal with the rest.
627                 // Since we have an output stream, this is our buffer
628                 // and buffer offset == 0
629                 if (length <= limit)
630                 {
631                     // Fits in new buffer.
632                     ByteArray.Copy(value, offset, buffer, 0, length);
633                     position = length;
634                 }
635                 else
636                 {
637                     // Write is very big.  Let's do it all at once.
638                     output.Write(value, offset, length);
639                 }
640             }
641         }
642 
643         #endregion
644 
645         /// <summary>
646         /// Encode a 32-bit value with ZigZag encoding.
647         /// </summary>
648         /// <remarks>
649         /// ZigZag encodes signed integers into values that can be efficiently
650         /// encoded with varint.  (Otherwise, negative values must be
651         /// sign-extended to 64 bits to be varint encoded, thus always taking
652         /// 10 bytes on the wire.)
653         /// </remarks>
EncodeZigZag32(int n)654         internal static uint EncodeZigZag32(int n)
655         {
656             // Note:  the right-shift must be arithmetic
657             return (uint) ((n << 1) ^ (n >> 31));
658         }
659 
660         /// <summary>
661         /// Encode a 64-bit value with ZigZag encoding.
662         /// </summary>
663         /// <remarks>
664         /// ZigZag encodes signed integers into values that can be efficiently
665         /// encoded with varint.  (Otherwise, negative values must be
666         /// sign-extended to 64 bits to be varint encoded, thus always taking
667         /// 10 bytes on the wire.)
668         /// </remarks>
EncodeZigZag64(long n)669         internal static ulong EncodeZigZag64(long n)
670         {
671             return (ulong) ((n << 1) ^ (n >> 63));
672         }
673 
RefreshBuffer()674         private void RefreshBuffer()
675         {
676             if (output == null)
677             {
678                 // We're writing to a single buffer.
679                 throw new OutOfSpaceException();
680             }
681 
682             // Since we have an output stream, this is our buffer
683             // and buffer offset == 0
684             output.Write(buffer, 0, position);
685             position = 0;
686         }
687 
688         /// <summary>
689         /// Indicates that a CodedOutputStream wrapping a flat byte array
690         /// ran out of space.
691         /// </summary>
692         public sealed class OutOfSpaceException : IOException
693         {
OutOfSpaceException()694             internal OutOfSpaceException()
695                 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
696             {
697             }
698         }
699 
700         /// <summary>
701         /// Flushes any buffered data and optionally closes the underlying stream, if any.
702         /// </summary>
703         /// <remarks>
704         /// <para>
705         /// By default, any underlying stream is closed by this method. To configure this behaviour,
706         /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not
707         /// have an underlying stream, this method does nothing.
708         /// </para>
709         /// <para>
710         /// For the sake of efficiency, calling this method does not prevent future write calls - but
711         /// if a later write ends up writing to a stream which has been disposed, that is likely to
712         /// fail. It is recommend that you not call any other methods after this.
713         /// </para>
714         /// </remarks>
Dispose()715         public void Dispose()
716         {
717             Flush();
718             if (!leaveOpen)
719             {
720                 output.Dispose();
721             }
722         }
723 
724         /// <summary>
725         /// Flushes any buffered data to the underlying stream (if there is one).
726         /// </summary>
Flush()727         public void Flush()
728         {
729             if (output != null)
730             {
731                 RefreshBuffer();
732             }
733         }
734 
735         /// <summary>
736         /// Verifies that SpaceLeft returns zero. It's common to create a byte array
737         /// that is exactly big enough to hold a message, then write to it with
738         /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
739         /// the message was actually as big as expected, which can help bugs.
740         /// </summary>
CheckNoSpaceLeft()741         public void CheckNoSpaceLeft()
742         {
743             if (SpaceLeft != 0)
744             {
745                 throw new InvalidOperationException("Did not write as much data as expected.");
746             }
747         }
748 
749         /// <summary>
750         /// If writing to a flat array, returns the space left in the array. Otherwise,
751         /// throws an InvalidOperationException.
752         /// </summary>
753         public int SpaceLeft
754         {
755             get
756             {
757                 if (output == null)
758                 {
759                     return limit - position;
760                 }
761                 else
762                 {
763                     throw new InvalidOperationException(
764                         "SpaceLeft can only be called on CodedOutputStreams that are " +
765                         "writing to a flat array.");
766                 }
767             }
768         }
769     }
770 }
771