CodedOutputStream.cs 46.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#region Copyright notice and license

// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#endregion

using System;
38
using System.Collections;
39
using System.Collections.Generic;
40 41
using System.IO;
using System.Text;
csharptest's avatar
csharptest committed
42
using Google.ProtocolBuffers.Collections;
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
using Google.ProtocolBuffers.Descriptors;

namespace Google.ProtocolBuffers
{
    /// <summary>
    /// Encodes and writes protocol message fields.
    /// </summary>
    /// <remarks>
    /// This class contains two kinds of methods:  methods that write specific
    /// protocol message constructs and field types (e.g. WriteTag and
    /// WriteInt32) and methods that write low-level values (e.g.
    /// WriteRawVarint32 and WriteRawBytes).  If you are writing encoded protocol
    /// messages, you should use the former methods, but if you are writing some
    /// other format of your own design, use the latter. The names of the former
    /// methods are taken from the protocol buffer type names, not .NET types.
    /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
    /// </remarks>
csharptest's avatar
csharptest committed
60
    public sealed partial class CodedOutputStream : ICodedOutputStream
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    {
        /// <summary>
        /// The buffer size used by CreateInstance(Stream).
        /// </summary>
        public static readonly int DefaultBufferSize = 4096;

        private readonly byte[] buffer;
        private readonly int limit;
        private int position;
        private readonly Stream output;

        #region Construction

        private CodedOutputStream(byte[] buffer, int offset, int length)
        {
            this.output = null;
            this.buffer = buffer;
            this.position = offset;
            this.limit = offset + length;
        }

        private CodedOutputStream(Stream output, byte[] buffer)
        {
            this.output = output;
            this.buffer = buffer;
            this.position = 0;
            this.limit = buffer.Length;
        }

        /// <summary>
        /// Creates a new CodedOutputStream which write to the given stream.
        /// </summary>
        public static CodedOutputStream CreateInstance(Stream output)
        {
            return CreateInstance(output, DefaultBufferSize);
        }

        /// <summary>
        /// Creates a new CodedOutputStream which write to the given stream and uses
        /// the specified buffer size.
        /// </summary>
        public static CodedOutputStream CreateInstance(Stream output, int bufferSize)
        {
            return new CodedOutputStream(output, new byte[bufferSize]);
        }

        /// <summary>
        /// Creates a new CodedOutputStream that writes directly to the given
        /// byte array. If more bytes are written than fit in the array,
        /// OutOfSpaceException will be thrown.
        /// </summary>
        public static CodedOutputStream CreateInstance(byte[] flatArray)
        {
            return CreateInstance(flatArray, 0, flatArray.Length);
        }

        /// <summary>
        /// Creates a new CodedOutputStream that writes directly to the given
        /// byte array slice. If more bytes are written than fit in the array,
        /// OutOfSpaceException will be thrown.
        /// </summary>
        public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length)
        {
            return new CodedOutputStream(flatArray, offset, length);
        }

        #endregion
128 129 130 131 132 133 134 135 136

        /// <summary>
        /// Returns the current position in the stream, or the position in the output buffer
        /// </summary>
        public long Position
        {
            get
            {
                if (output != null)
137
                {
138
                    return output.Position + position;
139
                }
140 141 142 143
                return position;
            }
        }

144 145 146
        void ICodedOutputStream.WriteMessageStart() { }
        void ICodedOutputStream.WriteMessageEnd() { Flush(); }

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
        #region Writing of unknown fields

        [Obsolete]
        public void WriteUnknownGroup(int fieldNumber, IMessageLite value)
        {
            WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
            value.WriteTo(this);
            WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
        }

        public void WriteUnknownBytes(int fieldNumber, ByteString value)
        {
            WriteBytes(fieldNumber, null /*not used*/, value);
        }

        public void WriteUnknownField(int fieldNumber, WireFormat.WireType wireType, ulong value)
        {
164 165
            if (wireType == WireFormat.WireType.Varint)
            {
166
                WriteUInt64(fieldNumber, null /*not used*/, value);
167
            }
168
            else if (wireType == WireFormat.WireType.Fixed32)
169 170 171
            {
                WriteFixed32(fieldNumber, null /*not used*/, (uint) value);
            }
172
            else if (wireType == WireFormat.WireType.Fixed64)
173
            {
174
                WriteFixed64(fieldNumber, null /*not used*/, value);
175
            }
176
            else
177
            {
178
                throw InvalidProtocolBufferException.InvalidWireType();
179
            }
180 181 182 183
        }

        #endregion

184 185 186 187 188 189 190
        #region Writing of tags and fields

