< Summary

Information
Class: Ice.OutputStream.EncapsEncoder11.InstanceData
Assembly: Ice
File(s): /_/csharp/src/Ice/OutputStream.cs
Tag: 125_37167941578
Line coverage
100%
Covered lines: 6
Uncovered lines: 0
Coverable lines: 6
Total lines: 2295
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%

File(s)

/_/csharp/src/Ice/OutputStream.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using System.Diagnostics;
 6
 7using Protocol = Ice.Internal.Protocol;
 8
 9namespace Ice;
 10
 11/// <summary>
 12/// Represents a byte buffer used for marshaling data using the Slice encoding.
 13/// </summary>
 14public sealed class OutputStream
 15{
 16    /// <summary>
 17    /// Initializes a new instance of the <see cref="OutputStream" /> class. The output stream is initially empty.
 18    /// </summary>
 19    /// <param name="encoding">The encoding version. null is equivalent to encoding 1.1.</param>
 20    /// <param name="format">The class format.</param>
 21    public OutputStream(EncodingVersion? encoding = null, FormatType format = FormatType.CompactFormat)
 22    {
 23        _buf = new Internal.Buffer();
 24        _encoding = encoding ?? Util.Encoding_1_1;
 25        _format = format;
 26    }
 27
 28    /// <summary>
 29    /// Initializes a new instance of the <see cref="OutputStream" /> class. The output stream is initially empty,
 30    /// and uses the communicator's default encoding version and default class format.
 31    /// </summary>
 32    /// <param name="communicator">The communicator that provides the encoding version and class format.</param>
 33    public OutputStream(Communicator communicator)
 34        : this(
 35            communicator.instance.defaultsAndOverrides().defaultEncoding,
 36            communicator.instance.defaultsAndOverrides().defaultFormat)
 37    {
 38    }
 39
 40    internal OutputStream(
 41        Internal.Buffer buf,
 42        EncodingVersion? encoding = null,
 43        FormatType format = FormatType.CompactFormat)
 44        : this(encoding, format) => _buf = buf;
 45
 46    /// <summary>
 47    /// Resets this output stream. This method allows the stream to be reused, to avoid creating
 48    /// unnecessary garbage.
 49    /// </summary>
 50    public void reset()
 51    {
 52        _buf.reset();
 53        clear();
 54    }
 55
 56    /// <summary>
 57    /// Releases any data retained by encapsulations. The reset() method internally calls clear().
 58    /// </summary>
 59    public void clear()
 60    {
 61        if (_encapsStack != null)
 62        {
 63            Debug.Assert(_encapsStack.next == null);
 64            _encapsStack.next = _encapsCache;
 65            _encapsCache = _encapsStack;
 66            _encapsStack = null;
 67            _encapsCache.reset();
 68        }
 69    }
 70
 71    /// <summary>
 72    /// Indicates that the marshaling of a request or reply is finished.
 73    /// </summary>
 74    /// <returns>The byte sequence containing the encoded request or reply.</returns>
 75    public byte[] finished()
 76    {
 77        Ice.Internal.Buffer buf = prepareWrite();
 78        byte[] result = new byte[buf.b.limit()];
 79        buf.b.get(result);
 80        return result;
 81    }
 82
 83    /// <summary>
 84    /// Swaps the contents of one stream with another.
 85    /// </summary>
 86    /// <param name="other">The other stream.</param>
 87    public void swap(OutputStream other)
 88    {
 89        Ice.Internal.Buffer tmpBuf = other._buf;
 90        other._buf = _buf;
 91        _buf = tmpBuf;
 92
 93        EncodingVersion tmpEncoding = other._encoding;
 94        other._encoding = _encoding;
 95        _encoding = tmpEncoding;
 96
 97        //
 98        // Swap is never called for streams that have encapsulations being written. However,
 99        // encapsulations might still be set in case marshaling failed. We just
 100        // reset the encapsulations if there are still some set.
 101        //
 102        resetEncapsulation();
 103        other.resetEncapsulation();
 104    }
 105
 106    private void resetEncapsulation() => _encapsStack = null;
 107
 108    /// <summary>
 109    /// Resizes the stream to a new size.
 110    /// </summary>
 111    /// <param name="sz">The new size.</param>
 112    public void resize(int sz)
 113    {
 114        _buf.resize(sz, false);
 115        _buf.b.position(sz);
 116    }
 117
 118    /// <summary>
 119    /// Marks the start of a class instance.
 120    /// </summary>
 121    /// <param name="data">Preserved slices for this instance, or null.</param>
 122    public void startValue(SlicedData? data)
 123    {
 124        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 125        _encapsStack.encoder.startInstance(SliceType.ValueSlice, data);
 126    }
 127
 128    /// <summary>
 129    /// Marks the end of a class instance.
 130    /// </summary>
 131    public void endValue()
 132    {
 133        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 134        _encapsStack.encoder.endInstance();
 135    }
 136
 137    /// <summary>
 138    /// Marks the start of a user exception.
 139    /// </summary>
 140    public void startException()
 141    {
 142        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 143        _encapsStack.encoder.startInstance(SliceType.ExceptionSlice, null);
 144    }
 145
 146    /// <summary>
 147    /// Marks the end of a user exception.
 148    /// </summary>
 149    public void endException()
 150    {
 151        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 152        _encapsStack.encoder.endInstance();
 153    }
 154
 155    /// <summary>
 156    /// Writes the start of an encapsulation to the stream.
 157    /// </summary>
 158    public void startEncapsulation()
 159    {
 160        //
 161        // If no encoding version is specified, use the current write
 162        // encapsulation encoding version if there's a current write
 163        // encapsulation, otherwise, use the stream encoding version.
 164        //
 165
 166        if (_encapsStack != null)
 167        {
 168            startEncapsulation(_encapsStack.encoding, _encapsStack.format);
 169        }
 170        else
 171        {
 172            startEncapsulation(_encoding, format: null);
 173        }
 174    }
 175
 176    /// <summary>
 177    /// Writes the start of an encapsulation to the stream.
 178    /// </summary>
 179    /// <param name="encoding">The encoding version of the encapsulation.</param>
 180    /// <param name="format">Specify the compact or sliced format. When null, use the OutputStream's class format.
 181    /// </param>
 182    public void startEncapsulation(EncodingVersion encoding, FormatType? format = null)
 183    {
 184        Protocol.checkSupportedEncoding(encoding);
 185
 186        Encaps? curr = _encapsCache;
 187        if (curr != null)
 188        {
 189            curr.reset();
 190            _encapsCache = _encapsCache!.next;
 191        }
 192        else
 193        {
 194            curr = new Encaps();
 195        }
 196        curr.next = _encapsStack;
 197        _encapsStack = curr;
 198
 199        _encapsStack.format = format ?? _format;
 200        _encapsStack.setEncoding(encoding);
 201        _encapsStack.start = _buf.b.position();
 202
 203        writeInt(0); // Placeholder for the encapsulation length.
 204        EncodingVersion.ice_write(this, _encapsStack.encoding);
 205    }
 206
 207    /// <summary>
 208    /// Ends the current encapsulation.
 209    /// </summary>
 210    public void endEncapsulation()
 211    {
 212        Debug.Assert(_encapsStack != null);
 213
 214        // Size includes size and version.
 215        int start = _encapsStack.start;
 216        int sz = _buf.size() - start;
 217        _buf.b.putInt(start, sz);
 218
 219        Encaps curr = _encapsStack;
 220        _encapsStack = curr.next;
 221        curr.next = _encapsCache;
 222        _encapsCache = curr;
 223        _encapsCache.reset();
 224    }
 225
 226    /// <summary>
 227    /// Writes an empty encapsulation using the given encoding version.
 228    /// </summary>
 229    /// <param name="encoding">The encoding version of the encapsulation.</param>
 230    public void writeEmptyEncapsulation(EncodingVersion encoding)
 231    {
 232        Protocol.checkSupportedEncoding(encoding);
 233        writeInt(6); // Size
 234        EncodingVersion.ice_write(this, encoding);
 235    }
 236
 237    /// <summary>
 238    /// Writes a pre-encoded encapsulation.
 239    /// </summary>
 240    /// <param name="v">The encapsulation data.</param>
 241    public void writeEncapsulation(byte[] v)
 242    {
 243        if (v.Length < 6)
 244        {
 245            throw new MarshalException($"A byte sequence with {v.Length} bytes is not a valid encapsulation.");
 246        }
 247        expand(v.Length);
 248        _buf.b.put(v);
 249    }
 250
 251    /// <summary>
 252    /// Determines the current encoding version.
 253    /// </summary>
 254    /// <returns>The encoding version.</returns>
 255    public EncodingVersion getEncoding() => _encapsStack != null ? _encapsStack.encoding : _encoding;
 256
 257    /// <summary>
 258    /// Marks the start of a new slice for a class instance or user exception.
 259    /// </summary>
 260    /// <param name="typeId">The Slice type ID corresponding to this slice.</param>
 261    /// <param name="compactId">The Slice compact type ID corresponding to this slice or -1 if no compact ID
 262    /// is defined for the type ID.</param>
 263    /// <param name="last"><see langword="true"/> if this is the last slice; otherwise, <see langword="false"/>.</param>
 264    public void startSlice(string typeId, int compactId, bool last)
 265    {
 266        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 267        _encapsStack.encoder.startSlice(typeId, compactId, last);
 268    }
 269
 270    /// <summary>
 271    /// Marks the end of a slice for a class instance or user exception.
 272    /// </summary>
 273    public void endSlice()
 274    {
 275        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 276        _encapsStack.encoder.endSlice();
 277    }
 278
 279    /// <summary>
 280    /// Writes the state of Slice classes whose index was previously written with writeValue() to the stream.
 281    /// </summary>
 282    public void writePendingValues()
 283    {
 284        if (_encapsStack != null && _encapsStack.encoder != null)
 285        {
 286            _encapsStack.encoder.writePendingValues();
 287        }
 288        else if (_encapsStack != null ?
 289                _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0))
 290        {
 291            //
 292            // If using the 1.0 encoding and no instances were written, we
 293            // still write an empty sequence for pending instances if
 294            // requested (i.e.: if this is called).
 295            //
 296            // This is required by the 1.0 encoding, even if no instances
 297            // are written we do marshal an empty sequence if marshaled
 298            // data types use classes.
 299            //
 300            writeSize(0);
 301        }
 302    }
 303
 304    /// <summary>
 305    /// Writes a size to the stream.
 306    /// </summary>
 307    /// <param name="v">The size to write.</param>
 308    public void writeSize(int v)
 309    {
 310        if (v > 254)
 311        {
 312            expand(5);
 313            _buf.b.put(255);
 314            _buf.b.putInt(v);
 315        }
 316        else
 317        {
 318            expand(1);
 319            _buf.b.put((byte)v);
 320        }
 321    }
 322
 323    /// <summary>
 324    /// Returns the current position and allocates four bytes for a fixed-length (32-bit) size value.
 325    /// </summary>
 326    /// <returns>The position at which to store the size value.</returns>
 327    public int startSize()
 328    {
 329        int pos = _buf.b.position();
 330        writeInt(0); // Placeholder for 32-bit size
 331        return pos;
 332    }
 333
 334    /// <summary>
 335    /// Computes the amount of data written since the previous call to startSize and writes that value
 336    /// at the saved position.
 337    /// </summary>
 338    /// <param name="pos">The saved position.</param>
 339    public void endSize(int pos)
 340    {
 341        Debug.Assert(pos >= 0);
 342        rewriteInt(_buf.b.position() - pos - 4, pos);
 343    }
 344
 345    /// <summary>
 346    /// Writes a blob of bytes to the stream.
 347    /// </summary>
 348    /// <param name="v">The byte array to be written. All of the bytes in the array are written.</param>
 349    public void writeBlob(byte[] v)
 350    {
 351        if (v == null)
 352        {
 353            return;
 354        }
 355        expand(v.Length);
 356        _buf.b.put(v);
 357    }
 358
 359    /// <summary>
 360    /// Writes a blob of bytes to the stream.
 361    /// </summary>
 362    /// <param name="v">The byte array to be written. All of the bytes in the array are written.</param>
 363    /// <param name="off">The offset into the byte array from which to copy.</param>
 364    /// <param name="len">The number of bytes from the byte array to copy.</param>
 365    public void writeBlob(byte[] v, int off, int len)
 366    {
 367        if (v == null)
 368        {
 369            return;
 370        }
 371        expand(len);
 372        _buf.b.put(v, off, len);
 373    }
 374
 375    /// <summary>
 376    /// Write the header information for an optional value.
 377    /// </summary>
 378    /// <param name="tag">The numeric tag associated with the value.</param>
 379    /// <param name="format">The optional format of the value.</param>
 380    /// <returns><see langword="true"/> if the encoding supports optional values and the header information was written
 381    /// to the stream; otherwise, <see langword="false"/>.</returns>
 382    public bool writeOptional(int tag, OptionalFormat format)
 383    {
 384        Debug.Assert(_encapsStack != null);
 385        if (_encapsStack.encoder != null)
 386        {
 387            return _encapsStack.encoder.writeOptional(tag, format);
 388        }
 389        else
 390        {
 391            return writeOptionalImpl(tag, format);
 392        }
 393    }
 394
 395    /// <summary>
 396    /// Writes a byte to the stream.
 397    /// </summary>
 398    /// <param name="v">The byte to write to the stream.</param>
 399    public void writeByte(byte v)
 400    {
 401        expand(1);
 402        _buf.b.put(v);
 403    }
 404
 405    /// <summary>
 406    /// Writes an optional byte to the stream.
 407    /// </summary>
 408    /// <param name="tag">The optional tag.</param>
 409    /// <param name="v">The optional byte to write to the stream.</param>
 410    public void writeByte(int tag, byte? v)
 411    {
 412        if (v.HasValue)
 413        {
 414            writeByte(tag, v.Value);
 415        }
 416    }
 417
 418    /// <summary>
 419    /// Writes an optional byte to the stream.
 420    /// </summary>
 421    /// <param name="tag">The optional tag.</param>
 422    /// <param name="v">The byte to write to the stream.</param>
 423    public void writeByte(int tag, byte v)
 424    {
 425        if (writeOptional(tag, OptionalFormat.F1))
 426        {
 427            writeByte(v);
 428        }
 429    }
 430
 431    /// <summary>
 432    /// Writes a byte to the stream at the given position. The current position of the stream is not modified.
 433    /// </summary>
 434    /// <param name="v">The byte to write to the stream.</param>
 435    /// <param name="dest">The position at which to store the byte in the buffer.</param>
 436    public void rewriteByte(byte v, int dest) => _buf.b.put(dest, v);
 437
 438    /// <summary>
 439    /// Writes a byte sequence to the stream.
 440    /// </summary>
 441    /// <param name="v">The byte sequence to write to the stream.
 442    /// Passing null causes an empty sequence to be written to the stream.</param>
 443    public void writeByteSeq(byte[] v)
 444    {
 445        if (v == null)
 446        {
 447            writeSize(0);
 448        }
 449        else
 450        {
 451            writeSize(v.Length);
 452            expand(v.Length);
 453            _buf.b.put(v);
 454        }
 455    }
 456
 457    /// <summary>
 458    /// Writes a byte sequence to the stream.
 459    /// </summary>
 460    /// <param name="count">The number of elements in the sequence.</param>
 461    /// <param name="v">An enumerator for the container holding the sequence.</param>
 462    public void writeByteSeq(int count, IEnumerable<byte> v)
 463    {
 464        if (count == 0)
 465        {
 466            writeSize(0);
 467            return;
 468        }
 469
 470        {
 471            if (v is List<byte> value)
 472            {
 473                writeByteSeq(value.ToArray());
 474                return;
 475            }
 476        }
 477
 478        {
 479            if (v is LinkedList<byte>)
 480            {
 481                writeSize(count);
 482                expand(count);
 483                IEnumerator<byte> i = v.GetEnumerator();
 484                while (i.MoveNext())
 485                {
 486                    _buf.b.put(i.Current);
 487                }
 488                return;
 489            }
 490        }
 491
 492        {
 493            if (v is Queue<byte> value)
 494            {
 495                writeByteSeq(value.ToArray());
 496                return;
 497            }
 498        }
 499
 500        {
 501            if (v is Stack<byte> value)
 502            {
 503                writeByteSeq(value.ToArray());
 504                return;
 505            }
 506        }
 507
 508        writeSize(count);
 509        expand(count);
 510        foreach (byte b in v)
 511        {
 512            _buf.b.put(b);
 513        }
 514    }
 515
 516    /// <summary>
 517    /// Writes an optional byte sequence to the stream.
 518    /// </summary>
 519    /// <param name="tag">The optional tag.</param>
 520    /// <param name="v">The byte sequence to write to the stream.</param>
 521    public void writeByteSeq(int tag, byte[]? v)
 522    {
 523        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 524        {
 525            writeByteSeq(v);
 526        }
 527    }
 528
 529    /// <summary>
 530    /// Writes an optional byte sequence to the stream.
 531    /// </summary>
 532    /// <param name="tag">The optional tag.</param>
 533    /// <param name="count">The number of elements in the sequence.</param>
 534    /// <param name="v">An enumerator for the byte sequence.</param>
 535    public void writeByteSeq(int tag, int count, IEnumerable<byte>? v)
 536    {
 537        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 538        {
 539            writeByteSeq(count, v);
 540        }
 541    }
 542
 543    /// <summary>
 544    /// Writes a boolean to the stream.
 545    /// </summary>
 546    /// <param name="v">The boolean to write to the stream.</param>
 547    public void writeBool(bool v)
 548    {
 549        expand(1);
 550        _buf.b.put(v ? (byte)1 : (byte)0);
 551    }
 552
 553    /// <summary>
 554    /// Writes an optional boolean to the stream.
 555    /// </summary>
 556    /// <param name="tag">The optional tag.</param>
 557    /// <param name="v">The optional boolean to write to the stream.</param>
 558    public void writeBool(int tag, bool? v)
 559    {
 560        if (v.HasValue)
 561        {
 562            writeBool(tag, v.Value);
 563        }
 564    }
 565
 566    /// <summary>
 567    /// Writes an optional boolean to the stream.
 568    /// </summary>
 569    /// <param name="tag">The optional tag.</param>
 570    /// <param name="v">The boolean to write to the stream.</param>
 571    public void writeBool(int tag, bool v)
 572    {
 573        if (writeOptional(tag, OptionalFormat.F1))
 574        {
 575            writeBool(v);
 576        }
 577    }
 578
 579    /// <summary>
 580    /// Writes a boolean to the stream at the given position. The current position of the stream is not modified.
 581    /// </summary>
 582    /// <param name="v">The boolean to write to the stream.</param>
 583    /// <param name="dest">The position at which to store the boolean in the buffer.</param>
 584    public void rewriteBool(bool v, int dest) => _buf.b.put(dest, v ? (byte)1 : (byte)0);
 585
 586    /// <summary>
 587    /// Writes a boolean sequence to the stream.
 588    /// </summary>
 589    /// <param name="v">The boolean sequence to write to the stream.
 590    /// Passing null causes an empty sequence to be written to the stream.</param>
 591    public void writeBoolSeq(bool[] v)
 592    {
 593        if (v == null)
 594        {
 595            writeSize(0);
 596        }
 597        else
 598        {
 599            writeSize(v.Length);
 600            expand(v.Length);
 601            _buf.b.putBoolSeq(v);
 602        }
 603    }
 604
 605    /// <summary>
 606    /// Writes a boolean sequence to the stream.
 607    /// </summary>
 608    /// <param name="count">The number of elements in the sequence.</param>
 609    /// <param name="v">An enumerator for the container holding the sequence.</param>
 610    public void writeBoolSeq(int count, IEnumerable<bool> v)
 611    {
 612        if (count == 0)
 613        {
 614            writeSize(0);
 615            return;
 616        }
 617
 618        {
 619            if (v is List<bool> value)
 620            {
 621                writeBoolSeq(value.ToArray());
 622                return;
 623            }
 624        }
 625
 626        {
 627            if (v is LinkedList<bool>)
 628            {
 629                writeSize(count);
 630                expand(count);
 631                IEnumerator<bool> i = v.GetEnumerator();
 632                while (i.MoveNext())
 633                {
 634                    _buf.b.putBool(i.Current);
 635                }
 636                return;
 637            }
 638        }
 639
 640        {
 641            if (v is Queue<bool> value)
 642            {
 643                writeBoolSeq(value.ToArray());
 644                return;
 645            }
 646        }
 647
 648        {
 649            if (v is Stack<bool> value)
 650            {
 651                writeBoolSeq(value.ToArray());
 652                return;
 653            }
 654        }
 655
 656        writeSize(count);
 657        expand(count);
 658        foreach (bool b in v)
 659        {
 660            _buf.b.putBool(b);
 661        }
 662    }
 663
 664    /// <summary>
 665    /// Writes an optional boolean sequence to the stream.
 666    /// </summary>
 667    /// <param name="tag">The optional tag.</param>
 668    /// <param name="v">The boolean sequence to write to the stream.</param>
 669    public void writeBoolSeq(int tag, bool[]? v)
 670    {
 671        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 672        {
 673            writeBoolSeq(v);
 674        }
 675    }
 676
 677    /// <summary>
 678    /// Writes an optional boolean sequence to the stream.
 679    /// </summary>
 680    /// <param name="tag">The optional tag.</param>
 681    /// <param name="count">The number of elements in the sequence.</param>
 682    /// <param name="v">An enumerator for the boolean sequence.</param>
 683    public void writeBoolSeq(int tag, int count, IEnumerable<bool>? v)
 684    {
 685        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 686        {
 687            writeBoolSeq(count, v);
 688        }
 689    }
 690
 691    /// <summary>
 692    /// Writes a short to the stream.
 693    /// </summary>
 694    /// <param name="v">The short to write to the stream.</param>
 695    public void writeShort(short v)
 696    {
 697        expand(2);
 698        _buf.b.putShort(v);
 699    }
 700
 701    /// <summary>
 702    /// Writes an optional short to the stream.
 703    /// </summary>
 704    /// <param name="tag">The optional tag.</param>
 705    /// <param name="v">The optional short to write to the stream.</param>
 706    public void writeShort(int tag, short? v)
 707    {
 708        if (v.HasValue)
 709        {
 710            writeShort(tag, v.Value);
 711        }
 712    }
 713
 714    /// <summary>
 715    /// Writes an optional short to the stream.
 716    /// </summary>
 717    /// <param name="tag">The optional tag.</param>
 718    /// <param name="v">The short to write to the stream.</param>
 719    public void writeShort(int tag, short v)
 720    {
 721        if (writeOptional(tag, OptionalFormat.F2))
 722        {
 723            writeShort(v);
 724        }
 725    }
 726
 727    /// <summary>
 728    /// Writes a short sequence to the stream.
 729    /// </summary>
 730    /// <param name="v">The short sequence to write to the stream.
 731    /// Passing null causes an empty sequence to be written to the stream.</param>
 732    public void writeShortSeq(short[] v)
 733    {
 734        if (v == null)
 735        {
 736            writeSize(0);
 737        }
 738        else
 739        {
 740            writeSize(v.Length);
 741            expand(v.Length * 2);
 742            _buf.b.putShortSeq(v);
 743        }
 744    }
 745
 746    /// <summary>
 747    /// Writes a short sequence to the stream.
 748    /// </summary>
 749    /// <param name="count">The number of elements in the sequence.</param>
 750    /// <param name="v">An enumerator for the container holding the sequence.</param>
 751    public void writeShortSeq(int count, IEnumerable<short> v)
 752    {
 753        if (count == 0)
 754        {
 755            writeSize(0);
 756            return;
 757        }
 758
 759        {
 760            if (v is List<short> value)
 761            {
 762                writeShortSeq(value.ToArray());
 763                return;
 764            }
 765        }
 766
 767        {
 768            if (v is LinkedList<short>)
 769            {
 770                writeSize(count);
 771                expand(count * 2);
 772                IEnumerator<short> i = v.GetEnumerator();
 773                while (i.MoveNext())
 774                {
 775                    _buf.b.putShort(i.Current);
 776                }
 777                return;
 778            }
 779        }
 780
 781        {
 782            if (v is Queue<short> value)
 783            {
 784                writeShortSeq(value.ToArray());
 785                return;
 786            }
 787        }
 788
 789        {
 790            if (v is Stack<short> value)
 791            {
 792                writeShortSeq(value.ToArray());
 793                return;
 794            }
 795        }
 796
 797        writeSize(count);
 798        expand(count * 2);
 799        foreach (short s in v)
 800        {
 801            _buf.b.putShort(s);
 802        }
 803    }
 804
 805    /// <summary>
 806    /// Writes an optional short sequence to the stream.
 807    /// </summary>
 808    /// <param name="tag">The optional tag.</param>
 809    /// <param name="v">The short sequence to write to the stream.</param>
 810    public void writeShortSeq(int tag, short[]? v)
 811    {
 812        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 813        {
 814            writeSize(v.Length == 0 ? 1 : (v.Length * 2) + (v.Length > 254 ? 5 : 1));
 815            writeShortSeq(v);
 816        }
 817    }
 818
 819    /// <summary>
 820    /// Writes an optional short sequence to the stream.
 821    /// </summary>
 822    /// <param name="tag">The optional tag.</param>
 823    /// <param name="count">The number of elements in the sequence.</param>
 824    /// <param name="v">An enumerator for the short sequence.</param>
 825    public void writeShortSeq(int tag, int count, IEnumerable<short>? v)
 826    {
 827        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 828        {
 829            writeSize(count == 0 ? 1 : (count * 2) + (count > 254 ? 5 : 1));
 830            writeShortSeq(count, v);
 831        }
 832    }
 833
 834    /// <summary>
 835    /// Writes an int to the stream.
 836    /// </summary>
 837    /// <param name="v">The int to write to the stream.</param>
 838    public void writeInt(int v)
 839    {
 840        expand(4);
 841        _buf.b.putInt(v);
 842    }
 843
 844    /// <summary>
 845    /// Writes an optional int to the stream.
 846    /// </summary>
 847    /// <param name="tag">The optional tag.</param>
 848    /// <param name="v">The optional int to write to the stream.</param>
 849    public void writeInt(int tag, int? v)
 850    {
 851        if (v.HasValue)
 852        {
 853            writeInt(tag, v.Value);
 854        }
 855    }
 856
 857    /// <summary>
 858    /// Writes an optional int to the stream.
 859    /// </summary>
 860    /// <param name="tag">The optional tag.</param>
 861    /// <param name="v">The int to write to the stream.</param>
 862    public void writeInt(int tag, int v)
 863    {
 864        if (writeOptional(tag, OptionalFormat.F4))
 865        {
 866            writeInt(v);
 867        }
 868    }
 869
 870    /// <summary>
 871    /// Writes an int to the stream at the given position. The current position of the stream is not modified.
 872    /// </summary>
 873    /// <param name="v">The int to write to the stream.</param>
 874    /// <param name="dest">The position at which to store the int in the buffer.</param>
 875    public void rewriteInt(int v, int dest) => _buf.b.putInt(dest, v);
 876
 877    /// <summary>
 878    /// Writes an int sequence to the stream.
 879    /// </summary>
 880    /// <param name="v">The int sequence to write to the stream.
 881    /// Passing null causes an empty sequence to be written to the stream.</param>
 882    public void writeIntSeq(int[] v)
 883    {
 884        if (v == null)
 885        {
 886            writeSize(0);
 887        }
 888        else
 889        {
 890            writeSize(v.Length);
 891            expand(v.Length * 4);
 892            _buf.b.putIntSeq(v);
 893        }
 894    }
 895
 896    /// <summary>
 897    /// Writes an int sequence to the stream.
 898    /// </summary>
 899    /// <param name="count">The number of elements in the sequence.</param>
 900    /// <param name="v">An enumerator for the container holding the sequence.</param>
 901    public void writeIntSeq(int count, IEnumerable<int> v)
 902    {
 903        if (count == 0)
 904        {
 905            writeSize(0);
 906            return;
 907        }
 908
 909        {
 910            if (v is List<int> value)
 911            {
 912                writeIntSeq(value.ToArray());
 913                return;
 914            }
 915        }
 916
 917        {
 918            if (v is LinkedList<int>)
 919            {
 920                writeSize(count);
 921                expand(count * 4);
 922                IEnumerator<int> i = v.GetEnumerator();
 923                while (i.MoveNext())
 924                {
 925                    _buf.b.putInt(i.Current);
 926                }
 927                return;
 928            }
 929        }
 930
 931        {
 932            if (v is Queue<int> value)
 933            {
 934                writeIntSeq(value.ToArray());
 935                return;
 936            }
 937        }
 938
 939        {
 940            if (v is Stack<int> value)
 941            {
 942                writeIntSeq(value.ToArray());
 943                return;
 944            }
 945        }
 946
 947        writeSize(count);
 948        expand(count * 4);
 949        foreach (int i in v)
 950        {
 951            _buf.b.putInt(i);
 952        }
 953    }
 954
 955    /// <summary>
 956    /// Writes an optional int sequence to the stream.
 957    /// </summary>
 958    /// <param name="tag">The optional tag.</param>
 959    /// <param name="v">The int sequence to write to the stream.</param>
 960    public void writeIntSeq(int tag, int[]? v)
 961    {
 962        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 963        {
 964            writeSize(v.Length == 0 ? 1 : (v.Length * 4) + (v.Length > 254 ? 5 : 1));
 965            writeIntSeq(v);
 966        }
 967    }
 968
 969    /// <summary>
 970    /// Writes an optional int sequence to the stream.
 971    /// </summary>
 972    /// <param name="tag">The optional tag.</param>
 973    /// <param name="count">The number of elements in the sequence.</param>
 974    /// <param name="v">An enumerator for the int sequence.</param>
 975    public void writeIntSeq(int tag, int count, IEnumerable<int>? v)
 976    {
 977        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 978        {
 979            writeSize(count == 0 ? 1 : (count * 4) + (count > 254 ? 5 : 1));
 980            writeIntSeq(count, v);
 981        }
 982    }
 983
 984    /// <summary>
 985    /// Writes a long to the stream.
 986    /// </summary>
 987    /// <param name="v">The long to write to the stream.</param>
 988    public void writeLong(long v)
 989    {
 990        expand(8);
 991        _buf.b.putLong(v);
 992    }
 993
 994    /// <summary>
 995    /// Writes an optional long to the stream.
 996    /// </summary>
 997    /// <param name="tag">The optional tag.</param>
 998    /// <param name="v">The optional long to write to the stream.</param>
 999    public void writeLong(int tag, long? v)
 1000    {
 1001        if (v.HasValue)
 1002        {
 1003            writeLong(tag, v.Value);
 1004        }
 1005    }
 1006
 1007    /// <summary>
 1008    /// Writes an optional long to the stream.
 1009    /// </summary>
 1010    /// <param name="tag">The optional tag.</param>
 1011    /// <param name="v">The long to write to the stream.</param>
 1012    public void writeLong(int tag, long v)
 1013    {
 1014        if (writeOptional(tag, OptionalFormat.F8))
 1015        {
 1016            writeLong(v);
 1017        }
 1018    }
 1019
 1020    /// <summary>
 1021    /// Writes a long sequence to the stream.
 1022    /// </summary>
 1023    /// <param name="v">The long sequence to write to the stream.
 1024    /// Passing null causes an empty sequence to be written to the stream.</param>
 1025    public void writeLongSeq(long[] v)
 1026    {
 1027        if (v == null)
 1028        {
 1029            writeSize(0);
 1030        }
 1031        else
 1032        {
 1033            writeSize(v.Length);
 1034            expand(v.Length * 8);
 1035            _buf.b.putLongSeq(v);
 1036        }
 1037    }
 1038
 1039    /// <summary>
 1040    /// Writes a long sequence to the stream.
 1041    /// </summary>
 1042    /// <param name="count">The number of elements in the sequence.</param>
 1043    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1044    public void writeLongSeq(int count, IEnumerable<long> v)
 1045    {
 1046        if (count == 0)
 1047        {
 1048            writeSize(0);
 1049            return;
 1050        }
 1051
 1052        {
 1053            if (v is List<long> value)
 1054            {
 1055                writeLongSeq(value.ToArray());
 1056                return;
 1057            }
 1058        }
 1059
 1060        {
 1061            if (v is LinkedList<long>)
 1062            {
 1063                writeSize(count);
 1064                expand(count * 8);
 1065                IEnumerator<long> i = v.GetEnumerator();
 1066                while (i.MoveNext())
 1067                {
 1068                    _buf.b.putLong(i.Current);
 1069                }
 1070                return;
 1071            }
 1072        }
 1073
 1074        {
 1075            if (v is Queue<long> value)
 1076            {
 1077                writeLongSeq(value.ToArray());
 1078                return;
 1079            }
 1080        }
 1081
 1082        {
 1083            if (v is Stack<long> value)
 1084            {
 1085                writeLongSeq(value.ToArray());
 1086                return;
 1087            }
 1088        }
 1089
 1090        writeSize(count);
 1091        expand(count * 8);
 1092        foreach (long l in v)
 1093        {
 1094            _buf.b.putLong(l);
 1095        }
 1096    }
 1097
 1098    /// <summary>
 1099    /// Writes an optional long sequence to the stream.
 1100    /// </summary>
 1101    /// <param name="tag">The optional tag.</param>
 1102    /// <param name="v">The long sequence to write to the stream.</param>
 1103    public void writeLongSeq(int tag, long[]? v)
 1104    {
 1105        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1106        {
 1107            writeSize(v.Length == 0 ? 1 : (v.Length * 8) + (v.Length > 254 ? 5 : 1));
 1108            writeLongSeq(v);
 1109        }
 1110    }
 1111
 1112    /// <summary>
 1113    /// Writes an optional long sequence to the stream.
 1114    /// </summary>
 1115    /// <param name="tag">The optional tag.</param>
 1116    /// <param name="count">The number of elements in the sequence.</param>
 1117    /// <param name="v">An enumerator for the long sequence.</param>
 1118    public void writeLongSeq(int tag, int count, IEnumerable<long>? v)
 1119    {
 1120        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1121        {
 1122            writeSize(count == 0 ? 1 : (count * 8) + (count > 254 ? 5 : 1));
 1123            writeLongSeq(count, v);
 1124        }
 1125    }
 1126
 1127    /// <summary>
 1128    /// Writes a float to the stream.
 1129    /// </summary>
 1130    /// <param name="v">The float to write to the stream.</param>
 1131    public void writeFloat(float v)
 1132    {
 1133        expand(4);
 1134        _buf.b.putFloat(v);
 1135    }
 1136
 1137    /// <summary>
 1138    /// Writes an optional float to the stream.
 1139    /// </summary>
 1140    /// <param name="tag">The optional tag.</param>
 1141    /// <param name="v">The optional float to write to the stream.</param>
 1142    public void writeFloat(int tag, float? v)
 1143    {
 1144        if (v.HasValue)
 1145        {
 1146            writeFloat(tag, v.Value);
 1147        }
 1148    }
 1149
 1150    /// <summary>
 1151    /// Writes an optional float to the stream.
 1152    /// </summary>
 1153    /// <param name="tag">The optional tag.</param>
 1154    /// <param name="v">The float to write to the stream.</param>
 1155    public void writeFloat(int tag, float v)
 1156    {
 1157        if (writeOptional(tag, OptionalFormat.F4))
 1158        {
 1159            writeFloat(v);
 1160        }
 1161    }
 1162
 1163    /// <summary>
 1164    /// Writes a float sequence to the stream.
 1165    /// </summary>
 1166    /// <param name="v">The float sequence to write to the stream.
 1167    /// Passing null causes an empty sequence to be written to the stream.</param>
 1168    public void writeFloatSeq(float[] v)
 1169    {
 1170        if (v == null)
 1171        {
 1172            writeSize(0);
 1173        }
 1174        else
 1175        {
 1176            writeSize(v.Length);
 1177            expand(v.Length * 4);
 1178            _buf.b.putFloatSeq(v);
 1179        }
 1180    }
 1181
 1182    /// <summary>
 1183    /// Writes a float sequence to the stream.
 1184    /// </summary>
 1185    /// <param name="count">The number of elements in the sequence.</param>
 1186    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1187    public void writeFloatSeq(int count, IEnumerable<float> v)
 1188    {
 1189        if (count == 0)
 1190        {
 1191            writeSize(0);
 1192            return;
 1193        }
 1194
 1195        {
 1196            if (v is List<float> value)
 1197            {
 1198                writeFloatSeq(value.ToArray());
 1199                return;
 1200            }
 1201        }
 1202
 1203        {
 1204            if (v is LinkedList<float>)
 1205            {
 1206                writeSize(count);
 1207                expand(count * 4);
 1208                IEnumerator<float> i = v.GetEnumerator();
 1209                while (i.MoveNext())
 1210                {
 1211                    _buf.b.putFloat(i.Current);
 1212                }
 1213                return;
 1214            }
 1215        }
 1216
 1217        {
 1218            if (v is Queue<float> value)
 1219            {
 1220                writeFloatSeq(value.ToArray());
 1221                return;
 1222            }
 1223        }
 1224
 1225        {
 1226            if (v is Stack<float> value)
 1227            {
 1228                writeFloatSeq(value.ToArray());
 1229                return;
 1230            }
 1231        }
 1232
 1233        writeSize(count);
 1234        expand(count * 4);
 1235        foreach (float f in v)
 1236        {
 1237            _buf.b.putFloat(f);
 1238        }
 1239    }
 1240
 1241    /// <summary>
 1242    /// Writes an optional float sequence to the stream.
 1243    /// </summary>
 1244    /// <param name="tag">The optional tag.</param>
 1245    /// <param name="v">The float sequence to write to the stream.</param>
 1246    public void writeFloatSeq(int tag, float[]? v)
 1247    {
 1248        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1249        {
 1250            writeSize(v.Length == 0 ? 1 : (v.Length * 4) + (v.Length > 254 ? 5 : 1));
 1251            writeFloatSeq(v);
 1252        }
 1253    }
 1254
 1255    /// <summary>
 1256    /// Writes an optional float sequence to the stream.
 1257    /// </summary>
 1258    /// <param name="tag">The optional tag.</param>
 1259    /// <param name="count">The number of elements in the sequence.</param>
 1260    /// <param name="v">An enumerator for the float sequence.</param>
 1261    public void writeFloatSeq(int tag, int count, IEnumerable<float>? v)
 1262    {
 1263        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1264        {
 1265            writeSize(count == 0 ? 1 : (count * 4) + (count > 254 ? 5 : 1));
 1266            writeFloatSeq(count, v);
 1267        }
 1268    }
 1269
 1270    /// <summary>
 1271    /// Writes a double to the stream.
 1272    /// </summary>
 1273    /// <param name="v">The double to write to the stream.</param>
 1274    public void writeDouble(double v)
 1275    {
 1276        expand(8);
 1277        _buf.b.putDouble(v);
 1278    }
 1279
 1280    /// <summary>
 1281    /// Writes an optional double to the stream.
 1282    /// </summary>
 1283    /// <param name="tag">The optional tag.</param>
 1284    /// <param name="v">The optional double to write to the stream.</param>
 1285    public void writeDouble(int tag, double? v)
 1286    {
 1287        if (v.HasValue)
 1288        {
 1289            writeDouble(tag, v.Value);
 1290        }
 1291    }
 1292
 1293    /// <summary>
 1294    /// Writes an optional double to the stream.
 1295    /// </summary>
 1296    /// <param name="tag">The optional tag.</param>
 1297    /// <param name="v">The double to write to the stream.</param>
 1298    public void writeDouble(int tag, double v)
 1299    {
 1300        if (writeOptional(tag, OptionalFormat.F8))
 1301        {
 1302            writeDouble(v);
 1303        }
 1304    }
 1305
 1306    /// <summary>
 1307    /// Writes a double sequence to the stream.
 1308    /// </summary>
 1309    /// <param name="v">The double sequence to write to the stream.
 1310    /// Passing null causes an empty sequence to be written to the stream.</param>
 1311    public void writeDoubleSeq(double[] v)
 1312    {
 1313        if (v == null)
 1314        {
 1315            writeSize(0);
 1316        }
 1317        else
 1318        {
 1319            writeSize(v.Length);
 1320            expand(v.Length * 8);
 1321            _buf.b.putDoubleSeq(v);
 1322        }
 1323    }
 1324
 1325    /// <summary>
 1326    /// Writes a double sequence to the stream.
 1327    /// </summary>
 1328    /// <param name="count">The number of elements in the sequence.</param>
 1329    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1330    public void writeDoubleSeq(int count, IEnumerable<double> v)
 1331    {
 1332        if (count == 0)
 1333        {
 1334            writeSize(0);
 1335            return;
 1336        }
 1337
 1338        {
 1339            if (v is List<double> value)
 1340            {
 1341                writeDoubleSeq(value.ToArray());
 1342                return;
 1343            }
 1344        }
 1345
 1346        {
 1347            if (v is LinkedList<double>)
 1348            {
 1349                writeSize(count);
 1350                expand(count * 8);
 1351                IEnumerator<double> i = v.GetEnumerator();
 1352                while (i.MoveNext())
 1353                {
 1354                    _buf.b.putDouble(i.Current);
 1355                }
 1356                return;
 1357            }
 1358        }
 1359
 1360        {
 1361            if (v is Queue<double> value)
 1362            {
 1363                writeDoubleSeq(value.ToArray());
 1364                return;
 1365            }
 1366        }
 1367
 1368        {
 1369            if (v is Stack<double> value)
 1370            {
 1371                writeDoubleSeq(value.ToArray());
 1372                return;
 1373            }
 1374        }
 1375
 1376        writeSize(count);
 1377        expand(count * 8);
 1378        foreach (double d in v)
 1379        {
 1380            _buf.b.putDouble(d);
 1381        }
 1382    }
 1383
 1384    /// <summary>
 1385    /// Writes an optional double sequence to the stream.
 1386    /// </summary>
 1387    /// <param name="tag">The optional tag.</param>
 1388    /// <param name="v">The double sequence to write to the stream.</param>
 1389    public void writeDoubleSeq(int tag, double[]? v)
 1390    {
 1391        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1392        {
 1393            writeSize(v.Length == 0 ? 1 : (v.Length * 8) + (v.Length > 254 ? 5 : 1));
 1394            writeDoubleSeq(v);
 1395        }
 1396    }
 1397
 1398    /// <summary>
 1399    /// Writes an optional double sequence to the stream.
 1400    /// </summary>
 1401    /// <param name="tag">The optional tag.</param>
 1402    /// <param name="count">The number of elements in the sequence.</param>
 1403    /// <param name="v">An enumerator for the double sequence.</param>
 1404    public void writeDoubleSeq(int tag, int count, IEnumerable<double>? v)
 1405    {
 1406        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1407        {
 1408            writeSize(count == 0 ? 1 : (count * 8) + (count > 254 ? 5 : 1));
 1409            writeDoubleSeq(count, v);
 1410        }
 1411    }
 1412
 1413    private static readonly System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true);
 1414
 1415    /// <summary>
 1416    /// Writes a string to the stream.
 1417    /// </summary>
 1418    /// <param name="v">The string to write to the stream. Passing null causes
 1419    /// an empty string to be written to the stream.</param>
 1420    public void writeString(string v)
 1421    {
 1422        if (v == null || v.Length == 0)
 1423        {
 1424            writeSize(0);
 1425            return;
 1426        }
 1427        byte[] arr = utf8.GetBytes(v);
 1428        writeSize(arr.Length);
 1429        expand(arr.Length);
 1430        _buf.b.put(arr);
 1431    }
 1432
 1433    /// <summary>
 1434    /// Writes an optional string to the stream.
 1435    /// </summary>
 1436    /// <param name="tag">The optional tag.</param>
 1437    /// <param name="v">The string to write to the stream.</param>
 1438    public void writeString(int tag, string? v)
 1439    {
 1440        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1441        {
 1442            writeString(v);
 1443        }
 1444    }
 1445
 1446    /// <summary>
 1447    /// Writes a string sequence to the stream.
 1448    /// </summary>
 1449    /// <param name="v">The string sequence to write to the stream.
 1450    /// Passing null causes an empty sequence to be written to the stream.</param>
 1451    public void writeStringSeq(string[] v)
 1452    {
 1453        if (v == null)
 1454        {
 1455            writeSize(0);
 1456        }
 1457        else
 1458        {
 1459            writeSize(v.Length);
 1460            for (int i = 0; i < v.Length; i++)
 1461            {
 1462                writeString(v[i]);
 1463            }
 1464        }
 1465    }
 1466
 1467    /// <summary>
 1468    /// Writes a string sequence to the stream.
 1469    /// </summary>
 1470    /// <param name="count">The number of elements in the sequence.</param>
 1471    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1472    public void writeStringSeq(int count, IEnumerable<string> v)
 1473    {
 1474        writeSize(count);
 1475        if (count != 0)
 1476        {
 1477            foreach (string s in v)
 1478            {
 1479                writeString(s);
 1480            }
 1481        }
 1482    }
 1483
 1484    /// <summary>
 1485    /// Writes an optional string sequence to the stream.
 1486    /// </summary>
 1487    /// <param name="tag">The optional tag.</param>
 1488    /// <param name="v">The string sequence to write to the stream.</param>
 1489    public void writeStringSeq(int tag, string[]? v)
 1490    {
 1491        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1492        {
 1493            int pos = startSize();
 1494            writeStringSeq(v);
 1495            endSize(pos);
 1496        }
 1497    }
 1498
 1499    /// <summary>
 1500    /// Writes an optional string sequence to the stream.
 1501    /// </summary>
 1502    /// <param name="tag">The optional tag.</param>
 1503    /// <param name="count">The number of elements in the sequence.</param>
 1504    /// <param name="v">An enumerator for the string sequence.</param>
 1505    public void writeStringSeq(int tag, int count, IEnumerable<string>? v)
 1506    {
 1507        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1508        {
 1509            int pos = startSize();
 1510            writeStringSeq(count, v);
 1511            endSize(pos);
 1512        }
 1513    }
 1514
 1515    /// <summary>
 1516    /// Writes a proxy to the stream.
 1517    /// </summary>
 1518    /// <param name="v">The proxy to write.</param>
 1519    public void writeProxy(ObjectPrx? v)
 1520    {
 1521        if (v is not null)
 1522        {
 1523            v.iceWrite(this);
 1524        }
 1525        else
 1526        {
 1527            Identity.ice_write(this, new Identity());
 1528        }
 1529    }
 1530
 1531    /// <summary>
 1532    /// Writes an optional proxy to the stream.
 1533    /// </summary>
 1534    /// <param name="tag">The optional tag.</param>
 1535    /// <param name="v">The proxy to write.</param>
 1536    public void writeProxy(int tag, ObjectPrx? v)
 1537    {
 1538        // We don't write anything for an optional proxy with a null value.
 1539        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1540        {
 1541            int pos = startSize();
 1542            writeProxy(v);
 1543            endSize(pos);
 1544        }
 1545    }
 1546
 1547    /// <summary>
 1548    /// Writes an enumerated value.
 1549    /// </summary>
 1550    /// <param name="v">The enumerator.</param>
 1551    /// <param name="maxValue">The maximum enumerator value in the definition.</param>
 1552    public void writeEnum(int v, int maxValue)
 1553    {
 1554        if (isEncoding_1_0())
 1555        {
 1556            if (maxValue < 127)
 1557            {
 1558                writeByte((byte)v);
 1559            }
 1560            else if (maxValue < 32767)
 1561            {
 1562                writeShort((short)v);
 1563            }
 1564            else
 1565            {
 1566                writeInt(v);
 1567            }
 1568        }
 1569        else
 1570        {
 1571            writeSize(v);
 1572        }
 1573    }
 1574
 1575    /// <summary>
 1576    /// Writes an optional enumerator to the stream.
 1577    /// </summary>
 1578    /// <param name="tag">The optional tag.</param>
 1579    /// <param name="v">The enumerator.</param>
 1580    /// <param name="maxValue">The maximum enumerator value in the definition.</param>
 1581    public void writeEnum(int tag, int v, int maxValue)
 1582    {
 1583        if (writeOptional(tag, OptionalFormat.Size))
 1584        {
 1585            writeEnum(v, maxValue);
 1586        }
 1587    }
 1588
 1589    /// <summary>
 1590    /// Writes a class instance to the stream.
 1591    /// </summary>
 1592    /// <param name="v">The value to write. With the 1.0 encoding, this method writes an index and the state of the
 1593    /// value is marshaled when <see cref="writePendingValues" /> is called; with the 1.1 encoding, the state is
 1594    /// marshaled eagerly, without waiting for <see cref="writePendingValues" />.</param>
 1595    public void writeValue(Value? v)
 1596    {
 1597        initEncaps();
 1598        _encapsStack!.encoder!.writeValue(v);
 1599    }
 1600
 1601    /// <summary>
 1602    /// Writes a user exception to the stream.
 1603    /// </summary>
 1604    /// <param name="v">The user exception to write.</param>
 1605    public void writeException(UserException v)
 1606    {
 1607        initEncaps();
 1608        // Exceptions are always encoded with the sliced format.
 1609        _encapsStack!.format = FormatType.SlicedFormat;
 1610        _encapsStack!.encoder!.writeException(v);
 1611    }
 1612
 1613    private bool writeOptionalImpl(int tag, OptionalFormat format)
 1614    {
 1615        if (isEncoding_1_0())
 1616        {
 1617            return false; // Optional members aren't supported with the 1.0 encoding.
 1618        }
 1619
 1620        int v = (int)format;
 1621        if (tag < 30)
 1622        {
 1623            v |= tag << 3;
 1624            writeByte((byte)v);
 1625        }
 1626        else
 1627        {
 1628            v |= 0x0F0; // tag = 30
 1629            writeByte((byte)v);
 1630            writeSize(tag);
 1631        }
 1632        return true;
 1633    }
 1634
 1635    /// <summary>
 1636    /// Determines the current position in the stream.
 1637    /// </summary>
 1638    /// <returns>The current position.</returns>
 1639    public int pos() => _buf.b.position();
 1640
 1641    /// <summary>
 1642    /// Sets the current position in the stream.
 1643    /// </summary>
 1644    /// <param name="n">The new position.</param>
 1645    public void pos(int n) => _buf.b.position(n);
 1646
 1647    /// <summary>
 1648    /// Determines the current size of the stream.
 1649    /// </summary>
 1650    /// <returns>The current size.</returns>
 1651    public int size() => _buf.size();
 1652
 1653    /// <summary>
 1654    /// Determines whether the stream is empty.
 1655    /// </summary>
 1656    /// <returns><see langword="true"/> if no data has been written yet; otherwise, <see langword="false"/>.</returns>
 1657    public bool isEmpty() => _buf.empty();
 1658
 1659    /// <summary>
 1660    /// Expand the stream to accept more data.
 1661    /// </summary>
 1662    /// <param name="n">The number of bytes to accommodate in the stream.</param>
 1663    public void expand(int n) => _buf.expand(n);
 1664
 1665    /// <summary>
 1666    /// Prepares the internal data buffer to be written to a socket.
 1667    /// </summary>
 1668    internal Ice.Internal.Buffer prepareWrite()
 1669    {
 1670        _buf.b.limit(_buf.size());
 1671        _buf.b.position(0);
 1672        return _buf;
 1673    }
 1674
 1675    /// <summary>
 1676    /// Retrieves the internal data buffer.
 1677    /// </summary>
 1678    /// <returns>The buffer.</returns>
 1679    internal Ice.Internal.Buffer getBuffer() => _buf;
 1680
 1681    private Ice.Internal.Buffer _buf;
 1682
 1683    private readonly FormatType _format;
 1684
 1685    private enum SliceType
 1686    {
 1687        NoSlice,
 1688        ValueSlice,
 1689        ExceptionSlice
 1690    }
 1691
 1692    private abstract class EncapsEncoder
 1693    {
 1694        protected EncapsEncoder(OutputStream stream, Encaps encaps)
 1695        {
 1696            _stream = stream;
 1697            _encaps = encaps;
 1698            _typeIdIndex = 0;
 1699            _marshaledMap = new Dictionary<Value, int>();
 1700        }
 1701
 1702        internal abstract void writeValue(Value? v);
 1703
 1704        internal abstract void writeException(UserException v);
 1705
 1706        internal abstract void startInstance(SliceType type, SlicedData? data);
 1707
 1708        internal abstract void endInstance();
 1709
 1710        internal abstract void startSlice(string typeId, int compactId, bool last);
 1711
 1712        internal abstract void endSlice();
 1713
 1714        internal virtual bool writeOptional(int tag, OptionalFormat format) => false;
 1715
 1716        internal virtual void writePendingValues()
 1717        {
 1718        }
 1719
 1720        protected int registerTypeId(string typeId)
 1721        {
 1722            _typeIdMap ??= new Dictionary<string, int>();
 1723
 1724            if (_typeIdMap.TryGetValue(typeId, out int p))
 1725            {
 1726                return p;
 1727            }
 1728            else
 1729            {
 1730                _typeIdMap.Add(typeId, ++_typeIdIndex);
 1731                return -1;
 1732            }
 1733        }
 1734
 1735        protected readonly OutputStream _stream;
 1736
 1737        protected readonly Encaps _encaps;
 1738
 1739        // Encapsulation attributes for instance marshaling.
 1740        protected readonly Dictionary<Value, int> _marshaledMap;
 1741
 1742        // Encapsulation attributes for instance marshaling.
 1743        private Dictionary<string, int>? _typeIdMap;
 1744
 1745        private int _typeIdIndex;
 1746    }
 1747
 1748    private sealed class EncapsEncoder10 : EncapsEncoder
 1749    {
 1750        internal EncapsEncoder10(OutputStream stream, Encaps encaps)
 1751            : base(stream, encaps)
 1752        {
 1753            _sliceType = SliceType.NoSlice;
 1754            _valueIdIndex = 0;
 1755            _toBeMarshaledMap = new Dictionary<Value, int>();
 1756        }
 1757
 1758        internal override void writeValue(Value? v)
 1759        {
 1760            //
 1761            // Object references are encoded as a negative integer in 1.0.
 1762            //
 1763            if (v != null)
 1764            {
 1765                _stream.writeInt(-registerValue(v));
 1766            }
 1767            else
 1768            {
 1769                _stream.writeInt(0);
 1770            }
 1771        }
 1772
 1773        internal override void writeException(UserException v)
 1774        {
 1775            //
 1776            // User exception with the 1.0 encoding start with a bool
 1777            // flag that indicates whether or not the exception uses
 1778            // classes.
 1779            //
 1780            // This allows reading the pending instances even if some part of
 1781            // the exception was sliced.
 1782            //
 1783            bool usesClasses = v.iceUsesClasses();
 1784            _stream.writeBool(usesClasses);
 1785            v.iceWrite(_stream);
 1786            if (usesClasses)
 1787            {
 1788                writePendingValues();
 1789            }
 1790        }
 1791
 1792        internal override void startInstance(SliceType sliceType, SlicedData? sliceData) => _sliceType = sliceType;
 1793
 1794        internal override void endInstance()
 1795        {
 1796            if (_sliceType == SliceType.ValueSlice)
 1797            {
 1798                //
 1799                // Write the Object slice.
 1800                //
 1801                startSlice(Value.ice_staticId(), -1, true);
 1802                _stream.writeSize(0); // For compatibility with the old AFM.
 1803                endSlice();
 1804            }
 1805            _sliceType = SliceType.NoSlice;
 1806        }
 1807
 1808        internal override void startSlice(string typeId, int compactId, bool last)
 1809        {
 1810            //
 1811            // For instance slices, encode a bool to indicate how the type ID
 1812            // is encoded and the type ID either as a string or index. For
 1813            // exception slices, always encode the type ID as a string.
 1814            //
 1815            if (_sliceType == SliceType.ValueSlice)
 1816            {
 1817                int index = registerTypeId(typeId);
 1818                if (index < 0)
 1819                {
 1820                    _stream.writeBool(false);
 1821                    _stream.writeString(typeId);
 1822                }
 1823                else
 1824                {
 1825                    _stream.writeBool(true);
 1826                    _stream.writeSize(index);
 1827                }
 1828            }
 1829            else
 1830            {
 1831                _stream.writeString(typeId);
 1832            }
 1833
 1834            _stream.writeInt(0); // Placeholder for the slice length.
 1835
 1836            _writeSlice = _stream.pos();
 1837        }
 1838
 1839        internal override void endSlice()
 1840        {
 1841            //
 1842            // Write the slice length.
 1843            //
 1844            int sz = _stream.pos() - _writeSlice + 4;
 1845            _stream.rewriteInt(sz, _writeSlice - 4);
 1846        }
 1847
 1848        internal override void writePendingValues()
 1849        {
 1850            while (_toBeMarshaledMap.Count > 0)
 1851            {
 1852                //
 1853                // Consider the to be marshaled instances as marshaled now,
 1854                // this is necessary to avoid adding again the "to be
 1855                // marshaled instances" into _toBeMarshaledMap while writing
 1856                // instances.
 1857                //
 1858                foreach (KeyValuePair<Value, int> e in _toBeMarshaledMap)
 1859                {
 1860                    _marshaledMap.Add(e.Key, e.Value);
 1861                }
 1862
 1863                Dictionary<Value, int> savedMap = _toBeMarshaledMap;
 1864                _toBeMarshaledMap = new Dictionary<Value, int>();
 1865                _stream.writeSize(savedMap.Count);
 1866                foreach (KeyValuePair<Value, int> p in savedMap)
 1867                {
 1868                    //
 1869                    // Ask the instance to marshal itself. Any new class
 1870                    // instances that are triggered by the classes marshaled
 1871                    // are added to toBeMarshaledMap.
 1872                    //
 1873                    _stream.writeInt(p.Value);
 1874
 1875                    p.Key.ice_preMarshal();
 1876                    p.Key.iceWrite(_stream);
 1877                }
 1878            }
 1879            _stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances.
 1880        }
 1881
 1882        private int registerValue(Value v)
 1883        {
 1884            Debug.Assert(v != null);
 1885
 1886            //
 1887            // Look for this instance in the to-be-marshaled map.
 1888            //
 1889            if (_toBeMarshaledMap.TryGetValue(v, out int p))
 1890            {
 1891                return p;
 1892            }
 1893
 1894            //
 1895            // Didn't find it, try the marshaled map next.
 1896            //
 1897            if (_marshaledMap.TryGetValue(v, out p))
 1898            {
 1899                return p;
 1900            }
 1901
 1902            //
 1903            // We haven't seen this instance previously, create a new
 1904            // index, and insert it into the to-be-marshaled map.
 1905            //
 1906            _toBeMarshaledMap.Add(v, ++_valueIdIndex);
 1907            return _valueIdIndex;
 1908        }
 1909
 1910        // Instance attributes
 1911        private SliceType _sliceType;
 1912
 1913        // Slice attributes
 1914        private int _writeSlice;        // Position of the slice data members
 1915
 1916        // Encapsulation attributes for instance marshaling.
 1917        private int _valueIdIndex;
 1918        private Dictionary<Value, int> _toBeMarshaledMap;
 1919    }
 1920
 1921    private sealed class EncapsEncoder11 : EncapsEncoder
 1922    {
 1923        internal EncapsEncoder11(OutputStream stream, Encaps encaps)
 1924            : base(stream, encaps)
 1925        {
 1926            _current = null;
 1927            _valueIdIndex = 1;
 1928        }
 1929
 1930        internal override void writeValue(Value? v)
 1931        {
 1932            if (v == null)
 1933            {
 1934                _stream.writeSize(0);
 1935            }
 1936            else if (_current != null && _encaps.format == FormatType.SlicedFormat)
 1937            {
 1938                if (_current.indirectionTable == null)
 1939                {
 1940                    _current.indirectionTable = new List<Value>();
 1941                    _current.indirectionMap = new Dictionary<Value, int>();
 1942                }
 1943
 1944                //
 1945                // If writing an instance within a slice and using the sliced
 1946                // format, write an index from the instance indirection table.
 1947                //
 1948                if (!_current.indirectionMap!.TryGetValue(v, out int index))
 1949                {
 1950                    _current.indirectionTable.Add(v);
 1951                    int idx = _current.indirectionTable.Count; // Position + 1 (0 is reserved for nil)
 1952                    _current.indirectionMap.Add(v, idx);
 1953                    _stream.writeSize(idx);
 1954                }
 1955                else
 1956                {
 1957                    _stream.writeSize(index);
 1958                }
 1959            }
 1960            else
 1961            {
 1962                writeInstance(v); // Write the instance or a reference if already marshaled.
 1963            }
 1964        }
 1965
 1966        internal override void writeException(UserException v) => v.iceWrite(_stream);
 1967
 1968        internal override void startInstance(SliceType sliceType, SlicedData? data)
 1969        {
 1970            if (_current == null)
 1971            {
 1972                _current = new InstanceData(null);
 1973            }
 1974            else
 1975            {
 1976                _current = _current.next ?? new InstanceData(_current);
 1977            }
 1978            _current.sliceType = sliceType;
 1979            _current.firstSlice = true;
 1980
 1981            if (data != null)
 1982            {
 1983                writeSlicedData(data);
 1984            }
 1985        }
 1986
 1987        internal override void endInstance() => _current = _current!.previous;
 1988
 1989        internal override void startSlice(string typeId, int compactId, bool last)
 1990        {
 1991            Debug.Assert((_current!.indirectionTable == null || _current.indirectionTable.Count == 0) &&
 1992                         (_current.indirectionMap == null || _current.indirectionMap.Count == 0));
 1993
 1994            _current.sliceFlagsPos = _stream.pos();
 1995
 1996            _current.sliceFlags = 0;
 1997            if (_encaps.format == FormatType.SlicedFormat)
 1998            {
 1999                //
 2000                // Encode the slice size if using the sliced format.
 2001                //
 2002                _current.sliceFlags |= Protocol.FLAG_HAS_SLICE_SIZE;
 2003            }
 2004            if (last)
 2005            {
 2006                _current.sliceFlags |= Protocol.FLAG_IS_LAST_SLICE; // This is the last slice.
 2007            }
 2008
 2009            _stream.writeByte(0); // Placeholder for the slice flags
 2010
 2011            //
 2012            // For instance slices, encode the flag and the type ID either as a
 2013            // string or index. For exception slices, always encode the type
 2014            // ID a string.
 2015            //
 2016            if (_current.sliceType == SliceType.ValueSlice)
 2017            {
 2018                //
 2019                // Encode the type ID (only in the first slice for the compact
 2020                // encoding).
 2021                //
 2022                if (_encaps.format == FormatType.SlicedFormat || _current.firstSlice)
 2023                {
 2024                    if (compactId != -1)
 2025                    {
 2026                        _current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_COMPACT;
 2027                        _stream.writeSize(compactId);
 2028                    }
 2029                    else
 2030                    {
 2031                        int index = registerTypeId(typeId);
 2032                        if (index < 0)
 2033                        {
 2034                            _current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_STRING;
 2035                            _stream.writeString(typeId);
 2036                        }
 2037                        else
 2038                        {
 2039                            _current.sliceFlags |= Protocol.FLAG_HAS_TYPE_ID_INDEX;
 2040                            _stream.writeSize(index);
 2041                        }
 2042                    }
 2043                }
 2044            }
 2045            else
 2046            {
 2047                _stream.writeString(typeId);
 2048            }
 2049
 2050            if ((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2051            {
 2052                _stream.writeInt(0); // Placeholder for the slice length.
 2053            }
 2054
 2055            _current.writeSlice = _stream.pos();
 2056            _current.firstSlice = false;
 2057        }
 2058
 2059        internal override void endSlice()
 2060        {
 2061            //
 2062            // Write the optional member end marker if some optional members
 2063            // were encoded. Note that the optional members are encoded before
 2064            // the indirection table and are included in the slice size.
 2065            //
 2066            if ((_current!.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
 2067            {
 2068                _stream.writeByte(Protocol.OPTIONAL_END_MARKER);
 2069            }
 2070
 2071            //
 2072            // Write the slice length if necessary.
 2073            //
 2074            if ((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2075            {
 2076                int sz = _stream.pos() - _current.writeSlice + 4;
 2077                _stream.rewriteInt(sz, _current.writeSlice - 4);
 2078            }
 2079
 2080            //
 2081            // Only write the indirection table if it contains entries.
 2082            //
 2083            if (_current.indirectionTable != null && _current.indirectionTable.Count > 0)
 2084            {
 2085                Debug.Assert(_encaps.format == FormatType.SlicedFormat);
 2086                _current.sliceFlags |= Protocol.FLAG_HAS_INDIRECTION_TABLE;
 2087
 2088                //
 2089                // Write the indirect instance table.
 2090                //
 2091                _stream.writeSize(_current.indirectionTable.Count);
 2092                foreach (Value v in _current.indirectionTable)
 2093                {
 2094                    writeInstance(v);
 2095                }
 2096                _current.indirectionTable.Clear();
 2097                _current.indirectionMap!.Clear();
 2098            }
 2099
 2100            //
 2101            // Finally, update the slice flags.
 2102            //
 2103            _stream.rewriteByte(_current.sliceFlags, _current.sliceFlagsPos);
 2104        }
 2105
 2106        internal override bool writeOptional(int tag, OptionalFormat format)
 2107        {
 2108            if (_current == null)
 2109            {
 2110                return _stream.writeOptionalImpl(tag, format);
 2111            }
 2112            else
 2113            {
 2114                if (_stream.writeOptionalImpl(tag, format))
 2115                {
 2116                    _current.sliceFlags |= Protocol.FLAG_HAS_OPTIONAL_MEMBERS;
 2117                    return true;
 2118                }
 2119                else
 2120                {
 2121                    return false;
 2122                }
 2123            }
 2124        }
 2125
 2126        private void writeSlicedData(SlicedData slicedData)
 2127        {
 2128            Debug.Assert(slicedData != null);
 2129
 2130            //
 2131            // We only remarshal preserved slices if we are using the sliced
 2132            // format. Otherwise, we ignore the preserved slices, which
 2133            // essentially "slices" the instance into the most-derived type
 2134            // known by the sender.
 2135            //
 2136            if (_encaps.format != FormatType.SlicedFormat)
 2137            {
 2138                return;
 2139            }
 2140
 2141            foreach (SliceInfo info in slicedData.slices)
 2142            {
 2143                startSlice(info.typeId, info.compactId, info.isLastSlice);
 2144
 2145                //
 2146                // Write the bytes associated with this slice.
 2147                //
 2148                _stream.writeBlob(info.bytes);
 2149
 2150                if (info.hasOptionalMembers)
 2151                {
 2152                    _current!.sliceFlags |= Protocol.FLAG_HAS_OPTIONAL_MEMBERS;
 2153                }
 2154
 2155                //
 2156                // Make sure to also re-write the instance indirection table.
 2157                //
 2158                if (info.instances != null && info.instances.Length > 0)
 2159                {
 2160                    if (_current!.indirectionTable == null)
 2161                    {
 2162                        _current.indirectionTable = new List<Value>();
 2163                        _current.indirectionMap = new Dictionary<Value, int>();
 2164                    }
 2165                    foreach (Value o in info.instances)
 2166                    {
 2167                        _current.indirectionTable.Add(o);
 2168                    }
 2169                }
 2170
 2171                endSlice();
 2172            }
 2173        }
 2174
 2175        private void writeInstance(Value v)
 2176        {
 2177            Debug.Assert(v != null);
 2178
 2179            //
 2180            // If the instance was already marshaled, just write its ID.
 2181            //
 2182            if (_marshaledMap.TryGetValue(v, out int p))
 2183            {
 2184                _stream.writeSize(p);
 2185                return;
 2186            }
 2187
 2188            //
 2189            // We haven't seen this instance previously, create a new ID,
 2190            // insert it into the marshaled map, and write the instance.
 2191            //
 2192            _marshaledMap.Add(v, ++_valueIdIndex);
 2193
 2194            v.ice_preMarshal();
 2195            _stream.writeSize(1); // Object instance marker.
 2196            v.iceWrite(_stream);
 2197        }
 2198
 2199        private sealed class InstanceData
 2200        {
 12201            internal InstanceData(InstanceData? previous)
 2202            {
 12203                if (previous != null)
 2204                {
 12205                    previous.next = this;
 2206                }
 12207                this.previous = previous;
 12208                next = null;
 12209            }
 2210
 2211            // Instance attributes
 2212            internal SliceType sliceType;
 2213            internal bool firstSlice;
 2214
 2215            // Slice attributes
 2216            internal byte sliceFlags;
 2217            internal int writeSlice;    // Position of the slice data members
 2218            internal int sliceFlagsPos; // Position of the slice flags
 2219            internal List<Value>? indirectionTable;
 2220            internal Dictionary<Value, int>? indirectionMap;
 2221
 2222            internal InstanceData? previous;
 2223            internal InstanceData? next;
 2224        }
 2225
 2226        private InstanceData? _current;
 2227
 2228        private int _valueIdIndex; // The ID of the next instance to marshal
 2229    }
 2230
 2231    private sealed class Encaps
 2232    {
 2233        internal void reset() => encoder = null;
 2234
 2235        internal void setEncoding(EncodingVersion encoding)
 2236        {
 2237            this.encoding = encoding;
 2238            encoding_1_0 = encoding.Equals(Util.Encoding_1_0);
 2239        }
 2240
 2241        internal int start;
 2242        internal EncodingVersion encoding;
 2243        internal bool encoding_1_0;
 2244        internal FormatType format;
 2245
 2246        internal EncapsEncoder? encoder;
 2247
 2248        internal Encaps? next;
 2249    }
 2250
 2251    //
 2252    // The encoding version to use when there's no encapsulation to
 2253    // read from or write to. This is for example used to read message
 2254    // headers or when the user is using the streaming API with no
 2255    // encapsulation.
 2256    //
 2257    private EncodingVersion _encoding;
 2258
 2259    private bool isEncoding_1_0() =>
 2260        _encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0);
 2261
 2262    private Encaps? _encapsStack;
 2263    private Encaps? _encapsCache;
 2264
 2265    private void initEncaps()
 2266    {
 2267        if (_encapsStack == null) // Lazy initialization
 2268        {
 2269            _encapsStack = _encapsCache;
 2270            if (_encapsStack != null)
 2271            {
 2272                _encapsCache = _encapsCache!.next;
 2273                // Detach the reused node from the cache: as the outermost encapsulation it has no next.
 2274                _encapsStack.next = null;
 2275            }
 2276            else
 2277            {
 2278                _encapsStack = new Encaps();
 2279            }
 2280            _encapsStack.setEncoding(_encoding);
 2281        }
 2282
 2283        if (_encapsStack.encoder == null) // Lazy initialization.
 2284        {
 2285            if (_encapsStack.encoding_1_0)
 2286            {
 2287                _encapsStack.encoder = new EncapsEncoder10(this, _encapsStack);
 2288            }
 2289            else
 2290            {
 2291                _encapsStack.encoder = new EncapsEncoder11(this, _encapsStack);
 2292            }
 2293        }
 2294    }
 2295}