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