        public void WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value)
        {
            switch (fieldType)
            {
                case FieldType.String:
191
                    WriteString(fieldNumber, fieldName, (string) value);
192 193
                    break;
                case FieldType.Message:
194
                    WriteMessage(fieldNumber, fieldName, (IMessageLite) value);
195 196
                    break;
                case FieldType.Group:
197
                    WriteGroup(fieldNumber, fieldName, (IMessageLite) value);
198 199
                    break;
                case FieldType.Bytes:
200
                    WriteBytes(fieldNumber, fieldName, (ByteString) value);
201 202
                    break;
                case FieldType.Bool:
203
                    WriteBool(fieldNumber, fieldName, (bool) value);
204 205
                    break;
                case FieldType.Enum:
206 207 208 209
                    if (value is Enum)
                    {
                        WriteEnum(fieldNumber, fieldName, (int) value, null /*not used*/);
                    }
210
                    else
211 212 213
                    {
                        WriteEnum(fieldNumber, fieldName, ((IEnumLite) value).Number, null /*not used*/);
                    }
214 215
                    break;
                case FieldType.Int32:
216
                    WriteInt32(fieldNumber, fieldName, (int) value);
217 218
                    break;
                case FieldType.Int64:
219
                    WriteInt64(fieldNumber, fieldName, (long) value);
220 221
                    break;
                case FieldType.UInt32:
222
                    WriteUInt32(fieldNumber, fieldName, (uint) value);
223 224
                    break;
                case FieldType.UInt64:
225
                    WriteUInt64(fieldNumber, fieldName, (ulong) value);
226 227
                    break;
                case FieldType.SInt32:
228
                    WriteSInt32(fieldNumber, fieldName, (int) value);
229 230
                    break;
                case FieldType.SInt64:
231
                    WriteSInt64(fieldNumber, fieldName, (long) value);
232 233
                    break;
                case FieldType.Fixed32:
234
                    WriteFixed32(fieldNumber, fieldName, (uint) value);
235 236
                    break;
                case FieldType.Fixed64:
237
                    WriteFixed64(fieldNumber, fieldName, (ulong) value);
238 239
                    break;
                case FieldType.SFixed32:
240
                    WriteSFixed32(fieldNumber, fieldName, (int) value);
241 242
                    break;
                case FieldType.SFixed64:
243
                    WriteSFixed64(fieldNumber, fieldName, (long) value);
244 245
                    break;
                case FieldType.Double:
246
                    WriteDouble(fieldNumber, fieldName, (double) value);
247 248
                    break;
                case FieldType.Float:
249
                    WriteFloat(fieldNumber, fieldName, (float) value);
250 251 252
                    break;
            }
        }
253 254 255 256

        /// <summary>
        /// Writes a double field value, including tag, to the stream.
        /// </summary>
257
        public void WriteDouble(int fieldNumber, string fieldName, double value)
258 259 260 261 262 263 264 265
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
            WriteDoubleNoTag(value);
        }

        /// <summary>
        /// Writes a float field value, including tag, to the stream.
        /// </summary>
266
        public void WriteFloat(int fieldNumber, string fieldName, float value)
267 268 269 270 271 272 273 274
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
            WriteFloatNoTag(value);
        }

        /// <summary>
        /// Writes a uint64 field value, including tag, to the stream.
        /// </summary>
275
        public void WriteUInt64(int fieldNumber, string fieldName, ulong value)
276 277 278 279 280 281 282 283
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawVarint64(value);
        }

        /// <summary>
        /// Writes an int64 field value, including tag, to the stream.
        /// </summary>
284
        public void WriteInt64(int fieldNumber, string fieldName, long value)
285 286 287 288 289 290 291 292
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawVarint64((ulong) value);
        }

        /// <summary>
        /// Writes an int32 field value, including tag, to the stream.
        /// </summary>
293
        public void WriteInt32(int fieldNumber, string fieldName, int value)
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            if (value >= 0)
            {
                WriteRawVarint32((uint) value);
            }
            else
            {
                // Must sign-extend.
                WriteRawVarint64((ulong) value);
            }
        }

        /// <summary>
        /// Writes a fixed64 field value, including tag, to the stream.
        /// </summary>
310
        public void WriteFixed64(int fieldNumber, string fieldName, ulong value)
311 312 313 314 315 316 317 318
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
            WriteRawLittleEndian64(value);
        }

        /// <summary>
        /// Writes a fixed32 field value, including tag, to the stream.
        /// </summary>
319
        public void WriteFixed32(int fieldNumber, string fieldName, uint value)
320 321 322 323 324 325 326 327
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
            WriteRawLittleEndian32(value);
        }

        /// <summary>
        /// Writes a bool field value, including tag, to the stream.
        /// </summary>
328
        public void WriteBool(int fieldNumber, string fieldName, bool value)
329 330 331 332 333 334 335 336
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawByte(value ? (byte) 1 : (byte) 0);
        }

        /// <summary>
        /// Writes a string field value, including tag, to the stream.
        /// </summary>
