blob: f30c80616e12d1ca8eda79229d63e5b97a62a430 [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc. All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15// * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19// * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System;
38using System.IO;
39using System.Text;
40using Google.ProtocolBuffers.Descriptors;
41
42namespace Google.ProtocolBuffers
43{
44 /// <summary>
45 /// Encodes and writes protocol message fields.
46 /// </summary>
47 /// <remarks>
48 /// This class contains two kinds of methods: methods that write specific
49 /// protocol message constructs and field types (e.g. WriteTag and
50 /// WriteInt32) and methods that write low-level values (e.g.
51 /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
52 /// messages, you should use the former methods, but if you are writing some
53 /// other format of your own design, use the latter. The names of the former
54 /// methods are taken from the protocol buffer type names, not .NET types.
55 /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
56 /// </remarks>
csharptest45a93fa2011-06-02 10:52:37 -050057 public sealed partial class CodedOutputStream
csharptest71f662c2011-05-20 15:15:34 -050058 {
59 /// <summary>
60 /// The buffer size used by CreateInstance(Stream).
61 /// </summary>
62 public static readonly int DefaultBufferSize = 4096;
63
64 private readonly byte[] buffer;
65 private readonly int limit;
66 private int position;
67 private readonly Stream output;
68
69 #region Construction
70
71 private CodedOutputStream(byte[] buffer, int offset, int length)
72 {
73 this.output = null;
74 this.buffer = buffer;
75 this.position = offset;
76 this.limit = offset + length;
77 }
78
79 private CodedOutputStream(Stream output, byte[] buffer)
80 {
81 this.output = output;
82 this.buffer = buffer;
83 this.position = 0;
84 this.limit = buffer.Length;
85 }
86
87 /// <summary>
88 /// Creates a new CodedOutputStream which write to the given stream.
89 /// </summary>
90 public static CodedOutputStream CreateInstance(Stream output)
91 {
92 return CreateInstance(output, DefaultBufferSize);
93 }
94
95 /// <summary>
96 /// Creates a new CodedOutputStream which write to the given stream and uses
97 /// the specified buffer size.
98 /// </summary>
99 public static CodedOutputStream CreateInstance(Stream output, int bufferSize)
100 {
101 return new CodedOutputStream(output, new byte[bufferSize]);
102 }
103
104 /// <summary>
105 /// Creates a new CodedOutputStream that writes directly to the given
106 /// byte array. If more bytes are written than fit in the array,
107 /// OutOfSpaceException will be thrown.
108 /// </summary>
109 public static CodedOutputStream CreateInstance(byte[] flatArray)
110 {
111 return CreateInstance(flatArray, 0, flatArray.Length);
112 }
113
114 /// <summary>
115 /// Creates a new CodedOutputStream that writes directly to the given
116 /// byte array slice. If more bytes are written than fit in the array,
117 /// OutOfSpaceException will be thrown.
118 /// </summary>
119 public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length)
120 {
121 return new CodedOutputStream(flatArray, offset, length);
122 }
123
124 #endregion
125
126 #region Writing of tags etc
127
128 /// <summary>
129 /// Writes a double field value, including tag, to the stream.
130 /// </summary>
131 public void WriteDouble(int fieldNumber, double value)
132 {
133 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
134 WriteDoubleNoTag(value);
135 }
136
137 /// <summary>
138 /// Writes a float field value, including tag, to the stream.
139 /// </summary>
140 public void WriteFloat(int fieldNumber, float value)
141 {
142 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
143 WriteFloatNoTag(value);
144 }
145
146 /// <summary>
147 /// Writes a uint64 field value, including tag, to the stream.
148 /// </summary>
149 [CLSCompliant(false)]
150 public void WriteUInt64(int fieldNumber, ulong value)
151 {
152 WriteTag(fieldNumber, WireFormat.WireType.Varint);
153 WriteRawVarint64(value);
154 }
155
156 /// <summary>
157 /// Writes an int64 field value, including tag, to the stream.
158 /// </summary>
159 public void WriteInt64(int fieldNumber, long value)
160 {
161 WriteTag(fieldNumber, WireFormat.WireType.Varint);
162 WriteRawVarint64((ulong) value);
163 }
164
165 /// <summary>
166 /// Writes an int32 field value, including tag, to the stream.
167 /// </summary>
168 public void WriteInt32(int fieldNumber, int value)
169 {
170 WriteTag(fieldNumber, WireFormat.WireType.Varint);
171 if (value >= 0)
172 {
173 WriteRawVarint32((uint) value);
174 }
175 else
176 {
177 // Must sign-extend.
178 WriteRawVarint64((ulong) value);
179 }
180 }
181
182 /// <summary>
183 /// Writes a fixed64 field value, including tag, to the stream.
184 /// </summary>
185 [CLSCompliant(false)]
186 public void WriteFixed64(int fieldNumber, ulong value)
187 {
188 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
189 WriteRawLittleEndian64(value);
190 }
191
192 /// <summary>
193 /// Writes a fixed32 field value, including tag, to the stream.
194 /// </summary>
195 [CLSCompliant(false)]
196 public void WriteFixed32(int fieldNumber, uint value)
197 {
198 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
199 WriteRawLittleEndian32(value);
200 }
201
202 /// <summary>
203 /// Writes a bool field value, including tag, to the stream.
204 /// </summary>
205 public void WriteBool(int fieldNumber, bool value)
206 {
207 WriteTag(fieldNumber, WireFormat.WireType.Varint);
208 WriteRawByte(value ? (byte) 1 : (byte) 0);
209 }
210
211 /// <summary>
212 /// Writes a string field value, including tag, to the stream.
213 /// </summary>
214 public void WriteString(int fieldNumber, string value)
215 {
216 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
217 // Optimise the case where we have enough space to write
218 // the string directly to the buffer, which should be common.
219 int length = Encoding.UTF8.GetByteCount(value);
220 WriteRawVarint32((uint) length);
221 if (limit - position >= length)
222 {
223 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
224 position += length;
225 }
226 else
227 {
228 byte[] bytes = Encoding.UTF8.GetBytes(value);
229 WriteRawBytes(bytes);
230 }
231 }
232
233 /// <summary>
234 /// Writes a group field value, including tag, to the stream.
235 /// </summary>
236 public void WriteGroup(int fieldNumber, IMessageLite value)
237 {
238 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
239 value.WriteTo(this);
240 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
241 }
242
243 [Obsolete]
244 public void WriteUnknownGroup(int fieldNumber, IMessageLite value)
245 {
246 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
247 value.WriteTo(this);
248 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
249 }
250
251 public void WriteMessage(int fieldNumber, IMessageLite value)
252 {
253 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
254 WriteRawVarint32((uint) value.SerializedSize);
255 value.WriteTo(this);
256 }
257
258 public void WriteBytes(int fieldNumber, ByteString value)
259 {
csharptest71f662c2011-05-20 15:15:34 -0500260 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
csharptest45a93fa2011-06-02 10:52:37 -0500261 WriteRawVarint32((uint)value.Length);
262 value.WriteTo(this);
csharptest71f662c2011-05-20 15:15:34 -0500263 }
264
265 [CLSCompliant(false)]
266 public void WriteUInt32(int fieldNumber, uint value)
267 {
268 WriteTag(fieldNumber, WireFormat.WireType.Varint);
269 WriteRawVarint32(value);
270 }
271
272 public void WriteEnum(int fieldNumber, int value)
273 {
274 WriteTag(fieldNumber, WireFormat.WireType.Varint);
275 WriteRawVarint32((uint) value);
276 }
277
278 public void WriteSFixed32(int fieldNumber, int value)
279 {
280 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
281 WriteRawLittleEndian32((uint) value);
282 }
283
284 public void WriteSFixed64(int fieldNumber, long value)
285 {
286 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
287 WriteRawLittleEndian64((ulong) value);
288 }
289
290 public void WriteSInt32(int fieldNumber, int value)
291 {
292 WriteTag(fieldNumber, WireFormat.WireType.Varint);
293 WriteRawVarint32(EncodeZigZag32(value));
294 }
295
296 public void WriteSInt64(int fieldNumber, long value)
297 {
298 WriteTag(fieldNumber, WireFormat.WireType.Varint);
299 WriteRawVarint64(EncodeZigZag64(value));
300 }
301
302 public void WriteMessageSetExtension(int fieldNumber, IMessageLite value)
303 {
304 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
305 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint) fieldNumber);
306 WriteMessage(WireFormat.MessageSetField.Message, value);
307 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
308 }
309
310 public void WriteRawMessageSetExtension(int fieldNumber, ByteString value)
311 {
312 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
313 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint) fieldNumber);
314 WriteBytes(WireFormat.MessageSetField.Message, value);
315 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
316 }
317
318 public void WriteField(FieldType fieldType, int fieldNumber, object value)
319 {
320 switch (fieldType)
321 {
322 case FieldType.Double:
323 WriteDouble(fieldNumber, (double) value);
324 break;
325 case FieldType.Float:
326 WriteFloat(fieldNumber, (float) value);
327 break;
328 case FieldType.Int64:
329 WriteInt64(fieldNumber, (long) value);
330 break;
331 case FieldType.UInt64:
332 WriteUInt64(fieldNumber, (ulong) value);
333 break;
334 case FieldType.Int32:
335 WriteInt32(fieldNumber, (int) value);
336 break;
337 case FieldType.Fixed64:
338 WriteFixed64(fieldNumber, (ulong) value);
339 break;
340 case FieldType.Fixed32:
341 WriteFixed32(fieldNumber, (uint) value);
342 break;
343 case FieldType.Bool:
344 WriteBool(fieldNumber, (bool) value);
345 break;
346 case FieldType.String:
347 WriteString(fieldNumber, (string) value);
348 break;
349 case FieldType.Group:
350 WriteGroup(fieldNumber, (IMessageLite) value);
351 break;
352 case FieldType.Message:
353 WriteMessage(fieldNumber, (IMessageLite) value);
354 break;
355 case FieldType.Bytes:
356 WriteBytes(fieldNumber, (ByteString) value);
357 break;
358 case FieldType.UInt32:
359 WriteUInt32(fieldNumber, (uint) value);
360 break;
361 case FieldType.SFixed32:
362 WriteSFixed32(fieldNumber, (int) value);
363 break;
364 case FieldType.SFixed64:
365 WriteSFixed64(fieldNumber, (long) value);
366 break;
367 case FieldType.SInt32:
368 WriteSInt32(fieldNumber, (int) value);
369 break;
370 case FieldType.SInt64:
371 WriteSInt64(fieldNumber, (long) value);
372 break;
373 case FieldType.Enum:
374 WriteEnum(fieldNumber, ((IEnumLite) value).Number);
375 break;
376 }
377 }
378
379 public void WriteFieldNoTag(FieldType fieldType, object value)
380 {
381 switch (fieldType)
382 {
383 case FieldType.Double:
384 WriteDoubleNoTag((double) value);
385 break;
386 case FieldType.Float:
387 WriteFloatNoTag((float) value);
388 break;
389 case FieldType.Int64:
390 WriteInt64NoTag((long) value);
391 break;
392 case FieldType.UInt64:
393 WriteUInt64NoTag((ulong) value);
394 break;
395 case FieldType.Int32:
396 WriteInt32NoTag((int) value);
397 break;
398 case FieldType.Fixed64:
399 WriteFixed64NoTag((ulong) value);
400 break;
401 case FieldType.Fixed32:
402 WriteFixed32NoTag((uint) value);
403 break;
404 case FieldType.Bool:
405 WriteBoolNoTag((bool) value);
406 break;
407 case FieldType.String:
408 WriteStringNoTag((string) value);
409 break;
410 case FieldType.Group:
411 WriteGroupNoTag((IMessageLite) value);
412 break;
413 case FieldType.Message:
414 WriteMessageNoTag((IMessageLite) value);
415 break;
416 case FieldType.Bytes:
417 WriteBytesNoTag((ByteString) value);
418 break;
419 case FieldType.UInt32:
420 WriteUInt32NoTag((uint) value);
421 break;
422 case FieldType.SFixed32:
423 WriteSFixed32NoTag((int) value);
424 break;
425 case FieldType.SFixed64:
426 WriteSFixed64NoTag((long) value);
427 break;
428 case FieldType.SInt32:
429 WriteSInt32NoTag((int) value);
430 break;
431 case FieldType.SInt64:
432 WriteSInt64NoTag((long) value);
433 break;
434 case FieldType.Enum:
435 WriteEnumNoTag(((IEnumLite) value).Number);
436 break;
437 }
438 }
439
440 #endregion
441
442 #region Writing of values without tags
443
444 /// <summary>
445 /// Writes a double field value, including tag, to the stream.
446 /// </summary>
447 public void WriteDoubleNoTag(double value)
448 {
449 // TODO(jonskeet): Test this on different endiannesses
450#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
451 byte[] bytes = BitConverter.GetBytes(value);
452 WriteRawBytes(bytes, 0, 8);
453#else
454 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
455#endif
456 }
457
458 /// <summary>
459 /// Writes a float field value, without a tag, to the stream.
460 /// </summary>
461 public void WriteFloatNoTag(float value)
462 {
463 // TODO(jonskeet): Test this on different endiannesses
464 byte[] rawBytes = BitConverter.GetBytes(value);
465 uint asInteger = BitConverter.ToUInt32(rawBytes, 0);
466 WriteRawLittleEndian32(asInteger);
467 }
468
469 /// <summary>
470 /// Writes a uint64 field value, without a tag, to the stream.
471 /// </summary>
472 [CLSCompliant(false)]
473 public void WriteUInt64NoTag(ulong value)
474 {
475 WriteRawVarint64(value);
476 }
477
478 /// <summary>
479 /// Writes an int64 field value, without a tag, to the stream.
480 /// </summary>
481 public void WriteInt64NoTag(long value)
482 {
483 WriteRawVarint64((ulong) value);
484 }
485
486 /// <summary>
487 /// Writes an int32 field value, without a tag, to the stream.
488 /// </summary>
489 public void WriteInt32NoTag(int value)
490 {
491 if (value >= 0)
492 {
493 WriteRawVarint32((uint) value);
494 }
495 else
496 {
497 // Must sign-extend.
498 WriteRawVarint64((ulong) value);
499 }
500 }
501
502 /// <summary>
503 /// Writes a fixed64 field value, without a tag, to the stream.
504 /// </summary>
505 [CLSCompliant(false)]
506 public void WriteFixed64NoTag(ulong value)
507 {
508 WriteRawLittleEndian64(value);
509 }
510
511 /// <summary>
512 /// Writes a fixed32 field value, without a tag, to the stream.
513 /// </summary>
514 [CLSCompliant(false)]
515 public void WriteFixed32NoTag(uint value)
516 {
517 WriteRawLittleEndian32(value);
518 }
519
520 /// <summary>
521 /// Writes a bool field value, without a tag, to the stream.
522 /// </summary>
523 public void WriteBoolNoTag(bool value)
524 {
525 WriteRawByte(value ? (byte) 1 : (byte) 0);
526 }
527
528 /// <summary>
529 /// Writes a string field value, without a tag, to the stream.
530 /// </summary>
531 public void WriteStringNoTag(string value)
532 {
533 // Optimise the case where we have enough space to write
534 // the string directly to the buffer, which should be common.
535 int length = Encoding.UTF8.GetByteCount(value);
536 WriteRawVarint32((uint) length);
537 if (limit - position >= length)
538 {
539 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
540 position += length;
541 }
542 else
543 {
544 byte[] bytes = Encoding.UTF8.GetBytes(value);
545 WriteRawBytes(bytes);
546 }
547 }
548
549 /// <summary>
550 /// Writes a group field value, without a tag, to the stream.
551 /// </summary>
552 public void WriteGroupNoTag(IMessageLite value)
553 {
554 value.WriteTo(this);
555 }
556
557 public void WriteMessageNoTag(IMessageLite value)
558 {
559 WriteRawVarint32((uint) value.SerializedSize);
560 value.WriteTo(this);
561 }
562
563 public void WriteBytesNoTag(ByteString value)
564 {
csharptest45a93fa2011-06-02 10:52:37 -0500565 WriteRawVarint32((uint)value.Length);
566 value.WriteTo(this);
csharptest71f662c2011-05-20 15:15:34 -0500567 }
568
569 [CLSCompliant(false)]
570 public void WriteUInt32NoTag(uint value)
571 {
572 WriteRawVarint32(value);
573 }
574
575 public void WriteEnumNoTag(int value)
576 {
577 WriteRawVarint32((uint) value);
578 }
579
580 public void WriteSFixed32NoTag(int value)
581 {
582 WriteRawLittleEndian32((uint) value);
583 }
584
585 public void WriteSFixed64NoTag(long value)
586 {
587 WriteRawLittleEndian64((ulong) value);
588 }
589
590 public void WriteSInt32NoTag(int value)
591 {
592 WriteRawVarint32(EncodeZigZag32(value));
593 }
594
595 public void WriteSInt64NoTag(long value)
596 {
597 WriteRawVarint64(EncodeZigZag64(value));
598 }
599
600 #endregion
601
602 #region Underlying writing primitives
603
604 /// <summary>
605 /// Encodes and writes a tag.
606 /// </summary>
607 [CLSCompliant(false)]
608 public void WriteTag(int fieldNumber, WireFormat.WireType type)
609 {
610 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
611 }
612
613 private void SlowWriteRawVarint32(uint value)
614 {
615 while (true)
616 {
617 if ((value & ~0x7F) == 0)
618 {
619 WriteRawByte(value);
620 return;
621 }
622 else
623 {
624 WriteRawByte((value & 0x7F) | 0x80);
625 value >>= 7;
626 }
627 }
628 }
629
630 /// <summary>
631 /// Writes a 32 bit value as a varint. The fast route is taken when
632 /// there's enough buffer space left to whizz through without checking
633 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
634 /// </summary>
635 [CLSCompliant(false)]
636 public void WriteRawVarint32(uint value)
637 {
638 if (position + 5 > limit)
639 {
640 SlowWriteRawVarint32(value);
641 return;
642 }
643
644 while (true)
645 {
646 if ((value & ~0x7F) == 0)
647 {
648 buffer[position++] = (byte) value;
649 return;
650 }
651 else
652 {
653 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
654 value >>= 7;
655 }
656 }
657 }
658
659 [CLSCompliant(false)]
660 public void WriteRawVarint64(ulong value)
661 {
662 while (true)
663 {
664 if ((value & ~0x7FUL) == 0)
665 {
666 WriteRawByte((uint) value);
667 return;
668 }
669 else
670 {
671 WriteRawByte(((uint) value & 0x7F) | 0x80);
672 value >>= 7;
673 }
674 }
675 }
676
677 [CLSCompliant(false)]
678 public void WriteRawLittleEndian32(uint value)
679 {
680 WriteRawByte((byte) value);
681 WriteRawByte((byte) (value >> 8));
682 WriteRawByte((byte) (value >> 16));
683 WriteRawByte((byte) (value >> 24));
684 }
685
686 [CLSCompliant(false)]
687 public void WriteRawLittleEndian64(ulong value)
688 {
689 WriteRawByte((byte) value);
690 WriteRawByte((byte) (value >> 8));
691 WriteRawByte((byte) (value >> 16));
692 WriteRawByte((byte) (value >> 24));
693 WriteRawByte((byte) (value >> 32));
694 WriteRawByte((byte) (value >> 40));
695 WriteRawByte((byte) (value >> 48));
696 WriteRawByte((byte) (value >> 56));
697 }
698
699 public void WriteRawByte(byte value)
700 {
701 if (position == limit)
702 {
703 RefreshBuffer();
704 }
705
706 buffer[position++] = value;
707 }
708
709 [CLSCompliant(false)]
710 public void WriteRawByte(uint value)
711 {
712 WriteRawByte((byte) value);
713 }
714
715 /// <summary>
716 /// Writes out an array of bytes.
717 /// </summary>
718 public void WriteRawBytes(byte[] value)
719 {
720 WriteRawBytes(value, 0, value.Length);
721 }
722
723 /// <summary>
724 /// Writes out part of an array of bytes.
725 /// </summary>
726 public void WriteRawBytes(byte[] value, int offset, int length)
727 {
728 if (limit - position >= length)
729 {
730 Array.Copy(value, offset, buffer, position, length);
731 // We have room in the current buffer.
732 position += length;
733 }
734 else
735 {
736 // Write extends past current buffer. Fill the rest of this buffer and
737 // flush.
738 int bytesWritten = limit - position;
739 Array.Copy(value, offset, buffer, position, bytesWritten);
740 offset += bytesWritten;
741 length -= bytesWritten;
742 position = limit;
743 RefreshBuffer();
744
745 // Now deal with the rest.
746 // Since we have an output stream, this is our buffer
747 // and buffer offset == 0
748 if (length <= limit)
749 {
750 // Fits in new buffer.
751 Array.Copy(value, offset, buffer, 0, length);
752 position = length;
753 }
754 else
755 {
756 // Write is very big. Let's do it all at once.
757 output.Write(value, offset, length);
758 }
759 }
760 }
761
762 #endregion
763
764 #region Size computations
765
766 private const int LittleEndian64Size = 8;
767 private const int LittleEndian32Size = 4;
768
769 /// <summary>
770 /// Compute the number of bytes that would be needed to encode a
771 /// double field, including the tag.
772 /// </summary>
773 public static int ComputeDoubleSize(int fieldNumber, double value)
774 {
775 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
776 }
777
778 /// <summary>
779 /// Compute the number of bytes that would be needed to encode a
780 /// float field, including the tag.
781 /// </summary>
782 public static int ComputeFloatSize(int fieldNumber, float value)
783 {
784 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
785 }
786
787 /// <summary>
788 /// Compute the number of bytes that would be needed to encode a
789 /// uint64 field, including the tag.
790 /// </summary>
791 [CLSCompliant(false)]
792 public static int ComputeUInt64Size(int fieldNumber, ulong value)
793 {
794 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
795 }
796
797 /// <summary>
798 /// Compute the number of bytes that would be needed to encode an
799 /// int64 field, including the tag.
800 /// </summary>
801 public static int ComputeInt64Size(int fieldNumber, long value)
802 {
803 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong) value);
804 }
805
806 /// <summary>
807 /// Compute the number of bytes that would be needed to encode an
808 /// int32 field, including the tag.
809 /// </summary>
810 public static int ComputeInt32Size(int fieldNumber, int value)
811 {
812 if (value >= 0)
813 {
814 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) value);
815 }
816 else
817 {
818 // Must sign-extend.
819 return ComputeTagSize(fieldNumber) + 10;
820 }
821 }
822
823 /// <summary>
824 /// Compute the number of bytes that would be needed to encode a
825 /// fixed64 field, including the tag.
826 /// </summary>
827 [CLSCompliant(false)]
828 public static int ComputeFixed64Size(int fieldNumber, ulong value)
829 {
830 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
831 }
832
833 /// <summary>
834 /// Compute the number of bytes that would be needed to encode a
835 /// fixed32 field, including the tag.
836 /// </summary>
837 [CLSCompliant(false)]
838 public static int ComputeFixed32Size(int fieldNumber, uint value)
839 {
840 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
841 }
842
843 /// <summary>
844 /// Compute the number of bytes that would be needed to encode a
845 /// bool field, including the tag.
846 /// </summary>
847 public static int ComputeBoolSize(int fieldNumber, bool value)
848 {
849 return ComputeTagSize(fieldNumber) + 1;
850 }
851
852 /// <summary>
853 /// Compute the number of bytes that would be needed to encode a
854 /// string field, including the tag.
855 /// </summary>
856 public static int ComputeStringSize(int fieldNumber, String value)
857 {
858 int byteArraySize = Encoding.UTF8.GetByteCount(value);
859 return ComputeTagSize(fieldNumber) +
860 ComputeRawVarint32Size((uint) byteArraySize) +
861 byteArraySize;
862 }
863
864 /// <summary>
865 /// Compute the number of bytes that would be needed to encode a
866 /// group field, including the tag.
867 /// </summary>
868 public static int ComputeGroupSize(int fieldNumber, IMessageLite value)
869 {
870 return ComputeTagSize(fieldNumber)*2 + value.SerializedSize;
871 }
872
873 /// <summary>
874 /// Compute the number of bytes that would be needed to encode a
875 /// group field represented by an UnknownFieldSet, including the tag.
876 /// </summary>
877 [Obsolete]
878 public static int ComputeUnknownGroupSize(int fieldNumber,
879 IMessageLite value)
880 {
881 return ComputeTagSize(fieldNumber)*2 + value.SerializedSize;
882 }
883
884 /// <summary>
885 /// Compute the number of bytes that would be needed to encode an
886 /// embedded message field, including the tag.
887 /// </summary>
888 public static int ComputeMessageSize(int fieldNumber, IMessageLite value)
889 {
890 int size = value.SerializedSize;
891 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) size) + size;
892 }
893
894 /// <summary>
895 /// Compute the number of bytes that would be needed to encode a
896 /// bytes field, including the tag.
897 /// </summary>
898 public static int ComputeBytesSize(int fieldNumber, ByteString value)
899 {
900 return ComputeTagSize(fieldNumber) +
901 ComputeRawVarint32Size((uint) value.Length) +
902 value.Length;
903 }
904
905 /// <summary>
906 /// Compute the number of bytes that would be needed to encode a
907 /// uint32 field, including the tag.
908 /// </summary>
909 [CLSCompliant(false)]
910 public static int ComputeUInt32Size(int fieldNumber, uint value)
911 {
912 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
913 }
914
915 /// <summary>
916 /// Compute the number of bytes that would be needed to encode a
917 /// enum field, including the tag. The caller is responsible for
918 /// converting the enum value to its numeric value.
919 /// </summary>
920 public static int ComputeEnumSize(int fieldNumber, int value)
921 {
922 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) value);
923 }
924
925 /// <summary>
926 /// Compute the number of bytes that would be needed to encode an
927 /// sfixed32 field, including the tag.
928 /// </summary>
929 public static int ComputeSFixed32Size(int fieldNumber, int value)
930 {
931 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
932 }
933
934 /// <summary>
935 /// Compute the number of bytes that would be needed to encode an
936 /// sfixed64 field, including the tag.
937 /// </summary>
938 public static int ComputeSFixed64Size(int fieldNumber, long value)
939 {
940 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
941 }
942
943 /// <summary>
944 /// Compute the number of bytes that would be needed to encode an
945 /// sint32 field, including the tag.
946 /// </summary>
947 public static int ComputeSInt32Size(int fieldNumber, int value)
948 {
949 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(EncodeZigZag32(value));
950 }
951
952 /// <summary>
953 /// Compute the number of bytes that would be needed to encode an
954 /// sint64 field, including the tag.
955 /// </summary>
956 public static int ComputeSInt64Size(int fieldNumber, long value)
957 {
958 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(EncodeZigZag64(value));
959 }
960
961 /// <summary>
962 /// Compute the number of bytes that would be needed to encode a
963 /// double field, including the tag.
964 /// </summary>
965 public static int ComputeDoubleSizeNoTag(double value)
966 {
967 return LittleEndian64Size;
968 }
969
970 /// <summary>
971 /// Compute the number of bytes that would be needed to encode a
972 /// float field, including the tag.
973 /// </summary>
974 public static int ComputeFloatSizeNoTag(float value)
975 {
976 return LittleEndian32Size;
977 }
978
979 /// <summary>
980 /// Compute the number of bytes that would be needed to encode a
981 /// uint64 field, including the tag.
982 /// </summary>
983 [CLSCompliant(false)]
984 public static int ComputeUInt64SizeNoTag(ulong value)
985 {
986 return ComputeRawVarint64Size(value);
987 }
988
989 /// <summary>
990 /// Compute the number of bytes that would be needed to encode an
991 /// int64 field, including the tag.
992 /// </summary>
993 public static int ComputeInt64SizeNoTag(long value)
994 {
995 return ComputeRawVarint64Size((ulong) value);
996 }
997
998 /// <summary>
999 /// Compute the number of bytes that would be needed to encode an
1000 /// int32 field, including the tag.
1001 /// </summary>
1002 public static int ComputeInt32SizeNoTag(int value)
1003 {
1004 if (value >= 0)
1005 {
1006 return ComputeRawVarint32Size((uint) value);
1007 }
1008 else
1009 {
1010 // Must sign-extend.
1011 return 10;
1012 }
1013 }
1014
1015 /// <summary>
1016 /// Compute the number of bytes that would be needed to encode a
1017 /// fixed64 field, including the tag.
1018 /// </summary>
1019 [CLSCompliant(false)]
1020 public static int ComputeFixed64SizeNoTag(ulong value)
1021 {
1022 return LittleEndian64Size;
1023 }
1024
1025 /// <summary>
1026 /// Compute the number of bytes that would be needed to encode a
1027 /// fixed32 field, including the tag.
1028 /// </summary>
1029 [CLSCompliant(false)]
1030 public static int ComputeFixed32SizeNoTag(uint value)
1031 {
1032 return LittleEndian32Size;
1033 }
1034
1035 /// <summary>
1036 /// Compute the number of bytes that would be needed to encode a
1037 /// bool field, including the tag.
1038 /// </summary>
1039 public static int ComputeBoolSizeNoTag(bool value)
1040 {
1041 return 1;
1042 }
1043
1044 /// <summary>
1045 /// Compute the number of bytes that would be needed to encode a
1046 /// string field, including the tag.
1047 /// </summary>
1048 public static int ComputeStringSizeNoTag(String value)
1049 {
1050 int byteArraySize = Encoding.UTF8.GetByteCount(value);
1051 return ComputeRawVarint32Size((uint) byteArraySize) +
1052 byteArraySize;
1053 }
1054
1055 /// <summary>
1056 /// Compute the number of bytes that would be needed to encode a
1057 /// group field, including the tag.
1058 /// </summary>
1059 public static int ComputeGroupSizeNoTag(IMessageLite value)
1060 {
1061 return value.SerializedSize;
1062 }
1063
1064 /// <summary>
1065 /// Compute the number of bytes that would be needed to encode a
1066 /// group field represented by an UnknownFieldSet, including the tag.
1067 /// </summary>
1068 [Obsolete]
1069 public static int ComputeUnknownGroupSizeNoTag(IMessageLite value)
1070 {
1071 return value.SerializedSize;
1072 }
1073
1074 /// <summary>
1075 /// Compute the number of bytes that would be needed to encode an
1076 /// embedded message field, including the tag.
1077 /// </summary>
1078 public static int ComputeMessageSizeNoTag(IMessageLite value)
1079 {
1080 int size = value.SerializedSize;
1081 return ComputeRawVarint32Size((uint) size) + size;
1082 }
1083
1084 /// <summary>
1085 /// Compute the number of bytes that would be needed to encode a
1086 /// bytes field, including the tag.
1087 /// </summary>
1088 public static int ComputeBytesSizeNoTag(ByteString value)
1089 {
1090 return ComputeRawVarint32Size((uint) value.Length) +
1091 value.Length;
1092 }
1093
1094 /// <summary>
1095 /// Compute the number of bytes that would be needed to encode a
1096 /// uint32 field, including the tag.
1097 /// </summary>
1098 [CLSCompliant(false)]
1099 public static int ComputeUInt32SizeNoTag(uint value)
1100 {
1101 return ComputeRawVarint32Size(value);
1102 }
1103
1104 /// <summary>
1105 /// Compute the number of bytes that would be needed to encode a
1106 /// enum field, including the tag. The caller is responsible for
1107 /// converting the enum value to its numeric value.
1108 /// </summary>
1109 public static int ComputeEnumSizeNoTag(int value)
1110 {
1111 return ComputeRawVarint32Size((uint) value);
1112 }
1113
1114 /// <summary>
1115 /// Compute the number of bytes that would be needed to encode an
1116 /// sfixed32 field, including the tag.
1117 /// </summary>
1118 public static int ComputeSFixed32SizeNoTag(int value)
1119 {
1120 return LittleEndian32Size;
1121 }
1122
1123 /// <summary>
1124 /// Compute the number of bytes that would be needed to encode an
1125 /// sfixed64 field, including the tag.
1126 /// </summary>
1127 public static int ComputeSFixed64SizeNoTag(long value)
1128 {
1129 return LittleEndian64Size;
1130 }
1131
1132 /// <summary>
1133 /// Compute the number of bytes that would be needed to encode an
1134 /// sint32 field, including the tag.
1135 /// </summary>
1136 public static int ComputeSInt32SizeNoTag(int value)
1137 {
1138 return ComputeRawVarint32Size(EncodeZigZag32(value));
1139 }
1140
1141 /// <summary>
1142 /// Compute the number of bytes that would be needed to encode an
1143 /// sint64 field, including the tag.
1144 /// </summary>
1145 public static int ComputeSInt64SizeNoTag(long value)
1146 {
1147 return ComputeRawVarint64Size(EncodeZigZag64(value));
1148 }
1149
1150 /*
1151 * Compute the number of bytes that would be needed to encode a
1152 * MessageSet extension to the stream. For historical reasons,
1153 * the wire format differs from normal fields.
1154 */
1155
1156 /// <summary>
1157 /// Compute the number of bytes that would be needed to encode a
1158 /// MessageSet extension to the stream. For historical reasons,
1159 /// the wire format differs from normal fields.
1160 /// </summary>
1161 public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessageLite value)
1162 {
1163 return ComputeTagSize(WireFormat.MessageSetField.Item)*2 +
1164 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
1165 ComputeMessageSize(WireFormat.MessageSetField.Message, value);
1166 }
1167
1168 /// <summary>
1169 /// Compute the number of bytes that would be needed to encode an
1170 /// unparsed MessageSet extension field to the stream. For
1171 /// historical reasons, the wire format differs from normal fields.
1172 /// </summary>
1173 public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value)
1174 {
1175 return ComputeTagSize(WireFormat.MessageSetField.Item)*2 +
1176 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
1177 ComputeBytesSize(WireFormat.MessageSetField.Message, value);
1178 }
1179
1180 /// <summary>
1181 /// Compute the number of bytes that would be needed to encode a varint.
1182 /// </summary>
1183 [CLSCompliant(false)]
1184 public static int ComputeRawVarint32Size(uint value)
1185 {
1186 if ((value & (0xffffffff << 7)) == 0) return 1;
1187 if ((value & (0xffffffff << 14)) == 0) return 2;
1188 if ((value & (0xffffffff << 21)) == 0) return 3;
1189 if ((value & (0xffffffff << 28)) == 0) return 4;
1190 return 5;
1191 }
1192
1193 /// <summary>
1194 /// Compute the number of bytes that would be needed to encode a varint.
1195 /// </summary>
1196 [CLSCompliant(false)]
1197 public static int ComputeRawVarint64Size(ulong value)
1198 {
1199 if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
1200 if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
1201 if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
1202 if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
1203 if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
1204 if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
1205 if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
1206 if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
1207 if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
1208 return 10;
1209 }
1210
1211 /// <summary>
1212 /// Compute the number of bytes that would be needed to encode a
1213 /// field of arbitrary type, including the tag, to the stream.
1214 /// </summary>
1215 public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value)
1216 {
1217 switch (fieldType)
1218 {
1219 case FieldType.Double:
1220 return ComputeDoubleSize(fieldNumber, (double) value);
1221 case FieldType.Float:
1222 return ComputeFloatSize(fieldNumber, (float) value);
1223 case FieldType.Int64:
1224 return ComputeInt64Size(fieldNumber, (long) value);
1225 case FieldType.UInt64:
1226 return ComputeUInt64Size(fieldNumber, (ulong) value);
1227 case FieldType.Int32:
1228 return ComputeInt32Size(fieldNumber, (int) value);
1229 case FieldType.Fixed64:
1230 return ComputeFixed64Size(fieldNumber, (ulong) value);
1231 case FieldType.Fixed32:
1232 return ComputeFixed32Size(fieldNumber, (uint) value);
1233 case FieldType.Bool:
1234 return ComputeBoolSize(fieldNumber, (bool) value);
1235 case FieldType.String:
1236 return ComputeStringSize(fieldNumber, (string) value);
1237 case FieldType.Group:
1238 return ComputeGroupSize(fieldNumber, (IMessageLite) value);
1239 case FieldType.Message:
1240 return ComputeMessageSize(fieldNumber, (IMessageLite) value);
1241 case FieldType.Bytes:
1242 return ComputeBytesSize(fieldNumber, (ByteString) value);
1243 case FieldType.UInt32:
1244 return ComputeUInt32Size(fieldNumber, (uint) value);
1245 case FieldType.SFixed32:
1246 return ComputeSFixed32Size(fieldNumber, (int) value);
1247 case FieldType.SFixed64:
1248 return ComputeSFixed64Size(fieldNumber, (long) value);
1249 case FieldType.SInt32:
1250 return ComputeSInt32Size(fieldNumber, (int) value);
1251 case FieldType.SInt64:
1252 return ComputeSInt64Size(fieldNumber, (long) value);
1253 case FieldType.Enum:
1254 return ComputeEnumSize(fieldNumber, ((IEnumLite) value).Number);
1255 default:
1256 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1257 }
1258 }
1259
1260 /// <summary>
1261 /// Compute the number of bytes that would be needed to encode a
1262 /// field of arbitrary type, excluding the tag, to the stream.
1263 /// </summary>
1264 public static int ComputeFieldSizeNoTag(FieldType fieldType, Object value)
1265 {
1266 switch (fieldType)
1267 {
1268 case FieldType.Double:
1269 return ComputeDoubleSizeNoTag((double) value);
1270 case FieldType.Float:
1271 return ComputeFloatSizeNoTag((float) value);
1272 case FieldType.Int64:
1273 return ComputeInt64SizeNoTag((long) value);
1274 case FieldType.UInt64:
1275 return ComputeUInt64SizeNoTag((ulong) value);
1276 case FieldType.Int32:
1277 return ComputeInt32SizeNoTag((int) value);
1278 case FieldType.Fixed64:
1279 return ComputeFixed64SizeNoTag((ulong) value);
1280 case FieldType.Fixed32:
1281 return ComputeFixed32SizeNoTag((uint) value);
1282 case FieldType.Bool:
1283 return ComputeBoolSizeNoTag((bool) value);
1284 case FieldType.String:
1285 return ComputeStringSizeNoTag((string) value);
1286 case FieldType.Group:
1287 return ComputeGroupSizeNoTag((IMessageLite) value);
1288 case FieldType.Message:
1289 return ComputeMessageSizeNoTag((IMessageLite) value);
1290 case FieldType.Bytes:
1291 return ComputeBytesSizeNoTag((ByteString) value);
1292 case FieldType.UInt32:
1293 return ComputeUInt32SizeNoTag((uint) value);
1294 case FieldType.SFixed32:
1295 return ComputeSFixed32SizeNoTag((int) value);
1296 case FieldType.SFixed64:
1297 return ComputeSFixed64SizeNoTag((long) value);
1298 case FieldType.SInt32:
1299 return ComputeSInt32SizeNoTag((int) value);
1300 case FieldType.SInt64:
1301 return ComputeSInt64SizeNoTag((long) value);
1302 case FieldType.Enum:
1303 return ComputeEnumSizeNoTag(((IEnumLite) value).Number);
1304 default:
1305 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1306 }
1307 }
1308
1309 /// <summary>
1310 /// Compute the number of bytes that would be needed to encode a tag.
1311 /// </summary>
1312 public static int ComputeTagSize(int fieldNumber)
1313 {
1314 return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
1315 }
1316
1317 #endregion
1318
1319 /// <summary>
1320 /// Encode a 32-bit value with ZigZag encoding.
1321 /// </summary>
1322 /// <remarks>
1323 /// ZigZag encodes signed integers into values that can be efficiently
1324 /// encoded with varint. (Otherwise, negative values must be
1325 /// sign-extended to 64 bits to be varint encoded, thus always taking
1326 /// 10 bytes on the wire.)
1327 /// </remarks>
1328 [CLSCompliant(false)]
1329 public static uint EncodeZigZag32(int n)
1330 {
1331 // Note: the right-shift must be arithmetic
1332 return (uint) ((n << 1) ^ (n >> 31));
1333 }
1334
1335 /// <summary>
1336 /// Encode a 64-bit value with ZigZag encoding.
1337 /// </summary>
1338 /// <remarks>
1339 /// ZigZag encodes signed integers into values that can be efficiently
1340 /// encoded with varint. (Otherwise, negative values must be
1341 /// sign-extended to 64 bits to be varint encoded, thus always taking
1342 /// 10 bytes on the wire.)
1343 /// </remarks>
1344 [CLSCompliant(false)]
1345 public static ulong EncodeZigZag64(long n)
1346 {
1347 return (ulong) ((n << 1) ^ (n >> 63));
1348 }
1349
1350 private void RefreshBuffer()
1351 {
1352 if (output == null)
1353 {
1354 // We're writing to a single buffer.
1355 throw new OutOfSpaceException();
1356 }
1357
1358 // Since we have an output stream, this is our buffer
1359 // and buffer offset == 0
1360 output.Write(buffer, 0, position);
1361 position = 0;
1362 }
1363
1364 /// <summary>
1365 /// Indicates that a CodedOutputStream wrapping a flat byte array
1366 /// ran out of space.
1367 /// </summary>
1368 public sealed class OutOfSpaceException : IOException
1369 {
1370 internal OutOfSpaceException()
1371 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
1372 {
1373 }
1374 }
1375
1376 public void Flush()
1377 {
1378 if (output != null)
1379 {
1380 RefreshBuffer();
1381 }
1382 }
1383
1384 /// <summary>
1385 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
1386 /// that is exactly big enough to hold a message, then write to it with
1387 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
1388 /// the message was actually as big as expected, which can help bugs.
1389 /// </summary>
1390 public void CheckNoSpaceLeft()
1391 {
1392 if (SpaceLeft != 0)
1393 {
1394 throw new InvalidOperationException("Did not write as much data as expected.");
1395 }
1396 }
1397
1398 /// <summary>
1399 /// If writing to a flat array, returns the space left in the array. Otherwise,
1400 /// throws an InvalidOperationException.
1401 /// </summary>
1402 public int SpaceLeft
1403 {
1404 get
1405 {
1406 if (output == null)
1407 {
1408 return limit - position;
1409 }
1410 else
1411 {
1412 throw new InvalidOperationException(
1413 "SpaceLeft can only be called on CodedOutputStreams that are " +
1414 "writing to a flat array.");
1415 }
1416 }
1417 }
1418 }
1419}