< Summary

Information
Class: Ice.InputStream.EncapsDecoder
Assembly: Ice
File(s): /_/csharp/src/Ice/InputStream.cs
Tag: 125_37167941578
Line coverage
97%
Covered lines: 47
Uncovered lines: 1
Coverable lines: 48
Total lines: 3026
Line coverage: 97.9%
Branch coverage
96%
Covered branches: 31
Total branches: 32
Branch coverage: 96.8%
Method coverage
100%
Covered methods: 8
Fully covered methods: 7
Total methods: 8
Method coverage: 100%
Full method coverage: 87.5%

Metrics

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

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/// Represents a byte buffer used for unmarshaling data encoded using the Slice encoding.
 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.
 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 current 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 to have more data than 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; otherwise, <see langword="false"/>.</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 runtime 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 runtime 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; otherwise,
 1840    /// <see langword="false"/>.</returns>
 1841    public bool isEmpty() => _buf.empty();
 1842
 1843    private bool readOptImpl(int readTag, OptionalFormat expectedFormat)
 1844    {
 1845        if (isEncoding_1_0())
 1846        {
 1847            return false; // Optional members aren't supported with the 1.0 encoding.
 1848        }
 1849
 1850        while (true)
 1851        {
 1852            if (_buf.b.position() >= _encapsStack!.start + _encapsStack.sz)
 1853            {
 1854                return false; // End of encapsulation also indicates end of optionals.
 1855            }
 1856
 1857            int v = readByte();
 1858            if (v == Protocol.OPTIONAL_END_MARKER)
 1859            {
 1860                _buf.b.position(_buf.b.position() - 1); // Rewind.
 1861                return false;
 1862            }
 1863
 1864            var format = (OptionalFormat)(v & 0x07); // First 3 bits.
 1865            int tag = v >> 3;
 1866            if (tag > 30)
 1867            {
 1868                // We check for '> 30' instead of '> 29' because 30 is a special sentinel tag, handled by the next
 1869                // block.
 1870                throw new MarshalException($"Invalid tag '{tag}': tags larger than 29 must be encoded as a size.");
 1871            }
 1872            if (tag == 30)
 1873            {
 1874                tag = readSize();
 1875            }
 1876
 1877            if (tag > readTag)
 1878            {
 1879                int offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind
 1880                _buf.b.position(_buf.b.position() - offset);
 1881                return false; // No optional data members with the requested tag.
 1882            }
 1883            else if (tag < readTag)
 1884            {
 1885                skipOptional(format); // Skip optional data members
 1886            }
 1887            else
 1888            {
 1889                if (format != expectedFormat)
 1890                {
 1891                    throw new MarshalException($"Invalid optional tag '{tag}': unexpected format.");
 1892                }
 1893                return true;
 1894            }
 1895        }
 1896    }
 1897
 1898    private void skipOptional(OptionalFormat format)
 1899    {
 1900        switch (format)
 1901        {
 1902            case OptionalFormat.F1:
 1903            {
 1904                skip(1);
 1905                break;
 1906            }
 1907            case OptionalFormat.F2:
 1908            {
 1909                skip(2);
 1910                break;
 1911            }
 1912            case OptionalFormat.F4:
 1913            {
 1914                skip(4);
 1915                break;
 1916            }
 1917            case OptionalFormat.F8:
 1918            {
 1919                skip(8);
 1920                break;
 1921            }
 1922            case OptionalFormat.Size:
 1923            {
 1924                skipSize();
 1925                break;
 1926            }
 1927            case OptionalFormat.VSize:
 1928            {
 1929                skip(readSize());
 1930                break;
 1931            }
 1932            case OptionalFormat.FSize:
 1933            {
 1934                skip(readInt());
 1935                break;
 1936            }
 1937            case OptionalFormat.Class:
 1938            {
 1939                throw new MarshalException("Cannot skip an optional class.");
 1940            }
 1941        }
 1942    }
 1943
 1944    private bool skipOptionals()
 1945    {
 1946        //
 1947        // Skip remaining un-read optional members.
 1948        //
 1949        while (true)
 1950        {
 1951            if (_buf.b.position() >= _encapsStack!.start + _encapsStack.sz)
 1952            {
 1953                return false; // End of encapsulation also indicates end of optionals.
 1954            }
 1955
 1956            int v = readByte();
 1957            if (v == Protocol.OPTIONAL_END_MARKER)
 1958            {
 1959                return true;
 1960            }
 1961
 1962            var format = (OptionalFormat)(v & 0x07); // Read first 3 bits.
 1963            if ((v >> 3) == 30)
 1964            {
 1965                skipSize();
 1966            }
 1967            skipOptional(format);
 1968        }
 1969    }
 1970
 1971    private UserException? createUserException(string id) => (UserException?)_instance.sliceLoader.newInstance(id);
 1972
 1973    private readonly Instance _instance;
 1974    private Internal.Buffer _buf;
 1975    private byte[]? _stringBytes; // Reusable array for reading strings.
 1976
 1977    private enum SliceType
 1978    {
 1979        NoSlice,
 1980        ValueSlice,
 1981        ExceptionSlice
 1982    }
 1983
 1984    private abstract class EncapsDecoder
 1985    {
 1986        protected struct PatchEntry
 1987        {
 1988            public PatchEntry(System.Action<Value> cb, int classGraphDepth)
 1989            {
 11990                this.cb = cb;
 11991                this.classGraphDepth = classGraphDepth;
 11992            }
 1993
 1994            public System.Action<Value> cb;
 1995            public int classGraphDepth;
 1996        }
 1997
 11998        internal EncapsDecoder(InputStream stream, Encaps encaps, int classGraphDepthMax)
 1999        {
 12000            _stream = stream;
 12001            _encaps = encaps;
 12002            _classGraphDepthMax = classGraphDepthMax;
 12003            _classGraphDepth = 0;
 12004            _typeIdIndex = 0;
 12005            _unmarshaledMap = new Dictionary<int, Value>();
 12006        }
 2007
 2008        internal abstract void readValue(System.Action<Value?> cb);
 2009
 2010        internal abstract void throwException();
 2011
 2012        internal abstract void startInstance(SliceType type);
 2013
 2014        internal abstract SlicedData? endInstance();
 2015
 2016        internal abstract void startSlice();
 2017
 2018        internal abstract void endSlice();
 2019
 2020        internal abstract void skipSlice();
 2021
 12022        internal virtual bool readOptional(int tag, OptionalFormat format) => false;
 2023
 2024        internal virtual void readPendingValues()
 2025        {
 12026        }
 2027
 2028        protected string readTypeId(bool isIndex)
 2029        {
 12030            _typeIdMap ??= new Dictionary<int, string>();
 2031
 12032            if (isIndex)
 2033            {
 12034                int index = _stream.readSize();
 12035                if (!_typeIdMap.TryGetValue(index, out string? typeId))
 2036                {
 02037                    throw new MarshalException(endOfBufferMessage);
 2038                }
 12039                return typeId;
 2040            }
 2041            else
 2042            {
 12043                string typeId = _stream.readString();
 12044                _typeIdMap.Add(++_typeIdIndex, typeId);
 12045                return typeId;
 2046            }
 2047        }
 2048
 12049        protected Value? newInstance(string typeId) => (Value?)_stream._instance.sliceLoader.newInstance(typeId);
 2050
 2051        protected void addPatchEntry(int index, System.Action<Value> cb)
 2052        {
 2053            Debug.Assert(index > 0);
 2054
 2055            //
 2056            // Check if we already unmarshaled the instance. If that's the case,
 2057            // just call the callback and we're done.
 2058            //
 12059            if (_unmarshaledMap.TryGetValue(index, out Value? obj))
 2060            {
 12061                cb(obj);
 12062                return;
 2063            }
 2064
 12065            _patchMap ??= new Dictionary<int, LinkedList<PatchEntry>>();
 2066
 2067            //
 2068            // Add patch entry if the instance isn't unmarshaled yet,
 2069            // the callback will be called when the instance is
 2070            // unmarshaled.
 2071            //
 12072            if (!_patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2073            {
 2074                //
 2075                // We have no outstanding instances to be patched for this
 2076                // index, so make a new entry in the patch map.
 2077                //
 12078                l = new LinkedList<PatchEntry>();
 12079                _patchMap.Add(index, l);
 2080            }
 2081
 2082            //
 2083            // Append a patch entry for this instance.
 2084            //
 12085            l.AddLast(new PatchEntry(cb, _classGraphDepth));
 12086        }
 2087
 2088        protected void unmarshal(int index, Value v)
 2089        {
 2090            //
 2091            // Add the instance to the map of unmarshaled instances, this must
 2092            // be done before reading the instances (for circular references).
 2093            //
 12094            _unmarshaledMap.Add(index, v);
 2095
 2096            //
 2097            // Read the instance.
 2098            //
 12099            v.iceRead(_stream);
 2100
 12101            if (_patchMap != null)
 2102            {
 2103                //
 2104                // Patch all instances now that the instance is unmarshaled.
 2105                //
 12106                if (_patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2107                {
 2108                    Debug.Assert(l.Count > 0);
 2109
 2110                    //
 2111                    // Patch all pointers that refer to the instance.
 2112                    //
 12113                    foreach (PatchEntry entry in l)
 2114                    {
 12115                        entry.cb(v);
 2116                    }
 2117
 2118                    //
 2119                    // Clear out the patch map for that index -- there is nothing left
 2120                    // to patch for that index for the time being.
 2121                    //
 12122                    _patchMap.Remove(index);
 2123                }
 2124            }
 2125
 12126            if ((_patchMap == null || _patchMap.Count == 0) && _valueList == null)
 2127            {
 12128                v.ice_postUnmarshal();
 2129            }
 2130            else
 2131            {
 12132                _valueList ??= new List<Value>();
 12133                _valueList.Add(v);
 2134
 12135                if (_patchMap == null || _patchMap.Count == 0)
 2136                {
 2137                    //
 2138                    // Iterate over the instance list and invoke ice_postUnmarshal on
 2139                    // each instance. We must do this after all instances have been
 2140                    // unmarshaled in order to ensure that any instance data members
 2141                    // have been properly patched.
 2142                    //
 12143                    foreach (Value p in _valueList)
 2144                    {
 12145                        p.ice_postUnmarshal();
 2146                    }
 12147                    _valueList.Clear();
 2148                }
 2149            }
 12150        }
 2151
 2152        protected readonly InputStream _stream;
 2153        protected readonly Encaps _encaps;
 2154        protected readonly int _classGraphDepthMax;
 2155        protected int _classGraphDepth;
 2156
 2157        //
 2158        // Encapsulation attributes for object unmarshaling.
 2159        //
 2160        protected Dictionary<int, LinkedList<PatchEntry>>? _patchMap;
 2161        private readonly Dictionary<int, Value> _unmarshaledMap;
 2162        private Dictionary<int, string>? _typeIdMap;
 2163        private int _typeIdIndex;
 2164        private List<Value>? _valueList;
 2165    }
 2166
 2167    private sealed class EncapsDecoder10 : EncapsDecoder
 2168    {
 2169        internal EncapsDecoder10(InputStream stream, Encaps encaps, int classGraphDepthMax)
 2170            : base(stream, encaps, classGraphDepthMax) => _sliceType = SliceType.NoSlice;
 2171
 2172        internal override void readValue(System.Action<Value?> cb)
 2173        {
 2174            //
 2175            // Object references are encoded as a negative integer in 1.0.
 2176            //
 2177            int index = _stream.readInt();
 2178            if (index > 0)
 2179            {
 2180                throw new MarshalException("Invalid object id.");
 2181            }
 2182            index = -index;
 2183
 2184            if (index == 0)
 2185            {
 2186                cb(null);
 2187            }
 2188            else
 2189            {
 2190                addPatchEntry(index, cb);
 2191            }
 2192        }
 2193
 2194        internal override void throwException()
 2195        {
 2196            Debug.Assert(_sliceType == SliceType.NoSlice);
 2197
 2198            //
 2199            // User exception with the 1.0 encoding start with a bool flag
 2200            // that indicates whether or not the exception has classes.
 2201            //
 2202            // This allows reading the pending instances even if some part of
 2203            // the exception was sliced.
 2204            //
 2205            bool usesClasses = _stream.readBool();
 2206
 2207            _sliceType = SliceType.ExceptionSlice;
 2208            _skipFirstSlice = false;
 2209
 2210            //
 2211            // Read the first slice header.
 2212            //
 2213            startSlice();
 2214            Debug.Assert(_typeId is not null);
 2215            string mostDerivedId = _typeId;
 2216            while (true)
 2217            {
 2218                UserException? userEx = _stream.createUserException(_typeId);
 2219
 2220                //
 2221                // We found the exception.
 2222                //
 2223                if (userEx != null)
 2224                {
 2225                    userEx.iceRead(_stream);
 2226                    if (usesClasses)
 2227                    {
 2228                        readPendingValues();
 2229                    }
 2230                    throw userEx;
 2231
 2232                    // Never reached.
 2233                }
 2234
 2235                //
 2236                // Slice off what we don't understand.
 2237                //
 2238                skipSlice();
 2239                try
 2240                {
 2241                    startSlice();
 2242                }
 2243                catch (MarshalException)
 2244                {
 2245                    // An oversight in the 1.0 encoding means there is no marker to indicate
 2246                    // the last slice of an exception. As a result, we just try to read the
 2247                    // next type ID, which raises MarshalException when the input buffer underflows.
 2248                    throw new MarshalException($"Unknown exception type '{mostDerivedId}'.");
 2249                }
 2250            }
 2251        }
 2252
 2253        internal override void startInstance(SliceType sliceType)
 2254        {
 2255            Debug.Assert(_sliceType == sliceType);
 2256            _skipFirstSlice = true;
 2257        }
 2258
 2259        internal override SlicedData? endInstance()
 2260        {
 2261            //
 2262            // Read the Ice::Object slice.
 2263            //
 2264            if (_sliceType == SliceType.ValueSlice)
 2265            {
 2266                startSlice();
 2267                int sz = _stream.readSize(); // For compatibility with the old AFM.
 2268                if (sz != 0)
 2269                {
 2270                    throw new MarshalException("Invalid Object slice.");
 2271                }
 2272                endSlice();
 2273            }
 2274
 2275            _sliceType = SliceType.NoSlice;
 2276            return null;
 2277        }
 2278
 2279        internal override void startSlice()
 2280        {
 2281            //
 2282            // If first slice, don't read the header, it was already read in
 2283            // readInstance or throwException to find the factory.
 2284            //
 2285            if (_skipFirstSlice)
 2286            {
 2287                _skipFirstSlice = false;
 2288                return;
 2289            }
 2290
 2291            //
 2292            // For instances, first read the type ID bool which indicates
 2293            // whether or not the type ID is encoded as a string or as an
 2294            // index. For exceptions, the type ID is always encoded as a
 2295            // string.
 2296            //
 2297            if (_sliceType == SliceType.ValueSlice) // For exceptions, the type ID is always encoded as a string
 2298            {
 2299                bool isIndex = _stream.readBool();
 2300                _typeId = readTypeId(isIndex);
 2301            }
 2302            else
 2303            {
 2304                _typeId = _stream.readString();
 2305            }
 2306
 2307            _sliceSize = _stream.readInt();
 2308            if (_sliceSize < 4)
 2309            {
 2310                throw new MarshalException(endOfBufferMessage);
 2311            }
 2312        }
 2313
 2314        internal override void endSlice()
 2315        {
 2316        }
 2317
 2318        internal override void skipSlice()
 2319        {
 2320            if (_stream.instance().traceLevels().slicing > 0)
 2321            {
 2322                Logger logger = _stream.instance().initializationData().logger!;
 2323                string slicingCat = _stream.instance().traceLevels().slicingCat;
 2324                if (_sliceType == SliceType.ValueSlice)
 2325                {
 2326                    Ice.Internal.TraceUtil.traceSlicing("object", _typeId, slicingCat, logger);
 2327                }
 2328                else
 2329                {
 2330                    Ice.Internal.TraceUtil.traceSlicing("exception", _typeId, slicingCat, logger);
 2331                }
 2332            }
 2333
 2334            Debug.Assert(_sliceSize >= 4);
 2335            _stream.skip(_sliceSize - 4);
 2336        }
 2337
 2338        internal override void readPendingValues()
 2339        {
 2340            int num;
 2341            do
 2342            {
 2343                num = _stream.readSize();
 2344                for (int k = num; k > 0; --k)
 2345                {
 2346                    readInstance();
 2347                }
 2348            }
 2349            while (num > 0);
 2350
 2351            if (_patchMap != null && _patchMap.Count > 0)
 2352            {
 2353                //
 2354                // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
 2355                // to supply the instance.
 2356                //
 2357                throw new MarshalException("Index for class received, but no instance.");
 2358            }
 2359        }
 2360
 2361        private void readInstance()
 2362        {
 2363            int index = _stream.readInt();
 2364
 2365            if (index <= 0)
 2366            {
 2367                throw new MarshalException("Invalid object id.");
 2368            }
 2369
 2370            _sliceType = SliceType.ValueSlice;
 2371            _skipFirstSlice = false;
 2372
 2373            //
 2374            // Read the first slice header.
 2375            //
 2376            startSlice();
 2377            Debug.Assert(_typeId is not null);
 2378            string mostDerivedId = _typeId;
 2379            Value? v;
 2380            while (true)
 2381            {
 2382                // For the 1.0 encoding, the type ID for the base Object class marks the last slice.
 2383                if (_typeId == Value.ice_staticId())
 2384                {
 2385                    throw new MarshalException(
 2386                        $"The Slice loader did not find a class for type ID '{mostDerivedId}'.");
 2387                }
 2388
 2389                v = newInstance(_typeId);
 2390
 2391                //
 2392                // We found a factory, we get out of this loop.
 2393                //
 2394                if (v != null)
 2395                {
 2396                    break;
 2397                }
 2398
 2399                //
 2400                // Slice off what we don't understand.
 2401                //
 2402                skipSlice();
 2403                startSlice(); // Read next Slice header for next iteration.
 2404            }
 2405
 2406            //
 2407            // Compute the biggest class graph depth of this object. To compute this,
 2408            // we get the class graph depth of each ancestor from the patch map and
 2409            // keep the biggest one.
 2410            //
 2411            _classGraphDepth = 0;
 2412            if (_patchMap != null && _patchMap.TryGetValue(index, out LinkedList<PatchEntry>? l))
 2413            {
 2414                Debug.Assert(l.Count > 0);
 2415                foreach (PatchEntry entry in l)
 2416                {
 2417                    if (entry.classGraphDepth > _classGraphDepth)
 2418                    {
 2419                        _classGraphDepth = entry.classGraphDepth;
 2420                    }
 2421                }
 2422            }
 2423
 2424            if (++_classGraphDepth > _classGraphDepthMax)
 2425            {
 2426                throw new MarshalException("Maximum class graph depth reached.");
 2427            }
 2428
 2429            //
 2430            // Unmarshal the instance and add it to the map of unmarshaled instances.
 2431            //
 2432            unmarshal(index, v);
 2433        }
 2434
 2435        // Object/exception attributes
 2436        private SliceType _sliceType;
 2437        private bool _skipFirstSlice;
 2438
 2439        // Slice attributes
 2440        private int _sliceSize;
 2441        private string? _typeId;
 2442    }
 2443
 2444    private sealed class EncapsDecoder11 : EncapsDecoder
 2445    {
 2446        internal EncapsDecoder11(InputStream stream, Encaps encaps, int classGraphDepthMax)
 2447            : base(stream, encaps, classGraphDepthMax)
 2448        {
 2449            _current = null;
 2450            _valueIdIndex = 1;
 2451        }
 2452
 2453        internal override void readValue(System.Action<Value?> cb)
 2454        {
 2455            int index = _stream.readSize();
 2456            if (index < 0)
 2457            {
 2458                throw new MarshalException("Invalid object id.");
 2459            }
 2460            else if (index == 0)
 2461            {
 2462                cb(null);
 2463            }
 2464            else if (_current != null && (_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2465            {
 2466                //
 2467                // When reading an instance within a slice and there's an
 2468                // indirect instance table, always read an indirect reference
 2469                // that points to an instance from the indirect instance table
 2470                // marshaled at the end of the Slice.
 2471                //
 2472                // Maintain a list of indirect references. Note that the
 2473                // indirect index starts at 1, so we decrement it by one to
 2474                // derive an index into the indirection table that we'll read
 2475                // at the end of the slice.
 2476                //
 2477                _current.indirectPatchList ??= new Stack<IndirectPatchEntry>();
 2478                var e = new IndirectPatchEntry(index - 1, cb);
 2479                _current.indirectPatchList.Push(e);
 2480            }
 2481            else
 2482            {
 2483                readInstance(index, cb);
 2484            }
 2485        }
 2486
 2487        internal override void throwException()
 2488        {
 2489            Debug.Assert(_current == null);
 2490
 2491            push(SliceType.ExceptionSlice);
 2492
 2493            //
 2494            // Read the first slice header.
 2495            //
 2496            startSlice();
 2497            string mostDerivedId = _current!.typeId!;
 2498            while (true)
 2499            {
 2500                UserException? userEx = _stream.createUserException(_current!.typeId!);
 2501
 2502                //
 2503                // We found the exception.
 2504                //
 2505                if (userEx != null)
 2506                {
 2507                    userEx.iceRead(_stream);
 2508                    throw userEx;
 2509
 2510                    // Never reached.
 2511                }
 2512
 2513                //
 2514                // Slice off what we don't understand.
 2515                //
 2516                skipSlice();
 2517
 2518                if ((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
 2519                {
 2520                    throw new MarshalException($"Cannot unmarshal exception with type ID '{mostDerivedId}'.");
 2521                }
 2522
 2523                startSlice();
 2524            }
 2525        }
 2526
 2527        internal override void startInstance(SliceType sliceType)
 2528        {
 2529            Debug.Assert(_current!.sliceType == sliceType);
 2530            _current.skipFirstSlice = true;
 2531        }
 2532
 2533        internal override SlicedData? endInstance()
 2534        {
 2535            SlicedData? slicedData = readSlicedData();
 2536            if (_current!.slices != null)
 2537            {
 2538                _current.slices.Clear();
 2539            }
 2540            _current.indirectionTables?.Clear();
 2541            _current = _current.previous;
 2542            return slicedData;
 2543        }
 2544
 2545        internal override void startSlice()
 2546        {
 2547            //
 2548            // If first slice, don't read the header, it was already read in
 2549            // readInstance or throwException to find the factory.
 2550            //
 2551            if (_current!.skipFirstSlice)
 2552            {
 2553                _current.skipFirstSlice = false;
 2554                return;
 2555            }
 2556
 2557            _current.sliceFlags = _stream.readByte();
 2558
 2559            //
 2560            // Read the type ID, for instance slices the type ID is encoded as a
 2561            // string or as an index, for exceptions it's always encoded as a
 2562            // string.
 2563            //
 2564            if (_current.sliceType == SliceType.ValueSlice)
 2565            {
 2566                //
 2567                // Must be checked first!
 2568                //
 2569                if ((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_COMPACT) == Protocol.FLAG_HAS_TYPE_ID_COMPACT)
 2570                {
 2571                    _current.compactId = _stream.readSize();
 2572                    _current.typeId = _current.compactId.ToString(CultureInfo.InvariantCulture);
 2573                }
 2574                else if ((_current.sliceFlags &
 2575                        (Protocol.FLAG_HAS_TYPE_ID_INDEX | Protocol.FLAG_HAS_TYPE_ID_STRING)) != 0)
 2576                {
 2577                    _current.typeId = readTypeId((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_INDEX) != 0);
 2578                    _current.compactId = -1;
 2579                }
 2580                else
 2581                {
 2582                    // Only the most derived slice encodes the type ID for the compact format.
 2583                    _current.typeId = "";
 2584                    _current.compactId = -1;
 2585                }
 2586            }
 2587            else
 2588            {
 2589                _current.typeId = _stream.readString();
 2590                _current.compactId = -1;
 2591            }
 2592
 2593            //
 2594            // Read the slice size if necessary.
 2595            //
 2596            if ((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2597            {
 2598                _current.sliceSize = _stream.readInt();
 2599                // A slice with optional members carries at least the 1-byte end marker in its body,
 2600                // so its size (which includes the 4-byte size field) must be >= 5. We rely on this in
 2601                // skipSlice's slice-preservation logic, which excludes the end marker by stepping back
 2602                // one byte.
 2603                int minSliceSize = (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0 ? 5 : 4;
 2604                if (_current.sliceSize < minSliceSize)
 2605                {
 2606                    throw new MarshalException(endOfBufferMessage);
 2607                }
 2608            }
 2609            else
 2610            {
 2611                _current.sliceSize = 0;
 2612            }
 2613        }
 2614
 2615        internal override void endSlice()
 2616        {
 2617            if ((_current!.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
 2618            {
 2619                _stream.skipOptionals();
 2620            }
 2621
 2622            //
 2623            // Read the indirection table if one is present and transform the
 2624            // indirect patch list into patch entries with direct references.
 2625            //
 2626            if ((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2627            {
 2628                //
 2629                // The table is written as a sequence<size> to conserve space.
 2630                //
 2631                int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
 2632                for (int i = 0; i < indirectionTable.Length; ++i)
 2633                {
 2634                    indirectionTable[i] = readInstance(_stream.readSize(), null);
 2635                }
 2636
 2637                //
 2638                // Sanity checks. If there are optional members, it's possible
 2639                // that not all instance references were read if they are from
 2640                // unknown optional data members.
 2641                //
 2642                if (indirectionTable.Length == 0)
 2643                {
 2644                    throw new MarshalException("Empty indirection table.");
 2645                }
 2646                if ((_current.indirectPatchList == null || _current.indirectPatchList.Count == 0) &&
 2647                   (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) == 0)
 2648                {
 2649                    throw new MarshalException("No references to indirection table.");
 2650                }
 2651
 2652                //
 2653                // Convert indirect references into direct references.
 2654                //
 2655                if (_current.indirectPatchList != null)
 2656                {
 2657                    foreach (IndirectPatchEntry e in _current.indirectPatchList)
 2658                    {
 2659                        Debug.Assert(e.index >= 0);
 2660                        if (e.index >= indirectionTable.Length)
 2661                        {
 2662                            throw new MarshalException("Indirection out of range.");
 2663                        }
 2664                        addPatchEntry(indirectionTable[e.index], e.patcher);
 2665                    }
 2666                    _current.indirectPatchList.Clear();
 2667                }
 2668            }
 2669        }
 2670
 2671        internal override void skipSlice()
 2672        {
 2673            if (_stream.instance().traceLevels().slicing > 0)
 2674            {
 2675                Logger logger = _stream.instance().initializationData().logger!;
 2676                string slicingCat = _stream.instance().traceLevels().slicingCat;
 2677                if (_current!.sliceType == SliceType.ExceptionSlice)
 2678                {
 2679                    Ice.Internal.TraceUtil.traceSlicing("exception", _current.typeId, slicingCat, logger);
 2680                }
 2681                else
 2682                {
 2683                    Ice.Internal.TraceUtil.traceSlicing("object", _current.typeId, slicingCat, logger);
 2684                }
 2685            }
 2686
 2687            int start = _stream.pos();
 2688
 2689            if ((_current!.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
 2690            {
 2691                Debug.Assert(_current.sliceSize >= 4);
 2692                _stream.skip(_current.sliceSize - 4);
 2693            }
 2694            else
 2695            {
 2696                if (_current.sliceType == SliceType.ValueSlice)
 2697                {
 2698                    throw new MarshalException(
 2699                        $"The Slice loader did not find a class for type ID '{_current.typeId}' and compact format preve
 2700                }
 2701                else
 2702                {
 2703                    throw new MarshalException(
 2704                        $"The Slice loader did not find a user exception class for type ID '{_current.typeId}' and compa
 2705                }
 2706            }
 2707
 2708            //
 2709            // Preserve this slice if unmarshaling a value in Slice format. Exception slices are not preserved.
 2710            //
 2711            if (_current.sliceType == SliceType.ValueSlice)
 2712            {
 2713                bool hasOptionalMembers = (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0;
 2714
 2715                Ice.Internal.ByteBuffer b = _stream.getBuffer().b;
 2716                int end = b.position();
 2717                int dataEnd = end;
 2718                if (hasOptionalMembers)
 2719                {
 2720                    //
 2721                    // Don't include the optional member end marker. It will be re-written by
 2722                    // endSlice when the sliced data is re-written.
 2723                    //
 2724                    --dataEnd;
 2725                }
 2726                byte[] bytes = new byte[dataEnd - start];
 2727                b.position(start);
 2728                b.get(bytes);
 2729                b.position(end);
 2730
 2731                var info = new SliceInfo(
 2732                    typeId: _current.compactId == -1 ? _current.typeId! : "",
 2733                    compactId: _current.compactId,
 2734                    bytes: bytes,
 2735                    hasOptionalMembers: hasOptionalMembers,
 2736                    isLastSlice: (_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0);
 2737
 2738                _current.slices ??= new List<SliceInfo>();
 2739                _current.slices.Add(info);
 2740            }
 2741
 2742            //
 2743            // Read the indirect instance table. We read the instances or their
 2744            // IDs if the instance is a reference to an already unmarshaled
 2745            // instance.
 2746            //
 2747
 2748            _current.indirectionTables ??= new List<int[]?>();
 2749
 2750            if ((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
 2751            {
 2752                int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
 2753                for (int i = 0; i < indirectionTable.Length; ++i)
 2754                {
 2755                    indirectionTable[i] = readInstance(_stream.readSize(), null);
 2756                }
 2757                _current.indirectionTables.Add(indirectionTable);
 2758            }
 2759            else
 2760            {
 2761                _current.indirectionTables.Add(null);
 2762            }
 2763        }
 2764
 2765        internal override bool readOptional(int readTag, OptionalFormat expectedFormat)
 2766        {
 2767            if (_current == null)
 2768            {
 2769                return _stream.readOptImpl(readTag, expectedFormat);
 2770            }
 2771            else if ((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
 2772            {
 2773                return _stream.readOptImpl(readTag, expectedFormat);
 2774            }
 2775            return false;
 2776        }
 2777
 2778        private int readInstance(int index, System.Action<Value>? cb)
 2779        {
 2780            if (index <= 0)
 2781            {
 2782                throw new MarshalException("invalid class instance index");
 2783            }
 2784
 2785            if (index > 1)
 2786            {
 2787                if (cb != null)
 2788                {
 2789                    addPatchEntry(index, cb);
 2790                }
 2791                return index;
 2792            }
 2793
 2794            push(SliceType.ValueSlice);
 2795
 2796            //
 2797            // Get the instance ID before we start reading slices. If some
 2798            // slices are skipped, the indirect instance table are still read and
 2799            // might read other instances.
 2800            //
 2801            index = ++_valueIdIndex;
 2802
 2803            //
 2804            // Read the first slice header.
 2805            //
 2806            startSlice();
 2807            string mostDerivedId = _current!.typeId!;
 2808            Value? v;
 2809            while (true)
 2810            {
 2811                string typeId = _current.typeId!;
 2812
 2813                if (typeId.Length > 0)
 2814                {
 2815                    v = newInstance(typeId);
 2816                    if (v is not null)
 2817                    {
 2818                        break;
 2819                    }
 2820                }
 2821
 2822                //
 2823                // Slice off what we don't understand.
 2824                //
 2825                skipSlice();
 2826
 2827                //
 2828                // If this is the last slice, keep the instance as an opaque
 2829                // UnknownSlicedValue object.
 2830                //
 2831                if ((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
 2832                {
 2833                    //
 2834                    // Provide a factory with an opportunity to supply the instance.
 2835                    // We pass the "::Ice::Object" ID to indicate that this is the
 2836                    // last chance to preserve the instance.
 2837                    //
 2838                    v = newInstance(Value.ice_staticId());
 2839                    v ??= new UnknownSlicedValue(mostDerivedId);
 2840
 2841                    break;
 2842                }
 2843
 2844                startSlice(); // Read next Slice header for next iteration.
 2845            }
 2846
 2847            if (++_classGraphDepth > _classGraphDepthMax)
 2848            {
 2849                throw new MarshalException("Maximum class graph depth reached.");
 2850            }
 2851
 2852            //
 2853            // Unmarshal the instance.
 2854            //
 2855            unmarshal(index, v);
 2856
 2857            --_classGraphDepth;
 2858
 2859            if (_current == null && _patchMap != null && _patchMap.Count > 0)
 2860            {
 2861                //
 2862                // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
 2863                // to supply the instance.
 2864                //
 2865                throw new MarshalException("Index for class received, but no instance.");
 2866            }
 2867
 2868            cb?.Invoke(v);
 2869            return index;
 2870        }
 2871
 2872        private SlicedData? readSlicedData()
 2873        {
 2874            if (_current!.slices == null) // No preserved slices.
 2875            {
 2876                return null;
 2877            }
 2878
 2879            //
 2880            // The _indirectionTables member holds the indirection table for each slice
 2881            // in _slices.
 2882            //
 2883            Debug.Assert(_current.slices.Count == _current.indirectionTables!.Count);
 2884            for (int n = 0; n < _current.slices.Count; ++n)
 2885            {
 2886                //
 2887                // We use the "instances" list in SliceInfo to hold references
 2888                // to the target instances. Note that the instances might not have
 2889                // been read yet in the case of a circular reference to an
 2890                // enclosing instance.
 2891                //
 2892                int[]? table = _current.indirectionTables[n];
 2893                SliceInfo info = _current.slices[n];
 2894                info.instances = new Value[table != null ? table.Length : 0];
 2895                for (int j = 0; j < info.instances.Length; ++j)
 2896                {
 2897                    int cj = j;
 2898                    addPatchEntry(table![j], (Ice.Value v) => info.instances[cj] = v);
 2899                }
 2900            }
 2901
 2902            return new SlicedData(_current.slices.ToArray());
 2903        }
 2904
 2905        private void push(SliceType sliceType)
 2906        {
 2907            if (_current == null)
 2908            {
 2909                _current = new InstanceData(null);
 2910            }
 2911            else
 2912            {
 2913                _current = _current.next ?? new InstanceData(_current);
 2914            }
 2915            _current.sliceType = sliceType;
 2916            _current.skipFirstSlice = false;
 2917        }
 2918
 2919        private sealed record class IndirectPatchEntry(int index, Action<Value?> patcher);
 2920
 2921        private sealed class InstanceData
 2922        {
 2923            internal InstanceData(InstanceData? previous)
 2924            {
 2925                if (previous != null)
 2926                {
 2927                    previous.next = this;
 2928                }
 2929                this.previous = previous;
 2930                this.next = null;
 2931            }
 2932
 2933            // Instance attributes
 2934            internal SliceType sliceType;
 2935            internal bool skipFirstSlice;
 2936            internal List<SliceInfo>? slices;     // Preserved slices.
 2937            internal List<int[]?>? indirectionTables;
 2938
 2939            // Slice attributes
 2940            internal byte sliceFlags;
 2941            internal int sliceSize;
 2942            internal string? typeId;
 2943            internal int compactId;
 2944            internal Stack<IndirectPatchEntry>? indirectPatchList;
 2945
 2946            internal InstanceData? previous;
 2947            internal InstanceData? next;
 2948        }
 2949
 2950        private InstanceData? _current;
 2951        private int _valueIdIndex; // The ID of the next instance to unmarshal.
 2952    }
 2953
 2954    private sealed class Encaps
 2955    {
 2956        internal void reset() => decoder = null;
 2957
 2958        internal void setEncoding(EncodingVersion encoding)
 2959        {
 2960            this.encoding = encoding;
 2961            encoding_1_0 = encoding.Equals(Util.Encoding_1_0);
 2962        }
 2963
 2964        internal int start;
 2965        internal int sz;
 2966        internal EncodingVersion encoding;
 2967        internal bool encoding_1_0;
 2968
 2969        internal EncapsDecoder? decoder;
 2970
 2971        internal Encaps? next;
 2972    }
 2973
 2974    //
 2975    // The encoding version to use when there's no encapsulation to
 2976    // read from. This is for example used to read message headers.
 2977    //
 2978    private EncodingVersion _encoding;
 2979
 2980    private bool isEncoding_1_0() =>
 2981        _encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0);
 2982
 2983    private Encaps? _encapsStack;
 2984    private Encaps? _encapsCache;
 2985
 2986    private void initEncaps()
 2987    {
 2988        if (_encapsStack == null) // Lazy initialization
 2989        {
 2990            _encapsStack = _encapsCache;
 2991            if (_encapsStack != null)
 2992            {
 2993                _encapsCache = _encapsCache!.next;
 2994                // Detach the reused node from the cache: as the outermost encapsulation it has no next.
 2995                _encapsStack.next = null;
 2996            }
 2997            else
 2998            {
 2999                _encapsStack = new Encaps();
 3000            }
 3001            _encapsStack.setEncoding(_encoding);
 3002            _encapsStack.sz = _buf.b.limit();
 3003        }
 3004
 3005        if (_encapsStack.decoder == null) // Lazy initialization.
 3006        {
 3007            if (_encapsStack.encoding_1_0)
 3008            {
 3009                _encapsStack.decoder =
 3010                    new EncapsDecoder10(this, _encapsStack, _classGraphDepthMax);
 3011            }
 3012            else
 3013            {
 3014                _encapsStack.decoder =
 3015                    new EncapsDecoder11(this, _encapsStack, _classGraphDepthMax);
 3016            }
 3017        }
 3018    }
 3019
 3020    private readonly int _classGraphDepthMax;
 3021
 3022    private int _startSeq = -1;
 3023    private int _minSeqSize;
 3024
 3025    private const string endOfBufferMessage = "Attempting to unmarshal past the end of the buffer.";
 3026}