blob: df80780a18b8198130792e521e0a67c9bdb2521b [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
301 #endregion
302
303 #region Underlying writing primitives
304 /// <summary>
305 /// Encodes and writes a tag.
306 /// </summary>
307 public void WriteTag(int fieldNumber, WireFormat.WireType type) {
308 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
309 }
310
311 private void SlowWriteRawVarint32(uint value) {
312 while (true) {
313 if ((value & ~0x7F) == 0) {
314 WriteRawByte(value);
315 return;
316 } else {
317 WriteRawByte((value & 0x7F) | 0x80);
318 value >>= 7;
319 }
320 }
321 }
322
323 /// <summary>
324 /// Writes a 32 bit value as a varint. The fast route is taken when
325 /// there's enough buffer space left to whizz through without checking
326 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
327 /// </summary>
328 public void WriteRawVarint32(uint value) {
329 if (position + 5 > limit) {
330 SlowWriteRawVarint32(value);
331 return;
332 }
333
334 while (true) {
335 if ((value & ~0x7F) == 0) {
336 buffer[position++] = (byte) value;
337 return;
338 } else {
339 buffer[position++] = (byte)((value & 0x7F) | 0x80);
340 value >>= 7;
341 }
342 }
343 }
344
345 public void WriteRawVarint64(ulong value) {
346 while (true) {
347 if ((value & ~0x7FUL) == 0) {
348 WriteRawByte((uint)value);
349 return;
350 } else {
351 WriteRawByte(((uint)value & 0x7F) | 0x80);
352 value >>= 7;
353 }
354 }
355 }
356
357 public void WriteRawLittleEndian32(uint value) {
358 WriteRawByte((byte)value);
359 WriteRawByte((byte)(value >> 8));
360 WriteRawByte((byte)(value >> 16));
361 WriteRawByte((byte)(value >> 24));
362 }
363
364 public void WriteRawLittleEndian64(ulong value) {
365 WriteRawByte((byte)value);
366 WriteRawByte((byte)(value >> 8));
367 WriteRawByte((byte)(value >> 16));
368 WriteRawByte((byte)(value >> 24));
369 WriteRawByte((byte)(value >> 32));
370 WriteRawByte((byte)(value >> 40));
371 WriteRawByte((byte)(value >> 48));
372 WriteRawByte((byte)(value >> 56));
373 }
374
375 public void WriteRawByte(byte value) {
376 if (position == limit) {
377 RefreshBuffer();
378 }
379
380 buffer[position++] = value;
381 }
382
383 public void WriteRawByte(uint value) {
384 WriteRawByte((byte)value);
385 }
386
387 /// <summary>
388 /// Writes out an array of bytes.
389 /// </summary>
390 public void WriteRawBytes(byte[] value) {
391 WriteRawBytes(value, 0, value.Length);
392 }
393
394 /// <summary>
395 /// Writes out part of an array of bytes.
396 /// </summary>
397 public void WriteRawBytes(byte[] value, int offset, int length) {
398 if (limit - position >= length) {
399 Array.Copy(value, offset, buffer, position, length);
400 // We have room in the current buffer.
401 position += length;
402 } else {
403 // Write extends past current buffer. Fill the rest of this buffer and
404 // flush.
405 int bytesWritten = limit - position;
406 Array.Copy(value, offset, buffer, position, bytesWritten);
407 offset += bytesWritten;
408 length -= bytesWritten;
409 position = limit;
410 RefreshBuffer();
411
412 // Now deal with the rest.
413 // Since we have an output stream, this is our buffer
414 // and buffer offset == 0
415 if (length <= limit) {
416 // Fits in new buffer.
417 Array.Copy(value, offset, buffer, 0, length);
418 position = length;
419 } else {
420 // Write is very big. Let's do it all at once.
421 output.Write(value, offset, length);
422 }
423 }
424 }
425 #endregion
426
427 #region Size computations
428
429 const int LittleEndian64Size = 8;
430 const int LittleEndian32Size = 4;
431
432 /// <summary>
433 /// Compute the number of bytes that would be needed to encode a
434 /// double field, including the tag.
435 /// </summary>
436 public static int ComputeDoubleSize(int fieldNumber, double value) {
437 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
438 }
439
440 /// <summary>
441 /// Compute the number of bytes that would be needed to encode a
442 /// float field, including the tag.
443 /// </summary>
444 public static int ComputeFloatSize(int fieldNumber, float value) {
445 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
446 }
447
448 /// <summary>
449 /// Compute the number of bytes that would be needed to encode a
450 /// uint64 field, including the tag.
451 /// </summary>
452 public static int ComputeUInt64Size(int fieldNumber, ulong value) {
453 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
454 }
455
456 /// <summary>
457 /// Compute the number of bytes that would be needed to encode an
458 /// int64 field, including the tag.
459 /// </summary>
460 public static int ComputeInt64Size(int fieldNumber, long value) {
461 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong)value);
462 }
463
464 /// <summary>
465 /// Compute the number of bytes that would be needed to encode an
466 /// int32 field, including the tag.
467 /// </summary>
468 public static int ComputeInt32Size(int fieldNumber, int value) {
469 if (value >= 0) {
470 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
471 } else {
472 // Must sign-extend.
473 return ComputeTagSize(fieldNumber) + 10;
474 }
475 }
476
477 /// <summary>
478 /// Compute the number of bytes that would be needed to encode a
479 /// fixed64 field, including the tag.
480 /// </summary>
481 public static int ComputeFixed64Size(int fieldNumber, ulong value) {
482 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
483 }
484
485 /// <summary>
486 /// Compute the number of bytes that would be needed to encode a
487 /// fixed32 field, including the tag.
488 /// </summary>
489 public static int ComputeFixed32Size(int fieldNumber, uint value) {
490 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
491 }
492
493 /// <summary>
494 /// Compute the number of bytes that would be needed to encode a
495 /// bool field, including the tag.
496 /// </summary>
497 public static int ComputeBoolSize(int fieldNumber, bool value) {
498 return ComputeTagSize(fieldNumber) + 1;
499 }
500
501 /// <summary>
502 /// Compute the number of bytes that would be needed to encode a
503 /// string field, including the tag.
504 /// </summary>
505 public static int ComputeStringSize(int fieldNumber, String value) {
506 int byteArraySize = Encoding.UTF8.GetByteCount(value);
507 return ComputeTagSize(fieldNumber) +
508 ComputeRawVarint32Size((uint)byteArraySize) +
509 byteArraySize;
510 }
511
512 /// <summary>
513 /// Compute the number of bytes that would be needed to encode a
514 /// group field, including the tag.
515 /// </summary>
516 public static int ComputeGroupSize(int fieldNumber, IMessage value) {
517 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
518 }
519
520 /// <summary>
521 /// Compute the number of bytes that would be needed to encode a
522 /// group field represented by an UnknownFieldSet, including the tag.
523 /// </summary>
524 public static int ComputeUnknownGroupSize(int fieldNumber,
525 UnknownFieldSet value) {
526 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
527 }
528
529 /// <summary>
530 /// Compute the number of bytes that would be needed to encode an
531 /// embedded message field, including the tag.
532 /// </summary>
533 public static int ComputeMessageSize(int fieldNumber, IMessage value) {
534 int size = value.SerializedSize;
535 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size;
536 }
537
538 /// <summary>
539 /// Compute the number of bytes that would be needed to encode a
540 /// bytes field, including the tag.
541 /// </summary>
542 public static int ComputeBytesSize(int fieldNumber, ByteString value) {
543 return ComputeTagSize(fieldNumber) +
544 ComputeRawVarint32Size((uint)value.Length) +
545 value.Length;
546 }
547
548 /// <summary>
549 /// Compute the number of bytes that would be needed to encode a
550 /// uint32 field, including the tag.
551 /// </summary>
552 public static int ComputeUInt32Size(int fieldNumber, uint value) {
553 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
554 }
555
556 /// <summary>
557 /// Compute the number of bytes that would be needed to encode a
558 /// enum field, including the tag. The caller is responsible for
559 /// converting the enum value to its numeric value.
560 /// </summary>
561 public static int ComputeEnumSize(int fieldNumber, int value) {
562 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
563 }
564
565 /// <summary>
566 /// Compute the number of bytes that would be needed to encode an
567 /// sfixed32 field, including the tag.
568 /// </summary>
569 public static int ComputeSFixed32Size(int fieldNumber, int value) {
570 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
571 }
572
573 /// <summary>
574 /// Compute the number of bytes that would be needed to encode an
575 /// sfixed64 field, including the tag.
576 /// </summary>
577 public static int ComputeSFixed64Size(int fieldNumber, long value) {
578 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
579 }
580
581 /// <summary>
582 /// Compute the number of bytes that would be needed to encode an
583 /// sint32 field, including the tag.
584 /// </summary>
585 public static int ComputeSInt32Size(int fieldNumber, int value) {
586 return ComputeTagSize(fieldNumber) +
587 ComputeRawVarint32Size(EncodeZigZag32(value));
588 }
589
590 /// <summary>
591 /// Compute the number of bytes that would be needed to encode an
592 /// sint64 field, including the tag.
593 /// </summary>
594 public static int ComputeSInt64Size(int fieldNumber, long value) {
595 return ComputeTagSize(fieldNumber) +
596 ComputeRawVarint64Size(EncodeZigZag64(value));
597 }
598
599 /*
600 * Compute the number of bytes that would be needed to encode a
601 * MessageSet extension to the stream. For historical reasons,
602 * the wire format differs from normal fields.
603 */
604 /// <summary>
605 /// Compute the number of bytes that would be needed to encode a
606 /// MessageSet extension to the stream. For historical reasons,
607 /// the wire format differs from normal fields.
608 /// </summary>
609 public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) {
610 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
611 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
612 ComputeMessageSize(WireFormat.MessageSetField.Message, value);
613 }
614
615 /// <summary>
616 /// Compute the number of bytes that would be needed to encode an
617 /// unparsed MessageSet extension field to the stream. For
618 /// historical reasons, the wire format differs from normal fields.
619 /// </summary>
620 public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value) {
621 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
622 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
623 ComputeBytesSize(WireFormat.MessageSetField.Message, value);
624 }
625
626 /// <summary>
627 /// Compute the number of bytes that would be needed to encode a varint.
628 /// </summary>
629 public static int ComputeRawVarint32Size(uint value) {
630 if ((value & (0xffffffff << 7)) == 0) return 1;
631 if ((value & (0xffffffff << 14)) == 0) return 2;
632 if ((value & (0xffffffff << 21)) == 0) return 3;
633 if ((value & (0xffffffff << 28)) == 0) return 4;
634 return 5;
635 }
636
637 /// <summary>
638 /// Compute the number of bytes that would be needed to encode a varint.
639 /// </summary>
640 public static int ComputeRawVarint64Size(ulong value) {
641 if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
642 if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
643 if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
644 if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
645 if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
646 if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
647 if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
648 if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
649 if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
650 return 10;
651 }
652
653
654 /*
655 * Compute the number of bytes that would be needed to encode a
656 * field of arbitrary type, including tag, to the stream.
657 *
658 * @param type The field's type.
659 * @param number The field's number.
660 * @param value Object representing the field's value. Must be of the exact
661 * type which would be returned by
662 * {@link Message#getField(FieldDescriptor)} for
663 * this field.
664 */
665 public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) {
666 switch (fieldType) {
667 case FieldType.Double: return ComputeDoubleSize(fieldNumber, (double)value);
668 case FieldType.Float: return ComputeFloatSize(fieldNumber, (float)value);
669 case FieldType.Int64: return ComputeInt64Size(fieldNumber, (long)value);
670 case FieldType.UInt64: return ComputeUInt64Size(fieldNumber, (ulong)value);
671 case FieldType.Int32: return ComputeInt32Size(fieldNumber, (int)value);
672 case FieldType.Fixed64: return ComputeFixed64Size(fieldNumber, (ulong)value);
673 case FieldType.Fixed32: return ComputeFixed32Size(fieldNumber, (uint)value);
674 case FieldType.Bool: return ComputeBoolSize(fieldNumber, (bool)value);
675 case FieldType.String: return ComputeStringSize(fieldNumber, (string)value);
676 case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessage)value);
677 case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessage)value);
678 case FieldType.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value);
679 case FieldType.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value);
680 case FieldType.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value);
681 case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
682 case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
683 case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
684 case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number);
685 default:
686 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
687 }
688 }
689
690 /// <summary>
691 /// Compute the number of bytes that would be needed to encode a tag.
692 /// </summary>
693 public static int ComputeTagSize(int fieldNumber) {
694 return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
695 }
696 #endregion
697
698 /// <summary>
699 /// Encode a 32-bit value with ZigZag encoding.
700 /// </summary>
701 /// <remarks>
702 /// ZigZag encodes signed integers into values that can be efficiently
703 /// encoded with varint. (Otherwise, negative values must be
704 /// sign-extended to 64 bits to be varint encoded, thus always taking
705 /// 10 bytes on the wire.)
706 /// </remarks>
707 public static uint EncodeZigZag32(int n) {
708 // Note: the right-shift must be arithmetic
709 return (uint)((n << 1) ^ (n >> 31));
710 }
711
712 /// <summary>
713 /// Encode a 64-bit value with ZigZag encoding.
714 /// </summary>
715 /// <remarks>
716 /// ZigZag encodes signed integers into values that can be efficiently
717 /// encoded with varint. (Otherwise, negative values must be
718 /// sign-extended to 64 bits to be varint encoded, thus always taking
719 /// 10 bytes on the wire.)
720 /// </remarks>
721 public static ulong EncodeZigZag64(long n) {
722 return (ulong)((n << 1) ^ (n >> 63));
723 }
724
725 private void RefreshBuffer() {
726 if (output == null) {
727 // We're writing to a single buffer.
728 throw new OutOfSpaceException();
729 }
730
731 // Since we have an output stream, this is our buffer
732 // and buffer offset == 0
733 output.Write(buffer, 0, position);
734 position = 0;
735 }
736
737 /// <summary>
738 /// Indicates that a CodedOutputStream wrapping a flat byte array
739 /// ran out of space.
740 /// </summary>
741 public sealed class OutOfSpaceException : IOException {
742 internal OutOfSpaceException()
743 : base("CodedOutputStream was writing to a flat byte array and ran out of space.") {
744 }
745 }
746
747 public void Flush() {
748 if (output != null) {
749 RefreshBuffer();
750 }
751 }
752
753 /// <summary>
754 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
755 /// that is exactly big enough to hold a message, then write to it with
756 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
757 /// the message was actually as big as expected, which can help bugs.
758 /// </summary>
759 public void CheckNoSpaceLeft() {
760 if (SpaceLeft != 0) {
761 throw new InvalidOperationException("Did not write as much data as expected.");
762 }
763 }
764
765 /// <summary>
766 /// If writing to a flat array, returns the space left in the array. Otherwise,
767 /// throws an InvalidOperationException.
768 /// </summary>
769 public int SpaceLeft {
770 get {
771 if (output == null) {
772 return limit - position;
773 } else {
774 throw new InvalidOperationException(
775 "SpaceLeft can only be called on CodedOutputStreams that are " +
776 "writing to a flat array.");
777 }
778 }
779 }
780 }
781}