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