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