blob: e5f890f946e4b4ab31e4c4c45301922d09b12ea4 [file] [log] [blame]
Jon Skeet60c059b2008-10-23 21:17:56 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// http://github.com/jskeet/dotnet-protobufs/
4// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01005// http://code.google.com/p/protobuf/
6//
Jon Skeet60c059b2008-10-23 21:17:56 +01007// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
Jon Skeet68036862008-10-22 13:30:34 +010010//
Jon Skeet60c059b2008-10-23 21:17:56 +010011// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010020//
Jon Skeet60c059b2008-10-23 21:17:56 +010021// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet68036862008-10-22 13:30:34 +010032using System;
33using System.IO;
34using System.Text;
35using Google.ProtocolBuffers.Descriptors;
36
37namespace Google.ProtocolBuffers {
38
39 /// <summary>
40 /// Encodes and writes protocol message fields.
41 /// </summary>
42 /// <remarks>
43 /// This class contains two kinds of methods: methods that write specific
44 /// protocol message constructs and field types (e.g. WriteTag and
45 /// WriteInt32) and methods that write low-level values (e.g.
46 /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
47 /// messages, you should use the former methods, but if you are writing some
48 /// other format of your own design, use the latter. The names of the former
49 /// methods are taken from the protocol buffer type names, not .NET types.
50 /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
51 /// </remarks>
52 public sealed class CodedOutputStream {
53 /// <summary>
54 /// The buffer size used by CreateInstance(Stream).
55 /// </summary>
56 public static readonly int DefaultBufferSize = 4096;
57
58 private readonly byte[] buffer;
59 private readonly int limit;
60 private int position;
61 private readonly Stream output;
62
63 #region Construction
64 private CodedOutputStream(byte[] buffer, int offset, int length) {
65 this.output = null;
66 this.buffer = buffer;
67 this.position = offset;
68 this.limit = offset + length;
69 }
70
71 private CodedOutputStream(Stream output, byte[] buffer) {
72 this.output = output;
73 this.buffer = buffer;
74 this.position = 0;
75 this.limit = buffer.Length;
76 }
77
78 /// <summary>
79 /// Creates a new CodedOutputStream which write to the given stream.
80 /// </summary>
81 public static CodedOutputStream CreateInstance(Stream output) {
82 return CreateInstance(output, DefaultBufferSize);
83 }
84
85 /// <summary>
86 /// Creates a new CodedOutputStream which write to the given stream and uses
87 /// the specified buffer size.
88 /// </summary>
89 public static CodedOutputStream CreateInstance(Stream output, int bufferSize) {
90 return new CodedOutputStream(output, new byte[bufferSize]);
91 }
92
93 /// <summary>
94 /// Creates a new CodedOutputStream that writes directly to the given
95 /// byte array. If more bytes are written than fit in the array,
96 /// OutOfSpaceException will be thrown.
97 /// </summary>
98 public static CodedOutputStream CreateInstance(byte[] flatArray) {
99 return CreateInstance(flatArray, 0, flatArray.Length);
100 }
101
102 /// <summary>
103 /// Creates a new CodedOutputStream that writes directly to the given
104 /// byte array slice. If more bytes are written than fit in the array,
105 /// OutOfSpaceException will be thrown.
106 /// </summary>
107 public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length) {
108 return new CodedOutputStream(flatArray, offset, length);
109 }
110 #endregion
111
112 #region Writing of tags etc
113 /// <summary>
114 /// Writes a double field value, including tag, to the stream.
115 /// </summary>
116 public void WriteDouble(int fieldNumber, double value) {
117 // TODO(jonskeet): Test this on different endiannesses
118 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
119 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
120 }
121
122 /// <summary>
123 /// Writes a float field value, including tag, to the stream.
124 /// </summary>
125 public void WriteFloat(int fieldNumber, float value) {
126 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
127 // TODO(jonskeet): Test this on different endiannesses
128 byte[] rawBytes = BitConverter.GetBytes(value);
129 uint asInteger = BitConverter.ToUInt32(rawBytes, 0);
130 WriteRawLittleEndian32(asInteger);
131 }
132
133 /// <summary>
134 /// Writes a uint64 field value, including tag, to the stream.
135 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100136 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100137 public void WriteUInt64(int fieldNumber, ulong value) {
138 WriteTag(fieldNumber, WireFormat.WireType.Varint);
139 WriteRawVarint64(value);
140 }
141
142 /// <summary>
143 /// Writes an int64 field value, including tag, to the stream.
144 /// </summary>
145 public void WriteInt64(int fieldNumber, long value) {
146 WriteTag(fieldNumber, WireFormat.WireType.Varint);
147 WriteRawVarint64((ulong)value);
148 }
149
150 /// <summary>
151 /// Writes an int32 field value, including tag, to the stream.
152 /// </summary>
153 public void WriteInt32(int fieldNumber, int value) {
154 WriteTag(fieldNumber, WireFormat.WireType.Varint);
155 if (value >= 0) {
156 WriteRawVarint32((uint)value);
157 } else {
158 // Must sign-extend.
159 WriteRawVarint64((ulong)value);
160 }
161 }
162
163 /// <summary>
164 /// Writes a fixed64 field value, including tag, to the stream.
165 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100166 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100167 public void WriteFixed64(int fieldNumber, ulong value) {
168 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
169 WriteRawLittleEndian64(value);
170 }
171
172 /// <summary>
173 /// Writes a fixed32 field value, including tag, to the stream.
174 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100175 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100176 public void WriteFixed32(int fieldNumber, uint value) {
177 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
178 WriteRawLittleEndian32(value);
179 }
180
181 /// <summary>
182 /// Writes a bool field value, including tag, to the stream.
183 /// </summary>
184 public void WriteBool(int fieldNumber, bool value) {
185 WriteTag(fieldNumber, WireFormat.WireType.Varint);
186 WriteRawByte(value ? (byte)1 : (byte)0);
187 }
188
189 /// <summary>
190 /// Writes a string field value, including tag, to the stream.
191 /// </summary>
192 public void WriteString(int fieldNumber, string value) {
193 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
194 // Optimise the case where we have enough space to write
195 // the string directly to the buffer, which should be common.
196 int length = Encoding.UTF8.GetByteCount(value);
197 WriteRawVarint32((uint) length);
198 if (limit - position >= length) {
199 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
200 position += length;
201 } else {
202 byte[] bytes = Encoding.UTF8.GetBytes(value);
203 WriteRawBytes(bytes);
204 }
205 }
206
207 /// <summary>
208 /// Writes a group field value, including tag, to the stream.
209 /// </summary>
210 public void WriteGroup(int fieldNumber, IMessage value) {
211 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
212 value.WriteTo(this);
213 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
214 }
215
216 public void WriteUnknownGroup(int fieldNumber, UnknownFieldSet value) {
217 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
218 value.WriteTo(this);
219 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
220 }
221
222 public void WriteMessage(int fieldNumber, IMessage value) {
223 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
224 WriteRawVarint32((uint)value.SerializedSize);
225 value.WriteTo(this);
226 }
227
228 public void WriteBytes(int fieldNumber, ByteString value) {
229 // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
230 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
231 byte[] bytes = value.ToByteArray();
232 WriteRawVarint32((uint)bytes.Length);
233 WriteRawBytes(bytes);
234 }
235
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100236 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100237 public void WriteUInt32(int fieldNumber, uint value) {
238 WriteTag(fieldNumber, WireFormat.WireType.Varint);
239 WriteRawVarint32(value);
240 }
241
242 public void WriteEnum(int fieldNumber, int value) {
243 WriteTag(fieldNumber, WireFormat.WireType.Varint);
244 WriteRawVarint32((uint)value);
245 }
246
247 public void WriteSFixed32(int fieldNumber, int value) {
248 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
249 WriteRawLittleEndian32((uint)value);
250 }
251
252 public void WriteSFixed64(int fieldNumber, long value) {
253 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
254 WriteRawLittleEndian64((ulong)value);
255 }
256
257 public void WriteSInt32(int fieldNumber, int value) {
258 WriteTag(fieldNumber, WireFormat.WireType.Varint);
259 WriteRawVarint32(EncodeZigZag32(value));
260 }
261
262 public void WriteSInt64(int fieldNumber, long value) {
263 WriteTag(fieldNumber, WireFormat.WireType.Varint);
264 WriteRawVarint64(EncodeZigZag64(value));
265 }
266
267 public void WriteMessageSetExtension(int fieldNumber, IMessage value) {
268 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
269 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
270 WriteMessage(WireFormat.MessageSetField.Message, value);
271 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
272 }
273
274 public void WriteRawMessageSetExtension(int fieldNumber, ByteString value) {
275 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
276 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
277 WriteBytes(WireFormat.MessageSetField.Message, value);
278 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
279 }
280
281 public void WriteField(FieldType fieldType, int fieldNumber, object value) {
282 switch (fieldType) {
283 case FieldType.Double: WriteDouble(fieldNumber, (double)value); break;
284 case FieldType.Float: WriteFloat(fieldNumber, (float)value); break;
285 case FieldType.Int64: WriteInt64(fieldNumber, (long)value); break;
286 case FieldType.UInt64: WriteUInt64(fieldNumber, (ulong)value); break;
287 case FieldType.Int32: WriteInt32(fieldNumber, (int)value); break;
288 case FieldType.Fixed64: WriteFixed64(fieldNumber, (ulong)value); break;
289 case FieldType.Fixed32: WriteFixed32(fieldNumber, (uint)value); break;
290 case FieldType.Bool: WriteBool(fieldNumber, (bool)value); break;
291 case FieldType.String: WriteString(fieldNumber, (string)value); break;
292 case FieldType.Group: WriteGroup(fieldNumber, (IMessage)value); break;
293 case FieldType.Message: WriteMessage(fieldNumber, (IMessage)value); break;
294 case FieldType.Bytes: WriteBytes(fieldNumber, (ByteString)value); break;
295 case FieldType.UInt32: WriteUInt32(fieldNumber, (uint)value); break;
296 case FieldType.SFixed32: WriteSFixed32(fieldNumber, (int)value); break;
297 case FieldType.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
298 case FieldType.SInt32: WriteSInt32(fieldNumber, (int)value); break;
299 case FieldType.SInt64: WriteSInt64(fieldNumber, (long)value); break;
300 case FieldType.Enum: WriteEnum(fieldNumber, ((EnumValueDescriptor)value).Number);
301 break;
302 }
303 }
304
Jon Skeet25a28582009-02-18 16:06:22 +0000305 public void WriteFieldNoTag(FieldType fieldType, object value) {
306 switch (fieldType) {
307 case FieldType.Double: WriteDoubleNoTag((double)value); break;
308 case FieldType.Float: WriteFloatNoTag((float)value); break;
309 case FieldType.Int64: WriteInt64NoTag((long)value); break;
310 case FieldType.UInt64: WriteUInt64NoTag((ulong)value); break;
311 case FieldType.Int32: WriteInt32NoTag((int)value); break;
312 case FieldType.Fixed64: WriteFixed64NoTag((ulong)value); break;
313 case FieldType.Fixed32: WriteFixed32NoTag((uint)value); break;
314 case FieldType.Bool: WriteBoolNoTag((bool)value); break;
315 case FieldType.String: WriteStringNoTag((string)value); break;
316 case FieldType.Group: WriteGroupNoTag((IMessage)value); break;
317 case FieldType.Message: WriteMessageNoTag((IMessage)value); break;
318 case FieldType.Bytes: WriteBytesNoTag((ByteString)value); break;
319 case FieldType.UInt32: WriteUInt32NoTag((uint)value); break;
320 case FieldType.SFixed32: WriteSFixed32NoTag((int)value); break;
321 case FieldType.SFixed64: WriteSFixed64NoTag((long)value); break;
322 case FieldType.SInt32: WriteSInt32NoTag((int)value); break;
323 case FieldType.SInt64: WriteSInt64NoTag((long)value); break;
324 case FieldType.Enum: WriteEnumNoTag(((EnumValueDescriptor)value).Number);
325 break;
326 }
327 }
328 #endregion
329
330 #region Writing of values without tags
331 /// <summary>
332 /// Writes a double field value, including tag, to the stream.
333 /// </summary>
334 public void WriteDoubleNoTag(double value) {
335 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
336 }
337
338 /// <summary>
339 /// Writes a float field value, without a tag, to the stream.
340 /// </summary>
341 public void WriteFloatNoTag(float value) {
342 // TODO(jonskeet): Test this on different endiannesses
343 byte[] rawBytes = BitConverter.GetBytes(value);
344 uint asInteger = BitConverter.ToUInt32(rawBytes, 0);
345 WriteRawLittleEndian32(asInteger);
346 }
347
348 /// <summary>
349 /// Writes a uint64 field value, without a tag, to the stream.
350 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100351 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000352 public void WriteUInt64NoTag(ulong value) {
353 WriteRawVarint64(value);
354 }
355
356 /// <summary>
357 /// Writes an int64 field value, without a tag, to the stream.
358 /// </summary>
359 public void WriteInt64NoTag(long value) {
360 WriteRawVarint64((ulong)value);
361 }
362
363 /// <summary>
364 /// Writes an int32 field value, without a tag, to the stream.
365 /// </summary>
366 public void WriteInt32NoTag(int value) {
367 if (value >= 0) {
368 WriteRawVarint32((uint)value);
369 } else {
370 // Must sign-extend.
371 WriteRawVarint64((ulong)value);
372 }
373 }
374
375 /// <summary>
376 /// Writes a fixed64 field value, without a tag, to the stream.
377 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100378 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000379 public void WriteFixed64NoTag(ulong value) {
380 WriteRawLittleEndian64(value);
381 }
382
383 /// <summary>
384 /// Writes a fixed32 field value, without a tag, to the stream.
385 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100386 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000387 public void WriteFixed32NoTag(uint value) {
388 WriteRawLittleEndian32(value);
389 }
390
391 /// <summary>
392 /// Writes a bool field value, without a tag, to the stream.
393 /// </summary>
394 public void WriteBoolNoTag(bool value) {
395 WriteRawByte(value ? (byte)1 : (byte)0);
396 }
397
398 /// <summary>
399 /// Writes a string field value, without a tag, to the stream.
400 /// </summary>
401 public void WriteStringNoTag(string value) {
402 // Optimise the case where we have enough space to write
403 // the string directly to the buffer, which should be common.
404 int length = Encoding.UTF8.GetByteCount(value);
405 WriteRawVarint32((uint)length);
406 if (limit - position >= length) {
407 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
408 position += length;
409 } else {
410 byte[] bytes = Encoding.UTF8.GetBytes(value);
411 WriteRawBytes(bytes);
412 }
413 }
414
415 /// <summary>
416 /// Writes a group field value, without a tag, to the stream.
417 /// </summary>
418 public void WriteGroupNoTag(IMessage value) {
419 value.WriteTo(this);
420 }
421
422 public void WriteMessageNoTag(IMessage value) {
423 WriteRawVarint32((uint)value.SerializedSize);
424 value.WriteTo(this);
425 }
426
427 public void WriteBytesNoTag(ByteString value) {
428 // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
429 byte[] bytes = value.ToByteArray();
430 WriteRawVarint32((uint)bytes.Length);
431 WriteRawBytes(bytes);
432 }
433
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100434 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000435 public void WriteUInt32NoTag(uint value) {
436 WriteRawVarint32(value);
437 }
438
439 public void WriteEnumNoTag(int value) {
440 WriteRawVarint32((uint)value);
441 }
442
443 public void WriteSFixed32NoTag(int value) {
444 WriteRawLittleEndian32((uint)value);
445 }
446
447 public void WriteSFixed64NoTag(long value) {
448 WriteRawLittleEndian64((ulong)value);
449 }
450
451 public void WriteSInt32NoTag(int value) {
452 WriteRawVarint32(EncodeZigZag32(value));
453 }
454
455 public void WriteSInt64NoTag(long value) {
456 WriteRawVarint64(EncodeZigZag64(value));
457 }
458
Jon Skeet68036862008-10-22 13:30:34 +0100459 #endregion
460
461 #region Underlying writing primitives
462 /// <summary>
463 /// Encodes and writes a tag.
464 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100465 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100466 public void WriteTag(int fieldNumber, WireFormat.WireType type) {
467 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
468 }
469
470 private void SlowWriteRawVarint32(uint value) {
471 while (true) {
472 if ((value & ~0x7F) == 0) {
473 WriteRawByte(value);
474 return;
475 } else {
476 WriteRawByte((value & 0x7F) | 0x80);
477 value >>= 7;
478 }
479 }
480 }
481
482 /// <summary>
483 /// Writes a 32 bit value as a varint. The fast route is taken when
484 /// there's enough buffer space left to whizz through without checking
485 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
486 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100487 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100488 public void WriteRawVarint32(uint value) {
489 if (position + 5 > limit) {
490 SlowWriteRawVarint32(value);
491 return;
492 }
493
494 while (true) {
495 if ((value & ~0x7F) == 0) {
496 buffer[position++] = (byte) value;
497 return;
498 } else {
499 buffer[position++] = (byte)((value & 0x7F) | 0x80);
500 value >>= 7;
501 }
502 }
503 }
504
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100505 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100506 public void WriteRawVarint64(ulong value) {
507 while (true) {
508 if ((value & ~0x7FUL) == 0) {
509 WriteRawByte((uint)value);
510 return;
511 } else {
512 WriteRawByte(((uint)value & 0x7F) | 0x80);
513 value >>= 7;
514 }
515 }
516 }
517
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100518 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100519 public void WriteRawLittleEndian32(uint value) {
520 WriteRawByte((byte)value);
521 WriteRawByte((byte)(value >> 8));
522 WriteRawByte((byte)(value >> 16));
523 WriteRawByte((byte)(value >> 24));
524 }
525
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100526 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100527 public void WriteRawLittleEndian64(ulong value) {
528 WriteRawByte((byte)value);
529 WriteRawByte((byte)(value >> 8));
530 WriteRawByte((byte)(value >> 16));
531 WriteRawByte((byte)(value >> 24));
532 WriteRawByte((byte)(value >> 32));
533 WriteRawByte((byte)(value >> 40));
534 WriteRawByte((byte)(value >> 48));
535 WriteRawByte((byte)(value >> 56));
536 }
537
538 public void WriteRawByte(byte value) {
539 if (position == limit) {
540 RefreshBuffer();
541 }
542
543 buffer[position++] = value;
544 }
545
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100546 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100547 public void WriteRawByte(uint value) {
548 WriteRawByte((byte)value);
549 }
550
551 /// <summary>
552 /// Writes out an array of bytes.
553 /// </summary>
554 public void WriteRawBytes(byte[] value) {
555 WriteRawBytes(value, 0, value.Length);
556 }
557
558 /// <summary>
559 /// Writes out part of an array of bytes.
560 /// </summary>
561 public void WriteRawBytes(byte[] value, int offset, int length) {
562 if (limit - position >= length) {
563 Array.Copy(value, offset, buffer, position, length);
564 // We have room in the current buffer.
565 position += length;
566 } else {
567 // Write extends past current buffer. Fill the rest of this buffer and
568 // flush.
569 int bytesWritten = limit - position;
570 Array.Copy(value, offset, buffer, position, bytesWritten);
571 offset += bytesWritten;
572 length -= bytesWritten;
573 position = limit;
574 RefreshBuffer();
575
576 // Now deal with the rest.
577 // Since we have an output stream, this is our buffer
578 // and buffer offset == 0
579 if (length <= limit) {
580 // Fits in new buffer.
581 Array.Copy(value, offset, buffer, 0, length);
582 position = length;
583 } else {
584 // Write is very big. Let's do it all at once.
585 output.Write(value, offset, length);
586 }
587 }
588 }
589 #endregion
590
591 #region Size computations
592
593 const int LittleEndian64Size = 8;
594 const int LittleEndian32Size = 4;
595
596 /// <summary>
597 /// Compute the number of bytes that would be needed to encode a
598 /// double field, including the tag.
599 /// </summary>
600 public static int ComputeDoubleSize(int fieldNumber, double value) {
601 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
602 }
603
604 /// <summary>
605 /// Compute the number of bytes that would be needed to encode a
606 /// float field, including the tag.
607 /// </summary>
608 public static int ComputeFloatSize(int fieldNumber, float value) {
609 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
610 }
611
612 /// <summary>
613 /// Compute the number of bytes that would be needed to encode a
614 /// uint64 field, including the tag.
615 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100616 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100617 public static int ComputeUInt64Size(int fieldNumber, ulong value) {
618 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
619 }
620
621 /// <summary>
622 /// Compute the number of bytes that would be needed to encode an
623 /// int64 field, including the tag.
624 /// </summary>
625 public static int ComputeInt64Size(int fieldNumber, long value) {
626 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong)value);
627 }
628
629 /// <summary>
630 /// Compute the number of bytes that would be needed to encode an
631 /// int32 field, including the tag.
632 /// </summary>
633 public static int ComputeInt32Size(int fieldNumber, int value) {
634 if (value >= 0) {
635 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
636 } else {
637 // Must sign-extend.
638 return ComputeTagSize(fieldNumber) + 10;
639 }
640 }
641
642 /// <summary>
643 /// Compute the number of bytes that would be needed to encode a
644 /// fixed64 field, including the tag.
645 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100646 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100647 public static int ComputeFixed64Size(int fieldNumber, ulong value) {
648 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
649 }
650
651 /// <summary>
652 /// Compute the number of bytes that would be needed to encode a
653 /// fixed32 field, including the tag.
654 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100655 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100656 public static int ComputeFixed32Size(int fieldNumber, uint value) {
657 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
658 }
659
660 /// <summary>
661 /// Compute the number of bytes that would be needed to encode a
662 /// bool field, including the tag.
663 /// </summary>
664 public static int ComputeBoolSize(int fieldNumber, bool value) {
665 return ComputeTagSize(fieldNumber) + 1;
666 }
667
668 /// <summary>
669 /// Compute the number of bytes that would be needed to encode a
670 /// string field, including the tag.
671 /// </summary>
672 public static int ComputeStringSize(int fieldNumber, String value) {
673 int byteArraySize = Encoding.UTF8.GetByteCount(value);
674 return ComputeTagSize(fieldNumber) +
675 ComputeRawVarint32Size((uint)byteArraySize) +
676 byteArraySize;
677 }
678
679 /// <summary>
680 /// Compute the number of bytes that would be needed to encode a
681 /// group field, including the tag.
682 /// </summary>
683 public static int ComputeGroupSize(int fieldNumber, IMessage value) {
684 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
685 }
686
687 /// <summary>
688 /// Compute the number of bytes that would be needed to encode a
689 /// group field represented by an UnknownFieldSet, including the tag.
690 /// </summary>
691 public static int ComputeUnknownGroupSize(int fieldNumber,
692 UnknownFieldSet value) {
693 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
694 }
695
696 /// <summary>
697 /// Compute the number of bytes that would be needed to encode an
698 /// embedded message field, including the tag.
699 /// </summary>
700 public static int ComputeMessageSize(int fieldNumber, IMessage value) {
701 int size = value.SerializedSize;
702 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size;
703 }
704
705 /// <summary>
706 /// Compute the number of bytes that would be needed to encode a
707 /// bytes field, including the tag.
708 /// </summary>
709 public static int ComputeBytesSize(int fieldNumber, ByteString value) {
710 return ComputeTagSize(fieldNumber) +
711 ComputeRawVarint32Size((uint)value.Length) +
712 value.Length;
713 }
714
715 /// <summary>
716 /// Compute the number of bytes that would be needed to encode a
717 /// uint32 field, including the tag.
718 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100719 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100720 public static int ComputeUInt32Size(int fieldNumber, uint value) {
721 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
722 }
723
724 /// <summary>
725 /// Compute the number of bytes that would be needed to encode a
726 /// enum field, including the tag. The caller is responsible for
727 /// converting the enum value to its numeric value.
728 /// </summary>
729 public static int ComputeEnumSize(int fieldNumber, int value) {
730 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
731 }
732
733 /// <summary>
734 /// Compute the number of bytes that would be needed to encode an
735 /// sfixed32 field, including the tag.
736 /// </summary>
737 public static int ComputeSFixed32Size(int fieldNumber, int value) {
738 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
739 }
740
741 /// <summary>
742 /// Compute the number of bytes that would be needed to encode an
743 /// sfixed64 field, including the tag.
744 /// </summary>
745 public static int ComputeSFixed64Size(int fieldNumber, long value) {
746 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
747 }
748
749 /// <summary>
750 /// Compute the number of bytes that would be needed to encode an
751 /// sint32 field, including the tag.
752 /// </summary>
753 public static int ComputeSInt32Size(int fieldNumber, int value) {
Jon Skeet25a28582009-02-18 16:06:22 +0000754 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(EncodeZigZag32(value));
Jon Skeet68036862008-10-22 13:30:34 +0100755 }
756
757 /// <summary>
758 /// Compute the number of bytes that would be needed to encode an
759 /// sint64 field, including the tag.
760 /// </summary>
761 public static int ComputeSInt64Size(int fieldNumber, long value) {
Jon Skeet25a28582009-02-18 16:06:22 +0000762 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(EncodeZigZag64(value));
763 }
764
765 /// <summary>
766 /// Compute the number of bytes that would be needed to encode a
767 /// double field, including the tag.
768 /// </summary>
769 public static int ComputeDoubleSizeNoTag(double value) {
770 return LittleEndian64Size;
771 }
772
773 /// <summary>
774 /// Compute the number of bytes that would be needed to encode a
775 /// float field, including the tag.
776 /// </summary>
777 public static int ComputeFloatSizeNoTag(float value) {
778 return LittleEndian32Size;
779 }
780
781 /// <summary>
782 /// Compute the number of bytes that would be needed to encode a
783 /// uint64 field, including the tag.
784 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100785 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000786 public static int ComputeUInt64SizeNoTag(ulong value) {
787 return ComputeRawVarint64Size(value);
788 }
789
790 /// <summary>
791 /// Compute the number of bytes that would be needed to encode an
792 /// int64 field, including the tag.
793 /// </summary>
794 public static int ComputeInt64SizeNoTag(long value) {
795 return ComputeRawVarint64Size((ulong)value);
796 }
797
798 /// <summary>
799 /// Compute the number of bytes that would be needed to encode an
800 /// int32 field, including the tag.
801 /// </summary>
802 public static int ComputeInt32SizeNoTag(int value) {
803 if (value >= 0) {
804 return ComputeRawVarint32Size((uint)value);
805 } else {
806 // Must sign-extend.
807 return 10;
808 }
809 }
810
811 /// <summary>
812 /// Compute the number of bytes that would be needed to encode a
813 /// fixed64 field, including the tag.
814 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100815 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000816 public static int ComputeFixed64SizeNoTag(ulong value) {
817 return LittleEndian64Size;
818 }
819
820 /// <summary>
821 /// Compute the number of bytes that would be needed to encode a
822 /// fixed32 field, including the tag.
823 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100824 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000825 public static int ComputeFixed32SizeNoTag(uint value) {
826 return LittleEndian32Size;
827 }
828
829 /// <summary>
830 /// Compute the number of bytes that would be needed to encode a
831 /// bool field, including the tag.
832 /// </summary>
833 public static int ComputeBoolSizeNoTag(bool value) {
834 return 1;
835 }
836
837 /// <summary>
838 /// Compute the number of bytes that would be needed to encode a
839 /// string field, including the tag.
840 /// </summary>
841 public static int ComputeStringSizeNoTag(String value) {
842 int byteArraySize = Encoding.UTF8.GetByteCount(value);
843 return ComputeRawVarint32Size((uint)byteArraySize) +
844 byteArraySize;
845 }
846
847 /// <summary>
848 /// Compute the number of bytes that would be needed to encode a
849 /// group field, including the tag.
850 /// </summary>
851 public static int ComputeGroupSizeNoTag(IMessage value) {
852 return value.SerializedSize;
853 }
854
855 /// <summary>
856 /// Compute the number of bytes that would be needed to encode a
857 /// group field represented by an UnknownFieldSet, including the tag.
858 /// </summary>
859 public static int ComputeUnknownGroupSizeNoTag(UnknownFieldSet value) {
860 return value.SerializedSize;
861 }
862
863 /// <summary>
864 /// Compute the number of bytes that would be needed to encode an
865 /// embedded message field, including the tag.
866 /// </summary>
867 public static int ComputeMessageSizeNoTag(IMessage value) {
868 int size = value.SerializedSize;
869 return ComputeRawVarint32Size((uint)size) + size;
870 }
871
872 /// <summary>
873 /// Compute the number of bytes that would be needed to encode a
874 /// bytes field, including the tag.
875 /// </summary>
876 public static int ComputeBytesSizeNoTag(ByteString value) {
877 return ComputeRawVarint32Size((uint)value.Length) +
878 value.Length;
879 }
880
881 /// <summary>
882 /// Compute the number of bytes that would be needed to encode a
883 /// uint32 field, including the tag.
884 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100885 [CLSCompliant(false)]
Jon Skeet25a28582009-02-18 16:06:22 +0000886 public static int ComputeUInt32SizeNoTag(uint value) {
887 return ComputeRawVarint32Size(value);
888 }
889
890 /// <summary>
891 /// Compute the number of bytes that would be needed to encode a
892 /// enum field, including the tag. The caller is responsible for
893 /// converting the enum value to its numeric value.
894 /// </summary>
895 public static int ComputeEnumSizeNoTag(int value) {
896 return ComputeRawVarint32Size((uint)value);
897 }
898
899 /// <summary>
900 /// Compute the number of bytes that would be needed to encode an
901 /// sfixed32 field, including the tag.
902 /// </summary>
903 public static int ComputeSFixed32SizeNoTag(int value) {
904 return LittleEndian32Size;
905 }
906
907 /// <summary>
908 /// Compute the number of bytes that would be needed to encode an
909 /// sfixed64 field, including the tag.
910 /// </summary>
911 public static int ComputeSFixed64SizeNoTag(long value) {
912 return LittleEndian64Size;
913 }
914
915 /// <summary>
916 /// Compute the number of bytes that would be needed to encode an
917 /// sint32 field, including the tag.
918 /// </summary>
919 public static int ComputeSInt32SizeNoTag(int value) {
920 return ComputeRawVarint32Size(EncodeZigZag32(value));
921 }
922
923 /// <summary>
924 /// Compute the number of bytes that would be needed to encode an
925 /// sint64 field, including the tag.
926 /// </summary>
927 public static int ComputeSInt64SizeNoTag(long value) {
928 return ComputeRawVarint64Size(EncodeZigZag64(value));
Jon Skeet68036862008-10-22 13:30:34 +0100929 }
930
931 /*
932 * Compute the number of bytes that would be needed to encode a
933 * MessageSet extension to the stream. For historical reasons,
934 * the wire format differs from normal fields.
935 */
936 /// <summary>
937 /// Compute the number of bytes that would be needed to encode a
938 /// MessageSet extension to the stream. For historical reasons,
939 /// the wire format differs from normal fields.
940 /// </summary>
941 public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) {
942 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
943 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
944 ComputeMessageSize(WireFormat.MessageSetField.Message, value);
945 }
946
947 /// <summary>
948 /// Compute the number of bytes that would be needed to encode an
949 /// unparsed MessageSet extension field to the stream. For
950 /// historical reasons, the wire format differs from normal fields.
951 /// </summary>
952 public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value) {
953 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
954 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
955 ComputeBytesSize(WireFormat.MessageSetField.Message, value);
956 }
957
958 /// <summary>
959 /// Compute the number of bytes that would be needed to encode a varint.
960 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100961 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100962 public static int ComputeRawVarint32Size(uint value) {
963 if ((value & (0xffffffff << 7)) == 0) return 1;
964 if ((value & (0xffffffff << 14)) == 0) return 2;
965 if ((value & (0xffffffff << 21)) == 0) return 3;
966 if ((value & (0xffffffff << 28)) == 0) return 4;
967 return 5;
968 }
969
970 /// <summary>
971 /// Compute the number of bytes that would be needed to encode a varint.
972 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100973 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100974 public static int ComputeRawVarint64Size(ulong value) {
975 if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
976 if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
977 if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
978 if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
979 if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
980 if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
981 if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
982 if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
983 if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
984 return 10;
985 }
986
Jon Skeet25a28582009-02-18 16:06:22 +0000987 /// <summary>
988 /// Compute the number of bytes that would be needed to encode a
989 /// field of arbitrary type, including the tag, to the stream.
990 /// </summary>
Jon Skeet68036862008-10-22 13:30:34 +0100991 public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) {
992 switch (fieldType) {
993 case FieldType.Double: return ComputeDoubleSize(fieldNumber, (double)value);
994 case FieldType.Float: return ComputeFloatSize(fieldNumber, (float)value);
995 case FieldType.Int64: return ComputeInt64Size(fieldNumber, (long)value);
996 case FieldType.UInt64: return ComputeUInt64Size(fieldNumber, (ulong)value);
997 case FieldType.Int32: return ComputeInt32Size(fieldNumber, (int)value);
998 case FieldType.Fixed64: return ComputeFixed64Size(fieldNumber, (ulong)value);
999 case FieldType.Fixed32: return ComputeFixed32Size(fieldNumber, (uint)value);
1000 case FieldType.Bool: return ComputeBoolSize(fieldNumber, (bool)value);
1001 case FieldType.String: return ComputeStringSize(fieldNumber, (string)value);
1002 case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessage)value);
1003 case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessage)value);
1004 case FieldType.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value);
1005 case FieldType.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value);
1006 case FieldType.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value);
1007 case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
1008 case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
1009 case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
1010 case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number);
1011 default:
1012 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1013 }
1014 }
1015
1016 /// <summary>
Jon Skeet25a28582009-02-18 16:06:22 +00001017 /// Compute the number of bytes that would be needed to encode a
1018 /// field of arbitrary type, excluding the tag, to the stream.
1019 /// </summary>
1020 public static int ComputeFieldSizeNoTag(FieldType fieldType, Object value) {
1021 switch (fieldType) {
1022 case FieldType.Double: return ComputeDoubleSizeNoTag((double)value);
1023 case FieldType.Float: return ComputeFloatSizeNoTag((float)value);
1024 case FieldType.Int64: return ComputeInt64SizeNoTag((long)value);
1025 case FieldType.UInt64: return ComputeUInt64SizeNoTag((ulong)value);
1026 case FieldType.Int32: return ComputeInt32SizeNoTag((int)value);
1027 case FieldType.Fixed64: return ComputeFixed64SizeNoTag((ulong)value);
1028 case FieldType.Fixed32: return ComputeFixed32SizeNoTag((uint)value);
1029 case FieldType.Bool: return ComputeBoolSizeNoTag((bool)value);
1030 case FieldType.String: return ComputeStringSizeNoTag((string)value);
1031 case FieldType.Group: return ComputeGroupSizeNoTag((IMessage)value);
1032 case FieldType.Message: return ComputeMessageSizeNoTag((IMessage)value);
1033 case FieldType.Bytes: return ComputeBytesSizeNoTag((ByteString)value);
1034 case FieldType.UInt32: return ComputeUInt32SizeNoTag((uint)value);
1035 case FieldType.SFixed32: return ComputeSFixed32SizeNoTag((int)value);
1036 case FieldType.SFixed64: return ComputeSFixed64SizeNoTag((long)value);
1037 case FieldType.SInt32: return ComputeSInt32SizeNoTag((int)value);
1038 case FieldType.SInt64: return ComputeSInt64SizeNoTag((long)value);
1039 case FieldType.Enum: return ComputeEnumSizeNoTag(((EnumValueDescriptor)value).Number);
1040 default:
1041 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1042 }
1043 }
1044
1045 /// <summary>
Jon Skeet68036862008-10-22 13:30:34 +01001046 /// Compute the number of bytes that would be needed to encode a tag.
1047 /// </summary>
1048 public static int ComputeTagSize(int fieldNumber) {
1049 return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
1050 }
1051 #endregion
1052
1053 /// <summary>
1054 /// Encode a 32-bit value with ZigZag encoding.
1055 /// </summary>
1056 /// <remarks>
1057 /// ZigZag encodes signed integers into values that can be efficiently
1058 /// encoded with varint. (Otherwise, negative values must be
1059 /// sign-extended to 64 bits to be varint encoded, thus always taking
1060 /// 10 bytes on the wire.)
1061 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +01001062 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +01001063 public static uint EncodeZigZag32(int n) {
1064 // Note: the right-shift must be arithmetic
1065 return (uint)((n << 1) ^ (n >> 31));
1066 }
1067
1068 /// <summary>
1069 /// Encode a 64-bit value with ZigZag encoding.
1070 /// </summary>
1071 /// <remarks>
1072 /// ZigZag encodes signed integers into values that can be efficiently
1073 /// encoded with varint. (Otherwise, negative values must be
1074 /// sign-extended to 64 bits to be varint encoded, thus always taking
1075 /// 10 bytes on the wire.)
1076 /// </remarks>
Jon Skeetd6dd0a42009-06-05 22:00:05 +01001077 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +01001078 public static ulong EncodeZigZag64(long n) {
1079 return (ulong)((n << 1) ^ (n >> 63));
1080 }
1081
1082 private void RefreshBuffer() {
1083 if (output == null) {
1084 // We're writing to a single buffer.
1085 throw new OutOfSpaceException();
1086 }
1087
1088 // Since we have an output stream, this is our buffer
1089 // and buffer offset == 0
1090 output.Write(buffer, 0, position);
1091 position = 0;
1092 }
1093
1094 /// <summary>
1095 /// Indicates that a CodedOutputStream wrapping a flat byte array
1096 /// ran out of space.
1097 /// </summary>
1098 public sealed class OutOfSpaceException : IOException {
1099 internal OutOfSpaceException()
1100 : base("CodedOutputStream was writing to a flat byte array and ran out of space.") {
1101 }
1102 }
1103
1104 public void Flush() {
1105 if (output != null) {
1106 RefreshBuffer();
1107 }
1108 }
1109
1110 /// <summary>
1111 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
1112 /// that is exactly big enough to hold a message, then write to it with
1113 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
1114 /// the message was actually as big as expected, which can help bugs.
1115 /// </summary>
1116 public void CheckNoSpaceLeft() {
1117 if (SpaceLeft != 0) {
1118 throw new InvalidOperationException("Did not write as much data as expected.");
1119 }
1120 }
1121
1122 /// <summary>
1123 /// If writing to a flat array, returns the space left in the array. Otherwise,
1124 /// throws an InvalidOperationException.
1125 /// </summary>
1126 public int SpaceLeft {
1127 get {
1128 if (output == null) {
1129 return limit - position;
1130 } else {
1131 throw new InvalidOperationException(
1132 "SpaceLeft can only be called on CodedOutputStreams that are " +
1133 "writing to a flat array.");
1134 }
1135 }
1136 }
1137 }
1138}