blob: 7f41cb7a62ac833f093b07dc666ad626b0615220 [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
csharptest71f662c2011-05-20 15:15:34 -05002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
Jon Skeetee835a32015-06-30 17:22:26 +01004// https://developers.google.com/protocol-buffers/
csharptest71f662c2011-05-20 15:15:34 -05005//
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.
csharptest71f662c2011-05-20 15:15:34 -050031#endregion
32
Jon Skeet6f300442015-08-06 14:29:34 +010033using Google.Protobuf.Collections;
csharptest71f662c2011-05-20 15:15:34 -050034using System;
35using System.IO;
36using System.Text;
csharptest71f662c2011-05-20 15:15:34 -050037
Jon Skeete38294a2015-06-09 19:30:44 +010038namespace Google.Protobuf
csharptest71f662c2011-05-20 15:15:34 -050039{
40 /// <summary>
41 /// Encodes and writes protocol message fields.
42 /// </summary>
43 /// <remarks>
Jon Skeet6f300442015-08-06 14:29:34 +010044 /// <para>
45 /// This class is generally used by generated code to write appropriate
46 /// primitives to the stream. It effectively encapsulates the lowest
47 /// levels of protocol buffer format. Unlike some other implementations,
48 /// this does not include combined "write tag and value" methods. Generated
49 /// code knows the exact byte representations of the tags they're going to write,
50 /// so there's no need to re-encode them each time. Manually-written code calling
51 /// this class should just call one of the <c>WriteTag</c> overloads before each value.
52 /// </para>
53 /// <para>
54 /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>
55 /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
56 /// </para>
csharptest71f662c2011-05-20 15:15:34 -050057 /// </remarks>
Jon Skeet96ddf012015-06-12 09:53:12 +010058 public sealed partial class CodedOutputStream
csharptest71f662c2011-05-20 15:15:34 -050059 {
Jon Skeeta0f95692015-06-17 15:34:29 +010060 // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
61 internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
Jon Skeet35e4dbd2015-06-11 14:19:30 +010062
csharptest71f662c2011-05-20 15:15:34 -050063 /// <summary>
64 /// The buffer size used by CreateInstance(Stream).
65 /// </summary>
66 public static readonly int DefaultBufferSize = 4096;
67
68 private readonly byte[] buffer;
69 private readonly int limit;
70 private int position;
71 private readonly Stream output;
72
73 #region Construction
Jon Skeet0e0e0c92015-08-03 11:08:53 +010074 /// <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 }
csharptest71f662c2011-05-20 15:15:34 -050082
Jon Skeet0e0e0c92015-08-03 11:08:53 +010083 /// <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>
csharptest71f662c2011-05-20 15:15:34 -050088 private CodedOutputStream(byte[] buffer, int offset, int length)
89 {
90 this.output = null;
91 this.buffer = buffer;
92 this.position = offset;
93 this.limit = offset + length;
94 }
95
96 private CodedOutputStream(Stream output, byte[] buffer)
97 {
98 this.output = output;
99 this.buffer = buffer;
100 this.position = 0;
101 this.limit = buffer.Length;
102 }
103
104 /// <summary>
105 /// Creates a new CodedOutputStream which write to the given stream.
106 /// </summary>
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100107 public CodedOutputStream(Stream output) : this(output, DefaultBufferSize)
csharptest71f662c2011-05-20 15:15:34 -0500108 {
csharptest71f662c2011-05-20 15:15:34 -0500109 }
110
111 /// <summary>
112 /// Creates a new CodedOutputStream which write to the given stream and uses
113 /// the specified buffer size.
114 /// </summary>
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100115 public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize])
csharptest71f662c2011-05-20 15:15:34 -0500116 {
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100117 }
csharptest71f662c2011-05-20 15:15:34 -0500118 #endregion
csharptest0a0dd032013-05-16 13:59:34 -0500119
120 /// <summary>
121 /// Returns the current position in the stream, or the position in the output buffer
122 /// </summary>
123 public long Position
124 {
125 get
126 {
127 if (output != null)
csharptestd63096d2013-09-21 13:51:04 -0500128 {
csharptest0a0dd032013-05-16 13:59:34 -0500129 return output.Position + position;
csharptestd63096d2013-09-21 13:51:04 -0500130 }
csharptest0a0dd032013-05-16 13:59:34 -0500131 return position;
132 }
133 }
134
Jon Skeetf34d37a2015-06-30 13:16:20 +0100135 #region Writing of values (not including tags)
csharptest71f662c2011-05-20 15:15:34 -0500136
csharptest71f662c2011-05-20 15:15:34 -0500137 /// <summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100138 /// Writes a double field value, without a tag, to the stream.
csharptest71f662c2011-05-20 15:15:34 -0500139 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100140 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100141 public void WriteDouble(double value)
csharptest71f662c2011-05-20 15:15:34 -0500142 {
Jon Skeetcdeda4b2015-06-19 17:30:13 +0100143 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
csharptest71f662c2011-05-20 15:15:34 -0500144 }
145
146 /// <summary>
147 /// Writes a float field value, without a tag, to the stream.
148 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100149 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100150 public void WriteFloat(float value)
csharptest71f662c2011-05-20 15:15:34 -0500151 {
csharptest71f662c2011-05-20 15:15:34 -0500152 byte[] rawBytes = BitConverter.GetBytes(value);
csharptestaef072a2011-06-08 18:00:43 -0500153 if (!BitConverter.IsLittleEndian)
csharptest74c5e0c2011-07-14 13:06:22 -0500154 {
csharptestaef072a2011-06-08 18:00:43 -0500155 ByteArray.Reverse(rawBytes);
csharptest74c5e0c2011-07-14 13:06:22 -0500156 }
csharptest2772dfe2011-06-08 15:50:58 -0500157
158 if (limit - position >= 4)
159 {
160 buffer[position++] = rawBytes[0];
161 buffer[position++] = rawBytes[1];
162 buffer[position++] = rawBytes[2];
163 buffer[position++] = rawBytes[3];
164 }
165 else
csharptest74c5e0c2011-07-14 13:06:22 -0500166 {
csharptest2772dfe2011-06-08 15:50:58 -0500167 WriteRawBytes(rawBytes, 0, 4);
csharptest74c5e0c2011-07-14 13:06:22 -0500168 }
csharptest71f662c2011-05-20 15:15:34 -0500169 }
170
171 /// <summary>
172 /// Writes a uint64 field value, without a tag, to the stream.
173 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100174 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100175 public void WriteUInt64(ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500176 {
177 WriteRawVarint64(value);
178 }
179
180 /// <summary>
181 /// Writes an int64 field value, without a tag, to the stream.
182 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100183 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100184 public void WriteInt64(long value)
csharptest71f662c2011-05-20 15:15:34 -0500185 {
csharptest74c5e0c2011-07-14 13:06:22 -0500186 WriteRawVarint64((ulong) value);
csharptest71f662c2011-05-20 15:15:34 -0500187 }
188
189 /// <summary>
190 /// Writes an int32 field value, without a tag, to the stream.
191 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100192 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100193 public void WriteInt32(int value)
csharptest71f662c2011-05-20 15:15:34 -0500194 {
195 if (value >= 0)
196 {
csharptest74c5e0c2011-07-14 13:06:22 -0500197 WriteRawVarint32((uint) value);
csharptest71f662c2011-05-20 15:15:34 -0500198 }
199 else
200 {
201 // Must sign-extend.
csharptest74c5e0c2011-07-14 13:06:22 -0500202 WriteRawVarint64((ulong) value);
csharptest71f662c2011-05-20 15:15:34 -0500203 }
204 }
205
206 /// <summary>
207 /// Writes a fixed64 field value, without a tag, to the stream.
208 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100209 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100210 public void WriteFixed64(ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500211 {
212 WriteRawLittleEndian64(value);
213 }
214
215 /// <summary>
216 /// Writes a fixed32 field value, without a tag, to the stream.
217 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100218 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100219 public void WriteFixed32(uint value)
csharptest71f662c2011-05-20 15:15:34 -0500220 {
221 WriteRawLittleEndian32(value);
222 }
223
224 /// <summary>
225 /// Writes a bool field value, without a tag, to the stream.
226 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100227 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100228 public void WriteBool(bool value)
csharptest71f662c2011-05-20 15:15:34 -0500229 {
csharptest74c5e0c2011-07-14 13:06:22 -0500230 WriteRawByte(value ? (byte) 1 : (byte) 0);
csharptest71f662c2011-05-20 15:15:34 -0500231 }
232
233 /// <summary>
234 /// Writes a string field value, without a tag, to the stream.
Jon Skeetf34d37a2015-06-30 13:16:20 +0100235 /// The data is length-prefixed.
csharptest71f662c2011-05-20 15:15:34 -0500236 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100237 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100238 public void WriteString(string value)
csharptest71f662c2011-05-20 15:15:34 -0500239 {
240 // Optimise the case where we have enough space to write
241 // the string directly to the buffer, which should be common.
Jon Skeeta0f95692015-06-17 15:34:29 +0100242 int length = Utf8Encoding.GetByteCount(value);
Jon Skeetf34d37a2015-06-30 13:16:20 +0100243 WriteLength(length);
csharptest71f662c2011-05-20 15:15:34 -0500244 if (limit - position >= length)
245 {
Jon Skeet828b7e62015-06-17 14:59:10 +0100246 if (length == value.Length) // Must be all ASCII...
247 {
248 for (int i = 0; i < length; i++)
249 {
250 buffer[position + i] = (byte)value[i];
251 }
252 }
253 else
254 {
Jon Skeeta0f95692015-06-17 15:34:29 +0100255 Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
Jon Skeet828b7e62015-06-17 14:59:10 +0100256 }
csharptest71f662c2011-05-20 15:15:34 -0500257 position += length;
258 }
259 else
260 {
Jon Skeeta0f95692015-06-17 15:34:29 +0100261 byte[] bytes = Utf8Encoding.GetBytes(value);
csharptest71f662c2011-05-20 15:15:34 -0500262 WriteRawBytes(bytes);
263 }
264 }
265
Jon Skeetf34d37a2015-06-30 13:16:20 +0100266 /// <summary>
267 /// Writes a message, without a tag, to the stream.
268 /// The data is length-prefixed.
269 /// </summary>
270 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100271 public void WriteMessage(IMessage value)
csharptest71f662c2011-05-20 15:15:34 -0500272 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100273 WriteLength(value.CalculateSize());
csharptest71f662c2011-05-20 15:15:34 -0500274 value.WriteTo(this);
275 }
276
Jon Skeetf34d37a2015-06-30 13:16:20 +0100277 /// <summary>
278 /// Write a byte string, without a tag, to the stream.
279 /// The data is length-prefixed.
280 /// </summary>
281 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100282 public void WriteBytes(ByteString value)
csharptest71f662c2011-05-20 15:15:34 -0500283 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100284 WriteLength(value.Length);
csharptested701ad2011-07-14 13:20:11 -0500285 value.WriteRawBytesTo(this);
csharptest71f662c2011-05-20 15:15:34 -0500286 }
287
Jon Skeetf34d37a2015-06-30 13:16:20 +0100288 /// <summary>
289 /// Writes a uint32 value, without a tag, to the stream.
290 /// </summary>
291 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100292 public void WriteUInt32(uint value)
csharptest71f662c2011-05-20 15:15:34 -0500293 {
294 WriteRawVarint32(value);
295 }
296
Jon Skeetf34d37a2015-06-30 13:16:20 +0100297 /// <summary>
298 /// Writes an enum value, without a tag, to the stream.
299 /// </summary>
300 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100301 public void WriteEnum(int value)
csharptest71f662c2011-05-20 15:15:34 -0500302 {
Jon Skeet828b7e62015-06-17 14:59:10 +0100303 WriteInt32(value);
csharptest71f662c2011-05-20 15:15:34 -0500304 }
305
Jon Skeet811fc892015-08-04 15:58:39 +0100306 /// <summary>
307 /// Writes an sfixed32 value, without a tag, to the stream.
308 /// </summary>
309 /// <param name="value">The value to write.</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100310 public void WriteSFixed32(int value)
csharptest71f662c2011-05-20 15:15:34 -0500311 {
csharptest74c5e0c2011-07-14 13:06:22 -0500312 WriteRawLittleEndian32((uint) value);
csharptest71f662c2011-05-20 15:15:34 -0500313 }
314
Jon Skeetf34d37a2015-06-30 13:16:20 +0100315 /// <summary>
316 /// Writes an sfixed64 value, without a tag, to the stream.
317 /// </summary>
318 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100319 public void WriteSFixed64(long value)
csharptest71f662c2011-05-20 15:15:34 -0500320 {
csharptest74c5e0c2011-07-14 13:06:22 -0500321 WriteRawLittleEndian64((ulong) value);
csharptest71f662c2011-05-20 15:15:34 -0500322 }
323
Jon Skeetf34d37a2015-06-30 13:16:20 +0100324 /// <summary>
325 /// Writes an sint32 value, without a tag, to the stream.
326 /// </summary>
327 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100328 public void WriteSInt32(int value)
csharptest71f662c2011-05-20 15:15:34 -0500329 {
330 WriteRawVarint32(EncodeZigZag32(value));
331 }
332
Jon Skeetf34d37a2015-06-30 13:16:20 +0100333 /// <summary>
334 /// Writes an sint64 value, without a tag, to the stream.
335 /// </summary>
336 /// <param name="value">The value to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100337 public void WriteSInt64(long value)
csharptest71f662c2011-05-20 15:15:34 -0500338 {
339 WriteRawVarint64(EncodeZigZag64(value));
340 }
341
Jon Skeetf34d37a2015-06-30 13:16:20 +0100342 /// <summary>
343 /// Writes a length (in bytes) for length-delimited data.
344 /// </summary>
345 /// <remarks>
346 /// This method simply writes a rawint, but exists for clarity in calling code.
347 /// </remarks>
348 /// <param name="length">Length value, in bytes.</param>
349 public void WriteLength(int length)
350 {
351 WriteRawVarint32((uint) length);
352 }
353
csharptest71f662c2011-05-20 15:15:34 -0500354 #endregion
355
Jon Skeet828b7e62015-06-17 14:59:10 +0100356 #region Raw tag writing
357 /// <summary>
358 /// Encodes and writes a tag.
359 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100360 /// <param name="fieldNumber">The number of the field to write the tag for</param>
361 /// <param name="type">The wire format type of the tag to write</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100362 public void WriteTag(int fieldNumber, WireFormat.WireType type)
363 {
364 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
365 }
366
367 /// <summary>
Jon Skeet0d684d32015-06-24 17:21:55 +0100368 /// Writes an already-encoded tag.
369 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100370 /// <param name="tag">The encoded tag</param>
Jon Skeet0d684d32015-06-24 17:21:55 +0100371 public void WriteTag(uint tag)
372 {
373 WriteRawVarint32(tag);
374 }
375
376 /// <summary>
Jon Skeet828b7e62015-06-17 14:59:10 +0100377 /// Writes the given single-byte tag directly to the stream.
378 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100379 /// <param name="b1">The encoded tag</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100380 public void WriteRawTag(byte b1)
381 {
382 WriteRawByte(b1);
383 }
384
385 /// <summary>
386 /// Writes the given two-byte tag directly to the stream.
387 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100388 /// <param name="b1">The first byte of the encoded tag</param>
389 /// <param name="b2">The second byte of the encoded tag</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100390 public void WriteRawTag(byte b1, byte b2)
391 {
392 WriteRawByte(b1);
393 WriteRawByte(b2);
394 }
395
396 /// <summary>
397 /// Writes the given three-byte tag directly to the stream.
398 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100399 /// <param name="b1">The first byte of the encoded tag</param>
400 /// <param name="b2">The second byte of the encoded tag</param>
401 /// <param name="b3">The third byte of the encoded tag</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100402 public void WriteRawTag(byte b1, byte b2, byte b3)
403 {
404 WriteRawByte(b1);
405 WriteRawByte(b2);
406 WriteRawByte(b3);
407 }
408
409 /// <summary>
410 /// Writes the given four-byte tag directly to the stream.
411 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100412 /// <param name="b1">The first byte of the encoded tag</param>
413 /// <param name="b2">The second byte of the encoded tag</param>
414 /// <param name="b3">The third byte of the encoded tag</param>
415 /// <param name="b4">The fourth byte of the encoded tag</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100416 public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
417 {
418 WriteRawByte(b1);
419 WriteRawByte(b2);
420 WriteRawByte(b3);
421 WriteRawByte(b4);
422 }
423
424 /// <summary>
425 /// Writes the given five-byte tag directly to the stream.
426 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100427 /// <param name="b1">The first byte of the encoded tag</param>
428 /// <param name="b2">The second byte of the encoded tag</param>
429 /// <param name="b3">The third byte of the encoded tag</param>
430 /// <param name="b4">The fourth byte of the encoded tag</param>
431 /// <param name="b5">The fifth byte of the encoded tag</param>
Jon Skeet828b7e62015-06-17 14:59:10 +0100432 public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
433 {
434 WriteRawByte(b1);
435 WriteRawByte(b2);
436 WriteRawByte(b3);
437 WriteRawByte(b4);
438 WriteRawByte(b5);
439 }
440 #endregion
441
csharptest71f662c2011-05-20 15:15:34 -0500442 #region Underlying writing primitives
csharptest71f662c2011-05-20 15:15:34 -0500443 /// <summary>
444 /// Writes a 32 bit value as a varint. The fast route is taken when
445 /// there's enough buffer space left to whizz through without checking
446 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
447 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100448 internal void WriteRawVarint32(uint value)
csharptest71f662c2011-05-20 15:15:34 -0500449 {
Jon Skeetce0e3482015-06-11 14:32:19 +0100450 // Optimize for the common case of a single byte value
451 if (value < 128 && position < limit)
452 {
453 buffer[position++] = (byte)value;
454 return;
455 }
456
csharptestc671a4b2011-06-08 11:51:24 -0500457 while (value > 127 && position < limit)
458 {
csharptest74c5e0c2011-07-14 13:06:22 -0500459 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
csharptestc671a4b2011-06-08 11:51:24 -0500460 value >>= 7;
461 }
462 while (value > 127)
463 {
csharptest74c5e0c2011-07-14 13:06:22 -0500464 WriteRawByte((byte) ((value & 0x7F) | 0x80));
csharptestc671a4b2011-06-08 11:51:24 -0500465 value >>= 7;
466 }
csharptest74c5e0c2011-07-14 13:06:22 -0500467 if (position < limit)
468 {
469 buffer[position++] = (byte) value;
470 }
csharptestc671a4b2011-06-08 11:51:24 -0500471 else
csharptest74c5e0c2011-07-14 13:06:22 -0500472 {
473 WriteRawByte((byte) value);
474 }
csharptest71f662c2011-05-20 15:15:34 -0500475 }
476
Jon Skeetf34d37a2015-06-30 13:16:20 +0100477 internal void WriteRawVarint64(ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500478 {
csharptestc671a4b2011-06-08 11:51:24 -0500479 while (value > 127 && position < limit)
480 {
csharptest74c5e0c2011-07-14 13:06:22 -0500481 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
csharptestc671a4b2011-06-08 11:51:24 -0500482 value >>= 7;
483 }
484 while (value > 127)
485 {
csharptest74c5e0c2011-07-14 13:06:22 -0500486 WriteRawByte((byte) ((value & 0x7F) | 0x80));
csharptestc671a4b2011-06-08 11:51:24 -0500487 value >>= 7;
488 }
csharptest74c5e0c2011-07-14 13:06:22 -0500489 if (position < limit)
490 {
491 buffer[position++] = (byte) value;
492 }
csharptestc671a4b2011-06-08 11:51:24 -0500493 else
csharptest74c5e0c2011-07-14 13:06:22 -0500494 {
495 WriteRawByte((byte) value);
496 }
csharptest71f662c2011-05-20 15:15:34 -0500497 }
498
Jon Skeetf34d37a2015-06-30 13:16:20 +0100499 internal void WriteRawLittleEndian32(uint value)
csharptest71f662c2011-05-20 15:15:34 -0500500 {
csharptestc671a4b2011-06-08 11:51:24 -0500501 if (position + 4 > limit)
502 {
503 WriteRawByte((byte) value);
504 WriteRawByte((byte) (value >> 8));
505 WriteRawByte((byte) (value >> 16));
506 WriteRawByte((byte) (value >> 24));
507 }
508 else
509 {
csharptest74c5e0c2011-07-14 13:06:22 -0500510 buffer[position++] = ((byte) value);
511 buffer[position++] = ((byte) (value >> 8));
512 buffer[position++] = ((byte) (value >> 16));
513 buffer[position++] = ((byte) (value >> 24));
csharptestc671a4b2011-06-08 11:51:24 -0500514 }
csharptest71f662c2011-05-20 15:15:34 -0500515 }
516
Jon Skeetf34d37a2015-06-30 13:16:20 +0100517 internal void WriteRawLittleEndian64(ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500518 {
csharptestc671a4b2011-06-08 11:51:24 -0500519 if (position + 8 > limit)
520 {
521 WriteRawByte((byte) value);
522 WriteRawByte((byte) (value >> 8));
523 WriteRawByte((byte) (value >> 16));
524 WriteRawByte((byte) (value >> 24));
525 WriteRawByte((byte) (value >> 32));
526 WriteRawByte((byte) (value >> 40));
527 WriteRawByte((byte) (value >> 48));
528 WriteRawByte((byte) (value >> 56));
529 }
530 else
531 {
csharptest74c5e0c2011-07-14 13:06:22 -0500532 buffer[position++] = ((byte) value);
533 buffer[position++] = ((byte) (value >> 8));
534 buffer[position++] = ((byte) (value >> 16));
535 buffer[position++] = ((byte) (value >> 24));
536 buffer[position++] = ((byte) (value >> 32));
537 buffer[position++] = ((byte) (value >> 40));
538 buffer[position++] = ((byte) (value >> 48));
539 buffer[position++] = ((byte) (value >> 56));
csharptestc671a4b2011-06-08 11:51:24 -0500540 }
csharptest71f662c2011-05-20 15:15:34 -0500541 }
542
Jon Skeetf34d37a2015-06-30 13:16:20 +0100543 internal void WriteRawByte(byte value)
csharptest71f662c2011-05-20 15:15:34 -0500544 {
545 if (position == limit)
546 {
547 RefreshBuffer();
548 }
549
550 buffer[position++] = value;
551 }
552
Jon Skeetf34d37a2015-06-30 13:16:20 +0100553 internal void WriteRawByte(uint value)
csharptest71f662c2011-05-20 15:15:34 -0500554 {
555 WriteRawByte((byte) value);
556 }
557
558 /// <summary>
559 /// Writes out an array of bytes.
560 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100561 internal void WriteRawBytes(byte[] value)
csharptest71f662c2011-05-20 15:15:34 -0500562 {
563 WriteRawBytes(value, 0, value.Length);
564 }
565
566 /// <summary>
567 /// Writes out part of an array of bytes.
568 /// </summary>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100569 internal void WriteRawBytes(byte[] value, int offset, int length)
csharptest71f662c2011-05-20 15:15:34 -0500570 {
571 if (limit - position >= length)
572 {
csharptestaef072a2011-06-08 18:00:43 -0500573 ByteArray.Copy(value, offset, buffer, position, length);
csharptest71f662c2011-05-20 15:15:34 -0500574 // We have room in the current buffer.
575 position += length;
576 }
577 else
578 {
579 // Write extends past current buffer. Fill the rest of this buffer and
580 // flush.
581 int bytesWritten = limit - position;
csharptestaef072a2011-06-08 18:00:43 -0500582 ByteArray.Copy(value, offset, buffer, position, bytesWritten);
csharptest71f662c2011-05-20 15:15:34 -0500583 offset += bytesWritten;
584 length -= bytesWritten;
585 position = limit;
586 RefreshBuffer();
587
588 // Now deal with the rest.
589 // Since we have an output stream, this is our buffer
590 // and buffer offset == 0
591 if (length <= limit)
592 {
593 // Fits in new buffer.
csharptestaef072a2011-06-08 18:00:43 -0500594 ByteArray.Copy(value, offset, buffer, 0, length);
csharptest71f662c2011-05-20 15:15:34 -0500595 position = length;
596 }
597 else
598 {
599 // Write is very big. Let's do it all at once.
600 output.Write(value, offset, length);
601 }
602 }
603 }
604
605 #endregion
csharptest74c5e0c2011-07-14 13:06:22 -0500606
csharptest71f662c2011-05-20 15:15:34 -0500607 /// <summary>
608 /// Encode a 32-bit value with ZigZag encoding.
609 /// </summary>
610 /// <remarks>
611 /// ZigZag encodes signed integers into values that can be efficiently
612 /// encoded with varint. (Otherwise, negative values must be
613 /// sign-extended to 64 bits to be varint encoded, thus always taking
614 /// 10 bytes on the wire.)
615 /// </remarks>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100616 internal static uint EncodeZigZag32(int n)
csharptest71f662c2011-05-20 15:15:34 -0500617 {
618 // Note: the right-shift must be arithmetic
619 return (uint) ((n << 1) ^ (n >> 31));
620 }
621
622 /// <summary>
623 /// Encode a 64-bit value with ZigZag encoding.
624 /// </summary>
625 /// <remarks>
626 /// ZigZag encodes signed integers into values that can be efficiently
627 /// encoded with varint. (Otherwise, negative values must be
628 /// sign-extended to 64 bits to be varint encoded, thus always taking
629 /// 10 bytes on the wire.)
630 /// </remarks>
Jon Skeetf34d37a2015-06-30 13:16:20 +0100631 internal static ulong EncodeZigZag64(long n)
csharptest71f662c2011-05-20 15:15:34 -0500632 {
633 return (ulong) ((n << 1) ^ (n >> 63));
634 }
635
636 private void RefreshBuffer()
637 {
638 if (output == null)
639 {
640 // We're writing to a single buffer.
641 throw new OutOfSpaceException();
642 }
643
644 // Since we have an output stream, this is our buffer
645 // and buffer offset == 0
646 output.Write(buffer, 0, position);
647 position = 0;
648 }
649
650 /// <summary>
651 /// Indicates that a CodedOutputStream wrapping a flat byte array
652 /// ran out of space.
653 /// </summary>
654 public sealed class OutOfSpaceException : IOException
655 {
656 internal OutOfSpaceException()
657 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
658 {
659 }
660 }
661
Jon Skeet811fc892015-08-04 15:58:39 +0100662 /// <summary>
663 /// Flushes any buffered data to the underlying stream (if there is one).
664 /// </summary>
csharptest71f662c2011-05-20 15:15:34 -0500665 public void Flush()
666 {
667 if (output != null)
668 {
669 RefreshBuffer();
670 }
671 }
672
673 /// <summary>
674 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
675 /// that is exactly big enough to hold a message, then write to it with
676 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
677 /// the message was actually as big as expected, which can help bugs.
678 /// </summary>
679 public void CheckNoSpaceLeft()
680 {
681 if (SpaceLeft != 0)
682 {
683 throw new InvalidOperationException("Did not write as much data as expected.");
684 }
685 }
686
687 /// <summary>
688 /// If writing to a flat array, returns the space left in the array. Otherwise,
689 /// throws an InvalidOperationException.
690 /// </summary>
691 public int SpaceLeft
692 {
693 get
694 {
695 if (output == null)
696 {
697 return limit - position;
698 }
699 else
700 {
701 throw new InvalidOperationException(
702 "SpaceLeft can only be called on CodedOutputStreams that are " +
703 "writing to a flat array.");
704 }
705 }
706 }
707 }
708}