• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;T&gt;</c>
31     /// and <c>MapField&lt;TKey, TValue&gt;</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