337
        public void WriteString(int fieldNumber, string fieldName, string value)
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
            // Optimise the case where we have enough space to write
            // the string directly to the buffer, which should be common.
            int length = Encoding.UTF8.GetByteCount(value);
            WriteRawVarint32((uint) length);
            if (limit - position >= length)
            {
                Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
                position += length;
            }
            else
            {
                byte[] bytes = Encoding.UTF8.GetBytes(value);
                WriteRawBytes(bytes);
            }
        }

        /// <summary>
        /// Writes a group field value, including tag, to the stream.
        /// </summary>
359
        public void WriteGroup(int fieldNumber, string fieldName, IMessageLite value)
360 361 362 363 364 365
        {
            WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
            value.WriteTo(this);
            WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
        }

366
        public void WriteMessage(int fieldNumber, string fieldName, IMessageLite value)
367 368 369 370 371 372
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
            WriteRawVarint32((uint) value.SerializedSize);
            value.WriteTo(this);
        }

373
        public void WriteBytes(int fieldNumber, string fieldName, ByteString value)
374 375
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
376
            WriteRawVarint32((uint) value.Length);
377
            value.WriteRawBytesTo(this);
378 379
        }

380
        public void WriteUInt32(int fieldNumber, string fieldName, uint value)
381 382 383 384 385
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawVarint32(value);
        }

386
        public void WriteEnum(int fieldNumber, string fieldName, int value, object rawValue)
387 388
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
389
            WriteInt32NoTag(value);
390 391
        }

392
        public void WriteSFixed32(int fieldNumber, string fieldName, int value)
393 394 395 396 397
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
            WriteRawLittleEndian32((uint) value);
        }

398
        public void WriteSFixed64(int fieldNumber, string fieldName, long value)
399 400 401 402 403
        {
            WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
            WriteRawLittleEndian64((ulong) value);
        }

404
        public void WriteSInt32(int fieldNumber, string fieldName, int value)
405 406 407 408 409
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawVarint32(EncodeZigZag32(value));
        }

410
        public void WriteSInt64(int fieldNumber, string fieldName, long value)
411 412 413 414 415
        {
            WriteTag(fieldNumber, WireFormat.WireType.Varint);
            WriteRawVarint64(EncodeZigZag64(value));
        }

416
        public void WriteMessageSetExtension(int fieldNumber, string fieldName, IMessageLite value)
417 418
        {
            WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
419 420
            WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
            WriteMessage(WireFormat.MessageSetField.Message, "message", value);
421 422 423
            WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
        }

424
        public void WriteMessageSetExtension(int fieldNumber, string fieldName, ByteString value)
425 426
        {
            WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
427 428
            WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
            WriteBytes(WireFormat.MessageSetField.Message, "message", value);
429 430
            WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
        }
431

432
        #endregion
433

434
        #region Writing of values without tags
