< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 300
Coverable lines: 300
Total lines: 710
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 78
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobReader.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Reflection.Internal;
 6using System.Reflection.Metadata.Ecma335;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9
 10namespace System.Reflection.Metadata
 11{
 12    [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
 13    public unsafe struct BlobReader
 14    {
 15        internal const int InvalidCompressedInteger = int.MaxValue;
 16
 17        private readonly MemoryBlock _block;
 18
 19        // Points right behind the last byte of the block.
 20        private readonly byte* _endPointer;
 21
 22        private byte* _currentPointer;
 23
 24        /// <summary>
 25        /// Creates a reader of the specified memory block.
 26        /// </summary>
 27        /// <param name="buffer">Pointer to the start of the memory block.</param>
 28        /// <param name="length">Length in bytes of the memory block.</param>
 29        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null and <paramref name="length"/> is g
 30        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is negative.</exception>
 31        /// <exception cref="PlatformNotSupportedException">The current platform is not little-endian.</exception>
 32        public BlobReader(byte* buffer, int length)
 033            : this(MemoryBlock.CreateChecked(buffer, length))
 034        {
 35
 036        }
 37
 38        internal BlobReader(MemoryBlock block)
 039        {
 040            Debug.Assert(block.Length >= 0 && (block.Pointer != null || block.Length == 0));
 041            _block = block;
 042            _currentPointer = block.Pointer;
 043            _endPointer = block.Pointer + block.Length;
 044        }
 45
 46        internal string GetDebuggerDisplay()
 047        {
 048            if (_block.Pointer == null)
 049            {
 050                return "<null>";
 51            }
 52
 53            int displayedBytes;
 054            string display = _block.GetDebuggerDisplay(out displayedBytes);
 055            if (this.Offset < displayedBytes)
 056            {
 057                display = display.Insert(this.Offset * 3, "*");
 058            }
 059            else if (displayedBytes == _block.Length)
 060            {
 061                display += "*";
 062            }
 63            else
 064            {
 065                display += "*...";
 066            }
 67
 068            return display;
 069        }
 70
 71        #region Offset, Skipping, Marking, Alignment, Bounds Checking
 72
 73        /// <summary>
 74        /// Pointer to the byte at the start of the underlying memory block.
 75        /// </summary>
 076        public byte* StartPointer => _block.Pointer;
 77
 78        /// <summary>
 79        /// Pointer to the byte at the current position of the reader.
 80        /// </summary>
 081        public byte* CurrentPointer => _currentPointer;
 82
 83        /// <summary>
 84        /// The total length of the underlying memory block.
 85        /// </summary>
 086        public int Length => _block.Length;
 87
 88        /// <summary>
 89        /// Gets or sets the offset from start of the blob to the current position.
 90        /// </summary>
 91        /// <exception cref="BadImageFormatException">Offset is set outside the bounds of underlying reader.</exception>
 92        public int Offset
 93        {
 94            get
 095            {
 096                return (int)(_currentPointer - _block.Pointer);
 097            }
 98            set
 099            {
 0100                if (unchecked((uint)value) > (uint)_block.Length)
 0101                {
 0102                    Throw.OutOfBounds();
 103                }
 104
 0105                _currentPointer = _block.Pointer + value;
 0106            }
 107        }
 108
 109        /// <summary>
 110        /// Bytes remaining from current position to end of underlying memory block.
 111        /// </summary>
 0112        public int RemainingBytes => (int)(_endPointer - _currentPointer);
 113
 114        /// <summary>
 115        /// Repositions the reader to the start of the underlying memory block.
 116        /// </summary>
 117        public void Reset()
 0118        {
 0119            _currentPointer = _block.Pointer;
 0120        }
 121
 122        /// <summary>
 123        /// Repositions the reader forward by the number of bytes required to satisfy the given alignment.
 124        /// </summary>
 125        public void Align(byte alignment)
 0126        {
 0127            if (!TryAlign(alignment))
 0128            {
 0129                Throw.OutOfBounds();
 130            }
 0131        }
 132
 133        internal bool TryAlign(byte alignment)
 0134        {
 0135            int remainder = this.Offset & (alignment - 1);
 136
 0137            Debug.Assert((alignment & (alignment - 1)) == 0, "Alignment must be a power of two.");
 0138            Debug.Assert(remainder >= 0 && remainder < alignment);
 139
 0140            if (remainder != 0)
 0141            {
 0142                int bytesToSkip = alignment - remainder;
 0143                if (bytesToSkip > RemainingBytes)
 0144                {
 0145                    return false;
 146                }
 147
 0148                _currentPointer += bytesToSkip;
 0149            }
 150
 0151            return true;
 0152        }
 153
 154        internal MemoryBlock GetMemoryBlockAt(int offset, int length)
 0155        {
 0156            CheckBounds(offset, length);
 0157            return new MemoryBlock(_currentPointer + offset, length);
 0158        }
 159
 160        #endregion
 161
 162        #region Bounds Checking
 163
 164        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 165        private void CheckBounds(int offset, int byteCount)
 0166        {
 0167            if (unchecked((ulong)(uint)offset + (uint)byteCount) > (ulong)(_endPointer - _currentPointer))
 0168            {
 0169                Throw.OutOfBounds();
 170            }
 0171        }
 172
 173        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 174        private void CheckBounds(int byteCount)
 175        {
 176            if (unchecked((uint)byteCount) > (_endPointer - _currentPointer))
 177            {
 178                Throw.OutOfBounds();
 179            }
 180        }
 181
 182        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 183        private byte* GetCurrentPointerAndAdvance(int length)
 0184        {
 0185            byte* p = _currentPointer;
 186
 0187            if (unchecked((uint)length) > (uint)(_endPointer - p))
 0188            {
 0189                Throw.OutOfBounds();
 190            }
 191
 0192            _currentPointer = p + length;
 0193            return p;
 0194        }
 195
 196        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 197        private byte* GetCurrentPointerAndAdvance1()
 0198        {
 0199            byte* p = _currentPointer;
 200
 0201            if (p == _endPointer)
 0202            {
 0203                Throw.OutOfBounds();
 204            }
 205
 0206            _currentPointer = p + 1;
 0207            return p;
 0208        }
 209
 210        #endregion
 211
 212        #region Read Methods
 213
 214        public bool ReadBoolean()
 0215        {
 216            // It's not clear from the ECMA spec what exactly is the encoding of Boolean.
 217            // Some metadata writers encode "true" as 0xff, others as 1. So we treat all non-zero values as "true".
 218            //
 219            // We propose to clarify and relax the current wording in the spec as follows:
 220            //
 221            // Chapter II.16.2 "Field init metadata"
 222            //   ... bool '(' true | false ')' Boolean value stored in a single byte, 0 represents false, any non-zero v
 223            //
 224            // Chapter 23.3 "Custom attributes"
 225            //   ... A bool is a single byte with value 0 representing false and any non-zero value representing true ..
 0226            return ReadByte() != 0;
 0227        }
 228
 229        public sbyte ReadSByte()
 0230        {
 0231            return *(sbyte*)GetCurrentPointerAndAdvance1();
 0232        }
 233
 234        public byte ReadByte()
 0235        {
 0236            return *(byte*)GetCurrentPointerAndAdvance1();
 0237        }
 238
 239        public char ReadChar()
 0240        {
 241            unchecked
 0242            {
 0243                byte* ptr = GetCurrentPointerAndAdvance(sizeof(char));
 0244                return (char)(ptr[0] + (ptr[1] << 8));
 245            }
 0246        }
 247
 248        public short ReadInt16()
 0249        {
 250            unchecked
 0251            {
 0252                byte* ptr = GetCurrentPointerAndAdvance(sizeof(short));
 0253                return (short)(ptr[0] + (ptr[1] << 8));
 254            }
 0255        }
 256
 257        public ushort ReadUInt16()
 0258        {
 259            unchecked
 0260            {
 0261                byte* ptr = GetCurrentPointerAndAdvance(sizeof(ushort));
 0262                return (ushort)(ptr[0] + (ptr[1] << 8));
 263            }
 0264        }
 265
 266        public int ReadInt32()
 0267        {
 268            unchecked
 0269            {
 0270                byte* ptr = GetCurrentPointerAndAdvance(sizeof(int));
 0271                return (int)(ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24));
 272            }
 0273        }
 274
 275        public uint ReadUInt32()
 0276        {
 277            unchecked
 0278            {
 0279                byte* ptr = GetCurrentPointerAndAdvance(sizeof(uint));
 0280                return (uint)(ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24));
 281            }
 0282        }
 283
 284        public long ReadInt64()
 0285        {
 286            unchecked
 0287            {
 0288                byte* ptr = GetCurrentPointerAndAdvance(sizeof(long));
 0289                uint lo = (uint)(ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24));
 0290                uint hi = (uint)(ptr[4] + (ptr[5] << 8) + (ptr[6] << 16) + (ptr[7] << 24));
 0291                return (long)(lo + ((ulong)hi << 32));
 292            }
 0293        }
 294
 295        public ulong ReadUInt64()
 0296        {
 0297            return unchecked((ulong)ReadInt64());
 0298        }
 299
 300        public float ReadSingle()
 0301        {
 0302            int val = ReadInt32();
 0303            return *(float*)&val;
 0304        }
 305
 306        public double ReadDouble()
 0307        {
 0308            long val = ReadInt64();
 0309            return *(double*)&val;
 0310        }
 311
 312        public Guid ReadGuid()
 0313        {
 314            const int size = 16;
 0315            byte* ptr = GetCurrentPointerAndAdvance(size);
 0316            if (BitConverter.IsLittleEndian)
 0317            {
 0318                return *(Guid*)ptr;
 319            }
 320            else
 0321            {
 322                unchecked
 0323                {
 0324                    return new Guid(
 0325                        (int)(ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)),
 0326                        (short)(ptr[4] | (ptr[5] << 8)),
 0327                        (short)(ptr[6] | (ptr[7] << 8)),
 0328                        ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
 329                }
 330            }
 0331        }
 332
 333        /// <summary>
 334        /// Reads <see cref="decimal"/> number.
 335        /// </summary>
 336        /// <remarks>
 337        /// Decimal number is encoded in 13 bytes as follows:
 338        /// - byte 0: highest bit indicates sign (1 for negative, 0 for non-negative); the remaining 7 bits encode scale
 339        /// - bytes 1..12: 96-bit unsigned integer in little endian encoding.
 340        /// </remarks>
 341        /// <exception cref="BadImageFormatException">The data at the current position was not a valid <see cref="decima
 342        public decimal ReadDecimal()
 0343        {
 0344            byte* ptr = GetCurrentPointerAndAdvance(13);
 345
 0346            byte scale = (byte)(*ptr & 0x7f);
 0347            if (scale > 28)
 0348            {
 0349                throw new BadImageFormatException(SR.ValueTooLarge);
 350            }
 351
 352            unchecked
 0353            {
 0354                return new decimal(
 0355                    (int)(ptr[1] | (ptr[2] << 8) | (ptr[3] << 16) | (ptr[4] << 24)),
 0356                    (int)(ptr[5] | (ptr[6] << 8) | (ptr[7] << 16) | (ptr[8] << 24)),
 0357                    (int)(ptr[9] | (ptr[10] << 8) | (ptr[11] << 16) | (ptr[12] << 24)),
 0358                    isNegative: (*ptr & 0x80) != 0,
 0359                    scale: scale);
 360            }
 0361        }
 362
 363        public DateTime ReadDateTime()
 0364        {
 0365            return new DateTime(ReadInt64());
 0366        }
 367
 368        public SignatureHeader ReadSignatureHeader()
 0369        {
 0370            return new SignatureHeader(ReadByte());
 0371        }
 372
 373        /// <summary>
 374        /// Finds specified byte in the blob following the current position.
 375        /// </summary>
 376        /// <returns>
 377        /// Index relative to the current position, or -1 if the byte is not found in the blob following the current pos
 378        /// </returns>
 379        /// <remarks>
 380        /// Doesn't change the current position.
 381        /// </remarks>
 382        public int IndexOf(byte value)
 0383        {
 0384            int start = Offset;
 0385            int absoluteIndex = _block.IndexOfUnchecked(value, start);
 0386            return (absoluteIndex >= 0) ? absoluteIndex - start : -1;
 0387        }
 388
 389        /// <summary>
 390        /// Reads UTF-8 encoded string starting at the current position.
 391        /// </summary>
 392        /// <param name="byteCount">The number of bytes to read.</param>
 393        /// <returns>The string.</returns>
 394        /// <exception cref="BadImageFormatException"><paramref name="byteCount"/> bytes not available.</exception>
 395        public string ReadUTF8(int byteCount)
 0396        {
 0397            string s = _block.PeekUtf8(this.Offset, byteCount);
 0398            _currentPointer += byteCount;
 0399            return s;
 0400        }
 401
 402        /// <summary>
 403        /// Reads UTF-16 (little-endian) encoded string starting at the current position.
 404        /// </summary>
 405        /// <param name="byteCount">The number of bytes to read.</param>
 406        /// <returns>The string.</returns>
 407        /// <exception cref="BadImageFormatException"><paramref name="byteCount"/> bytes not available.</exception>
 408        public string ReadUTF16(int byteCount)
 0409        {
 0410            string s = _block.PeekUtf16(this.Offset, byteCount);
 0411            _currentPointer += byteCount;
 0412            return s;
 0413        }
 414
 415        /// <summary>
 416        /// Reads bytes starting at the current position.
 417        /// </summary>
 418        /// <param name="byteCount">The number of bytes to read.</param>
 419        /// <returns>The byte array.</returns>
 420        /// <exception cref="BadImageFormatException"><paramref name="byteCount"/> bytes not available.</exception>
 421        public byte[] ReadBytes(int byteCount)
 0422        {
 0423            byte[] bytes = _block.PeekBytes(this.Offset, byteCount);
 0424            _currentPointer += byteCount;
 0425            return bytes;
 0426        }
 427
 428        /// <summary>
 429        /// Reads bytes starting at the current position in to the given buffer at the given offset;
 430        /// </summary>
 431        /// <param name="byteCount">The number of bytes to read.</param>
 432        /// <param name="buffer">The destination buffer the bytes read will be written.</param>
 433        /// <param name="bufferOffset">The offset in the destination buffer where the bytes read will be written.</param
 434        /// <exception cref="BadImageFormatException"><paramref name="byteCount"/> bytes not available.</exception>
 435        public void ReadBytes(int byteCount, byte[] buffer, int bufferOffset)
 0436        {
 0437            Marshal.Copy((IntPtr)GetCurrentPointerAndAdvance(byteCount), buffer, bufferOffset, byteCount);
 0438        }
 439
 440        internal string ReadUtf8NullTerminated()
 0441        {
 442            int bytesRead;
 0443            string value = _block.PeekUtf8NullTerminated(this.Offset, null, MetadataStringDecoder.DefaultUTF8, out bytes
 0444            _currentPointer += bytesRead;
 0445            return value;
 0446        }
 447
 448        private int ReadCompressedIntegerOrInvalid()
 0449        {
 450            int bytesRead;
 0451            int value = _block.PeekCompressedInteger(this.Offset, out bytesRead);
 0452            _currentPointer += bytesRead;
 0453            return value;
 0454        }
 455
 456        /// <summary>
 457        /// Reads an unsigned compressed integer value.
 458        /// See Metadata Specification section II.23.2: Blobs and signatures.
 459        /// </summary>
 460        /// <param name="value">The value of the compressed integer that was read.</param>
 461        /// <returns>true if the value was read successfully. false if the data at the current position was not a valid 
 462        public bool TryReadCompressedInteger(out int value)
 0463        {
 0464            value = ReadCompressedIntegerOrInvalid();
 0465            return value != InvalidCompressedInteger;
 0466        }
 467
 468        /// <summary>
 469        /// Reads an unsigned compressed integer value.
 470        /// See Metadata Specification section II.23.2: Blobs and signatures.
 471        /// </summary>
 472        /// <returns>The value of the compressed integer that was read.</returns>
 473        /// <exception cref="BadImageFormatException">The data at the current position was not a valid compressed intege
 474        public int ReadCompressedInteger()
 0475        {
 476            int value;
 0477            if (!TryReadCompressedInteger(out value))
 0478            {
 0479                Throw.InvalidCompressedInteger();
 480            }
 0481            return value;
 0482        }
 483
 484        /// <summary>
 485        /// Reads a signed compressed integer value.
 486        /// See Metadata Specification section II.23.2: Blobs and signatures.
 487        /// </summary>
 488        /// <param name="value">The value of the compressed integer that was read.</param>
 489        /// <returns>true if the value was read successfully. false if the data at the current position was not a valid 
 490        public bool TryReadCompressedSignedInteger(out int value)
 0491        {
 492            int bytesRead;
 0493            value = _block.PeekCompressedInteger(this.Offset, out bytesRead);
 494
 0495            if (value == InvalidCompressedInteger)
 0496            {
 0497                return false;
 498            }
 499
 0500            bool signExtend = (value & 0x1) != 0;
 0501            value >>= 1;
 502
 0503            if (signExtend)
 0504            {
 0505                switch (bytesRead)
 506                {
 507                    case 1:
 0508                        value |= unchecked((int)0xffffffc0);
 0509                        break;
 510                    case 2:
 0511                        value |= unchecked((int)0xffffe000);
 0512                        break;
 513                    default:
 0514                        Debug.Assert(bytesRead == 4);
 0515                        value |= unchecked((int)0xf0000000);
 0516                        break;
 517                }
 0518            }
 519
 0520            _currentPointer += bytesRead;
 0521            return true;
 0522        }
 523
 524        /// <summary>
 525        /// Reads a signed compressed integer value.
 526        /// See Metadata Specification section II.23.2: Blobs and signatures.
 527        /// </summary>
 528        /// <returns>The value of the compressed integer that was read.</returns>
 529        /// <exception cref="BadImageFormatException">The data at the current position was not a valid compressed intege
 530        public int ReadCompressedSignedInteger()
 0531        {
 532            int value;
 0533            if (!TryReadCompressedSignedInteger(out value))
 0534            {
 0535                Throw.InvalidCompressedInteger();
 536            }
 0537            return value;
 0538        }
 539
 540        /// <summary>
 541        /// Reads type code encoded in a serialized custom attribute value.
 542        /// </summary>
 543        /// <returns><see cref="SerializationTypeCode.Invalid"/> if the encoding is invalid.</returns>
 544        public SerializationTypeCode ReadSerializationTypeCode()
 0545        {
 0546            int value = ReadCompressedIntegerOrInvalid();
 0547            if (value > byte.MaxValue)
 0548            {
 0549                return SerializationTypeCode.Invalid;
 550            }
 551
 0552            return unchecked((SerializationTypeCode)value);
 0553        }
 554
 555        /// <summary>
 556        /// Reads type code encoded in a signature.
 557        /// </summary>
 558        /// <returns><see cref="SignatureTypeCode.Invalid"/> if the encoding is invalid.</returns>
 559        public SignatureTypeCode ReadSignatureTypeCode()
 0560        {
 0561            int value = ReadCompressedIntegerOrInvalid();
 562
 0563            switch (value)
 564            {
 565                case (int)CorElementType.ELEMENT_TYPE_CLASS:
 566                case (int)CorElementType.ELEMENT_TYPE_VALUETYPE:
 0567                    return SignatureTypeCode.TypeHandle;
 568
 569                default:
 0570                    if (value > byte.MaxValue)
 0571                    {
 0572                        return SignatureTypeCode.Invalid;
 573                    }
 574
 0575                    return unchecked((SignatureTypeCode)value);
 576            }
 0577        }
 578
 579        /// <summary>
 580        /// Reads a string encoded as a compressed integer containing its length followed by
 581        /// its contents in UTF-8. Null strings are encoded as a single 0xFF byte.
 582        /// </summary>
 583        /// <remarks>Defined as a 'SerString' in the ECMA CLI specification.</remarks>
 584        /// <returns>String value or null.</returns>
 585        /// <exception cref="BadImageFormatException">If the encoding is invalid.</exception>
 586        public string? ReadSerializedString()
 0587        {
 588            int length;
 0589            if (TryReadCompressedInteger(out length))
 0590            {
 0591                return ReadUTF8(length);
 592            }
 593
 0594            if (ReadByte() != 0xFF)
 0595            {
 0596                Throw.InvalidSerializedString();
 597            }
 598
 0599            return null;
 0600        }
 601
 602        /// <summary>
 603        /// Reads a type handle encoded in a signature as TypeDefOrRefOrSpecEncoded (see ECMA-335 II.23.2.8).
 604        /// </summary>
 605        /// <returns>The handle or nil if the encoding is invalid.</returns>
 606        public EntityHandle ReadTypeHandle()
 0607        {
 0608            uint value = (uint)ReadCompressedIntegerOrInvalid();
 0609            uint tokenType = CorEncodeTokenArray[(int)(value & 0x3)];
 610
 0611            if (value == InvalidCompressedInteger || tokenType == 0)
 0612            {
 0613                return default(EntityHandle);
 614            }
 615
 0616            return new EntityHandle(tokenType | (value >> 2));
 0617        }
 618
 0619        private static ReadOnlySpan<uint> CorEncodeTokenArray => [TokenTypeIds.TypeDef, TokenTypeIds.TypeRef, TokenTypeI
 620
 621        /// <summary>
 622        /// Reads a #Blob heap handle encoded as a compressed integer.
 623        /// </summary>
 624        /// <remarks>
 625        /// Blobs that contain references to other blobs are used in Portable PDB format, for example <see cref="Documen
 626        /// </remarks>
 627        public BlobHandle ReadBlobHandle()
 0628        {
 0629            return BlobHandle.FromOffset(ReadCompressedInteger());
 0630        }
 631
 632        /// <summary>
 633        /// Reads a constant value (see ECMA-335 Partition II section 22.9) from the current position.
 634        /// </summary>
 635        /// <exception cref="BadImageFormatException">Error while reading from the blob.</exception>
 636        /// <exception cref="ArgumentOutOfRangeException"><paramref name="typeCode"/> is not a valid <see cref="Constant
 637        /// <returns>
 638        /// Boxed constant value. To avoid allocating the object use Read* methods directly.
 639        /// Constants of type <see cref="ConstantTypeCode.String"/> are encoded as UTF16 strings, use <see cref="ReadUTF
 640        /// </returns>
 641        public object? ReadConstant(ConstantTypeCode typeCode)
 0642        {
 643            // Partition II section 22.9:
 644            //
 645            // Type shall be exactly one of: ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1,
 646            // ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4,
 647            // ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, or ELEMENT_TYPE_STRING;
 648            // or ELEMENT_TYPE_CLASS with a Value of zero  (23.1.16)
 649
 0650            switch (typeCode)
 651            {
 652                case ConstantTypeCode.Boolean:
 0653                    return ReadBoolean();
 654
 655                case ConstantTypeCode.Char:
 0656                    return ReadChar();
 657
 658                case ConstantTypeCode.SByte:
 0659                    return ReadSByte();
 660
 661                case ConstantTypeCode.Int16:
 0662                    return ReadInt16();
 663
 664                case ConstantTypeCode.Int32:
 0665                    return ReadInt32();
 666
 667                case ConstantTypeCode.Int64:
 0668                    return ReadInt64();
 669
 670                case ConstantTypeCode.Byte:
 0671                    return ReadByte();
 672
 673                case ConstantTypeCode.UInt16:
 0674                    return ReadUInt16();
 675
 676                case ConstantTypeCode.UInt32:
 0677                    return ReadUInt32();
 678
 679                case ConstantTypeCode.UInt64:
 0680                    return ReadUInt64();
 681
 682                case ConstantTypeCode.Single:
 0683                    return ReadSingle();
 684
 685                case ConstantTypeCode.Double:
 0686                    return ReadDouble();
 687
 688                case ConstantTypeCode.String:
 0689                    return ReadUTF16(RemainingBytes);
 690
 691                case ConstantTypeCode.NullReference:
 692                    // Partition II section 22.9:
 693                    // The encoding of Type for the nullref value is ELEMENT_TYPE_CLASS with a Value of a 4-byte zero.
 694                    // Unlike uses of ELEMENT_TYPE_CLASS in signatures, this one is not followed by a type token.
 0695                    if (ReadUInt32() != 0)
 0696                    {
 0697                        throw new BadImageFormatException(SR.InvalidConstantValue);
 698                    }
 699
 0700                    return null;
 701
 702                default:
 0703                    throw new ArgumentOutOfRangeException(nameof(typeCode));
 704            }
 0705        }
 706
 707        #endregion
 708    }
 709}
 710