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<T></c> 55 /// and <c>MapField<TKey, TValue></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