435 436 437 438 439

        public void WriteFieldNoTag(FieldType fieldType, object value)
        {
            switch (fieldType)
            {
440
                case FieldType.String:
441
                    WriteStringNoTag((string) value);
442
                    break;
443
                case FieldType.Message:
444
                    WriteMessageNoTag((IMessageLite) value);
445
                    break;
446
                case FieldType.Group:
447
                    WriteGroupNoTag((IMessageLite) value);
448
                    break;
449
                case FieldType.Bytes:
450
                    WriteBytesNoTag((ByteString) value);
451 452
                    break;
                case FieldType.Bool:
453
                    WriteBoolNoTag((bool) value);
454 455
                    break;
                case FieldType.Enum:
456 457 458 459
                    if (value is Enum)
                    {
                        WriteEnumNoTag((int) value);
                    }
460
                    else
461 462 463
                    {
                        WriteEnumNoTag(((IEnumLite) value).Number);
                    }
464 465
                    break;
                case FieldType.Int32:
466
                    WriteInt32NoTag((int) value);
467
                    break;
468
                case FieldType.Int64:
469
                    WriteInt64NoTag((long) value);
470
                    break;
471
                case FieldType.UInt32:
472
                    WriteUInt32NoTag((uint) value);
473
                    break;
474
                case FieldType.UInt64:
475
                    WriteUInt64NoTag((ulong) value);
476
                    break;
477
                case FieldType.SInt32:
478
                    WriteSInt32NoTag((int) value);
479
                    break;
480
                case FieldType.SInt64:
481
                    WriteSInt64NoTag((long) value);
482
                    break;
483
                case FieldType.Fixed32:
484
                    WriteFixed32NoTag((uint) value);
485
                    break;
486
                case FieldType.Fixed64:
487
                    WriteFixed64NoTag((ulong) value);
488 489
                    break;
                case FieldType.SFixed32:
490
                    WriteSFixed32NoTag((int) value);
491 492
                    break;
                case FieldType.SFixed64:
493
                    WriteSFixed64NoTag((long) value);
494
                    break;
495
                case FieldType.Double:
496
                    WriteDoubleNoTag((double) value);
497
                    break;
498
                case FieldType.Float:
499
                    WriteFloatNoTag((float) value);
500 501 502 503 504 505 506 507 508
                    break;
            }
        }

        /// <summary>
        /// Writes a double field value, including tag, to the stream.
        /// </summary>
        public void WriteDoubleNoTag(double value)
        {
509
            WriteRawLittleEndian64((ulong)FrameworkPortability.DoubleToInt64(value));
510 511 512 513 514 515 516 517
        }

        /// <summary>
        /// Writes a float field value, without a tag, to the stream.
        /// </summary>
        public void WriteFloatNoTag(float value)
        {
            byte[] rawBytes = BitConverter.GetBytes(value);
518
            if (!BitConverter.IsLittleEndian)
519
            {
520
                ByteArray.Reverse(rawBytes);
521
            }
522 523 524 525 526 527 528 529 530

            if (limit - position >= 4)
            {
                buffer[position++] = rawBytes[0];
                buffer[position++] = rawBytes[1];
                buffer[position++] = rawBytes[2];
                buffer[position++] = rawBytes[3];
            }
            else
531
            {
532
                WriteRawBytes(rawBytes, 0, 4);
533
            }
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
        }

        /// <summary>
        /// Writes a uint64 field value, without a tag, to the stream.
        /// </summary>
        public void WriteUInt64NoTag(ulong value)
        {
            WriteRawVarint64(value);
        }

        /// <summary>
        /// Writes an int64 field value, without a tag, to the stream.
        /// </summary>
        public void WriteInt64NoTag(long value)
        {
549
            WriteRawVarint64((ulong) value);
550 551 552 553 554 555 556 557 558
        }

        /// <summary>
        /// Writes an int32 field value, without a tag, to the stream.
        /// </summary>
        public void WriteInt32NoTag(int value)
        {
            if (value >= 0)
            {
559
                WriteRawVarint32((uint) value);
560 561 562 563
            }
            else
            {
                // Must sign-extend.
564
                WriteRawVarint64((ulong) value);
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
            }
        }

        /// <summary>
        /// Writes a fixed64 field value, without a tag, to the stream.
        /// </summary>
        public void WriteFixed64NoTag(ulong value)
        {
            WriteRawLittleEndian64(value);
        }

        /// <summary>
        /// Writes a fixed32 field value, without a tag, to the stream.
        /// </summary>
        public void WriteFixed32NoTag(uint value)
        {
            WriteRawLittleEndian32(value);
        }

        /// <summary>
        /// Writes a bool field value, without a tag, to the stream.
        /// </summary>
        public void WriteBoolNoTag(bool value)
        {
589
            WriteRawByte(value ? (byte) 1 : (byte) 0);
590 591 592 593 594 595 596 597 598 599
        }

        /// <summary>
        /// Writes a string field value, without a tag, to the stream.
        /// </summary>
        public void WriteStringNoTag(string value)
        {
            // Optimise the case where we have enough space to write
            // the string directly to the buffer, which should be common.
            int length = Encoding.UTF8.GetByteCount(value);
600
            WriteRawVarint32((uint) length);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
            if (limit - position >= length)
            {
                Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
                position += length;
            }
            else
            {
                byte[] bytes = Encoding.UTF8.GetBytes(value);
                WriteRawBytes(bytes);
            }
        }

        /// <summary>
        /// Writes a group field value, without a tag, to the stream.
        /// </summary>
        public void WriteGroupNoTag(IMessageLite value)
        {
            value.WriteTo(this);
        }

        public void WriteMessageNoTag(IMessageLite value)
        {
623
            WriteRawVarint32((uint) value.SerializedSize);
624 625 626 627 628
            value.WriteTo(this);
        }

        public void WriteBytesNoTag(ByteString value)
        {
629
            WriteRawVarint32((uint) value.Length);
630
            value.WriteRawBytesTo(this);
631 632 633 634 635 636 637 638 639
        }

        public void WriteUInt32NoTag(uint value)
        {
            WriteRawVarint32(value);
        }

        public void WriteEnumNoTag(int value)
        {
640
            WriteInt32NoTag(value);
641 642 643 644
        }

        public void WriteSFixed32NoTag(int value)
        {
645
            WriteRawLittleEndian32((uint) value);
646 647 648 649
        }

        public void WriteSFixed64NoTag(long value)
        {
650
            WriteRawLittleEndian64((ulong) value);
651 652 653 654 655 656 657 658 659 660 661 662 663 664
        }

        public void WriteSInt32NoTag(int value)
        {
            WriteRawVarint32(EncodeZigZag32(value));
        }

        public void WriteSInt64NoTag(long value)
        {
            WriteRawVarint64(EncodeZigZag64(value));
        }

        #endregion

