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.Security; 37 using System.Text; 38 39 namespace Google.Protobuf 40 { 41 /// <summary> 42 /// Encodes and writes protocol message fields. 43 /// </summary> 44 /// <remarks> 45 /// <para> 46 /// This class is generally used by generated code to write appropriate 47 /// primitives to the stream. It effectively encapsulates the lowest 48 /// levels of protocol buffer format. Unlike some other implementations, 49 /// this does not include combined "write tag and value" methods. Generated 50 /// code knows the exact byte representations of the tags they're going to write, 51 /// so there's no need to re-encode them each time. Manually-written code calling 52 /// this class should just call one of the <c>WriteTag</c> overloads before each value. 53 /// </para> 54 /// <para> 55 /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField<T></c> 56 /// and <c>MapField<TKey, TValue></c> to serialize such fields. 57 /// </para> 58 /// </remarks> 59 [SecuritySafeCritical] 60 public sealed partial class CodedOutputStream : IDisposable 61 { 62 /// <summary> 63 /// The buffer size used by CreateInstance(Stream). 64 /// </summary> 65 public static readonly int DefaultBufferSize = 4096; 66 67 private readonly bool leaveOpen; 68 private readonly byte[] buffer; 69 private WriterInternalState state; 70 71 private readonly Stream output; 72 73 #region Construction 74 /// <summary> 75 /// Creates a new CodedOutputStream that writes directly to the given 76 /// byte array. If more bytes are written than fit in the array, 77 /// OutOfSpaceException will be thrown. 78 /// </summary> CodedOutputStream(byte[] flatArray)79 public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length) 80 { 81 } 82 83 /// <summary> 84 /// Creates a new CodedOutputStream that writes directly to the given 85 /// byte array slice. If more bytes are written than fit in the array, 86 /// OutOfSpaceException will be thrown. 87 /// </summary> CodedOutputStream(byte[] buffer, int offset, int length)88 private CodedOutputStream(byte[] buffer, int offset, int length) 89 { 90 this.output = null; 91 this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer)); 92 this.state.position = offset; 93 this.state.limit = offset + length; 94 WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper); 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.state.position = 0; 103 this.state.limit = buffer.Length; 104 WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper); 105 this.leaveOpen = leaveOpen; 106 } 107 108 /// <summary> 109 /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that 110 /// stream when the returned <c>CodedOutputStream</c> is disposed. 111 /// </summary> 112 /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param> CodedOutputStream(Stream output)113 public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false) 114 { 115 } 116 117 /// <summary> 118 /// Creates a new CodedOutputStream which write to the given stream and uses 119 /// the specified buffer size. 120 /// </summary> 121 /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param> 122 /// <param name="bufferSize">The size of buffer to use internally.</param> CodedOutputStream(Stream output, int bufferSize)123 public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false) 124 { 125 } 126 127 /// <summary> 128 /// Creates a new CodedOutputStream which write to the given stream. 129 /// </summary> 130 /// <param name="output">The stream to write to.</param> 131 /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed; 132 /// if <c>false</c>, the provided stream is disposed as well.</param> CodedOutputStream(Stream output, bool leaveOpen)133 public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen) 134 { 135 } 136 137 /// <summary> 138 /// Creates a new CodedOutputStream which write to the given stream and uses 139 /// the specified buffer size. 140 /// </summary> 141 /// <param name="output">The stream to write to.</param> 142 /// <param name="bufferSize">The size of buffer to use internally.</param> 143 /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed; 144 /// if <c>false</c>, the provided stream is disposed as well.</param> CodedOutputStream(Stream output, int bufferSize, bool leaveOpen)145 public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen) 146 { 147 } 148 #endregion 149 150 /// <summary> 151 /// Returns the current position in the stream, or the position in the output buffer 152 /// </summary> 153 public long Position 154 { 155 get 156 { 157 if (output != null) 158 { 159 return output.Position + state.position; 160 } 161 return state.position; 162 } 163 } 164 165 #region Writing of values (not including tags) 166 167 /// <summary> 168 /// Writes a double field value, without a tag, to the stream. 169 /// </summary> 170 /// <param name="value">The value to write</param> WriteDouble(double value)171 public void WriteDouble(double value) 172 { 173 var span = new Span<byte>(buffer); 174 WritingPrimitives.WriteDouble(ref span, ref state, value); 175 } 176 177 /// <summary> 178 /// Writes a float field value, without a tag, to the stream. 179 /// </summary> 180 /// <param name="value">The value to write</param> WriteFloat(float value)181 public void WriteFloat(float value) 182 { 183 var span = new Span<byte>(buffer); 184 WritingPrimitives.WriteFloat(ref span, ref state, value); 185 } 186 187 /// <summary> 188 /// Writes a uint64 field value, without a tag, to the stream. 189 /// </summary> 190 /// <param name="value">The value to write</param> WriteUInt64(ulong value)191 public void WriteUInt64(ulong value) 192 { 193 var span = new Span<byte>(buffer); 194 WritingPrimitives.WriteUInt64(ref span, ref state, value); 195 } 196 197 /// <summary> 198 /// Writes an int64 field value, without a tag, to the stream. 199 /// </summary> 200 /// <param name="value">The value to write</param> WriteInt64(long value)201 public void WriteInt64(long value) 202 { 203 var span = new Span<byte>(buffer); 204 WritingPrimitives.WriteInt64(ref span, ref state, value); 205 } 206 207 /// <summary> 208 /// Writes an int32 field value, without a tag, to the stream. 209 /// </summary> 210 /// <param name="value">The value to write</param> WriteInt32(int value)211 public void WriteInt32(int value) 212 { 213 var span = new Span<byte>(buffer); 214 WritingPrimitives.WriteInt32(ref span, ref state, value); 215 } 216 217 /// <summary> 218 /// Writes a fixed64 field value, without a tag, to the stream. 219 /// </summary> 220 /// <param name="value">The value to write</param> WriteFixed64(ulong value)221 public void WriteFixed64(ulong value) 222 { 223 var span = new Span<byte>(buffer); 224 WritingPrimitives.WriteFixed64(ref span, ref state, value); 225 } 226 227 /// <summary> 228 /// Writes a fixed32 field value, without a tag, to the stream. 229 /// </summary> 230 /// <param name="value">The value to write</param> WriteFixed32(uint value)231 public void WriteFixed32(uint value) 232 { 233 var span = new Span<byte>(buffer); 234 WritingPrimitives.WriteFixed32(ref span, ref state, value); 235 } 236 237 /// <summary> 238 /// Writes a bool field value, without a tag, to the stream. 239 /// </summary> 240 /// <param name="value">The value to write</param> WriteBool(bool value)241 public void WriteBool(bool value) 242 { 243 var span = new Span<byte>(buffer); 244 WritingPrimitives.WriteBool(ref span, ref state, value); 245 } 246 247 /// <summary> 248 /// Writes a string field value, without a tag, to the stream. 249 /// The data is length-prefixed. 250 /// </summary> 251 /// <param name="value">The value to write</param> WriteString(string value)252 public void WriteString(string value) 253 { 254 var span = new Span<byte>(buffer); 255 WritingPrimitives.WriteString(ref span, ref state, value); 256 } 257 258 /// <summary> 259 /// Writes a message, without a tag, to the stream. 260 /// The data is length-prefixed. 261 /// </summary> 262 /// <param name="value">The value to write</param> WriteMessage(IMessage value)263 public void WriteMessage(IMessage value) 264 { 265 // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method), 266 // what we're doing here works fine, but could be more efficient. 267 // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it). 268 var span = new Span<byte>(buffer); 269 WriteContext.Initialize(ref span, ref state, out WriteContext ctx); 270 try 271 { 272 WritingPrimitivesMessages.WriteMessage(ref ctx, value); 273 } 274 finally 275 { 276 ctx.CopyStateTo(this); 277 } 278 } 279 280 /// <summary> 281 /// Writes a message, without a tag, to the stream. 282 /// Only the message data is written, without a length-delimiter. 283 /// </summary> 284 /// <param name="value">The value to write</param> WriteRawMessage(IMessage value)285 public void WriteRawMessage(IMessage value) 286 { 287 // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method), 288 // what we're doing here works fine, but could be more efficient. 289 // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it). 290 var span = new Span<byte>(buffer); 291 WriteContext.Initialize(ref span, ref state, out WriteContext ctx); 292 try 293 { 294 WritingPrimitivesMessages.WriteRawMessage(ref ctx, value); 295 } 296 finally 297 { 298 ctx.CopyStateTo(this); 299 } 300 } 301 302 /// <summary> 303 /// Writes a group, without a tag, to the stream. 304 /// </summary> 305 /// <param name="value">The value to write</param> WriteGroup(IMessage value)306 public void WriteGroup(IMessage value) 307 { 308 var span = new Span<byte>(buffer); 309 WriteContext.Initialize(ref span, ref state, out WriteContext ctx); 310 try 311 { 312 WritingPrimitivesMessages.WriteGroup(ref ctx, value); 313 } 314 finally 315 { 316 ctx.CopyStateTo(this); 317 } 318 } 319 320 /// <summary> 321 /// Write a byte string, without a tag, to the stream. 322 /// The data is length-prefixed. 323 /// </summary> 324 /// <param name="value">The value to write</param> WriteBytes(ByteString value)325 public void WriteBytes(ByteString value) 326 { 327 var span = new Span<byte>(buffer); 328 WritingPrimitives.WriteBytes(ref span, ref state, value); 329 } 330 331 /// <summary> 332 /// Writes a uint32 value, without a tag, to the stream. 333 /// </summary> 334 /// <param name="value">The value to write</param> WriteUInt32(uint value)335 public void WriteUInt32(uint value) 336 { 337 var span = new Span<byte>(buffer); 338 WritingPrimitives.WriteUInt32(ref span, ref state, value); 339 } 340 341 /// <summary> 342 /// Writes an enum value, without a tag, to the stream. 343 /// </summary> 344 /// <param name="value">The value to write</param> WriteEnum(int value)345 public void WriteEnum(int value) 346 { 347 var span = new Span<byte>(buffer); 348 WritingPrimitives.WriteEnum(ref span, ref state, value); 349 } 350 351 /// <summary> 352 /// Writes an sfixed32 value, without a tag, to the stream. 353 /// </summary> 354 /// <param name="value">The value to write.</param> WriteSFixed32(int value)355 public void WriteSFixed32(int value) 356 { 357 var span = new Span<byte>(buffer); 358 WritingPrimitives.WriteSFixed32(ref span, ref state, value); 359 } 360 361 /// <summary> 362 /// Writes an sfixed64 value, without a tag, to the stream. 363 /// </summary> 364 /// <param name="value">The value to write</param> WriteSFixed64(long value)365 public void WriteSFixed64(long value) 366 { 367 var span = new Span<byte>(buffer); 368 WritingPrimitives.WriteSFixed64(ref span, ref state, value); 369 } 370 371 /// <summary> 372 /// Writes an sint32 value, without a tag, to the stream. 373 /// </summary> 374 /// <param name="value">The value to write</param> WriteSInt32(int value)375 public void WriteSInt32(int value) 376 { 377 var span = new Span<byte>(buffer); 378 WritingPrimitives.WriteSInt32(ref span, ref state, value); 379 } 380 381 /// <summary> 382 /// Writes an sint64 value, without a tag, to the stream. 383 /// </summary> 384 /// <param name="value">The value to write</param> WriteSInt64(long value)385 public void WriteSInt64(long value) 386 { 387 var span = new Span<byte>(buffer); 388 WritingPrimitives.WriteSInt64(ref span, ref state, value); 389 } 390 391 /// <summary> 392 /// Writes a length (in bytes) for length-delimited data. 393 /// </summary> 394 /// <remarks> 395 /// This method simply writes a rawint, but exists for clarity in calling code. 396 /// </remarks> 397 /// <param name="length">Length value, in bytes.</param> WriteLength(int length)398 public void WriteLength(int length) 399 { 400 var span = new Span<byte>(buffer); 401 WritingPrimitives.WriteLength(ref span, ref state, length); 402 } 403 404 #endregion 405 406 #region Raw tag writing 407 /// <summary> 408 /// Encodes and writes a tag. 409 /// </summary> 410 /// <param name="fieldNumber">The number of the field to write the tag for</param> 411 /// <param name="type">The wire format type of the tag to write</param> WriteTag(int fieldNumber, WireFormat.WireType type)412 public void WriteTag(int fieldNumber, WireFormat.WireType type) 413 { 414 var span = new Span<byte>(buffer); 415 WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type); 416 } 417 418 /// <summary> 419 /// Writes an already-encoded tag. 420 /// </summary> 421 /// <param name="tag">The encoded tag</param> WriteTag(uint tag)422 public void WriteTag(uint tag) 423 { 424 var span = new Span<byte>(buffer); 425 WritingPrimitives.WriteTag(ref span, ref state, tag); 426 } 427 428 /// <summary> 429 /// Writes the given single-byte tag directly to the stream. 430 /// </summary> 431 /// <param name="b1">The encoded tag</param> WriteRawTag(byte b1)432 public void WriteRawTag(byte b1) 433 { 434 var span = new Span<byte>(buffer); 435 WritingPrimitives.WriteRawTag(ref span, ref state, b1); 436 } 437 438 /// <summary> 439 /// Writes the given two-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> WriteRawTag(byte b1, byte b2)443 public void WriteRawTag(byte b1, byte b2) 444 { 445 var span = new Span<byte>(buffer); 446 WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2); 447 } 448 449 /// <summary> 450 /// Writes the given three-byte tag directly to the stream. 451 /// </summary> 452 /// <param name="b1">The first byte of the encoded tag</param> 453 /// <param name="b2">The second byte of the encoded tag</param> 454 /// <param name="b3">The third byte of the encoded tag</param> WriteRawTag(byte b1, byte b2, byte b3)455 public void WriteRawTag(byte b1, byte b2, byte b3) 456 { 457 var span = new Span<byte>(buffer); 458 WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3); 459 } 460 461 /// <summary> 462 /// Writes the given four-byte tag directly to the stream. 463 /// </summary> 464 /// <param name="b1">The first byte of the encoded tag</param> 465 /// <param name="b2">The second byte of the encoded tag</param> 466 /// <param name="b3">The third byte of the encoded tag</param> 467 /// <param name="b4">The fourth byte of the encoded tag</param> WriteRawTag(byte b1, byte b2, byte b3, byte b4)468 public void WriteRawTag(byte b1, byte b2, byte b3, byte b4) 469 { 470 var span = new Span<byte>(buffer); 471 WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4); 472 } 473 474 /// <summary> 475 /// Writes the given five-byte tag directly to the stream. 476 /// </summary> 477 /// <param name="b1">The first byte of the encoded tag</param> 478 /// <param name="b2">The second byte of the encoded tag</param> 479 /// <param name="b3">The third byte of the encoded tag</param> 480 /// <param name="b4">The fourth byte of the encoded tag</param> 481 /// <param name="b5">The fifth byte of the encoded tag</param> WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)482 public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5) 483 { 484 var span = new Span<byte>(buffer); 485 WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5); 486 } 487 #endregion 488 489 #region Underlying writing primitives 490 491 /// <summary> 492 /// Writes a 32 bit value as a varint. The fast route is taken when 493 /// there's enough buffer space left to whizz through without checking 494 /// for each byte; otherwise, we resort to calling WriteRawByte each time. 495 /// </summary> WriteRawVarint32(uint value)496 internal void WriteRawVarint32(uint value) 497 { 498 var span = new Span<byte>(buffer); 499 WritingPrimitives.WriteRawVarint32(ref span, ref state, value); 500 } 501 WriteRawVarint64(ulong value)502 internal void WriteRawVarint64(ulong value) 503 { 504 var span = new Span<byte>(buffer); 505 WritingPrimitives.WriteRawVarint64(ref span, ref state, value); 506 } 507 WriteRawLittleEndian32(uint value)508 internal void WriteRawLittleEndian32(uint value) 509 { 510 var span = new Span<byte>(buffer); 511 WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value); 512 } 513 WriteRawLittleEndian64(ulong value)514 internal void WriteRawLittleEndian64(ulong value) 515 { 516 var span = new Span<byte>(buffer); 517 WritingPrimitives.WriteRawLittleEndian64(ref span, ref state, value); 518 } 519 520 /// <summary> 521 /// Writes out an array of bytes. 522 /// </summary> WriteRawBytes(byte[] value)523 internal void WriteRawBytes(byte[] value) 524 { 525 WriteRawBytes(value, 0, value.Length); 526 } 527 528 /// <summary> 529 /// Writes out part of an array of bytes. 530 /// </summary> WriteRawBytes(byte[] value, int offset, int length)531 internal void WriteRawBytes(byte[] value, int offset, int length) 532 { 533 var span = new Span<byte>(buffer); 534 WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length); 535 } 536 537 #endregion 538 539 /// <summary> 540 /// Indicates that a CodedOutputStream wrapping a flat byte array 541 /// ran out of space. 542 /// </summary> 543 public sealed class OutOfSpaceException : IOException 544 { OutOfSpaceException()545 internal OutOfSpaceException() 546 : base("CodedOutputStream was writing to a flat byte array and ran out of space.") 547 { 548 } 549 } 550 551 /// <summary> 552 /// Flushes any buffered data and optionally closes the underlying stream, if any. 553 /// </summary> 554 /// <remarks> 555 /// <para> 556 /// By default, any underlying stream is closed by this method. To configure this behaviour, 557 /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not 558 /// have an underlying stream, this method does nothing. 559 /// </para> 560 /// <para> 561 /// For the sake of efficiency, calling this method does not prevent future write calls - but 562 /// if a later write ends up writing to a stream which has been disposed, that is likely to 563 /// fail. It is recommend that you not call any other methods after this. 564 /// </para> 565 /// </remarks> Dispose()566 public void Dispose() 567 { 568 Flush(); 569 if (!leaveOpen) 570 { 571 output.Dispose(); 572 } 573 } 574 575 /// <summary> 576 /// Flushes any buffered data to the underlying stream (if there is one). 577 /// </summary> Flush()578 public void Flush() 579 { 580 var span = new Span<byte>(buffer); 581 WriteBufferHelper.Flush(ref span, ref state); 582 } 583 584 /// <summary> 585 /// Verifies that SpaceLeft returns zero. It's common to create a byte array 586 /// that is exactly big enough to hold a message, then write to it with 587 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that 588 /// the message was actually as big as expected, which can help finding bugs. 589 /// </summary> CheckNoSpaceLeft()590 public void CheckNoSpaceLeft() 591 { 592 WriteBufferHelper.CheckNoSpaceLeft(ref state); 593 } 594 595 /// <summary> 596 /// If writing to a flat array, returns the space left in the array. Otherwise, 597 /// throws an InvalidOperationException. 598 /// </summary> 599 public int SpaceLeft => WriteBufferHelper.GetSpaceLeft(ref state); 600 601 internal byte[] InternalBuffer => buffer; 602 603 internal Stream InternalOutputStream => output; 604 605 internal ref WriterInternalState InternalState => ref state; 606 } 607 } 608