blob: 0abfa39a296747db1e8773d20c976d5f556d604d [file] [log] [blame]
Jon Skeet68036862008-10-22 13:30:34 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.
3// http://code.google.com/p/protobuf/
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16using System;
17using System.IO;
18using System.Text;
19using Google.ProtocolBuffers.Descriptors;
20
21namespace Google.ProtocolBuffers {
22
23 /// <summary>
24 /// Encodes and writes protocol message fields.
25 /// </summary>
26 /// <remarks>
27 /// This class contains two kinds of methods: methods that write specific
28 /// protocol message constructs and field types (e.g. WriteTag and
29 /// WriteInt32) and methods that write low-level values (e.g.
30 /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
31 /// messages, you should use the former methods, but if you are writing some
32 /// other format of your own design, use the latter. The names of the former
33 /// methods are taken from the protocol buffer type names, not .NET types.
34 /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
35 /// </remarks>
36 public sealed class CodedOutputStream {
37 /// <summary>
38 /// The buffer size used by CreateInstance(Stream).
39 /// </summary>
40 public static readonly int DefaultBufferSize = 4096;
41
42 private readonly byte[] buffer;
43 private readonly int limit;
44 private int position;
45 private readonly Stream output;
46
47 #region Construction
48 private CodedOutputStream(byte[] buffer, int offset, int length) {
49 this.output = null;
50 this.buffer = buffer;
51 this.position = offset;
52 this.limit = offset + length;
53 }
54
55 private CodedOutputStream(Stream output, byte[] buffer) {
56 this.output = output;
57 this.buffer = buffer;
58 this.position = 0;
59 this.limit = buffer.Length;
60 }
61
62 /// <summary>
63 /// Creates a new CodedOutputStream which write to the given stream.
64 /// </summary>
65 public static CodedOutputStream CreateInstance(Stream output) {
66 return CreateInstance(output, DefaultBufferSize);
67 }
68
69 /// <summary>
70 /// Creates a new CodedOutputStream which write to the given stream and uses
71 /// the specified buffer size.
72 /// </summary>
73 public static CodedOutputStream CreateInstance(Stream output, int bufferSize) {
74 return new CodedOutputStream(output, new byte[bufferSize]);
75 }
76
77 /// <summary>
78 /// Creates a new CodedOutputStream that writes directly to the given
79 /// byte array. If more bytes are written than fit in the array,
80 /// OutOfSpaceException will be thrown.
81 /// </summary>
82 public static CodedOutputStream CreateInstance(byte[] flatArray) {
83 return CreateInstance(flatArray, 0, flatArray.Length);
84 }
85
86 /// <summary>
87 /// Creates a new CodedOutputStream that writes directly to the given
88 /// byte array slice. If more bytes are written than fit in the array,
89 /// OutOfSpaceException will be thrown.
90 /// </summary>
91 public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length) {
92 return new CodedOutputStream(flatArray, offset, length);
93 }
94 #endregion
95
96 #region Writing of tags etc
97 /// <summary>
98 /// Writes a double field value, including tag, to the stream.
99 /// </summary>
100 public void WriteDouble(int fieldNumber, double value) {
101 // TODO(jonskeet): Test this on different endiannesses
102 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
103 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
104 }
105
106 /// <summary>
107 /// Writes a float field value, including tag, to the stream.
108 /// </summary>
109 public void WriteFloat(int fieldNumber, float value) {
110 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
111 // TODO(jonskeet): Test this on different endiannesses
112 byte[] rawBytes = BitConverter.GetBytes(value);
113 uint asInteger = BitConverter.ToUInt32(rawBytes, 0);
114 WriteRawLittleEndian32(asInteger);
115 }
116
117 /// <summary>
118 /// Writes a uint64 field value, including tag, to the stream.
119 /// </summary>
120 public void WriteUInt64(int fieldNumber, ulong value) {
121 WriteTag(fieldNumber, WireFormat.WireType.Varint);
122 WriteRawVarint64(value);
123 }
124
125 /// <summary>
126 /// Writes an int64 field value, including tag, to the stream.
127 /// </summary>
128 public void WriteInt64(int fieldNumber, long value) {
129 WriteTag(fieldNumber, WireFormat.WireType.Varint);
130 WriteRawVarint64((ulong)value);
131 }
132
133 /// <summary>
134 /// Writes an int32 field value, including tag, to the stream.
135 /// </summary>
136 public void WriteInt32(int fieldNumber, int value) {
137 WriteTag(fieldNumber, WireFormat.WireType.Varint);
138 if (value >= 0) {
139 WriteRawVarint32((uint)value);
140 } else {
141 // Must sign-extend.
142 WriteRawVarint64((ulong)value);
143 }
144 }
145
146 /// <summary>
147 /// Writes a fixed64 field value, including tag, to the stream.
148 /// </summary>
149 public void WriteFixed64(int fieldNumber, ulong value) {
150 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
151 WriteRawLittleEndian64(value);
152 }
153
154 /// <summary>
155 /// Writes a fixed32 field value, including tag, to the stream.
156 /// </summary>
157 public void WriteFixed32(int fieldNumber, uint value) {
158 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
159 WriteRawLittleEndian32(value);
160 }
161
162 /// <summary>
163 /// Writes a bool field value, including tag, to the stream.
164 /// </summary>
165 public void WriteBool(int fieldNumber, bool value) {
166 WriteTag(fieldNumber, WireFormat.WireType.Varint);
167 WriteRawByte(value ? (byte)1 : (byte)0);
168 }
169
170 /// <summary>
171 /// Writes a string field value, including tag, to the stream.
172 /// </summary>
173 public void WriteString(int fieldNumber, string value) {
174 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
175 // Optimise the case where we have enough space to write
176 // the string directly to the buffer, which should be common.
177 int length = Encoding.UTF8.GetByteCount(value);
178 WriteRawVarint32((uint) length);
179 if (limit - position >= length) {
180 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
181 position += length;
182 } else {
183 byte[] bytes = Encoding.UTF8.GetBytes(value);
184 WriteRawBytes(bytes);
185 }
186 }
187
188 /// <summary>
189 /// Writes a group field value, including tag, to the stream.
190 /// </summary>
191 public void WriteGroup(int fieldNumber, IMessage value) {
192 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
193 value.WriteTo(this);
194 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
195 }
196
197 public void WriteUnknownGroup(int fieldNumber, UnknownFieldSet value) {
198 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
199 value.WriteTo(this);
200 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
201 }
202
203 public void WriteMessage(int fieldNumber, IMessage value) {
204 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
205 WriteRawVarint32((uint)value.SerializedSize);
206 value.WriteTo(this);
207 }
208
209 public void WriteBytes(int fieldNumber, ByteString value) {
210 // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
211 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
212 byte[] bytes = value.ToByteArray();
213 WriteRawVarint32((uint)bytes.Length);
214 WriteRawBytes(bytes);
215 }
216
217 public void WriteUInt32(int fieldNumber, uint value) {
218 WriteTag(fieldNumber, WireFormat.WireType.Varint);
219 WriteRawVarint32(value);
220 }
221
222 public void WriteEnum(int fieldNumber, int value) {
223 WriteTag(fieldNumber, WireFormat.WireType.Varint);
224 WriteRawVarint32((uint)value);
225 }
226
227 public void WriteSFixed32(int fieldNumber, int value) {
228 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
229 WriteRawLittleEndian32((uint)value);
230 }
231
232 public void WriteSFixed64(int fieldNumber, long value) {
233 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
234 WriteRawLittleEndian64((ulong)value);
235 }
236
237 public void WriteSInt32(int fieldNumber, int value) {
238 WriteTag(fieldNumber, WireFormat.WireType.Varint);
239 WriteRawVarint32(EncodeZigZag32(value));
240 }
241
242 public void WriteSInt64(int fieldNumber, long value) {
243 WriteTag(fieldNumber, WireFormat.WireType.Varint);
244 WriteRawVarint64(EncodeZigZag64(value));
245 }
246
247 public void WriteMessageSetExtension(int fieldNumber, IMessage value) {
248 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
249 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
250 WriteMessage(WireFormat.MessageSetField.Message, value);
251 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
252 }
253
254 public void WriteRawMessageSetExtension(int fieldNumber, ByteString value) {
255 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
256 WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
257 WriteBytes(WireFormat.MessageSetField.Message, value);
258 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
259 }
260
261 public void WriteField(FieldType fieldType, int fieldNumber, object value) {
262 switch (fieldType) {
263 case FieldType.Double: WriteDouble(fieldNumber, (double)value); break;
264 case FieldType.Float: WriteFloat(fieldNumber, (float)value); break;
265 case FieldType.Int64: WriteInt64(fieldNumber, (long)value); break;
266 case FieldType.UInt64: WriteUInt64(fieldNumber, (ulong)value); break;
267 case FieldType.Int32: WriteInt32(fieldNumber, (int)value); break;
268 case FieldType.Fixed64: WriteFixed64(fieldNumber, (ulong)value); break;
269 case FieldType.Fixed32: WriteFixed32(fieldNumber, (uint)value); break;
270 case FieldType.Bool: WriteBool(fieldNumber, (bool)value); break;
271 case FieldType.String: WriteString(fieldNumber, (string)value); break;
272 case FieldType.Group: WriteGroup(fieldNumber, (IMessage)value); break;
273 case FieldType.Message: WriteMessage(fieldNumber, (IMessage)value); break;
274 case FieldType.Bytes: WriteBytes(fieldNumber, (ByteString)value); break;
275 case FieldType.UInt32: WriteUInt32(fieldNumber, (uint)value); break;
276 case FieldType.SFixed32: WriteSFixed32(fieldNumber, (int)value); break;
277 case FieldType.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
278 case FieldType.SInt32: WriteSInt32(fieldNumber, (int)value); break;
279 case FieldType.SInt64: WriteSInt64(fieldNumber, (long)value); break;
280 case FieldType.Enum: WriteEnum(fieldNumber, ((EnumValueDescriptor)value).Number);
281 break;
282 }
283 }
284
285 #endregion
286
287 #region Underlying writing primitives
288 /// <summary>
289 /// Encodes and writes a tag.
290 /// </summary>
291 public void WriteTag(int fieldNumber, WireFormat.WireType type) {
292 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
293 }
294
295 private void SlowWriteRawVarint32(uint value) {
296 while (true) {
297 if ((value & ~0x7F) == 0) {
298 WriteRawByte(value);
299 return;
300 } else {
301 WriteRawByte((value & 0x7F) | 0x80);
302 value >>= 7;
303 }
304 }
305 }
306
307 /// <summary>
308 /// Writes a 32 bit value as a varint. The fast route is taken when
309 /// there's enough buffer space left to whizz through without checking
310 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
311 /// </summary>
312 public void WriteRawVarint32(uint value) {
313 if (position + 5 > limit) {
314 SlowWriteRawVarint32(value);
315 return;
316 }
317
318 while (true) {
319 if ((value & ~0x7F) == 0) {
320 buffer[position++] = (byte) value;
321 return;
322 } else {
323 buffer[position++] = (byte)((value & 0x7F) | 0x80);
324 value >>= 7;
325 }
326 }
327 }
328
329 public void WriteRawVarint64(ulong value) {
330 while (true) {
331 if ((value & ~0x7FUL) == 0) {
332 WriteRawByte((uint)value);
333 return;
334 } else {
335 WriteRawByte(((uint)value & 0x7F) | 0x80);
336 value >>= 7;
337 }
338 }
339 }
340
341 public void WriteRawLittleEndian32(uint value) {
342 WriteRawByte((byte)value);
343 WriteRawByte((byte)(value >> 8));
344 WriteRawByte((byte)(value >> 16));
345 WriteRawByte((byte)(value >> 24));
346 }
347
348 public void WriteRawLittleEndian64(ulong value) {
349 WriteRawByte((byte)value);
350 WriteRawByte((byte)(value >> 8));
351 WriteRawByte((byte)(value >> 16));
352 WriteRawByte((byte)(value >> 24));
353 WriteRawByte((byte)(value >> 32));
354 WriteRawByte((byte)(value >> 40));
355 WriteRawByte((byte)(value >> 48));
356 WriteRawByte((byte)(value >> 56));
357 }
358
359 public void WriteRawByte(byte value) {
360 if (position == limit) {
361 RefreshBuffer();
362 }
363
364 buffer[position++] = value;
365 }
366
367 public void WriteRawByte(uint value) {
368 WriteRawByte((byte)value);
369 }
370
371 /// <summary>
372 /// Writes out an array of bytes.
373 /// </summary>
374 public void WriteRawBytes(byte[] value) {
375 WriteRawBytes(value, 0, value.Length);
376 }
377
378 /// <summary>
379 /// Writes out part of an array of bytes.
380 /// </summary>
381 public void WriteRawBytes(byte[] value, int offset, int length) {
382 if (limit - position >= length) {
383 Array.Copy(value, offset, buffer, position, length);
384 // We have room in the current buffer.
385 position += length;
386 } else {
387 // Write extends past current buffer. Fill the rest of this buffer and
388 // flush.
389 int bytesWritten = limit - position;
390 Array.Copy(value, offset, buffer, position, bytesWritten);
391 offset += bytesWritten;
392 length -= bytesWritten;
393 position = limit;
394 RefreshBuffer();
395
396 // Now deal with the rest.
397 // Since we have an output stream, this is our buffer
398 // and buffer offset == 0
399 if (length <= limit) {
400 // Fits in new buffer.
401 Array.Copy(value, offset, buffer, 0, length);
402 position = length;
403 } else {
404 // Write is very big. Let's do it all at once.
405 output.Write(value, offset, length);
406 }
407 }
408 }
409 #endregion
410
411 #region Size computations
412
413 const int LittleEndian64Size = 8;
414 const int LittleEndian32Size = 4;
415
416 /// <summary>
417 /// Compute the number of bytes that would be needed to encode a
418 /// double field, including the tag.
419 /// </summary>
420 public static int ComputeDoubleSize(int fieldNumber, double value) {
421 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
422 }
423
424 /// <summary>
425 /// Compute the number of bytes that would be needed to encode a
426 /// float field, including the tag.
427 /// </summary>
428 public static int ComputeFloatSize(int fieldNumber, float value) {
429 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
430 }
431
432 /// <summary>
433 /// Compute the number of bytes that would be needed to encode a
434 /// uint64 field, including the tag.
435 /// </summary>
436 public static int ComputeUInt64Size(int fieldNumber, ulong value) {
437 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
438 }
439
440 /// <summary>
441 /// Compute the number of bytes that would be needed to encode an
442 /// int64 field, including the tag.
443 /// </summary>
444 public static int ComputeInt64Size(int fieldNumber, long value) {
445 return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong)value);
446 }
447
448 /// <summary>
449 /// Compute the number of bytes that would be needed to encode an
450 /// int32 field, including the tag.
451 /// </summary>
452 public static int ComputeInt32Size(int fieldNumber, int value) {
453 if (value >= 0) {
454 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
455 } else {
456 // Must sign-extend.
457 return ComputeTagSize(fieldNumber) + 10;
458 }
459 }
460
461 /// <summary>
462 /// Compute the number of bytes that would be needed to encode a
463 /// fixed64 field, including the tag.
464 /// </summary>
465 public static int ComputeFixed64Size(int fieldNumber, ulong value) {
466 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
467 }
468
469 /// <summary>
470 /// Compute the number of bytes that would be needed to encode a
471 /// fixed32 field, including the tag.
472 /// </summary>
473 public static int ComputeFixed32Size(int fieldNumber, uint value) {
474 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
475 }
476
477 /// <summary>
478 /// Compute the number of bytes that would be needed to encode a
479 /// bool field, including the tag.
480 /// </summary>
481 public static int ComputeBoolSize(int fieldNumber, bool value) {
482 return ComputeTagSize(fieldNumber) + 1;
483 }
484
485 /// <summary>
486 /// Compute the number of bytes that would be needed to encode a
487 /// string field, including the tag.
488 /// </summary>
489 public static int ComputeStringSize(int fieldNumber, String value) {
490 int byteArraySize = Encoding.UTF8.GetByteCount(value);
491 return ComputeTagSize(fieldNumber) +
492 ComputeRawVarint32Size((uint)byteArraySize) +
493 byteArraySize;
494 }
495
496 /// <summary>
497 /// Compute the number of bytes that would be needed to encode a
498 /// group field, including the tag.
499 /// </summary>
500 public static int ComputeGroupSize(int fieldNumber, IMessage value) {
501 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
502 }
503
504 /// <summary>
505 /// Compute the number of bytes that would be needed to encode a
506 /// group field represented by an UnknownFieldSet, including the tag.
507 /// </summary>
508 public static int ComputeUnknownGroupSize(int fieldNumber,
509 UnknownFieldSet value) {
510 return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
511 }
512
513 /// <summary>
514 /// Compute the number of bytes that would be needed to encode an
515 /// embedded message field, including the tag.
516 /// </summary>
517 public static int ComputeMessageSize(int fieldNumber, IMessage value) {
518 int size = value.SerializedSize;
519 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size;
520 }
521
522 /// <summary>
523 /// Compute the number of bytes that would be needed to encode a
524 /// bytes field, including the tag.
525 /// </summary>
526 public static int ComputeBytesSize(int fieldNumber, ByteString value) {
527 return ComputeTagSize(fieldNumber) +
528 ComputeRawVarint32Size((uint)value.Length) +
529 value.Length;
530 }
531
532 /// <summary>
533 /// Compute the number of bytes that would be needed to encode a
534 /// uint32 field, including the tag.
535 /// </summary>
536 public static int ComputeUInt32Size(int fieldNumber, uint value) {
537 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
538 }
539
540 /// <summary>
541 /// Compute the number of bytes that would be needed to encode a
542 /// enum field, including the tag. The caller is responsible for
543 /// converting the enum value to its numeric value.
544 /// </summary>
545 public static int ComputeEnumSize(int fieldNumber, int value) {
546 return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
547 }
548
549 /// <summary>
550 /// Compute the number of bytes that would be needed to encode an
551 /// sfixed32 field, including the tag.
552 /// </summary>
553 public static int ComputeSFixed32Size(int fieldNumber, int value) {
554 return ComputeTagSize(fieldNumber) + LittleEndian32Size;
555 }
556
557 /// <summary>
558 /// Compute the number of bytes that would be needed to encode an
559 /// sfixed64 field, including the tag.
560 /// </summary>
561 public static int ComputeSFixed64Size(int fieldNumber, long value) {
562 return ComputeTagSize(fieldNumber) + LittleEndian64Size;
563 }
564
565 /// <summary>
566 /// Compute the number of bytes that would be needed to encode an
567 /// sint32 field, including the tag.
568 /// </summary>
569 public static int ComputeSInt32Size(int fieldNumber, int value) {
570 return ComputeTagSize(fieldNumber) +
571 ComputeRawVarint32Size(EncodeZigZag32(value));
572 }
573
574 /// <summary>
575 /// Compute the number of bytes that would be needed to encode an
576 /// sint64 field, including the tag.
577 /// </summary>
578 public static int ComputeSInt64Size(int fieldNumber, long value) {
579 return ComputeTagSize(fieldNumber) +
580 ComputeRawVarint64Size(EncodeZigZag64(value));
581 }
582
583 /*
584 * Compute the number of bytes that would be needed to encode a
585 * MessageSet extension to the stream. For historical reasons,
586 * the wire format differs from normal fields.
587 */
588 /// <summary>
589 /// Compute the number of bytes that would be needed to encode a
590 /// MessageSet extension to the stream. For historical reasons,
591 /// the wire format differs from normal fields.
592 /// </summary>
593 public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) {
594 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
595 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
596 ComputeMessageSize(WireFormat.MessageSetField.Message, value);
597 }
598
599 /// <summary>
600 /// Compute the number of bytes that would be needed to encode an
601 /// unparsed MessageSet extension field to the stream. For
602 /// historical reasons, the wire format differs from normal fields.
603 /// </summary>
604 public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value) {
605 return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
606 ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
607 ComputeBytesSize(WireFormat.MessageSetField.Message, value);
608 }
609
610 /// <summary>
611 /// Compute the number of bytes that would be needed to encode a varint.
612 /// </summary>
613 public static int ComputeRawVarint32Size(uint value) {
614 if ((value & (0xffffffff << 7)) == 0) return 1;
615 if ((value & (0xffffffff << 14)) == 0) return 2;
616 if ((value & (0xffffffff << 21)) == 0) return 3;
617 if ((value & (0xffffffff << 28)) == 0) return 4;
618 return 5;
619 }
620
621 /// <summary>
622 /// Compute the number of bytes that would be needed to encode a varint.
623 /// </summary>
624 public static int ComputeRawVarint64Size(ulong value) {
625 if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
626 if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
627 if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
628 if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
629 if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
630 if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
631 if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
632 if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
633 if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
634 return 10;
635 }
636
637
638 /*
639 * Compute the number of bytes that would be needed to encode a
640 * field of arbitrary type, including tag, to the stream.
641 *
642 * @param type The field's type.
643 * @param number The field's number.
644 * @param value Object representing the field's value. Must be of the exact
645 * type which would be returned by
646 * {@link Message#getField(FieldDescriptor)} for
647 * this field.
648 */
649 public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) {
650 switch (fieldType) {
651 case FieldType.Double: return ComputeDoubleSize(fieldNumber, (double)value);
652 case FieldType.Float: return ComputeFloatSize(fieldNumber, (float)value);
653 case FieldType.Int64: return ComputeInt64Size(fieldNumber, (long)value);
654 case FieldType.UInt64: return ComputeUInt64Size(fieldNumber, (ulong)value);
655 case FieldType.Int32: return ComputeInt32Size(fieldNumber, (int)value);
656 case FieldType.Fixed64: return ComputeFixed64Size(fieldNumber, (ulong)value);
657 case FieldType.Fixed32: return ComputeFixed32Size(fieldNumber, (uint)value);
658 case FieldType.Bool: return ComputeBoolSize(fieldNumber, (bool)value);
659 case FieldType.String: return ComputeStringSize(fieldNumber, (string)value);
660 case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessage)value);
661 case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessage)value);
662 case FieldType.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value);
663 case FieldType.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value);
664 case FieldType.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value);
665 case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
666 case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
667 case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
668 case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number);
669 default:
670 throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
671 }
672 }
673
674 /// <summary>
675 /// Compute the number of bytes that would be needed to encode a tag.
676 /// </summary>
677 public static int ComputeTagSize(int fieldNumber) {
678 return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
679 }
680 #endregion
681
682 /// <summary>
683 /// Encode a 32-bit value with ZigZag encoding.
684 /// </summary>
685 /// <remarks>
686 /// ZigZag encodes signed integers into values that can be efficiently
687 /// encoded with varint. (Otherwise, negative values must be
688 /// sign-extended to 64 bits to be varint encoded, thus always taking
689 /// 10 bytes on the wire.)
690 /// </remarks>
691 public static uint EncodeZigZag32(int n) {
692 // Note: the right-shift must be arithmetic
693 return (uint)((n << 1) ^ (n >> 31));
694 }
695
696 /// <summary>
697 /// Encode a 64-bit value with ZigZag encoding.
698 /// </summary>
699 /// <remarks>
700 /// ZigZag encodes signed integers into values that can be efficiently
701 /// encoded with varint. (Otherwise, negative values must be
702 /// sign-extended to 64 bits to be varint encoded, thus always taking
703 /// 10 bytes on the wire.)
704 /// </remarks>
705 public static ulong EncodeZigZag64(long n) {
706 return (ulong)((n << 1) ^ (n >> 63));
707 }
708
709 private void RefreshBuffer() {
710 if (output == null) {
711 // We're writing to a single buffer.
712 throw new OutOfSpaceException();
713 }
714
715 // Since we have an output stream, this is our buffer
716 // and buffer offset == 0
717 output.Write(buffer, 0, position);
718 position = 0;
719 }
720
721 /// <summary>
722 /// Indicates that a CodedOutputStream wrapping a flat byte array
723 /// ran out of space.
724 /// </summary>
725 public sealed class OutOfSpaceException : IOException {
726 internal OutOfSpaceException()
727 : base("CodedOutputStream was writing to a flat byte array and ran out of space.") {
728 }
729 }
730
731 public void Flush() {
732 if (output != null) {
733 RefreshBuffer();
734 }
735 }
736
737 /// <summary>
738 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
739 /// that is exactly big enough to hold a message, then write to it with
740 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
741 /// the message was actually as big as expected, which can help bugs.
742 /// </summary>
743 public void CheckNoSpaceLeft() {
744 if (SpaceLeft != 0) {
745 throw new InvalidOperationException("Did not write as much data as expected.");
746 }
747 }
748
749 /// <summary>
750 /// If writing to a flat array, returns the space left in the array. Otherwise,
751 /// throws an InvalidOperationException.
752 /// </summary>
753 public int SpaceLeft {
754 get {
755 if (output == null) {
756 return limit - position;
757 } else {
758 throw new InvalidOperationException(
759 "SpaceLeft can only be called on CodedOutputStreams that are " +
760 "writing to a flat array.");
761 }
762 }
763 }
764 }
765}