< Summary

Information
Class: Ice.OutputStream.EncapsEncoder10
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/OutputStream.cs
Tag: 71_18251537082
Line coverage
100%
Covered lines: 54
Uncovered lines: 0
Coverable lines: 54
Total lines: 2293
Line coverage: 100%
Branch coverage
100%
Covered branches: 20
Total branches: 20
Branch coverage: 100%
Method coverage
100%
Covered methods: 9
Total methods: 9
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
writeValue(...)100%22100%
writeException(...)100%22100%
startInstance(...)100%11100%
endInstance()100%22100%
startSlice(...)100%44100%
endSlice()100%11100%
writePendingValues()100%66100%
registerValue(...)100%44100%

File(s)

/home/runner/work/ice/ice/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/// Interface for output streams used to write Slice types to a sequence
 13/// of bytes.
 14/// </summary>
 15public sealed class OutputStream
 16{
 17    /// <summary>
 18    /// Initializes a new instance of the <see cref="OutputStream" /> class. The output stream is initially empty.
 19    /// </summary>
 20    /// <param name="encoding">The encoding version. null is equivalent to encoding 1.1.</param>
 21    /// <param name="format">The class format.</param>
 22    public OutputStream(EncodingVersion? encoding = null, FormatType format = FormatType.CompactFormat)
 23    {
 24        _buf = new Internal.Buffer();
 25        _encoding = encoding ?? Util.currentEncoding;
 26        _format = format;
 27    }
 28
 29    /// <summary>
 30    /// Initializes a new instance of the <see cref="OutputStream" /> class. The output stream is initially empty,
 31    /// and uses the communicator's default encoding version and default class format.
 32    /// </summary>
 33    /// <param name="communicator">The communicator that provides the encoding version and class format.</param>
 34    public OutputStream(Communicator communicator)
 35        : this(
 36            communicator.instance.defaultsAndOverrides().defaultEncoding,
 37            communicator.instance.defaultsAndOverrides().defaultFormat)
 38    {
 39    }
 40
 41    internal OutputStream(
 42        Internal.Buffer buf,
 43        EncodingVersion? encoding = null,
 44        FormatType format = FormatType.CompactFormat)
 45        : this(encoding, format) => _buf = buf;
 46
 47    /// <summary>
 48    /// Resets this output stream. This method allows the stream to be reused, to avoid creating
 49    /// unnecessary garbage.
 50    /// </summary>
 51    public void reset()
 52    {
 53        _buf.reset();
 54        clear();
 55    }
 56
 57    /// <summary>
 58    /// Releases any data retained by encapsulations. The reset() method internally calls clear().
 59    /// </summary>
 60    public void clear()
 61    {
 62        if (_encapsStack != null)
 63        {
 64            Debug.Assert(_encapsStack.next == null);
 65            _encapsStack.next = _encapsCache;
 66            _encapsCache = _encapsStack;
 67            _encapsStack = null;
 68            _encapsCache.reset();
 69        }
 70    }
 71
 72    /// <summary>
 73    /// Indicates that the marshaling of a request or reply is finished.
 74    /// </summary>
 75    /// <returns>The byte sequence containing the encoded request or reply.</returns>
 76    public byte[] finished()
 77    {
 78        Ice.Internal.Buffer buf = prepareWrite();
 79        byte[] result = new byte[buf.b.limit()];
 80        buf.b.get(result);
 81        return result;
 82    }
 83
 84    /// <summary>
 85    /// Swaps the contents of one stream with another.
 86    /// </summary>
 87    /// <param name="other">The other stream.</param>
 88    public void swap(OutputStream other)
 89    {
 90        Ice.Internal.Buffer tmpBuf = other._buf;
 91        other._buf = _buf;
 92        _buf = tmpBuf;
 93
 94        EncodingVersion tmpEncoding = other._encoding;
 95        other._encoding = _encoding;
 96        _encoding = tmpEncoding;
 97
 98        //
 99        // Swap is never called for streams that have encapsulations being written. However,
 100        // encapsulations might still be set in case marshaling failed. We just
 101        // reset the encapsulations if there are still some set.
 102        //
 103        resetEncapsulation();
 104        other.resetEncapsulation();
 105    }
 106
 107    private void resetEncapsulation() => _encapsStack = null;
 108
 109    /// <summary>
 110    /// Resizes the stream to a new size.
 111    /// </summary>
 112    /// <param name="sz">The new size.</param>
 113    public void resize(int sz)
 114    {
 115        _buf.resize(sz, false);
 116        _buf.b.position(sz);
 117    }
 118
 119    /// <summary>
 120    /// Marks the start of a class instance.
 121    /// </summary>
 122    /// <param name="data">Preserved slices for this instance, or null.</param>
 123    public void startValue(SlicedData? data)
 124    {
 125        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 126        _encapsStack.encoder.startInstance(SliceType.ValueSlice, data);
 127    }
 128
 129    /// <summary>
 130    /// Marks the end of a class instance.
 131    /// </summary>
 132    public void endValue()
 133    {
 134        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 135        _encapsStack.encoder.endInstance();
 136    }
 137
 138    /// <summary>
 139    /// Marks the start of a user exception.
 140    /// </summary>
 141    public void startException()
 142    {
 143        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 144        _encapsStack.encoder.startInstance(SliceType.ExceptionSlice, null);
 145    }
 146
 147    /// <summary>
 148    /// Marks the end of a user exception.
 149    /// </summary>
 150    public void endException()
 151    {
 152        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 153        _encapsStack.encoder.endInstance();
 154    }
 155
 156    /// <summary>
 157    /// Writes the start of an encapsulation to the stream.
 158    /// </summary>
 159    public void startEncapsulation()
 160    {
 161        //
 162        // If no encoding version is specified, use the current write
 163        // encapsulation encoding version if there's a current write
 164        // encapsulation, otherwise, use the stream encoding version.
 165        //
 166
 167        if (_encapsStack != null)
 168        {
 169            startEncapsulation(_encapsStack.encoding, _encapsStack.format);
 170        }
 171        else
 172        {
 173            startEncapsulation(_encoding, format: null);
 174        }
 175    }
 176
 177    /// <summary>
 178    /// Writes the start of an encapsulation to the stream.
 179    /// </summary>
 180    /// <param name="encoding">The encoding version of the encapsulation.</param>
 181    /// <param name="format">Specify the compact or sliced format. When null, use the OutputStream's class format.
 182    /// </param>
 183    public void startEncapsulation(EncodingVersion encoding, FormatType? format = null)
 184    {
 185        Protocol.checkSupportedEncoding(encoding);
 186
 187        Encaps? curr = _encapsCache;
 188        if (curr != null)
 189        {
 190            curr.reset();
 191            _encapsCache = _encapsCache!.next;
 192        }
 193        else
 194        {
 195            curr = new Encaps();
 196        }
 197        curr.next = _encapsStack;
 198        _encapsStack = curr;
 199
 200        _encapsStack.format = format ?? _format;
 201        _encapsStack.setEncoding(encoding);
 202        _encapsStack.start = _buf.b.position();
 203
 204        writeInt(0); // Placeholder for the encapsulation length.
 205        EncodingVersion.ice_write(this, _encapsStack.encoding);
 206    }
 207
 208    /// <summary>
 209    /// Ends the previous encapsulation.
 210    /// </summary>
 211    public void endEncapsulation()
 212    {
 213        Debug.Assert(_encapsStack != null);
 214
 215        // Size includes size and version.
 216        int start = _encapsStack.start;
 217        int sz = _buf.size() - start;
 218        _buf.b.putInt(start, sz);
 219
 220        Encaps curr = _encapsStack;
 221        _encapsStack = curr.next;
 222        curr.next = _encapsCache;
 223        _encapsCache = curr;
 224        _encapsCache.reset();
 225    }
 226
 227    /// <summary>
 228    /// Writes an empty encapsulation using the given encoding version.
 229    /// </summary>
 230    /// <param name="encoding">The encoding version of the encapsulation.</param>
 231    public void writeEmptyEncapsulation(EncodingVersion encoding)
 232    {
 233        Protocol.checkSupportedEncoding(encoding);
 234        writeInt(6); // Size
 235        EncodingVersion.ice_write(this, encoding);
 236    }
 237
 238    /// <summary>
 239    /// Writes a pre-encoded encapsulation.
 240    /// </summary>
 241    /// <param name="v">The encapsulation data.</param>
 242    public void writeEncapsulation(byte[] v)
 243    {
 244        if (v.Length < 6)
 245        {
 246            throw new MarshalException($"A byte sequence with {v.Length} bytes is not a valid encapsulation.");
 247        }
 248        expand(v.Length);
 249        _buf.b.put(v);
 250    }
 251
 252    /// <summary>
 253    /// Determines the current encoding version.
 254    /// </summary>
 255    /// <returns>The encoding version.</returns>
 256    public EncodingVersion getEncoding() => _encapsStack != null ? _encapsStack.encoding : _encoding;
 257
 258    /// <summary>
 259    /// Marks the start of a new slice for a class instance or user exception.
 260    /// </summary>
 261    /// <param name="typeId">The Slice type ID corresponding to this slice.</param>
 262    /// <param name="compactId">The Slice compact type ID corresponding to this slice or -1 if no compact ID
 263    /// is defined for the type ID.</param>
 264    /// <param name="last">True if this is the last slice, false otherwise.</param>
 265    public void startSlice(string typeId, int compactId, bool last)
 266    {
 267        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 268        _encapsStack.encoder.startSlice(typeId, compactId, last);
 269    }
 270
 271    /// <summary>
 272    /// Marks the end of a slice for a class instance or user exception.
 273    /// </summary>
 274    public void endSlice()
 275    {
 276        Debug.Assert(_encapsStack != null && _encapsStack.encoder != null);
 277        _encapsStack.encoder.endSlice();
 278    }
 279
 280    /// <summary>
 281    /// Writes the state of Slice classes whose index was previously written with writeValue() to the stream.
 282    /// </summary>
 283    public void writePendingValues()
 284    {
 285        if (_encapsStack != null && _encapsStack.encoder != null)
 286        {
 287            _encapsStack.encoder.writePendingValues();
 288        }
 289        else if (_encapsStack != null ?
 290                _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0))
 291        {
 292            //
 293            // If using the 1.0 encoding and no instances were written, we
 294            // still write an empty sequence for pending instances if
 295            // requested (i.e.: if this is called).
 296            //
 297            // This is required by the 1.0 encoding, even if no instances
 298            // are written we do marshal an empty sequence if marshaled
 299            // data types use classes.
 300            //
 301            writeSize(0);
 302        }
 303    }
 304
 305    /// <summary>
 306    /// Writes a size to the stream.
 307    /// </summary>
 308    /// <param name="v">The size to write.</param>
 309    public void writeSize(int v)
 310    {
 311        if (v > 254)
 312        {
 313            expand(5);
 314            _buf.b.put(255);
 315            _buf.b.putInt(v);
 316        }
 317        else
 318        {
 319            expand(1);
 320            _buf.b.put((byte)v);
 321        }
 322    }
 323
 324    /// <summary>
 325    /// Returns the current position and allocates four bytes for a fixed-length (32-bit) size value.
 326    /// </summary>
 327    /// <returns>The position at which to store the size value.</returns>
 328    public int startSize()
 329    {
 330        int pos = _buf.b.position();
 331        writeInt(0); // Placeholder for 32-bit size
 332        return pos;
 333    }
 334
 335    /// <summary>
 336    /// Computes the amount of data written since the previous call to startSize and writes that value
 337    /// at the saved position.
 338    /// </summary>
 339    /// <param name="pos">The saved position.</param>
 340    public void endSize(int pos)
 341    {
 342        Debug.Assert(pos >= 0);
 343        rewriteInt(_buf.b.position() - pos - 4, pos);
 344    }
 345
 346    /// <summary>
 347    /// Writes a blob of bytes to the stream.
 348    /// </summary>
 349    /// <param name="v">The byte array to be written. All of the bytes in the array are written.</param>
 350    public void writeBlob(byte[] v)
 351    {
 352        if (v == null)
 353        {
 354            return;
 355        }
 356        expand(v.Length);
 357        _buf.b.put(v);
 358    }
 359
 360    /// <summary>
 361    /// Writes a blob of bytes to the stream.
 362    /// </summary>
 363    /// <param name="v">The byte array to be written. All of the bytes in the array are written.</param>
 364    /// <param name="off">The offset into the byte array from which to copy.</param>
 365    /// <param name="len">The number of bytes from the byte array to copy.</param>
 366    public void writeBlob(byte[] v, int off, int len)
 367    {
 368        if (v == null)
 369        {
 370            return;
 371        }
 372        expand(len);
 373        _buf.b.put(v, off, len);
 374    }
 375
 376    /// <summary>
 377    /// Write the header information for an optional value.
 378    /// </summary>
 379    /// <param name="tag">The numeric tag associated with the value.</param>
 380    /// <param name="format">The optional format of the value.</param>
 381    /// <returns><c>true</c> if the encoding supports optional values and the header information was written to the stre
 382    /// <c>false</c> otherwise.</returns>
 383    public bool writeOptional(int tag, OptionalFormat format)
 384    {
 385        Debug.Assert(_encapsStack != null);
 386        if (_encapsStack.encoder != null)
 387        {
 388            return _encapsStack.encoder.writeOptional(tag, format);
 389        }
 390        else
 391        {
 392            return writeOptionalImpl(tag, format);
 393        }
 394    }
 395
 396    /// <summary>
 397    /// Writes a byte to the stream.
 398    /// </summary>
 399    /// <param name="v">The byte to write to the stream.</param>
 400    public void writeByte(byte v)
 401    {
 402        expand(1);
 403        _buf.b.put(v);
 404    }
 405
 406    /// <summary>
 407    /// Writes an optional byte to the stream.
 408    /// </summary>
 409    /// <param name="tag">The optional tag.</param>
 410    /// <param name="v">The optional byte to write to the stream.</param>
 411    public void writeByte(int tag, byte? v)
 412    {
 413        if (v.HasValue)
 414        {
 415            writeByte(tag, v.Value);
 416        }
 417    }
 418
 419    /// <summary>
 420    /// Writes an optional byte to the stream.
 421    /// </summary>
 422    /// <param name="tag">The optional tag.</param>
 423    /// <param name="v">The byte to write to the stream.</param>
 424    public void writeByte(int tag, byte v)
 425    {
 426        if (writeOptional(tag, OptionalFormat.F1))
 427        {
 428            writeByte(v);
 429        }
 430    }
 431
 432    /// <summary>
 433    /// Writes a byte to the stream at the given position. The current position of the stream is not modified.
 434    /// </summary>
 435    /// <param name="v">The byte to write to the stream.</param>
 436    /// <param name="dest">The position at which to store the byte in the buffer.</param>
 437    public void rewriteByte(byte v, int dest) => _buf.b.put(dest, v);
 438
 439    /// <summary>
 440    /// Writes a byte sequence to the stream.
 441    /// </summary>
 442    /// <param name="v">The byte sequence to write to the stream.
 443    /// Passing null causes an empty sequence to be written to the stream.</param>
 444    public void writeByteSeq(byte[] v)
 445    {
 446        if (v == null)
 447        {
 448            writeSize(0);
 449        }
 450        else
 451        {
 452            writeSize(v.Length);
 453            expand(v.Length);
 454            _buf.b.put(v);
 455        }
 456    }
 457
 458    /// <summary>
 459    /// Writes a byte sequence to the stream.
 460    /// </summary>
 461    /// <param name="count">The number of elements in the sequence.</param>
 462    /// <param name="v">An enumerator for the container holding the sequence.</param>
 463    public void writeByteSeq(int count, IEnumerable<byte> v)
 464    {
 465        if (count == 0)
 466        {
 467            writeSize(0);
 468            return;
 469        }
 470
 471        {
 472            if (v is List<byte> value)
 473            {
 474                writeByteSeq(value.ToArray());
 475                return;
 476            }
 477        }
 478
 479        {
 480            if (v is LinkedList<byte>)
 481            {
 482                writeSize(count);
 483                expand(count);
 484                IEnumerator<byte> i = v.GetEnumerator();
 485                while (i.MoveNext())
 486                {
 487                    _buf.b.put(i.Current);
 488                }
 489                return;
 490            }
 491        }
 492
 493        {
 494            if (v is Queue<byte> value)
 495            {
 496                writeByteSeq(value.ToArray());
 497                return;
 498            }
 499        }
 500
 501        {
 502            if (v is Stack<byte> value)
 503            {
 504                writeByteSeq(value.ToArray());
 505                return;
 506            }
 507        }
 508
 509        writeSize(count);
 510        expand(count);
 511        foreach (byte b in v)
 512        {
 513            _buf.b.put(b);
 514        }
 515    }
 516
 517    /// <summary>
 518    /// Writes an optional byte sequence to the stream.
 519    /// </summary>
 520    /// <param name="tag">The optional tag.</param>
 521    /// <param name="v">The byte sequence to write to the stream.</param>
 522    public void writeByteSeq(int tag, byte[]? v)
 523    {
 524        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 525        {
 526            writeByteSeq(v);
 527        }
 528    }
 529
 530    /// <summary>
 531    /// Writes an optional byte sequence to the stream.
 532    /// </summary>
 533    /// <param name="tag">The optional tag.</param>
 534    /// <param name="count">The number of elements in the sequence.</param>
 535    /// <param name="v">An enumerator for the byte sequence.</param>
 536    public void writeByteSeq(int tag, int count, IEnumerable<byte>? v)
 537    {
 538        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 539        {
 540            writeByteSeq(count, v);
 541        }
 542    }
 543
 544    /// <summary>
 545    /// Writes a boolean to the stream.
 546    /// </summary>
 547    /// <param name="v">The boolean to write to the stream.</param>
 548    public void writeBool(bool v)
 549    {
 550        expand(1);
 551        _buf.b.put(v ? (byte)1 : (byte)0);
 552    }
 553
 554    /// <summary>
 555    /// Writes an optional boolean to the stream.
 556    /// </summary>
 557    /// <param name="tag">The optional tag.</param>
 558    /// <param name="v">The optional boolean to write to the stream.</param>
 559    public void writeBool(int tag, bool? v)
 560    {
 561        if (v.HasValue)
 562        {
 563            writeBool(tag, v.Value);
 564        }
 565    }
 566
 567    /// <summary>
 568    /// Writes an optional boolean to the stream.
 569    /// </summary>
 570    /// <param name="tag">The optional tag.</param>
 571    /// <param name="v">The boolean to write to the stream.</param>
 572    public void writeBool(int tag, bool v)
 573    {
 574        if (writeOptional(tag, OptionalFormat.F1))
 575        {
 576            writeBool(v);
 577        }
 578    }
 579
 580    /// <summary>
 581    /// Writes a boolean to the stream at the given position. The current position of the stream is not modified.
 582    /// </summary>
 583    /// <param name="v">The boolean to write to the stream.</param>
 584    /// <param name="dest">The position at which to store the boolean in the buffer.</param>
 585    public void rewriteBool(bool v, int dest) => _buf.b.put(dest, v ? (byte)1 : (byte)0);
 586
 587    /// <summary>
 588    /// Writes a boolean sequence to the stream.
 589    /// </summary>
 590    /// <param name="v">The boolean sequence to write to the stream.
 591    /// Passing null causes an empty sequence to be written to the stream.</param>
 592    public void writeBoolSeq(bool[] v)
 593    {
 594        if (v == null)
 595        {
 596            writeSize(0);
 597        }
 598        else
 599        {
 600            writeSize(v.Length);
 601            expand(v.Length);
 602            _buf.b.putBoolSeq(v);
 603        }
 604    }
 605
 606    /// <summary>
 607    /// Writes a boolean sequence to the stream.
 608    /// </summary>
 609    /// <param name="count">The number of elements in the sequence.</param>
 610    /// <param name="v">An enumerator for the container holding the sequence.</param>
 611    public void writeBoolSeq(int count, IEnumerable<bool> v)
 612    {
 613        if (count == 0)
 614        {
 615            writeSize(0);
 616            return;
 617        }
 618
 619        {
 620            if (v is List<bool> value)
 621            {
 622                writeBoolSeq(value.ToArray());
 623                return;
 624            }
 625        }
 626
 627        {
 628            if (v is LinkedList<bool>)
 629            {
 630                writeSize(count);
 631                expand(count);
 632                IEnumerator<bool> i = v.GetEnumerator();
 633                while (i.MoveNext())
 634                {
 635                    _buf.b.putBool(i.Current);
 636                }
 637                return;
 638            }
 639        }
 640
 641        {
 642            if (v is Queue<bool> value)
 643            {
 644                writeBoolSeq(value.ToArray());
 645                return;
 646            }
 647        }
 648
 649        {
 650            if (v is Stack<bool> value)
 651            {
 652                writeBoolSeq(value.ToArray());
 653                return;
 654            }
 655        }
 656
 657        writeSize(count);
 658        expand(count);
 659        foreach (bool b in v)
 660        {
 661            _buf.b.putBool(b);
 662        }
 663    }
 664
 665    /// <summary>
 666    /// Writes an optional boolean sequence to the stream.
 667    /// </summary>
 668    /// <param name="tag">The optional tag.</param>
 669    /// <param name="v">The boolean sequence to write to the stream.</param>
 670    public void writeBoolSeq(int tag, bool[]? v)
 671    {
 672        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 673        {
 674            writeBoolSeq(v);
 675        }
 676    }
 677
 678    /// <summary>
 679    /// Writes an optional boolean sequence to the stream.
 680    /// </summary>
 681    /// <param name="tag">The optional tag.</param>
 682    /// <param name="count">The number of elements in the sequence.</param>
 683    /// <param name="v">An enumerator for the boolean sequence.</param>
 684    public void writeBoolSeq(int tag, int count, IEnumerable<bool>? v)
 685    {
 686        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 687        {
 688            writeBoolSeq(count, v);
 689        }
 690    }
 691
 692    /// <summary>
 693    /// Writes a short to the stream.
 694    /// </summary>
 695    /// <param name="v">The short to write to the stream.</param>
 696    public void writeShort(short v)
 697    {
 698        expand(2);
 699        _buf.b.putShort(v);
 700    }
 701
 702    /// <summary>
 703    /// Writes an optional short to the stream.
 704    /// </summary>
 705    /// <param name="tag">The optional tag.</param>
 706    /// <param name="v">The optional short to write to the stream.</param>
 707    public void writeShort(int tag, short? v)
 708    {
 709        if (v.HasValue)
 710        {
 711            writeShort(tag, v.Value);
 712        }
 713    }
 714
 715    /// <summary>
 716    /// Writes an optional short to the stream.
 717    /// </summary>
 718    /// <param name="tag">The optional tag.</param>
 719    /// <param name="v">The short to write to the stream.</param>
 720    public void writeShort(int tag, short v)
 721    {
 722        if (writeOptional(tag, OptionalFormat.F2))
 723        {
 724            writeShort(v);
 725        }
 726    }
 727
 728    /// <summary>
 729    /// Writes a short sequence to the stream.
 730    /// </summary>
 731    /// <param name="v">The short sequence to write to the stream.
 732    /// Passing null causes an empty sequence to be written to the stream.</param>
 733    public void writeShortSeq(short[] v)
 734    {
 735        if (v == null)
 736        {
 737            writeSize(0);
 738        }
 739        else
 740        {
 741            writeSize(v.Length);
 742            expand(v.Length * 2);
 743            _buf.b.putShortSeq(v);
 744        }
 745    }
 746
 747    /// <summary>
 748    /// Writes a short sequence to the stream.
 749    /// </summary>
 750    /// <param name="count">The number of elements in the sequence.</param>
 751    /// <param name="v">An enumerator for the container holding the sequence.</param>
 752    public void writeShortSeq(int count, IEnumerable<short> v)
 753    {
 754        if (count == 0)
 755        {
 756            writeSize(0);
 757            return;
 758        }
 759
 760        {
 761            if (v is List<short> value)
 762            {
 763                writeShortSeq(value.ToArray());
 764                return;
 765            }
 766        }
 767
 768        {
 769            if (v is LinkedList<short>)
 770            {
 771                writeSize(count);
 772                expand(count * 2);
 773                IEnumerator<short> i = v.GetEnumerator();
 774                while (i.MoveNext())
 775                {
 776                    _buf.b.putShort(i.Current);
 777                }
 778                return;
 779            }
 780        }
 781
 782        {
 783            if (v is Queue<short> value)
 784            {
 785                writeShortSeq(value.ToArray());
 786                return;
 787            }
 788        }
 789
 790        {
 791            if (v is Stack<short> value)
 792            {
 793                writeShortSeq(value.ToArray());
 794                return;
 795            }
 796        }
 797
 798        writeSize(count);
 799        expand(count * 2);
 800        foreach (short s in v)
 801        {
 802            _buf.b.putShort(s);
 803        }
 804    }
 805
 806    /// <summary>
 807    /// Writes an optional short sequence to the stream.
 808    /// </summary>
 809    /// <param name="tag">The optional tag.</param>
 810    /// <param name="v">The short sequence to write to the stream.</param>
 811    public void writeShortSeq(int tag, short[]? v)
 812    {
 813        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 814        {
 815            writeSize(v.Length == 0 ? 1 : (v.Length * 2) + (v.Length > 254 ? 5 : 1));
 816            writeShortSeq(v);
 817        }
 818    }
 819
 820    /// <summary>
 821    /// Writes an optional short sequence to the stream.
 822    /// </summary>
 823    /// <param name="tag">The optional tag.</param>
 824    /// <param name="count">The number of elements in the sequence.</param>
 825    /// <param name="v">An enumerator for the short sequence.</param>
 826    public void writeShortSeq(int tag, int count, IEnumerable<short>? v)
 827    {
 828        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 829        {
 830            writeSize(count == 0 ? 1 : (count * 2) + (count > 254 ? 5 : 1));
 831            writeShortSeq(count, v);
 832        }
 833    }
 834
 835    /// <summary>
 836    /// Writes an int to the stream.
 837    /// </summary>
 838    /// <param name="v">The int to write to the stream.</param>
 839    public void writeInt(int v)
 840    {
 841        expand(4);
 842        _buf.b.putInt(v);
 843    }
 844
 845    /// <summary>
 846    /// Writes an optional int to the stream.
 847    /// </summary>
 848    /// <param name="tag">The optional tag.</param>
 849    /// <param name="v">The optional int to write to the stream.</param>
 850    public void writeInt(int tag, int? v)
 851    {
 852        if (v.HasValue)
 853        {
 854            writeInt(tag, v.Value);
 855        }
 856    }
 857
 858    /// <summary>
 859    /// Writes an optional int to the stream.
 860    /// </summary>
 861    /// <param name="tag">The optional tag.</param>
 862    /// <param name="v">The int to write to the stream.</param>
 863    public void writeInt(int tag, int v)
 864    {
 865        if (writeOptional(tag, OptionalFormat.F4))
 866        {
 867            writeInt(v);
 868        }
 869    }
 870
 871    /// <summary>
 872    /// Writes an int to the stream at the given position. The current position of the stream is not modified.
 873    /// </summary>
 874    /// <param name="v">The int to write to the stream.</param>
 875    /// <param name="dest">The position at which to store the int in the buffer.</param>
 876    public void rewriteInt(int v, int dest) => _buf.b.putInt(dest, v);
 877
 878    /// <summary>
 879    /// Writes an int sequence to the stream.
 880    /// </summary>
 881    /// <param name="v">The int sequence to write to the stream.
 882    /// Passing null causes an empty sequence to be written to the stream.</param>
 883    public void writeIntSeq(int[] v)
 884    {
 885        if (v == null)
 886        {
 887            writeSize(0);
 888        }
 889        else
 890        {
 891            writeSize(v.Length);
 892            expand(v.Length * 4);
 893            _buf.b.putIntSeq(v);
 894        }
 895    }
 896
 897    /// <summary>
 898    /// Writes an int sequence to the stream.
 899    /// </summary>
 900    /// <param name="count">The number of elements in the sequence.</param>
 901    /// <param name="v">An enumerator for the container holding the sequence.</param>
 902    public void writeIntSeq(int count, IEnumerable<int> v)
 903    {
 904        if (count == 0)
 905        {
 906            writeSize(0);
 907            return;
 908        }
 909
 910        {
 911            if (v is List<int> value)
 912            {
 913                writeIntSeq(value.ToArray());
 914                return;
 915            }
 916        }
 917
 918        {
 919            if (v is LinkedList<int>)
 920            {
 921                writeSize(count);
 922                expand(count * 4);
 923                IEnumerator<int> i = v.GetEnumerator();
 924                while (i.MoveNext())
 925                {
 926                    _buf.b.putInt(i.Current);
 927                }
 928                return;
 929            }
 930        }
 931
 932        {
 933            if (v is Queue<int> value)
 934            {
 935                writeIntSeq(value.ToArray());
 936                return;
 937            }
 938        }
 939
 940        {
 941            if (v is Stack<int> value)
 942            {
 943                writeIntSeq(value.ToArray());
 944                return;
 945            }
 946        }
 947
 948        writeSize(count);
 949        expand(count * 4);
 950        foreach (int i in v)
 951        {
 952            _buf.b.putInt(i);
 953        }
 954    }
 955
 956    /// <summary>
 957    /// Writes an optional int sequence to the stream.
 958    /// </summary>
 959    /// <param name="tag">The optional tag.</param>
 960    /// <param name="v">The int sequence to write to the stream.</param>
 961    public void writeIntSeq(int tag, int[]? v)
 962    {
 963        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 964        {
 965            writeSize(v.Length == 0 ? 1 : (v.Length * 4) + (v.Length > 254 ? 5 : 1));
 966            writeIntSeq(v);
 967        }
 968    }
 969
 970    /// <summary>
 971    /// Writes an optional int sequence to the stream.
 972    /// </summary>
 973    /// <param name="tag">The optional tag.</param>
 974    /// <param name="count">The number of elements in the sequence.</param>
 975    /// <param name="v">An enumerator for the int sequence.</param>
 976    public void writeIntSeq(int tag, int count, IEnumerable<int>? v)
 977    {
 978        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 979        {
 980            writeSize(count == 0 ? 1 : (count * 4) + (count > 254 ? 5 : 1));
 981            writeIntSeq(count, v);
 982        }
 983    }
 984
 985    /// <summary>
 986    /// Writes a long to the stream.
 987    /// </summary>
 988    /// <param name="v">The long to write to the stream.</param>
 989    public void writeLong(long v)
 990    {
 991        expand(8);
 992        _buf.b.putLong(v);
 993    }
 994
 995    /// <summary>
 996    /// Writes an optional long to the stream.
 997    /// </summary>
 998    /// <param name="tag">The optional tag.</param>
 999    /// <param name="v">The optional long to write to the stream.</param>
 1000    public void writeLong(int tag, long? v)
 1001    {
 1002        if (v.HasValue)
 1003        {
 1004            writeLong(tag, v.Value);
 1005        }
 1006    }
 1007
 1008    /// <summary>
 1009    /// Writes an optional long to the stream.
 1010    /// </summary>
 1011    /// <param name="tag">The optional tag.</param>
 1012    /// <param name="v">The long to write to the stream.</param>
 1013    public void writeLong(int tag, long v)
 1014    {
 1015        if (writeOptional(tag, OptionalFormat.F8))
 1016        {
 1017            writeLong(v);
 1018        }
 1019    }
 1020
 1021    /// <summary>
 1022    /// Writes a long sequence to the stream.
 1023    /// </summary>
 1024    /// <param name="v">The long sequence to write to the stream.
 1025    /// Passing null causes an empty sequence to be written to the stream.</param>
 1026    public void writeLongSeq(long[] v)
 1027    {
 1028        if (v == null)
 1029        {
 1030            writeSize(0);
 1031        }
 1032        else
 1033        {
 1034            writeSize(v.Length);
 1035            expand(v.Length * 8);
 1036            _buf.b.putLongSeq(v);
 1037        }
 1038    }
 1039
 1040    /// <summary>
 1041    /// Writes a long sequence to the stream.
 1042    /// </summary>
 1043    /// <param name="count">The number of elements in the sequence.</param>
 1044    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1045    public void writeLongSeq(int count, IEnumerable<long> v)
 1046    {
 1047        if (count == 0)
 1048        {
 1049            writeSize(0);
 1050            return;
 1051        }
 1052
 1053        {
 1054            if (v is List<long> value)
 1055            {
 1056                writeLongSeq(value.ToArray());
 1057                return;
 1058            }
 1059        }
 1060
 1061        {
 1062            if (v is LinkedList<long>)
 1063            {
 1064                writeSize(count);
 1065                expand(count * 8);
 1066                IEnumerator<long> i = v.GetEnumerator();
 1067                while (i.MoveNext())
 1068                {
 1069                    _buf.b.putLong(i.Current);
 1070                }
 1071                return;
 1072            }
 1073        }
 1074
 1075        {
 1076            if (v is Queue<long> value)
 1077            {
 1078                writeLongSeq(value.ToArray());
 1079                return;
 1080            }
 1081        }
 1082
 1083        {
 1084            if (v is Stack<long> value)
 1085            {
 1086                writeLongSeq(value.ToArray());
 1087                return;
 1088            }
 1089        }
 1090
 1091        writeSize(count);
 1092        expand(count * 8);
 1093        foreach (long l in v)
 1094        {
 1095            _buf.b.putLong(l);
 1096        }
 1097    }
 1098
 1099    /// <summary>
 1100    /// Writes an optional long sequence to the stream.
 1101    /// </summary>
 1102    /// <param name="tag">The optional tag.</param>
 1103    /// <param name="v">The long sequence to write to the stream.</param>
 1104    public void writeLongSeq(int tag, long[]? v)
 1105    {
 1106        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1107        {
 1108            writeSize(v.Length == 0 ? 1 : (v.Length * 8) + (v.Length > 254 ? 5 : 1));
 1109            writeLongSeq(v);
 1110        }
 1111    }
 1112
 1113    /// <summary>
 1114    /// Writes an optional long sequence to the stream.
 1115    /// </summary>
 1116    /// <param name="tag">The optional tag.</param>
 1117    /// <param name="count">The number of elements in the sequence.</param>
 1118    /// <param name="v">An enumerator for the long sequence.</param>
 1119    public void writeLongSeq(int tag, int count, IEnumerable<long>? v)
 1120    {
 1121        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1122        {
 1123            writeSize(count == 0 ? 1 : (count * 8) + (count > 254 ? 5 : 1));
 1124            writeLongSeq(count, v);
 1125        }
 1126    }
 1127
 1128    /// <summary>
 1129    /// Writes a float to the stream.
 1130    /// </summary>
 1131    /// <param name="v">The float to write to the stream.</param>
 1132    public void writeFloat(float v)
 1133    {
 1134        expand(4);
 1135        _buf.b.putFloat(v);
 1136    }
 1137
 1138    /// <summary>
 1139    /// Writes an optional float to the stream.
 1140    /// </summary>
 1141    /// <param name="tag">The optional tag.</param>
 1142    /// <param name="v">The optional float to write to the stream.</param>
 1143    public void writeFloat(int tag, float? v)
 1144    {
 1145        if (v.HasValue)
 1146        {
 1147            writeFloat(tag, v.Value);
 1148        }
 1149    }
 1150
 1151    /// <summary>
 1152    /// Writes an optional float to the stream.
 1153    /// </summary>
 1154    /// <param name="tag">The optional tag.</param>
 1155    /// <param name="v">The float to write to the stream.</param>
 1156    public void writeFloat(int tag, float v)
 1157    {
 1158        if (writeOptional(tag, OptionalFormat.F4))
 1159        {
 1160            writeFloat(v);
 1161        }
 1162    }
 1163
 1164    /// <summary>
 1165    /// Writes a float sequence to the stream.
 1166    /// </summary>
 1167    /// <param name="v">The float sequence to write to the stream.
 1168    /// Passing null causes an empty sequence to be written to the stream.</param>
 1169    public void writeFloatSeq(float[] v)
 1170    {
 1171        if (v == null)
 1172        {
 1173            writeSize(0);
 1174        }
 1175        else
 1176        {
 1177            writeSize(v.Length);
 1178            expand(v.Length * 4);
 1179            _buf.b.putFloatSeq(v);
 1180        }
 1181    }
 1182
 1183    /// <summary>
 1184    /// Writes a float sequence to the stream.
 1185    /// </summary>
 1186    /// <param name="count">The number of elements in the sequence.</param>
 1187    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1188    public void writeFloatSeq(int count, IEnumerable<float> v)
 1189    {
 1190        if (count == 0)
 1191        {
 1192            writeSize(0);
 1193            return;
 1194        }
 1195
 1196        {
 1197            if (v is List<float> value)
 1198            {
 1199                writeFloatSeq(value.ToArray());
 1200                return;
 1201            }
 1202        }
 1203
 1204        {
 1205            if (v is LinkedList<float>)
 1206            {
 1207                writeSize(count);
 1208                expand(count * 4);
 1209                IEnumerator<float> i = v.GetEnumerator();
 1210                while (i.MoveNext())
 1211                {
 1212                    _buf.b.putFloat(i.Current);
 1213                }
 1214                return;
 1215            }
 1216        }
 1217
 1218        {
 1219            if (v is Queue<float> value)
 1220            {
 1221                writeFloatSeq(value.ToArray());
 1222                return;
 1223            }
 1224        }
 1225
 1226        {
 1227            if (v is Stack<float> value)
 1228            {
 1229                writeFloatSeq(value.ToArray());
 1230                return;
 1231            }
 1232        }
 1233
 1234        writeSize(count);
 1235        expand(count * 4);
 1236        foreach (float f in v)
 1237        {
 1238            _buf.b.putFloat(f);
 1239        }
 1240    }
 1241
 1242    /// <summary>
 1243    /// Writes an optional float sequence to the stream.
 1244    /// </summary>
 1245    /// <param name="tag">The optional tag.</param>
 1246    /// <param name="v">The float sequence to write to the stream.</param>
 1247    public void writeFloatSeq(int tag, float[]? v)
 1248    {
 1249        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1250        {
 1251            writeSize(v.Length == 0 ? 1 : (v.Length * 4) + (v.Length > 254 ? 5 : 1));
 1252            writeFloatSeq(v);
 1253        }
 1254    }
 1255
 1256    /// <summary>
 1257    /// Writes an optional float sequence to the stream.
 1258    /// </summary>
 1259    /// <param name="tag">The optional tag.</param>
 1260    /// <param name="count">The number of elements in the sequence.</param>
 1261    /// <param name="v">An enumerator for the float sequence.</param>
 1262    public void writeFloatSeq(int tag, int count, IEnumerable<float>? v)
 1263    {
 1264        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1265        {
 1266            writeSize(count == 0 ? 1 : (count * 4) + (count > 254 ? 5 : 1));
 1267            writeFloatSeq(count, v);
 1268        }
 1269    }
 1270
 1271    /// <summary>
 1272    /// Writes a double to the stream.
 1273    /// </summary>
 1274    /// <param name="v">The double to write to the stream.</param>
 1275    public void writeDouble(double v)
 1276    {
 1277        expand(8);
 1278        _buf.b.putDouble(v);
 1279    }
 1280
 1281    /// <summary>
 1282    /// Writes an optional double to the stream.
 1283    /// </summary>
 1284    /// <param name="tag">The optional tag.</param>
 1285    /// <param name="v">The optional double to write to the stream.</param>
 1286    public void writeDouble(int tag, double? v)
 1287    {
 1288        if (v.HasValue)
 1289        {
 1290            writeDouble(tag, v.Value);
 1291        }
 1292    }
 1293
 1294    /// <summary>
 1295    /// Writes an optional double to the stream.
 1296    /// </summary>
 1297    /// <param name="tag">The optional tag.</param>
 1298    /// <param name="v">The double to write to the stream.</param>
 1299    public void writeDouble(int tag, double v)
 1300    {
 1301        if (writeOptional(tag, OptionalFormat.F8))
 1302        {
 1303            writeDouble(v);
 1304        }
 1305    }
 1306
 1307    /// <summary>
 1308    /// Writes a double sequence to the stream.
 1309    /// </summary>
 1310    /// <param name="v">The double sequence to write to the stream.
 1311    /// Passing null causes an empty sequence to be written to the stream.</param>
 1312    public void writeDoubleSeq(double[] v)
 1313    {
 1314        if (v == null)
 1315        {
 1316            writeSize(0);
 1317        }
 1318        else
 1319        {
 1320            writeSize(v.Length);
 1321            expand(v.Length * 8);
 1322            _buf.b.putDoubleSeq(v);
 1323        }
 1324    }
 1325
 1326    /// <summary>
 1327    /// Writes a double sequence to the stream.
 1328    /// </summary>
 1329    /// <param name="count">The number of elements in the sequence.</param>
 1330    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1331    public void writeDoubleSeq(int count, IEnumerable<double> v)
 1332    {
 1333        if (count == 0)
 1334        {
 1335            writeSize(0);
 1336            return;
 1337        }
 1338
 1339        {
 1340            if (v is List<double> value)
 1341            {
 1342                writeDoubleSeq(value.ToArray());
 1343                return;
 1344            }
 1345        }
 1346
 1347        {
 1348            if (v is LinkedList<double>)
 1349            {
 1350                writeSize(count);
 1351                expand(count * 8);
 1352                IEnumerator<double> i = v.GetEnumerator();
 1353                while (i.MoveNext())
 1354                {
 1355                    _buf.b.putDouble(i.Current);
 1356                }
 1357                return;
 1358            }
 1359        }
 1360
 1361        {
 1362            if (v is Queue<double> value)
 1363            {
 1364                writeDoubleSeq(value.ToArray());
 1365                return;
 1366            }
 1367        }
 1368
 1369        {
 1370            if (v is Stack<double> value)
 1371            {
 1372                writeDoubleSeq(value.ToArray());
 1373                return;
 1374            }
 1375        }
 1376
 1377        writeSize(count);
 1378        expand(count * 8);
 1379        foreach (double d in v)
 1380        {
 1381            _buf.b.putDouble(d);
 1382        }
 1383    }
 1384
 1385    /// <summary>
 1386    /// Writes an optional double sequence to the stream.
 1387    /// </summary>
 1388    /// <param name="tag">The optional tag.</param>
 1389    /// <param name="v">The double sequence to write to the stream.</param>
 1390    public void writeDoubleSeq(int tag, double[]? v)
 1391    {
 1392        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1393        {
 1394            writeSize(v.Length == 0 ? 1 : (v.Length * 8) + (v.Length > 254 ? 5 : 1));
 1395            writeDoubleSeq(v);
 1396        }
 1397    }
 1398
 1399    /// <summary>
 1400    /// Writes an optional double sequence to the stream.
 1401    /// </summary>
 1402    /// <param name="tag">The optional tag.</param>
 1403    /// <param name="count">The number of elements in the sequence.</param>
 1404    /// <param name="v">An enumerator for the double sequence.</param>
 1405    public void writeDoubleSeq(int tag, int count, IEnumerable<double>? v)
 1406    {
 1407        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1408        {
 1409            writeSize(count == 0 ? 1 : (count * 8) + (count > 254 ? 5 : 1));
 1410            writeDoubleSeq(count, v);
 1411        }
 1412    }
 1413
 1414    private static readonly System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true);
 1415
 1416    /// <summary>
 1417    /// Writes a string to the stream.
 1418    /// </summary>
 1419    /// <param name="v">The string to write to the stream. Passing null causes
 1420    /// an empty string to be written to the stream.</param>
 1421    public void writeString(string v)
 1422    {
 1423        if (v == null || v.Length == 0)
 1424        {
 1425            writeSize(0);
 1426            return;
 1427        }
 1428        byte[] arr = utf8.GetBytes(v);
 1429        writeSize(arr.Length);
 1430        expand(arr.Length);
 1431        _buf.b.put(arr);
 1432    }
 1433
 1434    /// <summary>
 1435    /// Writes an optional string to the stream.
 1436    /// </summary>
 1437    /// <param name="tag">The optional tag.</param>
 1438    /// <param name="v">The string to write to the stream.</param>
 1439    public void writeString(int tag, string? v)
 1440    {
 1441        if (v is not null && writeOptional(tag, OptionalFormat.VSize))
 1442        {
 1443            writeString(v);
 1444        }
 1445    }
 1446
 1447    /// <summary>
 1448    /// Writes a string sequence to the stream.
 1449    /// </summary>
 1450    /// <param name="v">The string sequence to write to the stream.
 1451    /// Passing null causes an empty sequence to be written to the stream.</param>
 1452    public void writeStringSeq(string[] v)
 1453    {
 1454        if (v == null)
 1455        {
 1456            writeSize(0);
 1457        }
 1458        else
 1459        {
 1460            writeSize(v.Length);
 1461            for (int i = 0; i < v.Length; i++)
 1462            {
 1463                writeString(v[i]);
 1464            }
 1465        }
 1466    }
 1467
 1468    /// <summary>
 1469    /// Writes a string sequence to the stream.
 1470    /// </summary>
 1471    /// <param name="count">The number of elements in the sequence.</param>
 1472    /// <param name="v">An enumerator for the container holding the sequence.</param>
 1473    public void writeStringSeq(int count, IEnumerable<string> v)
 1474    {
 1475        writeSize(count);
 1476        if (count != 0)
 1477        {
 1478            foreach (string s in v)
 1479            {
 1480                writeString(s);
 1481            }
 1482        }
 1483    }
 1484
 1485    /// <summary>
 1486    /// Writes an optional string sequence to the stream.
 1487    /// </summary>
 1488    /// <param name="tag">The optional tag.</param>
 1489    /// <param name="v">The string sequence to write to the stream.</param>
 1490    public void writeStringSeq(int tag, string[]? v)
 1491    {
 1492        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1493        {
 1494            int pos = startSize();
 1495            writeStringSeq(v);
 1496            endSize(pos);
 1497        }
 1498    }
 1499
 1500    /// <summary>
 1501    /// Writes an optional string sequence to the stream.
 1502    /// </summary>
 1503    /// <param name="tag">The optional tag.</param>
 1504    /// <param name="count">The number of elements in the sequence.</param>
 1505    /// <param name="v">An enumerator for the string sequence.</param>
 1506    public void writeStringSeq(int tag, int count, IEnumerable<string>? v)
 1507    {
 1508        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1509        {
 1510            int pos = startSize();
 1511            writeStringSeq(count, v);
 1512            endSize(pos);
 1513        }
 1514    }
 1515
 1516    /// <summary>
 1517    /// Writes a proxy to the stream.
 1518    /// </summary>
 1519    /// <param name="v">The proxy to write.</param>
 1520    public void writeProxy(ObjectPrx? v)
 1521    {
 1522        if (v is not null)
 1523        {
 1524            v.iceWrite(this);
 1525        }
 1526        else
 1527        {
 1528            Identity.ice_write(this, new Identity());
 1529        }
 1530    }
 1531
 1532    /// <summary>
 1533    /// Writes an optional proxy to the stream.
 1534    /// </summary>
 1535    /// <param name="tag">The optional tag.</param>
 1536    /// <param name="v">The proxy to write.</param>
 1537    public void writeProxy(int tag, ObjectPrx? v)
 1538    {
 1539        // We don't write anything for an optional proxy with a null value.
 1540        if (v is not null && writeOptional(tag, OptionalFormat.FSize))
 1541        {
 1542            int pos = startSize();
 1543            writeProxy(v);
 1544            endSize(pos);
 1545        }
 1546    }
 1547
 1548    /// <summary>
 1549    /// Writes an enumerated value.
 1550    /// </summary>
 1551    /// <param name="v">The enumerator.</param>
 1552    /// <param name="maxValue">The maximum enumerator value in the definition.</param>
 1553    public void writeEnum(int v, int maxValue)
 1554    {
 1555        if (isEncoding_1_0())
 1556        {
 1557            if (maxValue < 127)
 1558            {
 1559                writeByte((byte)v);
 1560            }
 1561            else if (maxValue < 32767)
 1562            {
 1563                writeShort((short)v);
 1564            }
 1565            else
 1566            {
 1567                writeInt(v);
 1568            }
 1569        }
 1570        else
 1571        {
 1572            writeSize(v);
 1573        }
 1574    }
 1575
 1576    /// <summary>
 1577    /// Writes an optional enumerator to the stream.
 1578    /// </summary>
 1579    /// <param name="tag">The optional tag.</param>
 1580    /// <param name="v">The enumerator.</param>
 1581    /// <param name="maxValue">The maximum enumerator value in the definition.</param>
 1582    public void writeEnum(int tag, int v, int maxValue)
 1583    {
 1584        if (writeOptional(tag, OptionalFormat.Size))
 1585        {
 1586            writeEnum(v, maxValue);
 1587        }
 1588    }
 1589
 1590    /// <summary>
 1591    /// Writes a class instance to the stream.
 1592    /// </summary>
 1593    /// <param name="v">The value to write. This method writes the index of an instance; the state of the value is
 1594    /// written once writePendingValues() is called.</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>True if no data has been written yet, false otherwise.</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)
 11751            : base(stream, encaps)
 1752        {
 11753            _sliceType = SliceType.NoSlice;
 11754            _valueIdIndex = 0;
 11755            _toBeMarshaledMap = new Dictionary<Value, int>();
 11756        }
 1757
 1758        internal override void writeValue(Value? v)
 1759        {
 1760            //
 1761            // Object references are encoded as a negative integer in 1.0.
 1762            //
 11763            if (v != null)
 1764            {
 11765                _stream.writeInt(-registerValue(v));
 1766            }
 1767            else
 1768            {
 11769                _stream.writeInt(0);
 1770            }
 11771        }
 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            //
 11783            bool usesClasses = v.iceUsesClasses();
 11784            _stream.writeBool(usesClasses);
 11785            v.iceWrite(_stream);
 11786            if (usesClasses)
 1787            {
 11788                writePendingValues();
 1789            }
 11790        }
 1791
 11792        internal override void startInstance(SliceType sliceType, SlicedData? sliceData) => _sliceType = sliceType;
 1793
 1794        internal override void endInstance()
 1795        {
 11796            if (_sliceType == SliceType.ValueSlice)
 1797            {
 1798                //
 1799                // Write the Object slice.
 1800                //
 11801                startSlice(Value.ice_staticId(), -1, true);
 11802                _stream.writeSize(0); // For compatibility with the old AFM.
 11803                endSlice();
 1804            }
 11805            _sliceType = SliceType.NoSlice;
 11806        }
 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            //
 11815            if (_sliceType == SliceType.ValueSlice)
 1816            {
 11817                int index = registerTypeId(typeId);
 11818                if (index < 0)
 1819                {
 11820                    _stream.writeBool(false);
 11821                    _stream.writeString(typeId);
 1822                }
 1823                else
 1824                {
 11825                    _stream.writeBool(true);
 11826                    _stream.writeSize(index);
 1827                }
 1828            }
 1829            else
 1830            {
 11831                _stream.writeString(typeId);
 1832            }
 1833
 11834            _stream.writeInt(0); // Placeholder for the slice length.
 1835
 11836            _writeSlice = _stream.pos();
 11837        }
 1838
 1839        internal override void endSlice()
 1840        {
 1841            //
 1842            // Write the slice length.
 1843            //
 11844            int sz = _stream.pos() - _writeSlice + 4;
 11845            _stream.rewriteInt(sz, _writeSlice - 4);
 11846        }
 1847
 1848        internal override void writePendingValues()
 1849        {
 11850            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                //
 11858                foreach (KeyValuePair<Value, int> e in _toBeMarshaledMap)
 1859                {
 11860                    _marshaledMap.Add(e.Key, e.Value);
 1861                }
 1862
 11863                Dictionary<Value, int> savedMap = _toBeMarshaledMap;
 11864                _toBeMarshaledMap = new Dictionary<Value, int>();
 11865                _stream.writeSize(savedMap.Count);
 11866                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                    //
 11873                    _stream.writeInt(p.Value);
 1874
 11875                    p.Key.ice_preMarshal();
 11876                    p.Key.iceWrite(_stream);
 1877                }
 1878            }
 11879            _stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances.
 11880        }
 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            //
 11889            if (_toBeMarshaledMap.TryGetValue(v, out int p))
 1890            {
 11891                return p;
 1892            }
 1893
 1894            //
 1895            // Didn't find it, try the marshaled map next.
 1896            //
 11897            if (_marshaledMap.TryGetValue(v, out p))
 1898            {
 11899                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            //
 11906            _toBeMarshaledMap.Add(v, ++_valueIdIndex);
 11907            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 it's 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        {
 2201            internal InstanceData(InstanceData? previous)
 2202            {
 2203                if (previous != null)
 2204                {
 2205                    previous.next = this;
 2206                }
 2207                this.previous = previous;
 2208                next = null;
 2209            }
 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            }
 2274            else
 2275            {
 2276                _encapsStack = new Encaps();
 2277            }
 2278            _encapsStack.setEncoding(_encoding);
 2279        }
 2280
 2281        if (_encapsStack.encoder == null) // Lazy initialization.
 2282        {
 2283            if (_encapsStack.encoding_1_0)
 2284            {
 2285                _encapsStack.encoder = new EncapsEncoder10(this, _encapsStack);
 2286            }
 2287            else
 2288            {
 2289                _encapsStack.encoder = new EncapsEncoder11(this, _encapsStack);
 2290            }
 2291        }
 2292    }
 2293}