665 666
        #region Write array members

667
        public void WriteArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
668 669
        {
            foreach (object element in list)
670
            {
671
                WriteField(fieldType, fieldNumber, fieldName, element);
672
            }
673 674 675 676 677 678
        }

        public void WriteGroupArray<T>(int fieldNumber, string fieldName, IEnumerable<T> list)
            where T : IMessageLite
        {
            foreach (IMessageLite value in list)
679
            {
680
                WriteGroup(fieldNumber, fieldName, value);
681
            }
682 683 684 685 686 687
        }

        public void WriteMessageArray<T>(int fieldNumber, string fieldName, IEnumerable<T> list)
            where T : IMessageLite
        {
            foreach (IMessageLite value in list)
688
            {
689
                WriteMessage(fieldNumber, fieldName, value);
690
            }
691 692 693 694 695
        }

        public void WriteStringArray(int fieldNumber, string fieldName, IEnumerable<string> list)
        {
            foreach (var value in list)
696
            {
697
                WriteString(fieldNumber, fieldName, value);
698
            }
699 700 701 702 703
        }

        public void WriteBytesArray(int fieldNumber, string fieldName, IEnumerable<ByteString> list)
        {
            foreach (var value in list)
704
            {
705
                WriteBytes(fieldNumber, fieldName, value);
706
            }
707 708 709 710 711
        }

        public void WriteBoolArray(int fieldNumber, string fieldName, IEnumerable<bool> list)
        {
            foreach (var value in list)
712
            {
713
                WriteBool(fieldNumber, fieldName, value);
714
            }
715 716 717 718 719
        }

        public void WriteInt32Array(int fieldNumber, string fieldName, IEnumerable<int> list)
        {
            foreach (var value in list)
720
            {
721
                WriteInt32(fieldNumber, fieldName, value);
722
            }
723 724 725 726 727
        }

        public void WriteSInt32Array(int fieldNumber, string fieldName, IEnumerable<int> list)
        {
            foreach (var value in list)
728
            {
729
                WriteSInt32(fieldNumber, fieldName, value);
730
            }
731 732 733 734 735
        }

        public void WriteUInt32Array(int fieldNumber, string fieldName, IEnumerable<uint> list)
        {
            foreach (var value in list)
736
            {
737
                WriteUInt32(fieldNumber, fieldName, value);
738
            }
739 740 741 742 743
        }

        public void WriteFixed32Array(int fieldNumber, string fieldName, IEnumerable<uint> list)
        {
            foreach (var value in list)
744
            {
745
                WriteFixed32(fieldNumber, fieldName, value);
746
            }
747 748 749 750 751
        }

        public void WriteSFixed32Array(int fieldNumber, string fieldName, IEnumerable<int> list)
        {
            foreach (var value in list)
752
            {
753
                WriteSFixed32(fieldNumber, fieldName, value);
754
            }
755 756 757 758 759
        }

        public void WriteInt64Array(int fieldNumber, string fieldName, IEnumerable<long> list)
        {
            foreach (var value in list)
760
            {
761
                WriteInt64(fieldNumber, fieldName, value);
762
            }
763 764 765 766 767
        }

        public void WriteSInt64Array(int fieldNumber, string fieldName, IEnumerable<long> list)
        {
            foreach (var value in list)
768
            {
769
                WriteSInt64(fieldNumber, fieldName, value);
770
            }
771 772 773 774 775
        }

        public void WriteUInt64Array(int fieldNumber, string fieldName, IEnumerable<ulong> list)
        {
            foreach (var value in list)
776
            {
777
                WriteUInt64(fieldNumber, fieldName, value);
778
            }
779 780 781 782 783
        }

        public void WriteFixed64Array(int fieldNumber, string fieldName, IEnumerable<ulong> list)
        {
            foreach (var value in list)
784
            {
785
                WriteFixed64(fieldNumber, fieldName, value);
786
            }
787 788 789 790 791
        }

        public void WriteSFixed64Array(int fieldNumber, string fieldName, IEnumerable<long> list)
        {
            foreach (var value in list)
792
            {
793
                WriteSFixed64(fieldNumber, fieldName, value);
794
            }
795 796 797 798 799
        }

        public void WriteDoubleArray(int fieldNumber, string fieldName, IEnumerable<double> list)
        {
            foreach (var value in list)
800
            {
801
                WriteDouble(fieldNumber, fieldName, value);
802
            }
803 804 805 806 807
        }

        public void WriteFloatArray(int fieldNumber, string fieldName, IEnumerable<float> list)
        {
            foreach (var value in list)
808
            {
809
                WriteFloat(fieldNumber, fieldName, value);
810
            }
811 812 813
        }

        public void WriteEnumArray<T>(int fieldNumber, string fieldName, IEnumerable<T> list)
