blob: 7ebba9b0432fc82a50a8c4acfb8d11c9e3eae3f3 [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc. All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15// * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19// * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System;
csharptest90922db2011-06-03 11:57:47 -050038using System.Globalization;
csharptest71f662c2011-05-20 15:15:34 -050039using System.IO;
40using System.Text;
41using Google.ProtocolBuffers.Descriptors;
42
43namespace Google.ProtocolBuffers
44{
45 /// <summary>
46 /// Encodes and writes protocol message fields.
47 /// </summary>
48 /// <remarks>
49 /// This class contains two kinds of methods: methods that write specific
50 /// protocol message constructs and field types (e.g. WriteTag and
51 /// WriteInt32) and methods that write low-level values (e.g.
52 /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
53 /// messages, you should use the former methods, but if you are writing some
54 /// other format of your own design, use the latter. The names of the former
55 /// methods are taken from the protocol buffer type names, not .NET types.
56 /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
57 /// </remarks>
csharptestcc8d2aa2011-06-03 12:15:42 -050058 public sealed partial class CodedOutputStream : ICodedOutputStream
csharptest71f662c2011-05-20 15:15:34 -050059 {
60 /// <summary>
61 /// The buffer size used by CreateInstance(Stream).
62 /// </summary>
63 public static readonly int DefaultBufferSize = 4096;
64
65 private readonly byte[] buffer;
66 private readonly int limit;
67 private int position;
68 private readonly Stream output;
69
70 #region Construction
71
72 private CodedOutputStream(byte[] buffer, int offset, int length)
73 {
74 this.output = null;
75 this.buffer = buffer;
76 this.position = offset;
77 this.limit = offset + length;
78 }
79
80 private CodedOutputStream(Stream output, byte[] buffer)
81 {
82 this.output = output;
83 this.buffer = buffer;
84 this.position = 0;
85 this.limit = buffer.Length;
86 }
87
88 /// <summary>
89 /// Creates a new CodedOutputStream which write to the given stream.
90 /// </summary>
91 public static CodedOutputStream CreateInstance(Stream output)
92 {
93 return CreateInstance(output, DefaultBufferSize);
94 }
95
96 /// <summary>
97 /// Creates a new CodedOutputStream which write to the given stream and uses
98 /// the specified buffer size.
99 /// </summary>
100 public static CodedOutputStream CreateInstance(Stream output, int bufferSize)
101 {
102 return new CodedOutputStream(output, new byte[bufferSize]);
103 }
104
105 /// <summary>
106 /// Creates a new CodedOutputStream that writes directly to the given
107 /// byte array. If more bytes are written than fit in the array,
108 /// OutOfSpaceException will be thrown.
109 /// </summary>
110 public static CodedOutputStream CreateInstance(byte[] flatArray)
111 {
112 return CreateInstance(flatArray, 0, flatArray.Length);
113 }
114
115 /// <summary>
116 /// Creates a new CodedOutputStream that writes directly to the given
117 /// byte array slice. If more bytes are written than fit in the array,
118 /// OutOfSpaceException will be thrown.
119 /// </summary>
120 public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length)
121 {
122 return new CodedOutputStream(flatArray, offset, length);
123 }
124
125 #endregion
126
127 #region Writing of tags etc
128
129 /// <summary>
130 /// Writes a double field value, including tag, to the stream.
131 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500132 public void WriteDouble(int fieldNumber, string fieldName, double value)
csharptest71f662c2011-05-20 15:15:34 -0500133 {
134 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
135 WriteDoubleNoTag(value);
136 }
137
138 /// <summary>
139 /// Writes a float field value, including tag, to the stream.
140 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500141 public void WriteFloat(int fieldNumber, string fieldName, float value)
csharptest71f662c2011-05-20 15:15:34 -0500142 {
143 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
144 WriteFloatNoTag(value);
145 }
146
147 /// <summary>
148 /// Writes a uint64 field value, including tag, to the stream.
149 /// </summary>
150 [CLSCompliant(false)]
csharptest90922db2011-06-03 11:57:47 -0500151 public void WriteUInt64(int fieldNumber, string fieldName, ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500152 {
153 WriteTag(fieldNumber, WireFormat.WireType.Varint);
154 WriteRawVarint64(value);
155 }
156
157 /// <summary>
158 /// Writes an int64 field value, including tag, to the stream.
159 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500160 public void WriteInt64(int fieldNumber, string fieldName, long value)
csharptest71f662c2011-05-20 15:15:34 -0500161 {
162 WriteTag(fieldNumber, WireFormat.WireType.Varint);
163 WriteRawVarint64((ulong) value);
164 }
165
166 /// <summary>
167 /// Writes an int32 field value, including tag, to the stream.
168 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500169 public void WriteInt32(int fieldNumber, string fieldName, int value)
csharptest71f662c2011-05-20 15:15:34 -0500170 {
171 WriteTag(fieldNumber, WireFormat.WireType.Varint);
172 if (value >= 0)
173 {
174 WriteRawVarint32((uint) value);
175 }
176 else
177 {
178 // Must sign-extend.
179 WriteRawVarint64((ulong) value);
180 }
181 }
182
183 /// <summary>
184 /// Writes a fixed64 field value, including tag, to the stream.
185 /// </summary>
186 [CLSCompliant(false)]
csharptest90922db2011-06-03 11:57:47 -0500187 public void WriteFixed64(int fieldNumber, string fieldName, ulong value)
csharptest71f662c2011-05-20 15:15:34 -0500188 {
189 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
190 WriteRawLittleEndian64(value);
191 }
192
193 /// <summary>
194 /// Writes a fixed32 field value, including tag, to the stream.
195 /// </summary>
196 [CLSCompliant(false)]
csharptest90922db2011-06-03 11:57:47 -0500197 public void WriteFixed32(int fieldNumber, string fieldName, uint value)
csharptest71f662c2011-05-20 15:15:34 -0500198 {
199 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
200 WriteRawLittleEndian32(value);
201 }
202
203 /// <summary>
204 /// Writes a bool field value, including tag, to the stream.
205 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500206 public void WriteBool(int fieldNumber, string fieldName, bool value)
csharptest71f662c2011-05-20 15:15:34 -0500207 {
208 WriteTag(fieldNumber, WireFormat.WireType.Varint);
209 WriteRawByte(value ? (byte) 1 : (byte) 0);
210 }
211
212 /// <summary>
213 /// Writes a string field value, including tag, to the stream.
214 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500215 public void WriteString(int fieldNumber, string fieldName, string value)
csharptest71f662c2011-05-20 15:15:34 -0500216 {
217 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
218 // Optimise the case where we have enough space to write
219 // the string directly to the buffer, which should be common.
220 int length = Encoding.UTF8.GetByteCount(value);
221 WriteRawVarint32((uint) length);
222 if (limit - position >= length)
223 {
224 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
225 position += length;
226 }
227 else
228 {
229 byte[] bytes = Encoding.UTF8.GetBytes(value);
230 WriteRawBytes(bytes);
231 }
232 }
233
234 /// <summary>
235 /// Writes a group field value, including tag, to the stream.
236 /// </summary>
csharptest90922db2011-06-03 11:57:47 -0500237 public void WriteGroup(int fieldNumber, string fieldName, IMessageLite value)
csharptest71f662c2011-05-20 15:15:34 -0500238 {
239 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
240 value.WriteTo(this);
241 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
242 }
243
244 [Obsolete]
csharptest90922db2011-06-03 11:57:47 -0500245 public void WriteUnknownGroup(int fieldNumber, string fieldName, IMessageLite value)
csharptest71f662c2011-05-20 15:15:34 -0500246 {
247 WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
248 value.WriteTo(this);
249 WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
250 }
251
csharptest90922db2011-06-03 11:57:47 -0500252 public void WriteMessage(int fieldNumber, string fieldName, IMessageLite value)
csharptest71f662c2011-05-20 15:15:34 -0500253 {
254 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
255 WriteRawVarint32((uint) value.SerializedSize);
256 value.WriteTo(this);
257 }
258
csharptest90922db2011-06-03 11:57:47 -0500259 public void WriteBytes(int fieldNumber, string fieldName, ByteString value)
csharptest71f662c2011-05-20 15:15:34 -0500260 {
csharptest71f662c2011-05-20 15:15:34 -0500261 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
csharptest45a93fa2011-06-02 10:52:37 -0500262 WriteRawVarint32((uint)value.Length);
263 value.WriteTo(this);
csharptest71f662c2011-05-20 15:15:34 -0500264 }
265
266 [CLSCompliant(false)]
csharptest90922db2011-06-03 11:57:47 -0500267 public void WriteUInt32(int fieldNumber, string fieldName, uint value)
csharptest71f662c2011-05-20 15:15:34 -0500268 {
269 WriteTag(fieldNumber, WireFormat.WireType.Varint);
270 WriteRawVarint32(value);
271 }
272
csharptest90922db2011-06-03 11:57:47 -0500273 public void WriteEnum(int fieldNumber, string fieldName, int value, string textValue)
csharptest71f662c2011-05-20 15:15:34 -0500274 {
275 WriteTag(fieldNumber, WireFormat.WireType.Varint);
276 WriteRawVarint32((uint) value);
277 }
278
csharptest90922db2011-06-03 11:57:47 -0500279 public void WriteSFixed32(int fieldNumber, string fieldName, int value)
csharptest71f662c2011-05-20 15:15:34 -0500280 {
281 WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
282 WriteRawLittleEndian32((uint) value);
283 }
284
csharptest90922db2011-06-03 11:57:47 -0500285 public void WriteSFixed64(int fieldNumber, string fieldName, long value)
csharptest71f662c2011-05-20 15:15:34 -0500286 {
287 WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
288 WriteRawLittleEndian64((ulong) value);
289 }
290
csharptest90922db2011-06-03 11:57:47 -0500291 public void WriteSInt32(int fieldNumber, string fieldName, int value)
csharptest71f662c2011-05-20 15:15:34 -0500292 {
293 WriteTag(fieldNumber, WireFormat.WireType.Varint);
294 WriteRawVarint32(EncodeZigZag32(value));
295 }
296
csharptest90922db2011-06-03 11:57:47 -0500297 public void WriteSInt64(int fieldNumber, string fieldName, long value)
csharptest71f662c2011-05-20 15:15:34 -0500298 {
299 WriteTag(fieldNumber, WireFormat.WireType.Varint);
300 WriteRawVarint64(EncodeZigZag64(value));
301 }
302
csharptest90922db2011-06-03 11:57:47 -0500303 public void WriteMessageSetExtension(int fieldNumber, string fieldName, IMessageLite value)
csharptest71f662c2011-05-20 15:15:34 -0500304 {
305 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
csharptest90922db2011-06-03 11:57:47 -0500306 WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
307 WriteMessage(WireFormat.MessageSetField.Message, "message", value);
csharptest71f662c2011-05-20 15:15:34 -0500308 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
309 }
310
311 public void WriteRawMessageSetExtension(int fieldNumber, ByteString value)
312 {
313 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
csharptest90922db2011-06-03 11:57:47 -0500314 WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
315 WriteBytes(WireFormat.MessageSetField.Message, "message", value);
csharptest71f662c2011-05-20 15:15:34 -0500316 WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
317 }
318
csharptest90922db2011-06-03 11:57:47 -0500319 public void WriteMessageArray(int fieldNumber, string fieldName, System.Collections.IEnumerable list)
320 {
321 foreach (IMessageLite msg in list)
322 WriteMessage(fieldNumber, fieldName, msg);
323 }
324
325 public void WriteGroupArray(int fieldNumber, string fieldName, System.Collections.IEnumerable list)
326 {
327 foreach (IMessageLite msg in list)
328 WriteGroup(fieldNumber, fieldName, msg);
329 }
330
331 public void WriteArray(FieldType fieldType, int fieldNumber, string fieldName, System.Collections.IEnumerable list)
332 {
333 foreach (object element in list)
334 WriteField(fieldType, fieldNumber, fieldName, element);
335 }
336
337 public void WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, System.Collections.IEnumerable list)
338 {
339 int calculatedSize = 0;
340 foreach (object element in list)
341 calculatedSize += CodedOutputStream.ComputeFieldSizeNoTag(fieldType, element);
342 WritePackedArray(fieldType, fieldNumber, fieldName, calculatedSize, list);
343 }
344
345 public void WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, int calculatedSize, System.Collections.IEnumerable list)
346 {
347 WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
348 WriteRawVarint32((uint)calculatedSize);
349
350 foreach (object element in list)
351 WriteFieldNoTag(fieldType, element);
352 }
353
354 public void WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value)
csharptest71f662c2011-05-20 15:15:34 -0500355 {
356 switch (fieldType)
357 {
358 case FieldType.Double:
csharptest90922db2011-06-03 11:57:47 -0500359 WriteDouble(fieldNumber, fieldName, (double) value);
csharptest71f662c2011-05-20 15:15:34 -0500360 break;
361 case FieldType.Float:
csharptest90922db2011-06-03 11:57:47 -0500362 WriteFloat(fieldNumber, fieldName, (float) value);
csharptest71f662c2011-05-20 15:15:34 -0500363 break;
364 case FieldType.Int64:
csharptest90922db2011-06-03 11:57:47 -0500365 WriteInt64(fieldNumber, fieldName, (long) value);
csharptest71f662c2011-05-20 15:15:34 -0500366 break;
367 case FieldType.UInt64:
csharptest90922db2011-06-03 11:57:47 -0500368 WriteUInt64(fieldNumber, fieldName, (ulong) value);
csharptest71f662c2011-05-20 15:15:34 -0500369 break;
370 case FieldType.Int32:
csharptest90922db2011-06-03 11:57:47 -0500371 WriteInt32(fieldNumber, fieldName, (int) value);
csharptest71f662c2011-05-20 15:15:34 -0500372 break;
373 case FieldType.Fixed64:
csharptest90922db2011-06-03 11:57:47 -0500374 WriteFixed64(fieldNumber, fieldName, (ulong) value);
csharptest71f662c2011-05-20 15:15:34 -0500375 break;
376 case FieldType.Fixed32:
csharptest90922db2011-06-03 11:57:47 -0500377 WriteFixed32(fieldNumber, fieldName, (uint) value);
csharptest71f662c2011-05-20 15:15:34 -0500378 break;
379 case FieldType.Bool:
csharptest90922db2011-06-03 11:57:47 -0500380 WriteBool(fieldNumber, fieldName, (bool) value);
csharptest71f662c2011-05-20 15:15:34 -0500381 break;
382 case FieldType.String:
csharptest90922db2011-06-03 11:57:47 -0500383 WriteString(fieldNumber, fieldName, (string) value);
csharptest71f662c2011-05-20 15:15:34 -0500384 break;
385 case FieldType.Group:
csharptest90922db2011-06-03 11:57:47 -0500386 WriteGroup(fieldNumber, fieldName, (IMessageLite) value);
csharptest71f662c2011-05-20 15:15:34 -0500387 break;
388 case FieldType.Message:
csharptest90922db2011-06-03 11:57:47 -0500389 WriteMessage(fieldNumber, fieldName, (IMessageLite) value);
csharptest71f662c2011-05-20 15:15:34 -0500390 break;
391 case FieldType.Bytes:
csharptest90922db2011-06-03 11:57:47 -0500392 WriteBytes(fieldNumber, fieldName, (ByteString) value);
csharptest71f662c2011-05-20 15:15:34 -0500393 break;
394 case FieldType.UInt32:
csharptest90922db2011-06-03 11:57:47 -0500395 WriteUInt32(fieldNumber, fieldName, (uint) value);
csharptest71f662c2011-05-20 15:15:34 -0500396 break;
397 case FieldType.SFixed32:
csharptest90922db2011-06-03 11:57:47 -0500398 WriteSFixed32(fieldNumber, fieldName, (int) value);
csharptest71f662c2011-05-20 15:15:34 -0500399 break;
400 case FieldType.SFixed64:
csharptest90922db2011-06-03 11:57:47 -0500401 WriteSFixed64(fieldNumber, fieldName, (long) value);
csharptest71f662c2011-05-20 15:15:34 -0500402 break;
403 case FieldType.SInt32:
csharptest90922db2011-06-03 11:57:47 -0500404 WriteSInt32(fieldNumber, fieldName, (int) value);
csharptest71f662c2011-05-20 15:15:34 -0500405 break;
406 case FieldType.SInt64:
csharptest90922db2011-06-03 11:57:47 -0500407 WriteSInt64(fieldNumber, fieldName, (long) value);
csharptest71f662c2011-05-20 15:15:34 -0500408 break;
409 case FieldType.Enum:
csharptest90922db2011-06-03 11:57:47 -0500410 if(value is System.Enum)
csharptestcc8d2aa2011-06-03 12:15:42 -0500411 WriteEnum(fieldNumber, fieldName, ((IConvertible)value).ToInt32(CultureInfo.InvariantCulture), null/*not used*/);
csharptest90922db2011-06-03 11:57:47 -0500412 else
csharptestcc8d2aa2011-06-03 12:15:42 -0500413 WriteEnum(fieldNumber, fieldName, ((IEnumLite) value).Number, null/*not used*/);
csharptest71f662c2011-05-20 15:15:34 -0500414 break;
415 }
416 }
417
418 public void WriteFieldNoTag(FieldType fieldType, object value)
419 {
420 switch (fieldType)
421 {
422 case FieldType.Double:
423 WriteDoubleNoTag((double) value);
424 break;
425 case FieldType.Float:
426 WriteFloatNoTag((float) value);
427 break;
428 case FieldType.Int64:
429 WriteInt64NoTag((long) value);
430 break;
431 case FieldType.UInt64:
432 WriteUInt64NoTag((ulong) value);
433 break;
434 case FieldType.Int32:
435 WriteInt32NoTag((int) value);
436 break;
437 case FieldType.Fixed64:
438 WriteFixed64NoTag((ulong) value);
439 break;
440 case FieldType.Fixed32:
441 WriteFixed32NoTag((uint) value);
442 break;
443 case FieldType.Bool:
444 WriteBoolNoTag((bool) value);
445 break;
446 case FieldType.String:
447 WriteStringNoTag((string) value);
448 break;
449 case FieldType.Group:
450 WriteGroupNoTag((IMessageLite) value);
451 break;
452 case FieldType.Message:
453 WriteMessageNoTag((IMessageLite) value);
454 break;
455 case FieldType.Bytes:
456 WriteBytesNoTag((ByteString) value);
457 break;
458 case FieldType.UInt32:
459 WriteUInt32NoTag((uint) value);
460 break;
461 case FieldType.SFixed32:
462 WriteSFixed32NoTag((int) value);
463 break;
464 case FieldType.SFixed64:
465 WriteSFixed64NoTag((long) value);
466 break;
467 case FieldType.SInt32:
468 WriteSInt32NoTag((int) value);
469 break;
470 case FieldType.SInt64:
471 WriteSInt64NoTag((long) value);
472 break;
473 case FieldType.Enum:
csharptest90922db2011-06-03 11:57:47 -0500474 if (value is System.Enum)
475 WriteEnumNoTag(((IConvertible)value).ToInt32(CultureInfo.InvariantCulture));
476 else
477 WriteEnumNoTag(((IEnumLite) value).Number);
csharptest71f662c2011-05-20 15:15:34 -0500478 break;
479 }
480 }
481
482 #endregion
483
484 #region Writing of values without tags
485
486 /// <summary>
487 /// Writes a double field value, including tag, to the stream.
488 /// </summary>
489 public void WriteDoubleNoTag(double value)
490 {
csharptest71f662c2011-05-20 15:15:34 -0500491#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
csharptest8a2d0f42011-06-02 17:02:41 -0500492 byte[] rawBytes = BitConverter.GetBytes(value);
493 if (!BitConverter.IsLittleEndian)
494 Array.Reverse(rawBytes);
495 WriteRawBytes(rawBytes, 0, 8);
csharptest71f662c2011-05-20 15:15:34 -0500496#else
csharptest8a2d0f42011-06-02 17:02:41 -0500497 WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
csharptest71f662c2011-05-20 15:15:34 -0500498#endif
499 }
500
501 /// <summary>
502 /// Writes a float field value, without a tag, to the stream.
503 /// </summary>
504 public void WriteFloatNoTag(float value)
505 {
csharptest71f662c2011-05-20 15:15:34 -0500506 byte[] rawBytes = BitConverter.GetBytes(value);
csharptest8a2d0f42011-06-02 17:02:41 -0500507 if (!BitConverter.IsLittleEndian)
508 Array.Reverse(rawBytes);
509 WriteRawBytes(rawBytes, 0, 4);
csharptest71f662c2011-05-20 15:15:34 -0500510 }
511
512 /// <summary>
513 /// Writes a uint64 field value, without a tag, to the stream.
514 /// </summary>
515 [CLSCompliant(false)]
516 public void WriteUInt64NoTag(ulong value)
517 {
518 WriteRawVarint64(value);
519 }
520
521 /// <summary>
522 /// Writes an int64 field value, without a tag, to the stream.
523 /// </summary>
524 public void WriteInt64NoTag(long value)
525 {
526 WriteRawVarint64((ulong) value);
527 }
528
529 /// <summary>
530 /// Writes an int32 field value, without a tag, to the stream.
531 /// </summary>
532 public void WriteInt32NoTag(int value)
533 {
534 if (value >= 0)
535 {
536 WriteRawVarint32((uint) value);
537 }
538 else
539 {
540 // Must sign-extend.
541 WriteRawVarint64((ulong) value);
542 }
543 }
544
545 /// <summary>
546 /// Writes a fixed64 field value, without a tag, to the stream.
547 /// </summary>
548 [CLSCompliant(false)]
549 public void WriteFixed64NoTag(ulong value)
550 {
551 WriteRawLittleEndian64(value);
552 }
553
554 /// <summary>
555 /// Writes a fixed32 field value, without a tag, to the stream.
556 /// </summary>
557 [CLSCompliant(false)]
558 public void WriteFixed32NoTag(uint value)
559 {
560 WriteRawLittleEndian32(value);
561 }
562
563 /// <summary>
564 /// Writes a bool field value, without a tag, to the stream.
565 /// </summary>
566 public void WriteBoolNoTag(bool value)
567 {
568 WriteRawByte(value ? (byte) 1 : (byte) 0);
569 }
570
571 /// <summary>
572 /// Writes a string field value, without a tag, to the stream.
573 /// </summary>
574 public void WriteStringNoTag(string value)
575 {
576 // Optimise the case where we have enough space to write
577 // the string directly to the buffer, which should be common.
578 int length = Encoding.UTF8.GetByteCount(value);
579 WriteRawVarint32((uint) length);
580 if (limit - position >= length)
581 {
582 Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
583 position += length;
584 }
585 else
586 {
587 byte[] bytes = Encoding.UTF8.GetBytes(value);
588 WriteRawBytes(bytes);
589 }
590 }
591
592 /// <summary>
593 /// Writes a group field value, without a tag, to the stream.
594 /// </summary>
595 public void WriteGroupNoTag(IMessageLite value)
596 {
597 value.WriteTo(this);
598 }
599
600 public void WriteMessageNoTag(IMessageLite value)
601 {
602 WriteRawVarint32((uint) value.SerializedSize);
603 value.WriteTo(this);
604 }
605
606 public void WriteBytesNoTag(ByteString value)
607 {
csharptest45a93fa2011-06-02 10:52:37 -0500608 WriteRawVarint32((uint)value.Length);
609 value.WriteTo(this);
csharptest71f662c2011-05-20 15:15:34 -0500610 }
611
612 [CLSCompliant(false)]
613 public void WriteUInt32NoTag(uint value)
614 {
615 WriteRawVarint32(value);
616 }
617
618 public void WriteEnumNoTag(int value)
619 {
620 WriteRawVarint32((uint) value);
621 }
622
623 public void WriteSFixed32NoTag(int value)
624 {
625 WriteRawLittleEndian32((uint) value);
626 }
627
628 public void WriteSFixed64NoTag(long value)
629 {
630 WriteRawLittleEndian64((ulong) value);
631 }
632
633 public void WriteSInt32NoTag(int value)
634 {
635 WriteRawVarint32(EncodeZigZag32(value));
636 }
637
638 public void WriteSInt64NoTag(long value)
639 {
640 WriteRawVarint64(EncodeZigZag64(value));
641 }
642
643 #endregion
644
645 #region Underlying writing primitives
646
647 /// <summary>
648 /// Encodes and writes a tag.
649 /// </summary>
650 [CLSCompliant(false)]
651 public void WriteTag(int fieldNumber, WireFormat.WireType type)
652 {
653 WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
654 }
655
656 private void SlowWriteRawVarint32(uint value)
657 {
658 while (true)
659 {
660 if ((value & ~0x7F) == 0)
661 {
662 WriteRawByte(value);
663 return;
664 }
665 else
666 {
667 WriteRawByte((value & 0x7F) | 0x80);
668 value >>= 7;
669 }
670 }
671 }
672
673 /// <summary>
674 /// Writes a 32 bit value as a varint. The fast route is taken when
675 /// there's enough buffer space left to whizz through without checking
676 /// for each byte; otherwise, we resort to calling WriteRawByte each time.
677 /// </summary>
678 [CLSCompliant(false)]
679 public void WriteRawVarint32(uint value)
680 {
681 if (position + 5 > limit)
682 {
683 SlowWriteRawVarint32(value);
684 return;
685 }
686
687 while (true)
688 {
689 if ((value & ~0x7F) == 0)
690 {
691 buffer[position++] = (byte) value;
692 return;
693 }
694 else
695 {
696 buffer[position++] = (byte) ((value & 0x7F) | 0x80);
697 value >>= 7;
698 }
699 }
700 }
701
702 [CLSCompliant(false)]
703 public void WriteRawVarint64(ulong value)
704 {
705 while (true)
706 {
707 if ((value & ~0x7FUL) == 0)
708 {
709 WriteRawByte((uint) value);
710 return;
711 }
712 else
713 {
714 WriteRawByte(((uint) value & 0x7F) | 0x80);
715 value >>= 7;
716 }
717 }
718 }
719
720 [CLSCompliant(false)]
721 public void WriteRawLittleEndian32(uint value)
722 {
723 WriteRawByte((byte) value);
724 WriteRawByte((byte) (value >> 8));
725 WriteRawByte((byte) (value >> 16));
726 WriteRawByte((byte) (value >> 24));
727 }
728
729 [CLSCompliant(false)]
730 public void WriteRawLittleEndian64(ulong value)
731 {
732 WriteRawByte((byte) value);
733 WriteRawByte((byte) (value >> 8));
734 WriteRawByte((byte) (value >> 16));
735 WriteRawByte((byte) (value >> 24));
736 WriteRawByte((byte) (value >> 32));
737 WriteRawByte((byte) (value >> 40));
738 WriteRawByte((byte) (value >> 48));
739 WriteRawByte((byte) (value >> 56));
740 }
741
742 public void WriteRawByte(byte value)
743 {
744 if (position == limit)
745 {
746 RefreshBuffer();
747 }
748
749 buffer[position++] = value;
750 }
751
752 [CLSCompliant(false)]
753 public void WriteRawByte(uint value)
754 {
755 WriteRawByte((byte) value);
756 }
757
758 /// <summary>
759 /// Writes out an array of bytes.
760 /// </summary>
761 public void WriteRawBytes(byte[] value)
762 {
763 WriteRawBytes(value, 0, value.Length);
764 }
765
766 /// <summary>
767 /// Writes out part of an array of bytes.
768 /// </summary>
769 public void WriteRawBytes(byte[] value, int offset, int length)
770 {
771 if (limit - position >= length)
772 {
773 Array.Copy(value, offset, buffer, position, length);
774 // We have room in the current buffer.
775 position += length;
776 }
777 else
778 {
779 // Write extends past current buffer. Fill the rest of this buffer and
780 // flush.
781 int bytesWritten = limit - position;
782 Array.Copy(value, offset, buffer, position, bytesWritten);
783 offset += bytesWritten;
784 length -= bytesWritten;
785 position = limit;
786 RefreshBuffer();
787
788 // Now deal with the rest.
789 // Since we have an output stream, this is our buffer
790 // and buffer offset == 0
791 if (length <= limit)
792 {
793 // Fits in new buffer.
794 Array.Copy(value, offset, buffer, 0, length);
795 position = length;
796 }
797 else
798 {
799 // Write is very big. Let's do it all at once.
800 output.Write(value, offset, length);
801 }
802 }
803 }
804
805 #endregion
csharptest71f662c2011-05-20 15:15:34 -0500806 /// <summary>
807 /// Encode a 32-bit value with ZigZag encoding.
808 /// </summary>
809 /// <remarks>
810 /// ZigZag encodes signed integers into values that can be efficiently
811 /// encoded with varint. (Otherwise, negative values must be
812 /// sign-extended to 64 bits to be varint encoded, thus always taking
813 /// 10 bytes on the wire.)
814 /// </remarks>
815 [CLSCompliant(false)]
816 public static uint EncodeZigZag32(int n)
817 {
818 // Note: the right-shift must be arithmetic
819 return (uint) ((n << 1) ^ (n >> 31));
820 }
821
822 /// <summary>
823 /// Encode a 64-bit value with ZigZag encoding.
824 /// </summary>
825 /// <remarks>
826 /// ZigZag encodes signed integers into values that can be efficiently
827 /// encoded with varint. (Otherwise, negative values must be
828 /// sign-extended to 64 bits to be varint encoded, thus always taking
829 /// 10 bytes on the wire.)
830 /// </remarks>
831 [CLSCompliant(false)]
832 public static ulong EncodeZigZag64(long n)
833 {
834 return (ulong) ((n << 1) ^ (n >> 63));
835 }
836
837 private void RefreshBuffer()
838 {
839 if (output == null)
840 {
841 // We're writing to a single buffer.
842 throw new OutOfSpaceException();
843 }
844
845 // Since we have an output stream, this is our buffer
846 // and buffer offset == 0
847 output.Write(buffer, 0, position);
848 position = 0;
849 }
850
851 /// <summary>
852 /// Indicates that a CodedOutputStream wrapping a flat byte array
853 /// ran out of space.
854 /// </summary>
855 public sealed class OutOfSpaceException : IOException
856 {
857 internal OutOfSpaceException()
858 : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
859 {
860 }
861 }
862
863 public void Flush()
864 {
865 if (output != null)
866 {
867 RefreshBuffer();
868 }
869 }
870
871 /// <summary>
872 /// Verifies that SpaceLeft returns zero. It's common to create a byte array
873 /// that is exactly big enough to hold a message, then write to it with
874 /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
875 /// the message was actually as big as expected, which can help bugs.
876 /// </summary>
877 public void CheckNoSpaceLeft()
878 {
879 if (SpaceLeft != 0)
880 {
881 throw new InvalidOperationException("Did not write as much data as expected.");
882 }
883 }
884
885 /// <summary>
886 /// If writing to a flat array, returns the space left in the array. Otherwise,
887 /// throws an InvalidOperationException.
888 /// </summary>
889 public int SpaceLeft
890 {
891 get
892 {
893 if (output == null)
894 {
895 return limit - position;
896 }
897 else
898 {
899 throw new InvalidOperationException(
900 "SpaceLeft can only be called on CodedOutputStreams that are " +
901 "writing to a flat array.");
902 }
903 }
904 }
905 }
906}