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
33using Google.Protobuf.Collections;
34using System;
35using System.IO;
36using System.Security;
37using System.Text;
38
39namespace 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>
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>
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
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
496        internal void WriteRawVarint32(uint value)
497        {
498            var span = new Span<byte>(buffer);
499            WritingPrimitives.WriteRawVarint32(ref span, ref state, value);
500        }
501
502        internal void WriteRawVarint64(ulong value)
503        {
504            var span = new Span<byte>(buffer);
505            WritingPrimitives.WriteRawVarint64(ref span, ref state, value);
506        }
507
508        internal void WriteRawLittleEndian32(uint value)
509        {
510            var span = new Span<byte>(buffer);
511            WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value);
512        }
513
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>
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>
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        {
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>
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>
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>
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