• 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 // 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&lt;T&gt;</c>
56     /// and <c>MapField&lt;TKey, TValue&gt;</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