< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 501
Coverable lines: 501
Total lines: 868
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 188
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/Internal/Utilities/MemoryBlock.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.Buffers.Binary;
 5using System.Diagnostics;
 6using System.Reflection.Metadata;
 7using System.Reflection.Metadata.Ecma335;
 8using System.Runtime.CompilerServices;
 9using System.Text;
 10
 11namespace System.Reflection.Internal
 12{
 13    [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
 14    internal readonly unsafe struct MemoryBlock
 15    {
 16        internal readonly byte* Pointer;
 17        internal readonly int Length;
 18
 19        internal MemoryBlock(byte* buffer, int length)
 020        {
 021            Debug.Assert(length >= 0 && (buffer != null || length == 0));
 022            this.Pointer = buffer;
 023            this.Length = length;
 024        }
 25
 26        internal static MemoryBlock CreateChecked(byte* buffer, int length)
 027        {
 028            if (length < 0)
 029            {
 030                throw new ArgumentOutOfRangeException(nameof(length));
 31            }
 32
 033            if (buffer == null && length != 0)
 034            {
 035                Throw.ArgumentNull(nameof(buffer));
 36            }
 37
 038            return new MemoryBlock(buffer, length);
 039        }
 40
 41        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 42        private void CheckBounds(int offset, int byteCount)
 043        {
 044            if (unchecked((ulong)(uint)offset + (uint)byteCount) > (ulong)Length)
 045            {
 046                Throw.OutOfBounds();
 47            }
 048        }
 49
 50        internal byte[]? ToArray()
 051        {
 052            return Pointer == null ? null : PeekBytes(0, Length);
 053        }
 54
 55        private string GetDebuggerDisplay()
 056        {
 057            if (Pointer == null)
 058            {
 059                return "<null>";
 60            }
 61
 062            return GetDebuggerDisplay(out _);
 063        }
 64
 65        internal string GetDebuggerDisplay(out int displayedBytes)
 066        {
 067            displayedBytes = Math.Min(Length, 64);
 068            string result = BitConverter.ToString(PeekBytes(0, displayedBytes));
 069            if (displayedBytes < Length)
 070            {
 071                result += "-...";
 072            }
 73
 074            return result;
 075        }
 76
 77        internal string GetDebuggerDisplay(int offset)
 078        {
 079            if (Pointer == null)
 080            {
 081                return "<null>";
 82            }
 83
 84            int displayedBytes;
 085            string display = GetDebuggerDisplay(out displayedBytes);
 086            if (offset < displayedBytes)
 087            {
 088                display = display.Insert(offset * 3, "*");
 089            }
 090            else if (displayedBytes == Length)
 091            {
 092                display += "*";
 093            }
 94            else
 095            {
 096                display += "*...";
 097            }
 98
 099            return display;
 0100        }
 101
 102        internal MemoryBlock GetMemoryBlockAt(int offset, int length)
 0103        {
 0104            CheckBounds(offset, length);
 0105            return new MemoryBlock(Pointer + offset, length);
 0106        }
 107
 108        internal byte PeekByte(int offset)
 0109        {
 0110            CheckBounds(offset, sizeof(byte));
 0111            return Pointer[offset];
 0112        }
 113
 114        internal int PeekInt32(int offset)
 0115        {
 0116            uint result = PeekUInt32(offset);
 0117            if (unchecked((int)result != result))
 0118            {
 0119                Throw.ValueOverflow();
 120            }
 121
 0122            return (int)result;
 0123        }
 124
 125        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 126        internal uint PeekUInt32(int offset)
 0127        {
 0128            CheckBounds(offset, sizeof(uint));
 129
 0130            uint result = Unsafe.ReadUnaligned<uint>(Pointer + offset);
 0131            return BitConverter.IsLittleEndian ? result : BinaryPrimitives.ReverseEndianness(result);
 0132        }
 133
 134        /// <summary>
 135        /// Decodes a compressed integer value starting at offset.
 136        /// See Metadata Specification section II.23.2: Blobs and signatures.
 137        /// </summary>
 138        /// <param name="offset">Offset to the start of the compressed data.</param>
 139        /// <param name="numberOfBytesRead">Bytes actually read.</param>
 140        /// <returns>
 141        /// Value between 0 and 0x1fffffff, or <see cref="BlobReader.InvalidCompressedInteger"/> if the value encoding i
 142        /// </returns>
 143        internal int PeekCompressedInteger(int offset, out int numberOfBytesRead)
 0144        {
 0145            CheckBounds(offset, 0);
 146
 0147            byte* ptr = Pointer + offset;
 0148            long limit = Length - offset;
 149
 0150            if (limit == 0)
 0151            {
 0152                numberOfBytesRead = 0;
 0153                return BlobReader.InvalidCompressedInteger;
 154            }
 155
 0156            byte headerByte = ptr[0];
 0157            if ((headerByte & 0x80) == 0)
 0158            {
 0159                numberOfBytesRead = 1;
 0160                return headerByte;
 161            }
 0162            else if ((headerByte & 0x40) == 0)
 0163            {
 0164                if (limit >= 2)
 0165                {
 0166                    numberOfBytesRead = 2;
 0167                    return ((headerByte & 0x3f) << 8) | ptr[1];
 168                }
 0169            }
 0170            else if ((headerByte & 0x20) == 0)
 0171            {
 0172                if (limit >= 4)
 0173                {
 0174                    numberOfBytesRead = 4;
 0175                    return ((headerByte & 0x1f) << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
 176                }
 0177            }
 178
 0179            numberOfBytesRead = 0;
 0180            return BlobReader.InvalidCompressedInteger;
 0181        }
 182
 183        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 184        internal ushort PeekUInt16(int offset)
 0185        {
 0186            CheckBounds(offset, sizeof(ushort));
 187
 0188            ushort result = Unsafe.ReadUnaligned<ushort>(Pointer + offset);
 0189            return BitConverter.IsLittleEndian ? result : BinaryPrimitives.ReverseEndianness(result);
 0190        }
 191
 192        // When reference has tag bits.
 193        internal uint PeekTaggedReference(int offset, bool smallRefSize)
 0194        {
 0195            return PeekReferenceUnchecked(offset, smallRefSize);
 0196        }
 197
 198        // Use when searching for a tagged or non-tagged reference.
 199        // The result may be an invalid reference and shall only be used to compare with a valid reference.
 200        internal uint PeekReferenceUnchecked(int offset, bool smallRefSize)
 0201        {
 0202            return smallRefSize ? PeekUInt16(offset) : PeekUInt32(offset);
 0203        }
 204
 205        // When reference has at most 24 bits.
 206        internal int PeekReference(int offset, bool smallRefSize)
 0207        {
 0208            if (smallRefSize)
 0209            {
 0210                return PeekUInt16(offset);
 211            }
 212
 0213            uint value = PeekUInt32(offset);
 214
 0215            if (!TokenTypeIds.IsValidRowId(value))
 0216            {
 0217                Throw.ReferenceOverflow();
 218            }
 219
 0220            return (int)value;
 0221        }
 222
 223        // #String, #Blob heaps
 224        internal int PeekHeapReference(int offset, bool smallRefSize)
 0225        {
 0226            if (smallRefSize)
 0227            {
 0228                return PeekUInt16(offset);
 229            }
 230
 0231            uint value = PeekUInt32(offset);
 232
 0233            if (!HeapHandleType.IsValidHeapOffset(value))
 0234            {
 0235                Throw.ReferenceOverflow();
 236            }
 237
 0238            return (int)value;
 0239        }
 240
 241        internal Guid PeekGuid(int offset)
 0242        {
 0243            CheckBounds(offset, sizeof(Guid));
 244
 0245            byte* ptr = Pointer + offset;
 0246            if (BitConverter.IsLittleEndian)
 0247            {
 0248                return Unsafe.ReadUnaligned<Guid>(ptr);
 249            }
 250            else
 0251            {
 252                unchecked
 0253                {
 0254                    return new Guid(
 0255                        (int)(ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)),
 0256                        (short)(ptr[4] | (ptr[5] << 8)),
 0257                        (short)(ptr[6] | (ptr[7] << 8)),
 0258                        ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
 259                }
 260            }
 0261        }
 262
 263        internal string PeekUtf16(int offset, int byteCount)
 0264        {
 0265            CheckBounds(offset, byteCount);
 266
 0267            byte* ptr = Pointer + offset;
 0268            if (BitConverter.IsLittleEndian)
 0269            {
 270                // doesn't allocate a new string if byteCount == 0
 0271                return new string((char*)ptr, 0, byteCount / sizeof(char));
 272            }
 273            else
 0274            {
 0275                return Encoding.Unicode.GetString(ptr, byteCount);
 276            }
 0277        }
 278
 279        internal string PeekUtf8(int offset, int byteCount)
 0280        {
 0281            CheckBounds(offset, byteCount);
 0282            return Encoding.UTF8.GetString(Pointer + offset, byteCount);
 0283        }
 284
 285        /// <summary>
 286        /// Read UTF-8 at the given offset up to the given terminator, null terminator, or end-of-block.
 287        /// </summary>
 288        /// <param name="offset">Offset in to the block where the UTF-8 bytes start.</param>
 289        /// <param name="prefix">UTF-8 encoded prefix to prepend to the bytes at the offset before decoding.</param>
 290        /// <param name="utf8Decoder">The UTF-8 decoder to use that allows user to adjust fallback and/or reuse existing
 291        /// <param name="numberOfBytesRead">The number of bytes read, which includes the terminator if we did not hit th
 292        /// <param name="terminator">A character in the ASCII range that marks the end of the string.
 293        /// If a value other than '\0' is passed we still stop at the null terminator if encountered first.</param>
 294        /// <returns>The decoded string.</returns>
 295        internal string PeekUtf8NullTerminated(int offset, byte[]? prefix, MetadataStringDecoder utf8Decoder, out int nu
 0296        {
 0297            Debug.Assert(terminator <= 0x7F);
 0298            CheckBounds(offset, 0);
 0299            int length = GetUtf8NullTerminatedLength(offset, out numberOfBytesRead, terminator);
 0300            return EncodingHelper.DecodeUtf8(Pointer + offset, length, prefix, utf8Decoder);
 0301        }
 302
 303        /// <summary>
 304        /// Get number of bytes from offset to given terminator, null terminator, or end-of-block (whichever comes first
 305        /// Returned length does not include the terminator, but numberOfBytesRead out parameter does.
 306        /// </summary>
 307        /// <param name="offset">Offset in to the block where the UTF-8 bytes start.</param>
 308        /// <param name="terminator">A character in the ASCII range that marks the end of the string.
 309        /// If a value other than '\0' is passed we still stop at the null terminator if encountered first.</param>
 310        /// <param name="numberOfBytesRead">The number of bytes read, which includes the terminator if we did not hit th
 311        /// <returns>Length (byte count) not including terminator.</returns>
 312        internal int GetUtf8NullTerminatedLength(int offset, out int numberOfBytesRead, char terminator)
 0313        {
 0314            CheckBounds(offset, 0);
 315
 0316            Debug.Assert(terminator <= 0x7f);
 317
 0318            ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(Pointer + offset, Length - offset);
 0319            int length = terminator != '\0' ?
 0320                span.IndexOfAny((byte)0, (byte)terminator) :
 0321                span.IndexOf((byte)0);
 0322            if (length >= 0)
 0323            {
 0324                numberOfBytesRead = length + 1; // we also read the terminator
 0325            }
 326            else
 0327            {
 0328                numberOfBytesRead = length = span.Length;
 0329            }
 330
 0331            return length;
 0332        }
 333
 334        internal int Utf8NullTerminatedOffsetOfAsciiChar(int startOffset, char asciiChar)
 0335        {
 0336            CheckBounds(startOffset, 0);
 337
 0338            Debug.Assert(asciiChar != 0 && asciiChar <= 0x7f);
 339
 0340            ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(Pointer + startOffset, Length - startOffset);
 0341            int i = span.IndexOfAny((byte)asciiChar, (byte)0);
 0342            return i >= 0 && span[i] == asciiChar ?
 0343                startOffset + i :
 0344                -1;
 0345        }
 346
 347        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
 348        internal bool Utf8NullTerminatedEquals(int offset, string text, MetadataStringDecoder utf8Decoder, char terminat
 0349        {
 0350            FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, 0, out _, terminator, ignoreCase);
 351
 0352            if (result == FastComparisonResult.Inconclusive)
 0353            {
 0354                string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out _, terminator);
 0355                return decoded.Equals(text, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 356            }
 357
 0358            return result == FastComparisonResult.Equal;
 0359        }
 360
 361        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
 362        internal bool Utf8NullTerminatedStartsWith(int offset, string text, MetadataStringDecoder utf8Decoder, char term
 0363        {
 0364            FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, 0, out _, terminator, ignoreCase);
 365
 0366            switch (result)
 367            {
 368                case FastComparisonResult.Equal:
 369                case FastComparisonResult.BytesStartWithText:
 0370                    return true;
 371
 372                case FastComparisonResult.Unequal:
 373                case FastComparisonResult.TextStartsWithBytes:
 0374                    return false;
 375
 376                default:
 0377                    Debug.Assert(result == FastComparisonResult.Inconclusive);
 0378                    string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out _, terminator);
 0379                    return decoded.StartsWith(text, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.O
 380            }
 0381        }
 382
 383        internal enum FastComparisonResult
 384        {
 385            Equal,
 386            BytesStartWithText,
 387            TextStartsWithBytes,
 388            Unequal,
 389            Inconclusive
 390        }
 391
 392        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
 393        internal FastComparisonResult Utf8NullTerminatedFastCompare(int offset, string text, int textStart, out int firs
 0394        {
 0395            CheckBounds(offset, 0);
 396
 0397            Debug.Assert(terminator <= 0x7F);
 398
 0399            byte* startPointer = Pointer + offset;
 0400            byte* endPointer = Pointer + Length;
 0401            byte* currentPointer = startPointer;
 402
 0403            int ignoreCaseMask = StringUtils.IgnoreCaseMask(ignoreCase);
 0404            int currentIndex = textStart;
 0405            while (currentIndex < text.Length && currentPointer != endPointer)
 0406            {
 0407                byte currentByte = *currentPointer;
 408
 409                // note that terminator is not compared case-insensitively even if ignore case is true
 0410                if (currentByte == 0 || currentByte == terminator)
 0411                {
 0412                    break;
 413                }
 414
 0415                char currentChar = text[currentIndex];
 0416                if ((currentByte & 0x80) == 0 && StringUtils.IsEqualAscii(currentChar, currentByte, ignoreCaseMask))
 0417                {
 0418                    currentIndex++;
 0419                    currentPointer++;
 0420                }
 421                else
 0422                {
 0423                    firstDifferenceIndex = currentIndex;
 424
 425                    // uncommon non-ascii case --> fall back to slow allocating comparison.
 0426                    return (currentChar > 0x7F) ? FastComparisonResult.Inconclusive : FastComparisonResult.Unequal;
 427                }
 0428            }
 429
 0430            firstDifferenceIndex = currentIndex;
 431
 0432            bool textTerminated = currentIndex == text.Length;
 0433            bool bytesTerminated = currentPointer == endPointer || *currentPointer == 0 || *currentPointer == terminator
 434
 0435            if (textTerminated && bytesTerminated)
 0436            {
 0437                return FastComparisonResult.Equal;
 438            }
 439
 0440            return textTerminated ? FastComparisonResult.BytesStartWithText : FastComparisonResult.TextStartsWithBytes;
 0441        }
 442
 443        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
 444        internal bool Utf8NullTerminatedStringStartsWithAsciiPrefix(int offset, string asciiPrefix)
 0445        {
 446            // Assumes stringAscii only contains ASCII characters and no nil characters.
 447
 0448            CheckBounds(offset, 0);
 449
 450            // Make sure that we won't read beyond the block even if the block doesn't end with 0 byte.
 0451            if (asciiPrefix.Length > Length - offset)
 0452            {
 0453                return false;
 454            }
 455
 0456            byte* p = Pointer + offset;
 457
 0458            for (int i = 0; i < asciiPrefix.Length; i++)
 0459            {
 0460                Debug.Assert(asciiPrefix[i] > 0 && asciiPrefix[i] <= 0x7f);
 461
 0462                if (asciiPrefix[i] != *p)
 0463                {
 0464                    return false;
 465                }
 466
 0467                p++;
 0468            }
 469
 0470            return true;
 0471        }
 472
 473        internal int CompareUtf8NullTerminatedStringWithAsciiString(int offset, string asciiString)
 0474        {
 475            // Assumes stringAscii only contains ASCII characters and no nil characters.
 476
 0477            CheckBounds(offset, 0);
 478
 0479            byte* p = Pointer + offset;
 0480            int limit = Length - offset;
 481
 0482            for (int i = 0; i < asciiString.Length; i++)
 0483            {
 0484                Debug.Assert(asciiString[i] > 0 && asciiString[i] <= 0x7f);
 485
 0486                if (i > limit)
 0487                {
 488                    // Heap value is shorter.
 0489                    return -1;
 490                }
 491
 0492                if (*p != asciiString[i])
 0493                {
 494                    // If we hit the end of the heap value (*p == 0)
 495                    // the heap value is shorter than the string, so we return negative value.
 0496                    return *p - asciiString[i];
 497                }
 498
 0499                p++;
 0500            }
 501
 502            // Either the heap value name matches exactly the given string or
 503            // it is longer so it is considered "greater".
 0504            return (*p == 0) ? 0 : +1;
 0505        }
 506
 507        internal byte[] PeekBytes(int offset, int byteCount)
 0508        {
 0509            CheckBounds(offset, byteCount);
 0510            return new ReadOnlySpan<byte>(Pointer + offset, byteCount).ToArray();
 0511        }
 512
 513        internal int IndexOf(byte b, int start)
 0514        {
 0515            CheckBounds(start, 0);
 0516            return IndexOfUnchecked(b, start);
 0517        }
 518
 519        internal int IndexOfUnchecked(byte b, int start)
 0520        {
 0521            int i = new ReadOnlySpan<byte>(Pointer + start, Length - start).IndexOf(b);
 0522            return i >= 0 ?
 0523                i + start :
 0524                -1;
 0525        }
 526
 527        // same as Array.BinarySearch, but without using IComparer
 528        internal int BinarySearch(string[] asciiKeys, int offset)
 0529        {
 0530            var low = 0;
 0531            var high = asciiKeys.Length - 1;
 532
 0533            while (low <= high)
 0534            {
 0535                var middle = low + ((high - low) >> 1);
 0536                var midValue = asciiKeys[middle];
 537
 0538                int comparison = CompareUtf8NullTerminatedStringWithAsciiString(offset, midValue);
 0539                if (comparison == 0)
 0540                {
 0541                    return middle;
 542                }
 543
 0544                if (comparison < 0)
 0545                {
 0546                    high = middle - 1;
 0547                }
 548                else
 0549                {
 0550                    low = middle + 1;
 0551                }
 0552            }
 553
 0554            return ~low;
 0555        }
 556
 557        /// <summary>
 558        /// In a table that specifies children via a list field (e.g. TypeDef.FieldList, TypeDef.MethodList),
 559        /// searches for the parent given a reference to a child.
 560        /// </summary>
 561        /// <returns>Returns row number [0..RowCount).</returns>
 562        internal int BinarySearchForSlot(
 563            int rowCount,
 564            int rowSize,
 565            int referenceListOffset,
 566            uint referenceValue,
 567            bool isReferenceSmall)
 0568        {
 0569            int startRowNumber = 0;
 0570            int endRowNumber = rowCount - 1;
 0571            uint startValue = PeekReferenceUnchecked(startRowNumber * rowSize + referenceListOffset, isReferenceSmall);
 0572            uint endValue = PeekReferenceUnchecked(endRowNumber * rowSize + referenceListOffset, isReferenceSmall);
 0573            if (endRowNumber == 1)
 0574            {
 0575                if (referenceValue >= endValue)
 0576                {
 0577                    return endRowNumber;
 578                }
 579
 0580                return startRowNumber;
 581            }
 582
 0583            while (endRowNumber - startRowNumber > 1)
 0584            {
 0585                if (referenceValue <= startValue)
 0586                {
 0587                    return referenceValue == startValue ? startRowNumber : startRowNumber - 1;
 588                }
 589
 0590                if (referenceValue >= endValue)
 0591                {
 0592                    return referenceValue == endValue ? endRowNumber : endRowNumber + 1;
 593                }
 594
 0595                int midRowNumber = (startRowNumber + endRowNumber) / 2;
 0596                uint midReferenceValue = PeekReferenceUnchecked(midRowNumber * rowSize + referenceListOffset, isReferenc
 0597                if (referenceValue > midReferenceValue)
 0598                {
 0599                    startRowNumber = midRowNumber;
 0600                    startValue = midReferenceValue;
 0601                }
 0602                else if (referenceValue < midReferenceValue)
 0603                {
 0604                    endRowNumber = midRowNumber;
 0605                    endValue = midReferenceValue;
 0606                }
 607                else
 0608                {
 0609                    return midRowNumber;
 610                }
 0611            }
 612
 0613            return startRowNumber;
 0614        }
 615
 616        /// <summary>
 617        /// In a table ordered by a column containing entity references searches for a row with the specified reference.
 618        /// </summary>
 619        /// <returns>Returns row number [0..RowCount) or -1 if not found.</returns>
 620        internal int BinarySearchReference(
 621            int rowCount,
 622            int rowSize,
 623            int referenceOffset,
 624            uint referenceValue,
 625            bool isReferenceSmall)
 0626        {
 0627            int startRowNumber = 0;
 0628            int endRowNumber = rowCount - 1;
 0629            while (startRowNumber <= endRowNumber)
 0630            {
 0631                int midRowNumber = (startRowNumber + endRowNumber) / 2;
 0632                uint midReferenceValue = PeekReferenceUnchecked(midRowNumber * rowSize + referenceOffset, isReferenceSma
 0633                if (referenceValue > midReferenceValue)
 0634                {
 0635                    startRowNumber = midRowNumber + 1;
 0636                }
 0637                else if (referenceValue < midReferenceValue)
 0638                {
 0639                    endRowNumber = midRowNumber - 1;
 0640                }
 641                else
 0642                {
 0643                    return midRowNumber;
 644                }
 0645            }
 646
 0647            return -1;
 0648        }
 649
 650        // Row number [0, ptrTable.Length) or -1 if not found.
 651        internal int BinarySearchReference(
 652            int[] ptrTable,
 653            int rowSize,
 654            int referenceOffset,
 655            uint referenceValue,
 656            bool isReferenceSmall)
 0657        {
 0658            int startRowNumber = 0;
 0659            int endRowNumber = ptrTable.Length - 1;
 0660            while (startRowNumber <= endRowNumber)
 0661            {
 0662                int midRowNumber = (startRowNumber + endRowNumber) / 2;
 0663                uint midReferenceValue = PeekReferenceUnchecked((ptrTable[midRowNumber] - 1) * rowSize + referenceOffset
 0664                if (referenceValue > midReferenceValue)
 0665                {
 0666                    startRowNumber = midRowNumber + 1;
 0667                }
 0668                else if (referenceValue < midReferenceValue)
 0669                {
 0670                    endRowNumber = midRowNumber - 1;
 0671                }
 672                else
 0673                {
 0674                    return midRowNumber;
 675                }
 0676            }
 677
 0678            return -1;
 0679        }
 680
 681        /// <summary>
 682        /// Calculates a range of rows that have specified value in the specified column in a table that is sorted by th
 683        /// </summary>
 684        internal void BinarySearchReferenceRange(
 685            int rowCount,
 686            int rowSize,
 687            int referenceOffset,
 688            uint referenceValue,
 689            bool isReferenceSmall,
 690            out int startRowNumber, // [0, rowCount) or -1
 691            out int endRowNumber)   // [0, rowCount) or -1
 0692        {
 0693            int foundRowNumber = BinarySearchReference(
 0694                rowCount,
 0695                rowSize,
 0696                referenceOffset,
 0697                referenceValue,
 0698                isReferenceSmall
 0699            );
 700
 0701            if (foundRowNumber == -1)
 0702            {
 0703                startRowNumber = -1;
 0704                endRowNumber = -1;
 0705                return;
 706            }
 707
 0708            startRowNumber = foundRowNumber;
 0709            while (startRowNumber > 0 &&
 0710                   PeekReferenceUnchecked((startRowNumber - 1) * rowSize + referenceOffset, isReferenceSmall) == referen
 0711            {
 0712                startRowNumber--;
 0713            }
 714
 0715            endRowNumber = foundRowNumber;
 0716            while (endRowNumber + 1 < rowCount &&
 0717                   PeekReferenceUnchecked((endRowNumber + 1) * rowSize + referenceOffset, isReferenceSmall) == reference
 0718            {
 0719                endRowNumber++;
 0720            }
 0721        }
 722
 723        /// <summary>
 724        /// Calculates a range of rows that have specified value in the specified column in a table that is sorted by th
 725        /// </summary>
 726        internal void BinarySearchReferenceRange(
 727            int[] ptrTable,
 728            int rowSize,
 729            int referenceOffset,
 730            uint referenceValue,
 731            bool isReferenceSmall,
 732            out int startRowNumber, // [0, ptrTable.Length) or -1
 733            out int endRowNumber)   // [0, ptrTable.Length) or -1
 0734        {
 0735            int foundRowNumber = BinarySearchReference(
 0736                ptrTable,
 0737                rowSize,
 0738                referenceOffset,
 0739                referenceValue,
 0740                isReferenceSmall
 0741            );
 742
 0743            if (foundRowNumber == -1)
 0744            {
 0745                startRowNumber = -1;
 0746                endRowNumber = -1;
 0747                return;
 748            }
 749
 0750            startRowNumber = foundRowNumber;
 0751            while (startRowNumber > 0 &&
 0752                   PeekReferenceUnchecked((ptrTable[startRowNumber - 1] - 1) * rowSize + referenceOffset, isReferenceSma
 0753            {
 0754                startRowNumber--;
 0755            }
 756
 0757            endRowNumber = foundRowNumber;
 0758            while (endRowNumber + 1 < ptrTable.Length &&
 0759                   PeekReferenceUnchecked((ptrTable[endRowNumber + 1] - 1) * rowSize + referenceOffset, isReferenceSmall
 0760            {
 0761                endRowNumber++;
 0762            }
 0763        }
 764
 765        // Always RowNumber....
 766        internal int LinearSearchReference(
 767            int rowSize,
 768            int referenceOffset,
 769            uint referenceValue,
 770            bool isReferenceSmall)
 0771        {
 0772            int currOffset = referenceOffset;
 0773            int totalSize = this.Length;
 0774            while (currOffset < totalSize)
 0775            {
 0776                uint currReference = PeekReferenceUnchecked(currOffset, isReferenceSmall);
 0777                if (currReference == referenceValue)
 0778                {
 0779                    return currOffset / rowSize;
 780                }
 781
 0782                currOffset += rowSize;
 0783            }
 784
 0785            return -1;
 0786        }
 787
 788        internal bool IsOrderedByReferenceAscending(
 789            int rowSize,
 790            int referenceOffset,
 791            bool isReferenceSmall)
 0792        {
 0793            int offset = referenceOffset;
 0794            int totalSize = this.Length;
 795
 0796            uint previous = 0;
 0797            while (offset < totalSize)
 0798            {
 0799                uint current = PeekReferenceUnchecked(offset, isReferenceSmall);
 0800                if (current < previous)
 0801                {
 0802                    return false;
 803                }
 804
 0805                previous = current;
 0806                offset += rowSize;
 0807            }
 808
 0809            return true;
 0810        }
 811
 812        internal int[] BuildPtrTable(
 813            int numberOfRows,
 814            int rowSize,
 815            int referenceOffset,
 816            bool isReferenceSmall)
 0817        {
 0818            int[] ptrTable = new int[numberOfRows];
 0819            uint[] unsortedReferences = new uint[numberOfRows];
 820
 0821            for (int i = 0; i < ptrTable.Length; i++)
 0822            {
 0823                ptrTable[i] = i + 1;
 0824            }
 825
 0826            ReadColumn(unsortedReferences, rowSize, referenceOffset, isReferenceSmall);
 0827            Array.Sort(ptrTable, (int a, int b) => { return unsortedReferences[a - 1].CompareTo(unsortedReferences[b - 1
 0828            return ptrTable;
 0829        }
 830
 831        private void ReadColumn(
 832            uint[] result,
 833            int rowSize,
 834            int referenceOffset,
 835            bool isReferenceSmall)
 0836        {
 0837            int offset = referenceOffset;
 0838            int totalSize = this.Length;
 839
 0840            int i = 0;
 0841            while (offset < totalSize)
 0842            {
 0843                result[i] = PeekReferenceUnchecked(offset, isReferenceSmall);
 0844                offset += rowSize;
 0845                i++;
 0846            }
 847
 0848            Debug.Assert(i == result.Length);
 0849        }
 850
 851        internal bool PeekHeapValueOffsetAndSize(int index, out int offset, out int size)
 0852        {
 853            int bytesRead;
 0854            int numberOfBytes = PeekCompressedInteger(index, out bytesRead);
 0855            if (numberOfBytes == BlobReader.InvalidCompressedInteger)
 0856            {
 0857                offset = 0;
 0858                size = 0;
 0859                return false;
 860            }
 861
 0862            offset = index + bytesRead;
 0863            size = numberOfBytes;
 0864            return true;
 0865        }
 866    }
 867}
 868

Methods/Properties

.ctor(System.Byte*,System.Int32)
CreateChecked(System.Byte*,System.Int32)
CheckBounds(System.Int32,System.Int32)
ToArray()
GetDebuggerDisplay()
GetDebuggerDisplay(System.Int32&)
GetDebuggerDisplay(System.Int32)
GetMemoryBlockAt(System.Int32,System.Int32)
PeekByte(System.Int32)
PeekInt32(System.Int32)
PeekUInt32(System.Int32)
PeekCompressedInteger(System.Int32,System.Int32&)
PeekUInt16(System.Int32)
PeekTaggedReference(System.Int32,System.Boolean)
PeekReferenceUnchecked(System.Int32,System.Boolean)
PeekReference(System.Int32,System.Boolean)
PeekHeapReference(System.Int32,System.Boolean)
PeekGuid(System.Int32)
PeekUtf16(System.Int32,System.Int32)
PeekUtf8(System.Int32,System.Int32)
PeekUtf8NullTerminated(System.Int32,System.Byte[],System.Reflection.Metadata.MetadataStringDecoder,System.Int32&,System.Char)
GetUtf8NullTerminatedLength(System.Int32,System.Int32&,System.Char)
Utf8NullTerminatedOffsetOfAsciiChar(System.Int32,System.Char)
Utf8NullTerminatedEquals(System.Int32,System.String,System.Reflection.Metadata.MetadataStringDecoder,System.Char,System.Boolean)
Utf8NullTerminatedStartsWith(System.Int32,System.String,System.Reflection.Metadata.MetadataStringDecoder,System.Char,System.Boolean)
Utf8NullTerminatedFastCompare(System.Int32,System.String,System.Int32,System.Int32&,System.Char,System.Boolean)
Utf8NullTerminatedStringStartsWithAsciiPrefix(System.Int32,System.String)
CompareUtf8NullTerminatedStringWithAsciiString(System.Int32,System.String)
PeekBytes(System.Int32,System.Int32)
IndexOf(System.Byte,System.Int32)
IndexOfUnchecked(System.Byte,System.Int32)
BinarySearch(System.String[],System.Int32)
BinarySearchForSlot(System.Int32,System.Int32,System.Int32,System.UInt32,System.Boolean)
BinarySearchReference(System.Int32,System.Int32,System.Int32,System.UInt32,System.Boolean)
BinarySearchReference(System.Int32[],System.Int32,System.Int32,System.UInt32,System.Boolean)
BinarySearchReferenceRange(System.Int32,System.Int32,System.Int32,System.UInt32,System.Boolean,System.Int32&,System.Int32&)
BinarySearchReferenceRange(System.Int32[],System.Int32,System.Int32,System.UInt32,System.Boolean,System.Int32&,System.Int32&)
LinearSearchReference(System.Int32,System.Int32,System.UInt32,System.Boolean)
IsOrderedByReferenceAscending(System.Int32,System.Int32,System.Boolean)
BuildPtrTable(System.Int32,System.Int32,System.Int32,System.Boolean)
ReadColumn(System.UInt32[],System.Int32,System.Int32,System.Boolean)
PeekHeapValueOffsetAndSize(System.Int32,System.Int32&,System.Int32&)