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 /// 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