< Summary

Information
Class: Ice.InputStream.EncapsDecoder
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/InputStream.cs
Tag: 71_18251537082
Line coverage
97%
Covered lines: 47
Uncovered lines: 1
Coverable lines: 48
Total lines: 3004
Line coverage: 97.9%
Branch coverage
96%
Covered branches: 31
Total branches: 32
Branch coverage: 96.8%
Method coverage
100%
Covered methods: 8
Total methods: 8
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
readOptional(...)100%11100%
readPendingValues()100%11100%
readTypeId(...)83.33%6.05688.89%
newInstance(...)100%11100%
addPatchEntry(...)100%66100%
unmarshal(...)100%2020100%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/InputStream.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using Ice.Internal;
 6using System.Diagnostics;
 7using System.Globalization;
 8using Protocol = Ice.Internal.Protocol;
 9
 10namespace Ice;
 11
 12/// <summary>
 13/// Interface for input streams used to extract Slice types from a sequence of bytes.
 14/// </summary>
 15public sealed class InputStream
 16{
 17    /// <summary>
 18    /// Initializes a new instance of the <see cref="InputStream" /> class. This constructor uses the communicator's
 19    /// default encoding version.
 20    /// </summary>
 21    /// <param name="communicator">The communicator to use when unmarshaling classes, exceptions and proxies.</param>
 22    /// <param name="data">The byte array containing encoded Slice types.</param>
 23    public InputStream(Communicator communicator, byte[] data)
 24        : this(
 25            communicator.instance,
 26            communicator.instance.defaultsAndOverrides().defaultEncoding,
 27            new Internal.Buffer(data))
 28    {
 29    }
 30
 31    /// <summary>
 32    /// Initializes a new instance of the <see cref="InputStream" /> class.
 33    /// </summary>
 34    /// <param name="communicator">The communicator to use when unmarshaling classes, exceptions and proxies.</param>
 35    /// <param name="encoding">The desired encoding version.</param>
 36    /// <param name="data">The byte array containing encoded Slice types.</param>
 37    public InputStream(Communicator communicator, EncodingVersion encoding, byte[] data)
 38        : this(communicator.instance, encoding, new Internal.Buffer(data))
 39    {
 40    }
 41
 42    /// <summary>
 43    /// Initializes a new instance of the <see cref="InputStream" /> class with an empty buffer.
 44    /// <param name="instance">The communicator instance.</param>
 45    /// <param name="encoding">The desired encoding version.</param>
 46    /// </summary>
 47    internal InputStream(Instance instance, EncodingVersion encoding)
 48        : this(instance, encoding, new Internal.Buffer())
 49    {
 50    }
 51
 52    /// <summary>
 53    /// Initializes a new instance of the <see cref="InputStream" /> class while adopting or borrowing the underlying
 54    /// buffer.
 55    /// </summary>
 56    internal InputStream(Instance instance, EncodingVersion encoding, Internal.Buffer buf, bool adopt)
 57        : this(instance, encoding, new Internal.Buffer(buf, adopt))
 58    {
 59    }
 60
 61    /// <summary>
 62    /// Initializes a new instance of the <see cref="InputStream" /> class. All other constructors delegate to this
 63    /// constructor.
 64    /// </summary>
 65    private InputStream(Instance instance, EncodingVersion encoding, Internal.Buffer buf)
 66    {
 67        _encoding = encoding;
 68        _instance = instance;
 69        _buf = buf;
 70        _classGraphDepthMax = _instance.classGraphDepthMax();
 71    }
 72
 73    /// <summary>
 74    /// Resets this stream. This method allows the stream to be reused, to avoid creating unnecessary garbage.
 75    /// </summary>
 76    public void reset()
 77    {
 78        _buf.reset();
 79        clear();
 80    }
 81
 82    /// <summary>
 83    /// Releases any data retained by encapsulations. Internally calls clear().
 84    /// </summary>
 85    public void clear()
 86    {
 87        if (_encapsStack != null)
 88        {
 89            Debug.Assert(_encapsStack.next == null);
 90            _encapsStack.next = _encapsCache;
 91            _encapsCache = _encapsStack;
 92            _encapsStack = null;
 93            _encapsCache.reset();
 94        }
 95
 96        _startSeq = -1;
 97    }
 98
 99    internal Instance instance() => _instance;
 100
 101    /// <summary>
 102    /// Swaps the contents of one stream with another.
 103    /// </summary>
 104    /// <param name="other">The other stream.</param>
 105    public void swap(InputStream other)
 106    {
 107        Debug.Assert(_instance == other._instance);
 108
 109        Internal.Buffer tmpBuf = other._buf;
 110        other._buf = _buf;
 111        _buf = tmpBuf;
 112
 113        EncodingVersion tmpEncoding = other._encoding;
 114        other._encoding = _encoding;
 115        _encoding = tmpEncoding;
 116
 117        int tmpStartSeq = other._startSeq;
 118        other._startSeq = _startSeq;
 119        _startSeq = tmpStartSeq;
 120
 121        int tmpMinSeqSize = other._minSeqSize;
 122        other._minSeqSize = _minSeqSize;
 123        _minSeqSize = tmpMinSeqSize;
 124
 125        // Swap is never called for InputStreams that have encapsulations being read. However,
 126        // encapsulations might still be set in case un-marshaling failed. We just
 127        // reset the encapsulations if there are still some set.
 128        resetEncapsulation();
 129        other.resetEncapsulation();
 130    }
 131
 132    private void resetEncapsulation() => _encapsStack = null;
 133
 134    /// <summary>
 135    /// Resizes the stream to a new size.
 136    /// </summary>
 137    /// <param name="sz">The new size.</param>
 138    internal void resize(int sz)
 139    {
 140        _buf.resize(sz, true);
 141        _buf.b.position(sz);
 142    }
 143
 144    internal Internal.Buffer getBuffer() => _buf;
 145
 146    /// <summary>
 147    /// Marks the start of a class instance.
 148    /// </summary>
 149    public void startValue()
 150    {
 151        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 152        _encapsStack.decoder.startInstance(SliceType.ValueSlice);
 153    }
 154
 155    /// <summary>
 156    /// Marks the end of a class instance.
 157    /// </summary>
 158    /// <returns>A SlicedData object containing the preserved slices for unknown types.</returns>
 159    public SlicedData? endValue()
 160    {
 161        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 162        return _encapsStack.decoder.endInstance();
 163    }
 164
 165    /// <summary>
 166    /// Marks the start of a user exception.
 167    /// </summary>
 168    public void startException()
 169    {
 170        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 171        _encapsStack.decoder.startInstance(SliceType.ExceptionSlice);
 172    }
 173
 174    /// <summary>
 175    /// Marks the end of a user exception.
 176    /// </summary>
 177    public void endException()
 178    {
 179        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 180        _encapsStack.decoder.endInstance();
 181    }
 182
 183    /// <summary>
 184    /// Reads the start of an encapsulation.
 185    /// </summary>
 186    /// <returns>The encapsulation encoding version.</returns>
 187    public EncodingVersion startEncapsulation()
 188    {
 189        Encaps? curr = _encapsCache;
 190        if (curr != null)
 191        {
 192            curr.reset();
 193            _encapsCache = _encapsCache!.next;
 194        }
 195        else
 196        {
 197            curr = new Encaps();
 198        }
 199        curr.next = _encapsStack;
 200        _encapsStack = curr;
 201
 202        _encapsStack.start = _buf.b.position();
 203
 204        //
 205        // I don't use readSize() for encapsulations, because when creating an encapsulation,
 206        // I must know in advance how many bytes the size information will require in the data
 207        // stream. If I use an Int, it is always 4 bytes. For readSize(), it could be 1 or 5 bytes.
 208        //
 209        int sz = readInt();
 210        if (sz < 6)
 211        {
 212            throw new MarshalException(endOfBufferMessage);
 213        }
 214        if (sz - 4 > _buf.b.remaining())
 215        {
 216            throw new MarshalException(endOfBufferMessage);
 217        }
 218        _encapsStack.sz = sz;
 219
 220        var encoding = new EncodingVersion(this);
 221        Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
 222        _encapsStack.setEncoding(encoding);
 223
 224        return encoding;
 225    }
 226
 227    /// <summary>
 228    /// Ends the previous encapsulation.
 229    /// </summary>
 230    public void endEncapsulation()
 231    {
 232        Debug.Assert(_encapsStack != null);
 233
 234        if (!_encapsStack.encoding_1_0)
 235        {
 236            skipOptionals();
 237            if (_buf.b.position() != _encapsStack.start + _encapsStack.sz)
 238            {
 239                throw new MarshalException("Failed to unmarshal encapsulation.");
 240            }
 241        }
 242        else if (_buf.b.position() != _encapsStack.start + _encapsStack.sz)
 243        {
 244            if (_buf.b.position() + 1 != _encapsStack.start + _encapsStack.sz)
 245            {
 246                throw new MarshalException("Failed to unmarshal encapsulation.");
 247            }
 248
 249            //
 250            // Ice version < 3.3 had a bug where user exceptions with
 251            // class members could be encoded with a trailing byte
 252            // when dispatched with AMD. So we tolerate an extra byte
 253            // in the encapsulation.
 254            //
 255            try
 256            {
 257                _buf.b.get();
 258            }
 259            catch (InvalidOperationException ex)
 260            {
 261                throw new MarshalException(endOfBufferMessage, ex);
 262            }
 263        }
 264
 265        Encaps curr = _encapsStack;
 266        _encapsStack = curr.next;
 267        curr.next = _encapsCache;
 268        _encapsCache = curr;
 269        _encapsCache.reset();
 270    }
 271
 272    /// <summary>
 273    /// Skips an empty encapsulation.
 274    /// </summary>
 275    /// <returns>The encapsulation's encoding version.</returns>
 276    public EncodingVersion skipEmptyEncapsulation()
 277    {
 278        int sz = readInt();
 279        if (sz < 6)
 280        {
 281            throw new MarshalException($"{sz} is not a valid encapsulation size.");
 282        }
 283        if (sz - 4 > _buf.b.remaining())
 284        {
 285            throw new MarshalException(endOfBufferMessage);
 286        }
 287
 288        var encoding = new EncodingVersion(this);
 289        Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
 290
 291        if (encoding.Equals(Util.Encoding_1_0))
 292        {
 293            if (sz != 6)
 294            {
 295                throw new MarshalException($"{sz} is not a valid encapsulation size for a 1.0 empty encapsulation.");
 296            }
 297        }
 298        else
 299        {
 300            // Skip the optional content of the encapsulation if we are expecting an
 301            // empty encapsulation.
 302            _buf.b.position(_buf.b.position() + sz - 6);
 303        }
 304        return encoding;
 305    }
 306
 307    /// <summary>
 308    /// Returns a blob of bytes representing an encapsulation. The encapsulation's encoding version
 309    /// is returned in the argument.
 310    /// </summary>
 311    /// <param name="encoding">The encapsulation's encoding version.</param>
 312    /// <returns>The encoded encapsulation.</returns>
 313    public byte[] readEncapsulation(out EncodingVersion encoding)
 314    {
 315        int sz = readInt();
 316        if (sz < 6)
 317        {
 318            throw new MarshalException(endOfBufferMessage);
 319        }
 320
 321        if (sz - 4 > _buf.b.remaining())
 322        {
 323            throw new MarshalException(endOfBufferMessage);
 324        }
 325
 326        encoding = new EncodingVersion(this);
 327        _buf.b.position(_buf.b.position() - 6);
 328
 329        byte[] v = new byte[sz];
 330        try
 331        {
 332            _buf.b.get(v);
 333            return v;
 334        }
 335        catch (InvalidOperationException ex)
 336        {
 337            throw new MarshalException(endOfBufferMessage, ex);
 338        }
 339    }
 340
 341    /// <summary>
 342    /// Determines the current encoding version.
 343    /// </summary>
 344    /// <returns>The encoding version.</returns>
 345    public EncodingVersion getEncoding() => _encapsStack != null ? _encapsStack.encoding : _encoding;
 346
 347    /// <summary>
 348    /// Determines the size of the current encapsulation, excluding the encapsulation header.
 349    /// </summary>
 350    /// <returns>The size of the encapsulated data.</returns>
 351    public int getEncapsulationSize()
 352    {
 353        Debug.Assert(_encapsStack != null);
 354        return _encapsStack.sz - 6;
 355    }
 356
 357    /// <summary>
 358    /// Skips over an encapsulation.
 359    /// </summary>
 360    /// <returns>The encoding version of the skipped encapsulation.</returns>
 361    public EncodingVersion skipEncapsulation()
 362    {
 363        int sz = readInt();
 364        if (sz < 6)
 365        {
 366            throw new MarshalException(endOfBufferMessage);
 367        }
 368        var encoding = new EncodingVersion(this);
 369        try
 370        {
 371            _buf.b.position(_buf.b.position() + sz - 6);
 372        }
 373        catch (ArgumentOutOfRangeException ex)
 374        {
 375            throw new MarshalException(endOfBufferMessage, ex);
 376        }
 377        return encoding;
 378    }
 379
 380    /// <summary>
 381    /// Reads the start of a class instance or exception slice.
 382    /// </summary>
 383    public void startSlice()
 384    {
 385        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 386        _encapsStack.decoder.startSlice();
 387    }
 388
 389    /// <summary>
 390    /// Indicates that the end of a class instance or exception slice has been reached.
 391    /// </summary>
 392    public void endSlice()
 393    {
 394        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 395        _encapsStack.decoder.endSlice();
 396    }
 397
 398    /// <summary>
 399    /// Skips over a class instance or exception slice.
 400    /// </summary>
 401    public void skipSlice()
 402    {
 403        Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
 404        _encapsStack.decoder.skipSlice();
 405    }
 406
 407    /// <summary>
 408    /// Indicates that unmarshaling is complete, except for any class instances. The application must call this
 409    /// method only if the stream actually contains class instances. Calling readPendingValues triggers the
 410    /// calls to the System.Action delegates to inform the application that unmarshaling of an instance
 411    /// is complete.
 412    /// </summary>
 413    public void readPendingValues()
 414    {
 415        if (_encapsStack != null && _encapsStack.decoder != null)
 416        {
 417            _encapsStack.decoder.readPendingValues();
 418        }
 419        else if (_encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0))
 420        {
 421            //
 422            // If using the 1.0 encoding and no instances were read, we
 423            // still read an empty sequence of pending instances if
 424            // requested (i.e.: if this is called).
 425            //
 426            // This is required by the 1.0 encoding, even if no instances
 427            // are written we do marshal an empty sequence if marshaled
 428            // data types use classes.
 429            //
 430            skipSize();
 431        }
 432    }
 433
 434    /// <summary>
 435    /// Extracts a size from the stream.
 436    /// </summary>
 437    /// <returns>The extracted size.</returns>
 438    public int readSize()
 439    {
 440        try
 441        {
 442            byte b = _buf.b.get();
 443            if (b == 255)
 444            {
 445                int v = _buf.b.getInt();
 446                if (v < 0)
 447                {
 448                    throw new MarshalException(endOfBufferMessage);
 449                }
 450                return v;
 451            }
 452            else
 453            {
 454                return b; // byte is unsigned
 455            }
 456        }
 457        catch (InvalidOperationException ex)
 458        {
 459            throw new MarshalException(endOfBufferMessage, ex);
 460        }
 461    }
 462
 463    /// <summary>
 464    /// Reads and validates a sequence size.
 465    /// </summary>
 466    /// <param name="minSize">The minimum size required to encode a sequence element.</param>
 467    /// <returns>The extracted size.</returns>
 468    public int readAndCheckSeqSize(int minSize)
 469    {
 470        int sz = readSize();
 471
 472        if (sz == 0)
 473        {
 474            return 0;
 475        }
 476
 477        //
 478        // The _startSeq variable points to the start of the sequence for which
 479        // we expect to read at least _minSeqSize bytes from the stream.
 480        //
 481        // If not initialized or if we already read more data than _minSeqSize,
 482        // we reset _startSeq and _minSeqSize for this sequence (possibly a
 483        // top-level sequence or enclosed sequence it doesn't really matter).
 484        //
 485        // Otherwise, we are reading an enclosed sequence and we have to bump
 486        // _minSeqSize by the minimum size that this sequence will require on
 487        // the stream.
 488        //
 489        // The goal of this check is to ensure that when we start un-marshaling
 490        // a new sequence, we check the minimal size of this new sequence against
 491        // the estimated remaining buffer size. This estimation is based on
 492        // the minimum size of the enclosing sequences, it's _minSeqSize.
 493        //
 494        if (_startSeq == -1 || _buf.b.position() > (_startSeq + _minSeqSize))
 495        {
 496            _startSeq = _buf.b.position();
 497            _minSeqSize = sz * minSize;
 498        }
 499        else
 500        {
 501            _minSeqSize += sz * minSize;
 502        }
 503
 504        //
 505        // If there isn't enough data to read on the stream for the sequence (and
 506        // possibly enclosed sequences), something is wrong with the marshaled
 507        // data: it's claiming having more data that what is possible to read.
 508        //
 509        if (_startSeq + _minSeqSize > _buf.size())
 510        {
 511            throw new MarshalException(endOfBufferMessage);
 512        }
 513
 514        return sz;
 515    }
 516
 517    /// <summary>
 518    /// Reads a blob of bytes from the stream. The length of the given array determines how many bytes are read.
 519    /// </summary>
 520    /// <param name="v">Bytes from the stream.</param>
 521    public void readBlob(byte[] v)
 522    {
 523        try
 524        {
 525            _buf.b.get(v);
 526        }
 527        catch (InvalidOperationException ex)
 528        {
 529            throw new MarshalException(endOfBufferMessage, ex);
 530        }
 531    }
 532
 533    /// <summary>
 534    /// Reads a blob of bytes from the stream.
 535    /// </summary>
 536    /// <param name="sz">The number of bytes to read.</param>
 537    /// <returns>The requested bytes as a byte array.</returns>
 538    public byte[] readBlob(int sz)
 539    {
 540        if (_buf.b.remaining() < sz)
 541        {
 542            throw new MarshalException(endOfBufferMessage);
 543        }
 544        byte[] v = new byte[sz];
 545        try
 546        {
 547            _buf.b.get(v);
 548            return v;
 549        }
 550        catch (InvalidOperationException ex)
 551        {
 552            throw new MarshalException(endOfBufferMessage, ex);
 553        }
 554    }
 555
 556    /// <summary>
 557    /// Determine if an optional value is available for reading.
 558    /// </summary>
 559    /// <param name="tag">The tag associated with the value.</param>
 560    /// <param name="expectedFormat">The optional format for the value.</param>
 561    /// <returns>True if the value is present, false otherwise.</returns>
 562    public bool readOptional(int tag, OptionalFormat expectedFormat)
 563    {
 564        Debug.Assert(_encapsStack != null);
 565        if (_encapsStack.decoder != null)
 566        {
 567            return _encapsStack.decoder.readOptional(tag, expectedFormat);
 568        }
 569        else
 570        {
 571            return readOptImpl(tag, expectedFormat);
 572        }
 573    }
 574
 575    /// <summary>
 576    /// Extracts a byte value from the stream.
 577    /// </summary>
 578    /// <returns>The extracted byte.</returns>
 579    public byte readByte()
 580    {
 581        try
 582        {
 583            return _buf.b.get();
 584        }
 585        catch (InvalidOperationException ex)
 586        {
 587            throw new MarshalException(endOfBufferMessage, ex);
 588        }
 589    }
 590
 591    /// <summary>
 592    /// Extracts an optional byte value from the stream.
 593    /// </summary>
 594    /// <param name="tag">The numeric tag associated with the value.</param>
 595    /// <returns>The optional value.</returns>
 596    public byte? readByte(int tag)
 597    {
 598        if (readOptional(tag, OptionalFormat.F1))
 599        {
 600            return readByte();
 601        }
 602        else
 603        {
 604            return null;
 605        }
 606    }
 607
 608    /// <summary>
 609    /// Extracts a sequence of byte values from the stream.
 610    /// </summary>
 611    /// <returns>The extracted byte sequence.</returns>
 612    public byte[] readByteSeq()
 613    {
 614        try
 615        {
 616            int sz = readAndCheckSeqSize(1);
 617            byte[] v = new byte[sz];
 618            _buf.b.get(v);
 619            return v;
 620        }
 621        catch (InvalidOperationException ex)
 622        {
 623            throw new MarshalException(endOfBufferMessage, ex);
 624        }
 625    }
 626
 627    /// <summary>
 628    /// Extracts a sequence of byte values from the stream.
 629    /// </summary>
 630    /// <param name="l">The extracted byte sequence as a list.</param>
 631    public void readByteSeq(out List<byte> l) =>
 632        //
 633        // Reading into an array and copy-constructing the
 634        // list is faster than constructing the list
 635        // and adding to it one element at a time.
 636        //
 637        l = new List<byte>(readByteSeq());
 638
 639    /// <summary>
 640    /// Extracts a sequence of byte values from the stream.
 641    /// </summary>
 642    /// <param name="l">The extracted byte sequence as a linked list.</param>
 643    public void readByteSeq(out LinkedList<byte> l) =>
 644        //
 645        // Reading into an array and copy-constructing the
 646        // list is faster than constructing the list
 647        // and adding to it one element at a time.
 648        //
 649        l = new LinkedList<byte>(readByteSeq());
 650
 651    /// <summary>
 652    /// Extracts a sequence of byte values from the stream.
 653    /// </summary>
 654    /// <param name="l">The extracted byte sequence as a queue.</param>
 655    public void readByteSeq(out Queue<byte> l) =>
 656        //
 657        // Reading into an array and copy-constructing the
 658        // queue is faster than constructing the queue
 659        // and adding to it one element at a time.
 660        //
 661        l = new Queue<byte>(readByteSeq());
 662
 663    /// <summary>
 664    /// Extracts a sequence of byte values from the stream.
 665    /// </summary>
 666    /// <param name="l">The extracted byte sequence as a stack.</param>
 667    public void readByteSeq(out Stack<byte> l)
 668    {
 669        //
 670        // Reverse the contents by copying into an array first
 671        // because the stack is marshaled in top-to-bottom order.
 672        //
 673        byte[] array = readByteSeq();
 674        Array.Reverse(array);
 675        l = new Stack<byte>(array);
 676    }
 677
 678    /// <summary>
 679    /// Extracts an optional byte sequence from the stream.
 680    /// </summary>
 681    /// <param name="tag">The numeric tag associated with the value.</param>
 682    /// <returns>The optional value.</returns>
 683    public byte[]? readByteSeq(int tag)
 684    {
 685        if (readOptional(tag, OptionalFormat.VSize))
 686        {
 687            return readByteSeq();
 688        }
 689        else
 690        {
 691            return null;
 692        }
 693    }
 694
 695    /// <summary>
 696    /// Extracts a boolean value from the stream.
 697    /// </summary>
 698    /// <returns>The extracted boolean.</returns>
 699    public bool readBool()
 700    {
 701        try
 702        {
 703            return _buf.b.get() == 1;
 704        }
 705        catch (InvalidOperationException ex)
 706        {
 707            throw new MarshalException(endOfBufferMessage, ex);
 708        }
 709    }
 710
 711    /// <summary>
 712    /// Extracts an optional boolean value from the stream.
 713    /// </summary>
 714    /// <param name="tag">The numeric tag associated with the value.</param>
 715    /// <returns>The optional value.</returns>
 716    public bool? readBool(int tag)
 717    {
 718        if (readOptional(tag, OptionalFormat.F1))
 719        {
 720            return readBool();
 721        }
 722        else
 723        {
 724            return null;
 725        }
 726    }
 727
 728    /// <summary>
 729    /// Extracts a sequence of boolean values from the stream.
 730    /// </summary>
 731    /// <returns>The extracted boolean sequence.</returns>
 732    public bool[] readBoolSeq()
 733    {
 734        try
 735        {
 736            int sz = readAndCheckSeqSize(1);
 737            bool[] v = new bool[sz];
 738            _buf.b.getBoolSeq(v);
 739            return v;
 740        }
 741        catch (InvalidOperationException ex)
 742        {
 743            throw new MarshalException(endOfBufferMessage, ex);
 744        }
 745    }
 746
 747    /// <summary>
 748    /// Extracts a sequence of boolean values from the stream.
 749    /// </summary>
 750    /// <param name="l">The extracted boolean sequence as a list.</param>
 751    public void readBoolSeq(out List<bool> l) =>
 752        //
 753        // Reading into an array and copy-constructing the
 754        // list is faster than constructing the list
 755        // and adding to it one element at a time.
 756        //
 757        l = new List<bool>(readBoolSeq());
 758
 759    /// <summary>
 760    /// Extracts a sequence of boolean values from the stream.
 761    /// </summary>
 762    /// <param name="l">The extracted boolean sequence as a linked list.</param>
 763    public void readBoolSeq(out LinkedList<bool> l) =>
 764        //
 765        // Reading into an array and copy-constructing the
 766        // list is faster than constructing the list
 767        // and adding to it one element at a time.
 768        //
 769        l = new LinkedList<bool>(readBoolSeq());
 770
 771    /// <summary>
 772    /// Extracts a sequence of boolean values from the stream.
 773    /// </summary>
 774    /// <param name="l">The extracted boolean sequence as a queue.</param>
 775    public void readBoolSeq(out Queue<bool> l) =>
 776        //
 777        // Reading into an array and copy-constructing the
 778        // queue is faster than constructing the queue
 779        // and adding to it one element at a time.
 780        //
 781        l = new Queue<bool>(readBoolSeq());
 782
 783    /// <summary>
 784    /// Extracts a sequence of boolean values from the stream.
 785    /// </summary>
 786    /// <param name="l">The extracted boolean sequence as a stack.</param>
 787    public void readBoolSeq(out Stack<bool> l)
 788    {
 789        //
 790        // Reverse the contents by copying into an array first
 791        // because the stack is marshaled in top-to-bottom order.
 792        //
 793        bool[] array = readBoolSeq();
 794        Array.Reverse(array);
 795        l = new Stack<bool>(array);
 796    }
 797
 798    /// <summary>
 799    /// Extracts an optional boolean sequence from the stream.
 800    /// </summary>
 801    /// <param name="tag">The numeric tag associated with the value.</param>
 802    /// <returns>The optional value.</returns>
 803    public bool[]? readBoolSeq(int tag)
 804    {
 805        if (readOptional(tag, OptionalFormat.VSize))
 806        {
 807            return readBoolSeq();
 808        }
 809        else
 810        {
 811            return null;
 812        }
 813    }
 814
 815    /// <summary>
 816    /// Extracts a short value from the stream.
 817    /// </summary>
 818    /// <returns>The extracted short.</returns>
 819    public short readShort()
 820    {
 821        try
 822        {
 823            return _buf.b.getShort();
 824        }
 825        catch (InvalidOperationException ex)
 826        {
 827            throw new MarshalException(endOfBufferMessage, ex);
 828        }
 829    }
 830
 831    /// <summary>
 832    /// Extracts an optional short value from the stream.
 833    /// </summary>
 834    /// <param name="tag">The numeric tag associated with the value.</param>
 835    /// <returns>The optional value.</returns>
 836    public short? readShort(int tag)
 837    {
 838        if (readOptional(tag, OptionalFormat.F2))
 839        {
 840            return readShort();
 841        }
 842        else
 843        {
 844            return null;
 845        }
 846    }
 847
 848    /// <summary>
 849    /// Extracts a sequence of short values from the stream.
 850    /// </summary>
 851    /// <returns>The extracted short sequence.</returns>
 852    public short[] readShortSeq()
 853    {
 854        try
 855        {
 856            int sz = readAndCheckSeqSize(2);
 857            short[] v = new short[sz];
 858            _buf.b.getShortSeq(v);
 859            return v;
 860        }
 861        catch (InvalidOperationException ex)
 862        {
 863            throw new MarshalException(endOfBufferMessage, ex);
 864        }
 865    }
 866
 867    /// <summary>
 868    /// Extracts a sequence of short values from the stream.
 869    /// </summary>
 870    /// <param name="l">The extracted short sequence as a list.</param>
 871    public void readShortSeq(out List<short> l) =>
 872        //
 873        // Reading into an array and copy-constructing the
 874        // list is faster than constructing the list
 875        // and adding to it one element at a time.
 876        //
 877        l = new List<short>(readShortSeq());
 878
 879    /// <summary>
 880    /// Extracts a sequence of short values from the stream.
 881    /// </summary>
 882    /// <param name="l">The extracted short sequence as a linked list.</param>
 883    public void readShortSeq(out LinkedList<short> l) =>
 884        //
 885        // Reading into an array and copy-constructing the
 886        // list is faster than constructing the list
 887        // and adding to it one element at a time.
 888        //
 889        l = new LinkedList<short>(readShortSeq());
 890
 891    /// <summary>
 892    /// Extracts a sequence of short values from the stream.
 893    /// </summary>
 894    /// <param name="l">The extracted short sequence as a queue.</param>
 895    public void readShortSeq(out Queue<short> l) =>
 896        //
 897        // Reading into an array and copy-constructing the
 898        // queue is faster than constructing the queue
 899        // and adding to it one element at a time.
 900        //
 901        l = new Queue<short>(readShortSeq());
 902
 903    /// <summary>
 904    /// Extracts a sequence of short values from the stream.
 905    /// </summary>
 906    /// <param name="l">The extracted short sequence as a stack.</param>
 907    public void readShortSeq(out Stack<short> l)
 908    {
 909        //
 910        // Reverse the contents by copying into an array first
 911        // because the stack is marshaled in top-to-bottom order.
 912        //
 913        short[] array = readShortSeq();
 914        Array.Reverse(array);
 915        l = new Stack<short>(array);
 916    }
 917
 918    /// <summary>
 919    /// Extracts an optional short sequence from the stream.
 920    /// </summary>
 921    /// <param name="tag">The numeric tag associated with the value.</param>
 922    /// <returns>The optional value.</returns>
 923    public short[]? readShortSeq(int tag)
 924    {
 925        if (readOptional(tag, OptionalFormat.VSize))
 926        {
 927            skipSize();
 928            return readShortSeq();
 929        }
 930        else
 931        {
 932            return null;
 933        }
 934    }
 935
 936    /// <summary>
 937    /// Extracts an int value from the stream.
 938    /// </summary>
 939    /// <returns>The extracted int.</returns>
 940    public int readInt()
 941    {
 942        try
 943        {
 944            return _buf.b.getInt();
 945        }
 946        catch (InvalidOperationException ex)
 947        {
 948            throw new MarshalException(endOfBufferMessage, ex);
 949        }
 950    }
 951
 952    /// <summary>
 953    /// Extracts an optional int value from the stream.
 954    /// </summary>
 955    /// <param name="tag">The numeric tag associated with the value.</param>
 956    /// <returns>The optional value.</returns>
 957    public int? readInt(int tag)
 958    {
 959        if (readOptional(tag, OptionalFormat.F4))
 960        {
 961            return readInt();
 962        }
 963        else
 964        {
 965            return null;
 966        }
 967    }
 968
 969    /// <summary>
 970    /// Extracts a sequence of int values from the stream.
 971    /// </summary>
 972    /// <returns>The extracted int sequence.</returns>
 973    public int[] readIntSeq()
 974    {
 975        try
 976        {
 977            int sz = readAndCheckSeqSize(4);
 978            int[] v = new int[sz];
 979            _buf.b.getIntSeq(v);
 980            return v;
 981        }
 982        catch (InvalidOperationException ex)
 983        {
 984            throw new MarshalException(endOfBufferMessage, ex);
 985        }
 986    }
 987
 988    /// <summary>
 989    /// Extracts a sequence of int values from the stream.
 990    /// </summary>
 991    /// <param name="l">The extracted int sequence as a list.</param>
 992    public void readIntSeq(out List<int> l) =>
 993        //
 994        // Reading into an array and copy-constructing the
 995        // list is faster than constructing the list
 996        // and adding to it one element at a time.
 997        //
 998        l = new List<int>(readIntSeq());
 999
 1000    /// <summary>
 1001    /// Extracts a sequence of int values from the stream.
 1002    /// </summary>
 1003    /// <param name="l">The extracted int sequence as a linked list.</param>
 1004    public void readIntSeq(out LinkedList<int> l)
 1005    {
 1006        try
 1007        {
 1008            int sz = readAndCheckSeqSize(4);
 1009            l = new LinkedList<int>();
 1010            for (int i = 0; i < sz; ++i)
 1011            {
 1012                l.AddLast(_buf.b.getInt());
 1013            }
 1014        }
 1015        catch (InvalidOperationException ex)
 1016        {
 1017            throw new MarshalException(endOfBufferMessage, ex);
 1018        }
 1019    }
 1020
 1021    /// <summary>
 1022    /// Extracts a sequence of int values from the stream.
 1023    /// </summary>
 1024    /// <param name="l">The extracted int sequence as a queue.</param>
 1025    public void readIntSeq(out Queue<int> l)
 1026    {
 1027        //
 1028        // Reading into an array and copy-constructing the
 1029        // queue takes the same time as constructing the queue
 1030        // and adding to it one element at a time, so
 1031        // we avoid the copy.
 1032        //
 1033        try
 1034        {
 1035            int sz = readAndCheckSeqSize(4);
 1036            l = new Queue<int>(sz);
 1037            for (int i = 0; i < sz; ++i)
 1038            {
 1039                l.Enqueue(_buf.b.getInt());
 1040            }
 1041        }
 1042        catch (InvalidOperationException ex)
 1043        {
 1044            throw new MarshalException(endOfBufferMessage, ex);
 1045        }
 1046    }
 1047
 1048    /// <summary>
 1049    /// Extracts a sequence of int values from the stream.
 1050    /// </summary>
 1051    /// <param name="l">The extracted int sequence as a stack.</param>
 1052    public void readIntSeq(out Stack<int> l)
 1053    {
 1054        //
 1055        // Reverse the contents by copying into an array first
 1056        // because the stack is marshaled in top-to-bottom order.
 1057        //
 1058        int[] array = readIntSeq();
 1059        Array.Reverse(array);
 1060        l = new Stack<int>(array);
 1061    }
 1062
 1063    /// <summary>
 1064    /// Extracts an optional int sequence from the stream.
 1065    /// </summary>
 1066    /// <param name="tag">The numeric tag associated with the value.</param>
 1067    /// <returns>The optional value.</returns>
 1068    public int[]? readIntSeq(int tag)
 1069    {
 1070        if (readOptional(tag, OptionalFormat.VSize))
 1071        {
 1072            skipSize();
 1073            return readIntSeq();
 1074        }
 1075        else
 1076        {
 1077            return null;
 1078        }
 1079    }
 1080
 1081    /// <summary>
 1082    /// Extracts a long value from the stream.
 1083    /// </summary>
 1084    /// <returns>The extracted long.</returns>
 1085    public long readLong()
 1086    {
 1087        try
 1088        {
 1089            return _buf.b.getLong();
 1090        }
 1091        catch (InvalidOperationException ex)
 1092        {
 1093            throw new MarshalException(endOfBufferMessage, ex);
 1094        }
 1095    }
 1096
 1097    /// <summary>
 1098    /// Extracts an optional long value from the stream.
 1099    /// </summary>
 1100    /// <param name="tag">The numeric tag associated with the value.</param>
 1101    /// <returns>The optional value.</returns>
 1102    public long? readLong(int tag)
 1103    {
 1104        if (readOptional(tag, OptionalFormat.F8))
 1105        {
 1106            return readLong();
 1107        }
 1108        else
 1109        {
 1110            return null;
 1111        }
 1112    }
 1113
 1114    /// <summary>
 1115    /// Extracts a sequence of long values from the stream.
 1116    /// </summary>
 1117    /// <returns>The extracted long sequence.</returns>
 1118    public long[] readLongSeq()
 1119    {
 1120        try
 1121        {
 1122            int sz = readAndCheckSeqSize(8);
 1123            long[] v = new long[sz];
 1124            _buf.b.getLongSeq(v);
 1125            return v;
 1126        }
 1127        catch (InvalidOperationException ex)
 1128        {
 1129            throw new MarshalException(endOfBufferMessage, ex);
 1130        }
 1131    }
 1132
 1133    /// <summary>
 1134    /// Extracts a sequence of long values from the stream.
 1135    /// </summary>
 1136    /// <param name="l">The extracted long sequence as a list.</param>
 1137    public void readLongSeq(out List<long> l) =>
 1138        //
 1139        // Reading into an array and copy-constructing the
 1140        // list is faster than constructing the list
 1141        // and adding to it one element at a time.
 1142        //
 1143        l = new List<long>(readLongSeq());
 1144
 1145    /// <summary>
 1146    /// Extracts a sequence of long values from the stream.
 1147    /// </summary>
 1148    /// <param name="l">The extracted long sequence as a linked list.</param>
 1149    public void readLongSeq(out LinkedList<long> l)
 1150    {
 1151        try
 1152        {
 1153            int sz = readAndCheckSeqSize(4);
 1154            l = new LinkedList<long>();
 1155            for (int i = 0; i < sz; ++i)
 1156            {
 1157                l.AddLast(_buf.b.getLong());
 1158            }
 1159        }
 1160        catch (InvalidOperationException ex)
 1161        {
 1162            throw new MarshalException(endOfBufferMessage, ex);
 1163        }
 1164    }
 1165
 1166    /// <summary>
 1167    /// Extracts a sequence of long values from the stream.
 1168    /// </summary>
 1169    /// <param name="l">The extracted long sequence as a queue.</param>
 1170    public void readLongSeq(out Queue<long> l)
 1171    {
 1172        //
 1173        // Reading into an array and copy-constructing the
 1174        // queue takes the same time as constructing the queue
 1175        // and adding to it one element at a time, so
 1176        // we avoid the copy.
 1177        //
 1178        try
 1179        {
 1180            int sz = readAndCheckSeqSize(4);
 1181            l = new Queue<long>(sz);
 1182            for (int i = 0; i < sz; ++i)
 1183            {
 1184                l.Enqueue(_buf.b.getLong());
 1185            }
 1186        }
 1187        catch (InvalidOperationException ex)
 1188        {
 1189            throw new MarshalException(endOfBufferMessage, ex);
 1190        }
 1191    }
 1192
 1193    /// <summary>
 1194    /// Extracts a sequence of long values from the stream.
 1195    /// </summary>
 1196    /// <param name="l">The extracted long sequence as a stack.</param>
 1197    public void readLongSeq(out Stack<long> l)
 1198    {
 1199        //
 1200        // Reverse the contents by copying into an array first
 1201        // because the stack is marshaled in top-to-bottom order.
 1202        //
 1203        long[] array = readLongSeq();
 1204        Array.Reverse(array);
 1205        l = new Stack<long>(array);
 1206    }
 1207
 1208    /// <summary>
 1209    /// Extracts an optional long sequence from the stream.
 1210    /// </summary>
 1211    /// <param name="tag">The numeric tag associated with the value.</param>
 1212    /// <returns>The optional value.</returns>
 1213    public long[]? readLongSeq(int tag)
 1214    {
 1215        if (readOptional(tag, OptionalFormat.VSize))
 1216        {
 1217            skipSize();
 1218            return readLongSeq();
 1219        }
 1220        else
 1221        {
 1222            return null;
 1223        }
 1224    }
 1225
 1226    /// <summary>
 1227    /// Extracts a float value from the stream.
 1228    /// </summary>
 1229    /// <returns>The extracted float.</returns>
 1230    public float readFloat()
 1231    {
 1232        try
 1233        {
 1234            return _buf.b.getFloat();
 1235        }
 1236        catch (InvalidOperationException ex)
 1237        {
 1238            throw new MarshalException(endOfBufferMessage, ex);
 1239        }
 1240    }
 1241
 1242    /// <summary>
 1243    /// Extracts an optional float value from the stream.
 1244    /// </summary>
 1245    /// <param name="tag">The numeric tag associated with the value.</param>
 1246    /// <returns>The optional value.</returns>
 1247    public float? readFloat(int tag)
 1248    {
 1249        if (readOptional(tag, OptionalFormat.F4))
 1250        {
 1251            return readFloat();
 1252        }
 1253        else
 1254        {
 1255            return null;
 1256        }
 1257    }
 1258
 1259    /// <summary>
 1260    /// Extracts a sequence of float values from the stream.
 1261    /// </summary>
 1262    /// <returns>The extracted float sequence.</returns>
 1263    public float[] readFloatSeq()
 1264    {
 1265        try
 1266        {
 1267            int sz = readAndCheckSeqSize(4);
 1268            float[] v = new float[sz];
 1269            _buf.b.getFloatSeq(v);
 1270            return v;
 1271        }
 1272        catch (InvalidOperationException ex)
 1273        {
 1274            throw new MarshalException(endOfBufferMessage, ex);
 1275        }
 1276    }
 1277
 1278    /// <summary>
 1279    /// Extracts a sequence of float values from the stream.
 1280    /// </summary>
 1281    /// <param name="l">The extracted float sequence as a list.</param>
 1282    public void readFloatSeq(out List<float> l) =>
 1283        //
 1284        // Reading into an array and copy-constructing the
 1285        // list is faster than constructing the list
 1286        // and adding to it one element at a time.
 1287        //
 1288        l = new List<float>(readFloatSeq());
 1289
 1290    /// <summary>
 1291    /// Extracts a sequence of float values from the stream.
 1292    /// </summary>
 1293    /// <param name="l">The extracted float sequence as a linked list.</param>
 1294    public void readFloatSeq(out LinkedList<float> l)
 1295    {
 1296        try
 1297        {
 1298            int sz = readAndCheckSeqSize(4);
 1299            l = new LinkedList<float>();
 1300            for (int i = 0; i < sz; ++i)
 1301            {
 1302                l.AddLast(_buf.b.getFloat());
 1303            }
 1304        }
 1305        catch (InvalidOperationException ex)
 1306        {
 1307            throw new MarshalException(endOfBufferMessage, ex);
 1308        }
 1309    }
 1310
 1311    /// <summary>
 1312    /// Extracts a sequence of float values from the stream.
 1313    /// </summary>
 1314    /// <param name="l">The extracted float sequence as a queue.</param>
 1315    public void readFloatSeq(out Queue<float> l)
 1316    {
 1317        //
 1318        // Reading into an array and copy-constructing the
 1319        // queue takes the same time as constructing the queue
 1320        // and adding to it one element at a time, so
 1321        // we avoid the copy.
 1322        //
 1323        try
 1324        {
 1325            int sz = readAndCheckSeqSize(4);
 1326            l = new Queue<float>(sz);
 1327            for (int i = 0; i < sz; ++i)
 1328            {
 1329                l.Enqueue(_buf.b.getFloat());
 1330            }
 1331        }
 1332        catch (InvalidOperationException ex)
 1333        {
 1334            throw new MarshalException(endOfBufferMessage, ex);
 1335        }
 1336    }
 1337
 1338    /// <summary>
 1339    /// Extracts a sequence of float values from the stream.
 1340    /// </summary>
 1341    /// <param name="l">The extracted float sequence as a stack.</param>
 1342    public void readFloatSeq(out Stack<float> l)
 1343    {
 1344        //
 1345        // Reverse the contents by copying into an array first
 1346        // because the stack is marshaled in top-to-bottom order.
 1347        //
 1348        float[] array = readFloatSeq();
 1349        Array.Reverse(array);
 1350        l = new Stack<float>(array);
 1351    }
 1352
 1353    /// <summary>
 1354    /// Extracts an optional float sequence from the stream.
 1355    /// </summary>
 1356    /// <param name="tag">The numeric tag associated with the value.</param>
 1357    /// <returns>The optional value.</returns>
 1358    public float[]? readFloatSeq(int tag)
 1359    {
 1360        if (readOptional(tag, OptionalFormat.VSize))
 1361        {
 1362            skipSize();
 1363            return readFloatSeq();
 1364        }
 1365        else
 1366        {
 1367            return null;
 1368        }
 1369    }
 1370
 1371    /// <summary>
 1372    /// Extracts a double value from the stream.
 1373    /// </summary>
 1374    /// <returns>The extracted double.</returns>
 1375    public double readDouble()
 1376    {
 1377        try
 1378        {
 1379            return _buf.b.getDouble();
 1380        }
 1381        catch (InvalidOperationException ex)
 1382        {
 1383            throw new MarshalException(endOfBufferMessage, ex);
 1384        }
 1385    }
 1386
 1387    /// <summary>
 1388    /// Extracts an optional double value from the stream.
 1389    /// </summary>
 1390    /// <param name="tag">The numeric tag associated with the value.</param>
 1391    /// <returns>The optional value.</returns>
 1392    public double? readDouble(int tag)
 1393    {
 1394        if (readOptional(tag, OptionalFormat.F8))
 1395        {
 1396            return readDouble();
 1397        }
 1398        else
 1399        {
 1400            return null;
 1401        }
 1402    }
 1403
 1404    /// <summary>
 1405    /// Extracts a sequence of double values from the stream.
 1406    /// </summary>
 1407    /// <returns>The extracted double sequence.</returns>
 1408    public double[] readDoubleSeq()
 1409    {
 1410        try
 1411        {
 1412            int sz = readAndCheckSeqSize(8);
 1413            double[] v = new double[sz];
 1414            _buf.b.getDoubleSeq(v);
 1415            return v;
 1416        }
 1417        catch (InvalidOperationException ex)
 1418        {
 1419            throw new MarshalException(endOfBufferMessage, ex);
 1420        }
 1421    }
 1422
 1423    /// <summary>
 1424    /// Extracts a sequence of double values from the stream.
 1425    /// </summary>
 1426    /// <param name="l">The extracted double sequence as a list.</param>
 1427    public void readDoubleSeq(out List<double> l) =>
 1428        //
 1429        // Reading into an array and copy-constructing the
 1430        // list is faster than constructing the list
 1431        // and adding to it one element at a time.
 1432        //
 1433        l = new List<double>(readDoubleSeq());
 1434
 1435    /// <summary>
 1436    /// Extracts a sequence of double values from the stream.
 1437    /// </summary>
 1438    /// <param name="l">The extracted double sequence as a linked list.</param>
 1439    public void readDoubleSeq(out LinkedList<double> l)
 1440    {
 1441        try
 1442        {
 1443            int sz = readAndCheckSeqSize(4);
 1444            l = new LinkedList<double>();
 1445            for (int i = 0; i < sz; ++i)
 1446            {
 1447                l.AddLast(_buf.b.getDouble());
 1448            }
 1449        }
 1450        catch (InvalidOperationException ex)
 1451        {
 1452            throw new MarshalException(endOfBufferMessage, ex);
 1453        }
 1454    }
 1455
 1456    /// <summary>
 1457    /// Extracts a sequence of double values from the stream.
 1458    /// </summary>
 1459    /// <param name="l">The extracted double sequence as a queue.</param>
 1460    public void readDoubleSeq(out Queue<double> l)
 1461    {
 1462        //
 1463        // Reading into an array and copy-constructing the
 1464        // queue takes the same time as constructing the queue
 1465        // and adding to it one element at a time, so
 1466        // we avoid the copy.
 1467        //
 1468        try
 1469        {
 1470            int sz = readAndCheckSeqSize(4);
 1471            l = new Queue<double>(sz);
 1472            for (int i = 0; i < sz; ++i)
 1473            {
 1474                l.Enqueue(_buf.b.getDouble());
 1475            }
 1476        }
 1477        catch (InvalidOperationException ex)
 1478        {
 1479            throw new MarshalException(endOfBufferMessage, ex);
 1480        }
 1481    }
 1482
 1483    /// <summary>
 1484    /// Extracts a sequence of double values from the stream.
 1485    /// </summary>
 1486    /// <param name="l">The extracted double sequence as a stack.</param>
 1487    public void readDoubleSeq(out Stack<double> l)
 1488    {
 1489        //
 1490        // Reverse the contents by copying into an array first
 1491        // because the stack is marshaled in top-to-bottom order.
 1492        //
 1493        double[] array = readDoubleSeq();
 1494        Array.Reverse(array);
 1495        l = new Stack<double>(array);
 1496    }
 1497
 1498    /// <summary>
 1499    /// Extracts an optional double sequence from the stream.
 1500    /// </summary>
 1501    /// <param name="tag">The numeric tag associated with the value.</param>
 1502    /// <returns>The optional value.</returns>
 1503    public double[]? readDoubleSeq(int tag)
 1504    {
 1505        if (readOptional(tag, OptionalFormat.VSize))
 1506        {
 1507            skipSize();
 1508            return readDoubleSeq();
 1509        }
 1510        else
 1511        {
 1512            return null;
 1513        }
 1514    }
 1515
 1516    private static readonly System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true);
 1517
 1518    /// <summary>
 1519    /// Extracts a string from the stream.
 1520    /// </summary>
 1521    /// <returns>The extracted string.</returns>
 1522    public string readString()
 1523    {
 1524        int len = readSize();
 1525
 1526        if (len == 0)
 1527        {
 1528            return "";
 1529        }
 1530
 1531        //
 1532        // Check the buffer has enough bytes to read.
 1533        //
 1534        if (_buf.b.remaining() < len)
 1535        {
 1536            throw new MarshalException(endOfBufferMessage);
 1537        }
 1538
 1539        try
 1540        {
 1541            //
 1542            // We reuse the _stringBytes array to avoid creating
 1543            // excessive garbage
 1544            //
 1545            if (_stringBytes == null || len > _stringBytes.Length)
 1546            {
 1547                _stringBytes = new byte[len];
 1548            }
 1549            _buf.b.get(_stringBytes, 0, len);
 1550            return utf8.GetString(_stringBytes, 0, len);
 1551        }
 1552        catch (InvalidOperationException ex)
 1553        {
 1554            throw new MarshalException(endOfBufferMessage, ex);
 1555        }
 1556        catch (ArgumentException ex)
 1557        {
 1558            throw new MarshalException("Invalid UTF8 string", ex);
 1559        }
 1560    }
 1561
 1562    /// <summary>
 1563    /// Extracts an optional string from the stream.
 1564    /// </summary>
 1565    /// <param name="tag">The numeric tag associated with the value.</param>
 1566    /// <returns>The optional value.</returns>
 1567    public string? readString(int tag)
 1568    {
 1569        if (readOptional(tag, OptionalFormat.VSize))
 1570        {
 1571            return readString();
 1572        }
 1573        else
 1574        {
 1575            return null;
 1576        }
 1577    }
 1578
 1579    /// <summary>
 1580    /// Extracts a sequence of strings from the stream.
 1581    /// </summary>
 1582    /// <returns>The extracted string sequence.</returns>
 1583    public string[] readStringSeq()
 1584    {
 1585        int sz = readAndCheckSeqSize(1);
 1586        string[] v = new string[sz];
 1587        for (int i = 0; i < sz; i++)
 1588        {
 1589            v[i] = readString();
 1590        }
 1591        return v;
 1592    }
 1593
 1594    /// <summary>
 1595    /// Extracts a sequence of strings from the stream.
 1596    /// </summary>
 1597    /// <param name="l">The extracted string sequence as a list.</param>
 1598    public void readStringSeq(out List<string> l)
 1599    {
 1600        //
 1601        // Reading into an array and copy-constructing the
 1602        // list is slower than constructing the list
 1603        // and adding to it one element at a time.
 1604        //
 1605        int sz = readAndCheckSeqSize(1);
 1606        l = new List<string>(sz);
 1607        for (int i = 0; i < sz; ++i)
 1608        {
 1609            l.Add(readString());
 1610        }
 1611    }
 1612
 1613    /// <summary>
 1614    /// Extracts a sequence of strings from the stream.
 1615    /// </summary>
 1616    /// <param name="l">The extracted string sequence as a linked list.</param>
 1617    public void readStringSeq(out LinkedList<string> l)
 1618    {
 1619        //
 1620        // Reading into an array and copy-constructing the
 1621        // list is slower than constructing the list
 1622        // and adding to it one element at a time.
 1623        //
 1624        int sz = readAndCheckSeqSize(1);
 1625        l = new LinkedList<string>();
 1626        for (int i = 0; i < sz; ++i)
 1627        {
 1628            l.AddLast(readString());
 1629        }
 1630    }
 1631
 1632    /// <summary>
 1633    /// Extracts a sequence of strings from the stream.
 1634    /// </summary>
 1635    /// <param name="l">The extracted string sequence as a queue.</param>
 1636    public void readStringSeq(out Queue<string> l)
 1637    {
 1638        //
 1639        // Reading into an array and copy-constructing the
 1640        // queue is slower than constructing the queue
 1641        // and adding to it one element at a time.
 1642        //
 1643        int sz = readAndCheckSeqSize(1);
 1644        l = new Queue<string>();
 1645        for (int i = 0; i < sz; ++i)
 1646        {
 1647            l.Enqueue(readString());
 1648        }
 1649    }
 1650
 1651    /// <summary>
 1652    /// Extracts a sequence of strings from the stream.
 1653    /// </summary>
 1654    /// <param name="l">The extracted string sequence as a stack.</param>
 1655    public void readStringSeq(out Stack<string> l)
 1656    {
 1657        //
 1658        // Reverse the contents by copying into an array first
 1659        // because the stack is marshaled in top-to-bottom order.
 1660        //
 1661        string[] array = readStringSeq();
 1662        Array.Reverse(array);
 1663        l = new Stack<string>(array);
 1664    }
 1665
 1666    /// <summary>
 1667    /// Extracts an optional string sequence from the stream.
 1668    /// </summary>
 1669    /// <param name="tag">The numeric tag associated with the value.</param>
 1670    /// <returns>The optional value.</returns>
 1671    public string[]? readStringSeq(int tag)
 1672    {
 1673        if (readOptional(tag, OptionalFormat.FSize))
 1674        {
 1675            skip(4);
 1676            return readStringSeq();
 1677        }
 1678        else
 1679        {
 1680            return null;
 1681        }
 1682    }
 1683
 1684    /// <summary>
 1685    /// Extracts a proxy from the stream. The stream must have been initialized with a communicator.
 1686    /// </summary>
 1687    /// <returns>The extracted proxy.</returns>
 1688    public ObjectPrx? readProxy()
 1689    {
 1690        var ident = new Identity(this);
 1691        if (ident.name.Length == 0)
 1692        {
 1693            return null;
 1694        }
 1695        else
 1696        {
 1697            return new ObjectPrxHelper(_instance.referenceFactory().create(ident, this));
 1698        }
 1699    }
 1700
 1701    /// <summary>
 1702    /// Extracts an optional proxy from the stream. The stream must have been initialized with a communicator.
 1703    /// </summary>
 1704    /// <param name="tag">The numeric tag associated with the value.</param>
 1705    /// <returns>The optional value.</returns>
 1706    /// <remarks>This method is not used by the generated code.</remarks>
 1707    public ObjectPrx? readProxy(int tag)
 1708    {
 1709        if (readOptional(tag, OptionalFormat.FSize))
 1710        {
 1711            skip(4);
 1712            return readProxy();
 1713        }
 1714        else
 1715        {
 1716            return null;
 1717        }
 1718    }
 1719
 1720    /// <summary>
 1721    /// Read an enumerated value.
 1722    /// </summary>
 1723    /// <param name="maxValue">The maximum enumerator value in the definition.</param>
 1724    /// <returns>The enumerator.</returns>
 1725    public int readEnum(int maxValue)
 1726    {
 1727        if (getEncoding().Equals(Util.Encoding_1_0))
 1728        {
 1729            if (maxValue < 127)
 1730            {
 1731                return readByte();
 1732            }
 1733            else if (maxValue < 32767)
 1734            {
 1735                return readShort();
 1736            }
 1737            else
 1738            {
 1739                return readInt();
 1740            }
 1741        }
 1742        else
 1743        {
 1744            return readSize();
 1745        }
 1746    }
 1747
 1748    /// <summary>
 1749    /// Extracts the index of a Slice value from the stream.
 1750    /// </summary>
 1751    /// <typeparam name="T">The type of the Slice value to extract.</typeparam>
 1752    /// <param name="cb">The callback to notify the application when the extracted instance is available.
 1753    /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
 1754    /// corresponding instance has been fully unmarshaled.</param>
 1755    public void readValue<T>(System.Action<T?> cb) where T : Value
 1756    {
 1757        initEncaps();
 1758        _encapsStack!.decoder!.readValue(v =>
 1759        {
 1760            if (v == null || v is T)
 1761            {
 1762                cb((T?)v);
 1763            }
 1764            else
 1765            {
 1766                Ice.Internal.Ex.throwUOE(typeof(T), v);
 1767            }
 1768        });
 1769    }
 1770
 1771    /// <summary>
 1772    /// Extracts the index of a Slice value from the stream.
 1773    /// </summary>
 1774    /// <param name="cb">The callback to notify the application when the extracted instance is available.
 1775    /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
 1776    /// corresponding instance has been fully unmarshaled.</param>
 1777    public void readValue(System.Action<Value?> cb) => readValue<Value>(cb);
 1778
 1779    /// <summary>
 1780    /// Extracts a user exception from the stream and throws it.
 1781    /// </summary>
 1782    public void throwException()
 1783    {
 1784        initEncaps();
 1785        _encapsStack!.decoder!.throwException();
 1786    }
 1787
 1788    /// <summary>
 1789    /// Skip the given number of bytes.
 1790    /// </summary>
 1791    /// <param name="size">The number of bytes to skip.</param>
 1792    public void skip(int size)
 1793    {
 1794        if (size < 0 || size > _buf.b.remaining())
 1795        {
 1796            throw new MarshalException(endOfBufferMessage);
 1797        }
 1798        _buf.b.position(_buf.b.position() + size);
 1799    }
 1800
 1801    /// <summary>
 1802    /// Skip over a size value.
 1803    /// </summary>
 1804    public void skipSize()
 1805    {
 1806        byte b = readByte();
 1807        if (b == 255)
 1808        {
 1809            skip(4);
 1810        }
 1811    }
 1812
 1813    /// <summary>
 1814    /// Determines the current position in the stream.
 1815    /// </summary>
 1816    /// <returns>The current position.</returns>
 1817    public int pos() => _buf.b.position();
 1818
 1819    /// <summary>
 1820    /// Sets the current position in the stream.
 1821    /// </summary>
 1822    /// <param name="n">The new position.</param>
 1823    public void pos(int n) => _buf.b.position(n);
 1824
 1825    /// <summary>
 1826    /// Determines the current size of the stream.
 1827    /// </summary>
 1828    /// <returns>The current size.</returns>
 1829    public int size() => _buf.size();
 1830
 1831    /// <summary>
 1832    /// Determines whether the stream is empty.
 1833    /// </summary>
 1834    /// <returns>True if the internal buffer has no data, false otherwise.</returns>
 1835    public bool isEmpty() => _buf.empty();
 1836
 1837    private bool readOptImpl(int readTag, OptionalFormat expectedFormat)
 1838    {
 1839        if (isEncoding_1_0())
 1840        {
 1841            return false; // Optional members aren't supported with the 1.0 encoding.
 1842        }
 1843
 1844        while (true)
 1845        {
 1846            if (_buf.b.position() >= _encapsStack!.start + _encapsStack.sz)
 1847            {
 1848                return false; // End of encapsulation also indicates end of optionals.
 1849            }
 1850
 1851            int v = readByte();
 1852            if (v == Protocol.OPTIONAL_END_MARKER)
 1853            {
 1854                _buf.b.position(_buf.b.position() - 1); // Rewind.
 1855                return false;
 1856            }
 1857
 1858            var format = (OptionalFormat)(v & 0x07); // First 3 bits.
 1859            int tag = v >> 3;
 1860            if (tag == 30)
 1861            {
 1862                tag = readSize();
 1863            }
 1864
 1865            if (tag > readTag)
 1866            {
 1867                int offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind
 1868                _buf.b.position(_buf.b.position() - offset);
 1869                return false; // No optional data members with the requested tag.
 1870            }
 1871            else if (tag < readTag)
 1872            {
 1873                skipOptional(format); // Skip optional data members
 1874            }
 1875            else
 1876            {
 1877                if (format != expectedFormat)
 1878                {
 1879                    throw new MarshalException("invalid optional data member `" + tag + "': unexpected format");
 1880                }
 1881                return true;
 1882            }
 1883        }
 1884    }
 1885
 1886    private void skipOptional(OptionalFormat format)
 1887    {
 1888        switch (format)
 1889        {
 1890            case OptionalFormat.F1:
 1891            {
 1892                skip(1);
 1893                break;
 1894            }
 1895            case OptionalFormat.F2:
 1896            {
 1897                skip(2);
 1898                break;
 1899            }
 1900            case OptionalFormat.F4:
 1901            {
 1902                skip(4);
 1903                break;
 1904            }
 1905            case OptionalFormat.F8:
 1906            {
 1907                skip(8);
 1908                break;
 1909            }
 1910            case OptionalFormat.Size:
 1911            {
 1912                skipSize();
 1913                break;
 1914            }
 1915            case OptionalFormat.VSize:
 1916            {
 1917                skip(readSize());
 1918                break;
 1919            }
 1920            case OptionalFormat.FSize:
 1921            {
 1922                skip(readInt());
 1923                break;
 1924            }
 1925            case OptionalFormat.Class:
 1926            {
 1927                throw new MarshalException("cannot skip an optional class");
 1928            }
 1929        }
 1930    }
 1931
 1932    private bool skipOptionals()
 1933    {
 1934        //
 1935        // Skip remaining un-read optional members.
 1936        //
 1937        while (true)
 1938        {
 1939            if (_buf.b.position() >= _encapsStack!.start + _encapsStack.sz)
 1940            {
 1941                return false; // End of encapsulation also indicates end of optionals.
 1942            }
 1943
 1944            int v = readByte();
 1945            if (v == Protocol.OPTIONAL_END_MARKER)
 1946            {
 1947                return true;
 1948            }
 1949
 1950            var format = (OptionalFormat)(v & 0x07); // Read first 3 bits.
 1951            if ((v >> 3) == 30)
 1952            {
 1953                skipSize();
 1954            }
 1955            skipOptional(format);
 1956        }
 1957    }
 1958
 1959    private UserException? createUserException(string id) => (UserException?)_instance.sliceLoader.newInstance(id);
 1960
 1961    private readonly Instance _instance;
 1962    private Internal.Buffer _buf;
 1963    private byte[]? _stringBytes; // Reusable array for reading strings.
 1964
 1965    private enum SliceType
 1966    {
 1967        NoSlice,
 1968        ValueSlice,
 1969        ExceptionSlice
 1970    }
 1971
 1972    private abstract class EncapsDecoder
 1973    {
 1974        protected struct PatchEntry
 1975        {
 1976            public PatchEntry(System.Action<Value> cb, int classGraphDepth)
 1977            {
 11978                this.cb = cb;
 11979                this.classGraphDepth = classGraphDepth;
 11980            }
 1981
 1982            public System.Action<Value> cb;
 1983            public int classGraphDepth;
 1984        }
 1985
 11986        internal EncapsDecoder(InputStream stream, Encaps encaps, int classGraphDepthMax)
 1987        {
 11988            _stream = stream;
 11989            _encaps = encaps;
 11990            _classGraphDepthMax = classGraphDepthMax;
 11991            _classGraphDepth = 0;
 11992            _typeIdIndex = 0;
 11993            _unmarshaledMap = new Dictionary<int, Value>();
 11994        }
 1995
 1996        internal abstract void readValue(System.Action<Value?> cb);
 1997
 1998        internal abstract void throwException();
 1999
 2000        internal abstract void startInstance(SliceType type);
 2001
 2002        internal abstract SlicedData? endInstance();
 2003
 2004        internal abstract void startSlice();
 2005
 2006        internal abstract void endSlice();
 2007
 2008        internal abstract void skipSlice();
 2009
 12010        internal virtual bool readOptional(int tag, OptionalFormat format) => false;
 2011
 2012        internal virtual void readPendingValues()
 2013        {
 12014        }
 2015
 2016        protected string readTypeId(bool isIndex)
 2017        {
 12018            _typeIdMap ??= new Dictionary<int, string>();
 2019
 12020            if (isIndex)
 2021            {
 12022                int index = _stream.readSize();
 12023                if (!_typeIdMap.TryGetValue(index, out string? typeId))
 2024                {
 02025                    throw new MarshalException(endOfBufferMessage);
 2026                }
 12027                return typeId;
 2028            }
 2029            else
 2030            {
 12031                string typeId = _stream.readString();
 12032                _typeIdMap.Add(++_typeIdIndex, typeId);
 12033                return typeId;
 2034            }
 2035        }
 2036
 12037        protected Value? newInstance(string typeId) => (Value?)_stream._instance.sliceLoader.newInstance(typeId);
 2038
 2039        protected void addPatchEntry(int index, System.Action<Value> cb)
 2040        {
 2041            Debug.Assert(index > 0);
 2042
 2043            //
 2044            // Check if we already unmarshaled the instance. If that's the case,
 2045            // just call the callback and we're done.
 2046            //
 12047            if (_unmarshaledMap.TryGetValue(index, out Value? obj))
 2048            {
 12049                cb(obj);
 12050                return;
 2051            }
 2052
 12053            _patchMap ??= new Dictionary<int, LinkedList<PatchEntry>>();
 2054
 2055            //
 2056            // Add patch entry if the instance isn't unmarshaled yet,
 2057            // the callback will be called when the instance is
 2058            // unmarshaled.
 2059            //
 12060            if (!_patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2061            {
 2062                //
 2063                // We have no outstanding instances to be patched for this
 2064                // index, so make a new entry in the patch map.
 2065                //
 12066                l = new LinkedList<PatchEntry>();
 12067                _patchMap.Add(index, l);
 2068            }
 2069
 2070            //
 2071            // Append a patch entry for this instance.
 2072            //
 12073            l.AddLast(new PatchEntry(cb, _classGraphDepth));
 12074        }
 2075
 2076        protected void unmarshal(int index, Value v)
 2077        {
 2078            //
 2079            // Add the instance to the map of unmarshaled instances, this must
 2080            // be done before reading the instances (for circular references).
 2081            //
 12082            _unmarshaledMap.Add(index, v);
 2083
 2084            //
 2085            // Read the instance.
 2086            //
 12087            v.iceRead(_stream);
 2088
 12089            if (_patchMap != null)
 2090            {
 2091                //
 2092                // Patch all instances now that the instance is unmarshaled.
 2093                //
 12094                if (_patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2095                {
 2096                    Debug.Assert(l.Count > 0);
 2097
 2098                    //
 2099                    // Patch all pointers that refer to the instance.
 2100                    //
 12101                    foreach (PatchEntry entry in l)
 2102                    {
 12103                        entry.cb(v);
 2104                    }
 2105
 2106                    //
 2107                    // Clear out the patch map for that index -- there is nothing left
 2108                    // to patch for that index for the time being.
 2109                    //
 12110                    _patchMap.Remove(index);
 2111                }
 2112            }
 2113
 12114            if ((_patchMap == null || _patchMap.Count == 0) && _valueList == null)
 2115            {
 12116                v.ice_postUnmarshal();
 2117            }
 2118            else
 2119            {
 12120                _valueList ??= new List<Value>();
 12121                _valueList.Add(v);
 2122
 12123                if (_patchMap == null || _patchMap.Count == 0)
 2124                {
 2125                    //
 2126                    // Iterate over the instance list and invoke ice_postUnmarshal on
 2127                    // each instance. We must do this after all instances have been
 2128                    // unmarshaled in order to ensure that any instance data members
 2129                    // have been properly patched.
 2130                    //
 12131                    foreach (Value p in _valueList)
 2132                    {
 12133                        p.ice_postUnmarshal();
 2134                    }
 12135                    _valueList.Clear();
 2136                }
 2137            }
 12138        }
 2139
 2140        protected readonly InputStream _stream;
 2141        protected readonly Encaps _encaps;
 2142        protected readonly int _classGraphDepthMax;
 2143        protected int _classGraphDepth;
 2144
 2145        //
 2146        // Encapsulation attributes for object unmarshaling.
 2147        //
 2148        protected Dictionary<int, LinkedList<PatchEntry>>? _patchMap;
 2149        private readonly Dictionary<int, Value> _unmarshaledMap;
 2150        private Dictionary<int, string>? _typeIdMap;
 2151        private int _typeIdIndex;
 2152        private List<Value>? _valueList;
 2153    }
 2154
 2155    private sealed class EncapsDecoder10 : EncapsDecoder
 2156    {
 2157        internal EncapsDecoder10(InputStream stream, Encaps encaps, int classGraphDepthMax)
 2158            : base(stream, encaps, classGraphDepthMax) => _sliceType = SliceType.NoSlice;
 2159
 2160        internal override void readValue(System.Action<Value?> cb)
 2161        {
 2162            //
 2163            // Object references are encoded as a negative integer in 1.0.
 2164            //
 2165            int index = _stream.readInt();
 2166            if (index > 0)
 2167            {
 2168                throw new MarshalException("invalid object id");
 2169            }
 2170            index = -index;
 2171
 2172            if (index == 0)
 2173            {
 2174                cb(null);
 2175            }
 2176            else
 2177            {
 2178                addPatchEntry(index, cb);
 2179            }
 2180        }
 2181
 2182        internal override void throwException()
 2183        {
 2184            Debug.Assert(_sliceType == SliceType.NoSlice);
 2185
 2186            //
 2187            // User exception with the 1.0 encoding start with a bool flag
 2188            // that indicates whether or not the exception has classes.
 2189            //
 2190            // This allows reading the pending instances even if some part of
 2191            // the exception was sliced.
 2192            //
 2193            bool usesClasses = _stream.readBool();
 2194
 2195            _sliceType = SliceType.ExceptionSlice;
 2196            _skipFirstSlice = false;
 2197
 2198            //
 2199            // Read the first slice header.
 2200            //
 2201            startSlice();
 2202            Debug.Assert(_typeId is not null);
 2203            string mostDerivedId = _typeId;
 2204            while (true)
 2205            {
 2206                UserException? userEx = _stream.createUserException(_typeId);
 2207
 2208                //
 2209                // We found the exception.
 2210                //
 2211                if (userEx != null)
 2212                {
 2213                    userEx.iceRead(_stream);
 2214                    if (usesClasses)
 2215                    {
 2216                        readPendingValues();
 2217                    }
 2218                    throw userEx;
 2219
 2220                    // Never reached.
 2221                }
 2222
 2223                //
 2224                // Slice off what we don't understand.
 2225                //
 2226                skipSlice();
 2227                try
 2228                {
 2229                    startSlice();
 2230                }
 2231                catch (MarshalException)
 2232                {
 2233                    // An oversight in the 1.0 encoding means there is no marker to indicate
 2234                    // the last slice of an exception. As a result, we just try to read the
 2235                    // next type ID, which raises MarshalException when the input buffer underflows.
 2236                    throw new MarshalException($"unknown exception type '{mostDerivedId}'");
 2237                }
 2238            }
 2239        }
 2240
 2241        internal override void startInstance(SliceType sliceType)
 2242        {
 2243            Debug.Assert(_sliceType == sliceType);
 2244            _skipFirstSlice = true;
 2245        }
 2246
 2247        internal override SlicedData? endInstance()
 2248        {
 2249            //
 2250            // Read the Ice::Object slice.
 2251            //
 2252            if (_sliceType == SliceType.ValueSlice)
 2253            {
 2254                startSlice();
 2255                int sz = _stream.readSize(); // For compatibility with the old AFM.
 2256                if (sz != 0)
 2257                {
 2258                    throw new MarshalException("invalid Object slice");
 2259                }
 2260                endSlice();
 2261            }
 2262
 2263            _sliceType = SliceType.NoSlice;
 2264            return null;
 2265        }
 2266
 2267        internal override void startSlice()
 2268        {
 2269            //
 2270            // If first slice, don't read the header, it was already read in
 2271            // readInstance or throwException to find the factory.
 2272            //
 2273            if (_skipFirstSlice)
 2274            {
 2275                _skipFirstSlice = false;
 2276                return;
 2277            }
 2278
 2279            //
 2280            // For instances, first read the type ID bool which indicates
 2281            // whether or not the type ID is encoded as a string or as an
 2282            // index. For exceptions, the type ID is always encoded as a
 2283            // string.
 2284            //
 2285            if (_sliceType == SliceType.ValueSlice) // For exceptions, the type ID is always encoded as a string
 2286            {
 2287                bool isIndex = _stream.readBool();
 2288                _typeId = readTypeId(isIndex);
 2289            }
 2290            else
 2291            {
 2292                _typeId = _stream.readString();
 2293            }
 2294
 2295            _sliceSize = _stream.readInt();
 2296            if (_sliceSize < 4)
 2297            {
 2298                throw new MarshalException(endOfBufferMessage);
 2299            }
 2300        }
 2301
 2302        internal override void endSlice()
 2303        {
 2304        }
 2305
 2306        internal override void skipSlice()
 2307        {
 2308            if (_stream.instance().traceLevels().slicing > 0)
 2309            {
 2310                Logger logger = _stream.instance().initializationData().logger!;
 2311                string slicingCat = _stream.instance().traceLevels().slicingCat;
 2312                if (_sliceType == SliceType.ValueSlice)
 2313                {
 2314                    Ice.Internal.TraceUtil.traceSlicing("object", _typeId, slicingCat, logger);
 2315                }
 2316                else
 2317                {
 2318                    Ice.Internal.TraceUtil.traceSlicing("exception", _typeId, slicingCat, logger);
 2319                }
 2320            }
 2321
 2322            Debug.Assert(_sliceSize >= 4);
 2323            _stream.skip(_sliceSize - 4);
 2324        }
 2325
 2326        internal override void readPendingValues()
 2327        {
 2328            int num;
 2329            do
 2330            {
 2331                num = _stream.readSize();
 2332                for (int k = num; k > 0; --k)
 2333                {
 2334                    readInstance();
 2335                }
 2336            }
 2337            while (num > 0);
 2338
 2339            if (_patchMap != null && _patchMap.Count > 0)
 2340            {
 2341                //
 2342                // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
 2343                // to supply the instance.
 2344                //
 2345                throw new MarshalException("index for class received, but no instance");
 2346            }
 2347        }
 2348
 2349        private void readInstance()
 2350        {
 2351            int index = _stream.readInt();
 2352
 2353            if (index <= 0)
 2354            {
 2355                throw new MarshalException("invalid object id");
 2356            }
 2357
 2358            _sliceType = SliceType.ValueSlice;
 2359            _skipFirstSlice = false;
 2360
 2361            //
 2362            // Read the first slice header.
 2363            //
 2364            startSlice();
 2365            Debug.Assert(_typeId is not null);
 2366            string mostDerivedId = _typeId;
 2367            Value? v;
 2368            while (true)
 2369            {
 2370                // For the 1.0 encoding, the type ID for the base Object class marks the last slice.
 2371                if (_typeId == Value.ice_staticId())
 2372                {
 2373                    throw new MarshalException(
 2374                        $"The Slice loader did not find a class for type ID '{mostDerivedId}'.");
 2375                }
 2376
 2377                v = newInstance(_typeId);
 2378
 2379                //
 2380                // We found a factory, we get out of this loop.
 2381                //
 2382                if (v != null)
 2383                {
 2384                    break;
 2385                }
 2386
 2387                //
 2388                // Slice off what we don't understand.
 2389                //
 2390                skipSlice();
 2391                startSlice(); // Read next Slice header for next iteration.
 2392            }
 2393
 2394            //
 2395            // Compute the biggest class graph depth of this object. To compute this,
 2396            // we get the class graph depth of each ancestor from the patch map and
 2397            // keep the biggest one.
 2398            //
 2399            _classGraphDepth = 0;
 2400            if (_patchMap != null && _patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2401            {
 2402                Debug.Assert(l.Count > 0);
 2403                foreach (PatchEntry entry in l)
 2404                {
 2405                    if (entry.classGraphDepth > _classGraphDepth)
 2406                    {
 2407                        _classGraphDepth = entry.classGraphDepth;
 2408                    }
 2409                }
 2410            }
 2411
 2412            if (++_classGraphDepth > _classGraphDepthMax)
 2413            {
 2414                throw new MarshalException("maximum class graph depth reached");
 2415            }
 2416
 2417            //
 2418            // Unmarshal the instance and add it to the map of unmarshaled instances.
 2419            //
 2420            unmarshal(index, v);
 2421        }
 2422
 2423        // Object/exception attributes
 2424        private SliceType _sliceType;
 2425        private bool _skipFirstSlice;
 2426
 2427        // Slice attributes
 2428        private int _sliceSize;
 2429        private string? _typeId;
 2430    }
 2431
 2432    private sealed class EncapsDecoder11 : EncapsDecoder
 2433    {
 2434        internal EncapsDecoder11(InputStream stream, Encaps encaps, int classGraphDepthMax)
 2435            : base(stream, encaps, classGraphDepthMax)
 2436        {
 2437            _current = null;
 2438            _valueIdIndex = 1;
 2439        }
 2440
 2441        internal override void readValue(System.Action<Value?> cb)
 2442        {
 2443            int index = _stream.readSize();
 2444            if (index < 0)
 2445            {
 2446                throw new MarshalException("invalid object id");
 2447            }
 2448            else if (index == 0)
 2449            {
 2450                cb(null);
 2451            }
 2452            else if (_current != null && (_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2453            {
 2454                //
 2455                // When reading an instance within a slice and there's an
 2456                // indirect instance table, always read an indirect reference
 2457                // that points to an instance from the indirect instance table
 2458                // marshaled at the end of the Slice.
 2459                //
 2460                // Maintain a list of indirect references. Note that the
 2461                // indirect index starts at 1, so we decrement it by one to
 2462                // derive an index into the indirection table that we'll read
 2463                // at the end of the slice.
 2464                //
 2465                _current.indirectPatchList ??= new Stack<IndirectPatchEntry>();
 2466                var e = new IndirectPatchEntry(index - 1, cb);
 2467                _current.indirectPatchList.Push(e);
 2468            }
 2469            else
 2470            {
 2471                readInstance(index, cb);
 2472            }
 2473        }
 2474
 2475        internal override void throwException()
 2476        {
 2477            Debug.Assert(_current == null);
 2478
 2479            push(SliceType.ExceptionSlice);
 2480
 2481            //
 2482            // Read the first slice header.
 2483            //
 2484            startSlice();
 2485            string mostDerivedId = _current!.typeId!;
 2486            while (true)
 2487            {
 2488                UserException? userEx = _stream.createUserException(_current!.typeId!);
 2489
 2490                //
 2491                // We found the exception.
 2492                //
 2493                if (userEx != null)
 2494                {
 2495                    userEx.iceRead(_stream);
 2496                    throw userEx;
 2497
 2498                    // Never reached.
 2499                }
 2500
 2501                //
 2502                // Slice off what we don't understand.
 2503                //
 2504                skipSlice();
 2505
 2506                if ((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
 2507                {
 2508                    throw new MarshalException($"Cannot unmarshal exception with type ID '{mostDerivedId}'");
 2509                }
 2510
 2511                startSlice();
 2512            }
 2513        }
 2514
 2515        internal override void startInstance(SliceType sliceType)
 2516        {
 2517            Debug.Assert(_current!.sliceType == sliceType);
 2518            _current.skipFirstSlice = true;
 2519        }
 2520
 2521        internal override SlicedData? endInstance()
 2522        {
 2523            SlicedData? slicedData = readSlicedData();
 2524            if (_current!.slices != null)
 2525            {
 2526                _current.slices.Clear();
 2527            }
 2528            _current.indirectionTables?.Clear();
 2529            _current = _current.previous;
 2530            return slicedData;
 2531        }
 2532
 2533        internal override void startSlice()
 2534        {
 2535            //
 2536            // If first slice, don't read the header, it was already read in
 2537            // readInstance or throwException to find the factory.
 2538            //
 2539            if (_current!.skipFirstSlice)
 2540            {
 2541                _current.skipFirstSlice = false;
 2542                return;
 2543            }
 2544
 2545            _current.sliceFlags = _stream.readByte();
 2546
 2547            //
 2548            // Read the type ID, for instance slices the type ID is encoded as a
 2549            // string or as an index, for exceptions it's always encoded as a
 2550            // string.
 2551            //
 2552            if (_current.sliceType == SliceType.ValueSlice)
 2553            {
 2554                //
 2555                // Must be checked first!
 2556                //
 2557                if ((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_COMPACT) == Protocol.FLAG_HAS_TYPE_ID_COMPACT)
 2558                {
 2559                    _current.compactId = _stream.readSize();
 2560                    _current.typeId = _current.compactId.ToString(CultureInfo.InvariantCulture);
 2561                }
 2562                else if ((_current.sliceFlags &
 2563                        (Protocol.FLAG_HAS_TYPE_ID_INDEX | Protocol.FLAG_HAS_TYPE_ID_STRING)) != 0)
 2564                {
 2565                    _current.typeId = readTypeId((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_INDEX) != 0);
 2566                    _current.compactId = -1;
 2567                }
 2568                else
 2569                {
 2570                    // Only the most derived slice encodes the type ID for the compact format.
 2571                    _current.typeId = "";
 2572                    _current.compactId = -1;
 2573                }
 2574            }
 2575            else
 2576            {
 2577                _current.typeId = _stream.readString();
 2578                _current.compactId = -1;
 2579            }
 2580
 2581            //
 2582            // Read the slice size if necessary.
 2583            //
 2584            if ((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2585            {
 2586                _current.sliceSize = _stream.readInt();
 2587                if (_current.sliceSize < 4)
 2588                {
 2589                    throw new MarshalException(endOfBufferMessage);
 2590                }
 2591            }
 2592            else
 2593            {
 2594                _current.sliceSize = 0;
 2595            }
 2596        }
 2597
 2598        internal override void endSlice()
 2599        {
 2600            if ((_current!.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
 2601            {
 2602                _stream.skipOptionals();
 2603            }
 2604
 2605            //
 2606            // Read the indirection table if one is present and transform the
 2607            // indirect patch list into patch entries with direct references.
 2608            //
 2609            if ((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2610            {
 2611                //
 2612                // The table is written as a sequence<size> to conserve space.
 2613                //
 2614                int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
 2615                for (int i = 0; i < indirectionTable.Length; ++i)
 2616                {
 2617                    indirectionTable[i] = readInstance(_stream.readSize(), null);
 2618                }
 2619
 2620                //
 2621                // Sanity checks. If there are optional members, it's possible
 2622                // that not all instance references were read if they are from
 2623                // unknown optional data members.
 2624                //
 2625                if (indirectionTable.Length == 0)
 2626                {
 2627                    throw new MarshalException("empty indirection table");
 2628                }
 2629                if ((_current.indirectPatchList == null || _current.indirectPatchList.Count == 0) &&
 2630                   (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) == 0)
 2631                {
 2632                    throw new MarshalException("no references to indirection table");
 2633                }
 2634
 2635                //
 2636                // Convert indirect references into direct references.
 2637                //
 2638                if (_current.indirectPatchList != null)
 2639                {
 2640                    foreach (IndirectPatchEntry e in _current.indirectPatchList)
 2641                    {
 2642                        Debug.Assert(e.index >= 0);
 2643                        if (e.index >= indirectionTable.Length)
 2644                        {
 2645                            throw new MarshalException("indirection out of range");
 2646                        }
 2647                        addPatchEntry(indirectionTable[e.index], e.patcher);
 2648                    }
 2649                    _current.indirectPatchList.Clear();
 2650                }
 2651            }
 2652        }
 2653
 2654        internal override void skipSlice()
 2655        {
 2656            if (_stream.instance().traceLevels().slicing > 0)
 2657            {
 2658                Logger logger = _stream.instance().initializationData().logger!;
 2659                string slicingCat = _stream.instance().traceLevels().slicingCat;
 2660                if (_current!.sliceType == SliceType.ExceptionSlice)
 2661                {
 2662                    Ice.Internal.TraceUtil.traceSlicing("exception", _current.typeId, slicingCat, logger);
 2663                }
 2664                else
 2665                {
 2666                    Ice.Internal.TraceUtil.traceSlicing("object", _current.typeId, slicingCat, logger);
 2667                }
 2668            }
 2669
 2670            int start = _stream.pos();
 2671
 2672            if ((_current!.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2673            {
 2674                Debug.Assert(_current.sliceSize >= 4);
 2675                _stream.skip(_current.sliceSize - 4);
 2676            }
 2677            else
 2678            {
 2679                if (_current.sliceType == SliceType.ValueSlice)
 2680                {
 2681                    throw new MarshalException(
 2682                        $"The Slice loader did not find a class for type ID '{_current.typeId}' and compact format preve
 2683                }
 2684                else
 2685                {
 2686                    throw new MarshalException(
 2687                        $"The Slice loader did not find a user exception class for type ID '{_current.typeId}' and compa
 2688                }
 2689            }
 2690
 2691            //
 2692            // Preserve this slice if unmarshaling a value in Slice format. Exception slices are not preserved.
 2693            //
 2694            if (_current.sliceType == SliceType.ValueSlice)
 2695            {
 2696                bool hasOptionalMembers = (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0;
 2697
 2698                Ice.Internal.ByteBuffer b = _stream.getBuffer().b;
 2699                int end = b.position();
 2700                int dataEnd = end;
 2701                if (hasOptionalMembers)
 2702                {
 2703                    //
 2704                    // Don't include the optional member end marker. It will be re-written by
 2705                    // endSlice when the sliced data is re-written.
 2706                    //
 2707                    --dataEnd;
 2708                }
 2709                byte[] bytes = new byte[dataEnd - start];
 2710                b.position(start);
 2711                b.get(bytes);
 2712                b.position(end);
 2713
 2714                var info = new SliceInfo(
 2715                    typeId: _current.typeId!,
 2716                    compactId: _current.compactId,
 2717                    bytes: bytes,
 2718                    hasOptionalMembers: hasOptionalMembers,
 2719                    isLastSlice: (_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0);
 2720
 2721                _current.slices ??= new List<SliceInfo>();
 2722                _current.slices.Add(info);
 2723            }
 2724
 2725            //
 2726            // Read the indirect instance table. We read the instances or their
 2727            // IDs if the instance is a reference to an already unmarshaled
 2728            // instance.
 2729            //
 2730
 2731            _current.indirectionTables ??= new List<int[]?>();
 2732
 2733            if ((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2734            {
 2735                int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
 2736                for (int i = 0; i < indirectionTable.Length; ++i)
 2737                {
 2738                    indirectionTable[i] = readInstance(_stream.readSize(), null);
 2739                }
 2740                _current.indirectionTables.Add(indirectionTable);
 2741            }
 2742            else
 2743            {
 2744                _current.indirectionTables.Add(null);
 2745            }
 2746        }
 2747
 2748        internal override bool readOptional(int readTag, OptionalFormat expectedFormat)
 2749        {
 2750            if (_current == null)
 2751            {
 2752                return _stream.readOptImpl(readTag, expectedFormat);
 2753            }
 2754            else if ((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
 2755            {
 2756                return _stream.readOptImpl(readTag, expectedFormat);
 2757            }
 2758            return false;
 2759        }
 2760
 2761        private int readInstance(int index, System.Action<Value>? cb)
 2762        {
 2763            Debug.Assert(index > 0);
 2764
 2765            if (index > 1)
 2766            {
 2767                if (cb != null)
 2768                {
 2769                    addPatchEntry(index, cb);
 2770                }
 2771                return index;
 2772            }
 2773
 2774            push(SliceType.ValueSlice);
 2775
 2776            //
 2777            // Get the instance ID before we start reading slices. If some
 2778            // slices are skipped, the indirect instance table are still read and
 2779            // might read other instances.
 2780            //
 2781            index = ++_valueIdIndex;
 2782
 2783            //
 2784            // Read the first slice header.
 2785            //
 2786            startSlice();
 2787            string mostDerivedId = _current!.typeId!;
 2788            Value? v;
 2789            while (true)
 2790            {
 2791                string typeId = _current.typeId!;
 2792
 2793                if (typeId.Length > 0)
 2794                {
 2795                    v = newInstance(typeId);
 2796                    if (v is not null)
 2797                    {
 2798                        break;
 2799                    }
 2800                }
 2801
 2802                //
 2803                // Slice off what we don't understand.
 2804                //
 2805                skipSlice();
 2806
 2807                //
 2808                // If this is the last slice, keep the instance as an opaque
 2809                // UnknownSlicedValue object.
 2810                //
 2811                if ((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
 2812                {
 2813                    //
 2814                    // Provide a factory with an opportunity to supply the instance.
 2815                    // We pass the "::Ice::Object" ID to indicate that this is the
 2816                    // last chance to preserve the instance.
 2817                    //
 2818                    v = newInstance(Value.ice_staticId());
 2819                    v ??= new UnknownSlicedValue(mostDerivedId);
 2820
 2821                    break;
 2822                }
 2823
 2824                startSlice(); // Read next Slice header for next iteration.
 2825            }
 2826
 2827            if (++_classGraphDepth > _classGraphDepthMax)
 2828            {
 2829                throw new MarshalException("maximum class graph depth reached");
 2830            }
 2831
 2832            //
 2833            // Unmarshal the instance.
 2834            //
 2835            unmarshal(index, v);
 2836
 2837            --_classGraphDepth;
 2838
 2839            if (_current == null && _patchMap != null && _patchMap.Count > 0)
 2840            {
 2841                //
 2842                // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
 2843                // to supply the instance.
 2844                //
 2845                throw new MarshalException("index for class received, but no instance");
 2846            }
 2847
 2848            cb?.Invoke(v);
 2849            return index;
 2850        }
 2851
 2852        private SlicedData? readSlicedData()
 2853        {
 2854            if (_current!.slices == null) // No preserved slices.
 2855            {
 2856                return null;
 2857            }
 2858
 2859            //
 2860            // The _indirectionTables member holds the indirection table for each slice
 2861            // in _slices.
 2862            //
 2863            Debug.Assert(_current.slices.Count == _current.indirectionTables!.Count);
 2864            for (int n = 0; n < _current.slices.Count; ++n)
 2865            {
 2866                //
 2867                // We use the "instances" list in SliceInfo to hold references
 2868                // to the target instances. Note that the instances might not have
 2869                // been read yet in the case of a circular reference to an
 2870                // enclosing instance.
 2871                //
 2872                int[]? table = _current.indirectionTables[n];
 2873                SliceInfo info = _current.slices[n];
 2874                info.instances = new Value[table != null ? table.Length : 0];
 2875                for (int j = 0; j < info.instances.Length; ++j)
 2876                {
 2877                    int cj = j;
 2878                    addPatchEntry(table![j], (Ice.Value v) => info.instances[cj] = v);
 2879                }
 2880            }
 2881
 2882            return new SlicedData(_current.slices.ToArray());
 2883        }
 2884
 2885        private void push(SliceType sliceType)
 2886        {
 2887            if (_current == null)
 2888            {
 2889                _current = new InstanceData(null);
 2890            }
 2891            else
 2892            {
 2893                _current = _current.next ?? new InstanceData(_current);
 2894            }
 2895            _current.sliceType = sliceType;
 2896            _current.skipFirstSlice = false;
 2897        }
 2898
 2899        private sealed record class IndirectPatchEntry(int index, Action<Value?> patcher);
 2900
 2901        private sealed class InstanceData
 2902        {
 2903            internal InstanceData(InstanceData? previous)
 2904            {
 2905                if (previous != null)
 2906                {
 2907                    previous.next = this;
 2908                }
 2909                this.previous = previous;
 2910                this.next = null;
 2911            }
 2912
 2913            // Instance attributes
 2914            internal SliceType sliceType;
 2915            internal bool skipFirstSlice;
 2916            internal List<SliceInfo>? slices;     // Preserved slices.
 2917            internal List<int[]?>? indirectionTables;
 2918
 2919            // Slice attributes
 2920            internal byte sliceFlags;
 2921            internal int sliceSize;
 2922            internal string? typeId;
 2923            internal int compactId;
 2924            internal Stack<IndirectPatchEntry>? indirectPatchList;
 2925
 2926            internal InstanceData? previous;
 2927            internal InstanceData? next;
 2928        }
 2929
 2930        private InstanceData? _current;
 2931        private int _valueIdIndex; // The ID of the next instance to unmarshal.
 2932    }
 2933
 2934    private sealed class Encaps
 2935    {
 2936        internal void reset() => decoder = null;
 2937
 2938        internal void setEncoding(EncodingVersion encoding)
 2939        {
 2940            this.encoding = encoding;
 2941            encoding_1_0 = encoding.Equals(Util.Encoding_1_0);
 2942        }
 2943
 2944        internal int start;
 2945        internal int sz;
 2946        internal EncodingVersion encoding;
 2947        internal bool encoding_1_0;
 2948
 2949        internal EncapsDecoder? decoder;
 2950
 2951        internal Encaps? next;
 2952    }
 2953
 2954    //
 2955    // The encoding version to use when there's no encapsulation to
 2956    // read from. This is for example used to read message headers.
 2957    //
 2958    private EncodingVersion _encoding;
 2959
 2960    private bool isEncoding_1_0() =>
 2961        _encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0);
 2962
 2963    private Encaps? _encapsStack;
 2964    private Encaps? _encapsCache;
 2965
 2966    private void initEncaps()
 2967    {
 2968        if (_encapsStack == null) // Lazy initialization
 2969        {
 2970            _encapsStack = _encapsCache;
 2971            if (_encapsStack != null)
 2972            {
 2973                _encapsCache = _encapsCache!.next;
 2974            }
 2975            else
 2976            {
 2977                _encapsStack = new Encaps();
 2978            }
 2979            _encapsStack.setEncoding(_encoding);
 2980            _encapsStack.sz = _buf.b.limit();
 2981        }
 2982
 2983        if (_encapsStack.decoder == null) // Lazy initialization.
 2984        {
 2985            if (_encapsStack.encoding_1_0)
 2986            {
 2987                _encapsStack.decoder =
 2988                    new EncapsDecoder10(this, _encapsStack, _classGraphDepthMax);
 2989            }
 2990            else
 2991            {
 2992                _encapsStack.decoder =
 2993                    new EncapsDecoder11(this, _encapsStack, _classGraphDepthMax);
 2994            }
 2995        }
 2996    }
 2997
 2998    private readonly int _classGraphDepthMax;
 2999
 3000    private int _startSeq = -1;
 3001    private int _minSeqSize;
 3002
 3003    private const string endOfBufferMessage = "Attempting to unmarshal past the end of the buffer.";
 3004}