blob: 6211aac32cb173712656f587705b4036a2140775 [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001#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
33using Google.Protobuf.Collections;
34using System;
35using System.IO;
36using System.Text;
37
38namespace 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>
Austin Schuh40c16522018-10-28 20:27:54 -070058 public sealed partial class CodedOutputStream : IDisposable
Brian Silverman9c614bc2016-02-15 20:20:02 -050059 {
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
Austin Schuh40c16522018-10-28 20:27:54 -070068 private readonly bool leaveOpen;
Brian Silverman9c614bc2016-02-15 20:20:02 -050069 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>
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>
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;
Austin Schuh40c16522018-10-28 20:27:54 -070095 leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
Brian Silverman9c614bc2016-02-15 20:20:02 -050096 }
97
Austin Schuh40c16522018-10-28 20:27:54 -070098 private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
Brian Silverman9c614bc2016-02-15 20:20:02 -050099 {
Austin Schuh40c16522018-10-28 20:27:54 -0700100 this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
Brian Silverman9c614bc2016-02-15 20:20:02 -0500101 this.buffer = buffer;
102 this.position = 0;
103 this.limit = buffer.Length;
Austin Schuh40c16522018-10-28 20:27:54 -0700104 this.leaveOpen = leaveOpen;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500105 }
106
107 /// <summary>
Austin Schuh40c16522018-10-28 20:27:54 -0700108 /// 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.
Brian Silverman9c614bc2016-02-15 20:20:02 -0500110 /// </summary>
Austin Schuh40c16522018-10-28 20:27:54 -0700111 /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
112 public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
Brian Silverman9c614bc2016-02-15 20:20:02 -0500113 {
114 }
115
116 /// <summary>
117 /// Creates a new CodedOutputStream which write to the given stream and uses
118 /// the specified buffer size.
119 /// </summary>
Austin Schuh40c16522018-10-28 20:27:54 -0700120 /// <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>
122 public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
Brian Silverman9c614bc2016-02-15 20:20:02 -0500123 {
Austin Schuh40c16522018-10-28 20:27:54 -0700124 }
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>
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>
144 public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
145 {
146 }
Brian Silverman9c614bc2016-02-15 20:20:02 -0500147 #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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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
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
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
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
572 internal void WriteRawByte(byte value)
573 {
574 if (position == limit)
575 {
576 RefreshBuffer();
577 }
578
579 buffer[position++] = value;
580 }
581
582 internal void WriteRawByte(uint value)
583 {
584 WriteRawByte((byte) value);
585 }
586
587 /// <summary>
588 /// Writes out an array of bytes.
589 /// </summary>
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>
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>
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>
660 internal static ulong EncodeZigZag64(long n)
661 {
662 return (ulong) ((n << 1) ^ (n >> 63));
663 }
664
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 {
685 internal OutOfSpaceException()
686 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
687 {
688 }
689 }
690
691 /// <summary>
Austin Schuh40c16522018-10-28 20:27:54 -0700692 /// 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>
706 public void Dispose()
707 {
708 Flush();
709 if (!leaveOpen)
710 {
711 output.Dispose();
712 }
713 }
714
715 /// <summary>
Brian Silverman9c614bc2016-02-15 20:20:02 -0500716 /// Flushes any buffered data to the underlying stream (if there is one).
717 /// </summary>
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>
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 }
Austin Schuh40c16522018-10-28 20:27:54 -0700761}