blob: f905b357cdfea9a3a00fb5b23a8240c332daae4c [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>
57 public sealed class CodedOutputStream
58 {
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 {
260 // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
261 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
262 byte[] bytes = value.ToByteArray();
263 WriteRawVarint32((uint) bytes.Length);
264 WriteRawBytes(bytes);
265 }
266
267 [CLSCompliant(false)]
268 public void WriteUInt32(int fieldNumber, uint value)
269 {
270 WriteTag(fieldNumber, WireFormat.WireType.Varint);
271 WriteRawVarint32(value);
272 }
273
274 public void WriteEnum(int fieldNumber, int value)
275 {
276 WriteTag(fieldNumber, WireFormat.WireType.Varint);
277 WriteRawVarint32((uint) value);
278 }
279
280 public void WriteSFixed32(int fieldNumber, int value)
281 {
282 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
283 WriteRawLittleEndian32((uint) value);
284 }
285
286 public void WriteSFixed64(int fieldNumber, long value)
287 {
288 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
289 WriteRawLittleEndian64((ulong) value);
290 }
291
292 public void WriteSInt32(int fieldNumber, int value)
293 {
294 WriteTag(fieldNumber, WireFormat.WireType.Varint);
295 WriteRawVarint32(EncodeZigZag32(value));
296 }
297
298 public void WriteSInt64(int fieldNumber, long value)
299 {
300 WriteTag(fieldNumber, WireFormat.WireType.Varint);
301 WriteRawVarint64(EncodeZigZag64(value));
302 }
303
304 public void WriteMessageSetExtension(int fieldNumber, IMessageLite value)
305 {
306 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
307 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint) fieldNumber);
308 WriteMessage(WireFormat.MessageSetField.Message, value);
309 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
310 }
311
312 public void WriteRawMessageSetExtension(int fieldNumber, ByteString value)
313 {
314 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
315 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint) fieldNumber);
316 WriteBytes(WireFormat.MessageSetField.Message, value);
317 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
318 }
319
320 public void WriteField(FieldType fieldType, int fieldNumber, object value)
321 {
322 switch (fieldType)
323 {
324 case FieldType.Double:
325 WriteDouble(fieldNumber, (double) value);
326 break;
327 case FieldType.Float:
328 WriteFloat(fieldNumber, (float) value);
329 break;
330 case FieldType.Int64:
331 WriteInt64(fieldNumber, (long) value);
332 break;
333 case FieldType.UInt64:
334 WriteUInt64(fieldNumber, (ulong) value);
335 break;
336 case FieldType.Int32:
337 WriteInt32(fieldNumber, (int) value);
338 break;
339 case FieldType.Fixed64:
340 WriteFixed64(fieldNumber, (ulong) value);
341 break;
342 case FieldType.Fixed32:
343 WriteFixed32(fieldNumber, (uint) value);
344 break;
345 case FieldType.Bool:
346 WriteBool(fieldNumber, (bool) value);
347 break;
348 case FieldType.String:
349 WriteString(fieldNumber, (string) value);
350 break;
351 case FieldType.Group:
352 WriteGroup(fieldNumber, (IMessageLite) value);
353 break;
354 case FieldType.Message:
355 WriteMessage(fieldNumber, (IMessageLite) value);
356 break;
357 case FieldType.Bytes:
358 WriteBytes(fieldNumber, (ByteString) value);
359 break;
360 case FieldType.UInt32:
361 WriteUInt32(fieldNumber, (uint) value);
362 break;
363 case FieldType.SFixed32:
364 WriteSFixed32(fieldNumber, (int) value);
365 break;
366 case FieldType.SFixed64:
367 WriteSFixed64(fieldNumber, (long) value);
368 break;
369 case FieldType.SInt32:
370 WriteSInt32(fieldNumber, (int) value);
371 break;
372 case FieldType.SInt64:
373 WriteSInt64(fieldNumber, (long) value);
374 break;
375 case FieldType.Enum:
376 WriteEnum(fieldNumber, ((IEnumLite) value).Number);
377 break;
378 }
379 }
380
381 public void WriteFieldNoTag(FieldType fieldType, object value)
382 {
383 switch (fieldType)
384 {
385 case FieldType.Double:
386 WriteDoubleNoTag((double) value);
387 break;
388 case FieldType.Float:
389 WriteFloatNoTag((float) value);
390 break;
391 case FieldType.Int64:
392 WriteInt64NoTag((long) value);
393 break;
394 case FieldType.UInt64:
395 WriteUInt64NoTag((ulong) value);
396 break;
397 case FieldType.Int32:
398 WriteInt32NoTag((int) value);
399 break;
400 case FieldType.Fixed64:
401 WriteFixed64NoTag((ulong) value);
402 break;
403 case FieldType.Fixed32:
404 WriteFixed32NoTag((uint) value);
405 break;
406 case FieldType.Bool:
407 WriteBoolNoTag((bool) value);
408 break;
409 case FieldType.String:
410 WriteStringNoTag((string) value);
411 break;
412 case FieldType.Group:
413 WriteGroupNoTag((IMessageLite) value);
414 break;
415 case FieldType.Message:
416 WriteMessageNoTag((IMessageLite) value);
417 break;
418 case FieldType.Bytes:
419 WriteBytesNoTag((ByteString) value);
420 break;
421 case FieldType.UInt32:
422 WriteUInt32NoTag((uint) value);
423 break;
424 case FieldType.SFixed32:
425 WriteSFixed32NoTag((int) value);
426 break;
427 case FieldType.SFixed64:
428 WriteSFixed64NoTag((long) value);
429 break;
430 case FieldType.SInt32:
431 WriteSInt32NoTag((int) value);
432 break;
433 case FieldType.SInt64:
434 WriteSInt64NoTag((long) value);
435 break;
436 case FieldType.Enum:
437 WriteEnumNoTag(((IEnumLite) value).Number);
438 break;
439 }
440 }
441
442 #endregion
443
444 #region Writing of values without tags
445
446 /// <summary>
447 /// Writes a double field value, including tag, to the stream.
448 /// </summary>
449 public void WriteDoubleNoTag(double value)
450 {
451 // TODO(jonskeet): Test this on different endiannesses
452#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
453 byte[] bytes = BitConverter.GetBytes(value);
454 WriteRawBytes(bytes, 0, 8);
455#else
456 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
457#endif
458 }
459
460 /// <summary>
461 /// Writes a float field value, without a tag, to the stream.
462 /// </summary>
463 public void WriteFloatNoTag(float value)
464 {
465 // TODO(jonskeet): Test this on different endiannesses
466 byte[] rawBytes = BitConverter.GetBytes(value);
467 uint asInteger = BitConverter.ToUInt32(rawBytes, 0);
468 WriteRawLittleEndian32(asInteger);
469 }
470
471 /// <summary>
472 /// Writes a uint64 field value, without a tag, to the stream.
473 /// </summary>
474 [CLSCompliant(false)]
475 public void WriteUInt64NoTag(ulong value)
476 {
477 WriteRawVarint64(value);
478 }
479
480 /// <summary>
481 /// Writes an int64 field value, without a tag, to the stream.
482 /// </summary>
483 public void WriteInt64NoTag(long value)
484 {
485 WriteRawVarint64((ulong) value);
486 }
487
488 /// <summary>
489 /// Writes an int32 field value, without a tag, to the stream.
490 /// </summary>
491 public void WriteInt32NoTag(int value)
492 {
493 if (value >= 0)
494 {
495 WriteRawVarint32((uint) value);
496 }
497 else
498 {
499 // Must sign-extend.
500 WriteRawVarint64((ulong) value);
501 }
502 }
503
504 /// <summary>
505 /// Writes a fixed64 field value, without a tag, to the stream.
506 /// </summary>
507 [CLSCompliant(false)]
508 public void WriteFixed64NoTag(ulong value)
509 {
510 WriteRawLittleEndian64(value);
511 }
512
513 /// <summary>
514 /// Writes a fixed32 field value, without a tag, to the stream.
515 /// </summary>
516 [CLSCompliant(false)]
517 public void WriteFixed32NoTag(uint value)
518 {
519 WriteRawLittleEndian32(value);
520 }
521
522 /// <summary>
523 /// Writes a bool field value, without a tag, to the stream.
524 /// </summary>
525 public void WriteBoolNoTag(bool value)
526 {
527 WriteRawByte(value ? (byte) 1 : (byte) 0);
528 }
529
530 /// <summary>
531 /// Writes a string field value, without a tag, to the stream.
532 /// </summary>
533 public void WriteStringNoTag(string value)
534 {
535 // Optimise the case where we have enough space to write
536 // the string directly to the buffer, which should be common.
537 int length = Encoding.UTF8.GetByteCount(value);
538 WriteRawVarint32((uint) length);
539 if (limit - position >= length)
540 {
541 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
542 position += length;
543 }
544 else
545 {
546 byte[] bytes = Encoding.UTF8.GetBytes(value);
547 WriteRawBytes(bytes);
548 }
549 }
550
551 /// <summary>
552 /// Writes a group field value, without a tag, to the stream.
553 /// </summary>
554 public void WriteGroupNoTag(IMessageLite value)
555 {
556 value.WriteTo(this);
557 }
558
559 public void WriteMessageNoTag(IMessageLite value)
560 {
561 WriteRawVarint32((uint) value.SerializedSize);
562 value.WriteTo(this);
563 }
564
565 public void WriteBytesNoTag(ByteString value)
566 {
567 // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
568 byte[] bytes = value.ToByteArray();
569 WriteRawVarint32((uint) bytes.Length);
570 WriteRawBytes(bytes);
571 }
572
573 [CLSCompliant(false)]
574 public void WriteUInt32NoTag(uint value)
575 {
576 WriteRawVarint32(value);
577 }
578
579 public void WriteEnumNoTag(int value)
580 {
581 WriteRawVarint32((uint) value);
582 }
583
584 public void WriteSFixed32NoTag(int value)
585 {
586 WriteRawLittleEndian32((uint) value);
587 }
588
589 public void WriteSFixed64NoTag(long value)
590 {
591 WriteRawLittleEndian64((ulong) value);
592 }
593
594 public void WriteSInt32NoTag(int value)
595 {
596 WriteRawVarint32(EncodeZigZag32(value));
597 }
598
599 public void WriteSInt64NoTag(long value)
600 {
601 WriteRawVarint64(EncodeZigZag64(value));
602 }
603
604 #endregion
605
606 #region Underlying writing primitives
607
608 /// <summary>
609 /// Encodes and writes a tag.
610 /// </summary>
611 [CLSCompliant(false)]
612 public void WriteTag(int fieldNumber, WireFormat.WireType type)
613 {
614 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
615 }
616
617 private void SlowWriteRawVarint32(uint value)
618 {
619 while (true)
620 {
621 if ((value & ~0x7F) == 0)
622 {
623 WriteRawByte(value);
624 return;
625 }
626 else
627 {
628 WriteRawByte((value & 0x7F) | 0x80);
629 value >>= 7;
630 }
631 }
632 }
633
634 /// <summary>
635 /// Writes a 32 bit value as a varint. The fast route is taken when
636 /// there's enough buffer space left to whizz through without checking
637 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
638 /// </summary>
639 [CLSCompliant(false)]
640 public void WriteRawVarint32(uint value)
641 {
642 if (position + 5 > limit)
643 {
644 SlowWriteRawVarint32(value);
645 return;
646 }
647
648 while (true)
649 {
650 if ((value & ~0x7F) == 0)
651 {
652 buffer[position++] = (byte) value;
653 return;
654 }
655 else
656 {
657 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
658 value >>= 7;
659 }
660 }
661 }
662
663 [CLSCompliant(false)]
664 public void WriteRawVarint64(ulong value)
665 {
666 while (true)
667 {
668 if ((value & ~0x7FUL) == 0)
669 {
670 WriteRawByte((uint) value);
671 return;
672 }
673 else
674 {
675 WriteRawByte(((uint) value & 0x7F) | 0x80);
676 value >>= 7;
677 }
678 }
679 }
680
681 [CLSCompliant(false)]
682 public void WriteRawLittleEndian32(uint value)
683 {
684 WriteRawByte((byte) value);
685 WriteRawByte((byte) (value >> 8));
686 WriteRawByte((byte) (value >> 16));
687 WriteRawByte((byte) (value >> 24));
688 }
689
690 [CLSCompliant(false)]
691 public void WriteRawLittleEndian64(ulong value)
692 {
693 WriteRawByte((byte) value);
694 WriteRawByte((byte) (value >> 8));
695 WriteRawByte((byte) (value >> 16));
696 WriteRawByte((byte) (value >> 24));
697 WriteRawByte((byte) (value >> 32));
698 WriteRawByte((byte) (value >> 40));
699 WriteRawByte((byte) (value >> 48));
700 WriteRawByte((byte) (value >> 56));
701 }
702
703 public void WriteRawByte(byte value)
704 {
705 if (position == limit)
706 {
707 RefreshBuffer();
708 }
709
710 buffer[position++] = value;
711 }
712
713 [CLSCompliant(false)]
714 public void WriteRawByte(uint value)
715 {
716 WriteRawByte((byte) value);
717 }
718
719 /// <summary>
720 /// Writes out an array of bytes.
721 /// </summary>
722 public void WriteRawBytes(byte[] value)
723 {
724 WriteRawBytes(value, 0, value.Length);
725 }
726
727 /// <summary>
728 /// Writes out part of an array of bytes.
729 /// </summary>
730 public void WriteRawBytes(byte[] value, int offset, int length)
731 {
732 if (limit - position >= length)
733 {
734 Array.Copy(value, offset, buffer, position, length);
735 // We have room in the current buffer.
736 position += length;
737 }
738 else
739 {
740 // Write extends past current buffer. Fill the rest of this buffer and
741 // flush.
742 int bytesWritten = limit - position;
743 Array.Copy(value, offset, buffer, position, bytesWritten);
744 offset += bytesWritten;
745 length -= bytesWritten;
746 position = limit;
747 RefreshBuffer();
748
749 // Now deal with the rest.
750 // Since we have an output stream, this is our buffer
751 // and buffer offset == 0
752 if (length <= limit)
753 {
754 // Fits in new buffer.
755 Array.Copy(value, offset, buffer, 0, length);
756 position = length;
757 }
758 else
759 {
760 // Write is very big. Let's do it all at once.
761 output.Write(value, offset, length);
762 }
763 }
764 }
765
766 #endregion
767
768 #region Size computations
769
770 private const int LittleEndian64Size = 8;
771 private const int LittleEndian32Size = 4;
772
773 /// <summary>
774 /// Compute the number of bytes that would be needed to encode a
775 /// double field, including the tag.
776 /// </summary>
777 public static int ComputeDoubleSize(int fieldNumber, double value)
778 {
779 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
780 }
781
782 /// <summary>
783 /// Compute the number of bytes that would be needed to encode a
784 /// float field, including the tag.
785 /// </summary>
786 public static int ComputeFloatSize(int fieldNumber, float value)
787 {
788 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
789 }
790
791 /// <summary>
792 /// Compute the number of bytes that would be needed to encode a
793 /// uint64 field, including the tag.
794 /// </summary>
795 [CLSCompliant(false)]
796 public static int ComputeUInt64Size(int fieldNumber, ulong value)
797 {
798 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
799 }
800
801 /// <summary>
802 /// Compute the number of bytes that would be needed to encode an
803 /// int64 field, including the tag.
804 /// </summary>
805 public static int ComputeInt64Size(int fieldNumber, long value)
806 {
807 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong) value);
808 }
809
810 /// <summary>
811 /// Compute the number of bytes that would be needed to encode an
812 /// int32 field, including the tag.
813 /// </summary>
814 public static int ComputeInt32Size(int fieldNumber, int value)
815 {
816 if (value >= 0)
817 {
818 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) value);
819 }
820 else
821 {
822 // Must sign-extend.
823 return ComputeTagSize(fieldNumber) + 10;
824 }
825 }
826
827 /// <summary>
828 /// Compute the number of bytes that would be needed to encode a
829 /// fixed64 field, including the tag.
830 /// </summary>
831 [CLSCompliant(false)]
832 public static int ComputeFixed64Size(int fieldNumber, ulong value)
833 {
834 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
835 }
836
837 /// <summary>
838 /// Compute the number of bytes that would be needed to encode a
839 /// fixed32 field, including the tag.
840 /// </summary>
841 [CLSCompliant(false)]
842 public static int ComputeFixed32Size(int fieldNumber, uint value)
843 {
844 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
845 }
846
847 /// <summary>
848 /// Compute the number of bytes that would be needed to encode a
849 /// bool field, including the tag.
850 /// </summary>
851 public static int ComputeBoolSize(int fieldNumber, bool value)
852 {
853 return ComputeTagSize(fieldNumber) + 1;
854 }
855
856 /// <summary>
857 /// Compute the number of bytes that would be needed to encode a
858 /// string field, including the tag.
859 /// </summary>
860 public static int ComputeStringSize(int fieldNumber, String value)
861 {
862 int byteArraySize = Encoding.UTF8.GetByteCount(value);
863 return ComputeTagSize(fieldNumber) +
864 ComputeRawVarint32Size((uint) byteArraySize) +
865 byteArraySize;
866 }
867
868 /// <summary>
869 /// Compute the number of bytes that would be needed to encode a
870 /// group field, including the tag.
871 /// </summary>
872 public static int ComputeGroupSize(int fieldNumber, IMessageLite value)
873 {
874 return ComputeTagSize(fieldNumber)*2 + value.SerializedSize;
875 }
876
877 /// <summary>
878 /// Compute the number of bytes that would be needed to encode a
879 /// group field represented by an UnknownFieldSet, including the tag.
880 /// </summary>
881 [Obsolete]
882 public static int ComputeUnknownGroupSize(int fieldNumber,
883 IMessageLite value)
884 {
885 return ComputeTagSize(fieldNumber)*2 + value.SerializedSize;
886 }
887
888 /// <summary>
889 /// Compute the number of bytes that would be needed to encode an
890 /// embedded message field, including the tag.
891 /// </summary>
892 public static int ComputeMessageSize(int fieldNumber, IMessageLite value)
893 {
894 int size = value.SerializedSize;
895 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) size) + size;
896 }
897
898 /// <summary>
899 /// Compute the number of bytes that would be needed to encode a
900 /// bytes field, including the tag.
901 /// </summary>
902 public static int ComputeBytesSize(int fieldNumber, ByteString value)
903 {
904 return ComputeTagSize(fieldNumber) +
905 ComputeRawVarint32Size((uint) value.Length) +
906 value.Length;
907 }
908
909 /// <summary>
910 /// Compute the number of bytes that would be needed to encode a
911 /// uint32 field, including the tag.
912 /// </summary>
913 [CLSCompliant(false)]
914 public static int ComputeUInt32Size(int fieldNumber, uint value)
915 {
916 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
917 }
918
919 /// <summary>
920 /// Compute the number of bytes that would be needed to encode a
921 /// enum field, including the tag. The caller is responsible for
922 /// converting the enum value to its numeric value.
923 /// </summary>
924 public static int ComputeEnumSize(int fieldNumber, int value)
925 {
926 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint) value);
927 }
928
929 /// <summary>
930 /// Compute the number of bytes that would be needed to encode an
931 /// sfixed32 field, including the tag.
932 /// </summary>
933 public static int ComputeSFixed32Size(int fieldNumber, int value)
934 {
935 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
936 }
937
938 /// <summary>
939 /// Compute the number of bytes that would be needed to encode an
940 /// sfixed64 field, including the tag.
941 /// </summary>
942 public static int ComputeSFixed64Size(int fieldNumber, long value)
943 {
944 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
945 }
946
947 /// <summary>
948 /// Compute the number of bytes that would be needed to encode an
949 /// sint32 field, including the tag.
950 /// </summary>
951 public static int ComputeSInt32Size(int fieldNumber, int value)
952 {
953 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(EncodeZigZag32(value));
954 }
955
956 /// <summary>
957 /// Compute the number of bytes that would be needed to encode an
958 /// sint64 field, including the tag.
959 /// </summary>
960 public static int ComputeSInt64Size(int fieldNumber, long value)
961 {
962 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(EncodeZigZag64(value));
963 }
964
965 /// <summary>
966 /// Compute the number of bytes that would be needed to encode a
967 /// double field, including the tag.
968 /// </summary>
969 public static int ComputeDoubleSizeNoTag(double value)
970 {
971 return LittleEndian64Size;
972 }
973
974 /// <summary>
975 /// Compute the number of bytes that would be needed to encode a
976 /// float field, including the tag.
977 /// </summary>
978 public static int ComputeFloatSizeNoTag(float value)
979 {
980 return LittleEndian32Size;
981 }
982
983 /// <summary>
984 /// Compute the number of bytes that would be needed to encode a
985 /// uint64 field, including the tag.
986 /// </summary>
987 [CLSCompliant(false)]
988 public static int ComputeUInt64SizeNoTag(ulong value)
989 {
990 return ComputeRawVarint64Size(value);
991 }
992
993 /// <summary>
994 /// Compute the number of bytes that would be needed to encode an
995 /// int64 field, including the tag.
996 /// </summary>
997 public static int ComputeInt64SizeNoTag(long value)
998 {
999 return ComputeRawVarint64Size((ulong) value);
1000 }
1001
1002 /// <summary>
1003 /// Compute the number of bytes that would be needed to encode an
1004 /// int32 field, including the tag.
1005 /// </summary>
1006 public static int ComputeInt32SizeNoTag(int value)
1007 {
1008 if (value >= 0)
1009 {
1010 return ComputeRawVarint32Size((uint) value);
1011 }
1012 else
1013 {
1014 // Must sign-extend.
1015 return 10;
1016 }
1017 }
1018
1019 /// <summary>
1020 /// Compute the number of bytes that would be needed to encode a
1021 /// fixed64 field, including the tag.
1022 /// </summary>
1023 [CLSCompliant(false)]
1024 public static int ComputeFixed64SizeNoTag(ulong value)
1025 {
1026 return LittleEndian64Size;
1027 }
1028
1029 /// <summary>
1030 /// Compute the number of bytes that would be needed to encode a
1031 /// fixed32 field, including the tag.
1032 /// </summary>
1033 [CLSCompliant(false)]
1034 public static int ComputeFixed32SizeNoTag(uint value)
1035 {
1036 return LittleEndian32Size;
1037 }
1038
1039 /// <summary>
1040 /// Compute the number of bytes that would be needed to encode a
1041 /// bool field, including the tag.
1042 /// </summary>
1043 public static int ComputeBoolSizeNoTag(bool value)
1044 {
1045 return 1;
1046 }
1047
1048 /// <summary>
1049 /// Compute the number of bytes that would be needed to encode a
1050 /// string field, including the tag.
1051 /// </summary>
1052 public static int ComputeStringSizeNoTag(String value)
1053 {
1054 int byteArraySize = Encoding.UTF8.GetByteCount(value);
1055 return ComputeRawVarint32Size((uint) byteArraySize) +
1056 byteArraySize;
1057 }
1058
1059 /// <summary>
1060 /// Compute the number of bytes that would be needed to encode a
1061 /// group field, including the tag.
1062 /// </summary>
1063 public static int ComputeGroupSizeNoTag(IMessageLite value)
1064 {
1065 return value.SerializedSize;
1066 }
1067
1068 /// <summary>
1069 /// Compute the number of bytes that would be needed to encode a
1070 /// group field represented by an UnknownFieldSet, including the tag.
1071 /// </summary>
1072 [Obsolete]
1073 public static int ComputeUnknownGroupSizeNoTag(IMessageLite value)
1074 {
1075 return value.SerializedSize;
1076 }
1077
1078 /// <summary>
1079 /// Compute the number of bytes that would be needed to encode an
1080 /// embedded message field, including the tag.
1081 /// </summary>
1082 public static int ComputeMessageSizeNoTag(IMessageLite value)
1083 {
1084 int size = value.SerializedSize;
1085 return ComputeRawVarint32Size((uint) size) + size;
1086 }
1087
1088 /// <summary>
1089 /// Compute the number of bytes that would be needed to encode a
1090 /// bytes field, including the tag.
1091 /// </summary>
1092 public static int ComputeBytesSizeNoTag(ByteString value)
1093 {
1094 return ComputeRawVarint32Size((uint) value.Length) +
1095 value.Length;
1096 }
1097
1098 /// <summary>
1099 /// Compute the number of bytes that would be needed to encode a
1100 /// uint32 field, including the tag.
1101 /// </summary>
1102 [CLSCompliant(false)]
1103 public static int ComputeUInt32SizeNoTag(uint value)
1104 {
1105 return ComputeRawVarint32Size(value);
1106 }
1107
1108 /// <summary>
1109 /// Compute the number of bytes that would be needed to encode a
1110 /// enum field, including the tag. The caller is responsible for
1111 /// converting the enum value to its numeric value.
1112 /// </summary>
1113 public static int ComputeEnumSizeNoTag(int value)
1114 {
1115 return ComputeRawVarint32Size((uint) value);
1116 }
1117
1118 /// <summary>
1119 /// Compute the number of bytes that would be needed to encode an
1120 /// sfixed32 field, including the tag.
1121 /// </summary>
1122 public static int ComputeSFixed32SizeNoTag(int value)
1123 {
1124 return LittleEndian32Size;
1125 }
1126
1127 /// <summary>
1128 /// Compute the number of bytes that would be needed to encode an
1129 /// sfixed64 field, including the tag.
1130 /// </summary>
1131 public static int ComputeSFixed64SizeNoTag(long value)
1132 {
1133 return LittleEndian64Size;
1134 }
1135
1136 /// <summary>
1137 /// Compute the number of bytes that would be needed to encode an
1138 /// sint32 field, including the tag.
1139 /// </summary>
1140 public static int ComputeSInt32SizeNoTag(int value)
1141 {
1142 return ComputeRawVarint32Size(EncodeZigZag32(value));
1143 }
1144
1145 /// <summary>
1146 /// Compute the number of bytes that would be needed to encode an
1147 /// sint64 field, including the tag.
1148 /// </summary>
1149 public static int ComputeSInt64SizeNoTag(long value)
1150 {
1151 return ComputeRawVarint64Size(EncodeZigZag64(value));
1152 }
1153
1154 /*
1155 * Compute the number of bytes that would be needed to encode a
1156 * MessageSet extension to the stream. For historical reasons,
1157 * the wire format differs from normal fields.
1158 */
1159
1160 /// <summary>
1161 /// Compute the number of bytes that would be needed to encode a
1162 /// MessageSet extension to the stream. For historical reasons,
1163 /// the wire format differs from normal fields.
1164 /// </summary>
1165 public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessageLite value)
1166 {
1167 return ComputeTagSize(WireFormat.MessageSetField.Item)*2 +
1168 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
1169 ComputeMessageSize(WireFormat.MessageSetField.Message, value);
1170 }
1171
1172 /// <summary>
1173 /// Compute the number of bytes that would be needed to encode an
1174 /// unparsed MessageSet extension field to the stream. For
1175 /// historical reasons, the wire format differs from normal fields.
1176 /// </summary>
1177 public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value)
1178 {
1179 return ComputeTagSize(WireFormat.MessageSetField.Item)*2 +
1180 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
1181 ComputeBytesSize(WireFormat.MessageSetField.Message, value);
1182 }
1183
1184 /// <summary>
1185 /// Compute the number of bytes that would be needed to encode a varint.
1186 /// </summary>
1187 [CLSCompliant(false)]
1188 public static int ComputeRawVarint32Size(uint value)
1189 {
1190 if ((value & (0xffffffff << 7)) == 0) return 1;
1191 if ((value & (0xffffffff << 14)) == 0) return 2;
1192 if ((value & (0xffffffff << 21)) == 0) return 3;
1193 if ((value & (0xffffffff << 28)) == 0) return 4;
1194 return 5;
1195 }
1196
1197 /// <summary>
1198 /// Compute the number of bytes that would be needed to encode a varint.
1199 /// </summary>
1200 [CLSCompliant(false)]
1201 public static int ComputeRawVarint64Size(ulong value)
1202 {
1203 if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
1204 if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
1205 if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
1206 if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
1207 if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
1208 if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
1209 if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
1210 if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
1211 if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
1212 return 10;
1213 }
1214
1215 /// <summary>
1216 /// Compute the number of bytes that would be needed to encode a
1217 /// field of arbitrary type, including the tag, to the stream.
1218 /// </summary>
1219 public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value)
1220 {
1221 switch (fieldType)
1222 {
1223 case FieldType.Double:
1224 return ComputeDoubleSize(fieldNumber, (double) value);
1225 case FieldType.Float:
1226 return ComputeFloatSize(fieldNumber, (float) value);
1227 case FieldType.Int64:
1228 return ComputeInt64Size(fieldNumber, (long) value);
1229 case FieldType.UInt64:
1230 return ComputeUInt64Size(fieldNumber, (ulong) value);
1231 case FieldType.Int32:
1232 return ComputeInt32Size(fieldNumber, (int) value);
1233 case FieldType.Fixed64:
1234 return ComputeFixed64Size(fieldNumber, (ulong) value);
1235 case FieldType.Fixed32:
1236 return ComputeFixed32Size(fieldNumber, (uint) value);
1237 case FieldType.Bool:
1238 return ComputeBoolSize(fieldNumber, (bool) value);
1239 case FieldType.String:
1240 return ComputeStringSize(fieldNumber, (string) value);
1241 case FieldType.Group:
1242 return ComputeGroupSize(fieldNumber, (IMessageLite) value);
1243 case FieldType.Message:
1244 return ComputeMessageSize(fieldNumber, (IMessageLite) value);
1245 case FieldType.Bytes:
1246 return ComputeBytesSize(fieldNumber, (ByteString) value);
1247 case FieldType.UInt32:
1248 return ComputeUInt32Size(fieldNumber, (uint) value);
1249 case FieldType.SFixed32:
1250 return ComputeSFixed32Size(fieldNumber, (int) value);
1251 case FieldType.SFixed64:
1252 return ComputeSFixed64Size(fieldNumber, (long) value);
1253 case FieldType.SInt32:
1254 return ComputeSInt32Size(fieldNumber, (int) value);
1255 case FieldType.SInt64:
1256 return ComputeSInt64Size(fieldNumber, (long) value);
1257 case FieldType.Enum:
1258 return ComputeEnumSize(fieldNumber, ((IEnumLite) value).Number);
1259 default:
1260 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1261 }
1262 }
1263
1264 /// <summary>
1265 /// Compute the number of bytes that would be needed to encode a
1266 /// field of arbitrary type, excluding the tag, to the stream.
1267 /// </summary>
1268 public static int ComputeFieldSizeNoTag(FieldType fieldType, Object value)
1269 {
1270 switch (fieldType)
1271 {
1272 case FieldType.Double:
1273 return ComputeDoubleSizeNoTag((double) value);
1274 case FieldType.Float:
1275 return ComputeFloatSizeNoTag((float) value);
1276 case FieldType.Int64:
1277 return ComputeInt64SizeNoTag((long) value);
1278 case FieldType.UInt64:
1279 return ComputeUInt64SizeNoTag((ulong) value);
1280 case FieldType.Int32:
1281 return ComputeInt32SizeNoTag((int) value);
1282 case FieldType.Fixed64:
1283 return ComputeFixed64SizeNoTag((ulong) value);
1284 case FieldType.Fixed32:
1285 return ComputeFixed32SizeNoTag((uint) value);
1286 case FieldType.Bool:
1287 return ComputeBoolSizeNoTag((bool) value);
1288 case FieldType.String:
1289 return ComputeStringSizeNoTag((string) value);
1290 case FieldType.Group:
1291 return ComputeGroupSizeNoTag((IMessageLite) value);
1292 case FieldType.Message:
1293 return ComputeMessageSizeNoTag((IMessageLite) value);
1294 case FieldType.Bytes:
1295 return ComputeBytesSizeNoTag((ByteString) value);
1296 case FieldType.UInt32:
1297 return ComputeUInt32SizeNoTag((uint) value);
1298 case FieldType.SFixed32:
1299 return ComputeSFixed32SizeNoTag((int) value);
1300 case FieldType.SFixed64:
1301 return ComputeSFixed64SizeNoTag((long) value);
1302 case FieldType.SInt32:
1303 return ComputeSInt32SizeNoTag((int) value);
1304 case FieldType.SInt64:
1305 return ComputeSInt64SizeNoTag((long) value);
1306 case FieldType.Enum:
1307 return ComputeEnumSizeNoTag(((IEnumLite) value).Number);
1308 default:
1309 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
1310 }
1311 }
1312
1313 /// <summary>
1314 /// Compute the number of bytes that would be needed to encode a tag.
1315 /// </summary>
1316 public static int ComputeTagSize(int fieldNumber)
1317 {
1318 return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
1319 }
1320
1321 #endregion
1322
1323 /// <summary>
1324 /// Encode a 32-bit value with ZigZag encoding.
1325 /// </summary>
1326 /// <remarks>
1327 /// ZigZag encodes signed integers into values that can be efficiently
1328 /// encoded with varint. (Otherwise, negative values must be
1329 /// sign-extended to 64 bits to be varint encoded, thus always taking
1330 /// 10 bytes on the wire.)
1331 /// </remarks>
1332 [CLSCompliant(false)]
1333 public static uint EncodeZigZag32(int n)
1334 {
1335 // Note: the right-shift must be arithmetic
1336 return (uint) ((n << 1) ^ (n >> 31));
1337 }
1338
1339 /// <summary>
1340 /// Encode a 64-bit value with ZigZag encoding.
1341 /// </summary>
1342 /// <remarks>
1343 /// ZigZag encodes signed integers into values that can be efficiently
1344 /// encoded with varint. (Otherwise, negative values must be
1345 /// sign-extended to 64 bits to be varint encoded, thus always taking
1346 /// 10 bytes on the wire.)
1347 /// </remarks>
1348 [CLSCompliant(false)]
1349 public static ulong EncodeZigZag64(long n)
1350 {
1351 return (ulong) ((n << 1) ^ (n >> 63));
1352 }
1353
1354 private void RefreshBuffer()
1355 {
1356 if (output == null)
1357 {
1358 // We're writing to a single buffer.
1359 throw new OutOfSpaceException();
1360 }
1361
1362 // Since we have an output stream, this is our buffer
1363 // and buffer offset == 0
1364 output.Write(buffer, 0, position);
1365 position = 0;
1366 }
1367
1368 /// <summary>
1369 /// Indicates that a CodedOutputStream wrapping a flat byte array
1370 /// ran out of space.
1371 /// </summary>
1372 public sealed class OutOfSpaceException : IOException
1373 {
1374 internal OutOfSpaceException()
1375 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
1376 {
1377 }
1378 }
1379
1380 public void Flush()
1381 {
1382 if (output != null)
1383 {
1384 RefreshBuffer();
1385 }
1386 }
1387
1388 /// <summary>
1389 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
1390 /// that is exactly big enough to hold a message, then write to it with
1391 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
1392 /// the message was actually as big as expected, which can help bugs.
1393 /// </summary>
1394 public void CheckNoSpaceLeft()
1395 {
1396 if (SpaceLeft != 0)
1397 {
1398 throw new InvalidOperationException("Did not write as much data as expected.");
1399 }
1400 }
1401
1402 /// <summary>
1403 /// If writing to a flat array, returns the space left in the array. Otherwise,
1404 /// throws an InvalidOperationException.
1405 /// </summary>
1406 public int SpaceLeft
1407 {
1408 get
1409 {
1410 if (output == null)
1411 {
1412 return limit - position;
1413 }
1414 else
1415 {
1416 throw new InvalidOperationException(
1417 "SpaceLeft can only be called on CodedOutputStreams that are " +
1418 "writing to a flat array.");
1419 }
1420 }
1421 }
1422 }
1423}