< Summary

Line coverage
41%
Covered lines: 1322
Uncovered lines: 1884
Coverable lines: 3206
Total lines: 7732
Line coverage: 41.2%
Branch coverage
41%
Covered branches: 1205
Total branches: 2910
Branch coverage: 41.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: BinarySearch(...)50%2266.66%
File 1: BinarySearch(...)87.5%88100%
File 1: .ctor(...)100%110%
File 1: CompareTo(...)100%110%
File 2: IndexOf(...)0%72720%
File 2: LastIndexOf(...)0%72720%
File 2: ThrowMustBeNullTerminatedString()100%110%
File 2: IndexOfNullByte(...)46.73%929244.82%
File 2: SequenceEqual(...)64.81%545460%
File 2: SequenceCompareTo(...)30.76%525225%
File 2: CommonPrefixLength(...)0%32320%
File 2: LoadUShort(...)100%110%
File 2: LoadUInt(...)100%11100%
File 2: LoadUInt(...)100%11100%
File 2: LoadNUInt(...)100%11100%
File 2: LoadNUInt(...)100%11100%
File 2: GetByteVector128SpanLength(...)100%110%
File 2: GetByteVector256SpanLength(...)100%11100%
File 2: GetByteVector512SpanLength(...)100%110%
File 2: UnalignedCountVector128(...)100%11100%
File 2: Reverse(...)0%28280%
File 3: Memmove(...)80%404093.33%
File 3: MemmoveNative(...)100%11100%
File 3: ClearWithoutReferences(...)80.55%363696.07%
File 3: ZeroMemoryNative(...)50%22100%
File 3: Fill(...)0%20200%
File 4: IndexOf(...)21.62%747424.36%
File 4: LastIndexOf(...)0%72720%
File 4: SequenceCompareTo(...)88.46%2626100%
File 4: IndexOfNullCharacter(...)25.64%787829.56%
File 4: GetCharVector128SpanLength(...)100%110%
File 4: GetCharVector256SpanLength(...)100%11100%
File 4: GetCharVector512SpanLength(...)100%110%
File 4: UnalignedCountVector128(...)100%11100%
File 4: Reverse(...)0%20200%
File 5: ClearWithReferences(...)37.5%8888.46%
File 5: Reverse(...)0%20200%
File 5: Reverse(...)0%20200%
File 5: Reverse(...)0%10100%
File 5: ReverseInner(...)0%220%
File 6: Fill(...)50%505067.64%
File 6: IndexOf(...)0%10100%
File 6: Contains(...)0%66660%
File 6: IndexOf(...)0%66660%
File 6: IndexOfAny(...)0%78780%
File 6: IndexOfAny(...)0%1121120%
File 6: IndexOfAny(...)0%24240%
File 6: LastIndexOf(...)0%12120%
File 6: LastIndexOf(...)0%66660%
File 6: LastIndexOfAny(...)0%78780%
File 6: LastIndexOfAny(...)0%1121120%
File 6: LastIndexOfAny(...)0%24240%
File 6: IndexOfAnyExcept(...)0%440%
File 6: LastIndexOfAnyExcept(...)0%440%
File 6: IndexOfAnyExcept(...)0%660%
File 6: LastIndexOfAnyExcept(...)0%660%
File 6: IndexOfAnyExcept(...)0%880%
File 6: LastIndexOfAnyExcept(...)0%880%
File 6: IndexOfAnyExcept(...)0%10100%
File 6: LastIndexOfAnyExcept(...)0%10100%
File 6: SequenceEqual(...)0%60600%
File 6: SequenceCompareTo(...)0%12120%
File 6: ContainsValueType(...)100%66100%
File 6: NonPackedContainsValueType(...)76.31%767680%
File 6: IndexOfChar(...)100%11100%
File 6: LastIndexOfChar(...)100%110%
File 6: NonPackedIndexOfChar(...)100%110%
File 6: IndexOfValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%110%
File 6: IndexOfValueType(...)87.5%88100%
File 6: NonPackedIndexOfValueType(...)80.26%767685.18%
File 6: IndexOfAnyChar(...)100%110%
File 6: LastIndexOfAnyChar(...)100%110%
File 6: IndexOfAnyValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%110%
File 6: IndexOfAnyValueType(...)44.44%181853.84%
File 6: NonPackedIndexOfAnyValueType(...)85.57%10410485.57%
File 6: IndexOfAnyValueType(...)100%110%
File 6: IndexOfAnyExceptValueType(...)100%110%
File 6: IndexOfAnyValueType(...)0%12120%
File 6: NonPackedIndexOfAnyValueType(...)88.46%13013085.57%
File 6: IndexOfAnyValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%11100%
File 6: IndexOfAnyValueType(...)82.95%888879.01%
File 6: IndexOfAnyValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%11100%
File 6: IndexOfAnyValueType(...)84.69%989878.57%
File 6: LastIndexOfValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfValueType(...)84.61%525297.77%
File 6: SimdImpl(TValue&,TValue,System.Int32)100%66100%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)86.73%989886.73%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)89.51%12412486.73%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)84.14%828280.28%
File 6: Replace(...)0%12120%
File 6: ReplaceValueType(...)0%26260%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)85.86%929279.22%
File 6: ComputeFirstIndex(...)100%11100%
File 6: ComputeFirstIndex(...)100%11100%
File 6: ComputeFirstIndex(...)100%110%
File 6: ComputeLastIndex(...)100%11100%
File 6: ComputeLastIndex(...)100%11100%
File 6: ComputeLastIndex(...)100%110%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%110%
File 6: HasMatch(...)100%11100%
File 6: GetMatchMask(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%110%
File 6: HasMatch(...)100%11100%
File 6: GetMatchMask(...)100%11100%
File 6: IndexOfAnyInRange(...)0%660%
File 6: IndexOfAnyExceptInRange(...)0%660%
File 6: IndexOfAnyInRangeUnsignedNumber(...)91.66%1212100%
File 6: IndexOfAnyExceptInRangeUnsignedNumber(...)100%11100%
File 6: NonPackedIndexOfAnyInRangeUnsignedNumber(...)70.58%343473.33%
File 6: LastIndexOfAnyInRange(...)0%660%
File 6: LastIndexOfAnyExceptInRange(...)0%660%
File 6: LastIndexOfAnyInRangeUnsignedNumber(...)73.52%343474.41%
File 6: LastIndexOfAnyExceptInRangeUnsignedNumber(...)100%11100%
File 6: Count(...)0%10100%
File 6: CountValueType(...)0%22220%

File(s)

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.BinarySearch.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.Collections.Generic;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7
 8namespace System
 9{
 10    internal static partial class SpanHelpers
 11    {
 12        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 13        public static int BinarySearch<T, TComparable>(
 14            ReadOnlySpan<T> span, TComparable comparable)
 15            where TComparable : IComparable<T>, allows ref struct
 16        {
 4355817            if (comparable == null)
 018                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparable);
 19
 4355820            return BinarySearch(ref MemoryMarshal.GetReference(span), span.Length, comparable);
 21        }
 22
 23        public static int BinarySearch<T, TComparable>(
 24            ref T spanStart, int length, TComparable comparable)
 25            where TComparable : IComparable<T>, allows ref struct
 26        {
 4355827            int lo = 0;
 4355828            int hi = length - 1;
 29            // If length == 0, hi == -1, and loop will not be entered
 29769530            while (lo <= hi)
 31            {
 32                // PERF: `lo` or `hi` will never be negative inside the loop,
 33                //       so computing median using uints is safe since we know
 34                //       `length <= int.MaxValue`, and indices are >= 0
 35                //       and thus cannot overflow an uint.
 36                //       Saves one subtraction per loop compared to
 37                //       `int i = lo + ((hi - lo) >> 1);`
 25414238                int i = (int)(((uint)hi + (uint)lo) >> 1);
 39
 25414240                int c = comparable.CompareTo(Unsafe.Add(ref spanStart, i));
 25414241                if (c == 0)
 42                {
 543                    return i;
 44                }
 25413745                else if (c > 0)
 46                {
 10426447                    lo = i + 1;
 48                }
 49                else
 50                {
 14987351                    hi = i - 1;
 52                }
 53            }
 54            // If none found, then a negative number that is the bitwise complement
 55            // of the index of the next element that is larger than or, if there is
 56            // no larger element, the bitwise complement of `length`, which
 57            // is `lo` at this point.
 4355358            return ~lo;
 59        }
 60
 61        // Helper to allow sharing all code via IComparable<T> inlineable
 62        internal readonly ref struct ComparerComparable<T, TComparer> : IComparable<T>
 63            where TComparer : IComparer<T>, allows ref struct
 64        {
 65            private readonly T _value;
 66            private readonly TComparer _comparer;
 67
 68            public ComparerComparable(T value, TComparer comparer)
 69            {
 070                _value = value;
 071                _comparer = comparer;
 072            }
 73
 74            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 075            public int CompareTo(T? other) => _comparer.Compare(_value, other);
 76        }
 77    }
 78}

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.Byte.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.Diagnostics.CodeAnalysis;
 7using System.Numerics;
 8using System.Runtime.CompilerServices;
 9using System.Runtime.Intrinsics;
 10using System.Runtime.Intrinsics.X86;
 11
 12namespace System
 13{
 14    internal static partial class SpanHelpers // .Byte
 15    {
 16        public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
 17        {
 018            Debug.Assert(searchSpaceLength >= 0);
 019            Debug.Assert(valueLength >= 0);
 20
 021            if (valueLength == 0)
 022                return 0;  // A zero-length sequence is always treated as "found" at the start of the search space.
 23
 024            int valueTailLength = valueLength - 1;
 025            if (valueTailLength == 0)
 026                return IndexOfValueType(ref searchSpace, value, searchSpaceLength); // for single-byte values use plain 
 27
 028            nint offset = 0;
 029            byte valueHead = value;
 030            int searchSpaceMinusValueTailLength = searchSpaceLength - valueTailLength;
 031            if (Vector128.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector128<byte>.Count)
 32            {
 33                goto SEARCH_TWO_BYTES;
 34            }
 35
 036            ref byte valueTail = ref Unsafe.Add(ref value, 1);
 037            int remainingSearchSpaceLength = searchSpaceMinusValueTailLength;
 38
 039            while (remainingSearchSpaceLength > 0)
 40            {
 41                // Do a quick search for the first element of "value".
 042                int relativeIndex = IndexOfValueType(ref Unsafe.Add(ref searchSpace, offset), valueHead, remainingSearch
 043                if (relativeIndex < 0)
 44                    break;
 45
 046                remainingSearchSpaceLength -= relativeIndex;
 047                offset += relativeIndex;
 48
 049                if (remainingSearchSpaceLength <= 0)
 50                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 51
 52                // Found the first element of "value". See if the tail matches.
 053                if (SequenceEqual(
 054                        ref Unsafe.Add(ref searchSpace, offset + 1),
 055                        ref valueTail, (nuint)(uint)valueTailLength))  // The (nuint)-cast is necessary to pick the corr
 056                    return (int)offset;  // The tail matched. Return a successful find.
 57
 058                remainingSearchSpaceLength--;
 059                offset++;
 60            }
 061            return -1;
 62
 63            // Based on http://0x80.pl/articles/simd-strfind.html#algorithm-1-generic-simd "Algorithm 1: Generic SIMD" b
 64            // Some details about the implementation can also be found in https://github.com/dotnet/runtime/pull/63285
 65        SEARCH_TWO_BYTES:
 066            if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector512<byte>.Count >= 0)
 67            {
 68                // Find the last unique (which is not equal to ch1) byte
 69                // the algorithm is fine if both are equal, just a little bit less efficient
 070                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 071                nint ch1ch2Distance = (nint)(uint)valueTailLength;
 072                while (ch2Val == value && ch1ch2Distance > 1)
 073                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 74
 075                Vector512<byte> ch1 = Vector512.Create(value);
 076                Vector512<byte> ch2 = Vector512.Create(ch2Val);
 77
 078                nint searchSpaceMinusValueTailLengthAndVector =
 079                    searchSpaceMinusValueTailLength - (nint)Vector512<byte>.Count;
 80
 81                do
 82                {
 083                    Debug.Assert(offset >= 0);
 84                    // Make sure we don't go out of bounds
 085                    Debug.Assert(offset + ch1ch2Distance + Vector512<byte>.Count <= searchSpaceLength);
 86
 087                    Vector512<byte> cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset 
 088                    Vector512<byte> cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset))
 089                    Vector512<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 90
 91                    // Early out: cmpAnd is all zeros
 092                    if (cmpAnd != Vector512<byte>.Zero)
 93                    {
 94                        goto CANDIDATE_FOUND;
 95                    }
 96
 97                LOOP_FOOTER:
 098                    offset += Vector512<byte>.Count;
 99
 0100                    if (offset == searchSpaceMinusValueTailLength)
 0101                        return -1;
 102
 103                    // Overlap with the current chunk for trailing elements
 0104                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0105                        offset = searchSpaceMinusValueTailLengthAndVector;
 106
 0107                    continue;
 108
 109                CANDIDATE_FOUND:
 0110                    ulong mask = cmpAnd.ExtractMostSignificantBits();
 111                    do
 112                    {
 0113                        int bitPos = BitOperations.TrailingZeroCount(mask);
 0114                        if (valueLength == 2 || // we already matched two bytes
 0115                            SequenceEqual(
 0116                                ref Unsafe.Add(ref searchSpace, offset + bitPos),
 0117                                ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the corre
 118                        {
 0119                            return (int)(offset + bitPos);
 120                        }
 0121                        mask = BitOperations.ResetLowestSetBit(mask); // Clear the lowest set bit
 0122                    } while (mask != 0);
 0123                    goto LOOP_FOOTER;
 124
 125                } while (true);
 126            }
 0127            else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256<byte>.Count >= 0)
 128            {
 129                // Find the last unique (which is not equal to ch1) byte
 130                // the algorithm is fine if both are equal, just a little bit less efficient
 0131                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 0132                nint ch1ch2Distance = valueTailLength;
 0133                while (ch2Val == value && ch1ch2Distance > 1)
 0134                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 135
 0136                Vector256<byte> ch1 = Vector256.Create(value);
 0137                Vector256<byte> ch2 = Vector256.Create(ch2Val);
 138
 0139                nint searchSpaceMinusValueTailLengthAndVector =
 0140                    searchSpaceMinusValueTailLength - (nint)Vector256<byte>.Count;
 141
 142                do
 143                {
 0144                    Debug.Assert(offset >= 0);
 145                    // Make sure we don't go out of bounds
 0146                    Debug.Assert(offset + ch1ch2Distance + Vector256<byte>.Count <= searchSpaceLength);
 147
 0148                    Vector256<byte> cmpCh2 = Vector256.Equals(ch2, Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset 
 0149                    Vector256<byte> cmpCh1 = Vector256.Equals(ch1, Vector256.LoadUnsafe(ref searchSpace, (nuint)offset))
 0150                    Vector256<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 151
 152                    // Early out: cmpAnd is all zeros
 0153                    if (cmpAnd != Vector256<byte>.Zero)
 154                    {
 155                        goto CANDIDATE_FOUND;
 156                    }
 157
 158                LOOP_FOOTER:
 0159                    offset += Vector256<byte>.Count;
 160
 0161                    if (offset == searchSpaceMinusValueTailLength)
 0162                        return -1;
 163
 164                    // Overlap with the current chunk for trailing elements
 0165                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0166                        offset = searchSpaceMinusValueTailLengthAndVector;
 167
 0168                    continue;
 169
 170                CANDIDATE_FOUND:
 0171                    uint mask = cmpAnd.ExtractMostSignificantBits();
 172                    do
 173                    {
 0174                        int bitPos = BitOperations.TrailingZeroCount(mask);
 0175                        if (valueLength == 2 || // we already matched two bytes
 0176                            SequenceEqual(
 0177                                ref Unsafe.Add(ref searchSpace, offset + bitPos),
 0178                                ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the corre
 179                        {
 0180                            return (int)(offset + bitPos);
 181                        }
 0182                        mask = BitOperations.ResetLowestSetBit(mask); // Clear the lowest set bit
 0183                    } while (mask != 0);
 0184                    goto LOOP_FOOTER;
 185
 186                } while (true);
 187            }
 188            else // 128bit vector path (SSE2 or AdvSimd)
 189            {
 190                // Find the last unique (which is not equal to ch1) byte
 191                // the algorithm is fine if both are equal, just a little bit less efficient
 0192                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 0193                int ch1ch2Distance = valueTailLength;
 0194                while (ch2Val == value && ch1ch2Distance > 1)
 0195                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 196
 0197                Vector128<byte> ch1 = Vector128.Create(value);
 0198                Vector128<byte> ch2 = Vector128.Create(ch2Val);
 199
 0200                nint searchSpaceMinusValueTailLengthAndVector =
 0201                    searchSpaceMinusValueTailLength - (nint)Vector128<byte>.Count;
 202
 203                do
 204                {
 0205                    Debug.Assert(offset >= 0);
 206                    // Make sure we don't go out of bounds
 0207                    Debug.Assert(offset + ch1ch2Distance + Vector128<byte>.Count <= searchSpaceLength);
 208
 0209                    Vector128<byte> cmpCh2 = Vector128.Equals(ch2, Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset 
 0210                    Vector128<byte> cmpCh1 = Vector128.Equals(ch1, Vector128.LoadUnsafe(ref searchSpace, (nuint)offset))
 0211                    Vector128<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 212
 213                    // Early out: cmpAnd is all zeros
 0214                    if (cmpAnd != Vector128<byte>.Zero)
 215                    {
 216                        goto CANDIDATE_FOUND;
 217                    }
 218
 219                LOOP_FOOTER:
 0220                    offset += Vector128<byte>.Count;
 221
 0222                    if (offset == searchSpaceMinusValueTailLength)
 0223                        return -1;
 224
 225                    // Overlap with the current chunk for trailing elements
 0226                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0227                        offset = searchSpaceMinusValueTailLengthAndVector;
 228
 0229                    continue;
 230
 231                CANDIDATE_FOUND:
 0232                    uint mask = cmpAnd.ExtractMostSignificantBits();
 233                    do
 234                    {
 0235                        int bitPos = BitOperations.TrailingZeroCount(mask);
 0236                        if (valueLength == 2 || // we already matched two bytes
 0237                            SequenceEqual(
 0238                                ref Unsafe.Add(ref searchSpace, offset + bitPos),
 0239                                ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the corre
 240                        {
 0241                            return (int)(offset + bitPos);
 242                        }
 243                        // Clear the lowest set bit
 0244                        mask = BitOperations.ResetLowestSetBit(mask);
 0245                    } while (mask != 0);
 0246                    goto LOOP_FOOTER;
 247
 248                } while (true);
 249            }
 250        }
 251
 252        public static int LastIndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
 253        {
 0254            Debug.Assert(searchSpaceLength >= 0);
 0255            Debug.Assert(valueLength >= 0);
 256
 0257            if (valueLength == 0)
 0258                return searchSpaceLength;  // A zero-length sequence is always treated as "found" at the end of the sear
 259
 0260            int valueTailLength = valueLength - 1;
 0261            if (valueTailLength == 0)
 0262                return LastIndexOfValueType(ref searchSpace, value, searchSpaceLength); // for single-byte values use pl
 263
 0264            int offset = 0;
 0265            byte valueHead = value;
 0266            int searchSpaceMinusValueTailLength = searchSpaceLength - valueTailLength;
 0267            if (Vector128.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector128<byte>.Count)
 268            {
 269                goto SEARCH_TWO_BYTES;
 270            }
 271
 0272            ref byte valueTail = ref Unsafe.Add(ref value, 1);
 273
 0274            while (true)
 275            {
 0276                Debug.Assert(0 <= offset && offset <= searchSpaceLength); // Ensures no deceptive underflows in the comp
 0277                int remainingSearchSpaceLength = searchSpaceLength - offset - valueTailLength;
 0278                if (remainingSearchSpaceLength <= 0)
 279                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 280
 281                // Do a quick search for the first element of "value".
 0282                int relativeIndex = LastIndexOfValueType(ref searchSpace, valueHead, remainingSearchSpaceLength);
 0283                if (relativeIndex < 0)
 284                    break;
 285
 286                // Found the first element of "value". See if the tail matches.
 0287                if (SequenceEqual(
 0288                        ref Unsafe.Add(ref searchSpace, relativeIndex + 1),
 0289                        ref valueTail, (nuint)(uint)valueTailLength)) // The (nuint)-cast is necessary to pick the corre
 0290                    return relativeIndex;  // The tail matched. Return a successful find.
 291
 0292                offset += remainingSearchSpaceLength - relativeIndex;
 293            }
 0294            return -1;
 295
 296        // Based on http://0x80.pl/articles/simd-strfind.html#algorithm-1-generic-simd "Algorithm 1: Generic SIMD" by Wo
 297        // Some details about the implementation can also be found in https://github.com/dotnet/runtime/pull/63285
 298        SEARCH_TWO_BYTES:
 0299            if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector512<byte>.Count)
 300            {
 0301                offset = searchSpaceMinusValueTailLength - Vector512<byte>.Count;
 302
 303                // Find the last unique (which is not equal to ch1) byte
 304                // the algorithm is fine if both are equal, just a little bit less efficient
 0305                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 0306                int ch1ch2Distance = valueTailLength;
 0307                while (ch2Val == value && ch1ch2Distance > 1)
 0308                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 309
 0310                Vector512<byte> ch1 = Vector512.Create(value);
 0311                Vector512<byte> ch2 = Vector512.Create(ch2Val);
 312                do
 313                {
 0314                    Vector512<byte> cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset))
 0315                    Vector512<byte> cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset 
 0316                    Vector512<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 317
 318                    // Early out: cmpAnd is all zeros
 0319                    if (cmpAnd != Vector512<byte>.Zero)
 320                    {
 0321                        ulong mask = cmpAnd.ExtractMostSignificantBits();
 322                        do
 323                        {
 324                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0325                            int highestSetBitIndex = 63 - BitOperations.LeadingZeroCount(mask);
 0326                            if (valueLength == 2 || // we already matched two bytes
 0327                                SequenceEqual(
 0328                                    ref Unsafe.Add(ref searchSpace, offset + highestSetBitIndex),
 0329                                    ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the c
 330                            {
 0331                                return highestSetBitIndex + offset;
 332                            }
 333                            // Clear the highest set bit.
 0334                            mask = BitOperations.FlipBit(mask, highestSetBitIndex);
 0335                        } while (mask != 0);
 336                    }
 337
 0338                    offset -= Vector512<byte>.Count;
 0339                    if (offset == -Vector512<byte>.Count)
 0340                        return -1;
 341                    // Overlap with the current chunk if there is not enough room for the next one
 0342                    if (offset < 0)
 0343                        offset = 0;
 0344                } while (true) ;
 345            }
 0346            else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector256<byte>.Count)
 347            {
 0348                offset = searchSpaceMinusValueTailLength - Vector256<byte>.Count;
 349
 350                // Find the last unique (which is not equal to ch1) byte
 351                // the algorithm is fine if both are equal, just a little bit less efficient
 0352                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 0353                int ch1ch2Distance = valueTailLength;
 0354                while (ch2Val == value && ch1ch2Distance > 1)
 0355                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 356
 0357                Vector256<byte> ch1 = Vector256.Create(value);
 0358                Vector256<byte> ch2 = Vector256.Create(ch2Val);
 359                do
 360                {
 0361                    Vector256<byte> cmpCh1 = Vector256.Equals(ch1, Vector256.LoadUnsafe(ref searchSpace, (nuint)offset))
 0362                    Vector256<byte> cmpCh2 = Vector256.Equals(ch2, Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset 
 0363                    Vector256<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 364
 365                    // Early out: cmpAnd is all zeros
 0366                    if (cmpAnd != Vector256<byte>.Zero)
 367                    {
 0368                        uint mask = cmpAnd.ExtractMostSignificantBits();
 369                        do
 370                        {
 371                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0372                            int highestSetBitIndex = 31 - BitOperations.LeadingZeroCount(mask);
 0373                            if (valueLength == 2 || // we already matched two bytes
 0374                                SequenceEqual(
 0375                                    ref Unsafe.Add(ref searchSpace, offset + highestSetBitIndex),
 0376                                    ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the c
 377                            {
 0378                                return highestSetBitIndex + offset;
 379                            }
 380                            // Clear the highest set bit.
 0381                            mask = BitOperations.FlipBit(mask, highestSetBitIndex);
 0382                        } while (mask != 0);
 383                    }
 384
 0385                    offset -= Vector256<byte>.Count;
 0386                    if (offset == -Vector256<byte>.Count)
 0387                        return -1;
 388                    // Overlap with the current chunk if there is not enough room for the next one
 0389                    if (offset < 0)
 0390                        offset = 0;
 0391                } while (true);
 392            }
 393            else // 128bit vector path (SSE2 or AdvSimd)
 394            {
 0395                offset = searchSpaceMinusValueTailLength - Vector128<byte>.Count;
 396
 397                // Find the last unique (which is not equal to ch1) byte
 398                // the algorithm is fine if both are equal, just a little bit less efficient
 0399                byte ch2Val = Unsafe.Add(ref value, valueTailLength);
 0400                int ch1ch2Distance = valueTailLength;
 0401                while (ch2Val == value && ch1ch2Distance > 1)
 0402                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 403
 0404                Vector128<byte> ch1 = Vector128.Create(value);
 0405                Vector128<byte> ch2 = Vector128.Create(ch2Val);
 406
 407                do
 408                {
 0409                    Vector128<byte> cmpCh1 = Vector128.Equals(ch1, Vector128.LoadUnsafe(ref searchSpace, (nuint)offset))
 0410                    Vector128<byte> cmpCh2 = Vector128.Equals(ch2, Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset 
 0411                    Vector128<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 412
 413                    // Early out: cmpAnd is all zeros
 414                    // it's especially important for ARM where ExtractMostSignificantBits is not cheap
 0415                    if (cmpAnd != Vector128<byte>.Zero)
 416                    {
 0417                        uint mask = cmpAnd.ExtractMostSignificantBits();
 418                        do
 419                        {
 420                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0421                            int highestSetBitIndex = 31 - BitOperations.LeadingZeroCount(mask);
 0422                            if (valueLength == 2 || // we already matched two bytes
 0423                                SequenceEqual(
 0424                                    ref Unsafe.Add(ref searchSpace, offset + highestSetBitIndex),
 0425                                    ref value, (nuint)(uint)valueLength)) // The (nuint)-cast is necessary to pick the c
 426                            {
 0427                                return highestSetBitIndex + offset;
 428                            }
 429                            // Clear the highest set bit.
 0430                            mask = BitOperations.FlipBit(mask, highestSetBitIndex);
 0431                        } while (mask != 0);
 432                    }
 433
 0434                    offset -= Vector128<byte>.Count;
 0435                    if (offset == -Vector128<byte>.Count)
 0436                        return -1;
 437                    // Overlap with the current chunk if there is not enough room for the next one
 0438                    if (offset < 0)
 0439                        offset = 0;
 440
 0441                } while (true);
 442            }
 443        }
 444
 445        [DoesNotReturn]
 446        private static void ThrowMustBeNullTerminatedString()
 447        {
 0448            throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
 449        }
 450
 451        // IndexOfNullByte processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond
 452        // This behavior is an implementation detail of the runtime and callers outside System.Private.CoreLib must not 
 453        internal static unsafe int IndexOfNullByte(byte* searchSpace)
 454        {
 455            const int Length = int.MaxValue;
 456            const uint uValue = 0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
 1016457            nuint offset = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations
 1016458            nuint lengthToExamine = (nuint)(uint)Length;
 459
 1016460            if (Vector128.IsHardwareAccelerated)
 461            {
 462                // Avx2 branch also operates on Sse2 sizes, so check is combined.
 1016463                lengthToExamine = UnalignedCountVector128(searchSpace);
 464            }
 465
 466        SequentialScan:
 1510467            while (lengthToExamine >= 8)
 468            {
 596469                lengthToExamine -= 8;
 470
 596471                if (uValue == searchSpace[offset])
 472                    goto Found;
 596473                if (uValue == searchSpace[offset + 1])
 474                    goto Found1;
 596475                if (uValue == searchSpace[offset + 2])
 476                    goto Found2;
 596477                if (uValue == searchSpace[offset + 3])
 478                    goto Found3;
 595479                if (uValue == searchSpace[offset + 4])
 480                    goto Found4;
 585481                if (uValue == searchSpace[offset + 5])
 482                    goto Found5;
 541483                if (uValue == searchSpace[offset + 6])
 484                    goto Found6;
 523485                if (uValue == searchSpace[offset + 7])
 486                    goto Found7;
 487
 494488                offset += 8;
 489            }
 490
 914491            if (lengthToExamine >= 4)
 492            {
 416493                lengthToExamine -= 4;
 494
 416495                if (uValue == searchSpace[offset])
 496                    goto Found;
 385497                if (uValue == searchSpace[offset + 1])
 498                    goto Found1;
 375499                if (uValue == searchSpace[offset + 2])
 500                    goto Found2;
 350501                if (uValue == searchSpace[offset + 3])
 502                    goto Found3;
 503
 330504                offset += 4;
 505            }
 506
 1937507            while (lengthToExamine > 0)
 508            {
 1160509                lengthToExamine -= 1;
 510
 1160511                if (uValue == searchSpace[offset])
 512                    goto Found;
 513
 1109514                offset += 1;
 515            }
 516
 517            // We get past SequentialScan only if IsHardwareAccelerated is true; and remain length is greater than Vecto
 518            // However, we still have the redundant check to allow the JIT to see that the code is unreachable and elimi
 519            // have hardware accelerated. After processing Vector lengths we return to SequentialScan to finish any rema
 777520            if (Vector512.IsHardwareAccelerated)
 521            {
 0522                if (offset < (nuint)(uint)Length)
 523                {
 0524                    if ((((nuint)(uint)searchSpace + offset) & (nuint)(Vector256<byte>.Count - 1)) != 0)
 525                    {
 526                        // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for se
 527                        // with no upper bound e.g. String.strlen.
 528                        // Start with a check on Vector128 to align to Vector256, before moving to processing Vector256.
 529                        // This ensures we do not fault across memory pages while searching for an end of string.
 0530                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 531
 532                        // Same method as below
 0533                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 0534                        if (matches == 0)
 535                        {
 536                            // Zero flags set so no matches
 0537                            offset += (nuint)Vector128<byte>.Count;
 538                        }
 539                        else
 540                        {
 541                            // Find bitflag offset of first match and add to current offset
 0542                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 543                        }
 544                    }
 545
 0546                    if ((((nuint)(uint)searchSpace + offset) & (nuint)(Vector512<byte>.Count - 1)) != 0)
 547                    {
 548                        // Not currently aligned to Vector512 (is aligned to Vector256); this can cause a problem for se
 549                        // with no upper bound e.g. String.strlen.
 550                        // Start with a check on Vector256 to align to Vector512, before moving to processing Vector256.
 551                        // This ensures we do not fault across memory pages while searching for an end of string.
 0552                        Vector256<byte> search = Vector256.Load(searchSpace + offset);
 553
 554                        // Same method as below
 0555                        uint matches = Vector256.Equals(Vector256<byte>.Zero, search).ExtractMostSignificantBits();
 0556                        if (matches == 0)
 557                        {
 558                            // Zero flags set so no matches
 0559                            offset += (nuint)Vector256<byte>.Count;
 560                        }
 561                        else
 562                        {
 563                            // Find bitflag offset of first match and add to current offset
 0564                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 565                        }
 566                    }
 0567                    lengthToExamine = GetByteVector512SpanLength(offset, Length);
 0568                    if (lengthToExamine > offset)
 569                    {
 570                        do
 571                        {
 0572                            Vector512<byte> search = Vector512.Load(searchSpace + offset);
 0573                            ulong matches = Vector512.Equals(Vector512<byte>.Zero, search).ExtractMostSignificantBits();
 574                            // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 575                            // So the bit position in 'matches' corresponds to the element offset.
 0576                            if (matches == 0)
 577                            {
 578                                // Zero flags set so no matches
 0579                                offset += (nuint)Vector512<byte>.Count;
 0580                                continue;
 581                            }
 582
 583                            // Find bitflag offset of first match and add to current offset
 0584                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 0585                        } while (lengthToExamine > offset);
 586                    }
 587
 0588                    lengthToExamine = GetByteVector256SpanLength(offset, Length);
 0589                    if (lengthToExamine > offset)
 590                    {
 0591                        Vector256<byte> search = Vector256.Load(searchSpace + offset);
 592
 593                        // Same method as above
 0594                        uint matches = Vector256.Equals(Vector256<byte>.Zero, search).ExtractMostSignificantBits();
 0595                        if (matches == 0)
 596                        {
 597                            // Zero flags set so no matches
 0598                            offset += (nuint)Vector256<byte>.Count;
 599                        }
 600                        else
 601                        {
 602                            // Find bitflag offset of first match and add to current offset
 0603                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 604                        }
 605                    }
 606
 0607                    lengthToExamine = GetByteVector128SpanLength(offset, Length);
 0608                    if (lengthToExamine > offset)
 609                    {
 0610                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 611
 612                        // Same method as above
 0613                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 0614                        if (matches == 0)
 615                        {
 616                            // Zero flags set so no matches
 0617                            offset += (nuint)Vector128<byte>.Count;
 618                        }
 619                        else
 620                        {
 621                            // Find bitflag offset of first match and add to current offset
 0622                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 623                        }
 624                    }
 625
 0626                    if (offset < (nuint)(uint)Length)
 627                    {
 0628                        lengthToExamine = ((nuint)(uint)Length - offset);
 0629                        goto SequentialScan;
 630                    }
 631                }
 632            }
 777633            else if (Vector256.IsHardwareAccelerated)
 634            {
 777635                if (offset < (nuint)(uint)Length)
 636                {
 777637                    if ((((nuint)(uint)searchSpace + offset) & (nuint)(Vector256<byte>.Count - 1)) != 0)
 638                    {
 639                        // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for se
 640                        // with no upper bound e.g. String.strlen.
 641                        // Start with a check on Vector128 to align to Vector256, before moving to processing Vector256.
 642                        // This ensures we do not fault across memory pages while searching for an end of string.
 459643                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 644
 645                        // Same method as below
 459646                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 459647                        if (matches == 0)
 648                        {
 649                            // Zero flags set so no matches
 53650                            offset += (nuint)Vector128<byte>.Count;
 651                        }
 652                        else
 653                        {
 654                            // Find bitflag offset of first match and add to current offset
 406655                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 656                        }
 657                    }
 658
 371659                    lengthToExamine = GetByteVector256SpanLength(offset, Length);
 371660                    if (lengthToExamine > offset)
 661                    {
 662                        do
 663                        {
 373664                            Vector256<byte> search = Vector256.Load(searchSpace + offset);
 373665                            uint matches = Vector256.Equals(Vector256<byte>.Zero, search).ExtractMostSignificantBits();
 666                            // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 667                            // So the bit position in 'matches' corresponds to the element offset.
 373668                            if (matches == 0)
 669                            {
 670                                // Zero flags set so no matches
 2671                                offset += (nuint)Vector256<byte>.Count;
 2672                                continue;
 673                            }
 674
 675                            // Find bitflag offset of first match and add to current offset
 371676                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 2677                        } while (lengthToExamine > offset);
 678                    }
 679
 0680                    lengthToExamine = GetByteVector128SpanLength(offset, Length);
 0681                    if (lengthToExamine > offset)
 682                    {
 0683                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 684
 685                        // Same method as above
 0686                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 0687                        if (matches == 0)
 688                        {
 689                            // Zero flags set so no matches
 0690                            offset += (nuint)Vector128<byte>.Count;
 691                        }
 692                        else
 693                        {
 694                            // Find bitflag offset of first match and add to current offset
 0695                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 696                        }
 697                    }
 698
 0699                    if (offset < (nuint)(uint)Length)
 700                    {
 0701                        lengthToExamine = ((nuint)(uint)Length - offset);
 0702                        goto SequentialScan;
 703                    }
 704                }
 705            }
 0706            else if (Vector128.IsHardwareAccelerated)
 707            {
 0708                if (offset < (nuint)(uint)Length)
 709                {
 0710                    lengthToExamine = GetByteVector128SpanLength(offset, Length);
 711
 0712                    while (lengthToExamine > offset)
 713                    {
 0714                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 715
 716                        // Same method as above
 0717                        Vector128<byte> compareResult = Vector128.Equals(Vector128<byte>.Zero, search);
 0718                        if (compareResult == Vector128<byte>.Zero)
 719                        {
 720                            // Zero flags set so no matches
 0721                            offset += (nuint)Vector128<byte>.Count;
 0722                            continue;
 723                        }
 724
 725                        // Find bitflag offset of first match and add to current offset
 0726                        uint matches = compareResult.ExtractMostSignificantBits();
 0727                        return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 728                    }
 729
 0730                    if (offset < (nuint)(uint)Length)
 731                    {
 0732                        lengthToExamine = ((nuint)(uint)Length - offset);
 0733                        goto SequentialScan;
 734                    }
 735                }
 736            }
 737
 0738            ThrowMustBeNullTerminatedString();
 739        Found: // Workaround for https://github.com/dotnet/runtime/issues/8795
 82740            return (int)offset;
 741        Found1:
 10742            return (int)(offset + 1);
 743        Found2:
 25744            return (int)(offset + 2);
 745        Found3:
 21746            return (int)(offset + 3);
 747        Found4:
 10748            return (int)(offset + 4);
 749        Found5:
 44750            return (int)(offset + 5);
 751        Found6:
 18752            return (int)(offset + 6);
 753        Found7:
 29754            return (int)(offset + 7);
 755        }
 756
 757        // Optimized byte-based SequenceEqual. The "length" parameter for this one is declared a nuint rather than int a
 758        // where the length can exceed 2Gb once scaled by sizeof(T).
 759        [Intrinsic] // Unrolled for constant length
 760        public static unsafe bool SequenceEqual(ref byte first, ref byte second, nuint length)
 761        {
 762            bool result;
 763            // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations
 913764            if (length >= (nuint)sizeof(nuint))
 765            {
 766                // Conditional jmp forward to favor shorter lengths. (See comment at "Equal:" label)
 767                // The longer lengths can make back the time due to branch misprediction
 768                // better than shorter lengths.
 769                goto Longer;
 770            }
 771
 772#if TARGET_64BIT
 773            // On 32-bit, this will always be true since sizeof(nuint) == 4
 19774            if (length < sizeof(uint))
 775#endif
 776            {
 0777                uint differentBits = 0;
 0778                nuint offset = (length & 2);
 0779                if (offset != 0)
 780                {
 0781                    differentBits = LoadUShort(ref first);
 0782                    differentBits -= LoadUShort(ref second);
 783                }
 0784                if ((length & 1) != 0)
 785                {
 0786                    differentBits |= (uint)Unsafe.AddByteOffset(ref first, offset) - (uint)Unsafe.AddByteOffset(ref seco
 787                }
 0788                result = (differentBits == 0);
 0789                goto Result;
 790            }
 791#if TARGET_64BIT
 792            else
 793            {
 19794                nuint offset = length - sizeof(uint);
 19795                uint differentBits = LoadUInt(ref first) - LoadUInt(ref second);
 19796                differentBits |= LoadUInt(ref first, offset) - LoadUInt(ref second, offset);
 19797                result = (differentBits == 0);
 19798                goto Result;
 799            }
 800#endif
 801        Longer:
 802            // Only check that the ref is the same if buffers are large,
 803            // and hence its worth avoiding doing unnecessary comparisons
 894804            if (!Unsafe.AreSame(ref first, ref second))
 805            {
 806                // C# compiler inverts this test, making the outer goto the conditional jmp.
 894807                goto Vector;
 808            }
 809
 810            // This becomes a conditional jmp forward to not favor it.
 811            goto Equal;
 812
 813        Result:
 664814            return result;
 815        // When the sequence is equal; which is the longest execution, we want it to determine that
 816        // as fast as possible so we do not want the early outs to be "predicted not taken" branches.
 817        Equal:
 208818            return true;
 819
 820        Vector:
 894821            if (Vector128.IsHardwareAccelerated)
 822            {
 894823                if (Vector512.IsHardwareAccelerated && length >= (nuint)Vector512<byte>.Count)
 824                {
 0825                    nuint offset = 0;
 0826                    nuint lengthToExamine = length - (nuint)Vector512<byte>.Count;
 827                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 0828                    Debug.Assert(lengthToExamine < length);
 0829                    if (lengthToExamine != 0)
 830                    {
 831                        do
 832                        {
 0833                            if (Vector512.LoadUnsafe(ref first, offset) !=
 0834                                Vector512.LoadUnsafe(ref second, offset))
 835                            {
 836                                goto NotEqual;
 837                            }
 0838                            offset += (nuint)Vector512<byte>.Count;
 0839                        } while (lengthToExamine > offset);
 840                    }
 841
 842                    // Do final compare as Vector512<byte>.Count from end rather than start
 0843                    if (Vector512.LoadUnsafe(ref first, lengthToExamine) ==
 0844                        Vector512.LoadUnsafe(ref second, lengthToExamine))
 845                    {
 846                        // C# compiler inverts this test, making the outer goto the conditional jmp.
 0847                        goto Equal;
 848                    }
 849
 850                    // This becomes a conditional jmp forward to not favor it.
 851                    goto NotEqual;
 852                }
 894853                else if (Vector256.IsHardwareAccelerated && length >= (nuint)Vector256<byte>.Count)
 854                {
 93855                    nuint offset = 0;
 93856                    nuint lengthToExamine = length - (nuint)Vector256<byte>.Count;
 857                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 93858                    Debug.Assert(lengthToExamine < length);
 93859                    if (lengthToExamine != 0)
 860                    {
 861                        do
 862                        {
 136863                            if (Vector256.LoadUnsafe(ref first, offset) !=
 136864                                Vector256.LoadUnsafe(ref second, offset))
 865                            {
 866                                goto NotEqual;
 867                            }
 95868                            offset += (nuint)Vector256<byte>.Count;
 95869                        } while (lengthToExamine > offset);
 870                    }
 871
 872                    // Do final compare as Vector256<byte>.Count from end rather than start
 52873                    if (Vector256.LoadUnsafe(ref first, lengthToExamine) ==
 52874                        Vector256.LoadUnsafe(ref second, lengthToExamine))
 875                    {
 876                        // C# compiler inverts this test, making the outer goto the conditional jmp.
 52877                        goto Equal;
 878                    }
 879
 880                    // This becomes a conditional jmp forward to not favor it.
 881                    goto NotEqual;
 882                }
 801883                else if (length >= (nuint)Vector128<byte>.Count)
 884                {
 156885                    nuint offset = 0;
 156886                    nuint lengthToExamine = length - (nuint)Vector128<byte>.Count;
 887                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 156888                    Debug.Assert(lengthToExamine < length);
 156889                    if (lengthToExamine != 0)
 890                    {
 891                        do
 892                        {
 3893                            if (Vector128.LoadUnsafe(ref first, offset) !=
 3894                                Vector128.LoadUnsafe(ref second, offset))
 895                            {
 896                                goto NotEqual;
 897                            }
 3898                            offset += (nuint)Vector128<byte>.Count;
 3899                        } while (lengthToExamine > offset);
 900                    }
 901
 902                    // Do final compare as Vector128<byte>.Count from end rather than start
 156903                    if (Vector128.LoadUnsafe(ref first, lengthToExamine) ==
 156904                        Vector128.LoadUnsafe(ref second, lengthToExamine))
 905                    {
 906                        // C# compiler inverts this test, making the outer goto the conditional jmp.
 156907                        goto Equal;
 908                    }
 909
 910                    // This becomes a conditional jmp forward to not favor it.
 911                    goto NotEqual;
 912                }
 913            }
 914
 915#if TARGET_64BIT
 645916            if (Vector128.IsHardwareAccelerated)
 917            {
 645918                Debug.Assert(length <= (nuint)sizeof(nuint) * 2);
 919
 645920                nuint offset = length - (nuint)sizeof(nuint);
 645921                nuint differentBits = LoadNUInt(ref first) - LoadNUInt(ref second);
 645922                differentBits |= LoadNUInt(ref first, offset) - LoadNUInt(ref second, offset);
 645923                result = (differentBits == 0);
 645924                goto Result;
 925            }
 926            else
 927#endif
 928            {
 0929                Debug.Assert(length >= (nuint)sizeof(nuint));
 930                {
 0931                    nuint offset = 0;
 0932                    nuint lengthToExamine = length - (nuint)sizeof(nuint);
 933                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 0934                    Debug.Assert(lengthToExamine < length);
 0935                    if (lengthToExamine > 0)
 936                    {
 937                        do
 938                        {
 939                            // Compare unsigned so not do a sign extend mov on 64 bit
 0940                            if (LoadNUInt(ref first, offset) != LoadNUInt(ref second, offset))
 941                            {
 942                                goto NotEqual;
 943                            }
 0944                            offset += (nuint)sizeof(nuint);
 0945                        } while (lengthToExamine > offset);
 946                    }
 947
 948                    // Do final compare as sizeof(nuint) from end rather than start
 0949                    result = (LoadNUInt(ref first, lengthToExamine) == LoadNUInt(ref second, lengthToExamine));
 0950                    goto Result;
 951                }
 952            }
 953
 954            // As there are so many true/false exit points the Jit will coalesce them to one location.
 955            // We want them at the end so the conditional early exit jmps are all jmp forwards so the
 956            // branch predictor in a uninitialized state will not take them e.g.
 957            // - loops are conditional jmps backwards and predicted
 958            // - exceptions are conditional forwards jmps and not predicted
 959        NotEqual:
 41960            return false;
 961        }
 962
 963        public static unsafe int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength)
 964        {
 18965            Debug.Assert(firstLength >= 0);
 18966            Debug.Assert(secondLength >= 0);
 967
 18968            if (Unsafe.AreSame(ref first, ref second))
 969                goto Equal;
 970
 18971            nuint minLength = (nuint)(((uint)firstLength < (uint)secondLength) ? (uint)firstLength : (uint)secondLength)
 972
 18973            nuint offset = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations
 18974            nuint lengthToExamine = minLength;
 975
 18976            if (Vector256.IsHardwareAccelerated)
 977            {
 18978                if (Vector512.IsHardwareAccelerated && (lengthToExamine >= (nuint)Vector512<byte>.Count))
 979                {
 0980                    lengthToExamine -= (nuint)Vector512<byte>.Count;
 981                    ulong matches;
 0982                    while (lengthToExamine > offset)
 983                    {
 0984                        matches = Vector512.Equals(Vector512.LoadUnsafe(ref first, offset), Vector512.LoadUnsafe(ref sec
 985                        // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 986                        // So the bit position in 'matches' corresponds to the element offset.
 987
 988                        // 32 elements in Vector256<byte> so we compare to uint.MaxValue to check if everything matched
 0989                        if (matches == ulong.MaxValue)
 990                        {
 991                            // All matched
 0992                            offset += (nuint)Vector512<byte>.Count;
 993                            continue;
 994                        }
 995
 996                        goto Difference;
 997                    }
 998                    // Move to Vector length from end for final compare
 0999                    offset = lengthToExamine;
 1000                    // Same as method as above
 01001                    matches = Vector512.Equals(Vector512.LoadUnsafe(ref first, offset), Vector512.LoadUnsafe(ref second,
 01002                    if (matches == ulong.MaxValue)
 1003                    {
 1004                        // All matched
 1005                        goto Equal;
 1006                    }
 1007                Difference:
 1008                    // Invert matches to find differences
 01009                    ulong differences = ~matches;
 1010                    // Find bitflag offset of first difference and add to current offset
 01011                    offset += (uint)BitOperations.TrailingZeroCount(differences);
 1012
 01013                    int result = Unsafe.AddByteOffset(ref first, offset).CompareTo(Unsafe.AddByteOffset(ref second, offs
 01014                    Debug.Assert(result != 0);
 1015
 01016                    return result;
 1017                }
 1018
 181019                if (lengthToExamine >= (nuint)Vector256<byte>.Count)
 1020                {
 01021                    lengthToExamine -= (nuint)Vector256<byte>.Count;
 1022                    uint matches;
 01023                    while (lengthToExamine > offset)
 1024                    {
 01025                        matches = Vector256.Equals(Vector256.LoadUnsafe(ref first, offset), Vector256.LoadUnsafe(ref sec
 1026                        // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 1027                        // So the bit position in 'matches' corresponds to the element offset.
 1028
 1029                        // 32 elements in Vector256<byte> so we compare to uint.MaxValue to check if everything matched
 01030                        if (matches == uint.MaxValue)
 1031                        {
 1032                            // All matched
 01033                            offset += (nuint)Vector256<byte>.Count;
 1034                            continue;
 1035                        }
 1036
 1037                        goto Difference;
 1038                    }
 1039                    // Move to Vector length from end for final compare
 01040                    offset = lengthToExamine;
 1041                    // Same as method as above
 01042                    matches = Vector256.Equals(Vector256.LoadUnsafe(ref first, offset), Vector256.LoadUnsafe(ref second,
 01043                    if (matches == uint.MaxValue)
 1044                    {
 1045                        // All matched
 1046                        goto Equal;
 1047                    }
 1048                Difference:
 1049                    // Invert matches to find differences
 01050                    uint differences = ~matches;
 1051                    // Find bitflag offset of first difference and add to current offset
 01052                    offset += (uint)BitOperations.TrailingZeroCount(differences);
 1053
 01054                    int result = Unsafe.AddByteOffset(ref first, offset).CompareTo(Unsafe.AddByteOffset(ref second, offs
 01055                    Debug.Assert(result != 0);
 1056
 01057                    return result;
 1058                }
 1059
 181060                if (lengthToExamine >= (nuint)Vector128<byte>.Count)
 1061                {
 01062                    lengthToExamine -= (nuint)Vector128<byte>.Count;
 1063                    uint matches;
 01064                    if (lengthToExamine > offset)
 1065                    {
 01066                        matches = Vector128.Equals(Vector128.LoadUnsafe(ref first, offset), Vector128.LoadUnsafe(ref sec
 1067                        // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 1068                        // So the bit position in 'matches' corresponds to the element offset.
 1069
 1070                        // 16 elements in Vector128<byte> so we compare to ushort.MaxValue to check if everything matche
 01071                        if (matches != ushort.MaxValue)
 1072                        {
 1073                            goto Difference;
 1074                        }
 1075                    }
 1076                    // Move to Vector length from end for final compare
 01077                    offset = lengthToExamine;
 1078                    // Same as method as above
 01079                    matches = Vector128.Equals(Vector128.LoadUnsafe(ref first, offset), Vector128.LoadUnsafe(ref second,
 01080                    if (matches == ushort.MaxValue)
 1081                    {
 1082                        // All matched
 1083                        goto Equal;
 1084                    }
 1085                Difference:
 1086                    // Invert matches to find differences
 01087                    uint differences = ~matches;
 1088                    // Find bitflag offset of first difference and add to current offset
 01089                    offset += (uint)BitOperations.TrailingZeroCount(differences);
 1090
 01091                    int result = Unsafe.AddByteOffset(ref first, offset).CompareTo(Unsafe.AddByteOffset(ref second, offs
 01092                    Debug.Assert(result != 0);
 1093
 01094                    return result;
 1095                }
 1096            }
 01097            else if (Vector128.IsHardwareAccelerated)
 1098            {
 01099                if (lengthToExamine >= (nuint)Vector128<byte>.Count)
 1100                {
 01101                    lengthToExamine -= (nuint)Vector128<byte>.Count;
 01102                    while (lengthToExamine > offset)
 1103                    {
 01104                        if (Vector128.LoadUnsafe(ref first, offset) == Vector128.LoadUnsafe(ref second, offset))
 1105                        {
 1106                            // All matched
 01107                            offset += (nuint)Vector128<byte>.Count;
 1108                            continue;
 1109                        }
 1110
 1111                        goto BytewiseCheck;
 1112                    }
 1113                    // Move to Vector length from end for final compare
 01114                    offset = lengthToExamine;
 01115                    if (Vector128.LoadUnsafe(ref first, offset) == Vector128.LoadUnsafe(ref second, offset))
 1116                    {
 1117                        // All matched
 01118                        goto Equal;
 1119                    }
 1120                    goto BytewiseCheck;
 1121                }
 1122            }
 1123
 181124            if (lengthToExamine > (nuint)sizeof(nuint))
 1125            {
 01126                lengthToExamine -= (nuint)sizeof(nuint);
 01127                while (lengthToExamine > offset)
 1128                {
 01129                    if (LoadNUInt(ref first, offset) != LoadNUInt(ref second, offset))
 1130                    {
 1131                        goto BytewiseCheck;
 1132                    }
 01133                    offset += (nuint)sizeof(nuint);
 1134                }
 1135            }
 1136
 1137        BytewiseCheck:  // Workaround for https://github.com/dotnet/runtime/issues/8795
 621138            while (minLength > offset)
 1139            {
 601140                int result = Unsafe.AddByteOffset(ref first, offset).CompareTo(Unsafe.AddByteOffset(ref second, offset))
 601141                if (result != 0)
 161142                    return result;
 441143                offset += 1;
 1144            }
 1145
 1146        Equal:
 21147            return firstLength - secondLength;
 1148        }
 1149
 1150        public static nuint CommonPrefixLength(ref byte first, ref byte second, nuint length)
 1151        {
 1152            nuint i;
 1153
 1154            // It is ordered this way to match the default branch predictor rules, to don't have too much
 1155            // overhead for short input-lengths.
 01156            if (!Vector128.IsHardwareAccelerated || length < (nuint)Vector128<byte>.Count)
 1157            {
 1158                // To have kind of fast path for small inputs, we handle as much elements needed
 1159                // so that either we are done or can use the unrolled loop below.
 01160                i = length % 4;
 1161
 01162                if (i > 0)
 1163                {
 01164                    if (first != second)
 1165                    {
 01166                        return 0;
 1167                    }
 1168
 01169                    if (i > 1)
 1170                    {
 01171                        if (Unsafe.Add(ref first, 1) != Unsafe.Add(ref second, 1))
 1172                        {
 01173                            return 1;
 1174                        }
 1175
 01176                        if (i > 2 && Unsafe.Add(ref first, 2) != Unsafe.Add(ref second, 2))
 1177                        {
 01178                            return 2;
 1179                        }
 1180                    }
 1181                }
 1182
 01183                for (; (nint)i <= (nint)length - 4; i += 4)
 1184                {
 01185                    if (Unsafe.Add(ref first, i + 0) != Unsafe.Add(ref second, i + 0)) goto Found0;
 01186                    if (Unsafe.Add(ref first, i + 1) != Unsafe.Add(ref second, i + 1)) goto Found1;
 01187                    if (Unsafe.Add(ref first, i + 2) != Unsafe.Add(ref second, i + 2)) goto Found2;
 01188                    if (Unsafe.Add(ref first, i + 3) != Unsafe.Add(ref second, i + 3)) goto Found3;
 1189                }
 1190
 01191                return length;
 1192            Found0:
 01193                return i;
 1194            Found1:
 01195                return i + 1;
 1196            Found2:
 01197                return i + 2;
 1198            Found3:
 01199                return i + 3;
 1200            }
 1201
 01202            Debug.Assert(length >= (uint)Vector128<byte>.Count);
 1203
 1204            uint mask;
 01205            nuint lengthToExamine = length - (nuint)Vector128<byte>.Count;
 1206
 1207            Vector128<byte> maskVec;
 01208            i = 0;
 1209
 01210            while (i < lengthToExamine)
 1211            {
 01212                maskVec = Vector128.Equals(
 01213                    Vector128.LoadUnsafe(ref first, i),
 01214                    Vector128.LoadUnsafe(ref second, i));
 1215
 01216                mask = maskVec.ExtractMostSignificantBits();
 01217                if (mask != 0xFFFF)
 1218                {
 1219                    goto Found;
 1220                }
 1221
 01222                i += (nuint)Vector128<byte>.Count;
 1223            }
 1224
 1225            // Do final compare as Vector128<byte>.Count from end rather than start
 01226            i = lengthToExamine;
 01227            maskVec = Vector128.Equals(
 01228                Vector128.LoadUnsafe(ref first, i),
 01229                Vector128.LoadUnsafe(ref second, i));
 1230
 01231            mask = maskVec.ExtractMostSignificantBits();
 01232            if (mask != 0xFFFF)
 1233            {
 1234                goto Found;
 1235            }
 1236
 01237            return length;
 1238
 1239        Found:
 01240            mask = ~mask;
 01241            return i + uint.TrailingZeroCount(mask);
 1242        }
 1243
 1244        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1245        private static int LocateFirstFoundByte(ulong match)
 1246            => BitOperations.TrailingZeroCount(match) >> 3;
 1247
 1248        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1249        private static int LocateLastFoundByte(ulong match)
 1250            => BitOperations.Log2(match) >> 3;
 1251
 1252        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1253        private static ushort LoadUShort(ref byte start)
 01254            => Unsafe.ReadUnaligned<ushort>(ref start);
 1255
 1256        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1257        private static uint LoadUInt(ref byte start)
 381258            => Unsafe.ReadUnaligned<uint>(ref start);
 1259
 1260        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1261        private static uint LoadUInt(ref byte start, nuint offset)
 381262            => Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref start, offset));
 1263
 1264        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1265        private static nuint LoadNUInt(ref byte start)
 12901266            => Unsafe.ReadUnaligned<nuint>(ref start);
 1267
 1268        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1269        private static nuint LoadNUInt(ref byte start, nuint offset)
 12901270            => Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref start, offset));
 1271
 1272        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1273        private static Vector128<byte> LoadVector128(ref byte start, nuint offset)
 1274            => Unsafe.ReadUnaligned<Vector128<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
 1275
 1276        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1277        private static Vector256<byte> LoadVector256(ref byte start, nuint offset)
 1278            => Unsafe.ReadUnaligned<Vector256<byte>>(ref Unsafe.AddByteOffset(ref start, offset));
 1279
 1280        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1281        private static nuint GetByteVector128SpanLength(nuint offset, int length)
 01282            => (nuint)(uint)((length - (int)offset) & ~(Vector128<byte>.Count - 1));
 1283
 1284        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1285        private static nuint GetByteVector256SpanLength(nuint offset, int length)
 3711286            => (nuint)(uint)((length - (int)offset) & ~(Vector256<byte>.Count - 1));
 1287
 1288        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1289        private static nuint GetByteVector512SpanLength(nuint offset, int length)
 01290            => (nuint)(uint)((length - (int)offset) & ~(Vector512<byte>.Count - 1));
 1291
 1292        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1293        private static unsafe nuint UnalignedCountVector128(byte* searchSpace)
 1294        {
 10161295            nint unaligned = (nint)searchSpace & (Vector128<byte>.Count - 1);
 10161296            return (nuint)(uint)((Vector128<byte>.Count - unaligned) & (Vector128<byte>.Count - 1));
 1297        }
 1298
 1299        public static void Reverse(ref byte buf, nuint length)
 1300        {
 01301            Debug.Assert(length > 1);
 1302
 01303            nint remainder = (nint)length;
 01304            nint offset = 0;
 1305
 01306            if (Vector512.IsHardwareAccelerated && remainder >= Vector512<byte>.Count * 2)
 1307            {
 01308                nint lastOffset = remainder - Vector512<byte>.Count;
 1309                do
 1310                {
 1311                    // Load the values into vectors
 01312                    Vector512<byte> tempFirst = Vector512.LoadUnsafe(ref buf, (nuint)offset);
 01313                    Vector512<byte> tempLast = Vector512.LoadUnsafe(ref buf, (nuint)lastOffset);
 1314
 1315                    // Shuffle to reverse each vector:
 1316                    //     +---------------------------------------------------------------+
 1317                    //     | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P |
 1318                    //     +---------------------------------------------------------------+
 1319                    //          --->
 1320                    //     +---------------------------------------------------------------+
 1321                    //     | P | O | N | M | L | K | J | I | H | G | F | E | D | C | B | A |
 1322                    //     +---------------------------------------------------------------+
 01323                    tempFirst = Vector512.Shuffle(tempFirst, Vector512.Create(
 01324                        (byte)63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48,
 01325                        47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
 01326                        31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
 01327                        15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 01328                    tempLast = Vector512.Shuffle(tempLast, Vector512.Create(
 01329                        (byte)63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48,
 01330                        47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
 01331                        31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
 01332                        15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 1333
 1334                    // Store the reversed vectors
 01335                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 01336                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 1337
 01338                    offset += Vector512<byte>.Count;
 01339                    lastOffset -= Vector512<byte>.Count;
 01340                } while (lastOffset >= offset);
 1341
 01342                remainder = lastOffset + Vector512<byte>.Count - offset;
 1343            }
 01344            else if (Avx2.IsSupported && remainder >= (nint)(Vector256<byte>.Count * 1.5))
 1345            {
 01346                Vector256<byte> reverseMask = Vector256.Create(
 01347                    (byte)15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, // first 128-bit lane
 01348                    15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); // second 128-bit lane
 1349
 01350                nint lastOffset = remainder - Vector256<byte>.Count;
 1351                do
 1352                {
 1353                    // Load the values into vectors
 01354                    Vector256<byte> tempFirst = Vector256.LoadUnsafe(ref buf, (nuint)offset);
 01355                    Vector256<byte> tempLast = Vector256.LoadUnsafe(ref buf, (nuint)lastOffset);
 1356
 1357                    // Avx2 operates on two 128-bit lanes rather than the full 256-bit vector.
 1358                    // Perform a shuffle to reverse each 128-bit lane, then permute to finish reversing the vector:
 1359                    //     +-------------------------------------------------------------------------------+
 1360                    //     | A1 | B1 | C1 | D1 | E1 | F1 | G1 | H1 | I1 | J1 | K1 | L1 | M1 | N1 | O1 | P1 |
 1361                    //     +-------------------------------------------------------------------------------+
 1362                    //     | A2 | B2 | C2 | D2 | E2 | F2 | G2 | H2 | I2 | J2 | K2 | L2 | M2 | N2 | O2 | P2 |
 1363                    //     +-------------------------------------------------------------------------------+
 1364                    //         Shuffle --->
 1365                    //     +-------------------------------------------------------------------------------+
 1366                    //     | P1 | O1 | N1 | M1 | L1 | K1 | J1 | I1 | H1 | G1 | F1 | E1 | D1 | C1 | B1 | A1 |
 1367                    //     +-------------------------------------------------------------------------------+
 1368                    //     | P2 | O2 | N2 | M2 | L2 | K2 | J2 | I2 | H2 | G2 | F2 | E2 | D2 | C2 | B2 | A2 |
 1369                    //     +-------------------------------------------------------------------------------+
 1370                    //         Permute --->
 1371                    //     +-------------------------------------------------------------------------------+
 1372                    //     | P2 | O2 | N2 | M2 | L2 | K2 | J2 | I2 | H2 | G2 | F2 | E2 | D2 | C2 | B2 | A2 |
 1373                    //     +-------------------------------------------------------------------------------+
 1374                    //     | P1 | O1 | N1 | M1 | L1 | K1 | J1 | I1 | H1 | G1 | F1 | E1 | D1 | C1 | B1 | A1 |
 1375                    //     +-------------------------------------------------------------------------------+
 01376                    tempFirst = Avx2.Shuffle(tempFirst, reverseMask);
 01377                    tempFirst = Avx2.Permute2x128(tempFirst, tempFirst, 0b00_01);
 01378                    tempLast = Avx2.Shuffle(tempLast, reverseMask);
 01379                    tempLast = Avx2.Permute2x128(tempLast, tempLast, 0b00_01);
 1380
 1381                    // Store the reversed vectors
 01382                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 01383                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 1384
 01385                    offset += Vector256<byte>.Count;
 01386                    lastOffset -= Vector256<byte>.Count;
 01387                } while (lastOffset >= offset);
 1388
 01389                remainder = lastOffset + Vector256<byte>.Count - offset;
 1390            }
 01391            else if (Vector128.IsHardwareAccelerated && remainder >= Vector128<byte>.Count * 2)
 1392            {
 01393                nint lastOffset = remainder - Vector128<byte>.Count;
 1394                do
 1395                {
 1396                    // Load the values into vectors
 01397                    Vector128<byte> tempFirst = Vector128.LoadUnsafe(ref buf, (nuint)offset);
 01398                    Vector128<byte> tempLast = Vector128.LoadUnsafe(ref buf, (nuint)lastOffset);
 1399
 1400                    // Shuffle to reverse each vector:
 1401                    //     +---------------------------------------------------------------+
 1402                    //     | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P |
 1403                    //     +---------------------------------------------------------------+
 1404                    //          --->
 1405                    //     +---------------------------------------------------------------+
 1406                    //     | P | O | N | M | L | K | J | I | H | G | F | E | D | C | B | A |
 1407                    //     +---------------------------------------------------------------+
 01408                    tempFirst = Vector128.Shuffle(tempFirst, Vector128.Create(
 01409                        (byte)15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 01410                    tempLast = Vector128.Shuffle(tempLast, Vector128.Create(
 01411                        (byte)15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 1412
 1413                    // Store the reversed vectors
 01414                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 01415                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 1416
 01417                    offset += Vector128<byte>.Count;
 01418                    lastOffset -= Vector128<byte>.Count;
 01419                } while (lastOffset >= offset);
 1420
 01421                remainder = lastOffset + Vector128<byte>.Count - offset;
 1422            }
 1423
 01424            if (remainder >= sizeof(long))
 1425            {
 01426                nint lastOffset = (nint)length - offset - sizeof(long);
 1427                do
 1428                {
 01429                    long tempFirst = Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref buf, offset));
 01430                    long tempLast = Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref buf, lastOffset));
 1431
 1432                    // swap and store in reversed position
 01433                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref buf, offset), BinaryPrimitives.ReverseEndianness(tempLast))
 01434                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref buf, lastOffset), BinaryPrimitives.ReverseEndianness(tempFi
 1435
 01436                    offset += sizeof(long);
 01437                    lastOffset -= sizeof(long);
 01438                } while (lastOffset >= offset);
 1439
 01440                remainder = lastOffset + sizeof(long) - offset;
 1441            }
 1442
 01443            if (remainder >= sizeof(int))
 1444            {
 01445                nint lastOffset = (nint)length - offset - sizeof(int);
 1446                do
 1447                {
 01448                    int tempFirst = Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref buf, offset));
 01449                    int tempLast = Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref buf, lastOffset));
 1450
 1451                    // swap and store in reversed position
 01452                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref buf, offset), BinaryPrimitives.ReverseEndianness(tempLast))
 01453                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref buf, lastOffset), BinaryPrimitives.ReverseEndianness(tempFi
 1454
 01455                    offset += sizeof(int);
 01456                    lastOffset -= sizeof(int);
 01457                } while (lastOffset >= offset);
 1458
 01459                remainder = lastOffset + sizeof(int) - offset;
 1460            }
 1461
 01462            if (remainder > 1)
 1463            {
 01464                ReverseInner(ref Unsafe.Add(ref buf, offset), (nuint)remainder);
 1465            }
 01466        }
 1467    }
 1468}

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.ByteMemOps.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
 4#if TARGET_AMD64 || TARGET_ARM64 || (TARGET_32BIT && !TARGET_ARM) || TARGET_LOONGARCH64
 5// JIT is guaranteed to unroll blocks up to 64 bytes in size
 6#define HAS_CUSTOM_BLOCKS
 7#endif
 8
 9using System.Diagnostics;
 10using System.Diagnostics.CodeAnalysis;
 11using System.Numerics;
 12using System.Runtime.CompilerServices;
 13using System.Runtime.InteropServices;
 14
 15namespace System
 16{
 17    internal static partial class SpanHelpers // .ByteMemOps
 18    {
 19#if TARGET_ARM64 || TARGET_LOONGARCH64
 20        private const ulong MemmoveNativeThreshold = ulong.MaxValue;
 21#elif TARGET_ARM
 22        private const nuint MemmoveNativeThreshold = 512;
 23#elif TARGET_WASM && !MONO
 24        private const nuint MemmoveNativeThreshold = 1024;
 25#else
 26        private const nuint MemmoveNativeThreshold = 2048;
 27#endif
 28        private const nuint ZeroMemoryNativeThreshold = 1024;
 29
 30
 31#if HAS_CUSTOM_BLOCKS
 32        [StructLayout(LayoutKind.Sequential, Size = 16)]
 33        private struct Block16 {}
 34
 35        [StructLayout(LayoutKind.Sequential, Size = 64)]
 36        private struct Block64 {}
 37#endif // HAS_CUSTOM_BLOCKS
 38
 39        [Intrinsic] // Unrolled for small constant lengths
 40        internal static void Memmove(ref byte dest, ref byte src, nuint len)
 41        {
 42            // P/Invoke into the native version when the buffers are overlapping.
 8721543            if ((nuint)Unsafe.ByteOffset(ref src, ref dest) < len ||
 8721544                (nuint)Unsafe.ByteOffset(ref dest, ref src) < len)
 45            {
 46                goto BuffersOverlap;
 47            }
 48
 8721549            ref byte srcEnd = ref Unsafe.Add(ref src, len);
 8721550            ref byte destEnd = ref Unsafe.Add(ref dest, len);
 51
 8721552            if (len <= 16)
 53                goto MCPY02;
 5564454            if (len > 64)
 55                goto MCPY05;
 56
 57        MCPY00:
 58            // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
 2506259            Debug.Assert(len > 16 && len <= 64);
 60#if HAS_CUSTOM_BLOCKS
 2506261            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block16>(ref src));
 62#elif TARGET_64BIT
 63            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 64            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 8)));
 65#else
 66            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 67            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 68            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 8)));
 69            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 12)));
 70#endif
 2506271            if (len <= 32)
 72                goto MCPY01;
 73#if HAS_CUSTOM_BLOCKS
 1774274            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src, 16
 75#elif TARGET_64BIT
 76            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 16)))
 77            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 24)))
 78#else
 79            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 16)));
 80            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 20), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 20)));
 81            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 24)));
 82            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 28), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 28)));
 83#endif
 1774284            if (len <= 48)
 85                goto MCPY01;
 86#if HAS_CUSTOM_BLOCKS
 291387            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src, 32
 88#elif TARGET_64BIT
 89            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 32)))
 90            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 40)))
 91#else
 92            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 32)));
 93            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 36), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 36)));
 94            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 40)));
 95            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 44), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 44)));
 96#endif
 97
 98        MCPY01:
 99            // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
 25062100            Debug.Assert(len > 16 && len <= 64);
 101#if HAS_CUSTOM_BLOCKS
 25062102            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src
 103#elif TARGET_64BIT
 104            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd
 105            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 106#else
 107            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 108            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 109            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 110            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 111#endif
 25062112            return;
 113
 114        MCPY02:
 115            // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
 31571116            if ((len & 24) == 0)
 117                goto MCPY03;
 8434118            Debug.Assert(len >= 8 && len <= 16);
 119#if TARGET_64BIT
 8434120            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 8434121            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 122#else
 123            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 124            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 125            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 126            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 127#endif
 8434128            return;
 129
 130        MCPY03:
 131            // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
 23137132            if ((len & 4) == 0)
 133                goto MCPY04;
 7295134            Debug.Assert(len >= 4 && len < 8);
 7295135            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 7295136            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 7295137            return;
 138
 139        MCPY04:
 140            // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
 15842141            Debug.Assert(len < 4);
 15842142            if (len == 0)
 3942143                return;
 11900144            dest = src;
 11900145            if ((len & 2) == 0)
 761146                return;
 11139147            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -2), Unsafe.ReadUnaligned<short>(ref Unsafe.Add(ref srcEnd
 11139148            return;
 149
 150        MCPY05:
 151            // PInvoke to the native version when the copy length exceeds the threshold.
 46746152            if (len > MemmoveNativeThreshold)
 153            {
 154                goto PInvoke;
 155            }
 156
 157#if HAS_CUSTOM_BLOCKS
 45161158            if (len >= 256)
 159            {
 160                // Try to opportunistically align the destination below. The input isn't pinned, so the GC
 161                // is free to move the references. We're therefore assuming that reads may still be unaligned.
 162                //
 163                // dest is more important to align than src because an unaligned store is more expensive
 164                // than an unaligned load.
 10846165                nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64);
 10846166                Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block64>(ref src));
 10846167                src = ref Unsafe.Add(ref src, misalignedElements);
 10846168                dest = ref Unsafe.Add(ref dest, misalignedElements);
 10846169                len -= misalignedElements;
 170            }
 171#endif
 172
 173            // Copy 64-bytes at a time until the remainder is less than 64.
 174            // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 b
 45161175            Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
 45161176            nuint n = len >> 6;
 177
 178        MCPY06:
 179#if HAS_CUSTOM_BLOCKS
 158002180            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block64>(ref src));
 181#elif TARGET_64BIT
 182            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 183            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 8)));
 184            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 16)))
 185            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 24)))
 186            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 32)))
 187            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 40)))
 188            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 48), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 48)))
 189            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 56), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 56)))
 190#else
 191            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 192            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 193            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 8)));
 194            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 12)));
 195            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 16)));
 196            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 20), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 20)));
 197            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 24)));
 198            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 28), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 28)));
 199            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 32)));
 200            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 36), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 36)));
 201            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 40)));
 202            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 44), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 44)));
 203            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 48), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 48)));
 204            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 52), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 52)));
 205            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 56), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 56)));
 206            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 60), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 60)));
 207#endif
 158002208            dest = ref Unsafe.Add(ref dest, 64);
 158002209            src = ref Unsafe.Add(ref src, 64);
 158002210            n--;
 158002211            if (n != 0)
 212                goto MCPY06;
 213
 45161214            len %= 64;
 45161215            if (len > 16)
 216                goto MCPY00;
 217#if HAS_CUSTOM_BLOCKS
 28997218            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src
 219#elif TARGET_64BIT
 220            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd
 221            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 222#else
 223            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 224            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 225            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 226            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 227#endif
 28997228            return;
 229
 230        BuffersOverlap:
 0231            Debug.Assert(len > 0);
 232            // If the buffers overlap perfectly, there's no point to copying the data.
 0233            if (Unsafe.AreSame(ref dest, ref src))
 234            {
 235                // Both could be null with a non-zero length, perform an implicit null check.
 0236                _ = Unsafe.ReadUnaligned<byte>(ref dest);
 0237                return;
 238            }
 239
 240        PInvoke:
 241            // Implicit nullchecks
 1585242            Debug.Assert(len > 0);
 1585243            _ = Unsafe.ReadUnaligned<byte>(ref dest);
 1585244            _ = Unsafe.ReadUnaligned<byte>(ref src);
 1585245            MemmoveNative(ref dest, ref src, len);
 1585246        }
 247
 248        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
 249        // with P/Invoke prolog/epilog.
 250        [MethodImpl(MethodImplOptions.NoInlining)]
 251        private static unsafe void MemmoveNative(ref byte dest, ref byte src, nuint len)
 1585252        {
 1585253            fixed (byte* pDest = &dest)
 1585254            fixed (byte* pSrc = &src)
 255            {
 1585256                memmove(pDest, pSrc, len);
 257            }
 1585258        }
 259
 260#if MONO
 261        [MethodImpl(MethodImplOptions.InternalCall)]
 262        private static extern unsafe void memmove(void* dest, void* src, nuint len);
 263#else
 264#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
 265        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "memmove")]
 266        [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
 267        private static unsafe partial void* memmove(void* dest, void* src, nuint len);
 268#pragma warning restore CS3016
 269#endif
 270
 271        [Intrinsic] // Unrolled for small sizes
 272        public static void ClearWithoutReferences(ref byte dest, nuint len)
 273        {
 72514274            if (len == 0)
 0275                return;
 276
 72514277            ref byte destEnd = ref Unsafe.Add(ref dest, len);
 278
 72514279            if (len <= 16)
 280                goto MZER02;
 66318281            if (len > 64)
 282                goto MZER05;
 283
 284        MZER00:
 285            // Clear bytes which are multiples of 16 and leave the remainder for MZER01 to handle.
 27199286            Debug.Assert(len > 16 && len <= 64);
 287#if HAS_CUSTOM_BLOCKS
 27199288            Unsafe.WriteUnaligned<Block16>(ref dest, default);
 289#elif TARGET_64BIT
 290            Unsafe.WriteUnaligned<long>(ref dest, 0);
 291            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 8), 0);
 292#else
 293            Unsafe.WriteUnaligned<int>(ref dest, 0);
 294            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 295            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 8), 0);
 296            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 12), 0);
 297#endif
 27199298            if (len <= 32)
 299                goto MZER01;
 300#if HAS_CUSTOM_BLOCKS
 14404301            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref dest, 16), default);
 302#elif TARGET_64BIT
 303            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 16), 0);
 304            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 24), 0);
 305#else
 306            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 16), 0);
 307            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 20), 0);
 308            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 24), 0);
 309            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 28), 0);
 310#endif
 14404311            if (len <= 48)
 312                goto MZER01;
 313#if HAS_CUSTOM_BLOCKS
 2685314            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref dest, 32), default);
 315#elif TARGET_64BIT
 316            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 32), 0);
 317            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 40), 0);
 318#else
 319            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 32), 0);
 320            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 36), 0);
 321            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 40), 0);
 322            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 44), 0);
 323#endif
 324
 325        MZER01:
 326            // Unconditionally clear the last 16 bytes using destEnd and return.
 27199327            Debug.Assert(len > 16 && len <= 64);
 328#if HAS_CUSTOM_BLOCKS
 27199329            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref destEnd, -16), default);
 330#elif TARGET_64BIT
 331            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -16), 0);
 332            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 333#else
 334            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -16), 0);
 335            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -12), 0);
 336            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 337            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 338#endif
 27199339            return;
 340
 341        MZER02:
 342            // Clear the first 8 bytes and then unconditionally clear the last 8 bytes and return.
 6196343            if ((len & 24) == 0)
 344                goto MZER03;
 2490345            Debug.Assert(len >= 8 && len <= 16);
 346#if TARGET_64BIT
 2490347            Unsafe.WriteUnaligned<long>(ref dest, 0);
 2490348            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 349#else
 350            Unsafe.WriteUnaligned<int>(ref dest, 0);
 351            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 352            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 353            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 354#endif
 2490355            return;
 356
 357        MZER03:
 358            // Clear the first 4 bytes and then unconditionally clear the last 4 bytes and return.
 3706359            if ((len & 4) == 0)
 360                goto MZER04;
 2154361            Debug.Assert(len >= 4 && len < 8);
 2154362            Unsafe.WriteUnaligned<int>(ref dest, 0);
 2154363            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 2154364            return;
 365
 366        MZER04:
 367            // Clear the first byte. For pending bytes, do an unconditionally clear of the last 2 bytes and return.
 1552368            Debug.Assert(len < 4);
 1552369            if (len == 0)
 0370                return;
 1552371            dest = 0;
 1552372            if ((len & 2) == 0)
 790373                return;
 762374            Unsafe.WriteUnaligned<short>(ref Unsafe.Add(ref destEnd, -2), 0);
 762375            return;
 376
 377        MZER05:
 378            // PInvoke to the native version when the clear length exceeds the threshold.
 55390379            if (len > ZeroMemoryNativeThreshold)
 380            {
 381                goto PInvoke;
 382            }
 383
 384#if HAS_CUSTOM_BLOCKS
 25784385            if (len >= 256)
 386            {
 387                // Try to opportunistically align the destination below. The input isn't pinned, so the GC
 388                // is free to move the references. We're therefore assuming that reads may still be unaligned.
 13068389                nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64);
 13068390                Unsafe.WriteUnaligned<Block64>(ref dest, default);
 13068391                dest = ref Unsafe.Add(ref dest, misalignedElements);
 13068392                len -= misalignedElements;
 393            }
 394#endif
 395            // Clear 64-bytes at a time until the remainder is less than 64.
 396            // If remainder is greater than 16 bytes, then jump to MZER00. Otherwise, unconditionally clear the last 16 
 25784397            Debug.Assert(len > 64 && len <= ZeroMemoryNativeThreshold);
 25784398            nuint n = len >> 6;
 399
 400        MZER06:
 401#if HAS_CUSTOM_BLOCKS
 121413402            Unsafe.WriteUnaligned<Block64>(ref dest, default);
 403#elif TARGET_64BIT
 404            Unsafe.WriteUnaligned<long>(ref dest, 0);
 405            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 8), 0);
 406            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 16), 0);
 407            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 24), 0);
 408            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 32), 0);
 409            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 40), 0);
 410            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 48), 0);
 411            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 56), 0);
 412#else
 413            Unsafe.WriteUnaligned<int>(ref dest, 0);
 414            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 415            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 8), 0);
 416            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 12), 0);
 417            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 16), 0);
 418            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 20), 0);
 419            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 24), 0);
 420            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 28), 0);
 421            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 32), 0);
 422            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 36), 0);
 423            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 40), 0);
 424            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 44), 0);
 425            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 48), 0);
 426            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 52), 0);
 427            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 56), 0);
 428            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 60), 0);
 429#endif
 121413430            dest = ref Unsafe.Add(ref dest, 64);
 121413431            n--;
 121413432            if (n != 0)
 433                goto MZER06;
 434
 25784435            len %= 64;
 25784436            if (len > 16)
 437                goto MZER00;
 438#if HAS_CUSTOM_BLOCKS
 9513439            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref destEnd, -16), default);
 440#elif TARGET_64BIT
 441            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -16), 0);
 442            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 443#else
 444            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -16), 0);
 445            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -12), 0);
 446            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 447            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 448#endif
 9513449            return;
 450
 451        PInvoke:
 452            // Implicit nullchecks
 29606453            _ = Unsafe.ReadUnaligned<byte>(ref dest);
 29606454            ZeroMemoryNative(ref dest, len);
 29606455        }
 456
 457        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
 458        // with P/Invoke prolog/epilog.
 459        [MethodImpl(MethodImplOptions.NoInlining)]
 460        private static unsafe void ZeroMemoryNative(ref byte b, nuint byteLength)
 29606461        {
 29606462            fixed (byte* ptr = &b)
 463            {
 29606464                byte* adjustedPtr = ptr;
 465#if TARGET_X86 || TARGET_AMD64
 29606466                if (byteLength > 0x100)
 467                {
 468                    // memset ends up calling rep stosb if the hardware claims to support it efficiently. rep stosb is u
 469                    // on misaligned blocks. Workaround this issue by aligning the blocks passed to memset upfront.
 29606470                    Unsafe.WriteUnaligned<Block16>(ptr, default);
 29606471                    Unsafe.WriteUnaligned<Block16>(ptr + byteLength - 16, default);
 472
 29606473                    byte* alignedEnd = (byte*)((nuint)(ptr + byteLength - 1) & ~(nuint)(16 - 1));
 474
 29606475                    adjustedPtr = (byte*)(((nuint)ptr + 16) & ~(nuint)(16 - 1));
 29606476                    byteLength = (nuint)(alignedEnd - adjustedPtr);
 477                }
 478#endif
 29606479                memset(adjustedPtr, 0, byteLength);
 480            }
 29606481        }
 482
 483#if MONO
 484        [MethodImpl(MethodImplOptions.InternalCall)]
 485        private static extern unsafe void memset(void* dest, int value, nuint len);
 486#else
 487#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
 488        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "memset")]
 489        [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
 490        private static unsafe partial void* memset(void* dest, int value, nuint len);
 491#pragma warning restore CS3016
 492#endif
 493
 494        internal static void Fill(ref byte dest, byte value, nuint len)
 495        {
 0496            if (!Vector.IsHardwareAccelerated)
 497            {
 498                goto CannotVectorize;
 499            }
 500
 0501            if (len >= (nuint)Vector<byte>.Count)
 502            {
 503                // We have enough data for at least one vectorized write.
 0504                Vector<byte> vector = new(value);
 0505                nuint stopLoopAtOffset = len & (nuint)(nint)(2 * (int)-Vector<byte>.Count); // intentional sign extensio
 0506                nuint offset = 0;
 507
 508                // Loop, writing 2 vectors at a time.
 509                // Compare 'numElements' rather than 'stopLoopAtOffset' because we don't want a dependency
 510                // on the very recently calculated 'stopLoopAtOffset' value.
 0511                if (len >= (uint)(2 * Vector<byte>.Count))
 512                {
 513                    do
 514                    {
 0515                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset), vector);
 0516                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset + (nuint)Vector<byte>.Count), ve
 0517                        offset += (uint)(2 * Vector<byte>.Count);
 0518                    } while (offset < stopLoopAtOffset);
 519                }
 520
 521                // At this point, if any data remains to be written, it's strictly less than
 522                // 2 * sizeof(Vector) bytes. The loop above had us write an even number of vectors.
 523                // If the total byte length instead involves us writing an odd number of vectors, write
 524                // one additional vector now. The bit check below tells us if we're in an "odd vector
 525                // count" situation.
 0526                if ((len & (nuint)Vector<byte>.Count) != 0)
 527                {
 0528                    Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset), vector);
 529                }
 530
 531                // It's possible that some small buffer remains to be populated - something that won't
 532                // fit an entire vector's worth of data. Instead of falling back to a loop, we'll write
 533                // a vector at the very end of the buffer. This may involve overwriting previously
 534                // populated data, which is fine since we're splatting the same value for all entries.
 535                // There's no need to perform a length check here because we already performed this
 536                // check before entering the vectorized code path.
 0537                Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, len - (nuint)Vector<byte>.Count), vector);
 538
 539                // And we're done!
 0540                return;
 541            }
 542
 543        CannotVectorize:
 544
 545            // If we reached this point, we cannot vectorize this T, or there are too few
 546            // elements for us to vectorize. Fall back to an unrolled loop.
 0547            nuint i = 0;
 548
 549            // Write 8 elements at a time
 0550            if (len >= 8)
 551            {
 0552                nuint stopLoopAtOffset = len & ~(nuint)7;
 553                do
 554                {
 0555                    Unsafe.Add(ref dest, (nint)i + 0) = value;
 0556                    Unsafe.Add(ref dest, (nint)i + 1) = value;
 0557                    Unsafe.Add(ref dest, (nint)i + 2) = value;
 0558                    Unsafe.Add(ref dest, (nint)i + 3) = value;
 0559                    Unsafe.Add(ref dest, (nint)i + 4) = value;
 0560                    Unsafe.Add(ref dest, (nint)i + 5) = value;
 0561                    Unsafe.Add(ref dest, (nint)i + 6) = value;
 0562                    Unsafe.Add(ref dest, (nint)i + 7) = value;
 0563                } while ((i += 8) < stopLoopAtOffset);
 564            }
 565
 566            // Write next 4 elements if needed
 0567            if ((len & 4) != 0)
 568            {
 0569                Unsafe.Add(ref dest, (nint)i + 0) = value;
 0570                Unsafe.Add(ref dest, (nint)i + 1) = value;
 0571                Unsafe.Add(ref dest, (nint)i + 2) = value;
 0572                Unsafe.Add(ref dest, (nint)i + 3) = value;
 0573                i += 4;
 574            }
 575
 576            // Write next 2 elements if needed
 0577            if ((len & 2) != 0)
 578            {
 0579                Unsafe.Add(ref dest, (nint)i + 0) = value;
 0580                Unsafe.Add(ref dest, (nint)i + 1) = value;
 0581                i += 2;
 582            }
 583
 584            // Write final element if needed
 0585            if ((len & 1) != 0)
 586            {
 0587                Unsafe.Add(ref dest, (nint)i) = value;
 588            }
 0589        }
 590    }
 591}

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.Char.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.Diagnostics.CodeAnalysis;
 6using System.Numerics;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.Intrinsics;
 9using System.Runtime.Intrinsics.X86;
 10
 11namespace System
 12{
 13    internal static partial class SpanHelpers // .Char
 14    {
 15        public static int IndexOf(ref char searchSpace, int searchSpaceLength, ref char value, int valueLength)
 16        {
 217            Debug.Assert(searchSpaceLength >= 0);
 218            Debug.Assert(valueLength >= 0);
 19
 220            if (valueLength == 0)
 021                return 0;  // A zero-length sequence is always treated as "found" at the start of the search space.
 22
 223            int valueTailLength = valueLength - 1;
 224            if (valueTailLength == 0)
 25            {
 26                // for single-char values use plain IndexOf
 027                return IndexOfChar(ref searchSpace, value, searchSpaceLength);
 28            }
 29
 230            nint offset = 0;
 231            char valueHead = value;
 232            int searchSpaceMinusValueTailLength = searchSpaceLength - valueTailLength;
 233            if (Vector128.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector128<ushort>.Count)
 34            {
 35                goto SEARCH_TWO_CHARS;
 36            }
 37
 038            ref byte valueTail = ref Unsafe.As<char, byte>(ref Unsafe.Add(ref value, 1));
 039            int remainingSearchSpaceLength = searchSpaceMinusValueTailLength;
 40
 041            while (remainingSearchSpaceLength > 0)
 42            {
 43                // Do a quick search for the first element of "value".
 44                // Using the non-packed variant as the input is short and would not benefit from the packed implementati
 045                int relativeIndex = NonPackedIndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueHead, remainingSe
 046                if (relativeIndex < 0)
 47                    break;
 48
 049                remainingSearchSpaceLength -= relativeIndex;
 050                offset += relativeIndex;
 51
 052                if (remainingSearchSpaceLength <= 0)
 53                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 54
 55                // Found the first element of "value". See if the tail matches.
 056                if (SequenceEqual(
 057                        ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + 1)),
 058                        ref valueTail,
 059                        (nuint)(uint)valueTailLength * 2))
 60                {
 061                    return (int)offset;  // The tail matched. Return a successful find.
 62                }
 63
 064                remainingSearchSpaceLength--;
 065                offset++;
 66            }
 067            return -1;
 68
 69            // Based on http://0x80.pl/articles/simd-strfind.html#algorithm-1-generic-simd "Algorithm 1: Generic SIMD" b
 70            // Some details about the implementation can also be found in https://github.com/dotnet/runtime/pull/63285
 71        SEARCH_TWO_CHARS:
 272            if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector512<ushort>.Count >= 0)
 73            {
 74                // Find the last unique (which is not equal to ch1) character
 75                // the algorithm is fine if both are equal, just a little bit less efficient
 076                ushort ch2Val = Unsafe.Add(ref value, valueTailLength);
 077                nint ch1ch2Distance = (nint)(uint)valueTailLength;
 078                while (ch2Val == valueHead && ch1ch2Distance > 1)
 079                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 80
 081                Vector512<ushort> ch1 = Vector512.Create((ushort)valueHead);
 082                Vector512<ushort> ch2 = Vector512.Create(ch2Val);
 83
 084                nint searchSpaceMinusValueTailLengthAndVector =
 085                    searchSpaceMinusValueTailLength - (nint)Vector512<ushort>.Count;
 86
 87                do
 88                {
 89                    // Make sure we don't go out of bounds
 090                    Debug.Assert(offset + ch1ch2Distance + Vector512<ushort>.Count <= searchSpaceLength);
 91
 092                    Vector512<ushort> cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offse
 093                    Vector512<ushort> cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset
 094                    Vector512<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 95
 96                    // Early out: cmpAnd is all zeros
 097                    if (cmpAnd != Vector512<byte>.Zero)
 98                    {
 99                        goto CANDIDATE_FOUND;
 100                    }
 101
 102                LOOP_FOOTER:
 0103                    offset += Vector512<ushort>.Count;
 104
 0105                    if (offset == searchSpaceMinusValueTailLength)
 0106                        return -1;
 107
 108                    // Overlap with the current chunk for trailing elements
 0109                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0110                        offset = searchSpaceMinusValueTailLengthAndVector;
 111
 0112                    continue;
 113
 114                CANDIDATE_FOUND:
 0115                    ulong mask = cmpAnd.ExtractMostSignificantBits();
 116                    do
 117                    {
 0118                        int bitPos = BitOperations.TrailingZeroCount(mask);
 119                        // div by 2 (shr) because we work with 2-byte chars
 0120                        nint charPos = (nint)((uint)bitPos / 2);
 0121                        if (valueLength == 2 || // we already matched two chars
 0122                            SequenceEqual(
 0123                                ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0124                                ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 125                        {
 0126                            return (int)(offset + charPos);
 127                        }
 128
 129                        // Clear two the lowest set bits
 0130                        if (Bmi1.X64.IsSupported)
 0131                            mask = Bmi1.X64.ResetLowestSetBit(Bmi1.X64.ResetLowestSetBit(mask));
 132                        else
 0133                            mask &= ~(ulong)((ulong)0b11 << bitPos);
 0134                    } while (mask != 0);
 0135                    goto LOOP_FOOTER;
 136
 137                } while (true);
 138            }
 2139            else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256<ushort>.Count >= 0)
 140            {
 141                // Find the last unique (which is not equal to ch1) character
 142                // the algorithm is fine if both are equal, just a little bit less efficient
 2143                ushort ch2Val = Unsafe.Add(ref value, valueTailLength);
 2144                nint ch1ch2Distance = valueTailLength;
 2145                while (ch2Val == valueHead && ch1ch2Distance > 1)
 0146                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 147
 2148                Vector256<ushort> ch1 = Vector256.Create((ushort)valueHead);
 2149                Vector256<ushort> ch2 = Vector256.Create(ch2Val);
 150
 2151                nint searchSpaceMinusValueTailLengthAndVector =
 2152                    searchSpaceMinusValueTailLength - (nint)Vector256<ushort>.Count;
 153
 154                do
 155                {
 156                    // Make sure we don't go out of bounds
 14157                    Debug.Assert(offset + ch1ch2Distance + Vector256<ushort>.Count <= searchSpaceLength);
 158
 14159                    Vector256<ushort> cmpCh2 = Vector256.Equals(ch2, Vector256.LoadUnsafe(ref searchSpace, (nuint)(offse
 14160                    Vector256<ushort> cmpCh1 = Vector256.Equals(ch1, Vector256.LoadUnsafe(ref searchSpace, (nuint)offset
 14161                    Vector256<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 162
 163                    // Early out: cmpAnd is all zeros
 14164                    if (cmpAnd != Vector256<byte>.Zero)
 165                    {
 166                        goto CANDIDATE_FOUND;
 167                    }
 168
 169                LOOP_FOOTER:
 14170                    offset += Vector256<ushort>.Count;
 171
 14172                    if (offset == searchSpaceMinusValueTailLength)
 2173                        return -1;
 174
 175                    // Overlap with the current chunk for trailing elements
 12176                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 2177                        offset = searchSpaceMinusValueTailLengthAndVector;
 178
 2179                    continue;
 180
 181                CANDIDATE_FOUND:
 0182                    uint mask = cmpAnd.ExtractMostSignificantBits();
 183                    do
 184                    {
 0185                        nint charPos = (nint)(uint.TrailingZeroCount(mask) / sizeof(ushort));
 0186                        if (valueLength == 2 || // we already matched two chars
 0187                            SequenceEqual(
 0188                                ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0189                                ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 190                        {
 0191                            return (int)(offset + charPos);
 192                        }
 193
 194                        // Clear two the lowest set bits
 0195                        mask = BitOperations.ResetLowestSetBit(BitOperations.ResetLowestSetBit(mask));
 0196                    } while (mask != 0);
 0197                    goto LOOP_FOOTER;
 198
 199                } while (true);
 200            }
 201            else // 128bit vector path (SSE2 or AdvSimd)
 202            {
 203                // Find the last unique (which is not equal to ch1) character
 204                // the algorithm is fine if both are equal, just a little bit less efficient
 0205                ushort ch2Val = Unsafe.Add(ref value, valueTailLength);
 0206                nint ch1ch2Distance = valueTailLength;
 0207                while (ch2Val == valueHead && ch1ch2Distance > 1)
 0208                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 209
 0210                Vector128<ushort> ch1 = Vector128.Create((ushort)valueHead);
 0211                Vector128<ushort> ch2 = Vector128.Create(ch2Val);
 212
 0213                nint searchSpaceMinusValueTailLengthAndVector =
 0214                    searchSpaceMinusValueTailLength - (nint)Vector128<ushort>.Count;
 215
 216                do
 217                {
 218                    // Make sure we don't go out of bounds
 0219                    Debug.Assert(offset + ch1ch2Distance + Vector128<ushort>.Count <= searchSpaceLength);
 220
 0221                    Vector128<ushort> cmpCh2 = Vector128.Equals(ch2, Vector128.LoadUnsafe(ref searchSpace, (nuint)(offse
 0222                    Vector128<ushort> cmpCh1 = Vector128.Equals(ch1, Vector128.LoadUnsafe(ref searchSpace, (nuint)offset
 0223                    Vector128<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 224
 225                    // Early out: cmpAnd is all zeros
 0226                    if (cmpAnd != Vector128<byte>.Zero)
 227                    {
 228                        goto CANDIDATE_FOUND;
 229                    }
 230
 231                LOOP_FOOTER:
 0232                    offset += Vector128<ushort>.Count;
 233
 0234                    if (offset == searchSpaceMinusValueTailLength)
 0235                        return -1;
 236
 237                    // Overlap with the current chunk for trailing elements
 0238                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0239                        offset = searchSpaceMinusValueTailLengthAndVector;
 240
 0241                    continue;
 242
 243                CANDIDATE_FOUND:
 0244                    uint mask = cmpAnd.ExtractMostSignificantBits();
 245                    do
 246                    {
 0247                        nint charPos = (nint)(uint.TrailingZeroCount(mask) / sizeof(ushort));
 0248                        if (valueLength == 2 || // we already matched two chars
 0249                            SequenceEqual(
 0250                                ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0251                                ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 252                        {
 0253                            return (int)(offset + charPos);
 254                        }
 255
 256                        // Clear two lowest set bits
 0257                        mask = BitOperations.ResetLowestSetBit(BitOperations.ResetLowestSetBit(mask));
 0258                    } while (mask != 0);
 0259                    goto LOOP_FOOTER;
 260
 261                } while (true);
 262            }
 263        }
 264
 265        public static int LastIndexOf(ref char searchSpace, int searchSpaceLength, ref char value, int valueLength)
 266        {
 0267            Debug.Assert(searchSpaceLength >= 0);
 0268            Debug.Assert(valueLength >= 0);
 269
 0270            if (valueLength == 0)
 0271                return searchSpaceLength;  // A zero-length sequence is always treated as "found" at the end of the sear
 272
 0273            int valueTailLength = valueLength - 1;
 0274            if (valueTailLength == 0)
 0275                return LastIndexOfValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value, searchSpaceLength
 276
 0277            int offset = 0;
 0278            char valueHead = value;
 0279            int searchSpaceMinusValueTailLength = searchSpaceLength - valueTailLength;
 0280            if (Vector128.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector128<ushort>.Count)
 281            {
 282                goto SEARCH_TWO_CHARS;
 283            }
 284
 0285            ref byte valueTail = ref Unsafe.As<char, byte>(ref Unsafe.Add(ref value, 1));
 286
 0287            while (true)
 288            {
 0289                Debug.Assert(0 <= offset && offset <= searchSpaceLength); // Ensures no deceptive underflows in the comp
 0290                int remainingSearchSpaceLength = searchSpaceLength - offset - valueTailLength;
 0291                if (remainingSearchSpaceLength <= 0)
 292                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 293
 294                // Do a quick search for the first element of "value".
 0295                int relativeIndex = LastIndexOfValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)valueHead, 
 0296                if (relativeIndex == -1)
 297                    break;
 298
 299                // Found the first element of "value". See if the tail matches.
 0300                if (SequenceEqual(
 0301                        ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, relativeIndex + 1)),
 0302                        ref valueTail, (nuint)(uint)valueTailLength * 2))
 303                {
 0304                    return relativeIndex; // The tail matched. Return a successful find.
 305                }
 306
 0307                offset += remainingSearchSpaceLength - relativeIndex;
 308            }
 0309            return -1;
 310
 311            // Based on http://0x80.pl/articles/simd-strfind.html#algorithm-1-generic-simd "Algorithm 1: Generic SIMD" b
 312            // Some details about the implementation can also be found in https://github.com/dotnet/runtime/pull/63285
 313        SEARCH_TWO_CHARS:
 0314            if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector512<ushort>.Count)
 315            {
 0316                offset = searchSpaceMinusValueTailLength - Vector512<ushort>.Count;
 317
 318                // Find the last unique (which is not equal to ch1) char
 319                // the algorithm is fine if both are equal, just a little bit less efficient
 0320                char ch2Val = Unsafe.Add(ref value, valueTailLength);
 0321                int ch1ch2Distance = valueTailLength;
 0322                while (ch2Val == valueHead && ch1ch2Distance > 1)
 0323                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 324
 0325                Vector512<ushort> ch1 = Vector512.Create((ushort)valueHead);
 0326                Vector512<ushort> ch2 = Vector512.Create((ushort)ch2Val);
 327
 328                do
 329                {
 330
 0331                    Vector512<ushort> cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset
 0332                    Vector512<ushort> cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offse
 0333                    Vector512<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 334
 335                    // Early out: cmpAnd is all zeros
 0336                    if (cmpAnd != Vector512<byte>.Zero)
 337                    {
 0338                        ulong mask = cmpAnd.ExtractMostSignificantBits();
 339                        do
 340                        {
 341                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0342                            int bitPos = 62 - BitOperations.LeadingZeroCount(mask);
 0343                            int charPos = (int)((uint)bitPos / 2);
 344
 0345                            if (valueLength == 2 || // we already matched two chars
 0346                                SequenceEqual(
 0347                                    ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0348                                    ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 349                            {
 0350                                return charPos + offset;
 351                            }
 0352                            mask &= ~(ulong)((ulong)0b11 << bitPos); // clear two highest set bits.
 0353                        } while (mask != 0);
 354                    }
 355
 0356                    offset -= Vector512<ushort>.Count;
 0357                    if (offset == -Vector512<ushort>.Count)
 0358                        return -1;
 359                    // Overlap with the current chunk if there is not enough room for the next one
 0360                    if (offset < 0)
 0361                        offset = 0;
 0362                } while (true);
 363            }
 0364            else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength >= Vector256<ushort>.Count)
 365            {
 0366                offset = searchSpaceMinusValueTailLength - Vector256<ushort>.Count;
 367
 368                // Find the last unique (which is not equal to ch1) char
 369                // the algorithm is fine if both are equal, just a little bit less efficient
 0370                char ch2Val = Unsafe.Add(ref value, valueTailLength);
 0371                int ch1ch2Distance = valueTailLength;
 0372                while (ch2Val == valueHead && ch1ch2Distance > 1)
 0373                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 374
 0375                Vector256<ushort> ch1 = Vector256.Create((ushort)valueHead);
 0376                Vector256<ushort> ch2 = Vector256.Create((ushort)ch2Val);
 377
 378                do
 379                {
 380
 0381                    Vector256<ushort> cmpCh1 = Vector256.Equals(ch1, Vector256.LoadUnsafe(ref searchSpace, (nuint)offset
 0382                    Vector256<ushort> cmpCh2 = Vector256.Equals(ch2, Vector256.LoadUnsafe(ref searchSpace, (nuint)(offse
 0383                    Vector256<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 384
 385                    // Early out: cmpAnd is all zeros
 0386                    if (cmpAnd != Vector256<byte>.Zero)
 387                    {
 0388                        uint mask = cmpAnd.ExtractMostSignificantBits();
 389                        do
 390                        {
 391                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0392                            int bitPos = 30 - BitOperations.LeadingZeroCount(mask);
 0393                            int charPos = (int)((uint)bitPos / 2);
 394
 0395                            if (valueLength == 2 || // we already matched two chars
 0396                                SequenceEqual(
 0397                                    ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0398                                    ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 399                            {
 0400                                return charPos + offset;
 401                            }
 0402                            mask &= ~(uint)(0b11 << bitPos); // clear two highest set bits.
 0403                        } while (mask != 0);
 404                    }
 405
 0406                    offset -= Vector256<ushort>.Count;
 0407                    if (offset == -Vector256<ushort>.Count)
 0408                        return -1;
 409                    // Overlap with the current chunk if there is not enough room for the next one
 0410                    if (offset < 0)
 0411                        offset = 0;
 0412                } while (true);
 413            }
 414            else // 128bit vector path (SSE2 or AdvSimd)
 415            {
 0416                offset = searchSpaceMinusValueTailLength - Vector128<ushort>.Count;
 417
 418                // Find the last unique (which is not equal to ch1) char
 419                // the algorithm is fine if both are equal, just a little bit less efficient
 0420                char ch2Val = Unsafe.Add(ref value, valueTailLength);
 0421                int ch1ch2Distance = valueTailLength;
 0422                while (ch2Val == value && ch1ch2Distance > 1)
 0423                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 424
 0425                Vector128<ushort> ch1 = Vector128.Create((ushort)value);
 0426                Vector128<ushort> ch2 = Vector128.Create((ushort)ch2Val);
 427
 428                do
 429                {
 0430                    Vector128<ushort> cmpCh1 = Vector128.Equals(ch1, Vector128.LoadUnsafe(ref searchSpace, (nuint)offset
 0431                    Vector128<ushort> cmpCh2 = Vector128.Equals(ch2, Vector128.LoadUnsafe(ref searchSpace, (nuint)(offse
 0432                    Vector128<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 433
 434                    // Early out: cmpAnd is all zeros
 435                    // it's especially important for ARM where ExtractMostSignificantBits is not cheap
 0436                    if (cmpAnd != Vector128<byte>.Zero)
 437                    {
 0438                        uint mask = cmpAnd.ExtractMostSignificantBits();
 439                        do
 440                        {
 441                            // unlike IndexOf, here we use LZCNT to process matches starting from the end
 0442                            int bitPos = 30 - BitOperations.LeadingZeroCount(mask);
 0443                            int charPos = (int)((uint)bitPos / 2);
 444
 0445                            if (valueLength == 2 || // we already matched two chars
 0446                                SequenceEqual(
 0447                                    ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, offset + charPos)),
 0448                                    ref Unsafe.As<char, byte>(ref value), (nuint)(uint)valueLength * 2))
 449                            {
 0450                                return charPos + offset;
 451                            }
 0452                            mask &= ~(uint)(0b11 << bitPos); // clear two the highest set bits.
 0453                        } while (mask != 0);
 454                    }
 455
 0456                    offset -= Vector128<ushort>.Count;
 0457                    if (offset == -Vector128<ushort>.Count)
 0458                        return -1;
 459                    // Overlap with the current chunk if there is not enough room for the next one
 0460                    if (offset < 0)
 0461                        offset = 0;
 0462                } while (true);
 463            }
 464        }
 465
 466        public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength)
 467        {
 1293468            Debug.Assert(firstLength >= 0);
 1293469            Debug.Assert(secondLength >= 0);
 470
 1293471            int lengthDelta = firstLength - secondLength;
 472
 1293473            if (Unsafe.AreSame(ref first, ref second))
 474                goto Equal;
 475
 1293476            nuint minLength = (nuint)(((uint)firstLength < (uint)secondLength) ? (uint)firstLength : (uint)secondLength)
 1293477            nuint i = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations
 478
 1293479            if (minLength >= (nuint)(sizeof(nuint) / sizeof(char)))
 480            {
 1293481                if (Vector.IsHardwareAccelerated && minLength >= (nuint)Vector<ushort>.Count)
 482                {
 1018483                    nuint nLength = minLength - (nuint)Vector<ushort>.Count;
 484                    do
 485                    {
 1052486                        if (Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, (ni
 1052487                            Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, (n
 488                        {
 489                            break;
 490                        }
 73491                        i += (nuint)Vector<ushort>.Count;
 492                    }
 73493                    while (nLength >= i);
 494                }
 495
 2472496                while (minLength >= (i + (nuint)(sizeof(nuint) / sizeof(char))))
 497                {
 2448498                    if (Unsafe.ReadUnaligned<nuint>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, (nint)i))) !=
 2448499                        Unsafe.ReadUnaligned<nuint>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, (nint)i))))
 500                    {
 501                        break;
 502                    }
 1179503                    i += (nuint)(sizeof(nuint) / sizeof(char));
 504                }
 505            }
 506
 507#if TARGET_64BIT
 1293508            if (minLength >= (i + sizeof(int) / sizeof(char)))
 509            {
 1286510                if (Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, (nint)i))) ==
 1286511                    Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, (nint)i))))
 512                {
 800513                    i += sizeof(int) / sizeof(char);
 514                }
 515            }
 516#endif
 517
 2161518            while (i < minLength)
 519            {
 2148520                int result = Unsafe.Add(ref first, (nint)i).CompareTo(Unsafe.Add(ref second, (nint)i));
 2148521                if (result != 0)
 1280522                    return result;
 868523                i += 1;
 524            }
 525
 526        Equal:
 13527            return lengthDelta;
 528        }
 529
 530        // IndexOfNullCharacter processes memory in aligned chunks, and thus it won't crash even if it accesses memory b
 531        // This behavior is an implementation detail of the runtime and callers outside System.Private.CoreLib must not 
 532        public static unsafe int IndexOfNullCharacter(char* searchSpace)
 533        {
 534            const char value = '\0';
 535            const int length = int.MaxValue;
 536
 34537            nint offset = 0;
 34538            nint lengthToExamine = length;
 539
 34540            if (((int)searchSpace & 1) != 0)
 541            {
 542                // Input isn't char aligned, we won't be able to align it to a Vector
 543            }
 34544            else if (Vector128.IsHardwareAccelerated)
 545            {
 546                // Avx2 branch also operates on Sse2 sizes, so check is combined.
 547                // Needs to be double length to allow us to align the data first.
 34548                lengthToExamine = UnalignedCountVector128(searchSpace);
 549            }
 550
 551        SequentialScan:
 552            // In the non-vector case lengthToExamine is the total length.
 553            // In the vector case lengthToExamine first aligns to Vector,
 554            // then in a second pass after the Vector lengths is the
 555            // remaining data that is shorter than a Vector length.
 41556            while (lengthToExamine >= 4)
 557            {
 7558                if (value == searchSpace[offset])
 559                    goto Found;
 7560                if (value == searchSpace[offset + 1])
 561                    goto Found1;
 7562                if (value == searchSpace[offset + 2])
 563                    goto Found2;
 7564                if (value == searchSpace[offset + 3])
 565                    goto Found3;
 566
 7567                offset += 4;
 7568                lengthToExamine -= 4;
 569            }
 570
 34571            while (lengthToExamine > 0)
 572            {
 0573                if (value == searchSpace[offset])
 574                    goto Found;
 575
 0576                offset++;
 0577                lengthToExamine--;
 578            }
 579
 580            // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant ch
 581            // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware acc
 34582            if (Vector512.IsHardwareAccelerated)
 583            {
 0584                if (offset < length)
 585                {
 0586                    Debug.Assert(length - offset >= Vector128<ushort>.Count);
 0587                    if (((nint)(searchSpace + (nint)offset) & (nint)(Vector256<byte>.Count - 1)) != 0)
 588                    {
 589                        // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for se
 590                        // with no upper bound e.g. String.wcslen. Start with a check on Vector128 to align to Vector256
 591                        // before moving to processing Vector256.
 592
 593                        // This ensures we do not fault across memory pages
 594                        // while searching for an end of string. Specifically that this assumes that the length is eithe
 595                        // or that the data is pinned otherwise it may cause an AccessViolation from crossing a page bou
 596                        // unowned page. If the search is unbounded (e.g. null terminator in wcslen) and the search valu
 597                        // again this will likely cause an AccessViolation. However, correctly bounded searches will ret
 598                        // than ever causing an AV.
 0599                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 600
 601                        // Same method as below
 0602                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0603                        if (matches == 0)
 604                        {
 605                            // Zero flags set so no matches
 0606                            offset += Vector128<ushort>.Count;
 607                        }
 608                        else
 609                        {
 610                            // Find bitflag offset of first match and add to current offset
 0611                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 612                        }
 613                    }
 0614                    if (((nint)(searchSpace + (nint)offset) & (nint)(Vector512<byte>.Count - 1)) != 0)
 615                    {
 616                        // Not currently aligned to Vector512 (is aligned to Vector256); this can cause a problem for se
 617                        // with no upper bound e.g. String.wcslen. Start with a check on Vector256 to align to Vector512
 618                        // before moving to processing Vector256.
 619
 620                        // This ensures we do not fault across memory pages
 621                        // while searching for an end of string. Specifically that this assumes that the length is eithe
 622                        // or that the data is pinned otherwise it may cause an AccessViolation from crossing a page bou
 623                        // unowned page. If the search is unbounded (e.g. null terminator in wcslen) and the search valu
 624                        // again this will likely cause an AccessViolation. However, correctly bounded searches will ret
 625                        // than ever causing an AV.
 0626                        Vector256<ushort> search = *(Vector256<ushort>*)(searchSpace + (nuint)offset);
 627
 628                        // Same method as below
 0629                        uint matches = Vector256.Equals(Vector256<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0630                        if (matches == 0)
 631                        {
 632                            // Zero flags set so no matches
 0633                            offset += Vector256<ushort>.Count;
 634                        }
 635                        else
 636                        {
 637                            // Find bitflag offset of first match and add to current offset
 0638                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 639                        }
 640                    }
 641
 0642                    lengthToExamine = GetCharVector512SpanLength(offset, length);
 0643                    if (lengthToExamine > 0)
 644                    {
 645                        do
 646                        {
 0647                            Debug.Assert(lengthToExamine >= Vector512<ushort>.Count);
 648
 0649                            Vector512<ushort> search = *(Vector512<ushort>*)(searchSpace + (nuint)offset);
 650
 651                            // AVX-512 returns comparison results in a mask register, so we want to optimize
 652                            // the core check to simply be an "none match" check. This will slightly increase
 653                            // the cost for the early match case, but greatly improves perf otherwise.
 654
 0655                            if (!Vector512.EqualsAny(search, Vector512<ushort>.Zero))
 656                            {
 657                                // Zero flags set so no matches
 0658                                offset += Vector512<ushort>.Count;
 0659                                lengthToExamine -= Vector512<ushort>.Count;
 0660                                continue;
 661                            }
 662
 663                            // Note that ExtractMostSignificantBits has converted the equal vector elements into a set o
 664                            // So the bit position in 'matches' corresponds to the element offset.
 665                            //
 666                            // Find bitflag offset of first match and add to current offset,
 667                            // flags are in bytes so divide for chars
 0668                            ulong matches = Vector512.Equals(search, Vector512<ushort>.Zero).ExtractMostSignificantBits(
 0669                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 0670                        } while (lengthToExamine > 0);
 671                    }
 672
 0673                    lengthToExamine = GetCharVector256SpanLength(offset, length);
 0674                    if (lengthToExamine > 0)
 675                    {
 0676                        Debug.Assert(lengthToExamine >= Vector256<ushort>.Count);
 677
 0678                        Vector256<ushort> search = *(Vector256<ushort>*)(searchSpace + (nuint)offset);
 679
 680                        // Same method as above
 0681                        uint matches = Vector256.Equals(Vector256<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0682                        if (matches == 0)
 683                        {
 684                            // Zero flags set so no matches
 0685                            offset += Vector256<ushort>.Count;
 686                            // Don't need to change lengthToExamine here as we don't use its current value again.
 687                        }
 688                        else
 689                        {
 690                            // Find bitflag offset of first match and add to current offset,
 691                            // flags are in bytes so divide for chars
 0692                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 693                        }
 694                    }
 695
 0696                    lengthToExamine = GetCharVector128SpanLength(offset, length);
 0697                    if (lengthToExamine > 0)
 698                    {
 0699                        Debug.Assert(lengthToExamine >= Vector128<ushort>.Count);
 700
 0701                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 702
 703                        // Same method as above
 0704                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0705                        if (matches == 0)
 706                        {
 707                            // Zero flags set so no matches
 0708                            offset += Vector128<ushort>.Count;
 709                            // Don't need to change lengthToExamine here as we don't use its current value again.
 710                        }
 711                        else
 712                        {
 713                            // Find bitflag offset of first match and add to current offset,
 714                            // flags are in bytes so divide for chars
 0715                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 716                        }
 717                    }
 718
 0719                    if (offset < length)
 720                    {
 0721                        lengthToExamine = length - offset;
 0722                        goto SequentialScan;
 723                    }
 724                }
 725            }
 34726            else if (Vector256.IsHardwareAccelerated)
 727            {
 34728                if (offset < length)
 729                {
 34730                    Debug.Assert(length - offset >= Vector128<ushort>.Count);
 34731                    if (((nint)(searchSpace + (nint)offset) & (nint)(Vector256<byte>.Count - 1)) != 0)
 732                    {
 733                        // Not currently aligned to Vector256 (is aligned to Vector128); this can cause a problem for se
 734                        // with no upper bound e.g. String.wcslen. Start with a check on Vector128 to align to Vector256
 735                        // before moving to processing Vector256.
 736
 737                        // This ensures we do not fault across memory pages
 738                        // while searching for an end of string. Specifically that this assumes that the length is eithe
 739                        // or that the data is pinned otherwise it may cause an AccessViolation from crossing a page bou
 740                        // unowned page. If the search is unbounded (e.g. null terminator in wcslen) and the search valu
 741                        // again this will likely cause an AccessViolation. However, correctly bounded searches will ret
 742                        // than ever causing an AV.
 14743                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 744
 745                        // Same method as below
 14746                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 14747                        if (matches == 0)
 748                        {
 749                            // Zero flags set so no matches
 10750                            offset += Vector128<ushort>.Count;
 751                        }
 752                        else
 753                        {
 754                            // Find bitflag offset of first match and add to current offset
 4755                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 756                        }
 757                    }
 758
 30759                    lengthToExamine = GetCharVector256SpanLength(offset, length);
 30760                    if (lengthToExamine > 0)
 761                    {
 762                        do
 763                        {
 58764                            Debug.Assert(lengthToExamine >= Vector256<ushort>.Count);
 765
 58766                            Vector256<ushort> search = *(Vector256<ushort>*)(searchSpace + (nuint)offset);
 58767                            uint matches = Vector256.Equals(Vector256<ushort>.Zero, search).AsByte().ExtractMostSignific
 768                            // Note that MoveMask has converted the equal vector elements into a set of bit flags,
 769                            // So the bit position in 'matches' corresponds to the element offset.
 58770                            if (matches == 0)
 771                            {
 772                                // Zero flags set so no matches
 28773                                offset += Vector256<ushort>.Count;
 28774                                lengthToExamine -= Vector256<ushort>.Count;
 28775                                continue;
 776                            }
 777
 778                            // Find bitflag offset of first match and add to current offset,
 779                            // flags are in bytes so divide for chars
 30780                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 28781                        } while (lengthToExamine > 0);
 782                    }
 783
 0784                    lengthToExamine = GetCharVector128SpanLength(offset, length);
 0785                    if (lengthToExamine > 0)
 786                    {
 0787                        Debug.Assert(lengthToExamine >= Vector128<ushort>.Count);
 788
 0789                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 790
 791                        // Same method as above
 0792                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0793                        if (matches == 0)
 794                        {
 795                            // Zero flags set so no matches
 0796                            offset += Vector128<ushort>.Count;
 797                            // Don't need to change lengthToExamine here as we don't use its current value again.
 798                        }
 799                        else
 800                        {
 801                            // Find bitflag offset of first match and add to current offset,
 802                            // flags are in bytes so divide for chars
 0803                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 804                        }
 805                    }
 806
 0807                    if (offset < length)
 808                    {
 0809                        lengthToExamine = length - offset;
 0810                        goto SequentialScan;
 811                    }
 812                }
 813            }
 0814            else if (Vector128.IsHardwareAccelerated)
 815            {
 0816                if (offset < length)
 817                {
 0818                    Debug.Assert(length - offset >= Vector128<ushort>.Count);
 819
 0820                    lengthToExamine = GetCharVector128SpanLength(offset, length);
 0821                    if (lengthToExamine > 0)
 822                    {
 823                        do
 824                        {
 0825                            Debug.Assert(lengthToExamine >= Vector128<ushort>.Count);
 826
 0827                            Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 828
 829                            // Same method as above
 0830                            Vector128<ushort> compareResult = Vector128.Equals(Vector128<ushort>.Zero, search);
 0831                            if (compareResult == Vector128<ushort>.Zero)
 832                            {
 833                                // Zero flags set so no matches
 0834                                offset += Vector128<ushort>.Count;
 0835                                lengthToExamine -= Vector128<ushort>.Count;
 0836                                continue;
 837                            }
 838
 839                            // Find bitflag offset of first match and add to current offset,
 840                            // flags are in bytes so divide for chars
 0841                            uint matches = compareResult.AsByte().ExtractMostSignificantBits();
 0842                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 0843                        } while (lengthToExamine > 0);
 844                    }
 845
 0846                    if (offset < length)
 847                    {
 0848                        lengthToExamine = length - offset;
 0849                        goto SequentialScan;
 850                    }
 851                }
 852            }
 853
 0854            ThrowMustBeNullTerminatedString();
 855        Found3:
 0856            return (int)(offset + 3);
 857        Found2:
 0858            return (int)(offset + 2);
 859        Found1:
 0860            return (int)(offset + 1);
 861        Found:
 0862            return (int)(offset);
 863        }
 864
 865        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 866        private static int LocateFirstFoundChar(ulong match)
 867            => BitOperations.TrailingZeroCount(match) >> 4;
 868
 869        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 870        private static nint GetCharVector128SpanLength(nint offset, nint length)
 0871            => (length - offset) & ~(Vector128<ushort>.Count - 1);
 872
 873        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 874        private static nint GetCharVector256SpanLength(nint offset, nint length)
 30875            => (length - offset) & ~(Vector256<ushort>.Count - 1);
 876
 877        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 878        private static nint GetCharVector512SpanLength(nint offset, nint length)
 0879            => (length - offset) & ~(Vector512<ushort>.Count - 1);
 880
 881        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 882        private static unsafe nint UnalignedCountVector128(char* searchSpace)
 883        {
 884            const int ElementsPerByte = sizeof(ushort) / sizeof(byte);
 34885            return (nint)(uint)(-(int)searchSpace / ElementsPerByte) & (Vector128<ushort>.Count - 1);
 886        }
 887
 888        public static void Reverse(ref char buf, nuint length)
 889        {
 0890            Debug.Assert(length > 1);
 891
 0892            nint remainder = (nint)length;
 0893            nint offset = 0;
 894
 0895            if (Vector512.IsHardwareAccelerated && remainder >= Vector512<ushort>.Count * 2)
 896            {
 0897                nint lastOffset = remainder - Vector512<ushort>.Count;
 898                do
 899                {
 0900                    ref ushort first = ref Unsafe.As<char, ushort>(ref Unsafe.Add(ref buf, offset));
 0901                    ref ushort last = ref Unsafe.As<char, ushort>(ref Unsafe.Add(ref buf, lastOffset));
 902
 0903                    Vector512<ushort> tempFirst = Vector512.LoadUnsafe(ref first);
 0904                    Vector512<ushort> tempLast = Vector512.LoadUnsafe(ref last);
 905
 906                    // Shuffle to reverse each vector:
 907                    //     +-------------------------------+
 908                    //     | A | B | C | D | E | F | G | H |
 909                    //     +-------------------------------+
 910                    //          --->
 911                    //     +-------------------------------+
 912                    //     | H | G | F | E | D | C | B | A |
 913                    //     +-------------------------------+
 0914                    tempFirst = Vector512.Shuffle(tempFirst, Vector512.Create((ushort)31, 30, 29, 28, 27, 26, 25, 24, 23
 0915                        15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 0916                    tempLast = Vector512.Shuffle(tempLast, Vector512.Create((ushort)31, 30, 29, 28, 27, 26, 25, 24, 23, 
 0917                        15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
 918
 919                    // Store the reversed vectors
 0920                    tempLast.StoreUnsafe(ref first);
 0921                    tempFirst.StoreUnsafe(ref last);
 922
 0923                    offset += Vector512<ushort>.Count;
 0924                    lastOffset -= Vector512<ushort>.Count;
 0925                } while (lastOffset >= offset);
 926
 0927                remainder = (lastOffset + Vector512<ushort>.Count - offset);
 928            }
 929            // overlapping has a positive performance benefit around 24 elements
 0930            else if (Avx2.IsSupported && remainder >= (nint)(Vector256<ushort>.Count * 1.5))
 931            {
 0932                Vector256<byte> reverseMask = Vector256.Create(
 0933                    (byte)14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, // first 128-bit lane
 0934                    14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); // second 128-bit lane
 935
 0936                nint lastOffset = remainder - Vector256<ushort>.Count;
 937                do
 938                {
 0939                    ref byte first = ref Unsafe.As<char, byte>(ref Unsafe.Add(ref buf, offset));
 0940                    ref byte last = ref Unsafe.As<char, byte>(ref Unsafe.Add(ref buf, lastOffset));
 941
 0942                    Vector256<byte> tempFirst = Vector256.LoadUnsafe(ref first);
 0943                    Vector256<byte> tempLast = Vector256.LoadUnsafe(ref last);
 944
 945                    // Avx2 operates on two 128-bit lanes rather than the full 256-bit vector.
 946                    // Perform a shuffle to reverse each 128-bit lane, then permute to finish reversing the vector:
 947                    //     +---------------------------------------------------------------+
 948                    //     | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P |
 949                    //     +---------------------------------------------------------------+
 950                    //         Shuffle --->
 951                    //     +---------------------------------------------------------------+
 952                    //     | H | G | F | E | D | C | B | A | P | O | N | M | L | K | J | I |
 953                    //     +---------------------------------------------------------------+
 954                    //         Permute --->
 955                    //     +---------------------------------------------------------------+
 956                    //     | P | O | N | M | L | K | J | I | H | G | F | E | D | C | B | A |
 957                    //     +---------------------------------------------------------------+
 0958                    tempFirst = Avx2.Shuffle(tempFirst, reverseMask);
 0959                    tempFirst = Avx2.Permute2x128(tempFirst, tempFirst, 0b00_01);
 0960                    tempLast = Avx2.Shuffle(tempLast, reverseMask);
 0961                    tempLast = Avx2.Permute2x128(tempLast, tempLast, 0b00_01);
 962
 963                    // Store the reversed vectors
 0964                    tempLast.StoreUnsafe(ref first);
 0965                    tempFirst.StoreUnsafe(ref last);
 966
 0967                    offset += Vector256<ushort>.Count;
 0968                    lastOffset -= Vector256<ushort>.Count;
 0969                } while (lastOffset >= offset);
 970
 0971                remainder = (lastOffset + Vector256<ushort>.Count - offset);
 972            }
 0973            else if (Vector128.IsHardwareAccelerated && remainder >= Vector128<ushort>.Count * 2)
 974            {
 0975                nint lastOffset = remainder - Vector128<ushort>.Count;
 976                do
 977                {
 0978                    ref ushort first = ref Unsafe.As<char, ushort>(ref Unsafe.Add(ref buf, offset));
 0979                    ref ushort last = ref Unsafe.As<char, ushort>(ref Unsafe.Add(ref buf, lastOffset));
 980
 0981                    Vector128<ushort> tempFirst = Vector128.LoadUnsafe(ref first);
 0982                    Vector128<ushort> tempLast = Vector128.LoadUnsafe(ref last);
 983
 984                    // Shuffle to reverse each vector:
 985                    //     +-------------------------------+
 986                    //     | A | B | C | D | E | F | G | H |
 987                    //     +-------------------------------+
 988                    //          --->
 989                    //     +-------------------------------+
 990                    //     | H | G | F | E | D | C | B | A |
 991                    //     +-------------------------------+
 0992                    tempFirst = Vector128.Shuffle(tempFirst, Vector128.Create((ushort)7, 6, 5, 4, 3, 2, 1, 0));
 0993                    tempLast = Vector128.Shuffle(tempLast, Vector128.Create((ushort)7, 6, 5, 4, 3, 2, 1, 0));
 994
 995                    // Store the reversed vectors
 0996                    tempLast.StoreUnsafe(ref first);
 0997                    tempFirst.StoreUnsafe(ref last);
 998
 0999                    offset += Vector128<ushort>.Count;
 01000                    lastOffset -= Vector128<ushort>.Count;
 01001                } while (lastOffset >= offset);
 1002
 01003                remainder = (lastOffset + Vector128<ushort>.Count - offset);
 1004            }
 1005
 1006            // Store any remaining values one-by-one
 01007            if (remainder > 1)
 1008            {
 01009                ReverseInner(ref Unsafe.Add(ref buf, offset), (nuint)remainder);
 1010            }
 01011        }
 1012    }
 1013}

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.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.Runtime.CompilerServices;
 6using System.Runtime.Intrinsics;
 7using System.Runtime.Intrinsics.X86;
 8
 9namespace System
 10{
 11    internal static partial class SpanHelpers
 12    {
 13        public static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
 14        {
 115            Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref ip, (uint)sizeof(IntPtr)), "Should've been aligned on nat
 16
 17            // First write backward 8 natural words at a time.
 18            // Writing backward allows us to get away with only simple modifications to the
 19            // mov instruction's base and index registers between loop iterations.
 20
 321            for (; pointerSizeLength >= 8; pointerSizeLength -= 8)
 22            {
 123                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -1) = default;
 124                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -2) = default;
 125                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -3) = default;
 126                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -4) = default;
 127                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -5) = default;
 128                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -6) = default;
 129                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -7) = default;
 130                Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -8) = default;
 31            }
 32
 133            Debug.Assert(pointerSizeLength <= 7);
 34
 35            // The logic below works by trying to minimize the number of branches taken for any
 36            // given range of lengths. For example, the lengths [ 4 .. 7 ] are handled by a single
 37            // branch, [ 2 .. 3 ] are handled by a single branch, and [ 1 ] is handled by a single
 38            // branch.
 39            //
 40            // We can write both forward and backward as a perf improvement. For example,
 41            // the lengths [ 4 .. 7 ] can be handled by zeroing out the first four natural
 42            // words and the last 3 natural words. In the best case (length = 7), there are
 43            // no overlapping writes. In the worst case (length = 4), there are three
 44            // overlapping writes near the middle of the buffer. In perf testing, the
 45            // penalty for performing duplicate writes is less expensive than the penalty
 46            // for complex branching.
 47
 148            if (pointerSizeLength >= 4)
 49            {
 50                goto Write4To7;
 51            }
 052            else if (pointerSizeLength >= 2)
 53            {
 54                goto Write2To3;
 55            }
 056            else if (pointerSizeLength > 0)
 57            {
 58                goto Write1;
 59            }
 60            else
 61            {
 062                return; // nothing to write
 63            }
 64
 65        Write4To7:
 166            Debug.Assert(pointerSizeLength >= 4);
 67
 68            // Write first four and last three.
 169            Unsafe.Add(ref ip, 2) = default;
 170            Unsafe.Add(ref ip, 3) = default;
 171            Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -3) = default;
 172            Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -2) = default;
 73
 74        Write2To3:
 175            Debug.Assert(pointerSizeLength >= 2);
 76
 77            // Write first two and last one.
 178            Unsafe.Add(ref ip, 1) = default;
 179            Unsafe.Add(ref Unsafe.Add(ref ip, (nint)pointerSizeLength), -1) = default;
 80
 81        Write1:
 182            Debug.Assert(pointerSizeLength >= 1);
 83
 84            // Write only element.
 185            ip = default;
 186        }
 87
 88        public static void Reverse(ref int buf, nuint length)
 89        {
 090            Debug.Assert(length > 1);
 91
 092            nint remainder = (nint)length;
 093            nint offset = 0;
 94
 095            if (Vector512.IsHardwareAccelerated && remainder >= Vector512<int>.Count * 2)
 96            {
 097                nint lastOffset = remainder - Vector512<int>.Count;
 98                do
 99                {
 100                    // Load in values from beginning and end of the array.
 0101                    Vector512<int> tempFirst = Vector512.LoadUnsafe(ref buf, (nuint)offset);
 0102                    Vector512<int> tempLast = Vector512.LoadUnsafe(ref buf, (nuint)lastOffset);
 103
 104                    // Shuffle to reverse each vector:
 105                    //     +---------------+
 106                    //     | A | B | C | D |
 107                    //     +---------------+
 108                    //          --->
 109                    //     +---------------+
 110                    //     | D | C | B | A |
 111                    //     +---------------+
 0112                    tempFirst = Vector512.Shuffle(tempFirst, Vector512.Create(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 
 0113                    tempLast = Vector512.Shuffle(tempLast, Vector512.Create(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3,
 114
 115                    // Store the reversed vectors
 0116                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0117                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 118
 0119                    offset += Vector512<int>.Count;
 0120                    lastOffset -= Vector512<int>.Count;
 0121                } while (lastOffset >= offset);
 122
 0123                remainder = lastOffset + Vector512<int>.Count - offset;
 124            }
 0125            else if (Avx2.IsSupported && remainder >= Vector256<int>.Count * 2)
 126            {
 0127                nint lastOffset = remainder - Vector256<int>.Count;
 128                do
 129                {
 130                    // Load the values into vectors
 0131                    Vector256<int> tempFirst = Vector256.LoadUnsafe(ref buf, (nuint)offset);
 0132                    Vector256<int> tempLast = Vector256.LoadUnsafe(ref buf, (nuint)lastOffset);
 133
 134                    // Permute to reverse each vector:
 135                    //     +-------------------------------+
 136                    //     | A | B | C | D | E | F | G | H |
 137                    //     +-------------------------------+
 138                    //         --->
 139                    //     +-------------------------------+
 140                    //     | H | G | F | E | D | C | B | A |
 141                    //     +-------------------------------+
 0142                    tempFirst = Avx2.PermuteVar8x32(tempFirst, Vector256.Create(7, 6, 5, 4, 3, 2, 1, 0));
 0143                    tempLast = Avx2.PermuteVar8x32(tempLast, Vector256.Create(7, 6, 5, 4, 3, 2, 1, 0));
 144
 145                    // Store the reversed vectors
 0146                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0147                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 148
 0149                    offset += Vector256<int>.Count;
 0150                    lastOffset -= Vector256<int>.Count;
 0151                } while (lastOffset >= offset);
 152
 0153                remainder = lastOffset + Vector256<int>.Count - offset;
 154            }
 0155            else if (Vector128.IsHardwareAccelerated && remainder >= Vector128<int>.Count * 2)
 156            {
 0157                nint lastOffset = remainder - Vector128<int>.Count;
 158                do
 159                {
 160                    // Load in values from beginning and end of the array.
 0161                    Vector128<int> tempFirst = Vector128.LoadUnsafe(ref buf, (nuint)offset);
 0162                    Vector128<int> tempLast = Vector128.LoadUnsafe(ref buf, (nuint)lastOffset);
 163
 164                    // Shuffle to reverse each vector:
 165                    //     +---------------+
 166                    //     | A | B | C | D |
 167                    //     +---------------+
 168                    //          --->
 169                    //     +---------------+
 170                    //     | D | C | B | A |
 171                    //     +---------------+
 0172                    tempFirst = Vector128.Shuffle(tempFirst, Vector128.Create(3, 2, 1, 0));
 0173                    tempLast = Vector128.Shuffle(tempLast, Vector128.Create(3, 2, 1, 0));
 174
 175                    // Store the reversed vectors
 0176                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0177                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 178
 0179                    offset += Vector128<int>.Count;
 0180                    lastOffset -= Vector128<int>.Count;
 0181                } while (lastOffset >= offset);
 182
 0183                remainder = lastOffset + Vector128<int>.Count - offset;
 184            }
 185
 186            // Store any remaining values one-by-one
 0187            if (remainder > 1)
 188            {
 0189                ReverseInner(ref Unsafe.Add(ref buf, offset), (nuint)remainder);
 190            }
 0191        }
 192
 193        public static void Reverse(ref long buf, nuint length)
 194        {
 0195            Debug.Assert(length > 1);
 196
 0197            nint remainder = (nint)length;
 0198            nint offset = 0;
 199
 0200            if (Vector512.IsHardwareAccelerated && remainder >= Vector512<long>.Count * 2)
 201            {
 0202                nint lastOffset = remainder - Vector512<long>.Count;
 203                do
 204                {
 205                    // Load in values from beginning and end of the array.
 0206                    Vector512<long> tempFirst = Vector512.LoadUnsafe(ref buf, (nuint)offset);
 0207                    Vector512<long> tempLast = Vector512.LoadUnsafe(ref buf, (nuint)lastOffset);
 208
 209                    // Shuffle to reverse each vector:
 210                    //     +-------+
 211                    //     | A | B |
 212                    //     +-------+
 213                    //          --->
 214                    //     +-------+
 215                    //     | B | A |
 216                    //     +-------+
 0217                    tempFirst = Vector512.Shuffle(tempFirst, Vector512.Create(7, 6, 5, 4, 3, 2, 1, 0));
 0218                    tempLast = Vector512.Shuffle(tempLast, Vector512.Create(7, 6, 5, 4, 3, 2, 1, 0));
 219
 220                    // Store the reversed vectors
 0221                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0222                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 223
 0224                    offset += Vector512<long>.Count;
 0225                    lastOffset -= Vector512<long>.Count;
 0226                } while (lastOffset >= offset);
 227
 0228                remainder = lastOffset + Vector512<long>.Count - offset;
 229            }
 0230            else if (Avx2.IsSupported && remainder >= Vector256<long>.Count * 2)
 231            {
 0232                nint lastOffset = remainder - Vector256<long>.Count;
 233                do
 234                {
 235                    // Load the values into vectors
 0236                    Vector256<long> tempFirst = Vector256.LoadUnsafe(ref buf, (nuint)offset);
 0237                    Vector256<long> tempLast = Vector256.LoadUnsafe(ref buf, (nuint)lastOffset);
 238
 239                    // Permute to reverse each vector:
 240                    //     +---------------+
 241                    //     | A | B | C | D |
 242                    //     +---------------+
 243                    //         --->
 244                    //     +---------------+
 245                    //     | D | C | B | A |
 246                    //     +---------------+
 0247                    tempFirst = Avx2.Permute4x64(tempFirst, 0b00_01_10_11);
 0248                    tempLast = Avx2.Permute4x64(tempLast, 0b00_01_10_11);
 249
 250                    // Store the reversed vectors
 0251                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0252                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 253
 0254                    offset += Vector256<long>.Count;
 0255                    lastOffset -= Vector256<long>.Count;
 0256                } while (lastOffset >= offset);
 257
 0258                remainder = lastOffset + Vector256<long>.Count - offset;
 259            }
 0260            else if (Vector128.IsHardwareAccelerated && remainder >= Vector128<long>.Count * 2)
 261            {
 0262                nint lastOffset = remainder - Vector128<long>.Count;
 263                do
 264                {
 265                    // Load in values from beginning and end of the array.
 0266                    Vector128<long> tempFirst = Vector128.LoadUnsafe(ref buf, (nuint)offset);
 0267                    Vector128<long> tempLast = Vector128.LoadUnsafe(ref buf, (nuint)lastOffset);
 268
 269                    // Shuffle to reverse each vector:
 270                    //     +-------+
 271                    //     | A | B |
 272                    //     +-------+
 273                    //          --->
 274                    //     +-------+
 275                    //     | B | A |
 276                    //     +-------+
 0277                    tempFirst = Vector128.Shuffle(tempFirst, Vector128.Create(1, 0));
 0278                    tempLast = Vector128.Shuffle(tempLast, Vector128.Create(1, 0));
 279
 280                    // Store the reversed vectors
 0281                    tempLast.StoreUnsafe(ref buf, (nuint)offset);
 0282                    tempFirst.StoreUnsafe(ref buf, (nuint)lastOffset);
 283
 0284                    offset += Vector128<long>.Count;
 0285                    lastOffset -= Vector128<long>.Count;
 0286                } while (lastOffset >= offset);
 287
 0288                remainder = lastOffset + Vector128<long>.Count - offset;
 289            }
 290
 291            // Store any remaining values one-by-one
 0292            if (remainder > 1)
 293            {
 0294                ReverseInner(ref Unsafe.Add(ref buf, offset), (nuint)remainder);
 295            }
 0296        }
 297
 298        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 299        public static unsafe void Reverse<T>(ref T elements, nuint length)
 300        {
 0301            Debug.Assert(length > 1);
 302
 0303            if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 304            {
 0305                if (sizeof(T) == sizeof(byte))
 306                {
 0307                    Reverse(ref Unsafe.As<T, byte>(ref elements), length);
 0308                    return;
 309                }
 0310                else if (sizeof(T) == sizeof(char))
 311                {
 0312                    Reverse(ref Unsafe.As<T, char>(ref elements), length);
 0313                    return;
 314                }
 0315                else if (sizeof(T) == sizeof(int))
 316                {
 0317                    Reverse(ref Unsafe.As<T, int>(ref elements), length);
 0318                    return;
 319                }
 0320                else if (sizeof(T) == sizeof(long))
 321                {
 0322                    Reverse(ref Unsafe.As<T, long>(ref elements), length);
 0323                    return;
 324                }
 325            }
 326
 0327            ReverseInner(ref elements, length);
 0328        }
 329
 330        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 331        private static void ReverseInner<T>(ref T elements, nuint length)
 332        {
 0333            Debug.Assert(length > 1);
 334
 0335            ref T first = ref elements;
 0336            ref T last = ref Unsafe.Subtract(ref Unsafe.Add(ref first, length), 1);
 337            do
 338            {
 0339                T temp = first;
 0340                first = last;
 0341                last = temp;
 0342                first = ref Unsafe.Add(ref first, 1);
 0343                last = ref Unsafe.Subtract(ref last, 1);
 0344            } while (Unsafe.IsAddressLessThan(ref first, ref last));
 0345        }
 346    }
 347}

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SpanHelpers.T.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.Collections.Generic;
 5using System.Diagnostics;
 6using System.Numerics;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.Intrinsics;
 9
 10namespace System
 11{
 12    internal static partial class SpanHelpers // .T
 13    {
 14        [Intrinsic] // Unrolled for small sizes
 15        public static unsafe void Fill<T>(ref T refData, nuint numElements, T value)
 16        {
 17            // Early checks to see if it's even possible to vectorize - JIT will turn these checks into consts.
 18            // - T cannot contain references (GC can't track references in vectors)
 19            // - Vectorization must be hardware-accelerated
 20            // - T's size must not exceed the vector's size
 21            // - T's size must be a whole power of 2
 22
 195123            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 24            {
 25                goto CannotVectorize;
 26            }
 27
 195128            if (!Vector.IsHardwareAccelerated)
 29            {
 30                goto CannotVectorize;
 31            }
 32
 195133            if (sizeof(T) > Vector<byte>.Count)
 34            {
 35                goto CannotVectorize;
 36            }
 37
 195138            if (!BitOperations.IsPow2(sizeof(T)))
 39            {
 40                goto CannotVectorize;
 41            }
 42
 195143            if (numElements >= (uint)(Vector<byte>.Count / sizeof(T)))
 44            {
 45                // We have enough data for at least one vectorized write.
 46                Vector<byte> vector;
 47
 120948                if (sizeof(T) == 1)
 49                {
 050                    vector = new Vector<byte>(Unsafe.BitCast<T, byte>(value));
 51                }
 120952                else if (sizeof(T) == 2)
 53                {
 120954                    vector = (Vector<byte>)new Vector<ushort>(Unsafe.BitCast<T, ushort>(value));
 55                }
 056                else if (sizeof(T) == 4)
 57                {
 58                    // special-case float since it's already passed in a SIMD reg
 059                    vector = (typeof(T) == typeof(float))
 060                        ? (Vector<byte>)new Vector<float>(Unsafe.BitCast<T, float>(value))
 061                        : (Vector<byte>)new Vector<uint>(Unsafe.BitCast<T, uint>(value));
 62                }
 063                else if (sizeof(T) == 8)
 64                {
 65                    // special-case double since it's already passed in a SIMD reg
 066                    vector = (typeof(T) == typeof(double))
 067                        ? (Vector<byte>)new Vector<double>(Unsafe.BitCast<T, double>(value))
 068                        : (Vector<byte>)new Vector<ulong>(Unsafe.BitCast<T, ulong>(value));
 69                }
 070                else if (sizeof(T) == Vector<byte>.Count)
 71                {
 072                    vector = Unsafe.BitCast<T, Vector<byte>>(value);
 73                }
 074                else if (sizeof(T) == 16)
 75                {
 076                    if (Vector<byte>.Count == 32)
 77                    {
 078                        vector = Vector256.Create(Unsafe.BitCast<T, Vector128<byte>>(value)).AsVector();
 79                    }
 080                    else if (Vector<byte>.Count == 64)
 81                    {
 082                        vector = Vector512.Create(Unsafe.BitCast<T, Vector128<byte>>(value)).AsVector();
 83                    }
 84                    else
 85                    {
 086                        Debug.Fail("Vector<T> is unexpected size.");
 87                        goto CannotVectorize;
 88                    }
 89                }
 090                else if (sizeof(T) == 32)
 91                {
 092                    if (Vector<byte>.Count == 64)
 93                    {
 094                        vector = Vector512.Create(Unsafe.BitCast<T, Vector256<byte>>(value)).AsVector();
 95                    }
 96                    else
 97                    {
 098                        Debug.Fail("Vector<T> is unexpected size.");
 99                        goto CannotVectorize;
 100                    }
 101                }
 102                else
 103                {
 0104                    Debug.Fail("Vector<T> is greater than 512 bits in size?");
 105                    goto CannotVectorize;
 106                }
 107
 1209108                ref byte refDataAsBytes = ref Unsafe.As<T, byte>(ref refData);
 1209109                nuint totalByteLength = numElements * (nuint)sizeof(T); // get this calculation ready ahead of time
 1209110                nuint stopLoopAtOffset = totalByteLength & (nuint)(nint)(2 * (int)-Vector<byte>.Count); // intentional s
 1209111                nuint offset = 0;
 112
 113                // Loop, writing 2 vectors at a time.
 114                // Compare 'numElements' rather than 'stopLoopAtOffset' because we don't want a dependency
 115                // on the very recently calculated 'stopLoopAtOffset' value.
 116
 1209117                if (numElements >= (uint)(2 * Vector<byte>.Count / sizeof(T)))
 118                {
 119                    do
 120                    {
 18289121                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset), vector);
 18289122                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset + (nuint)Vector<byte>.
 18289123                        offset += (uint)(2 * Vector<byte>.Count);
 18289124                    } while (offset < stopLoopAtOffset);
 125                }
 126
 127                // At this point, if any data remains to be written, it's strictly less than
 128                // 2 * sizeof(Vector) bytes. The loop above had us write an even number of vectors.
 129                // If the total byte length instead involves us writing an odd number of vectors, write
 130                // one additional vector now. The bit check below tells us if we're in an "odd vector
 131                // count" situation.
 132
 1209133                if ((totalByteLength & (nuint)Vector<byte>.Count) != 0)
 134                {
 797135                    Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset), vector);
 136                }
 137
 138                // It's possible that some small buffer remains to be populated - something that won't
 139                // fit an entire vector's worth of data. Instead of falling back to a loop, we'll write
 140                // a vector at the very end of the buffer. This may involve overwriting previously
 141                // populated data, which is fine since we're splatting the same value for all entries.
 142                // There's no need to perform a length check here because we already performed this
 143                // check before entering the vectorized code path.
 144
 1209145                Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, totalByteLength - (nuint)Vector<byte>
 146
 147                // And we're done!
 148
 1209149                return;
 150            }
 151
 152        CannotVectorize:
 153
 154            // If we reached this point, we cannot vectorize this T, or there are too few
 155            // elements for us to vectorize. Fall back to an unrolled loop.
 156
 742157            nuint i = 0;
 158
 159            // Write 8 elements at a time
 160
 742161            if (numElements >= 8)
 162            {
 304163                nuint stopLoopAtOffset = numElements & ~(nuint)7;
 164                do
 165                {
 304166                    Unsafe.Add(ref refData, (nint)i + 0) = value;
 304167                    Unsafe.Add(ref refData, (nint)i + 1) = value;
 304168                    Unsafe.Add(ref refData, (nint)i + 2) = value;
 304169                    Unsafe.Add(ref refData, (nint)i + 3) = value;
 304170                    Unsafe.Add(ref refData, (nint)i + 4) = value;
 304171                    Unsafe.Add(ref refData, (nint)i + 5) = value;
 304172                    Unsafe.Add(ref refData, (nint)i + 6) = value;
 304173                    Unsafe.Add(ref refData, (nint)i + 7) = value;
 304174                } while ((i += 8) < stopLoopAtOffset);
 175            }
 176
 177            // Write next 4 elements if needed
 178
 742179            if ((numElements & 4) != 0)
 180            {
 339181                Unsafe.Add(ref refData, (nint)i + 0) = value;
 339182                Unsafe.Add(ref refData, (nint)i + 1) = value;
 339183                Unsafe.Add(ref refData, (nint)i + 2) = value;
 339184                Unsafe.Add(ref refData, (nint)i + 3) = value;
 339185                i += 4;
 186            }
 187
 188            // Write next 2 elements if needed
 189
 742190            if ((numElements & 2) != 0)
 191            {
 742192                Unsafe.Add(ref refData, (nint)i + 0) = value;
 742193                Unsafe.Add(ref refData, (nint)i + 1) = value;
 742194                i += 2;
 195            }
 196
 197            // Write final element if needed
 198
 742199            if ((numElements & 1) != 0)
 200            {
 742201                Unsafe.Add(ref refData, (nint)i) = value;
 202            }
 742203        }
 204
 205        public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : I
 206        {
 0207            Debug.Assert(searchSpaceLength >= 0);
 0208            Debug.Assert(valueLength >= 0);
 209
 0210            if (valueLength == 0)
 0211                return 0;  // A zero-length sequence is always treated as "found" at the start of the search space.
 212
 0213            T valueHead = value;
 0214            ref T valueTail = ref Unsafe.Add(ref value, 1);
 0215            int valueTailLength = valueLength - 1;
 216
 0217            int index = 0;
 0218            while (true)
 219            {
 0220                Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the comput
 0221                int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
 0222                if (remainingSearchSpaceLength <= 0)
 223                {
 224                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 225                }
 226
 227                // Do a quick search for the first element of "value".
 0228                int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLengt
 0229                if (relativeIndex < 0)
 230                {
 231                    break;
 232                }
 0233                index += relativeIndex;
 234
 235                // Found the first element of "value". See if the tail matches.
 0236                if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
 237                {
 0238                    return index;  // The tail matched. Return a successful find.
 239                }
 240
 0241                index++;
 242            }
 0243            return -1;
 244        }
 245
 246        // Adapted from IndexOf(...)
 247        public static bool Contains<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>?
 248        {
 0249            Debug.Assert(length >= 0);
 250
 0251            nint index = 0; // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations
 252
 0253            if (default(T) != null || (object?)value != null)
 254            {
 0255                Debug.Assert(value is not null);
 256
 0257                while (length >= 8)
 258                {
 0259                    length -= 8;
 260
 0261                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
 0262                        value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
 0263                        value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
 0264                        value.Equals(Unsafe.Add(ref searchSpace, index + 3)) ||
 0265                        value.Equals(Unsafe.Add(ref searchSpace, index + 4)) ||
 0266                        value.Equals(Unsafe.Add(ref searchSpace, index + 5)) ||
 0267                        value.Equals(Unsafe.Add(ref searchSpace, index + 6)) ||
 0268                        value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
 269                    {
 0270                        return true;
 271                    }
 272
 0273                    index += 8;
 274                }
 275
 0276                if (length >= 4)
 277                {
 0278                    length -= 4;
 279
 0280                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
 0281                        value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
 0282                        value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
 0283                        value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
 284                    {
 0285                        return true;
 286                    }
 287
 0288                    index += 4;
 289                }
 290
 0291                while (length > 0)
 292                {
 0293                    length--;
 294
 0295                    if (value.Equals(Unsafe.Add(ref searchSpace, index)))
 296                    {
 0297                        return true;
 298                    }
 299
 0300                    index += 1;
 301                }
 302            }
 303            else
 304            {
 0305                nint len = length;
 0306                for (index = 0; index < len; index++)
 307                {
 0308                    if ((object?)Unsafe.Add(ref searchSpace, index) is null)
 309                    {
 0310                        return true;
 311                    }
 312                }
 313            }
 314
 0315            return false;
 316        }
 317
 318        public static int IndexOf<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>?
 319        {
 0320            Debug.Assert(length >= 0);
 321
 0322            nint index = 0; // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations
 0323            if (default(T) != null || (object?)value != null)
 324            {
 0325                Debug.Assert(value is not null);
 326
 0327                while (length >= 8)
 328                {
 0329                    length -= 8;
 330
 0331                    if (value.Equals(Unsafe.Add(ref searchSpace, index)))
 332                    {
 0333                        return (int)index;
 334                    }
 0335                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
 336                    {
 0337                        return (int)(index + 1);
 338                    }
 0339                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
 340                    {
 0341                        return (int)(index + 2);
 342                    }
 0343                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
 344                    {
 0345                        return (int)(index + 3);
 346                    }
 0347                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 4)))
 348                    {
 0349                        return (int)(index + 4);
 350                    }
 0351                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 5)))
 352                    {
 0353                        return (int)(index + 5);
 354                    }
 0355                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 6)))
 356                    {
 0357                        return (int)(index + 6);
 358                    }
 0359                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
 360                    {
 0361                        return (int)(index + 7);
 362                    }
 363
 0364                    index += 8;
 365                }
 366
 0367                if (length >= 4)
 368                {
 0369                    length -= 4;
 370
 0371                    if (value.Equals(Unsafe.Add(ref searchSpace, index)))
 372                    {
 0373                        return (int)index;
 374                    }
 0375                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
 376                    {
 0377                        return (int)(index + 1);
 378                    }
 0379                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
 380                    {
 0381                        return (int)(index + 2);
 382                    }
 0383                    if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
 384                    {
 0385                        return (int)(index + 3);
 386                    }
 387
 0388                    index += 4;
 389                }
 390
 0391                while (length > 0)
 392                {
 0393                    if (value.Equals(Unsafe.Add(ref searchSpace, index)))
 394                    {
 0395                        return (int)index;
 396                    }
 397
 0398                    index += 1;
 0399                    length--;
 400                }
 401            }
 402            else
 403            {
 0404                nint len = (nint)length;
 0405                for (index = 0; index < len; index++)
 406                {
 0407                    if ((object?)Unsafe.Add(ref searchSpace, index) is null)
 408                    {
 0409                        return (int)index;
 410                    }
 411                }
 412            }
 413
 0414            return -1;
 415        }
 416
 417        public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, int length) where T : IEquatable<T>?
 418        {
 0419            Debug.Assert(length >= 0);
 420
 421            T lookUp;
 0422            int index = 0;
 0423            if (default(T) != null || ((object?)value0 != null && (object?)value1 != null))
 424            {
 0425                Debug.Assert(value0 is not null && value1 is not null);
 426
 0427                while ((length - index) >= 8)
 428                {
 0429                    lookUp = Unsafe.Add(ref searchSpace, index);
 0430                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 431                    {
 0432                        return index;
 433                    }
 434
 0435                    lookUp = Unsafe.Add(ref searchSpace, index + 1);
 0436                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 437                    {
 0438                        return index + 1;
 439                    }
 440
 0441                    lookUp = Unsafe.Add(ref searchSpace, index + 2);
 0442                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 443                    {
 0444                        return index + 2;
 445                    }
 446
 0447                    lookUp = Unsafe.Add(ref searchSpace, index + 3);
 0448                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 449                    {
 0450                        return index + 3;
 451                    }
 452
 0453                    lookUp = Unsafe.Add(ref searchSpace, index + 4);
 0454                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 455                    {
 0456                        return index + 4;
 457                    }
 458
 0459                    lookUp = Unsafe.Add(ref searchSpace, index + 5);
 0460                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 461                    {
 0462                        return index + 5;
 463                    }
 464
 0465                    lookUp = Unsafe.Add(ref searchSpace, index + 6);
 0466                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 467                    {
 0468                        return index + 6;
 469                    }
 470
 0471                    lookUp = Unsafe.Add(ref searchSpace, index + 7);
 0472                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 473                    {
 0474                        return index + 7;
 475                    }
 476
 0477                    index += 8;
 478                }
 479
 0480                if ((length - index) >= 4)
 481                {
 0482                    lookUp = Unsafe.Add(ref searchSpace, index);
 0483                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 484                    {
 0485                        return index;
 486                    }
 487
 0488                    lookUp = Unsafe.Add(ref searchSpace, index + 1);
 0489                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 490                    {
 0491                        return index + 1;
 492                    }
 493
 0494                    lookUp = Unsafe.Add(ref searchSpace, index + 2);
 0495                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 496                    {
 0497                        return index + 2;
 498                    }
 499
 0500                    lookUp = Unsafe.Add(ref searchSpace, index + 3);
 0501                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 502                    {
 0503                        return index + 3;
 504                    }
 505
 0506                    index += 4;
 507                }
 508
 0509                while (index < length)
 510                {
 0511                    lookUp = Unsafe.Add(ref searchSpace, index);
 0512                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 513                    {
 0514                        return index;
 515                    }
 516
 0517                    index++;
 518                }
 519            }
 520            else
 521            {
 0522                for (index = 0; index < length; index++)
 523                {
 0524                    lookUp = Unsafe.Add(ref searchSpace, index);
 0525                    if ((object?)lookUp is null)
 526                    {
 0527                        if ((object?)value0 is null || (object?)value1 is null)
 528                        {
 0529                            return index;
 530                        }
 531                    }
 0532                    else if (lookUp.Equals(value0) || lookUp.Equals(value1))
 533                    {
 0534                        return index;
 535                    }
 536                }
 537            }
 538
 0539            return -1;
 540        }
 541
 542        public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length) where T : IEquatabl
 543        {
 0544            Debug.Assert(length >= 0);
 545
 546            T lookUp;
 0547            int index = 0;
 0548            if (default(T) != null || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
 549            {
 0550                Debug.Assert(value0 is not null && value1 is not null && value2 is not null);
 551
 0552                while ((length - index) >= 8)
 553                {
 0554                    lookUp = Unsafe.Add(ref searchSpace, index);
 0555                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 556                    {
 0557                        return index;
 558                    }
 559
 0560                    lookUp = Unsafe.Add(ref searchSpace, index + 1);
 0561                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 562                    {
 0563                        return index + 1;
 564                    }
 565
 0566                    lookUp = Unsafe.Add(ref searchSpace, index + 2);
 0567                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 568                    {
 0569                        return index + 2;
 570                    }
 571
 0572                    lookUp = Unsafe.Add(ref searchSpace, index + 3);
 0573                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 574                    {
 0575                        return index + 3;
 576                    }
 577
 0578                    lookUp = Unsafe.Add(ref searchSpace, index + 4);
 0579                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 580                    {
 0581                        return index + 4;
 582                    }
 583
 0584                    lookUp = Unsafe.Add(ref searchSpace, index + 5);
 0585                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 586                    {
 0587                        return index + 5;
 588                    }
 589
 0590                    lookUp = Unsafe.Add(ref searchSpace, index + 6);
 0591                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 592                    {
 0593                        return index + 6;
 594                    }
 595
 0596                    lookUp = Unsafe.Add(ref searchSpace, index + 7);
 0597                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 598                    {
 0599                        return index + 7;
 600                    }
 601
 0602                    index += 8;
 603                }
 604
 0605                if ((length - index) >= 4)
 606                {
 0607                    lookUp = Unsafe.Add(ref searchSpace, index);
 0608                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 609                    {
 0610                        return index;
 611                    }
 612
 0613                    lookUp = Unsafe.Add(ref searchSpace, index + 1);
 0614                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 615                    {
 0616                        return index + 1;
 617                    }
 618
 0619                    lookUp = Unsafe.Add(ref searchSpace, index + 2);
 0620                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 621                    {
 0622                        return index + 2;
 623                    }
 624
 0625                    lookUp = Unsafe.Add(ref searchSpace, index + 3);
 0626                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 627                    {
 0628                        return index + 3;
 629                    }
 630
 0631                    index += 4;
 632                }
 633
 0634                while (index < length)
 635                {
 0636                    lookUp = Unsafe.Add(ref searchSpace, index);
 0637                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 638                    {
 0639                        return index;
 640                    }
 641
 0642                    index++;
 643                }
 644            }
 645            else
 646            {
 0647                for (index = 0; index < length; index++)
 648                {
 0649                    lookUp = Unsafe.Add(ref searchSpace, index);
 0650                    if ((object?)lookUp is null)
 651                    {
 0652                        if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
 653                        {
 0654                            return index;
 655                        }
 656                    }
 0657                    else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
 658                    {
 0659                        return index;
 660                    }
 661                }
 662            }
 663
 0664            return -1;
 665        }
 666
 667        public static int IndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T 
 668        {
 0669            Debug.Assert(searchSpaceLength >= 0);
 0670            Debug.Assert(valueLength >= 0);
 671
 0672            if (valueLength == 0)
 0673                return -1;  // A zero-length set of values is always treated as "not found".
 674
 675            // For the following paragraph, let:
 676            //   n := length of haystack
 677            //   i := index of first occurrence of any needle within haystack
 678            //   l := length of needle array
 679            //
 680            // We use a naive non-vectorized search because we want to bound the complexity of IndexOfAny
 681            // to O(i * l) rather than O(n * l), or just O(n * l) if no needle is found. The reason for
 682            // this is that it's common for callers to invoke IndexOfAny immediately before slicing,
 683            // and when this is called in a loop, we want the entire loop to be bounded by O(n * l)
 684            // rather than O(n^2 * l).
 685
 0686            if (typeof(T).IsValueType)
 687            {
 688                // Calling ValueType.Equals (devirtualized), which takes 'this' byref. We'll make
 689                // a byval copy of the candidate from the search space in the outer loop, then in
 690                // the inner loop we'll pass a ref (as 'this') to each element in the needle.
 0691                for (int i = 0; i < searchSpaceLength; i++)
 692                {
 0693                    T candidate = Unsafe.Add(ref searchSpace, i);
 0694                    for (int j = 0; j < valueLength; j++)
 695                    {
 0696                        if (Unsafe.Add(ref value, j)!.Equals(candidate))
 697                        {
 0698                            return i;
 699                        }
 700                    }
 701                }
 702            }
 703            else
 704            {
 705                // Calling IEquatable<T>.Equals (virtual dispatch). We'll perform the null check
 706                // in the outer loop instead of in the inner loop to save some branching.
 0707                for (int i = 0; i < searchSpaceLength; i++)
 708                {
 0709                    T candidate = Unsafe.Add(ref searchSpace, i);
 0710                    if (candidate is not null)
 711                    {
 0712                        for (int j = 0; j < valueLength; j++)
 713                        {
 0714                            if (candidate.Equals(Unsafe.Add(ref value, j)))
 715                            {
 0716                                return i;
 717                            }
 718                        }
 719                    }
 720                    else
 721                    {
 0722                        for (int j = 0; j < valueLength; j++)
 723                        {
 0724                            if (Unsafe.Add(ref value, j) is null)
 725                            {
 0726                                return i;
 727                            }
 728                        }
 729                    }
 730                }
 731            }
 732
 0733            return -1; // not found
 734        }
 735
 736        public static int LastIndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T
 737        {
 0738            Debug.Assert(searchSpaceLength >= 0);
 0739            Debug.Assert(valueLength >= 0);
 740
 0741            if (valueLength == 0)
 0742                return searchSpaceLength;  // A zero-length sequence is always treated as "found" at the end of the sear
 743
 0744            int valueTailLength = valueLength - 1;
 0745            if (valueTailLength == 0)
 746            {
 0747                return LastIndexOf(ref searchSpace, value, searchSpaceLength);
 748            }
 749
 0750            int index = 0;
 751
 0752            T valueHead = value;
 0753            ref T valueTail = ref Unsafe.Add(ref value, 1);
 754
 0755            while (true)
 756            {
 0757                Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the comput
 0758                int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
 0759                if (remainingSearchSpaceLength <= 0)
 760                {
 761                    break;  // The unsearched portion is now shorter than the sequence we're looking for. So it can't be
 762                }
 763
 764                // Do a quick search for the first element of "value".
 0765                int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
 0766                if (relativeIndex < 0)
 767                {
 768                    break;
 769                }
 770
 771                // Found the first element of "value". See if the tail matches.
 0772                if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
 773                {
 0774                    return relativeIndex;  // The tail matched. Return a successful find.
 775                }
 776
 0777                index += remainingSearchSpaceLength - relativeIndex;
 778            }
 0779            return -1;
 780        }
 781
 782        public static int LastIndexOf<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>?
 783        {
 0784            Debug.Assert(length >= 0);
 785
 0786            if (default(T) != null || (object?)value != null)
 787            {
 0788                Debug.Assert(value is not null);
 789
 0790                while (length >= 8)
 791                {
 0792                    length -= 8;
 793
 0794                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 7)))
 795                    {
 0796                        return length + 7;
 797                    }
 0798                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 6)))
 799                    {
 0800                        return length + 6;
 801                    }
 0802                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 5)))
 803                    {
 0804                        return length + 5;
 805                    }
 0806                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 4)))
 807                    {
 0808                        return length + 4;
 809                    }
 0810                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
 811                    {
 0812                        return length + 3;
 813                    }
 0814                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
 815                    {
 0816                        return length + 2;
 817                    }
 0818                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
 819                    {
 0820                        return length + 1;
 821                    }
 0822                    if (value.Equals(Unsafe.Add(ref searchSpace, length)))
 823                    {
 0824                        return length;
 825                    }
 826                }
 827
 0828                if (length >= 4)
 829                {
 0830                    length -= 4;
 831
 0832                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
 833                    {
 0834                        return length + 3;
 835                    }
 0836                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
 837                    {
 0838                        return length + 2;
 839                    }
 0840                    if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
 841                    {
 0842                        return length + 1;
 843                    }
 0844                    if (value.Equals(Unsafe.Add(ref searchSpace, length)))
 845                    {
 0846                        return length;
 847                    }
 848                }
 849
 0850                while (length > 0)
 851                {
 0852                    length--;
 853
 0854                    if (value.Equals(Unsafe.Add(ref searchSpace, length)))
 855                    {
 0856                        return length;
 857                    }
 858                }
 859            }
 860            else
 861            {
 0862                for (length--; length >= 0; length--)
 863                {
 0864                    if ((object?)Unsafe.Add(ref searchSpace, length) is null)
 865                    {
 0866                        return length;
 867                    }
 868                }
 869            }
 870
 0871            return -1;
 872        }
 873
 874        public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int length) where T : IEquatable<T>?
 875        {
 0876            Debug.Assert(length >= 0);
 877
 878            T lookUp;
 0879            if (default(T) != null || ((object?)value0 != null && (object?)value1 != null))
 880            {
 0881                Debug.Assert(value0 is not null && value1 is not null);
 882
 0883                while (length >= 8)
 884                {
 0885                    length -= 8;
 886
 0887                    lookUp = Unsafe.Add(ref searchSpace, length + 7);
 0888                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 889                    {
 0890                        return length + 7;
 891                    }
 892
 0893                    lookUp = Unsafe.Add(ref searchSpace, length + 6);
 0894                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 895                    {
 0896                        return length + 6;
 897                    }
 898
 0899                    lookUp = Unsafe.Add(ref searchSpace, length + 5);
 0900                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 901                    {
 0902                        return length + 5;
 903                    }
 904
 0905                    lookUp = Unsafe.Add(ref searchSpace, length + 4);
 0906                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 907                    {
 0908                        return length + 4;
 909                    }
 910
 0911                    lookUp = Unsafe.Add(ref searchSpace, length + 3);
 0912                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 913                    {
 0914                        return length + 3;
 915                    }
 916
 0917                    lookUp = Unsafe.Add(ref searchSpace, length + 2);
 0918                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 919                    {
 0920                        return length + 2;
 921                    }
 922
 0923                    lookUp = Unsafe.Add(ref searchSpace, length + 1);
 0924                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 925                    {
 0926                        return length + 1;
 927                    }
 928
 0929                    lookUp = Unsafe.Add(ref searchSpace, length);
 0930                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 931                    {
 0932                        return length;
 933                    }
 934                }
 935
 0936                if (length >= 4)
 937                {
 0938                    length -= 4;
 939
 0940                    lookUp = Unsafe.Add(ref searchSpace, length + 3);
 0941                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 942                    {
 0943                        return length + 3;
 944                    }
 945
 0946                    lookUp = Unsafe.Add(ref searchSpace, length + 2);
 0947                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 948                    {
 0949                        return length + 2;
 950                    }
 951
 0952                    lookUp = Unsafe.Add(ref searchSpace, length + 1);
 0953                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 954                    {
 0955                        return length + 1;
 956                    }
 957
 0958                    lookUp = Unsafe.Add(ref searchSpace, length);
 0959                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 960                    {
 0961                        return length;
 962                    }
 963                }
 964
 0965                while (length > 0)
 966                {
 0967                    length--;
 968
 0969                    lookUp = Unsafe.Add(ref searchSpace, length);
 0970                    if (value0.Equals(lookUp) || value1.Equals(lookUp))
 971                    {
 0972                        return length;
 973                    }
 974                }
 975            }
 976            else
 977            {
 0978                for (length--; length >= 0; length--)
 979                {
 0980                    lookUp = Unsafe.Add(ref searchSpace, length);
 0981                    if ((object?)lookUp is null)
 982                    {
 0983                        if ((object?)value0 is null || (object?)value1 is null)
 984                        {
 0985                            return length;
 986                        }
 987                    }
 0988                    else if (lookUp.Equals(value0) || lookUp.Equals(value1))
 989                    {
 0990                        return length;
 991                    }
 992                }
 993            }
 994
 0995            return -1;
 996        }
 997
 998        public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length) where T : IEqua
 999        {
 01000            Debug.Assert(length >= 0);
 1001
 1002            T lookUp;
 01003            if (default(T) != null || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
 1004            {
 01005                Debug.Assert(value0 is not null && value1 is not null && value2 is not null);
 1006
 01007                while (length >= 8)
 1008                {
 01009                    length -= 8;
 1010
 01011                    lookUp = Unsafe.Add(ref searchSpace, length + 7);
 01012                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1013                    {
 01014                        return length + 7;
 1015                    }
 1016
 01017                    lookUp = Unsafe.Add(ref searchSpace, length + 6);
 01018                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1019                    {
 01020                        return length + 6;
 1021                    }
 1022
 01023                    lookUp = Unsafe.Add(ref searchSpace, length + 5);
 01024                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1025                    {
 01026                        return length + 5;
 1027                    }
 1028
 01029                    lookUp = Unsafe.Add(ref searchSpace, length + 4);
 01030                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1031                    {
 01032                        return length + 4;
 1033                    }
 1034
 01035                    lookUp = Unsafe.Add(ref searchSpace, length + 3);
 01036                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1037                    {
 01038                        return length + 3;
 1039                    }
 1040
 01041                    lookUp = Unsafe.Add(ref searchSpace, length + 2);
 01042                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1043                    {
 01044                        return length + 2;
 1045                    }
 1046
 01047                    lookUp = Unsafe.Add(ref searchSpace, length + 1);
 01048                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1049                    {
 01050                        return length + 1;
 1051                    }
 1052
 01053                    lookUp = Unsafe.Add(ref searchSpace, length);
 01054                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1055                    {
 01056                        return length;
 1057                    }
 1058                }
 1059
 01060                if (length >= 4)
 1061                {
 01062                    length -= 4;
 1063
 01064                    lookUp = Unsafe.Add(ref searchSpace, length + 3);
 01065                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1066                    {
 01067                        return length + 3;
 1068                    }
 1069
 01070                    lookUp = Unsafe.Add(ref searchSpace, length + 2);
 01071                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1072                    {
 01073                        return length + 2;
 1074                    }
 1075
 01076                    lookUp = Unsafe.Add(ref searchSpace, length + 1);
 01077                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1078                    {
 01079                        return length + 1;
 1080                    }
 1081
 01082                    lookUp = Unsafe.Add(ref searchSpace, length);
 01083                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1084                    {
 01085                        return length;
 1086                    }
 1087                }
 1088
 01089                while (length > 0)
 1090                {
 01091                    length--;
 1092
 01093                    lookUp = Unsafe.Add(ref searchSpace, length);
 01094                    if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
 1095                    {
 01096                        return length;
 1097                    }
 1098                }
 1099            }
 1100            else
 1101            {
 01102                for (length--; length >= 0; length--)
 1103                {
 01104                    lookUp = Unsafe.Add(ref searchSpace, length);
 01105                    if ((object?)lookUp is null)
 1106                    {
 01107                        if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
 1108                        {
 01109                            return length;
 1110                        }
 1111                    }
 01112                    else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
 1113                    {
 01114                        return length;
 1115                    }
 1116                }
 1117            }
 1118
 01119            return -1;
 1120        }
 1121
 1122        public static int LastIndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) wher
 1123        {
 01124            Debug.Assert(searchSpaceLength >= 0);
 01125            Debug.Assert(valueLength >= 0);
 1126
 01127            if (valueLength == 0)
 01128                return -1;  // A zero-length set of values is always treated as "not found".
 1129
 1130            // See comments in IndexOfAny(ref T, int, ref T, int) above regarding algorithmic complexity concerns.
 1131            // This logic is similar, but it runs backward.
 01132            if (typeof(T).IsValueType)
 1133            {
 01134                for (int i = searchSpaceLength - 1; i >= 0; i--)
 1135                {
 01136                    T candidate = Unsafe.Add(ref searchSpace, i);
 01137                    for (int j = 0; j < valueLength; j++)
 1138                    {
 01139                        if (Unsafe.Add(ref value, j)!.Equals(candidate))
 1140                        {
 01141                            return i;
 1142                        }
 1143                    }
 1144                }
 1145            }
 1146            else
 1147            {
 01148                for (int i = searchSpaceLength - 1; i >= 0; i--)
 1149                {
 01150                    T candidate = Unsafe.Add(ref searchSpace, i);
 01151                    if (candidate is not null)
 1152                    {
 01153                        for (int j = 0; j < valueLength; j++)
 1154                        {
 01155                            if (candidate.Equals(Unsafe.Add(ref value, j)))
 1156                            {
 01157                                return i;
 1158                            }
 1159                        }
 1160                    }
 1161                    else
 1162                    {
 01163                        for (int j = 0; j < valueLength; j++)
 1164                        {
 01165                            if (Unsafe.Add(ref value, j) is null)
 1166                            {
 01167                                return i;
 1168                            }
 1169                        }
 1170                    }
 1171                }
 1172            }
 1173
 01174            return -1; // not found
 1175        }
 1176
 1177        internal static int IndexOfAnyExcept<T>(ref T searchSpace, T value0, int length)
 1178        {
 01179            Debug.Assert(length >= 0, "Expected non-negative length");
 1180
 01181            for (int i = 0; i < length; i++)
 1182            {
 01183                if (!EqualityComparer<T>.Default.Equals(Unsafe.Add(ref searchSpace, i), value0))
 1184                {
 01185                    return i;
 1186                }
 1187            }
 1188
 01189            return -1;
 1190        }
 1191
 1192        internal static int LastIndexOfAnyExcept<T>(ref T searchSpace, T value0, int length)
 1193        {
 01194            Debug.Assert(length >= 0, "Expected non-negative length");
 1195
 01196            for (int i = length - 1; i >= 0; i--)
 1197            {
 01198                if (!EqualityComparer<T>.Default.Equals(Unsafe.Add(ref searchSpace, i), value0))
 1199                {
 01200                    return i;
 1201                }
 1202            }
 1203
 01204            return -1;
 1205        }
 1206
 1207        internal static int IndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, int length)
 1208        {
 01209            Debug.Assert(length >= 0, "Expected non-negative length");
 1210
 01211            for (int i = 0; i < length; i++)
 1212            {
 01213                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01214                if (!EqualityComparer<T>.Default.Equals(current, value0) && !EqualityComparer<T>.Default.Equals(current,
 1215                {
 01216                    return i;
 1217                }
 1218            }
 1219
 01220            return -1;
 1221        }
 1222
 1223        internal static int LastIndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, int length)
 1224        {
 01225            Debug.Assert(length >= 0, "Expected non-negative length");
 1226
 01227            for (int i = length - 1; i >= 0; i--)
 1228            {
 01229                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01230                if (!EqualityComparer<T>.Default.Equals(current, value0) && !EqualityComparer<T>.Default.Equals(current,
 1231                {
 01232                    return i;
 1233                }
 1234            }
 1235
 01236            return -1;
 1237        }
 1238
 1239        internal static int IndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, T value2, int length)
 1240        {
 01241            Debug.Assert(length >= 0, "Expected non-negative length");
 1242
 01243            for (int i = 0; i < length; i++)
 1244            {
 01245                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01246                if (!EqualityComparer<T>.Default.Equals(current, value0)
 01247                    && !EqualityComparer<T>.Default.Equals(current, value1)
 01248                    && !EqualityComparer<T>.Default.Equals(current, value2))
 1249                {
 01250                    return i;
 1251                }
 1252            }
 1253
 01254            return -1;
 1255        }
 1256
 1257        internal static int LastIndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, T value2, int length)
 1258        {
 01259            Debug.Assert(length >= 0, "Expected non-negative length");
 1260
 01261            for (int i = length - 1; i >= 0; i--)
 1262            {
 01263                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01264                if (!EqualityComparer<T>.Default.Equals(current, value0)
 01265                    && !EqualityComparer<T>.Default.Equals(current, value1)
 01266                    && !EqualityComparer<T>.Default.Equals(current, value2))
 1267                {
 01268                    return i;
 1269                }
 1270            }
 1271
 01272            return -1;
 1273        }
 1274
 1275        internal static int IndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, T value2, T value3, int length)
 1276        {
 01277            Debug.Assert(length >= 0, "Expected non-negative length");
 1278
 01279            for (int i = 0; i < length; i++)
 1280            {
 01281                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01282                if (!EqualityComparer<T>.Default.Equals(current, value0)
 01283                    && !EqualityComparer<T>.Default.Equals(current, value1)
 01284                    && !EqualityComparer<T>.Default.Equals(current, value2)
 01285                    && !EqualityComparer<T>.Default.Equals(current, value3))
 1286                {
 01287                    return i;
 1288                }
 1289            }
 1290
 01291            return -1;
 1292        }
 1293
 1294        internal static int LastIndexOfAnyExcept<T>(ref T searchSpace, T value0, T value1, T value2, T value3, int lengt
 1295        {
 01296            Debug.Assert(length >= 0, "Expected non-negative length");
 1297
 01298            for (int i = length - 1; i >= 0; i--)
 1299            {
 01300                ref T current = ref Unsafe.Add(ref searchSpace, i);
 01301                if (!EqualityComparer<T>.Default.Equals(current, value0)
 01302                    && !EqualityComparer<T>.Default.Equals(current, value1)
 01303                    && !EqualityComparer<T>.Default.Equals(current, value2)
 01304                    && !EqualityComparer<T>.Default.Equals(current, value3))
 1305                {
 01306                    return i;
 1307                }
 1308            }
 1309
 01310            return -1;
 1311        }
 1312
 1313        public static bool SequenceEqual<T>(ref T first, ref T second, int length) where T : IEquatable<T>?
 1314        {
 01315            Debug.Assert(length >= 0);
 1316
 01317            if (Unsafe.AreSame(ref first, ref second))
 1318            {
 01319                return true;
 1320            }
 1321
 01322            nint index = 0; // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations
 1323            T lookUp0;
 1324            T lookUp1;
 01325            while (length >= 8)
 1326            {
 01327                length -= 8;
 1328
 01329                lookUp0 = Unsafe.Add(ref first, index);
 01330                lookUp1 = Unsafe.Add(ref second, index);
 01331                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1332                {
 01333                    return false;
 1334                }
 1335
 01336                lookUp0 = Unsafe.Add(ref first, index + 1);
 01337                lookUp1 = Unsafe.Add(ref second, index + 1);
 01338                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1339                {
 01340                    return false;
 1341                }
 1342
 01343                lookUp0 = Unsafe.Add(ref first, index + 2);
 01344                lookUp1 = Unsafe.Add(ref second, index + 2);
 01345                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1346                {
 01347                    return false;
 1348                }
 1349
 01350                lookUp0 = Unsafe.Add(ref first, index + 3);
 01351                lookUp1 = Unsafe.Add(ref second, index + 3);
 01352                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1353                {
 01354                    return false;
 1355                }
 1356
 01357                lookUp0 = Unsafe.Add(ref first, index + 4);
 01358                lookUp1 = Unsafe.Add(ref second, index + 4);
 01359                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1360                {
 01361                    return false;
 1362                }
 1363
 01364                lookUp0 = Unsafe.Add(ref first, index + 5);
 01365                lookUp1 = Unsafe.Add(ref second, index + 5);
 01366                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1367                {
 01368                    return false;
 1369                }
 1370
 01371                lookUp0 = Unsafe.Add(ref first, index + 6);
 01372                lookUp1 = Unsafe.Add(ref second, index + 6);
 01373                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1374                {
 01375                    return false;
 1376                }
 1377
 01378                lookUp0 = Unsafe.Add(ref first, index + 7);
 01379                lookUp1 = Unsafe.Add(ref second, index + 7);
 01380                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1381                {
 01382                    return false;
 1383                }
 1384
 01385                index += 8;
 1386            }
 1387
 01388            if (length >= 4)
 1389            {
 01390                length -= 4;
 1391
 01392                lookUp0 = Unsafe.Add(ref first, index);
 01393                lookUp1 = Unsafe.Add(ref second, index);
 01394                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1395                {
 01396                    return false;
 1397                }
 1398
 01399                lookUp0 = Unsafe.Add(ref first, index + 1);
 01400                lookUp1 = Unsafe.Add(ref second, index + 1);
 01401                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1402                {
 01403                    return false;
 1404                }
 1405
 01406                lookUp0 = Unsafe.Add(ref first, index + 2);
 01407                lookUp1 = Unsafe.Add(ref second, index + 2);
 01408                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1409                {
 01410                    return false;
 1411                }
 1412
 01413                lookUp0 = Unsafe.Add(ref first, index + 3);
 01414                lookUp1 = Unsafe.Add(ref second, index + 3);
 01415                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1416                {
 01417                    return false;
 1418                }
 1419
 01420                index += 4;
 1421            }
 1422
 01423            while (length > 0)
 1424            {
 01425                lookUp0 = Unsafe.Add(ref first, index);
 01426                lookUp1 = Unsafe.Add(ref second, index);
 01427                if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
 1428                {
 01429                    return false;
 1430                }
 1431
 01432                index += 1;
 01433                length--;
 1434            }
 1435
 01436            return true;
 1437        }
 1438
 1439        public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T second, int secondLength)
 1440            where T : IComparable<T>?
 1441        {
 01442            Debug.Assert(firstLength >= 0);
 01443            Debug.Assert(secondLength >= 0);
 1444
 01445            int minLength = firstLength;
 01446            if (minLength > secondLength)
 01447                minLength = secondLength;
 01448            for (int i = 0; i < minLength; i++)
 1449            {
 01450                T lookUp = Unsafe.Add(ref second, i);
 01451                int result = (Unsafe.Add(ref first, i)?.CompareTo(lookUp) ?? (((object?)lookUp is null) ? 0 : -1));
 01452                if (result != 0)
 1453                {
 01454                    return result;
 1455                }
 1456            }
 1457
 01458            return firstLength.CompareTo(secondLength);
 1459        }
 1460
 1461        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1462        internal static bool ContainsValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T>
 1463        {
 286351464            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePack
 1465            {
 85201466                return PackedSpanHelpers.Contains(ref Unsafe.As<T, short>(ref searchSpace), Unsafe.BitCast<T, short>(val
 1467            }
 1468
 201151469            return NonPackedContainsValueType(ref searchSpace, value, length);
 1470        }
 1471
 1472        internal static bool NonPackedContainsValueType<T>(ref T searchSpace, T value, int length) where T : struct, INu
 1473        {
 201151474            Debug.Assert(length >= 0, "Expected non-negative length");
 201151475            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 1476
 201151477            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 1478            {
 19121479                nuint offset = 0;
 1480
 19381481                while (length >= 8)
 1482                {
 4061483                    length -= 8;
 1484
 4061485                    if (Unsafe.Add(ref searchSpace, offset) == value
 4061486                     || Unsafe.Add(ref searchSpace, offset + 1) == value
 4061487                     || Unsafe.Add(ref searchSpace, offset + 2) == value
 4061488                     || Unsafe.Add(ref searchSpace, offset + 3) == value
 4061489                     || Unsafe.Add(ref searchSpace, offset + 4) == value
 4061490                     || Unsafe.Add(ref searchSpace, offset + 5) == value
 4061491                     || Unsafe.Add(ref searchSpace, offset + 6) == value
 4061492                     || Unsafe.Add(ref searchSpace, offset + 7) == value)
 1493                    {
 3801494                        return true;
 1495                    }
 1496
 261497                    offset += 8;
 1498                }
 1499
 15321500                if (length >= 4)
 1501                {
 2261502                    length -= 4;
 1503
 2261504                    if (Unsafe.Add(ref searchSpace, offset) == value
 2261505                     || Unsafe.Add(ref searchSpace, offset + 1) == value
 2261506                     || Unsafe.Add(ref searchSpace, offset + 2) == value
 2261507                     || Unsafe.Add(ref searchSpace, offset + 3) == value)
 1508                    {
 1781509                        return true;
 1510                    }
 1511
 481512                    offset += 4;
 1513                }
 1514
 31401515                while (length > 0)
 1516                {
 18121517                    length -= 1;
 1518
 18121519                    if (Unsafe.Add(ref searchSpace, offset) == value)
 1520                    {
 261521                        return true;
 1522                    }
 1523
 17861524                    offset += 1;
 1525                }
 1526            }
 182031527            else if (Vector512.IsHardwareAccelerated && length >= Vector512<T>.Count)
 1528            {
 01529                Vector512<T> current, values = Vector512.Create(value);
 01530                ref T currentSearchSpace = ref searchSpace;
 01531                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector512<T>.Count));
 1532
 1533                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1534                do
 1535                {
 01536                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 1537
 01538                    if (Vector512.EqualsAny(values, current))
 1539                    {
 01540                        return true;
 1541                    }
 1542
 01543                    currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<T>.Count);
 1544                }
 01545                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1546
 1547                // If any elements remain, process the last vector in the search space.
 01548                if ((uint)length % Vector512<T>.Count != 0)
 1549                {
 01550                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 1551
 01552                    if (Vector512.EqualsAny(values, current))
 1553                    {
 01554                        return true;
 1555                    }
 1556                }
 1557            }
 182031558            else if (Vector256.IsHardwareAccelerated && length >= Vector256<T>.Count)
 1559            {
 181771560                Vector256<T> equals, values = Vector256.Create(value);
 181771561                ref T currentSearchSpace = ref searchSpace;
 181771562                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector256<T>.Count));
 1563
 1564                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1565                do
 1566                {
 693091567                    equals = Vector256.Equals(values, Vector256.LoadUnsafe(ref currentSearchSpace));
 693091568                    if (equals == Vector256<T>.Zero)
 1569                    {
 681771570                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<T>.Count);
 681771571                        continue;
 1572                    }
 1573
 11321574                    return true;
 1575                }
 681771576                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1577
 1578                // If any elements remain, process the last vector in the search space.
 170451579                if ((uint)length % Vector256<T>.Count != 0)
 1580                {
 170441581                    equals = Vector256.Equals(values, Vector256.LoadUnsafe(ref oneVectorAwayFromEnd));
 170441582                    if (equals != Vector256<T>.Zero)
 1583                    {
 01584                        return true;
 1585                    }
 1586                }
 1587            }
 1588            else
 1589            {
 261590                Vector128<T> equals, values = Vector128.Create(value);
 261591                ref T currentSearchSpace = ref searchSpace;
 261592                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector128<T>.Count));
 1593
 1594                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1595                do
 1596                {
 261597                    equals = Vector128.Equals(values, Vector128.LoadUnsafe(ref currentSearchSpace));
 261598                    if (equals == Vector128<T>.Zero)
 1599                    {
 11600                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<T>.Count);
 11601                        continue;
 1602                    }
 1603
 251604                    return true;
 1605                }
 11606                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1607
 1608                // If any elements remain, process the first vector in the search space.
 11609                if ((uint)length % Vector128<T>.Count != 0)
 1610                {
 11611                    equals = Vector128.Equals(values, Vector128.LoadUnsafe(ref oneVectorAwayFromEnd));
 11612                    if (equals != Vector128<T>.Zero)
 1613                    {
 01614                        return true;
 1615                    }
 1616                }
 1617            }
 1618
 183741619            return false;
 1620        }
 1621
 1622        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1623        internal static int IndexOfChar(ref char searchSpace, char value, int length)
 13511624            => IndexOfValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value, length);
 1625
 1626        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1627        internal static int LastIndexOfChar(ref char searchSpace, char value, int length)
 01628            => LastIndexOfValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value, length);
 1629
 1630        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1631        internal static int NonPackedIndexOfChar(ref char searchSpace, char value, int length) =>
 01632            NonPackedIndexOfValueType<short, DontNegate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)valu
 1633
 1634        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1635        internal static int IndexOfValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T>
 102011636            => IndexOfValueType<T, DontNegate<T>>(ref searchSpace, value, length);
 1637
 1638        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1639        internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumb
 01640            => IndexOfValueType<T, Negate<T>>(ref searchSpace, value, length);
 1641
 1642        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1643        private static int IndexOfValueType<TValue, TNegator>(ref TValue searchSpace, TValue value, int length)
 1644            where TValue : struct, INumber<TValue>
 1645            where TNegator : struct, INegator<TValue>
 1646        {
 102011647            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUs
 1648            {
 13511649                return typeof(TNegator) == typeof(DontNegate<short>)
 13511650                    ? PackedSpanHelpers.IndexOf(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, cha
 13511651                    : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TV
 1652            }
 1653
 88501654            return NonPackedIndexOfValueType<TValue, TNegator>(ref searchSpace, value, length);
 1655        }
 1656
 1657        internal static int NonPackedIndexOfValueType<TValue, TNegator>(ref TValue searchSpace, TValue value, int length
 1658            where TValue : struct, INumber<TValue>
 1659            where TNegator : struct, INegator<TValue>
 1660        {
 178421661            Debug.Assert(length >= 0, "Expected non-negative length");
 178421662            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 1663
 178421664            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 1665            {
 81841666                nuint offset = 0;
 1667
 85791668                while (length >= 8)
 1669                {
 20341670                    length -= 8;
 1671
 20341672                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1673                    {
 2721674                        return (int)(offset);
 1675                    }
 17621676                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 1) == value))
 1677                    {
 1611678                        return (int)(offset + 1);
 1679                    }
 16011680                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 2) == value))
 1681                    {
 1841682                        return (int)(offset + 2);
 1683                    }
 14171684                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 3) == value))
 1685                    {
 2521686                        return (int)(offset + 3);
 1687                    }
 11651688                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 4) == value))
 1689                    {
 3471690                        return (int)(offset + 4);
 1691                    }
 8181692                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 5) == value))
 1693                    {
 2751694                        return (int)(offset + 5);
 1695                    }
 5431696                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 6) == value))
 1697                    {
 981698                        return (int)(offset + 6);
 1699                    }
 4451700                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 7) == value))
 1701                    {
 501702                        return (int)(offset + 7);
 1703                    }
 1704
 3951705                    offset += 8;
 1706                }
 1707
 65451708                if (length >= 4)
 1709                {
 20521710                    length -= 4;
 1711
 20521712                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1713                    {
 7261714                        return (int)(offset);
 1715                    }
 13261716                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 1) == value))
 1717                    {
 1741718                        return (int)(offset + 1);
 1719                    }
 11521720                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 2) == value))
 1721                    {
 1571722                        return (int)(offset + 2);
 1723                    }
 9951724                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 3) == value))
 1725                    {
 1381726                        return (int)(offset + 3);
 1727                    }
 1728
 8571729                    offset += 4;
 1730                }
 1731
 85421732                while (length > 0)
 1733                {
 56541734                    length -= 1;
 1735
 56541736                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1737                    {
 24621738                        return (int)(offset);
 1739                    }
 1740
 31921741                    offset += 1;
 1742                }
 1743
 28881744                return -1;
 1745            }
 96581746            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 1747            {
 01748                Vector512<TValue> current, values = Vector512.Create(value);
 01749                ref TValue currentSearchSpace = ref searchSpace;
 01750                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<TValue>.Count);
 1751
 1752                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1753                do
 1754                {
 01755                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 1756
 01757                    if (TNegator.HasMatch(values, current))
 1758                    {
 01759                        return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, TNegator.GetMatchMask(values, 
 1760                    }
 1761
 01762                    currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 1763                }
 01764                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1765
 1766                // If any elements remain, process the last vector in the search space.
 01767                if ((uint)length % Vector512<TValue>.Count != 0)
 1768                {
 01769                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 1770
 01771                    if (TNegator.HasMatch(values, current))
 1772                    {
 01773                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, TNegator.GetMatchMask(values
 1774                    }
 1775                }
 1776            }
 96581777            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 1778            {
 75501779                Vector256<TValue> equals, values = Vector256.Create(value);
 75501780                ref TValue currentSearchSpace = ref searchSpace;
 75501781                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<TValue>.Count);
 1782
 1783                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1784                do
 1785                {
 449201786                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values, Vector256.LoadUnsafe(ref currentSearchSpac
 449201787                    if (equals == Vector256<TValue>.Zero)
 1788                    {
 390001789                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 390001790                        continue;
 1791                    }
 1792
 59201793                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 1794                }
 390001795                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1796
 1797                // If any elements remain, process the last vector in the search space.
 16301798                if ((uint)length % Vector256<TValue>.Count != 0)
 1799                {
 13521800                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values, Vector256.LoadUnsafe(ref oneVectorAwayFrom
 13521801                    if (equals != Vector256<TValue>.Zero)
 1802                    {
 6381803                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 1804                    }
 1805                }
 1806            }
 1807            else
 1808            {
 21081809                Vector128<TValue> equals, values = Vector128.Create(value);
 21081810                ref TValue currentSearchSpace = ref searchSpace;
 21081811                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<TValue>.Count);
 1812
 1813                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 1814                do
 1815                {
 21081816                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values, Vector128.LoadUnsafe(ref currentSearchSpac
 21081817                    if (equals == Vector128<TValue>.Zero)
 1818                    {
 6041819                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 6041820                        continue;
 1821                    }
 1822
 15041823                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 1824                }
 6041825                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1826
 1827                // If any elements remain, process the first vector in the search space.
 6041828                if ((uint)length % Vector128<TValue>.Count != 0)
 1829                {
 4751830                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values, Vector128.LoadUnsafe(ref oneVectorAwayFrom
 4751831                    if (equals != Vector128<TValue>.Zero)
 1832                    {
 3911833                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 1834                    }
 1835                }
 1836            }
 1837
 12051838            return -1;
 1839        }
 1840
 1841        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1842        internal static int IndexOfAnyChar(ref char searchSpace, char value0, char value1, int length)
 01843            => IndexOfAnyValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value0, (short)value1, length);
 1844
 1845        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1846        internal static int LastIndexOfAnyChar(ref char searchSpace, char value0, char value1, int length)
 01847            => LastIndexOfAnyValueType(ref Unsafe.As<char, short>(ref searchSpace), (short)value0, (short)value1, length
 1848
 1849        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1850        internal static int IndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, int length) where T : struct, 
 51851            => IndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, length);
 1852
 1853        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1854        internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, int length) where T : st
 01855            => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, length);
 1856
 1857        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1858        private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, i
 1859            where TValue : struct, INumber<TValue>
 1860            where TNegator : struct, INegator<TValue>
 1861        {
 51862            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUs
 1863            {
 51864                char char0 = Unsafe.BitCast<TValue, char>(value0);
 51865                char char1 = Unsafe.BitCast<TValue, char>(value1);
 1866
 51867                if (RuntimeHelpers.IsKnownConstant(value0) && RuntimeHelpers.IsKnownConstant(value1))
 1868                {
 1869                    // If the values differ only in the 0x20 bit, we can optimize the search by reducing the number of c
 1870                    // This optimization only applies to a small subset of values and the throughput difference is not t
 1871                    // We avoid introducing per-call overhead for non-constant values by guarding this optimization behi
 01872                    if ((char0 ^ char1) == 0x20)
 1873                    {
 01874                        char lowerCase = (char)Math.Max(char0, char1);
 1875
 01876                        return typeof(TNegator) == typeof(DontNegate<short>)
 01877                            ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace), lower
 01878                            : PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace),
 1879                    }
 1880                }
 1881
 51882                return typeof(TNegator) == typeof(DontNegate<short>)
 51883                    ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), char0, char1, length)
 51884                    : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), char0, char1, len
 1885            }
 1886
 01887            return NonPackedIndexOfAnyValueType<TValue, TNegator>(ref searchSpace, value0, value1, length);
 1888        }
 1889
 1890        // having INumber<T> constraint here allows to use == operator and get better perf compared to .Equals
 1891        internal static int NonPackedIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue
 1892            where TValue : struct, INumber<TValue>
 1893            where TNegator : struct, INegator<TValue>
 1894        {
 132161895            Debug.Assert(length >= 0, "Expected non-negative length");
 132161896            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 1897
 132161898            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 1899            {
 81281900                nuint offset = 0;
 1901                TValue lookUp;
 1902
 81281903                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 1904                {
 20901905                    while (length >= 8)
 1906                    {
 6541907                        length -= 8;
 1908
 6541909                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 6541910                        lookUp = current;
 6541911                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1912                        {
 3271913                            return (int)(offset);
 1914                        }
 1915
 3271916                        lookUp = Unsafe.Add(ref current, 1);
 3271917                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1918                        {
 1141919                            return (int)(offset + 1);
 1920                        }
 1921
 2131922                        lookUp = Unsafe.Add(ref current, 2);
 2131923                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1924                        {
 271925                            return (int)(offset + 2);
 1926                        }
 1927
 1861928                        lookUp = Unsafe.Add(ref current, 3);
 1861929                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1930                        {
 361931                            return (int)(offset + 3);
 1932                        }
 1933
 1501934                        lookUp = Unsafe.Add(ref current, 4);
 1501935                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1936                        {
 181937                            return (int)(offset + 4);
 1938                        }
 1939
 1321940                        lookUp = Unsafe.Add(ref current, 5);
 1321941                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1942                        {
 241943                            return (int)(offset + 5);
 1944                        }
 1945
 1081946                        lookUp = Unsafe.Add(ref current, 6);
 1081947                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1948                        {
 181949                            return (int)(offset + 6);
 1950                        }
 1951
 901952                        lookUp = Unsafe.Add(ref current, 7);
 901953                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1954                        {
 181955                            return (int)(offset + 7);
 1956                        }
 1957
 721958                        offset += 8;
 1959                    }
 1960                }
 1961
 78761962                while (length >= 4)
 1963                {
 10801964                    length -= 4;
 1965
 10801966                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 10801967                    lookUp = current;
 10801968                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1969                    {
 5251970                        return (int)(offset);
 1971                    }
 1972
 5551973                    lookUp = Unsafe.Add(ref current, 1);
 5551974                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1975                    {
 1411976                        return (int)(offset + 1);
 1977                    }
 1978
 4141979                    lookUp = Unsafe.Add(ref current, 2);
 4141980                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1981                    {
 601982                        return (int)(offset + 2);
 1983                    }
 1984
 3541985                    lookUp = Unsafe.Add(ref current, 3);
 3541986                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1987                    {
 241988                        return (int)(offset + 3);
 1989                    }
 1990
 3301991                    offset += 4;
 1992                }
 1993
 123351994                while (length > 0)
 1995                {
 85231996                    length -= 1;
 1997
 85231998                    lookUp = Unsafe.Add(ref searchSpace, offset);
 85231999                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2000                    {
 29842001                        return (int)(offset);
 2002                    }
 2003
 55392004                    offset += 1;
 2005                }
 2006
 38122007                return -1;
 2008            }
 50882009            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2010            {
 02011                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 02012                ref TValue currentSearchSpace = ref searchSpace;
 02013                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<TValue>.Count);
 2014
 2015                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2016                do
 2017                {
 02018                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 02019                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02020                    if (equals == Vector512<TValue>.Zero)
 2021                    {
 02022                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 02023                        continue;
 2024                    }
 2025
 02026                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2027                }
 02028                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2029
 2030                // If any elements remain, process the last vector in the search space.
 02031                if ((uint)length % Vector512<TValue>.Count != 0)
 2032                {
 02033                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 02034                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02035                    if (equals != Vector512<TValue>.Zero)
 2036                    {
 02037                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2038                    }
 2039                }
 2040            }
 50882041            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2042            {
 87002043                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 43502044                ref TValue currentSearchSpace = ref searchSpace;
 43502045                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<TValue>.Count);
 2046
 2047                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2048                do
 2049                {
 434582050                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 434582051                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 434582052                    if (equals == Vector256<TValue>.Zero)
 2053                    {
 408242054                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 408242055                        continue;
 2056                    }
 2057
 26342058                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2059                }
 408242060                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2061
 2062                // If any elements remain, process the last vector in the search space.
 17162063                if ((uint)length % Vector256<TValue>.Count != 0)
 2064                {
 12002065                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 12002066                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 12002067                    if (equals != Vector256<TValue>.Zero)
 2068                    {
 782069                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2070                    }
 2071                }
 2072            }
 2073            else
 2074            {
 14762075                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 7382076                ref TValue currentSearchSpace = ref searchSpace;
 7382077                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<TValue>.Count);
 2078
 2079                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2080                do
 2081                {
 7382082                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 7382083                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 7382084                    if (equals == Vector128<TValue>.Zero)
 2085                    {
 2792086                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 2792087                        continue;
 2088                    }
 2089
 4592090                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2091                }
 2792092                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2093
 2094                // If any elements remain, process the first vector in the search space.
 2792095                if ((uint)length % Vector128<TValue>.Count != 0)
 2096                {
 1232097                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1232098                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 1232099                    if (equals != Vector128<TValue>.Zero)
 2100                    {
 362101                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2102                    }
 2103                }
 2104            }
 2105
 18812106            return -1;
 2107        }
 2108
 2109        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2110        internal static int IndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, int length) where T 
 02111            => IndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, length);
 2112
 2113        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2114        internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, int length) wh
 02115            => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, length);
 2116
 2117        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2118        private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, T
 2119            where TValue : struct, INumber<TValue>
 2120            where TNegator : struct, INegator<TValue>
 2121        {
 02122            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUs
 2123            {
 02124                return typeof(TNegator) == typeof(DontNegate<short>)
 02125                    ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, 
 02126                    : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TV
 2127            }
 2128
 02129            return NonPackedIndexOfAnyValueType<TValue, TNegator>(ref searchSpace, value0, value1, value2, length);
 2130        }
 2131
 2132        internal static int NonPackedIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue
 2133            where TValue : struct, INumber<TValue>
 2134            where TNegator : struct, INegator<TValue>
 2135        {
 78482136            Debug.Assert(length >= 0, "Expected non-negative length");
 78482137            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2138
 78482139            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2140            {
 47762141                nuint offset = 0;
 2142                TValue lookUp;
 2143
 47762144                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 2145                {
 32022146                    while (length >= 8)
 2147                    {
 11282148                        length -= 8;
 2149
 11282150                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 11282151                        lookUp = current;
 11282152                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2153                        {
 5642154                            return (int)(offset);
 2155                        }
 2156
 5642157                        lookUp = Unsafe.Add(ref current, 1);
 5642158                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2159                        {
 1622160                            return (int)(offset + 1);
 2161                        }
 2162
 4022163                        lookUp = Unsafe.Add(ref current, 2);
 4022164                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2165                        {
 722166                            return (int)(offset + 2);
 2167                        }
 2168
 3302169                        lookUp = Unsafe.Add(ref current, 3);
 3302170                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2171                        {
 602172                            return (int)(offset + 3);
 2173                        }
 2174
 2702175                        lookUp = Unsafe.Add(ref current, 4);
 2702176                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2177                        {
 572178                            return (int)(offset + 4);
 2179                        }
 2180
 2132181                        lookUp = Unsafe.Add(ref current, 5);
 2132182                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2183                        {
 482184                            return (int)(offset + 5);
 2185                        }
 2186
 1652187                        lookUp = Unsafe.Add(ref current, 6);
 1652188                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2189                        {
 392190                            return (int)(offset + 6);
 2191                        }
 2192
 1262193                        lookUp = Unsafe.Add(ref current, 7);
 1262194                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2195                        {
 302196                            return (int)(offset + 7);
 2197                        }
 2198
 962199                        offset += 8;
 2200                    }
 2201                }
 2202
 39062203                while (length >= 4)
 2204                {
 6092205                    length -= 4;
 2206
 6092207                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 6092208                    lookUp = current;
 6092209                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2210                    {
 3032211                        return (int)(offset);
 2212                    }
 2213
 3062214                    lookUp = Unsafe.Add(ref current, 1);
 3062215                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2216                    {
 782217                        return (int)(offset + 1);
 2218                    }
 2219
 2282220                    lookUp = Unsafe.Add(ref current, 2);
 2282221                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2222                    {
 392223                        return (int)(offset + 2);
 2224                    }
 2225
 1892226                    lookUp = Unsafe.Add(ref current, 3);
 1892227                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2228                    {
 272229                        return (int)(offset + 3);
 2230                    }
 2231
 1622232                    offset += 4;
 2233                }
 2234
 70442235                while (length > 0)
 2236                {
 51872237                    length -= 1;
 2238
 51872239                    lookUp = Unsafe.Add(ref searchSpace, offset);
 51872240                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2241                    {
 14402242                        return (int)(offset);
 2243                    }
 2244
 37472245                    offset += 1;
 2246                }
 2247
 18572248                return -1;
 2249            }
 30722250            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2251            {
 02252                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 02253                ref TValue currentSearchSpace = ref searchSpace;
 02254                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<TValue>.Count);
 2255
 2256                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2257                do
 2258                {
 02259                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 02260                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02261                    if (equals == Vector512<TValue>.Zero)
 2262                    {
 02263                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 02264                        continue;
 2265                    }
 2266
 02267                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2268                }
 02269                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2270
 2271                // If any elements remain, process the last vector in the search space.
 02272                if ((uint)length % Vector512<TValue>.Count != 0)
 2273                {
 02274                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 02275                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02276                    if (equals != Vector512<TValue>.Zero)
 2277                    {
 02278                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2279                    }
 2280                }
 2281            }
 30722282            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2283            {
 76862284                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 25622285                ref TValue currentSearchSpace = ref searchSpace;
 25622286                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<TValue>.Count);
 2287
 2288                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2289                do
 2290                {
 226412291                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 226412292                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 226412293                    if (equals == Vector256<TValue>.Zero)
 2294                    {
 208232295                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 208232296                        continue;
 2297                    }
 2298
 18182299                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2300                }
 208232301                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2302
 2303                // If any elements remain, process the last vector in the search space.
 7442304                if ((uint)length % Vector256<TValue>.Count != 0)
 2305                {
 5342306                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 5342307                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 5342308                    if (equals != Vector256<TValue>.Zero)
 2309                    {
 1292310                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2311                    }
 2312                }
 2313            }
 2314            else
 2315            {
 15302316                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 5102317                ref TValue currentSearchSpace = ref searchSpace;
 5102318                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<TValue>.Count);
 2319
 2320                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2321                do
 2322                {
 5102323                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 5102324                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 5102325                    if (equals == Vector128<TValue>.Zero)
 2326                    {
 1712327                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1712328                        continue;
 2329                    }
 2330
 3392331                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2332                }
 1712333                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2334
 2335                // If any elements remain, process the first vector in the search space.
 1712336                if ((uint)length % Vector128<TValue>.Count != 0)
 2337                {
 1172338                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1172339                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 1172340                    if (equals != Vector128<TValue>.Zero)
 2341                    {
 602342                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2343                    }
 2344                }
 2345            }
 2346
 7262347            return -1;
 2348        }
 2349
 2350        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2351        internal static int IndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, int length
 38122352            => IndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, value3, length);
 2353
 2354        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2355        internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, int 
 38122356            => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, value3, length);
 2357
 2358        private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, T
 2359            where TValue : struct, INumber<TValue>
 2360            where TNegator : struct, INegator<TValue>
 2361        {
 76242362            Debug.Assert(length >= 0, "Expected non-negative length");
 76242363            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2364
 76242365            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2366            {
 45882367                nuint offset = 0;
 2368                TValue lookUp;
 2369
 60482370                while (length >= 4)
 2371                {
 38862372                    length -= 4;
 2373
 38862374                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 38862375                    lookUp = current;
 38862376                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2377                    {
 18412378                        return (int)(offset);
 2379                    }
 2380
 20452381                    lookUp = Unsafe.Add(ref current, 1);
 20452382                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2383                    {
 3032384                        return (int)(offset + 1);
 2385                    }
 2386
 17422387                    lookUp = Unsafe.Add(ref current, 2);
 17422388                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2389                    {
 1352390                        return (int)(offset + 2);
 2391                    }
 2392
 16072393                    lookUp = Unsafe.Add(ref current, 3);
 16072394                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2395                    {
 1472396                        return (int)(offset + 3);
 2397                    }
 2398
 14602399                    offset += 4;
 2400                }
 2401
 27502402                while (length > 0)
 2403                {
 9752404                    length -= 1;
 2405
 9752406                    lookUp = Unsafe.Add(ref searchSpace, offset);
 9752407                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2408                    {
 3872409                        return (int)(offset);
 2410                    }
 2411
 5882412                    offset += 1;
 2413                }
 2414
 17752415                return -1;
 2416            }
 30362417            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2418            {
 02419                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 02420                ref TValue currentSearchSpace = ref searchSpace;
 02421                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<TValue>.Count);
 2422
 2423                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2424                do
 2425                {
 02426                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 02427                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02428                        | Vector512.Equals(values2, current) | Vector512.Equals(values3, current));
 02429                    if (equals == Vector512<TValue>.Zero)
 2430                    {
 02431                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 02432                        continue;
 2433                    }
 2434
 02435                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2436                }
 02437                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2438
 2439                // If any elements remain, process the last vector in the search space.
 02440                if ((uint)length % Vector512<TValue>.Count != 0)
 2441                {
 02442                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 02443                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02444                        | Vector512.Equals(values2, current) | Vector512.Equals(values3, current));
 02445                    if (equals != Vector512<TValue>.Zero)
 2446                    {
 02447                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2448                    }
 2449                }
 2450            }
 30362451            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2452            {
 102962453                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 25742454                ref TValue currentSearchSpace = ref searchSpace;
 25742455                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<TValue>.Count);
 2456
 2457                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2458                do
 2459                {
 131072460                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 131072461                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 131072462                        | Vector256.Equals(values2, current) | Vector256.Equals(values3, current));
 131072463                    if (equals == Vector256<TValue>.Zero)
 2464                    {
 111602465                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 111602466                        continue;
 2467                    }
 2468
 19472469                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2470                }
 111602471                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2472
 2473                // If any elements remain, process the last vector in the search space.
 6272474                if ((uint)length % Vector256<TValue>.Count != 0)
 2475                {
 5552476                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 5552477                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 5552478                        | Vector256.Equals(values2, current) | Vector256.Equals(values3, current));
 5552479                    if (equals != Vector256<TValue>.Zero)
 2480                    {
 632481                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2482                    }
 2483                }
 2484            }
 2485            else
 2486            {
 18482487                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 4622488                ref TValue currentSearchSpace = ref searchSpace;
 4622489                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<TValue>.Count);
 2490
 2491                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2492                do
 2493                {
 4622494                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 4622495                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 4622496                        | Vector128.Equals(values2, current) | Vector128.Equals(values3, current));
 4622497                    if (equals == Vector128<TValue>.Zero)
 2498                    {
 1172499                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1172500                        continue;
 2501                    }
 2502
 3452503                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2504                }
 1172505                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2506
 2507                // If any elements remain, process the first vector in the search space.
 1172508                if ((uint)length % Vector128<TValue>.Count != 0)
 2509                {
 992510                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 992511                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 992512                        | Vector128.Equals(values2, current) | Vector128.Equals(values3, current));
 992513                    if (equals != Vector128<TValue>.Zero)
 2514                    {
 212515                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2516                    }
 2517                }
 2518            }
 2519
 6602520            return -1;
 2521        }
 2522
 2523        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2524        internal static int IndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, T value4, 
 29082525            => IndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, value3, value4, length);
 2526
 2527        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2528        internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, T va
 29082529            => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, value3, value4, length);
 2530
 2531        private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, T
 2532            where TValue : struct, INumber<TValue>
 2533            where TNegator : struct, INegator<TValue>
 2534        {
 58162535            Debug.Assert(length >= 0, "Expected non-negative length");
 58162536            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2537
 58162538            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2539            {
 35662540                nuint offset = 0;
 2541                TValue lookUp;
 2542
 48392543                while (length >= 4)
 2544                {
 30802545                    length -= 4;
 2546
 30802547                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 30802548                    lookUp = current;
 30802549                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2550                    {
 14322551                        return (int)(offset);
 2552                    }
 2553
 16482554                    lookUp = Unsafe.Add(ref current, 1);
 16482555                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2556                    {
 1892557                        return (int)(offset + 1);
 2558                    }
 2559
 14592560                    lookUp = Unsafe.Add(ref current, 2);
 14592561                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2562                    {
 1112563                        return (int)(offset + 2);
 2564                    }
 2565
 13482566                    lookUp = Unsafe.Add(ref current, 3);
 13482567                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2568                    {
 752569                        return (int)(offset + 3);
 2570                    }
 2571
 12732572                    offset += 4;
 2573                }
 2574
 30532575                while (length > 0)
 2576                {
 16512577                    length -= 1;
 2578
 16512579                    lookUp = Unsafe.Add(ref searchSpace, offset);
 16512580                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2581                    {
 3572582                        return (int)(offset);
 2583                    }
 2584
 12942585                    offset += 1;
 2586                }
 2587
 14022588                return -1;
 2589            }
 22502590            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2591            {
 02592                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 02593                    values2 = Vector512.Create(value2), values3 = Vector512.Create(value3), values4 = Vector512.Create(v
 02594                ref TValue currentSearchSpace = ref searchSpace;
 02595                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector512<TValue>.Coun
 2596
 2597                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2598                do
 2599                {
 02600                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 02601                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02602                           | Vector512.Equals(values3, current) | Vector512.Equals(values4, current));
 02603                    if (equals == Vector512<TValue>.Zero)
 2604                    {
 02605                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 02606                        continue;
 2607                    }
 2608
 02609                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2610                }
 02611                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2612
 2613                // If any elements remain, process the last vector in the search space.
 02614                if ((uint)length % Vector512<TValue>.Count != 0)
 2615                {
 02616                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 02617                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 02618                           | Vector512.Equals(values3, current) | Vector512.Equals(values4, current));
 02619                    if (equals != Vector512<TValue>.Zero)
 2620                    {
 02621                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2622                    }
 2623                }
 2624            }
 22502625            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2626            {
 39362627                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 59042628                    values2 = Vector256.Create(value2), values3 = Vector256.Create(value3), values4 = Vector256.Create(v
 19682629                ref TValue currentSearchSpace = ref searchSpace;
 19682630                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector256<TValue>.Coun
 2631
 2632                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2633                do
 2634                {
 149882635                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 149882636                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 149882637                           | Vector256.Equals(values3, current) | Vector256.Equals(values4, current));
 149882638                    if (equals == Vector256<TValue>.Zero)
 2639                    {
 136232640                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 136232641                        continue;
 2642                    }
 2643
 13652644                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2645                }
 136232646                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2647
 2648                // If any elements remain, process the last vector in the search space.
 6032649                if ((uint)length % Vector256<TValue>.Count != 0)
 2650                {
 5162651                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 5162652                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 5162653                           | Vector256.Equals(values3, current) | Vector256.Equals(values4, current));
 5162654                    if (equals != Vector256<TValue>.Zero)
 2655                    {
 902656                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2657                    }
 2658                }
 2659            }
 2660            else
 2661            {
 5642662                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 8462663                    values2 = Vector128.Create(value2), values3 = Vector128.Create(value3), values4 = Vector128.Create(v
 2822664                ref TValue currentSearchSpace = ref searchSpace;
 2822665                ref TValue oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector128<TValue>.Coun
 2666
 2667                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2668                do
 2669                {
 2822670                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 2822671                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 2822672                           | Vector128.Equals(values3, current) | Vector128.Equals(values4, current));
 2822673                    if (equals == Vector128<TValue>.Zero)
 2674                    {
 1022675                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1022676                        continue;
 2677                    }
 2678
 1802679                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2680                }
 1022681                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2682
 2683                // If any elements remain, process the first vector in the search space.
 1022684                if ((uint)length % Vector128<TValue>.Count != 0)
 2685                {
 872686                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 872687                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 872688                           | Vector128.Equals(values3, current) | Vector128.Equals(values4, current));
 872689                    if (equals != Vector128<TValue>.Zero)
 2690                    {
 332691                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2692                    }
 2693                }
 2694            }
 2695
 5822696            return -1;
 2697        }
 2698
 2699        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2700        internal static int LastIndexOfValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T>
 28752701            => LastIndexOfValueType<T, DontNegate<T>>(ref searchSpace, value, length);
 2702
 2703        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2704        internal static int LastIndexOfAnyExceptValueType<T>(ref T searchSpace, T value, int length) where T : struct, I
 27162705            => LastIndexOfValueType<T, Negate<T>>(ref searchSpace, value, length);
 2706
 2707        private static int LastIndexOfValueType<TValue, TNegator>(ref TValue searchSpace, TValue value, int length)
 2708            where TValue : struct, INumber<TValue>
 2709            where TNegator : struct, INegator<TValue>
 2710        {
 55912711            Debug.Assert(length >= 0, "Expected non-negative length");
 55912712            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 2713
 55912714            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2715            {
 30662716                nuint offset = (nuint)length - 1;
 2717
 30902718                while (length >= 8)
 2719                {
 3362720                    length -= 8;
 2721
 3362722                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2723                    {
 1682724                        return (int)(offset);
 2725                    }
 1682726                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 1) == value))
 2727                    {
 442728                        return (int)(offset - 1);
 2729                    }
 1242730                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 2) == value))
 2731                    {
 282732                        return (int)(offset - 2);
 2733                    }
 962734                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 3) == value))
 2735                    {
 182736                        return (int)(offset - 3);
 2737                    }
 782738                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 4) == value))
 2739                    {
 162740                        return (int)(offset - 4);
 2741                    }
 622742                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 5) == value))
 2743                    {
 142744                        return (int)(offset - 5);
 2745                    }
 482746                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 6) == value))
 2747                    {
 142748                        return (int)(offset - 6);
 2749                    }
 342750                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 7) == value))
 2751                    {
 102752                        return (int)(offset - 7);
 2753                    }
 2754
 242755                    offset -= 8;
 2756                }
 2757
 27542758                if (length >= 4)
 2759                {
 12202760                    length -= 4;
 2761
 12202762                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2763                    {
 6022764                        return (int)(offset);
 2765                    }
 6182766                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 1) == value))
 2767                    {
 1082768                        return (int)(offset - 1);
 2769                    }
 5102770                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 2) == value))
 2771                    {
 382772                        return (int)(offset - 2);
 2773                    }
 4722774                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 3) == value))
 2775                    {
 662776                        return (int)(offset - 3);
 2777                    }
 2778
 4062779                    offset -= 4;
 2780                }
 2781
 32802782                while (length > 0)
 2783                {
 19782784                    length -= 1;
 2785
 19782786                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2787                    {
 6382788                        return (int)(offset);
 2789                    }
 2790
 13402791                    offset -= 1;
 2792                }
 2793
 13022794                return -1;
 2795            }
 25252796            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2797            {
 02798                return SimdImpl<Vector512<TValue>>(ref searchSpace, value, length);
 2799            }
 25252800            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2801            {
 19452802                return SimdImpl<Vector256<TValue>>(ref searchSpace, value, length);
 2803            }
 2804            else
 2805            {
 5802806                return SimdImpl<Vector128<TValue>>(ref searchSpace, value, length);
 2807            }
 2808
 2809            static int SimdImpl<TVector>(ref TValue searchSpace, TValue value, int length)
 2810                where TVector : struct, ISimdVector<TVector, TValue>
 2811            {
 2812                TVector current;
 25252813                TVector values = TVector.Create(value);
 2814
 25252815                int offset = length - TVector.ElementCount;
 2816
 2817                // Loop until either we've finished all elements -or- there's one or less than a vector's-worth remainin
 136922818                while (offset > 0)
 2819                {
 124622820                    current = TVector.LoadUnsafe(ref searchSpace, (uint)(offset));
 2821
 124622822                    if (TNegator.HasMatch(values, current))
 2823                    {
 12952824                        return offset + TVector.LastIndexOfWhereAllBitsSet(TNegator.GetMatchMask(values, current));
 2825                    }
 2826
 111672827                    offset -= TVector.ElementCount;
 2828                }
 2829
 2830                // Process the first vector in the search space.
 2831
 12302832                current = TVector.LoadUnsafe(ref searchSpace);
 2833
 12302834                if (TNegator.HasMatch(values, current))
 2835                {
 3002836                    return TVector.LastIndexOfWhereAllBitsSet(TNegator.GetMatchMask(values, current));
 2837                }
 2838
 9302839                return -1;
 2840            }
 2841        }
 2842
 2843        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2844        internal static int LastIndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, int length) where T : stru
 41562845            => LastIndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, length);
 2846
 2847        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2848        internal static int LastIndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, int length) where T 
 41542849            => LastIndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, length);
 2850
 2851        private static int LastIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value
 2852            where TValue : struct, INumber<TValue>
 2853            where TNegator : struct, INegator<TValue>
 2854        {
 83102855            Debug.Assert(length >= 0, "Expected non-negative length");
 83102856            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2857
 83102858            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2859            {
 38282860                nuint offset = (nuint)length - 1;
 2861                TValue lookUp;
 2862
 38282863                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 2864                {
 9022865                    while (length >= 8)
 2866                    {
 4362867                        length -= 8;
 2868
 4362869                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 4362870                        lookUp = current;
 4362871                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2872                        {
 2182873                            return (int)(offset);
 2874                        }
 2875
 2182876                        lookUp = Unsafe.Add(ref current, -1);
 2182877                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2878                        {
 642879                            return (int)(offset - 1);
 2880                        }
 2881
 1542882                        lookUp = Unsafe.Add(ref current, -2);
 1542883                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2884                        {
 222885                            return (int)(offset - 2);
 2886                        }
 2887
 1322888                        lookUp = Unsafe.Add(ref current, -3);
 1322889                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2890                        {
 162891                            return (int)(offset - 3);
 2892                        }
 2893
 1162894                        lookUp = Unsafe.Add(ref current, -4);
 1162895                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2896                        {
 202897                            return (int)(offset - 4);
 2898                        }
 2899
 962900                        lookUp = Unsafe.Add(ref current, -5);
 962901                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2902                        {
 142903                            return (int)(offset - 5);
 2904                        }
 2905
 822906                        lookUp = Unsafe.Add(ref current, -6);
 822907                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2908                        {
 82909                            return (int)(offset - 6);
 2910                        }
 2911
 742912                        lookUp = Unsafe.Add(ref current, -7);
 742913                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2914                        {
 242915                            return (int)(offset - 7);
 2916                        }
 2917
 502918                        offset -= 8;
 2919                    }
 2920                }
 2921
 37402922                while (length >= 4)
 2923                {
 11682924                    length -= 4;
 2925
 11682926                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 11682927                    lookUp = current;
 11682928                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2929                    {
 5762930                        return (int)(offset);
 2931                    }
 2932
 5922933                    lookUp = Unsafe.Add(ref current, -1);
 5922934                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2935                    {
 1522936                        return (int)(offset - 1);
 2937                    }
 2938
 4402939                    lookUp = Unsafe.Add(ref current, -2);
 4402940                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2941                    {
 842942                        return (int)(offset - 2);
 2943                    }
 2944
 3562945                    lookUp = Unsafe.Add(ref current, -3);
 3562946                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2947                    {
 582948                        return (int)(offset - 3);
 2949                    }
 2950
 2982951                    offset -= 4;
 2952                }
 2953
 42342954                while (length > 0)
 2955                {
 26702956                    length -= 1;
 2957
 26702958                    lookUp = Unsafe.Add(ref searchSpace, offset);
 26702959                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2960                    {
 10082961                        return (int)(offset);
 2962                    }
 2963
 16622964                    offset -= 1;
 2965                }
 2966
 15642967                return -1;
 2968            }
 44822969            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2970            {
 02971                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 02972                nint offset = length - Vector512<TValue>.Count;
 2973
 2974                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 02975                while (offset > 0)
 2976                {
 02977                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 02978                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 2979
 02980                    if (equals == Vector512<TValue>.Zero)
 2981                    {
 02982                        offset -= Vector512<TValue>.Count;
 02983                        continue;
 2984                    }
 2985
 02986                    return ComputeLastIndex(offset, equals);
 2987                }
 2988
 2989                // Process the first vector in the search space.
 2990
 02991                current = Vector512.LoadUnsafe(ref searchSpace);
 02992                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 2993
 02994                if (equals != Vector512<TValue>.Zero)
 2995                {
 02996                    return ComputeLastIndex(offset: 0, equals);
 2997                }
 2998            }
 44822999            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3000            {
 76683001                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 38343002                nint offset = length - Vector256<TValue>.Count;
 3003
 3004                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 378143005                while (offset > 0)
 3006                {
 361903007                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 361903008                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 3009
 361903010                    if (equals == Vector256<TValue>.Zero)
 3011                    {
 339803012                        offset -= Vector256<TValue>.Count;
 339803013                        continue;
 3014                    }
 3015
 22103016                    return ComputeLastIndex(offset, equals);
 3017                }
 3018
 3019                // Process the first vector in the search space.
 3020
 16243021                current = Vector256.LoadUnsafe(ref searchSpace);
 16243022                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 3023
 16243024                if (equals != Vector256<TValue>.Zero)
 3025                {
 2083026                    return ComputeLastIndex(offset: 0, equals);
 3027                }
 3028            }
 3029            else
 3030            {
 12963031                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 6483032                nint offset = length - Vector128<TValue>.Count;
 3033
 3034                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 7263035                while (offset > 0)
 3036                {
 2923037                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2923038                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 2923039                    if (equals == Vector128<TValue>.Zero)
 3040                    {
 783041                        offset -= Vector128<TValue>.Count;
 783042                        continue;
 3043                    }
 3044
 2143045                    return ComputeLastIndex(offset, equals);
 3046                }
 3047
 3048                // Process the first vector in the search space.
 4343049                current = Vector128.LoadUnsafe(ref searchSpace);
 4343050                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 4343051                if (equals != Vector128<TValue>.Zero)
 3052                {
 2243053                    return ComputeLastIndex(offset: 0, equals);
 3054                }
 3055            }
 3056
 16263057            return -1;
 3058        }
 3059
 3060        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3061        internal static int LastIndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, int length) wher
 25583062            => LastIndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, length);
 3063
 3064        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3065        internal static int LastIndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, int length
 25583066            => LastIndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, length);
 3067
 3068        private static int LastIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value
 3069            where TValue : struct, INumber<TValue>
 3070            where TNegator : struct, INegator<TValue>
 3071        {
 51163072            Debug.Assert(length >= 0, "Expected non-negative length");
 51163073            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3074
 51163075            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3076            {
 23603077                nuint offset = (nuint)length - 1;
 3078                TValue lookUp;
 3079
 23603080                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 3081                {
 13583082                    while (length >= 8)
 3083                    {
 7523084                        length -= 8;
 3085
 7523086                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 7523087                        lookUp = current;
 7523088                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3089                        {
 3763090                            return (int)(offset);
 3091                        }
 3092
 3763093                        lookUp = Unsafe.Add(ref current, -1);
 3763094                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3095                        {
 1003096                            return (int)(offset - 1);
 3097                        }
 3098
 2763099                        lookUp = Unsafe.Add(ref current, -2);
 2763100                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3101                        {
 583102                            return (int)(offset - 2);
 3103                        }
 3104
 2183105                        lookUp = Unsafe.Add(ref current, -3);
 2183106                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3107                        {
 463108                            return (int)(offset - 3);
 3109                        }
 3110
 1723111                        lookUp = Unsafe.Add(ref current, -4);
 1723112                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3113                        {
 303114                            return (int)(offset - 4);
 3115                        }
 3116
 1423117                        lookUp = Unsafe.Add(ref current, -5);
 1423118                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3119                        {
 223120                            return (int)(offset - 5);
 3121                        }
 3122
 1203123                        lookUp = Unsafe.Add(ref current, -6);
 1203124                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3125                        {
 363126                            return (int)(offset - 6);
 3127                        }
 3128
 843129                        lookUp = Unsafe.Add(ref current, -7);
 843130                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3131                        {
 223132                            return (int)(offset - 7);
 3133                        }
 3134
 623135                        offset -= 8;
 3136                    }
 3137                }
 3138
 18183139                while (length >= 4)
 3140                {
 7303141                    length -= 4;
 3142
 7303143                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 7303144                    lookUp = current;
 7303145                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3146                    {
 3583147                        return (int)(offset);
 3148                    }
 3149
 3723150                    lookUp = Unsafe.Add(ref current, -1);
 3723151                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3152                    {
 1123153                        return (int)(offset - 1);
 3154                    }
 3155
 2603156                    lookUp = Unsafe.Add(ref current, -2);
 2603157                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3158                    {
 683159                        return (int)(offset - 2);
 3160                    }
 3161
 1923162                    lookUp = Unsafe.Add(ref current, -3);
 1923163                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3164                    {
 443165                        return (int)(offset - 3);
 3166                    }
 3167
 1483168                    offset -= 4;
 3169                }
 3170
 17603171                while (length > 0)
 3172                {
 10843173                    length -= 1;
 3174
 10843175                    lookUp = Unsafe.Add(ref searchSpace, offset);
 10843176                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3177                    {
 4123178                        return (int)(offset);
 3179                    }
 3180
 6723181                    offset -= 1;
 3182                }
 3183
 6763184                return -1;
 3185            }
 27563186            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3187            {
 03188                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 03189                nint offset = length - Vector512<TValue>.Count;
 3190
 3191                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 03192                while (offset > 0)
 3193                {
 03194                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 03195                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 3196
 03197                    if (equals == Vector512<TValue>.Zero)
 3198                    {
 03199                        offset -= Vector512<TValue>.Count;
 03200                        continue;
 3201                    }
 3202
 03203                    return ComputeLastIndex(offset, equals);
 3204                }
 3205
 3206                // Process the first vector in the search space.
 03207                current = Vector512.LoadUnsafe(ref searchSpace);
 03208                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 3209
 03210                if (equals != Vector512<TValue>.Zero)
 3211                {
 03212                    return ComputeLastIndex(offset: 0, equals);
 3213                }
 3214            }
 27563215            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3216            {
 69843217                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 23283218                nint offset = length - Vector256<TValue>.Count;
 3219
 3220                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 185343221                while (offset > 0)
 3222                {
 177223223                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 177223224                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 3225
 177223226                    if (equals == Vector256<TValue>.Zero)
 3227                    {
 162063228                        offset -= Vector256<TValue>.Count;
 162063229                        continue;
 3230                    }
 3231
 15163232                    return ComputeLastIndex(offset, equals);
 3233                }
 3234
 3235                // Process the first vector in the search space.
 8123236                current = Vector256.LoadUnsafe(ref searchSpace);
 8123237                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 3238
 8123239                if (equals != Vector256<TValue>.Zero)
 3240                {
 1463241                    return ComputeLastIndex(offset: 0, equals);
 3242                }
 3243            }
 3244            else
 3245            {
 12843246                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 4283247                nint offset = length - Vector128<TValue>.Count;
 3248
 3249                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 4863250                while (offset > 0)
 3251                {
 2443252                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2443253                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 3254
 2443255                    if (equals == Vector128<TValue>.Zero)
 3256                    {
 583257                        offset -= Vector128<TValue>.Count;
 583258                        continue;
 3259                    }
 3260
 1863261                    return ComputeLastIndex(offset, equals);
 3262                }
 3263
 3264                // Process the first vector in the search space.
 2423265                current = Vector128.LoadUnsafe(ref searchSpace);
 2423266                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 3267
 2423268                if (equals != Vector128<TValue>.Zero)
 3269                {
 1363270                    return ComputeLastIndex(offset: 0, equals);
 3271                }
 3272            }
 3273
 7723274            return -1;
 3275        }
 3276
 3277        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3278        internal static int LastIndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, int le
 19063279            => LastIndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, value3, length);
 3280
 3281        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3282        internal static int LastIndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, 
 19063283            => LastIndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, value3, length);
 3284
 3285        private static int LastIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value
 3286            where TValue : struct, INumber<TValue>
 3287            where TNegator : struct, INegator<TValue>
 3288        {
 38123289            Debug.Assert(length >= 0, "Expected non-negative length");
 38123290            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3291
 38123292            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3293            {
 17883294                nuint offset = (nuint)length - 1;
 3295                TValue lookUp;
 3296
 21403297                while (length >= 4)
 3298                {
 13383299                    length -= 4;
 3300
 13383301                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 13383302                    lookUp = current;
 13383303                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3304                    {
 6003305                        return (int)(offset);
 3306                    }
 3307
 7383308                    lookUp = Unsafe.Add(ref current, -1);
 7383309                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3310                    {
 2263311                        return (int)(offset - 1);
 3312                    }
 3313
 5123314                    lookUp = Unsafe.Add(ref current, -2);
 5123315                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3316                    {
 943317                        return (int)(offset - 2);
 3318                    }
 3319
 4183320                    lookUp = Unsafe.Add(ref current, -3);
 4183321                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3322                    {
 663323                        return (int)(offset - 3);
 3324                    }
 3325
 3523326                    offset -= 4;
 3327                }
 3328
 11963329                while (length > 0)
 3330                {
 6483331                    length -= 1;
 3332
 6483333                    lookUp = Unsafe.Add(ref searchSpace, offset);
 6483334                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3335                    {
 2543336                        return (int)(offset);
 3337                    }
 3338
 3943339                    offset -= 1;
 3340                }
 3341
 5483342                return -1;
 3343            }
 20243344            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3345            {
 03346                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 03347                nint offset = length - Vector512<TValue>.Count;
 3348
 3349                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 03350                while (offset > 0)
 3351                {
 03352                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 03353                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 03354                                            | Vector512.Equals(current, values2) | Vector512.Equals(current, values3));
 03355                    if (equals == Vector512<TValue>.Zero)
 3356                    {
 03357                        offset -= Vector512<TValue>.Count;
 03358                        continue;
 3359                    }
 3360
 03361                    return ComputeLastIndex(offset, equals);
 3362                }
 3363
 3364                // Process the first vector in the search space.
 03365                current = Vector512.LoadUnsafe(ref searchSpace);
 03366                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 3367
 03368                if (equals != Vector512<TValue>.Zero)
 3369                {
 03370                    return ComputeLastIndex(offset: 0, equals);
 3371                }
 3372            }
 20243373            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3374            {
 68643375                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 17163376                nint offset = length - Vector256<TValue>.Count;
 3377
 3378                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 97743379                while (offset > 0)
 3380                {
 93463381                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 93463382                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 93463383                                            | Vector256.Equals(current, values2) | Vector256.Equals(current, values3));
 93463384                    if (equals == Vector256<TValue>.Zero)
 3385                    {
 80583386                        offset -= Vector256<TValue>.Count;
 80583387                        continue;
 3388                    }
 3389
 12883390                    return ComputeLastIndex(offset, equals);
 3391                }
 3392
 3393                // Process the first vector in the search space.
 4283394                current = Vector256.LoadUnsafe(ref searchSpace);
 4283395                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 4283396                if (equals != Vector256<TValue>.Zero)
 3397                {
 523398                    return ComputeLastIndex(offset: 0, equals);
 3399                }
 3400            }
 3401            else
 3402            {
 12323403                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 3083404                nint offset = length - Vector128<TValue>.Count;
 3405
 3406                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 3743407                while (offset > 0)
 3408                {
 2723409                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2723410                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 2723411                    if (equals == Vector128<TValue>.Zero)
 3412                    {
 663413                        offset -= Vector128<TValue>.Count;
 663414                        continue;
 3415                    }
 3416
 2063417                    return ComputeLastIndex(offset, equals);
 3418                }
 3419
 3420                // Process the first vector in the search space.
 1023421                current = Vector128.LoadUnsafe(ref searchSpace);
 1023422                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 1023423                if (equals != Vector128<TValue>.Zero)
 3424                {
 383425                    return ComputeLastIndex(offset: 0, equals);
 3426                }
 3427            }
 3428
 4403429            return -1;
 3430        }
 3431
 3432        public static void Replace<T>(ref T src, ref T dst, T oldValue, T newValue, nuint length) where T : IEquatable<T
 3433        {
 03434            if (default(T) is not null || oldValue is not null)
 3435            {
 03436                Debug.Assert(oldValue is not null);
 3437
 03438                for (nuint idx = 0; idx < length; ++idx)
 3439                {
 03440                    T original = Unsafe.Add(ref src, idx);
 03441                    Unsafe.Add(ref dst, idx) = oldValue.Equals(original) ? newValue : original;
 3442                }
 3443            }
 3444            else
 3445            {
 03446                for (nuint idx = 0; idx < length; ++idx)
 3447                {
 03448                    T original = Unsafe.Add(ref src, idx);
 03449                    Unsafe.Add(ref dst, idx) = original is null ? newValue : original;
 3450                }
 3451            }
 03452        }
 3453
 3454        public static void ReplaceValueType<T>(ref T src, ref T dst, T oldValue, T newValue, nuint length) where T : str
 3455        {
 03456            if (!Vector128.IsHardwareAccelerated || length < (uint)Vector128<T>.Count)
 3457            {
 03458                for (nuint idx = 0; idx < length; ++idx)
 3459                {
 03460                    T original = Unsafe.Add(ref src, idx);
 03461                    Unsafe.Add(ref dst, idx) = EqualityComparer<T>.Default.Equals(original, oldValue) ? newValue : origi
 3462                }
 3463            }
 3464            else
 3465            {
 03466                Debug.Assert(Vector128.IsHardwareAccelerated && Vector128<T>.IsSupported, "Vector128 is not HW-accelerat
 3467
 03468                nuint idx = 0;
 3469
 03470                if (!Vector256.IsHardwareAccelerated || length < (uint)Vector256<T>.Count)
 3471                {
 03472                    nuint lastVectorIndex = length - (uint)Vector128<T>.Count;
 03473                    Vector128<T> oldValues = Vector128.Create(oldValue);
 03474                    Vector128<T> newValues = Vector128.Create(newValue);
 3475                    Vector128<T> original, mask, result;
 3476
 3477                    do
 3478                    {
 03479                        original = Vector128.LoadUnsafe(ref src, idx);
 03480                        mask = Vector128.Equals(oldValues, original);
 03481                        result = Vector128.ConditionalSelect(mask, newValues, original);
 03482                        result.StoreUnsafe(ref dst, idx);
 3483
 03484                        idx += (uint)Vector128<T>.Count;
 3485                    }
 03486                    while (idx < lastVectorIndex);
 3487
 3488                    // There are (0, Vector128<T>.Count] elements remaining now.
 3489                    // As the operation is idempotent, and we know that in total there are at least Vector128<T>.Count
 3490                    // elements available, we read a vector from the very end, perform the replace and write to the
 3491                    // the resulting vector at the very end.
 3492                    // Thus we can eliminate the scalar processing of the remaining elements.
 03493                    original = Vector128.LoadUnsafe(ref src, lastVectorIndex);
 03494                    mask = Vector128.Equals(oldValues, original);
 03495                    result = Vector128.ConditionalSelect(mask, newValues, original);
 03496                    result.StoreUnsafe(ref dst, lastVectorIndex);
 3497                }
 03498                else if (!Vector512.IsHardwareAccelerated || length < (uint)Vector512<T>.Count)
 3499                {
 03500                    nuint lastVectorIndex = length - (uint)Vector256<T>.Count;
 03501                    Vector256<T> oldValues = Vector256.Create(oldValue);
 03502                    Vector256<T> newValues = Vector256.Create(newValue);
 3503                    Vector256<T> original, mask, result;
 3504
 3505                    do
 3506                    {
 03507                        original = Vector256.LoadUnsafe(ref src, idx);
 03508                        mask = Vector256.Equals(oldValues, original);
 03509                        result = Vector256.ConditionalSelect(mask, newValues, original);
 03510                        result.StoreUnsafe(ref dst, idx);
 3511
 03512                        idx += (uint)Vector256<T>.Count;
 3513                    }
 03514                    while (idx < lastVectorIndex);
 3515
 03516                    original = Vector256.LoadUnsafe(ref src, lastVectorIndex);
 03517                    mask = Vector256.Equals(oldValues, original);
 03518                    result = Vector256.ConditionalSelect(mask, newValues, original);
 03519                    result.StoreUnsafe(ref dst, lastVectorIndex);
 3520                }
 3521                else
 3522                {
 03523                    Debug.Assert(Vector512.IsHardwareAccelerated && Vector512<T>.IsSupported, "Vector512 is not HW-accel
 3524
 03525                    nuint lastVectorIndex = length - (uint)Vector512<T>.Count;
 03526                    Vector512<T> oldValues = Vector512.Create(oldValue);
 03527                    Vector512<T> newValues = Vector512.Create(newValue);
 3528                    Vector512<T> original, mask, result;
 3529
 3530                    do
 3531                    {
 03532                        original = Vector512.LoadUnsafe(ref src, idx);
 03533                        mask = Vector512.Equals(oldValues, original);
 03534                        result = Vector512.ConditionalSelect(mask, newValues, original);
 03535                        result.StoreUnsafe(ref dst, idx);
 3536
 03537                        idx += (uint)Vector512<T>.Count;
 3538                    }
 03539                    while (idx < lastVectorIndex);
 3540
 03541                    original = Vector512.LoadUnsafe(ref src, lastVectorIndex);
 03542                    mask = Vector512.Equals(oldValues, original);
 03543                    result = Vector512.ConditionalSelect(mask, newValues, original);
 03544                    result.StoreUnsafe(ref dst, lastVectorIndex);
 3545                }
 3546            }
 03547        }
 3548
 3549        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3550        internal static int LastIndexOfAnyValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, T valu
 14543551            => LastIndexOfAnyValueType<T, DontNegate<T>>(ref searchSpace, value0, value1, value2, value3, value4, length
 3552
 3553        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3554        internal static int LastIndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T value1, T value2, T value3, 
 14543555            => LastIndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, value3, value4, length);
 3556
 3557        private static int LastIndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value
 3558            where TValue : struct, INumber<TValue>
 3559            where TNegator : struct, INegator<TValue>
 3560        {
 29083561            Debug.Assert(length >= 0, "Expected non-negative length");
 29083562            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3563
 29083564            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3565            {
 14083566                nuint offset = (nuint)length - 1;
 3567                TValue lookUp;
 3568
 17523569                while (length >= 4)
 3570                {
 10743571                    length -= 4;
 3572
 10743573                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 10743574                    lookUp = current;
 10743575                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3576                    {
 4683577                        return (int)offset;
 3578                    }
 3579
 6063580                    lookUp = Unsafe.Add(ref current, -1);
 6063581                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3582                    {
 1603583                        return (int)offset - 1;
 3584                    }
 3585
 4463586                    lookUp = Unsafe.Add(ref current, -2);
 4463587                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3588                    {
 603589                        return (int)offset - 2;
 3590                    }
 3591
 3863592                    lookUp = Unsafe.Add(ref current, -3);
 3863593                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3594                    {
 423595                        return (int)offset - 3;
 3596                    }
 3597
 3443598                    offset -= 4;
 3599                }
 3600
 10443601                while (length > 0)
 3602                {
 5943603                    length -= 1;
 3604
 5943605                    lookUp = Unsafe.Add(ref searchSpace, offset);
 5943606                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3607                    {
 2283608                        return (int)offset;
 3609                    }
 3610
 3663611                    offset -= 1;
 3612                }
 3613            }
 15003614            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3615            {
 03616                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 03617                    values2 = Vector512.Create(value2), values3 = Vector512.Create(value3), values4 = Vector512.Create(v
 03618                nint offset = length - Vector512<TValue>.Count;
 3619
 3620                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 03621                while (offset > 0)
 3622                {
 03623                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 03624                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 03625                        | Vector512.Equals(current, values3) | Vector512.Equals(current, values4));
 03626                    if (equals == Vector512<TValue>.Zero)
 3627                    {
 03628                        offset -= Vector512<TValue>.Count;
 03629                        continue;
 3630                    }
 3631
 03632                    return ComputeLastIndex(offset, equals);
 3633                }
 3634
 3635                // Process the first vector in the search space.
 03636                current = Vector512.LoadUnsafe(ref searchSpace);
 03637                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 03638                    | Vector512.Equals(current, values3) | Vector512.Equals(current, values4));
 3639
 03640                if (equals != Vector512<TValue>.Zero)
 3641                {
 03642                    return ComputeLastIndex(offset: 0, equals);
 3643                }
 3644            }
 15003645            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3646            {
 26243647                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 39363648                    values2 = Vector256.Create(value2), values3 = Vector256.Create(value3), values4 = Vector256.Create(v
 13123649                nint offset = length - Vector256<TValue>.Count;
 3650
 3651                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 83463652                while (offset > 0)
 3653                {
 79363654                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 79363655                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 79363656                        | Vector256.Equals(current, values3) | Vector256.Equals(current, values4));
 79363657                    if (equals == Vector256<TValue>.Zero)
 3658                    {
 70343659                        offset -= Vector256<TValue>.Count;
 70343660                        continue;
 3661                    }
 3662
 9023663                    return ComputeLastIndex(offset, equals);
 3664                }
 3665
 3666                // Process the first vector in the search space.
 4103667                current = Vector256.LoadUnsafe(ref searchSpace);
 4103668                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 4103669                    | Vector256.Equals(current, values3) | Vector256.Equals(current, values4));
 3670
 4103671                if (equals != Vector256<TValue>.Zero)
 3672                {
 683673                    return ComputeLastIndex(offset: 0, equals);
 3674                }
 3675            }
 3676            else
 3677            {
 3763678                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 5643679                    values2 = Vector128.Create(value2), values3 = Vector128.Create(value3), values4 = Vector128.Create(v
 1883680                nint offset = length - Vector128<TValue>.Count;
 3681
 3682                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 2283683                while (offset > 0)
 3684                {
 1403685                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 1403686                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 1403687                        | Vector128.Equals(current, values3) | Vector128.Equals(current, values4));
 3688
 1403689                    if (equals == Vector128<TValue>.Zero)
 3690                    {
 403691                        offset -= Vector128<TValue>.Count;
 403692                        continue;
 3693                    }
 3694
 1003695                    return ComputeLastIndex(offset, equals);
 3696                }
 3697
 3698                // Process the first vector in the search space.
 3699
 883700                current = Vector128.LoadUnsafe(ref searchSpace);
 883701                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 883702                    | Vector128.Equals(current, values3) | Vector128.Equals(current, values4));
 3703
 883704                if (equals != Vector128<TValue>.Zero)
 3705                {
 423706                    return ComputeLastIndex(offset: 0, equals);
 3707                }
 3708            }
 3709
 8383710            return -1;
 3711        }
 3712
 3713        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3714        private static unsafe int ComputeFirstIndex<T>(ref T searchSpace, ref T current, Vector128<T> equals) where T : 
 3715        {
 46313716            uint notEqualsElements = equals.ExtractMostSignificantBits();
 46313717            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 46313718            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T));
 3719        }
 3720
 3721        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3722        private static unsafe int ComputeFirstIndex<T>(ref T searchSpace, ref T current, Vector256<T> equals) where T : 
 3723        {
 183523724            uint notEqualsElements = equals.ExtractMostSignificantBits();
 183523725            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 183523726            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T));
 3727        }
 3728
 3729        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3730        private static unsafe int ComputeFirstIndex<T>(ref T searchSpace, ref T current, Vector512<T> equals) where T : 
 3731        {
 03732            ulong notEqualsElements = equals.ExtractMostSignificantBits();
 03733            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 03734            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T));
 3735        }
 3736
 3737        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3738        private static int ComputeLastIndex<T>(nint offset, Vector128<T> equals) where T : struct
 3739        {
 17663740            uint notEqualsElements = equals.ExtractMostSignificantBits();
 17663741            int index = 31 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 17663742            return (int)offset + index;
 3743        }
 3744
 3745        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3746        private static int ComputeLastIndex<T>(nint offset, Vector256<T> equals) where T : struct
 3747        {
 86083748            uint notEqualsElements = equals.ExtractMostSignificantBits();
 86083749            int index = 31 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 86083750            return (int)offset + index;
 3751        }
 3752
 3753        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3754        private static int ComputeLastIndex<T>(nint offset, Vector512<T> equals) where T : struct
 3755        {
 03756            ulong notEqualsElements = equals.ExtractMostSignificantBits();
 03757            int index = 63 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 03758            return (int)offset + index;
 3759        }
 3760
 3761        internal interface INegator<T> where T : struct
 3762        {
 3763            static abstract bool NegateIfNeeded(bool equals);
 3764            static abstract Vector128<T> NegateIfNeeded(Vector128<T> equals);
 3765            static abstract Vector256<T> NegateIfNeeded(Vector256<T> equals);
 3766            static abstract Vector512<T> NegateIfNeeded(Vector512<T> equals);
 3767
 3768            // The generic vector APIs assume use for IndexOf where `DontNegate` is
 3769            // for `IndexOfAny` and `Negate` is for `IndexOfAnyExcept`
 3770
 3771            static abstract bool HasMatch<TVector>(TVector left, TVector right)
 3772                where TVector : struct, ISimdVector<TVector, T>;
 3773
 3774            static abstract TVector GetMatchMask<TVector>(TVector left, TVector right)
 3775                where TVector : struct, ISimdVector<TVector, T>;
 3776        }
 3777
 3778        internal readonly struct DontNegate<T> : INegator<T>
 3779            where T : struct
 3780        {
 613673781            public static bool NegateIfNeeded(bool equals) => equals;
 61643782            public static Vector128<T> NegateIfNeeded(Vector128<T> equals) => equals;
 1839773783            public static Vector256<T> NegateIfNeeded(Vector256<T> equals) => equals;
 03784            public static Vector512<T> NegateIfNeeded(Vector512<T> equals) => equals;
 3785
 3786            // The generic vector APIs assume use for `IndexOfAny` where we
 3787            // want "HasMatch" to mean any of the two elements match.
 3788
 3789            public static bool HasMatch<TVector>(TVector left, TVector right)
 3790                where TVector : struct, ISimdVector<TVector, T>
 3791            {
 111563792                return TVector.EqualsAny(left, right);
 3793            }
 3794
 3795            public static TVector GetMatchMask<TVector>(TVector left, TVector right)
 3796                where TVector : struct, ISimdVector<TVector, T>
 3797            {
 4553798                return TVector.Equals(left, right);
 3799            }
 3800        }
 3801
 3802        internal readonly struct Negate<T> : INegator<T>
 3803            where T : struct
 3804        {
 519883805            public static bool NegateIfNeeded(bool equals) => !equals;
 37303806            public static Vector128<T> NegateIfNeeded(Vector128<T> equals) => ~equals;
 593213807            public static Vector256<T> NegateIfNeeded(Vector256<T> equals) => ~equals;
 03808            public static Vector512<T> NegateIfNeeded(Vector512<T> equals) => ~equals;
 3809
 3810            // The generic vector APIs assume use for `IndexOfAnyExcept` where we
 3811            // want "HasMatch" to mean any of the two elements don't match
 3812
 3813            public static bool HasMatch<TVector>(TVector left, TVector right)
 3814                where TVector : struct, ISimdVector<TVector, T>
 3815            {
 25363816                return !TVector.EqualsAll(left, right);
 3817            }
 3818
 3819            public static TVector GetMatchMask<TVector>(TVector left, TVector right)
 3820                where TVector : struct, ISimdVector<TVector, T>
 3821            {
 11403822                return ~TVector.Equals(left, right);
 3823            }
 3824        }
 3825
 3826        internal static int IndexOfAnyInRange<T>(ref T searchSpace, T lowInclusive, T highInclusive, int length)
 3827            where T : IComparable<T>
 3828        {
 03829            for (int i = 0; i < length; i++)
 3830            {
 03831                ref T current = ref Unsafe.Add(ref searchSpace, i);
 03832                if ((lowInclusive.CompareTo(current) <= 0) && (highInclusive.CompareTo(current) >= 0))
 3833                {
 03834                    return i;
 3835                }
 3836            }
 3837
 03838            return -1;
 3839        }
 3840
 3841        internal static int IndexOfAnyExceptInRange<T>(ref T searchSpace, T lowInclusive, T highInclusive, int length)
 3842            where T : IComparable<T>
 3843        {
 03844            for (int i = 0; i < length; i++)
 3845            {
 03846                ref T current = ref Unsafe.Add(ref searchSpace, i);
 03847                if ((lowInclusive.CompareTo(current) > 0) || (highInclusive.CompareTo(current) < 0))
 3848                {
 03849                    return i;
 3850                }
 3851            }
 3852
 03853            return -1;
 3854        }
 3855
 3856        internal static int IndexOfAnyInRangeUnsignedNumber<T>(ref T searchSpace, T lowInclusive, T highInclusive, int l
 3857            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool> =>
 52243858            IndexOfAnyInRangeUnsignedNumber<T, DontNegate<T>>(ref searchSpace, lowInclusive, highInclusive, length);
 3859
 3860        internal static int IndexOfAnyExceptInRangeUnsignedNumber<T>(ref T searchSpace, T lowInclusive, T highInclusive,
 3861            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool> =>
 28043862            IndexOfAnyInRangeUnsignedNumber<T, Negate<T>>(ref searchSpace, lowInclusive, highInclusive, length);
 3863
 3864        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3865        private static int IndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T searchSpace, T lowInclusive, T highInclusi
 3866            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool>
 3867            where TNegator : struct, INegator<T>
 3868        {
 80283869            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePac
 3870            {
 43871                ref char charSearchSpace = ref Unsafe.As<T, char>(ref searchSpace);
 43872                char charLowInclusive = Unsafe.BitCast<T, char>(lowInclusive);
 43873                char charRange = (char)(Unsafe.BitCast<T, char>(highInclusive) - charLowInclusive);
 3874
 43875                return typeof(TNegator) == typeof(DontNegate<ushort>)
 43876                    ? PackedSpanHelpers.IndexOfAnyInRange(ref charSearchSpace, charLowInclusive, charRange, length)
 43877                    : PackedSpanHelpers.IndexOfAnyExceptInRange(ref charSearchSpace, charLowInclusive, charRange, length
 3878            }
 3879
 80243880            return NonPackedIndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref searchSpace, lowInclusive, highInclusive, l
 3881        }
 3882
 3883        internal static int NonPackedIndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T searchSpace, T lowInclusive, T h
 3884            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool>
 3885            where TNegator : struct, INegator<T>
 3886        {
 3887            // T must be a type whose comparison operator semantics match that of Vector128/256.
 3888
 114483889            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 3890            {
 52393891                T rangeInclusive = highInclusive - lowInclusive;
 293683892                for (int i = 0; i < length; i++)
 3893                {
 121533894                    ref T current = ref Unsafe.Add(ref searchSpace, i);
 121533895                    if (TNegator.NegateIfNeeded((current - lowInclusive) <= rangeInclusive))
 3896                    {
 27083897                        return i;
 3898                    }
 3899                }
 3900            }
 62093901            else if (!Vector256.IsHardwareAccelerated || length < Vector256<T>.Count)
 3902            {
 16743903                Vector128<T> lowVector = Vector128.Create(lowInclusive);
 16743904                Vector128<T> rangeVector = Vector128.Create(highInclusive - lowInclusive);
 3905                Vector128<T> inRangeVector;
 3906
 16743907                ref T current = ref searchSpace;
 16743908                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector128<T>.Count));
 3909
 3910                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 3911                do
 3912                {
 16743913                    inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref current) 
 16743914                    if (inRangeVector != Vector128<T>.Zero)
 3915                    {
 11293916                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3917                    }
 3918
 5453919                    current = ref Unsafe.Add(ref current, Vector128<T>.Count);
 3920                }
 5453921                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3922
 3923                // Process the last vector in the search space (which might overlap with already processed elements).
 5453924                inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref oneVectorAway
 5453925                if (inRangeVector != Vector128<T>.Zero)
 3926                {
 1343927                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3928                }
 3929            }
 45353930            else if (!Vector512.IsHardwareAccelerated || length < (uint)Vector512<T>.Count)
 3931            {
 45353932                Vector256<T> lowVector = Vector256.Create(lowInclusive);
 45353933                Vector256<T> rangeVector = Vector256.Create(highInclusive - lowInclusive);
 3934                Vector256<T> inRangeVector;
 3935
 45353936                ref T current = ref searchSpace;
 45353937                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector256<T>.Count));
 3938
 3939                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 3940                do
 3941                {
 113653942                    inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref current) 
 113653943                    if (inRangeVector != Vector256<T>.Zero)
 3944                    {
 33493945                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3946                    }
 3947
 80163948                    current = ref Unsafe.Add(ref current, Vector256<T>.Count);
 3949                }
 80163950                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3951
 3952                // Process the last vector in the search space (which might overlap with already processed elements).
 11863953                inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref oneVectorAway
 11863954                if (inRangeVector != Vector256<T>.Zero)
 3955                {
 3213956                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3957                }
 3958            }
 3959            else
 3960            {
 03961                Vector512<T> lowVector = Vector512.Create(lowInclusive);
 03962                Vector512<T> rangeVector = Vector512.Create(highInclusive - lowInclusive);
 3963                Vector512<T> inRangeVector;
 3964
 03965                ref T current = ref searchSpace;
 03966                ref T oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, (uint)(length - Vector512<T>.Count));
 3967
 3968                // Loop until either we've finished all elements or there's less than a vector's-worth remaining.
 3969                do
 3970                {
 03971                    inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref current) 
 03972                    if (inRangeVector != Vector512<T>.Zero)
 3973                    {
 03974                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3975                    }
 3976
 03977                    current = ref Unsafe.Add(ref current, Vector512<T>.Count);
 3978                }
 03979                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3980
 3981                // Process the last vector in the search space (which might overlap with already processed elements).
 03982                inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref oneVectorAway
 03983                if (inRangeVector != Vector512<T>.Zero)
 3984                {
 03985                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3986                }
 3987            }
 3988
 38073989            return -1;
 3990        }
 3991
 3992        internal static int LastIndexOfAnyInRange<T>(ref T searchSpace, T lowInclusive, T highInclusive, int length)
 3993            where T : IComparable<T>
 3994        {
 03995            for (int i = length - 1; i >= 0; i--)
 3996            {
 03997                ref T current = ref Unsafe.Add(ref searchSpace, i);
 03998                if ((lowInclusive.CompareTo(current) <= 0) && (highInclusive.CompareTo(current) >= 0))
 3999                {
 04000                    return i;
 4001                }
 4002            }
 4003
 04004            return -1;
 4005        }
 4006
 4007        internal static int LastIndexOfAnyExceptInRange<T>(ref T searchSpace, T lowInclusive, T highInclusive, int lengt
 4008            where T : IComparable<T>
 4009        {
 04010            for (int i = length - 1; i >= 0; i--)
 4011            {
 04012                ref T current = ref Unsafe.Add(ref searchSpace, i);
 04013                if ((lowInclusive.CompareTo(current) > 0) || (highInclusive.CompareTo(current) < 0))
 4014                {
 04015                    return i;
 4016                }
 4017            }
 4018
 04019            return -1;
 4020        }
 4021
 4022        internal static int LastIndexOfAnyInRangeUnsignedNumber<T>(ref T searchSpace, T lowInclusive, T highInclusive, i
 4023            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool> =>
 26544024            LastIndexOfAnyInRangeUnsignedNumber<T, DontNegate<T>>(ref searchSpace, lowInclusive, highInclusive, length);
 4025
 4026        internal static int LastIndexOfAnyExceptInRangeUnsignedNumber<T>(ref T searchSpace, T lowInclusive, T highInclus
 4027            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool> =>
 26544028            LastIndexOfAnyInRangeUnsignedNumber<T, Negate<T>>(ref searchSpace, lowInclusive, highInclusive, length);
 4029
 4030        private static int LastIndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T searchSpace, T lowInclusive, T highInc
 4031            where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool>
 4032            where TNegator : struct, INegator<T>
 4033        {
 4034            // T must be a type whose comparison operator semantics match that of Vector128/256.
 4035
 53084036            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 4037            {
 17284038                T rangeInclusive = highInclusive - lowInclusive;
 74924039                for (int i = length - 1; i >= 0; i--)
 4040                {
 29044041                    ref T current = ref Unsafe.Add(ref searchSpace, i);
 29044042                    if (TNegator.NegateIfNeeded((current - lowInclusive) <= rangeInclusive))
 4043                    {
 8864044                        return i;
 4045                    }
 4046                }
 4047            }
 35804048            else if (!Vector256.IsHardwareAccelerated || length < Vector256<T>.Count)
 4049            {
 7604050                Vector128<T> lowVector = Vector128.Create(lowInclusive);
 7604051                Vector128<T> rangeVector = Vector128.Create(highInclusive - lowInclusive);
 4052                Vector128<T> inRangeVector;
 4053
 7604054                nint offset = length - Vector128<T>.Count;
 4055
 4056                // Loop until either we've finished all elements or there's a vector's-worth or less remaining.
 8604057                while (offset > 0)
 4058                {
 4924059                    inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref searchSpa
 4924060                    if (inRangeVector != Vector128<T>.Zero)
 4061                    {
 3924062                        return ComputeLastIndex(offset, inRangeVector);
 4063                    }
 4064
 1004065                    offset -= Vector128<T>.Count;
 4066                }
 4067
 4068                // Process the first vector in the search space.
 3684069                inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref searchSpace) 
 3684070                if (inRangeVector != Vector128<T>.Zero)
 4071                {
 2284072                    return ComputeLastIndex(offset: 0, inRangeVector);
 4073                }
 4074            }
 28204075            else if (!Vector512.IsHardwareAccelerated || length < Vector512<T>.Count)
 4076            {
 28204077                Vector256<T> lowVector = Vector256.Create(lowInclusive);
 28204078                Vector256<T> rangeVector = Vector256.Create(highInclusive - lowInclusive);
 4079                Vector256<T> inRangeVector;
 4080
 28204081                nint offset = length - Vector256<T>.Count;
 4082
 4083                // Loop until either we've finished all elements or there's a vector's-worth or less remaining.
 130084084                while (offset > 0)
 4085                {
 121224086                    inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref searchSpa
 121224087                    if (inRangeVector != Vector256<T>.Zero)
 4088                    {
 19344089                        return ComputeLastIndex(offset, inRangeVector);
 4090                    }
 4091
 101884092                    offset -= Vector256<T>.Count;
 4093                }
 4094
 4095                // Process the first vector in the search space.
 8864096                inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref searchSpace) 
 8864097                if (inRangeVector != Vector256<T>.Zero)
 4098                {
 2844099                    return ComputeLastIndex(offset: 0, inRangeVector);
 4100                }
 4101            }
 4102            else
 4103            {
 04104                Vector512<T> lowVector = Vector512.Create(lowInclusive);
 04105                Vector512<T> rangeVector = Vector512.Create(highInclusive - lowInclusive);
 4106                Vector512<T> inRangeVector;
 4107
 04108                nint offset = length - Vector512<T>.Count;
 4109
 4110                // Loop until either we've finished all elements or there's a vector's-worth or less remaining.
 04111                while (offset > 0)
 4112                {
 04113                    inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref searchSpa
 04114                    if (inRangeVector != Vector512<T>.Zero)
 4115                    {
 04116                        return ComputeLastIndex(offset, inRangeVector);
 4117                    }
 4118
 04119                    offset -= Vector512<T>.Count;
 4120                }
 4121
 4122                // Process the first vector in the search space.
 04123                inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref searchSpace) 
 04124                if (inRangeVector != Vector512<T>.Zero)
 4125                {
 04126                    return ComputeLastIndex(offset: 0, inRangeVector);
 4127                }
 4128            }
 4129
 15844130            return -1;
 4131        }
 4132
 4133        public static int Count<T>(ref T current, T value, int length) where T : IEquatable<T>?
 4134        {
 04135            int count = 0;
 4136
 04137            ref T end = ref Unsafe.Add(ref current, length);
 04138            if (value is not null)
 4139            {
 04140                while (Unsafe.IsAddressLessThan(ref current, ref end))
 4141                {
 04142                    if (value.Equals(current))
 4143                    {
 04144                        count++;
 4145                    }
 4146
 04147                    current = ref Unsafe.Add(ref current, 1);
 4148                }
 4149            }
 4150            else
 4151            {
 04152                while (Unsafe.IsAddressLessThan(ref current, ref end))
 4153                {
 04154                    if (current is null)
 4155                    {
 04156                        count++;
 4157                    }
 4158
 04159                    current = ref Unsafe.Add(ref current, 1);
 4160                }
 4161            }
 4162
 04163            return count;
 4164        }
 4165
 4166        public static unsafe int CountValueType<T>(ref T current, T value, int length) where T : struct, IEquatable<T>
 4167        {
 04168            int count = 0;
 04169            ref T end = ref Unsafe.Add(ref current, length);
 4170
 04171            if (Vector128.IsHardwareAccelerated && length >= Vector128<T>.Count)
 4172            {
 04173                if (Vector512.IsHardwareAccelerated && length >= Vector512<T>.Count)
 4174                {
 04175                    Vector512<T> targetVector = Vector512.Create(value);
 04176                    ref T oneVectorAwayFromEnd = ref Unsafe.Subtract(ref end, Vector512<T>.Count);
 04177                    while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd))
 4178                    {
 04179                        count += BitOperations.PopCount(Vector512.Equals(Vector512.LoadUnsafe(ref current), targetVector
 04180                        current = ref Unsafe.Add(ref current, Vector512<T>.Count);
 4181                    }
 4182
 4183                    // Count the last vector and mask off the elements that were already counted (number of elements bet
 04184                    ulong mask = Vector512.Equals(Vector512.LoadUnsafe(ref oneVectorAwayFromEnd), targetVector).ExtractM
 04185                    mask >>= (int)((nuint)Unsafe.ByteOffset(ref oneVectorAwayFromEnd, ref current) / (uint)sizeof(T));
 04186                    count += BitOperations.PopCount(mask);
 4187                }
 04188                else if (Vector256.IsHardwareAccelerated && length >= Vector256<T>.Count)
 4189                {
 04190                    Vector256<T> targetVector = Vector256.Create(value);
 04191                    ref T oneVectorAwayFromEnd = ref Unsafe.Subtract(ref end, Vector256<T>.Count);
 04192                    while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd))
 4193                    {
 04194                        count += BitOperations.PopCount(Vector256.Equals(Vector256.LoadUnsafe(ref current), targetVector
 04195                        current = ref Unsafe.Add(ref current, Vector256<T>.Count);
 4196                    }
 4197
 4198                    // Count the last vector and mask off the elements that were already counted (number of elements bet
 04199                    uint mask = Vector256.Equals(Vector256.LoadUnsafe(ref oneVectorAwayFromEnd), targetVector).ExtractMo
 04200                    mask >>= (int)((nuint)Unsafe.ByteOffset(ref oneVectorAwayFromEnd, ref current) / (uint)sizeof(T));
 04201                    count += BitOperations.PopCount(mask);
 4202                }
 4203                else
 4204                {
 04205                    Vector128<T> targetVector = Vector128.Create(value);
 04206                    ref T oneVectorAwayFromEnd = ref Unsafe.Subtract(ref end, Vector128<T>.Count);
 04207                    while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd))
 4208                    {
 04209                        count += BitOperations.PopCount(Vector128.Equals(Vector128.LoadUnsafe(ref current), targetVector
 04210                        current = ref Unsafe.Add(ref current, Vector128<T>.Count);
 4211                    }
 4212
 4213                    // Count the last vector and mask off the elements that were already counted (number of elements bet
 04214                    uint mask = Vector128.Equals(Vector128.LoadUnsafe(ref oneVectorAwayFromEnd), targetVector).ExtractMo
 04215                    mask >>= (int)((nuint)Unsafe.ByteOffset(ref oneVectorAwayFromEnd, ref current) / (uint)sizeof(T));
 04216                    count += BitOperations.PopCount(mask);
 4217                }
 4218            }
 4219            else
 4220            {
 04221                while (Unsafe.IsAddressLessThan(ref current, ref end))
 4222                {
 04223                    if (current.Equals(value))
 4224                    {
 04225                        count++;
 4226                    }
 4227
 04228                    current = ref Unsafe.Add(ref current, 1);
 4229                }
 4230            }
 4231
 04232            return count;
 4233        }
 4234    }
 4235}