csharptest's avatar
csharptest committed
814
            where T : struct, IComparable, IFormattable
815 816 817
        {
            if (list is ICastArray)
            {
818 819
                foreach (int value in ((ICastArray) list).CastArray<int>())
                {
820
                    WriteEnum(fieldNumber, fieldName, value, null /*unused*/);
821
                }
822 823 824 825
            }
            else
            {
                foreach (object value in list)
826
                {
827
                    WriteEnum(fieldNumber, fieldName, (int) value, null /*unused*/);
828
                }
829 830 831 832 833 834 835
            }
        }

        #endregion

        #region Write packed array members

836
        public void WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
837 838 839
        {
            int calculatedSize = 0;
            foreach (object element in list)
840 841 842
            {
                calculatedSize += ComputeFieldSizeNoTag(fieldType, element);
            }
843 844

            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
845
            WriteRawVarint32((uint) calculatedSize);
846 847

            foreach (object element in list)
848
            {
849
                WriteFieldNoTag(fieldType, element);
850
            }
851 852 853 854 855 856
        }

        public void WritePackedGroupArray<T>(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<T> list)
            where T : IMessageLite
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
857
            WriteRawVarint32((uint) calculatedSize);
858
            foreach (IMessageLite value in list)
859
            {
860
                WriteGroupNoTag(value);
861
            }
862 863
        }

864 865
        public void WritePackedMessageArray<T>(int fieldNumber, string fieldName, int calculatedSize,
                                               IEnumerable<T> list)
866 867 868
            where T : IMessageLite
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
869
            WriteRawVarint32((uint) calculatedSize);
870
            foreach (IMessageLite value in list)
871
            {
872
                WriteMessageNoTag(value);
873
            }
874 875
        }

876 877
        public void WritePackedStringArray(int fieldNumber, string fieldName, int calculatedSize,
                                           IEnumerable<string> list)
878 879
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
880
            WriteRawVarint32((uint) calculatedSize);
881
            foreach (var value in list)
882
            {
883
                WriteStringNoTag(value);
884
            }
885 886
        }

887 888
        public void WritePackedBytesArray(int fieldNumber, string fieldName, int calculatedSize,
                                          IEnumerable<ByteString> list)
889 890
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
891
            WriteRawVarint32((uint) calculatedSize);
892
            foreach (var value in list)
893
            {
894
                WriteBytesNoTag(value);
895
            }
896 897 898 899 900
        }

        public void WritePackedBoolArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<bool> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
901
            WriteRawVarint32((uint) calculatedSize);
902
            foreach (var value in list)
903
            {
904
                WriteBoolNoTag(value);
905
            }
906 907 908 909 910
        }

        public void WritePackedInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<int> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
911
            WriteRawVarint32((uint) calculatedSize);
912
            foreach (var value in list)
913
            {
914
                WriteInt32NoTag(value);
915
            }
916 917 918 919 920
        }

        public void WritePackedSInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<int> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
921
            WriteRawVarint32((uint) calculatedSize);
922
            foreach (var value in list)
923
            {
924
                WriteSInt32NoTag(value);
925
            }
926 927 928 929 930
        }

        public void WritePackedUInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<uint> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
931
            WriteRawVarint32((uint) calculatedSize);
932
            foreach (var value in list)
933
            {
934
                WriteUInt32NoTag(value);
935
            }
936 937
        }

938 939
        public void WritePackedFixed32Array(int fieldNumber, string fieldName, int calculatedSize,
                                            IEnumerable<uint> list)
940 941
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
942
            WriteRawVarint32((uint) calculatedSize);
943
            foreach (var value in list)
944
            {
945
                WriteFixed32NoTag(value);
946
            }
947 948
        }

949 950
        public void WritePackedSFixed32Array(int fieldNumber, string fieldName, int calculatedSize,
                                             IEnumerable<int> list)
951 952
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
953
            WriteRawVarint32((uint) calculatedSize);
954
            foreach (var value in list)
955
            {
956
                WriteSFixed32NoTag(value);
957
            }
958 959 960 961 962
        }

        public void WritePackedInt64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<long> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
963
            WriteRawVarint32((uint) calculatedSize);
964
            foreach (var value in list)
965
            {
966
                WriteInt64NoTag(value);
967
            }
968 969 970 971 972
        }

        public void WritePackedSInt64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<long> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
973
            WriteRawVarint32((uint) calculatedSize);
974
            foreach (var value in list)
975
            {
976
                WriteSInt64NoTag(value);
977
            }
978 979
        }

980 981
        public void WritePackedUInt64Array(int fieldNumber, string fieldName, int calculatedSize,
                                           IEnumerable<ulong> list)
982 983
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
984
            WriteRawVarint32((uint) calculatedSize);
985
            foreach (var value in list)
986
            {
987
                WriteUInt64NoTag(value);
988
            }
