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