< Summary

Information
Class: Ice.InputStream.EncapsDecoder11.InstanceData
Assembly: Ice
File(s): /_/csharp/src/Ice/InputStream.cs
Tag: 106_26348240760
Line coverage
100%
Covered lines: 6
Uncovered lines: 0
Coverable lines: 6
Total lines: 3014
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

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

File(s)

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