989 990
        }

991 992
        public void WritePackedFixed64Array(int fieldNumber, string fieldName, int calculatedSize,
                                            IEnumerable<ulong> list)
993 994
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
995
            WriteRawVarint32((uint) calculatedSize);
996
            foreach (var value in list)
997
            {
998
                WriteFixed64NoTag(value);
999
            }
1000 1001
        }

1002 1003
        public void WritePackedSFixed64Array(int fieldNumber, string fieldName, int calculatedSize,
                                             IEnumerable<long> list)
1004 1005
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
1006
            WriteRawVarint32((uint) calculatedSize);
1007
            foreach (var value in list)
1008
            {
1009
                WriteSFixed64NoTag(value);
1010
            }
1011 1012
        }

1013 1014
        public void WritePackedDoubleArray(int fieldNumber, string fieldName, int calculatedSize,
                                           IEnumerable<double> list)
1015 1016
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
1017
            WriteRawVarint32((uint) calculatedSize);
1018
            foreach (var value in list)
1019
            {
1020
                WriteDoubleNoTag(value);
1021
            }
1022 1023 1024 1025 1026
        }

        public void WritePackedFloatArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<float> list)
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
1027
            WriteRawVarint32((uint) calculatedSize);
1028
            foreach (var value in list)
1029
            {
1030
                WriteFloatNoTag(value);
1031
            }
1032 1033 1034
        }

        public void WritePackedEnumArray<T>(int fieldNumber, string fieldName, int calculatedSize, IEnumerable<T> list)
csharptest's avatar
csharptest committed
1035
            where T : struct, IComparable, IFormattable
1036 1037
        {
            WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
1038
            WriteRawVarint32((uint) calculatedSize);
1039 1040
            if (list is ICastArray)
            {
1041 1042
                foreach (int value in ((ICastArray) list).CastArray<int>())
                {
1043
                    WriteEnumNoTag(value);
1044
                }
1045 1046 1047 1048
            }
            else
            {
                foreach (object value in list)
1049 1050 1051
                {
                    WriteEnumNoTag((int) value);
                }
1052 1053 1054 1055 1056
            }
        }

        #endregion

1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
        #region Underlying writing primitives

        /// <summary>
        /// Encodes and writes a tag.
        /// </summary>
        public void WriteTag(int fieldNumber, WireFormat.WireType type)
        {
            WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
        }

        /// <summary>
        /// Writes a 32 bit value as a varint. The fast route is taken when
        /// there's enough buffer space left to whizz through without checking
        /// for each byte; otherwise, we resort to calling WriteRawByte each time.
        /// </summary>
        public void WriteRawVarint32(uint value)
        {
1074 1075
            while (value > 127 && position < limit)
            {
1076
                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
1077 1078 1079 1080
                value >>= 7;
            }
            while (value > 127)
            {
1081
                WriteRawByte((byte) ((value & 0x7F) | 0x80));
1082 1083
                value >>= 7;
            }
1084 1085 1086 1087
            if (position < limit)
            {
                buffer[position++] = (byte) value;
            }
1088
            else
1089 1090 1091
            {
                WriteRawByte((byte) value);
            }
1092 1093 1094 1095
        }

        public void WriteRawVarint64(ulong value)
        {
1096 1097
            while (value > 127 && position < limit)
            {
1098
                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
1099 1100 1101 1102
                value >>= 7;
            }
            while (value > 127)
            {
1103
                WriteRawByte((byte) ((value & 0x7F) | 0x80));
1104 1105
                value >>= 7;
            }
1106 1107 1108 1109
            if (position < limit)
            {
                buffer[position++] = (byte) value;
            }
1110
            else
1111 1112 1113
            {
                WriteRawByte((byte) value);
            }
1114 1115 1116 1117
        }

        public void WriteRawLittleEndian32(uint value)
        {
1118 1119 1120 1121 1122 1123 1124 1125 1126
            if (position + 4 > limit)
            {
                WriteRawByte((byte) value);
                WriteRawByte((byte) (value >> 8));
                WriteRawByte((byte) (value >> 16));
                WriteRawByte((byte) (value >> 24));
            }
            else
            {
1127 1128 1129 1130
                buffer[position++] = ((byte) value);
                buffer[position++] = ((byte) (value >> 8));
                buffer[position++] = ((byte) (value >> 16));
                buffer[position++] = ((byte) (value >> 24));
1131
            }
1132 1133 1134 1135
        }

        public void WriteRawLittleEndian64(ulong value)
        {
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
            if (position + 8 > limit)
            {
                WriteRawByte((byte) value);
                WriteRawByte((byte) (value >> 8));
                WriteRawByte((byte) (value >> 16));
                WriteRawByte((byte) (value >> 24));
                WriteRawByte((byte) (value >> 32));
                WriteRawByte((byte) (value >> 40));
                WriteRawByte((byte) (value >> 48));
                WriteRawByte((byte) (value >> 56));
            }
            else
            {
1149 1150 1151 1152 1153 1154 1155 1156
                buffer[position++] = ((byte) value);
                buffer[position++] = ((byte) (value >> 8));
                buffer[position++] = ((byte) (value >> 16));
                buffer[position++] = ((byte) (value >> 24));
                buffer[position++] = ((byte) (value >> 32));
                buffer[position++] = ((byte) (value >> 40));
                buffer[position++] = ((byte) (value >> 48));
                buffer[position++] = ((byte) (value >> 56));
1157
            }
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
        }

        public void WriteRawByte(byte value)
        {
            if (position == limit)
            {
                RefreshBuffer();
            }

            buffer[position++] = value;
        }

        public void WriteRawByte(uint value)
        {
            WriteRawByte((byte) value);
        }

        /// <summary>
        /// Writes out an array of bytes.
        /// </summary>
        public void WriteRawBytes(byte[] value)
        {
            WriteRawBytes(value, 0, value.Length);
        }

        /// <summary>
        /// Writes out part of an array of bytes.
        /// </summary>
        public void WriteRawBytes(byte[] value, int offset, int length)
        {
            if (limit - position >= length)
            {
1190
                ByteArray.Copy(value, offset, buffer, position, length);
1191 1192 1193 1194 1195 1196 1197 1198
                // We have room in the current buffer.
                position += length;
            }
            else
            {
                // Write extends past current buffer.  Fill the rest of this buffer and
                // flush.
                int bytesWritten = limit - position;
1199
                ByteArray.Copy(value, offset, buffer, position, bytesWritten);
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
                offset += bytesWritten;
                length -= bytesWritten;
                position = limit;
                RefreshBuffer();

                // Now deal with the rest.
                // Since we have an output stream, this is our buffer
                // and buffer offset == 0
                if (length <= limit)
                {
                    // Fits in new buffer.
1211
                    ByteArray.Copy(value, offset, buffer, 0, length);
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
                    position = length;
                }
                else
                {
                    // Write is very big.  Let's do it all at once.
                    output.Write(value, offset, length);
                }
            }
        }

        #endregion