Methods/Properties

BinarySearch(System.ReadOnlySpan`1<T>,TComparable)
BinarySearch(T&,System.Int32,TComparable)
.ctor(T,TComparer)
CompareTo(T)
IndexOf(System.Byte&,System.Int32,System.Byte&,System.Int32)
LastIndexOf(System.Byte&,System.Int32,System.Byte&,System.Int32)
ThrowMustBeNullTerminatedString()
IndexOfNullByte(System.Byte*)
SequenceEqual(System.Byte&,System.Byte&,System.UIntPtr)
SequenceCompareTo(System.Byte&,System.Int32,System.Byte&,System.Int32)
CommonPrefixLength(System.Byte&,System.Byte&,System.UIntPtr)
LoadUShort(System.Byte&)
LoadUInt(System.Byte&)
LoadUInt(System.Byte&,System.UIntPtr)
LoadNUInt(System.Byte&)
LoadNUInt(System.Byte&,System.UIntPtr)
GetByteVector128SpanLength(System.UIntPtr,System.Int32)
GetByteVector256SpanLength(System.UIntPtr,System.Int32)
GetByteVector512SpanLength(System.UIntPtr,System.Int32)
UnalignedCountVector128(System.Byte*)
Reverse(System.Byte&,System.UIntPtr)
Memmove(System.Byte&,System.Byte&,System.UIntPtr)
MemmoveNative(System.Byte&,System.Byte&,System.UIntPtr)
ClearWithoutReferences(System.Byte&,System.UIntPtr)
ZeroMemoryNative(System.Byte&,System.UIntPtr)
Fill(System.Byte&,System.Byte,System.UIntPtr)
IndexOf(System.Char&,System.Int32,System.Char&,System.Int32)
LastIndexOf(System.Char&,System.Int32,System.Char&,System.Int32)
SequenceCompareTo(System.Char&,System.Int32,System.Char&,System.Int32)
IndexOfNullCharacter(System.Char*)
GetCharVector128SpanLength(System.IntPtr,System.IntPtr)
GetCharVector256SpanLength(System.IntPtr,System.IntPtr)
GetCharVector512SpanLength(System.IntPtr,System.IntPtr)
UnalignedCountVector128(System.Char*)
Reverse(System.Char&,System.UIntPtr)
ClearWithReferences(System.IntPtr&,System.UIntPtr)
Reverse(System.Int32&,System.UIntPtr)
Reverse(System.Int64&,System.UIntPtr)
Reverse(T&,System.UIntPtr)
ReverseInner(T&,System.UIntPtr)
Fill(T&,System.UIntPtr,T)
IndexOf(T&,System.Int32,T&,System.Int32)
Contains(T&,T,System.Int32)
IndexOf(T&,T,System.Int32)
IndexOfAny(T&,T,T,System.Int32)
IndexOfAny(T&,T,T,T,System.Int32)
IndexOfAny(T&,System.Int32,T&,System.Int32)
LastIndexOf(T&,System.Int32,T&,System.Int32)
LastIndexOf(T&,T,System.Int32)
LastIndexOfAny(T&,T,T,System.Int32)
LastIndexOfAny(T&,T,T,T,System.Int32)
LastIndexOfAny(T&,System.Int32,T&,System.Int32)
IndexOfAnyExcept(T&,T,System.Int32)
LastIndexOfAnyExcept(T&,T,System.Int32)
IndexOfAnyExcept(T&,T,T,System.Int32)
LastIndexOfAnyExcept(T&,T,T,System.Int32)
IndexOfAnyExcept(T&,T,T,T,System.Int32)
LastIndexOfAnyExcept(T&,T,T,T,System.Int32)
IndexOfAnyExcept(T&,T,T,T,T,System.Int32)
LastIndexOfAnyExcept(T&,T,T,T,T,System.Int32)
SequenceEqual(T&,T&,System.Int32)
SequenceCompareTo(T&,System.Int32,T&,System.Int32)
ContainsValueType(T&,T,System.Int32)
NonPackedContainsValueType(T&,T,System.Int32)
IndexOfChar(System.Char&,System.Char,System.Int32)
LastIndexOfChar(System.Char&,System.Char,System.Int32)
NonPackedIndexOfChar(System.Char&,System.Char,System.Int32)
IndexOfValueType(T&,T,System.Int32)
IndexOfAnyExceptValueType(T&,T,System.Int32)
IndexOfValueType(TValue&,TValue,System.Int32)
NonPackedIndexOfValueType(TValue&,TValue,System.Int32)
IndexOfAnyChar(System.Char&,System.Char,System.Char,System.Int32)
LastIndexOfAnyChar(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAnyValueType(T&,T,T,System.Int32)
IndexOfAnyExceptValueType(T&,T,T,System.Int32)
IndexOfAnyValueType(TValue&,TValue,TValue,System.Int32)
NonPackedIndexOfAnyValueType(TValue&,TValue,TValue,System.Int32)
IndexOfAnyValueType(T&,T,T,T,System.Int32)
IndexOfAnyExceptValueType(T&,T,T,T,System.Int32)
IndexOfAnyValueType(TValue&,TValue,TValue,TValue,System.Int32)
NonPackedIndexOfAnyValueType(TValue&,TValue,TValue,TValue,System.Int32)
IndexOfAnyValueType(T&,T,T,T,T,System.Int32)
IndexOfAnyExceptValueType(T&,T,T,T,T,System.Int32)
IndexOfAnyValueType(TValue&,TValue,TValue,TValue,TValue,System.Int32)
IndexOfAnyValueType(T&,T,T,T,T,T,System.Int32)
IndexOfAnyExceptValueType(T&,T,T,T,T,T,System.Int32)
IndexOfAnyValueType(TValue&,TValue,TValue,TValue,TValue,TValue,System.Int32)
LastIndexOfValueType(T&,T,System.Int32)
LastIndexOfAnyExceptValueType(T&,T,System.Int32)
LastIndexOfValueType(TValue&,TValue,System.Int32)
SimdImpl(TValue&,TValue,System.Int32)
LastIndexOfAnyValueType(T&,T,T,System.Int32)
LastIndexOfAnyExceptValueType(T&,T,T,System.Int32)
LastIndexOfAnyValueType(TValue&,TValue,TValue,System.Int32)
LastIndexOfAnyValueType(T&,T,T,T,System.Int32)
LastIndexOfAnyExceptValueType(T&,T,T,T,System.Int32)
LastIndexOfAnyValueType(TValue&,TValue,TValue,TValue,System.Int32)
LastIndexOfAnyValueType(T&,T,T,T,T,System.Int32)
LastIndexOfAnyExceptValueType(T&,T,T,T,T,System.Int32)
LastIndexOfAnyValueType(TValue&,TValue,TValue,TValue,TValue,System.Int32)
Replace(T&,T&,T,T,System.UIntPtr)
ReplaceValueType(T&,T&,T,T,System.UIntPtr)
LastIndexOfAnyValueType(T&,T,T,T,T,T,System.Int32)
LastIndexOfAnyExceptValueType(T&,T,T,T,T,T,System.Int32)
LastIndexOfAnyValueType(TValue&,TValue,TValue,TValue,TValue,TValue,System.Int32)
ComputeFirstIndex(T&,T&,System.Runtime.Intrinsics.Vector128`1<T>)
ComputeFirstIndex(T&,T&,System.Runtime.Intrinsics.Vector256`1<T>)
ComputeFirstIndex(T&,T&,System.Runtime.Intrinsics.Vector512`1<T>)
ComputeLastIndex(System.IntPtr,System.Runtime.Intrinsics.Vector128`1<T>)
ComputeLastIndex(System.IntPtr,System.Runtime.Intrinsics.Vector256`1<T>)
ComputeLastIndex(System.IntPtr,System.Runtime.Intrinsics.Vector512`1<T>)
NegateIfNeeded(System.Boolean)
NegateIfNeeded(System.Runtime.Intrinsics.Vector128`1<T>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector256`1<T>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector512`1<T>)
HasMatch(TVector,TVector)
GetMatchMask(TVector,TVector)
NegateIfNeeded(System.Boolean)
NegateIfNeeded(System.Runtime.Intrinsics.Vector128`1<T>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector256`1<T>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector512`1<T>)
HasMatch(TVector,TVector)
GetMatchMask(TVector,TVector)
IndexOfAnyInRange(T&,T,T,System.Int32)
IndexOfAnyExceptInRange(T&,T,T,System.Int32)
IndexOfAnyInRangeUnsignedNumber(T&,T,T,System.Int32)
IndexOfAnyExceptInRangeUnsignedNumber(T&,T,T,System.Int32)
NonPackedIndexOfAnyInRangeUnsignedNumber(T&,T,T,System.Int32)
LastIndexOfAnyInRange(T&,T,T,System.Int32)
LastIndexOfAnyExceptInRange(T&,T,T,System.Int32)
LastIndexOfAnyInRangeUnsignedNumber(T&,T,T,System.Int32)
LastIndexOfAnyExceptInRangeUnsignedNumber(T&,T,T,System.Int32)
Count(T&,T,System.Int32)
CountValueType(T&,T,System.Int32)