• 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         /// Write a byte string, without a tag, to the stream.
308         /// The data is length-prefixed.
309         /// </summary>
310         /// <param name="value">The value to write</param>
WriteBytes(ByteString value)311         public void WriteBytes(ByteString value)
312         {
313             WriteLength(value.Length);
314             value.WriteRawBytesTo(this);
315         }
316 
317         /// <summary>
318         /// Writes a uint32 value, without a tag, to the stream.
319         /// </summary>
320         /// <param name="value">The value to write</param>
WriteUInt32(uint value)321         public void WriteUInt32(uint value)
322         {
323             WriteRawVarint32(value);
324         }
325 
326         /// <summary>
327         /// Writes an enum value, without a tag, to the stream.
328         /// </summary>
329         /// <param name="value">The value to write</param>
WriteEnum(int value)330         public void WriteEnum(int value)
331         {
332             WriteInt32(value);
333         }
334 
335         /// <summary>
336         /// Writes an sfixed32 value, without a tag, to the stream.
337         /// </summary>
338         /// <param name="value">The value to write.</param>
WriteSFixed32(int value)339         public void WriteSFixed32(int value)
340         {
341             WriteRawLittleEndian32((uint) value);
342         }
343 
344         /// <summary>
345         /// Writes an sfixed64 value, without a tag, to the stream.
346         /// </summary>
347         /// <param name="value">The value to write</param>
WriteSFixed64(long value)348         public void WriteSFixed64(long value)
349         {
350             WriteRawLittleEndian64((ulong) value);
351         }
352 
353         /// <summary>
354         /// Writes an sint32 value, without a tag, to the stream.
355         /// </summary>
356         /// <param name="value">The value to write</param>
WriteSInt32(int value)357         public void WriteSInt32(int value)
358         {
359             WriteRawVarint32(EncodeZigZag32(value));
360         }
361 
362         /// <summary>
363         /// Writes an sint64 value, without a tag, to the stream.
364         /// </summary>
365         /// <param name="value">The value to write</param>
WriteSInt64(long value)366         public void WriteSInt64(long value)
367         {
368             WriteRawVarint64(EncodeZigZag64(value));
369         }
370 
371         /// <summary>
372         /// Writes a length (in bytes) for length-delimited data.
373         /// </summary>
374         /// <remarks>
375         /// This method simply writes a rawint, but exists for clarity in calling code.
376         /// </remarks>
377         /// <param name="length">Length value, in bytes.</param>
WriteLength(int length)378         public void WriteLength(int length)
379         {
380             WriteRawVarint32((uint) length);
381         }
382 
383         #endregion
384 
385         #region Raw tag writing
386         /// <summary>
387         /// Encodes and writes a tag.
388         /// </summary>
389         /// <param name="fieldNumber">The number of the field to write the tag for</param>
390         /// <param name="type">The wire format type of the tag to write</param>
WriteTag(int fieldNumber, WireFormat.WireType type)391         public void WriteTag(int fieldNumber, WireFormat.WireType type)
392         {
393             WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
394         }
395 
396         /// <summary>
397         /// Writes an already-encoded tag.
398         /// </summary>
399         /// <param name="tag">The encoded tag</param>
WriteTag(uint tag)400         public void WriteTag(uint tag)
401         {
402             WriteRawVarint32(tag);
403         }
404 
405         /// <summary>
406         /// Writes the given single-byte tag directly to the stream.
407         /// </summary>
408         /// <param name="b1">The encoded tag</param>
WriteRawTag(byte b1)409         public void WriteRawTag(byte b1)
410         {
411             WriteRawByte(b1);
412         }
413 
414         /// <summary>
415         /// Writes the given two-byte tag directly to the stream.
416         /// </summary>
417         /// <param name="b1">The first byte of the encoded tag</param>
418         /// <param name="b2">The second byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2)419         public void WriteRawTag(byte b1, byte b2)
420         {
421             WriteRawByte(b1);
422             WriteRawByte(b2);
423         }
424 
425         /// <summary>
426         /// Writes the given three-byte tag directly to the stream.
427         /// </summary>
428         /// <param name="b1">The first byte of the encoded tag</param>
429         /// <param name="b2">The second byte of the encoded tag</param>
430         /// <param name="b3">The third byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3)431         public void WriteRawTag(byte b1, byte b2, byte b3)
432         {
433             WriteRawByte(b1);
434             WriteRawByte(b2);
435             WriteRawByte(b3);
436         }
437 
438         /// <summary>
439         /// Writes the given four-byte tag directly to the stream.
440         /// </summary>
441         /// <param name="b1">The first byte of the encoded tag</param>
442         /// <param name="b2">The second byte of the encoded tag</param>
443         /// <param name="b3">The third byte of the encoded tag</param>
444         /// <param name="b4">The fourth byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3, byte b4)445         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
446         {
447             WriteRawByte(b1);
448             WriteRawByte(b2);
449             WriteRawByte(b3);
450             WriteRawByte(b4);
451         }
452 
453         /// <summary>
454         /// Writes the given five-byte tag directly to the stream.
455         /// </summary>
456         /// <param name="b1">The first byte of the encoded tag</param>
457         /// <param name="b2">The second byte of the encoded tag</param>
458         /// <param name="b3">The third byte of the encoded tag</param>
459         /// <param name="b4">The fourth byte of the encoded tag</param>
460         /// <param name="b5">The fifth byte of the encoded tag</param>
WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)461         public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
462         {
463             WriteRawByte(b1);
464             WriteRawByte(b2);
465             WriteRawByte(b3);
466             WriteRawByte(b4);
467             WriteRawByte(b5);
468         }
469         #endregion
470 
471         #region Underlying writing primitives
472         /// <summary>
473         /// Writes a 32 bit value as a varint. The fast route is taken when
474         /// there's enough buffer space left to whizz through without checking
475         /// for each byte; otherwise, we resort to calling WriteRawByte each time.
476         /// </summary>
WriteRawVarint32(uint value)477         internal void WriteRawVarint32(uint value)
478         {
479             // Optimize for the common case of a single byte value
480             if (value < 128 && position < limit)
481             {
482                 buffer[position++] = (byte)value;
483                 return;
484             }
485 
486             while (value > 127 && position < limit)
487             {
488                 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
489                 value >>= 7;
490             }
491             while (value > 127)
492             {
493                 WriteRawByte((byte) ((value & 0x7F) | 0x80));
494                 value >>= 7;
495             }
496             if (position < limit)
497             {
498                 buffer[position++] = (byte) value;
499             }
500             else
501             {
502                 WriteRawByte((byte) value);
503             }
504         }
505 
WriteRawVarint64(ulong value)506         internal void WriteRawVarint64(ulong value)
507         {
508             while (value > 127 && position < limit)
509             {
510                 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
511                 value >>= 7;
512             }
513             while (value > 127)
514             {
515                 WriteRawByte((byte) ((value & 0x7F) | 0x80));
516                 value >>= 7;
517             }
518             if (position < limit)
519             {
520                 buffer[position++] = (byte) value;
521             }
522             else
523             {
524                 WriteRawByte((byte) value);
525             }
526         }
527 
WriteRawLittleEndian32(uint value)528         internal void WriteRawLittleEndian32(uint value)
529         {
530             if (position + 4 > limit)
531             {
532                 WriteRawByte((byte) value);
533                 WriteRawByte((byte) (value >> 8));
534                 WriteRawByte((byte) (value >> 16));
535                 WriteRawByte((byte) (value >> 24));
536             }
537             else
538             {
539                 buffer[position++] = ((byte) value);
540                 buffer[position++] = ((byte) (value >> 8));
541                 buffer[position++] = ((byte) (value >> 16));
542                 buffer[position++] = ((byte) (value >> 24));
543             }
544         }
545 
WriteRawLittleEndian64(ulong value)546         internal void WriteRawLittleEndian64(ulong value)
547         {
548             if (position + 8 > limit)
549             {
550                 WriteRawByte((byte) value);
551                 WriteRawByte((byte) (value >> 8));
552                 WriteRawByte((byte) (value >> 16));
553                 WriteRawByte((byte) (value >> 24));
554                 WriteRawByte((byte) (value >> 32));
555                 WriteRawByte((byte) (value >> 40));
556                 WriteRawByte((byte) (value >> 48));
557                 WriteRawByte((byte) (value >> 56));
558             }
559             else
560             {
561                 buffer[position++] = ((byte) value);
562                 buffer[position++] = ((byte) (value >> 8));
563                 buffer[position++] = ((byte) (value >> 16));
564                 buffer[position++] = ((byte) (value >> 24));
565                 buffer[position++] = ((byte) (value >> 32));
566                 buffer[position++] = ((byte) (value >> 40));
567                 buffer[position++] = ((byte) (value >> 48));
568                 buffer[position++] = ((byte) (value >> 56));
569             }
570         }
571 
WriteRawByte(byte value)572         internal void WriteRawByte(byte value)
573         {
574             if (position == limit)
575             {
576                 RefreshBuffer();
577             }
578 
579             buffer[position++] = value;
580         }
581 
WriteRawByte(uint value)582         internal void WriteRawByte(uint value)
583         {
584             WriteRawByte((byte) value);
585         }
586 
587         /// <summary>
588         /// Writes out an array of bytes.
589         /// </summary>
WriteRawBytes(byte[] value)590         internal void WriteRawBytes(byte[] value)
591         {
592             WriteRawBytes(value, 0, value.Length);
593         }
594 
595         /// <summary>
596         /// Writes out part of an array of bytes.
597         /// </summary>
WriteRawBytes(byte[] value, int offset, int length)598         internal void WriteRawBytes(byte[] value, int offset, int length)
599         {
600             if (limit - position >= length)
601             {
602                 ByteArray.Copy(value, offset, buffer, position, length);
603                 // We have room in the current buffer.
604                 position += length;
605             }
606             else
607             {
608                 // Write extends past current buffer.  Fill the rest of this buffer and
609                 // flush.
610                 int bytesWritten = limit - position;
611                 ByteArray.Copy(value, offset, buffer, position, bytesWritten);
612                 offset += bytesWritten;
613                 length -= bytesWritten;
614                 position = limit;
615                 RefreshBuffer();
616 
617                 // Now deal with the rest.
618                 // Since we have an output stream, this is our buffer
619                 // and buffer offset == 0
620                 if (length <= limit)
621                 {
622                     // Fits in new buffer.
623                     ByteArray.Copy(value, offset, buffer, 0, length);
624                     position = length;
625                 }
626                 else
627                 {
628                     // Write is very big.  Let's do it all at once.
629                     output.Write(value, offset, length);
630                 }
631             }
632         }
633 
634         #endregion
635 
636         /// <summary>
637         /// Encode a 32-bit value with ZigZag encoding.
638         /// </summary>
639         /// <remarks>
640         /// ZigZag encodes signed integers into values that can be efficiently
641         /// encoded with varint.  (Otherwise, negative values must be
642         /// sign-extended to 64 bits to be varint encoded, thus always taking
643         /// 10 bytes on the wire.)
644         /// </remarks>
EncodeZigZag32(int n)645         internal static uint EncodeZigZag32(int n)
646         {
647             // Note:  the right-shift must be arithmetic
648             return (uint) ((n << 1) ^ (n >> 31));
649         }
650 
651         /// <summary>
652         /// Encode a 64-bit value with ZigZag encoding.
653         /// </summary>
654         /// <remarks>
655         /// ZigZag encodes signed integers into values that can be efficiently
656         /// encoded with varint.  (Otherwise, negative values must be
657         /// sign-extended to 64 bits to be varint encoded, thus always taking
658         /// 10 bytes on the wire.)
659         /// </remarks>
EncodeZigZag64(long n)660         internal static ulong EncodeZigZag64(long n)
661         {
662             return (ulong) ((n << 1) ^ (n >> 63));
663         }
664 
RefreshBuffer()665         private void RefreshBuffer()
666         {
667             if (output == null)
668             {
669                 // We're writing to a single buffer.
670                 throw new OutOfSpaceException();
671             }
672 
673             // Since we have an output stream, this is our buffer
674             // and buffer offset == 0
675             output.Write(buffer, 0, position);
676             position = 0;
677         }
678 
679         /// <summary>
680         /// Indicates that a CodedOutputStream wrapping a flat byte array
681         /// ran out of space.
682         /// </summary>
683         public sealed class OutOfSpaceException : IOException
684         {
OutOfSpaceException()685             internal OutOfSpaceException()
686                 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
687             {
688             }
689         }
690 
691         /// <summary>
692         /// Flushes any buffered data and optionally closes the underlying stream, if any.
693         /// </summary>
694         /// <remarks>
695         /// <para>
696         /// By default, any underlying stream is closed by this method. To configure this behaviour,
697         /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not
698         /// have an underlying stream, this method does nothing.
699         /// </para>
700         /// <para>
701         /// For the sake of efficiency, calling this method does not prevent future write calls - but
702         /// if a later write ends up writing to a stream which has been disposed, that is likely to
703         /// fail. It is recommend that you not call any other methods after this.
704         /// </para>
705         /// </remarks>
Dispose()706         public void Dispose()
707         {
708             Flush();
709             if (!leaveOpen)
710             {
711                 output.Dispose();
712             }
713         }
714 
715         /// <summary>
716         /// Flushes any buffered data to the underlying stream (if there is one).
717         /// </summary>
Flush()718         public void Flush()
719         {
720             if (output != null)
721             {
722                 RefreshBuffer();
723             }
724         }
725 
726         /// <summary>
727         /// Verifies that SpaceLeft returns zero. It's common to create a byte array
728         /// that is exactly big enough to hold a message, then write to it with
729         /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
730         /// the message was actually as big as expected, which can help bugs.
731         /// </summary>
CheckNoSpaceLeft()732         public void CheckNoSpaceLeft()
733         {
734             if (SpaceLeft != 0)
735             {
736                 throw new InvalidOperationException("Did not write as much data as expected.");
737             }
738         }
739 
740         /// <summary>
741         /// If writing to a flat array, returns the space left in the array. Otherwise,
742         /// throws an InvalidOperationException.
743         /// </summary>
744         public int SpaceLeft
745         {
746             get
747             {
748                 if (output == null)
749                 {
750                     return limit - position;
751                 }
752                 else
753                 {
754                     throw new InvalidOperationException(
755                         "SpaceLeft can only be called on CodedOutputStreams that are " +
756                         "writing to a flat array.");
757                 }
758             }
759         }
760     }
761 }
762