1223

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
        /// <summary>
        /// Encode a 32-bit value with ZigZag encoding.
        /// </summary>
        /// <remarks>
        /// ZigZag encodes signed integers into values that can be efficiently
        /// encoded with varint.  (Otherwise, negative values must be 
        /// sign-extended to 64 bits to be varint encoded, thus always taking
        /// 10 bytes on the wire.)
        /// </remarks>
        public static uint EncodeZigZag32(int n)
        {
            // Note:  the right-shift must be arithmetic
            return (uint) ((n << 1) ^ (n >> 31));
        }

        /// <summary>
        /// Encode a 64-bit value with ZigZag encoding.
        /// </summary>
        /// <remarks>
        /// ZigZag encodes signed integers into values that can be efficiently
        /// encoded with varint.  (Otherwise, negative values must be 
        /// sign-extended to 64 bits to be varint encoded, thus always taking
        /// 10 bytes on the wire.)
        /// </remarks>
        public static ulong EncodeZigZag64(long n)
        {
            return (ulong) ((n << 1) ^ (n >> 63));
        }

        private void RefreshBuffer()
        {
            if (output == null)
            {
                // We're writing to a single buffer.
                throw new OutOfSpaceException();
            }

            // Since we have an output stream, this is our buffer
            // and buffer offset == 0
            output.Write(buffer, 0, position);
            position = 0;
        }

        /// <summary>
        /// Indicates that a CodedOutputStream wrapping a flat byte array
        /// ran out of space.
        /// </summary>
        public sealed class OutOfSpaceException : IOException
        {
            internal OutOfSpaceException()
                : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
            {
            }
        }

        public void Flush()
        {
            if (output != null)
            {
                RefreshBuffer();
            }
        }

        /// <summary>
        /// Verifies that SpaceLeft returns zero. It's common to create a byte array
        /// that is exactly big enough to hold a message, then write to it with
        /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
        /// the message was actually as big as expected, which can help bugs.
        /// </summary>
        public void CheckNoSpaceLeft()
        {
            if (SpaceLeft != 0)
            {
                throw new InvalidOperationException("Did not write as much data as expected.");
            }
        }

        /// <summary>
        /// If writing to a flat array, returns the space left in the array. Otherwise,
        /// throws an InvalidOperationException.
        /// </summary>
        public int SpaceLeft
        {
            get
            {
                if (output == null)
                {
                    return limit - position;
                }
                else
                {
                    throw new InvalidOperationException(
                        "SpaceLeft can only be called on CodedOutputStreams that are " +
                        "writing to a flat array.");
                }
            }
        }
    }
}