< Summary

Line coverage
46%
Covered lines: 1493
Uncovered lines: 1713
Coverable lines: 3206
Total lines: 7736
Line coverage: 46.5%
Branch coverage
43%
Covered branches: 1274
Total branches: 2910
Branch coverage: 43.7%
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(...)47.82%929246.55%
File 2: SequenceEqual(...)62.96%545473.33%
File 2: SequenceCompareTo(...)32.69%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%110%
File 2: GetByteVector512SpanLength(...)100%11100%
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(...)18.91%747423.52%
File 4: LastIndexOf(...)0%72720%
File 4: SequenceCompareTo(...)88.46%2626100%
File 4: IndexOfNullCharacter(...)29.48%787833.91%
File 4: GetCharVector128SpanLength(...)100%110%
File 4: GetCharVector256SpanLength(...)100%110%
File 4: GetCharVector512SpanLength(...)100%11100%
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(...)83.33%6666.66%
File 6: NonPackedContainsValueType(...)76.31%767678.57%
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(...)89.47%7676100%
File 6: IndexOfAnyChar(...)100%110%
File 6: LastIndexOfAnyChar(...)100%110%
File 6: IndexOfAnyValueType(...)100%110%
File 6: IndexOfAnyExceptValueType(...)100%110%
File 6: IndexOfAnyValueType(...)0%18180%
File 6: NonPackedIndexOfAnyValueType(...)92.3%104104100%
File 6: IndexOfAnyValueType(...)100%110%
File 6: IndexOfAnyExceptValueType(...)100%110%
File 6: IndexOfAnyValueType(...)0%12120%
File 6: NonPackedIndexOfAnyValueType(...)93.84%130130100%
File 6: IndexOfAnyValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%11100%
File 6: IndexOfAnyValueType(...)90.9%8888100%
File 6: IndexOfAnyValueType(...)100%11100%
File 6: IndexOfAnyExceptValueType(...)100%11100%
File 6: IndexOfAnyValueType(...)91.83%9898100%
File 6: LastIndexOfValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfValueType(...)88.46%5252100%
File 6: SimdImpl(TValue&,TValue,System.Int32)100%66100%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)93.87%9898100%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)95.16%124124100%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)92.68%8282100%
File 6: Replace(...)0%12120%
File 6: ReplaceValueType(...)0%26260%
File 6: LastIndexOfAnyValueType(...)100%11100%
File 6: LastIndexOfAnyExceptValueType(...)100%11100%
File 6: LastIndexOfAnyValueType(...)93.47%9292100%
File 6: ComputeFirstIndex(...)100%11100%
File 6: ComputeFirstIndex(...)100%11100%
File 6: ComputeFirstIndex(...)100%11100%
File 6: ComputeLastIndex(...)100%11100%
File 6: ComputeLastIndex(...)100%11100%
File 6: ComputeLastIndex(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
File 6: NegateIfNeeded(...)100%11100%
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%11100%
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(...)85.29%3434100%
File 6: LastIndexOfAnyInRange(...)0%660%
File 6: LastIndexOfAnyExceptInRange(...)0%660%
File 6: LastIndexOfAnyInRangeUnsignedNumber(...)91.17%3434100%
File 6: LastIndexOfAnyExceptInRangeUnsignedNumber(...)100%11100%
File 6: Count(...)0%10100%
File 6: CountValueType(...)0%22220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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        {
 4247117            if (comparable == null)
 018                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparable);
 19
 4247120            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        {
 4247127            int lo = 0;
 4247128            int hi = length - 1;
 29            // If length == 0, hi == -1, and loop will not be entered
 29032230            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);`
 24785438                int i = (int)(((uint)hi + (uint)lo) >> 1);
 39
 24785440                int c = comparable.CompareTo(Unsafe.Add(ref spanStart, i));
 24785441                if (c == 0)
 42                {
 343                    return i;
 44                }
 24785145                else if (c > 0)
 46                {
 10356347                    lo = i + 1;
 48                }
 49                else
 50                {
 14428851                    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.
 4246858            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}
 79

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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
 1015457            nuint offset = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations
 1015458            nuint lengthToExamine = (nuint)(uint)Length;
 459
 1015460            if (Vector128.IsHardwareAccelerated)
 461            {
 462                // Avx2 branch also operates on Sse2 sizes, so check is combined.
 1015463                lengthToExamine = UnalignedCountVector128(searchSpace);
 464            }
 465
 466        SequentialScan:
 1400467            while (lengthToExamine >= 8)
 468            {
 444469                lengthToExamine -= 8;
 470
 444471                if (uValue == searchSpace[offset])
 472                    goto Found;
 444473                if (uValue == searchSpace[offset + 1])
 474                    goto Found1;
 444475                if (uValue == searchSpace[offset + 2])
 476                    goto Found2;
 442477                if (uValue == searchSpace[offset + 3])
 478                    goto Found3;
 441479                if (uValue == searchSpace[offset + 4])
 480                    goto Found4;
 428481                if (uValue == searchSpace[offset + 5])
 482                    goto Found5;
 416483                if (uValue == searchSpace[offset + 6])
 484                    goto Found6;
 412485                if (uValue == searchSpace[offset + 7])
 486                    goto Found7;
 487
 385488                offset += 8;
 489            }
 490
 956491            if (lengthToExamine >= 4)
 492            {
 499493                lengthToExamine -= 4;
 494
 499495                if (uValue == searchSpace[offset])
 496                    goto Found;
 471497                if (uValue == searchSpace[offset + 1])
 498                    goto Found1;
 466499                if (uValue == searchSpace[offset + 2])
 500                    goto Found2;
 458501                if (uValue == searchSpace[offset + 3])
 502                    goto Found3;
 503
 447504                offset += 4;
 505            }
 506
 2313507            while (lengthToExamine > 0)
 508            {
 1461509                lengthToExamine -= 1;
 510
 1461511                if (uValue == searchSpace[offset])
 512                    goto Found;
 513
 1409514                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
 852520            if (Vector512.IsHardwareAccelerated)
 521            {
 852522                if (offset < (nuint)(uint)Length)
 523                {
 852524                    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.
 345530                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 531
 532                        // Same method as below
 345533                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 345534                        if (matches == 0)
 535                        {
 536                            // Zero flags set so no matches
 58537                            offset += (nuint)Vector128<byte>.Count;
 538                        }
 539                        else
 540                        {
 541                            // Find bitflag offset of first match and add to current offset
 287542                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 543                        }
 544                    }
 545
 565546                    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.
 337552                        Vector256<byte> search = Vector256.Load(searchSpace + offset);
 553
 554                        // Same method as below
 337555                        uint matches = Vector256.Equals(Vector256<byte>.Zero, search).ExtractMostSignificantBits();
 337556                        if (matches == 0)
 557                        {
 558                            // Zero flags set so no matches
 1559                            offset += (nuint)Vector256<byte>.Count;
 560                        }
 561                        else
 562                        {
 563                            // Find bitflag offset of first match and add to current offset
 336564                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 565                        }
 566                    }
 229567                    lengthToExamine = GetByteVector512SpanLength(offset, Length);
 229568                    if (lengthToExamine > offset)
 569                    {
 570                        do
 571                        {
 229572                            Vector512<byte> search = Vector512.Load(searchSpace + offset);
 229573                            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.
 229576                            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
 229584                            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            }
 0633            else if (Vector256.IsHardwareAccelerated)
 634            {
 0635                if (offset < (nuint)(uint)Length)
 636                {
 0637                    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.
 0643                        Vector128<byte> search = Vector128.Load(searchSpace + offset);
 644
 645                        // Same method as below
 0646                        uint matches = Vector128.Equals(Vector128<byte>.Zero, search).ExtractMostSignificantBits();
 0647                        if (matches == 0)
 648                        {
 649                            // Zero flags set so no matches
 0650                            offset += (nuint)Vector128<byte>.Count;
 651                        }
 652                        else
 653                        {
 654                            // Find bitflag offset of first match and add to current offset
 0655                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 656                        }
 657                    }
 658
 0659                    lengthToExamine = GetByteVector256SpanLength(offset, Length);
 0660                    if (lengthToExamine > offset)
 661                    {
 662                        do
 663                        {
 0664                            Vector256<byte> search = Vector256.Load(searchSpace + offset);
 0665                            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.
 0668                            if (matches == 0)
 669                            {
 670                                // Zero flags set so no matches
 0671                                offset += (nuint)Vector256<byte>.Count;
 0672                                continue;
 673                            }
 674
 675                            // Find bitflag offset of first match and add to current offset
 0676                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 0677                        } 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
 80740            return (int)offset;
 741        Found1:
 5742            return (int)(offset + 1);
 743        Found2:
 10744            return (int)(offset + 2);
 745        Found3:
 12746            return (int)(offset + 3);
 747        Found4:
 13748            return (int)(offset + 4);
 749        Found5:
 12750            return (int)(offset + 5);
 751        Found6:
 4752            return (int)(offset + 6);
 753        Found7:
 27754            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
 859764            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
 840804            if (!Unsafe.AreSame(ref first, ref second))
 805            {
 806                // C# compiler inverts this test, making the outer goto the conditional jmp.
 840807                goto Vector;
 808            }
 809
 810            // This becomes a conditional jmp forward to not favor it.
 811            goto Equal;
 812
 813        Result:
 661814            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:
 198818            return true;
 819
 820        Vector:
 840821            if (Vector128.IsHardwareAccelerated)
 822            {
 840823                if (Vector512.IsHardwareAccelerated && length >= (nuint)Vector512<byte>.Count)
 824                {
 17825                    nuint offset = 0;
 17826                    nuint lengthToExamine = length - (nuint)Vector512<byte>.Count;
 827                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 17828                    Debug.Assert(lengthToExamine < length);
 17829                    if (lengthToExamine != 0)
 830                    {
 831                        do
 832                        {
 15833                            if (Vector512.LoadUnsafe(ref first, offset) !=
 15834                                Vector512.LoadUnsafe(ref second, offset))
 835                            {
 836                                goto NotEqual;
 837                            }
 15838                            offset += (nuint)Vector512<byte>.Count;
 15839                        } while (lengthToExamine > offset);
 840                    }
 841
 842                    // Do final compare as Vector512<byte>.Count from end rather than start
 17843                    if (Vector512.LoadUnsafe(ref first, lengthToExamine) ==
 17844                        Vector512.LoadUnsafe(ref second, lengthToExamine))
 845                    {
 846                        // C# compiler inverts this test, making the outer goto the conditional jmp.
 17847                        goto Equal;
 848                    }
 849
 850                    // This becomes a conditional jmp forward to not favor it.
 851                    goto NotEqual;
 852                }
 823853                else if (Vector256.IsHardwareAccelerated && length >= (nuint)Vector256<byte>.Count)
 854                {
 25855                    nuint offset = 0;
 25856                    nuint lengthToExamine = length - (nuint)Vector256<byte>.Count;
 857                    // Unsigned, so it shouldn't have overflowed larger than length (rather than negative)
 25858                    Debug.Assert(lengthToExamine < length);
 25859                    if (lengthToExamine != 0)
 860                    {
 861                        do
 862                        {
 23863                            if (Vector256.LoadUnsafe(ref first, offset) !=
 23864                                Vector256.LoadUnsafe(ref second, offset))
 865                            {
 866                                goto NotEqual;
 867                            }
 23868                            offset += (nuint)Vector256<byte>.Count;
 23869                        } while (lengthToExamine > offset);
 870                    }
 871
 872                    // Do final compare as Vector256<byte>.Count from end rather than start
 25873                    if (Vector256.LoadUnsafe(ref first, lengthToExamine) ==
 25874                        Vector256.LoadUnsafe(ref second, lengthToExamine))
 875                    {
 876                        // C# compiler inverts this test, making the outer goto the conditional jmp.
 25877                        goto Equal;
 878                    }
 879
 880                    // This becomes a conditional jmp forward to not favor it.
 881                    goto NotEqual;
 882                }
 798883                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
 642916            if (Vector128.IsHardwareAccelerated)
 917            {
 642918                Debug.Assert(length <= (nuint)sizeof(nuint) * 2);
 919
 642920                nuint offset = length - (nuint)sizeof(nuint);
 642921                nuint differentBits = LoadNUInt(ref first) - LoadNUInt(ref second);
 642922                differentBits |= LoadNUInt(ref first, offset) - LoadNUInt(ref second, offset);
 642923                result = (differentBits == 0);
 642924                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:
 0960            return false;
 961        }
 962
 963        public static unsafe int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength)
 964        {
 9965            Debug.Assert(firstLength >= 0);
 9966            Debug.Assert(secondLength >= 0);
 967
 9968            if (Unsafe.AreSame(ref first, ref second))
 969                goto Equal;
 970
 9971            nuint minLength = (nuint)(((uint)firstLength < (uint)secondLength) ? (uint)firstLength : (uint)secondLength)
 972
 9973            nuint offset = 0; // Use nuint for arithmetic to avoid unnecessary 64->32->64 truncations
 9974            nuint lengthToExamine = minLength;
 975
 9976            if (Vector256.IsHardwareAccelerated)
 977            {
 9978                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
 91019                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
 91060                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
 91124            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
 311138            while (minLength > offset)
 1139            {
 301140                int result = Unsafe.AddByteOffset(ref first, offset).CompareTo(Unsafe.AddByteOffset(ref second, offset))
 301141                if (result != 0)
 81142                    return result;
 221143                offset += 1;
 1144            }
 1145
 1146        Equal:
 11147            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)
 12841266            => Unsafe.ReadUnaligned<nuint>(ref start);
 1267
 1268        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1269        private static nuint LoadNUInt(ref byte start, nuint offset)
 12841270            => 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)
 01286            => (nuint)(uint)((length - (int)offset) & ~(Vector256<byte>.Count - 1));
 1287
 1288        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1289        private static nuint GetByteVector512SpanLength(nuint offset, int length)
 2291290            => (nuint)(uint)((length - (int)offset) & ~(Vector512<byte>.Count - 1));
 1291
 1292        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1293        private static unsafe nuint UnalignedCountVector128(byte* searchSpace)
 1294        {
 10151295            nint unaligned = (nint)searchSpace & (Vector128<byte>.Count - 1);
 10151296            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}
 1469

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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#else
 24        private const nuint MemmoveNativeThreshold = 2048;
 25#endif
 26        private const nuint ZeroMemoryNativeThreshold = 1024;
 27
 28
 29#if HAS_CUSTOM_BLOCKS
 30        [StructLayout(LayoutKind.Sequential, Size = 16)]
 31        private struct Block16 {}
 32
 33        [StructLayout(LayoutKind.Sequential, Size = 64)]
 34        private struct Block64 {}
 35#endif // HAS_CUSTOM_BLOCKS
 36
 37        [Intrinsic] // Unrolled for small constant lengths
 38        internal static void Memmove(ref byte dest, ref byte src, nuint len)
 39        {
 40            // P/Invoke into the native version when the buffers are overlapping.
 8821141            if ((nuint)Unsafe.ByteOffset(ref src, ref dest) < len ||
 8821142                (nuint)Unsafe.ByteOffset(ref dest, ref src) < len)
 43            {
 44                goto BuffersOverlap;
 45            }
 46
 8821147            ref byte srcEnd = ref Unsafe.Add(ref src, len);
 8821148            ref byte destEnd = ref Unsafe.Add(ref dest, len);
 49
 8821150            if (len <= 16)
 51                goto MCPY02;
 5990252            if (len > 64)
 53                goto MCPY05;
 54
 55        MCPY00:
 56            // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
 2736457            Debug.Assert(len > 16 && len <= 64);
 58#if HAS_CUSTOM_BLOCKS
 2736459            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block16>(ref src));
 60#elif TARGET_64BIT
 61            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 62            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 8)));
 63#else
 64            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 65            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 66            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 8)));
 67            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 12)));
 68#endif
 2736469            if (len <= 32)
 70                goto MCPY01;
 71#if HAS_CUSTOM_BLOCKS
 1819772            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src, 16
 73#elif TARGET_64BIT
 74            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 16)))
 75            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 24)))
 76#else
 77            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 16)));
 78            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 20), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 20)));
 79            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 24)));
 80            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 28), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 28)));
 81#endif
 1819782            if (len <= 48)
 83                goto MCPY01;
 84#if HAS_CUSTOM_BLOCKS
 311685            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src, 32
 86#elif TARGET_64BIT
 87            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 32)))
 88            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 40)))
 89#else
 90            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 32)));
 91            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 36), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 36)));
 92            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 40)));
 93            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 44), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 44)));
 94#endif
 95
 96        MCPY01:
 97            // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
 2736498            Debug.Assert(len > 16 && len <= 64);
 99#if HAS_CUSTOM_BLOCKS
 27364100            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src
 101#elif TARGET_64BIT
 102            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd
 103            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 104#else
 105            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 106            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 107            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 108            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 109#endif
 27364110            return;
 111
 112        MCPY02:
 113            // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
 28309114            if ((len & 24) == 0)
 115                goto MCPY03;
 9382116            Debug.Assert(len >= 8 && len <= 16);
 117#if TARGET_64BIT
 9382118            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 9382119            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 120#else
 121            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 122            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 123            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 124            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 125#endif
 9382126            return;
 127
 128        MCPY03:
 129            // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
 18927130            if ((len & 4) == 0)
 131                goto MCPY04;
 3608132            Debug.Assert(len >= 4 && len < 8);
 3608133            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 3608134            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 3608135            return;
 136
 137        MCPY04:
 138            // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
 15319139            Debug.Assert(len < 4);
 15319140            if (len == 0)
 3480141                return;
 11839142            dest = src;
 11839143            if ((len & 2) == 0)
 658144                return;
 11181145            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -2), Unsafe.ReadUnaligned<short>(ref Unsafe.Add(ref srcEnd
 11181146            return;
 147
 148        MCPY05:
 149            // PInvoke to the native version when the copy length exceeds the threshold.
 49894150            if (len > MemmoveNativeThreshold)
 151            {
 152                goto PInvoke;
 153            }
 154
 155#if HAS_CUSTOM_BLOCKS
 48855156            if (len >= 256)
 157            {
 158                // Try to opportunistically align the destination below. The input isn't pinned, so the GC
 159                // is free to move the references. We're therefore assuming that reads may still be unaligned.
 160                //
 161                // dest is more important to align than src because an unaligned store is more expensive
 162                // than an unaligned load.
 12359163                nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64);
 12359164                Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block64>(ref src));
 12359165                src = ref Unsafe.Add(ref src, misalignedElements);
 12359166                dest = ref Unsafe.Add(ref dest, misalignedElements);
 12359167                len -= misalignedElements;
 168            }
 169#endif
 170
 171            // Copy 64-bytes at a time until the remainder is less than 64.
 172            // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 b
 48855173            Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
 48855174            nuint n = len >> 6;
 175
 176        MCPY06:
 177#if HAS_CUSTOM_BLOCKS
 181200178            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<Block64>(ref src));
 179#elif TARGET_64BIT
 180            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<long>(ref src));
 181            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 8)));
 182            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 16)))
 183            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 24)))
 184            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 32)))
 185            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 40)))
 186            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 48), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 48)))
 187            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 56), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref src, 56)))
 188#else
 189            Unsafe.WriteUnaligned(ref dest, Unsafe.ReadUnaligned<int>(ref src));
 190            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 4)));
 191            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 8)));
 192            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 12)));
 193            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 16)));
 194            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 20), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 20)));
 195            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 24), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 24)));
 196            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 28), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 28)));
 197            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 32), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 32)));
 198            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 36), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 36)));
 199            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 40), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 40)));
 200            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 44), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 44)));
 201            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 48), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 48)));
 202            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 52), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 52)));
 203            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 56), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 56)));
 204            Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, 60), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref src, 60)));
 205#endif
 181200206            dest = ref Unsafe.Add(ref dest, 64);
 181200207            src = ref Unsafe.Add(ref src, 64);
 181200208            n--;
 181200209            if (n != 0)
 210                goto MCPY06;
 211
 48855212            len %= 64;
 48855213            if (len > 16)
 214                goto MCPY00;
 215#if HAS_CUSTOM_BLOCKS
 31499216            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<Block16>(ref Unsafe.Add(ref src
 217#elif TARGET_64BIT
 218            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd
 219            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref srcEnd,
 220#else
 221            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -16), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 222            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -12), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd,
 223            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -8), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 224            Unsafe.WriteUnaligned(ref Unsafe.Add(ref destEnd, -4), Unsafe.ReadUnaligned<int>(ref Unsafe.Add(ref srcEnd, 
 225#endif
 31499226            return;
 227
 228        BuffersOverlap:
 0229            Debug.Assert(len > 0);
 230            // If the buffers overlap perfectly, there's no point to copying the data.
 0231            if (Unsafe.AreSame(ref dest, ref src))
 232            {
 233                // Both could be null with a non-zero length, perform an implicit null check.
 0234                _ = Unsafe.ReadUnaligned<byte>(ref dest);
 0235                return;
 236            }
 237
 238        PInvoke:
 239            // Implicit nullchecks
 1039240            Debug.Assert(len > 0);
 1039241            _ = Unsafe.ReadUnaligned<byte>(ref dest);
 1039242            _ = Unsafe.ReadUnaligned<byte>(ref src);
 1039243            MemmoveNative(ref dest, ref src, len);
 1039244        }
 245
 246        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
 247        // with P/Invoke prolog/epilog.
 248        [MethodImpl(MethodImplOptions.NoInlining)]
 249        private static unsafe void MemmoveNative(ref byte dest, ref byte src, nuint len)
 1039250        {
 1039251            fixed (byte* pDest = &dest)
 1039252            fixed (byte* pSrc = &src)
 253            {
 1039254                memmove(pDest, pSrc, len);
 255            }
 1039256        }
 257
 258#if MONO
 259        [MethodImpl(MethodImplOptions.InternalCall)]
 260        private static extern unsafe void memmove(void* dest, void* src, nuint len);
 261#else
 262#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
 263        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "memmove")]
 264        [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
 265        private static unsafe partial void* memmove(void* dest, void* src, nuint len);
 266#pragma warning restore CS3016
 267#endif
 268
 269        [Intrinsic] // Unrolled for small sizes
 270        public static void ClearWithoutReferences(ref byte dest, nuint len)
 271        {
 70168272            if (len == 0)
 0273                return;
 274
 70168275            ref byte destEnd = ref Unsafe.Add(ref dest, len);
 276
 70168277            if (len <= 16)
 278                goto MZER02;
 63494279            if (len > 64)
 280                goto MZER05;
 281
 282        MZER00:
 283            // Clear bytes which are multiples of 16 and leave the remainder for MZER01 to handle.
 25304284            Debug.Assert(len > 16 && len <= 64);
 285#if HAS_CUSTOM_BLOCKS
 25304286            Unsafe.WriteUnaligned<Block16>(ref dest, default);
 287#elif TARGET_64BIT
 288            Unsafe.WriteUnaligned<long>(ref dest, 0);
 289            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 8), 0);
 290#else
 291            Unsafe.WriteUnaligned<int>(ref dest, 0);
 292            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 293            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 8), 0);
 294            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 12), 0);
 295#endif
 25304296            if (len <= 32)
 297                goto MZER01;
 298#if HAS_CUSTOM_BLOCKS
 18217299            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref dest, 16), default);
 300#elif TARGET_64BIT
 301            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 16), 0);
 302            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 24), 0);
 303#else
 304            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 16), 0);
 305            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 20), 0);
 306            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 24), 0);
 307            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 28), 0);
 308#endif
 18217309            if (len <= 48)
 310                goto MZER01;
 311#if HAS_CUSTOM_BLOCKS
 6517312            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref dest, 32), default);
 313#elif TARGET_64BIT
 314            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 32), 0);
 315            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 40), 0);
 316#else
 317            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 32), 0);
 318            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 36), 0);
 319            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 40), 0);
 320            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 44), 0);
 321#endif
 322
 323        MZER01:
 324            // Unconditionally clear the last 16 bytes using destEnd and return.
 25304325            Debug.Assert(len > 16 && len <= 64);
 326#if HAS_CUSTOM_BLOCKS
 25304327            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref destEnd, -16), default);
 328#elif TARGET_64BIT
 329            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -16), 0);
 330            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 331#else
 332            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -16), 0);
 333            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -12), 0);
 334            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 335            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 336#endif
 25304337            return;
 338
 339        MZER02:
 340            // Clear the first 8 bytes and then unconditionally clear the last 8 bytes and return.
 6674341            if ((len & 24) == 0)
 342                goto MZER03;
 2313343            Debug.Assert(len >= 8 && len <= 16);
 344#if TARGET_64BIT
 2313345            Unsafe.WriteUnaligned<long>(ref dest, 0);
 2313346            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 347#else
 348            Unsafe.WriteUnaligned<int>(ref dest, 0);
 349            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 350            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 351            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 352#endif
 2313353            return;
 354
 355        MZER03:
 356            // Clear the first 4 bytes and then unconditionally clear the last 4 bytes and return.
 4361357            if ((len & 4) == 0)
 358                goto MZER04;
 1957359            Debug.Assert(len >= 4 && len < 8);
 1957360            Unsafe.WriteUnaligned<int>(ref dest, 0);
 1957361            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 1957362            return;
 363
 364        MZER04:
 365            // Clear the first byte. For pending bytes, do an unconditionally clear of the last 2 bytes and return.
 2404366            Debug.Assert(len < 4);
 2404367            if (len == 0)
 0368                return;
 2404369            dest = 0;
 2404370            if ((len & 2) == 0)
 1441371                return;
 963372            Unsafe.WriteUnaligned<short>(ref Unsafe.Add(ref destEnd, -2), 0);
 963373            return;
 374
 375        MZER05:
 376            // PInvoke to the native version when the clear length exceeds the threshold.
 53367377            if (len > ZeroMemoryNativeThreshold)
 378            {
 379                goto PInvoke;
 380            }
 381
 382#if HAS_CUSTOM_BLOCKS
 23644383            if (len >= 256)
 384            {
 385                // Try to opportunistically align the destination below. The input isn't pinned, so the GC
 386                // is free to move the references. We're therefore assuming that reads may still be unaligned.
 11384387                nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64);
 11384388                Unsafe.WriteUnaligned<Block64>(ref dest, default);
 11384389                dest = ref Unsafe.Add(ref dest, misalignedElements);
 11384390                len -= misalignedElements;
 391            }
 392#endif
 393            // Clear 64-bytes at a time until the remainder is less than 64.
 394            // If remainder is greater than 16 bytes, then jump to MZER00. Otherwise, unconditionally clear the last 16 
 23644395            Debug.Assert(len > 64 && len <= ZeroMemoryNativeThreshold);
 23644396            nuint n = len >> 6;
 397
 398        MZER06:
 399#if HAS_CUSTOM_BLOCKS
 109973400            Unsafe.WriteUnaligned<Block64>(ref dest, default);
 401#elif TARGET_64BIT
 402            Unsafe.WriteUnaligned<long>(ref dest, 0);
 403            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 8), 0);
 404            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 16), 0);
 405            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 24), 0);
 406            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 32), 0);
 407            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 40), 0);
 408            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 48), 0);
 409            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref dest, 56), 0);
 410#else
 411            Unsafe.WriteUnaligned<int>(ref dest, 0);
 412            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 4), 0);
 413            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 8), 0);
 414            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 12), 0);
 415            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 16), 0);
 416            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 20), 0);
 417            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 24), 0);
 418            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 28), 0);
 419            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 32), 0);
 420            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 36), 0);
 421            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 40), 0);
 422            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 44), 0);
 423            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 48), 0);
 424            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 52), 0);
 425            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 56), 0);
 426            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref dest, 60), 0);
 427#endif
 109973428            dest = ref Unsafe.Add(ref dest, 64);
 109973429            n--;
 109973430            if (n != 0)
 431                goto MZER06;
 432
 23644433            len %= 64;
 23644434            if (len > 16)
 435                goto MZER00;
 436#if HAS_CUSTOM_BLOCKS
 8467437            Unsafe.WriteUnaligned<Block16>(ref Unsafe.Add(ref destEnd, -16), default);
 438#elif TARGET_64BIT
 439            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -16), 0);
 440            Unsafe.WriteUnaligned<long>(ref Unsafe.Add(ref destEnd, -8), 0);
 441#else
 442            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -16), 0);
 443            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -12), 0);
 444            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -8), 0);
 445            Unsafe.WriteUnaligned<int>(ref Unsafe.Add(ref destEnd, -4), 0);
 446#endif
 8467447            return;
 448
 449        PInvoke:
 450            // Implicit nullchecks
 29723451            _ = Unsafe.ReadUnaligned<byte>(ref dest);
 29723452            ZeroMemoryNative(ref dest, len);
 29723453        }
 454
 455        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
 456        // with P/Invoke prolog/epilog.
 457        [MethodImpl(MethodImplOptions.NoInlining)]
 458        private static unsafe void ZeroMemoryNative(ref byte b, nuint byteLength)
 29723459        {
 29723460            fixed (byte* ptr = &b)
 461            {
 29723462                byte* adjustedPtr = ptr;
 463#if TARGET_X86 || TARGET_AMD64
 29723464                if (byteLength > 0x100)
 465                {
 466                    // memset ends up calling rep stosb if the hardware claims to support it efficiently. rep stosb is u
 467                    // on misaligned blocks. Workaround this issue by aligning the blocks passed to memset upfront.
 29723468                    Unsafe.WriteUnaligned<Block16>(ptr, default);
 29723469                    Unsafe.WriteUnaligned<Block16>(ptr + byteLength - 16, default);
 470
 29723471                    byte* alignedEnd = (byte*)((nuint)(ptr + byteLength - 1) & ~(nuint)(16 - 1));
 472
 29723473                    adjustedPtr = (byte*)(((nuint)ptr + 16) & ~(nuint)(16 - 1));
 29723474                    byteLength = (nuint)(alignedEnd - adjustedPtr);
 475                }
 476#endif
 29723477                memset(adjustedPtr, 0, byteLength);
 478            }
 29723479        }
 480
 481#if MONO
 482        [MethodImpl(MethodImplOptions.InternalCall)]
 483        private static extern unsafe void memset(void* dest, int value, nuint len);
 484#else
 485#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
 486        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "memset")]
 487        [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
 488        private static unsafe partial void* memset(void* dest, int value, nuint len);
 489#pragma warning restore CS3016
 490#endif
 491
 492        internal static void Fill(ref byte dest, byte value, nuint len)
 493        {
 0494            if (!Vector.IsHardwareAccelerated)
 495            {
 496                goto CannotVectorize;
 497            }
 498
 0499            if (len >= (nuint)Vector<byte>.Count)
 500            {
 501                // We have enough data for at least one vectorized write.
 0502                Vector<byte> vector = new(value);
 0503                nuint stopLoopAtOffset = len & (nuint)(nint)(2 * (int)-Vector<byte>.Count); // intentional sign extensio
 0504                nuint offset = 0;
 505
 506                // Loop, writing 2 vectors at a time.
 507                // Compare 'numElements' rather than 'stopLoopAtOffset' because we don't want a dependency
 508                // on the very recently calculated 'stopLoopAtOffset' value.
 0509                if (len >= (uint)(2 * Vector<byte>.Count))
 510                {
 511                    do
 512                    {
 0513                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset), vector);
 0514                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset + (nuint)Vector<byte>.Count), ve
 0515                        offset += (uint)(2 * Vector<byte>.Count);
 0516                    } while (offset < stopLoopAtOffset);
 517                }
 518
 519                // At this point, if any data remains to be written, it's strictly less than
 520                // 2 * sizeof(Vector) bytes. The loop above had us write an even number of vectors.
 521                // If the total byte length instead involves us writing an odd number of vectors, write
 522                // one additional vector now. The bit check below tells us if we're in an "odd vector
 523                // count" situation.
 0524                if ((len & (nuint)Vector<byte>.Count) != 0)
 525                {
 0526                    Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, offset), vector);
 527                }
 528
 529                // It's possible that some small buffer remains to be populated - something that won't
 530                // fit an entire vector's worth of data. Instead of falling back to a loop, we'll write
 531                // a vector at the very end of the buffer. This may involve overwriting previously
 532                // populated data, which is fine since we're splatting the same value for all entries.
 533                // There's no need to perform a length check here because we already performed this
 534                // check before entering the vectorized code path.
 0535                Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest, len - (nuint)Vector<byte>.Count), vector);
 536
 537                // And we're done!
 0538                return;
 539            }
 540
 541        CannotVectorize:
 542
 543            // If we reached this point, we cannot vectorize this T, or there are too few
 544            // elements for us to vectorize. Fall back to an unrolled loop.
 0545            nuint i = 0;
 546
 547            // Write 8 elements at a time
 0548            if (len >= 8)
 549            {
 0550                nuint stopLoopAtOffset = len & ~(nuint)7;
 551                do
 552                {
 0553                    Unsafe.Add(ref dest, (nint)i + 0) = value;
 0554                    Unsafe.Add(ref dest, (nint)i + 1) = value;
 0555                    Unsafe.Add(ref dest, (nint)i + 2) = value;
 0556                    Unsafe.Add(ref dest, (nint)i + 3) = value;
 0557                    Unsafe.Add(ref dest, (nint)i + 4) = value;
 0558                    Unsafe.Add(ref dest, (nint)i + 5) = value;
 0559                    Unsafe.Add(ref dest, (nint)i + 6) = value;
 0560                    Unsafe.Add(ref dest, (nint)i + 7) = value;
 0561                } while ((i += 8) < stopLoopAtOffset);
 562            }
 563
 564            // Write next 4 elements if needed
 0565            if ((len & 4) != 0)
 566            {
 0567                Unsafe.Add(ref dest, (nint)i + 0) = value;
 0568                Unsafe.Add(ref dest, (nint)i + 1) = value;
 0569                Unsafe.Add(ref dest, (nint)i + 2) = value;
 0570                Unsafe.Add(ref dest, (nint)i + 3) = value;
 0571                i += 4;
 572            }
 573
 574            // Write next 2 elements if needed
 0575            if ((len & 2) != 0)
 576            {
 0577                Unsafe.Add(ref dest, (nint)i + 0) = value;
 0578                Unsafe.Add(ref dest, (nint)i + 1) = value;
 0579                i += 2;
 580            }
 581
 582            // Write final element if needed
 0583            if ((len & 1) != 0)
 584            {
 0585                Unsafe.Add(ref dest, (nint)i) = value;
 586            }
 0587        }
 588    }
 589}
 590

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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
 276                ushort ch2Val = Unsafe.Add(ref value, valueTailLength);
 277                nint ch1ch2Distance = (nint)(uint)valueTailLength;
 278                while (ch2Val == valueHead && ch1ch2Distance > 1)
 079                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 80
 281                Vector512<ushort> ch1 = Vector512.Create((ushort)valueHead);
 282                Vector512<ushort> ch2 = Vector512.Create(ch2Val);
 83
 284                nint searchSpaceMinusValueTailLengthAndVector =
 285                    searchSpaceMinusValueTailLength - (nint)Vector512<ushort>.Count;
 86
 87                do
 88                {
 89                    // Make sure we don't go out of bounds
 890                    Debug.Assert(offset + ch1ch2Distance + Vector512<ushort>.Count <= searchSpaceLength);
 91
 892                    Vector512<ushort> cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offse
 893                    Vector512<ushort> cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset
 894                    Vector512<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 95
 96                    // Early out: cmpAnd is all zeros
 897                    if (cmpAnd != Vector512<byte>.Zero)
 98                    {
 99                        goto CANDIDATE_FOUND;
 100                    }
 101
 102                LOOP_FOOTER:
 8103                    offset += Vector512<ushort>.Count;
 104
 8105                    if (offset == searchSpaceMinusValueTailLength)
 2106                        return -1;
 107
 108                    // Overlap with the current chunk for trailing elements
 6109                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 2110                        offset = searchSpaceMinusValueTailLengthAndVector;
 111
 2112                    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            }
 0139            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
 0143                ushort ch2Val = Unsafe.Add(ref value, valueTailLength);
 0144                nint ch1ch2Distance = valueTailLength;
 0145                while (ch2Val == valueHead && ch1ch2Distance > 1)
 0146                    ch2Val = Unsafe.Add(ref value, --ch1ch2Distance);
 147
 0148                Vector256<ushort> ch1 = Vector256.Create((ushort)valueHead);
 0149                Vector256<ushort> ch2 = Vector256.Create(ch2Val);
 150
 0151                nint searchSpaceMinusValueTailLengthAndVector =
 0152                    searchSpaceMinusValueTailLength - (nint)Vector256<ushort>.Count;
 153
 154                do
 155                {
 156                    // Make sure we don't go out of bounds
 0157                    Debug.Assert(offset + ch1ch2Distance + Vector256<ushort>.Count <= searchSpaceLength);
 158
 0159                    Vector256<ushort> cmpCh2 = Vector256.Equals(ch2, Vector256.LoadUnsafe(ref searchSpace, (nuint)(offse
 0160                    Vector256<ushort> cmpCh1 = Vector256.Equals(ch1, Vector256.LoadUnsafe(ref searchSpace, (nuint)offset
 0161                    Vector256<byte> cmpAnd = (cmpCh1 & cmpCh2).AsByte();
 162
 163                    // Early out: cmpAnd is all zeros
 0164                    if (cmpAnd != Vector256<byte>.Zero)
 165                    {
 166                        goto CANDIDATE_FOUND;
 167                    }
 168
 169                LOOP_FOOTER:
 0170                    offset += Vector256<ushort>.Count;
 171
 0172                    if (offset == searchSpaceMinusValueTailLength)
 0173                        return -1;
 174
 175                    // Overlap with the current chunk for trailing elements
 0176                    if (offset > searchSpaceMinusValueTailLengthAndVector)
 0177                        offset = searchSpaceMinusValueTailLengthAndVector;
 178
 0179                    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
 31537            nint offset = 0;
 31538            nint lengthToExamine = length;
 539
 31540            if (((int)searchSpace & 1) != 0)
 541            {
 542                // Input isn't char aligned, we won't be able to align it to a Vector
 543            }
 31544            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.
 31548                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.
 37556            while (lengthToExamine >= 4)
 557            {
 6558                if (value == searchSpace[offset])
 559                    goto Found;
 6560                if (value == searchSpace[offset + 1])
 561                    goto Found1;
 6562                if (value == searchSpace[offset + 2])
 563                    goto Found2;
 6564                if (value == searchSpace[offset + 3])
 565                    goto Found3;
 566
 6567                offset += 4;
 6568                lengthToExamine -= 4;
 569            }
 570
 31571            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
 31582            if (Vector512.IsHardwareAccelerated)
 583            {
 31584                if (offset < length)
 585                {
 31586                    Debug.Assert(length - offset >= Vector128<ushort>.Count);
 31587                    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.
 16599                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 600
 601                        // Same method as below
 16602                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 16603                        if (matches == 0)
 604                        {
 605                            // Zero flags set so no matches
 12606                            offset += Vector128<ushort>.Count;
 607                        }
 608                        else
 609                        {
 610                            // Find bitflag offset of first match and add to current offset
 4611                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 612                        }
 613                    }
 27614                    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.
 14626                        Vector256<ushort> search = *(Vector256<ushort>*)(searchSpace + (nuint)offset);
 627
 628                        // Same method as below
 14629                        uint matches = Vector256.Equals(Vector256<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 14630                        if (matches == 0)
 631                        {
 632                            // Zero flags set so no matches
 7633                            offset += Vector256<ushort>.Count;
 634                        }
 635                        else
 636                        {
 637                            // Find bitflag offset of first match and add to current offset
 7638                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 639                        }
 640                    }
 641
 20642                    lengthToExamine = GetCharVector512SpanLength(offset, length);
 20643                    if (lengthToExamine > 0)
 644                    {
 645                        do
 646                        {
 28647                            Debug.Assert(lengthToExamine >= Vector512<ushort>.Count);
 648
 28649                            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
 28655                            if (!Vector512.EqualsAny(search, Vector512<ushort>.Zero))
 656                            {
 657                                // Zero flags set so no matches
 8658                                offset += Vector512<ushort>.Count;
 8659                                lengthToExamine -= Vector512<ushort>.Count;
 8660                                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
 20668                            ulong matches = Vector512.Equals(search, Vector512<ushort>.Zero).ExtractMostSignificantBits(
 20669                            return (int)(offset + (uint)BitOperations.TrailingZeroCount(matches));
 8670                        } 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            }
 0726            else if (Vector256.IsHardwareAccelerated)
 727            {
 0728                if (offset < length)
 729                {
 0730                    Debug.Assert(length - offset >= Vector128<ushort>.Count);
 0731                    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.
 0743                        Vector128<ushort> search = *(Vector128<ushort>*)(searchSpace + (nuint)offset);
 744
 745                        // Same method as below
 0746                        uint matches = Vector128.Equals(Vector128<ushort>.Zero, search).AsByte().ExtractMostSignificantB
 0747                        if (matches == 0)
 748                        {
 749                            // Zero flags set so no matches
 0750                            offset += Vector128<ushort>.Count;
 751                        }
 752                        else
 753                        {
 754                            // Find bitflag offset of first match and add to current offset
 0755                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 756                        }
 757                    }
 758
 0759                    lengthToExamine = GetCharVector256SpanLength(offset, length);
 0760                    if (lengthToExamine > 0)
 761                    {
 762                        do
 763                        {
 0764                            Debug.Assert(lengthToExamine >= Vector256<ushort>.Count);
 765
 0766                            Vector256<ushort> search = *(Vector256<ushort>*)(searchSpace + (nuint)offset);
 0767                            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.
 0770                            if (matches == 0)
 771                            {
 772                                // Zero flags set so no matches
 0773                                offset += Vector256<ushort>.Count;
 0774                                lengthToExamine -= Vector256<ushort>.Count;
 0775                                continue;
 776                            }
 777
 778                            // Find bitflag offset of first match and add to current offset,
 779                            // flags are in bytes so divide for chars
 0780                            return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
 0781                        } 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)
 0875            => (length - offset) & ~(Vector256<ushort>.Count - 1);
 876
 877        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 878        private static nint GetCharVector512SpanLength(nint offset, nint length)
 20879            => (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);
 31885            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}
 1014

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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}
 348

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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
 183123            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 24            {
 25                goto CannotVectorize;
 26            }
 27
 183128            if (!Vector.IsHardwareAccelerated)
 29            {
 30                goto CannotVectorize;
 31            }
 32
 183133            if (sizeof(T) > Vector<byte>.Count)
 34            {
 35                goto CannotVectorize;
 36            }
 37
 183138            if (!BitOperations.IsPow2(sizeof(T)))
 39            {
 40                goto CannotVectorize;
 41            }
 42
 183143            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
 104848                if (sizeof(T) == 1)
 49                {
 050                    vector = new Vector<byte>(Unsafe.BitCast<T, byte>(value));
 51                }
 104852                else if (sizeof(T) == 2)
 53                {
 104854                    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
 1048108                ref byte refDataAsBytes = ref Unsafe.As<T, byte>(ref refData);
 1048109                nuint totalByteLength = numElements * (nuint)sizeof(T); // get this calculation ready ahead of time
 1048110                nuint stopLoopAtOffset = totalByteLength & (nuint)(nint)(2 * (int)-Vector<byte>.Count); // intentional s
 1048111                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
 1048117                if (numElements >= (uint)(2 * Vector<byte>.Count / sizeof(T)))
 118                {
 119                    do
 120                    {
 14969121                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset), vector);
 14969122                        Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, offset + (nuint)Vector<byte>.
 14969123                        offset += (uint)(2 * Vector<byte>.Count);
 14969124                    } 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
 1048133                if ((totalByteLength & (nuint)Vector<byte>.Count) != 0)
 134                {
 712135                    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
 1048145                Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref refDataAsBytes, totalByteLength - (nuint)Vector<byte>
 146
 147                // And we're done!
 148
 1048149                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
 783157            nuint i = 0;
 158
 159            // Write 8 elements at a time
 160
 783161            if (numElements >= 8)
 162            {
 340163                nuint stopLoopAtOffset = numElements & ~(nuint)7;
 164                do
 165                {
 340166                    Unsafe.Add(ref refData, (nint)i + 0) = value;
 340167                    Unsafe.Add(ref refData, (nint)i + 1) = value;
 340168                    Unsafe.Add(ref refData, (nint)i + 2) = value;
 340169                    Unsafe.Add(ref refData, (nint)i + 3) = value;
 340170                    Unsafe.Add(ref refData, (nint)i + 4) = value;
 340171                    Unsafe.Add(ref refData, (nint)i + 5) = value;
 340172                    Unsafe.Add(ref refData, (nint)i + 6) = value;
 340173                    Unsafe.Add(ref refData, (nint)i + 7) = value;
 340174                } while ((i += 8) < stopLoopAtOffset);
 175            }
 176
 177            // Write next 4 elements if needed
 178
 783179            if ((numElements & 4) != 0)
 180            {
 298181                Unsafe.Add(ref refData, (nint)i + 0) = value;
 298182                Unsafe.Add(ref refData, (nint)i + 1) = value;
 298183                Unsafe.Add(ref refData, (nint)i + 2) = value;
 298184                Unsafe.Add(ref refData, (nint)i + 3) = value;
 298185                i += 4;
 186            }
 187
 188            // Write next 2 elements if needed
 189
 783190            if ((numElements & 2) != 0)
 191            {
 783192                Unsafe.Add(ref refData, (nint)i + 0) = value;
 783193                Unsafe.Add(ref refData, (nint)i + 1) = value;
 783194                i += 2;
 195            }
 196
 197            // Write final element if needed
 198
 783199            if ((numElements & 1) != 0)
 200            {
 783201                Unsafe.Add(ref refData, (nint)i) = value;
 202            }
 783203        }
 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        {
 213091464            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePack
 1465            {
 01466                return PackedSpanHelpers.Contains(ref Unsafe.As<T, short>(ref searchSpace), Unsafe.BitCast<T, short>(val
 1467            }
 1468
 213091469            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        {
 213091474            Debug.Assert(length >= 0, "Expected non-negative length");
 213091475            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 1476
 213091477            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 1478            {
 24511479                nuint offset = 0;
 1480
 24871481                while (length >= 8)
 1482                {
 2591483                    length -= 8;
 1484
 2591485                    if (Unsafe.Add(ref searchSpace, offset) == value
 2591486                     || Unsafe.Add(ref searchSpace, offset + 1) == value
 2591487                     || Unsafe.Add(ref searchSpace, offset + 2) == value
 2591488                     || Unsafe.Add(ref searchSpace, offset + 3) == value
 2591489                     || Unsafe.Add(ref searchSpace, offset + 4) == value
 2591490                     || Unsafe.Add(ref searchSpace, offset + 5) == value
 2591491                     || Unsafe.Add(ref searchSpace, offset + 6) == value
 2591492                     || Unsafe.Add(ref searchSpace, offset + 7) == value)
 1493                    {
 2231494                        return true;
 1495                    }
 1496
 361497                    offset += 8;
 1498                }
 1499
 22281500                if (length >= 4)
 1501                {
 1261502                    length -= 4;
 1503
 1261504                    if (Unsafe.Add(ref searchSpace, offset) == value
 1261505                     || Unsafe.Add(ref searchSpace, offset + 1) == value
 1261506                     || Unsafe.Add(ref searchSpace, offset + 2) == value
 1261507                     || Unsafe.Add(ref searchSpace, offset + 3) == value)
 1508                    {
 991509                        return true;
 1510                    }
 1511
 271512                    offset += 4;
 1513                }
 1514
 48251515                while (length > 0)
 1516                {
 27921517                    length -= 1;
 1518
 27921519                    if (Unsafe.Add(ref searchSpace, offset) == value)
 1520                    {
 961521                        return true;
 1522                    }
 1523
 26961524                    offset += 1;
 1525                }
 1526            }
 188581527            else if (Vector512.IsHardwareAccelerated && length >= Vector512<T>.Count)
 1528            {
 187651529                Vector512<T> current, values = Vector512.Create(value);
 187651530                ref T currentSearchSpace = ref searchSpace;
 187651531                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                {
 366871536                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 1537
 366871538                    if (Vector512.EqualsAny(values, current))
 1539                    {
 8401540                        return true;
 1541                    }
 1542
 358471543                    currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<T>.Count);
 1544                }
 358471545                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1546
 1547                // If any elements remain, process the last vector in the search space.
 179251548                if ((uint)length % Vector512<T>.Count != 0)
 1549                {
 179251550                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 1551
 179251552                    if (Vector512.EqualsAny(values, current))
 1553                    {
 01554                        return true;
 1555                    }
 1556                }
 1557            }
 931558            else if (Vector256.IsHardwareAccelerated && length >= Vector256<T>.Count)
 1559            {
 321560                Vector256<T> equals, values = Vector256.Create(value);
 321561                ref T currentSearchSpace = ref searchSpace;
 321562                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                {
 321567                    equals = Vector256.Equals(values, Vector256.LoadUnsafe(ref currentSearchSpace));
 321568                    if (equals == Vector256<T>.Zero)
 1569                    {
 01570                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<T>.Count);
 01571                        continue;
 1572                    }
 1573
 321574                    return true;
 1575                }
 01576                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1577
 1578                // If any elements remain, process the last vector in the search space.
 01579                if ((uint)length % Vector256<T>.Count != 0)
 1580                {
 01581                    equals = Vector256.Equals(values, Vector256.LoadUnsafe(ref oneVectorAwayFromEnd));
 01582                    if (equals != Vector256<T>.Zero)
 1583                    {
 01584                        return true;
 1585                    }
 1586                }
 1587            }
 1588            else
 1589            {
 611590                Vector128<T> equals, values = Vector128.Create(value);
 611591                ref T currentSearchSpace = ref searchSpace;
 611592                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                {
 611597                    equals = Vector128.Equals(values, Vector128.LoadUnsafe(ref currentSearchSpace));
 611598                    if (equals == Vector128<T>.Zero)
 1599                    {
 01600                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<T>.Count);
 01601                        continue;
 1602                    }
 1603
 611604                    return true;
 1605                }
 01606                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1607
 1608                // If any elements remain, process the first vector in the search space.
 01609                if ((uint)length % Vector128<T>.Count != 0)
 1610                {
 01611                    equals = Vector128.Equals(values, Vector128.LoadUnsafe(ref oneVectorAwayFromEnd));
 01612                    if (equals != Vector128<T>.Zero)
 1613                    {
 01614                        return true;
 1615                    }
 1616                }
 1617            }
 1618
 199581619            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>
 196111636            => 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        {
 196111647            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUs
 1648            {
 103081649                return typeof(TNegator) == typeof(DontNegate<short>)
 103081650                    ? PackedSpanHelpers.IndexOf(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, cha
 103081651                    : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TV
 1652            }
 1653
 93031654            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        {
 178551661            Debug.Assert(length >= 0, "Expected non-negative length");
 178551662            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 1663
 178551664            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 1665            {
 77301666                nuint offset = 0;
 1667
 81401668                while (length >= 8)
 1669                {
 21581670                    length -= 8;
 1671
 21581672                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1673                    {
 3101674                        return (int)(offset);
 1675                    }
 18481676                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 1) == value))
 1677                    {
 1821678                        return (int)(offset + 1);
 1679                    }
 16661680                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 2) == value))
 1681                    {
 1821682                        return (int)(offset + 2);
 1683                    }
 14841684                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 3) == value))
 1685                    {
 2461686                        return (int)(offset + 3);
 1687                    }
 12381688                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 4) == value))
 1689                    {
 3161690                        return (int)(offset + 4);
 1691                    }
 9221692                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 5) == value))
 1693                    {
 3491694                        return (int)(offset + 5);
 1695                    }
 5731696                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 6) == value))
 1697                    {
 1011698                        return (int)(offset + 6);
 1699                    }
 4721700                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 7) == value))
 1701                    {
 621702                        return (int)(offset + 7);
 1703                    }
 1704
 4101705                    offset += 8;
 1706                }
 1707
 59821708                if (length >= 4)
 1709                {
 18501710                    length -= 4;
 1711
 18501712                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1713                    {
 6151714                        return (int)(offset);
 1715                    }
 12351716                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 1) == value))
 1717                    {
 1431718                        return (int)(offset + 1);
 1719                    }
 10921720                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 2) == value))
 1721                    {
 1401722                        return (int)(offset + 2);
 1723                    }
 9521724                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset + 3) == value))
 1725                    {
 1271726                        return (int)(offset + 3);
 1727                    }
 1728
 8251729                    offset += 4;
 1730                }
 1731
 80271732                while (length > 0)
 1733                {
 53831734                    length -= 1;
 1735
 53831736                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 1737                    {
 23131738                        return (int)(offset);
 1739                    }
 1740
 30701741                    offset += 1;
 1742                }
 1743
 26441744                return -1;
 1745            }
 101251746            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 1747            {
 63041748                Vector512<TValue> current, values = Vector512.Create(value);
 63041749                ref TValue currentSearchSpace = ref searchSpace;
 63041750                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                {
 209891755                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 1756
 209891757                    if (TNegator.HasMatch(values, current))
 1758                    {
 51621759                        return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, TNegator.GetMatchMask(values, 
 1760                    }
 1761
 158271762                    currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 1763                }
 158271764                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1765
 1766                // If any elements remain, process the last vector in the search space.
 11421767                if ((uint)length % Vector512<TValue>.Count != 0)
 1768                {
 9781769                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 1770
 9781771                    if (TNegator.HasMatch(values, current))
 1772                    {
 4951773                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, TNegator.GetMatchMask(values
 1774                    }
 1775                }
 1776            }
 38211777            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 1778            {
 18241779                Vector256<TValue> equals, values = Vector256.Create(value);
 18241780                ref TValue currentSearchSpace = ref searchSpace;
 18241781                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                {
 18241786                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values, Vector256.LoadUnsafe(ref currentSearchSpac
 18241787                    if (equals == Vector256<TValue>.Zero)
 1788                    {
 6391789                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 6391790                        continue;
 1791                    }
 1792
 11851793                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 1794                }
 6391795                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1796
 1797                // If any elements remain, process the last vector in the search space.
 6391798                if ((uint)length % Vector256<TValue>.Count != 0)
 1799                {
 5651800                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values, Vector256.LoadUnsafe(ref oneVectorAwayFrom
 5651801                    if (equals != Vector256<TValue>.Zero)
 1802                    {
 1401803                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 1804                    }
 1805                }
 1806            }
 1807            else
 1808            {
 19971809                Vector128<TValue> equals, values = Vector128.Create(value);
 19971810                ref TValue currentSearchSpace = ref searchSpace;
 19971811                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                {
 19971816                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values, Vector128.LoadUnsafe(ref currentSearchSpac
 19971817                    if (equals == Vector128<TValue>.Zero)
 1818                    {
 4711819                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 4711820                        continue;
 1821                    }
 1822
 15261823                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 1824                }
 4711825                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 1826
 1827                // If any elements remain, process the first vector in the search space.
 4711828                if ((uint)length % Vector128<TValue>.Count != 0)
 1829                {
 3661830                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values, Vector128.LoadUnsafe(ref oneVectorAwayFrom
 3661831                    if (equals != Vector128<TValue>.Zero)
 1832                    {
 2721833                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 1834                    }
 1835                }
 1836            }
 1837
 13451838            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, 
 01851            => 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        {
 01862            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUs
 1863            {
 01864                char char0 = Unsafe.BitCast<TValue, char>(value0);
 01865                char char1 = Unsafe.BitCast<TValue, char>(value1);
 1866
 01867                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
 01882                return typeof(TNegator) == typeof(DontNegate<short>)
 01883                    ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), char0, char1, length)
 01884                    : 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        {
 141361895            Debug.Assert(length >= 0, "Expected non-negative length");
 141361896            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 1897
 141361898            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 1899            {
 87541900                nuint offset = 0;
 1901                TValue lookUp;
 1902
 87541903                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 1904                {
 19411905                    while (length >= 8)
 1906                    {
 6841907                        length -= 8;
 1908
 6841909                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 6841910                        lookUp = current;
 6841911                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1912                        {
 3421913                            return (int)(offset);
 1914                        }
 1915
 3421916                        lookUp = Unsafe.Add(ref current, 1);
 3421917                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1918                        {
 901919                            return (int)(offset + 1);
 1920                        }
 1921
 2521922                        lookUp = Unsafe.Add(ref current, 2);
 2521923                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1924                        {
 631925                            return (int)(offset + 2);
 1926                        }
 1927
 1891928                        lookUp = Unsafe.Add(ref current, 3);
 1891929                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1930                        {
 421931                            return (int)(offset + 3);
 1932                        }
 1933
 1471934                        lookUp = Unsafe.Add(ref current, 4);
 1471935                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1936                        {
 241937                            return (int)(offset + 4);
 1938                        }
 1939
 1231940                        lookUp = Unsafe.Add(ref current, 5);
 1231941                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1942                        {
 211943                            return (int)(offset + 5);
 1944                        }
 1945
 1021946                        lookUp = Unsafe.Add(ref current, 6);
 1021947                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1948                        {
 271949                            return (int)(offset + 6);
 1950                        }
 1951
 751952                        lookUp = Unsafe.Add(ref current, 7);
 751953                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1954                        {
 241955                            return (int)(offset + 7);
 1956                        }
 1957
 511958                        offset += 8;
 1959                    }
 1960                }
 1961
 86101962                while (length >= 4)
 1963                {
 14101964                    length -= 4;
 1965
 14101966                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 14101967                    lookUp = current;
 14101968                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1969                    {
 6961970                        return (int)(offset);
 1971                    }
 1972
 7141973                    lookUp = Unsafe.Add(ref current, 1);
 7141974                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1975                    {
 1351976                        return (int)(offset + 1);
 1977                    }
 1978
 5791979                    lookUp = Unsafe.Add(ref current, 2);
 5791980                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1981                    {
 481982                        return (int)(offset + 2);
 1983                    }
 1984
 5311985                    lookUp = Unsafe.Add(ref current, 3);
 5311986                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 1987                    {
 421988                        return (int)(offset + 3);
 1989                    }
 1990
 4891991                    offset += 4;
 1992                }
 1993
 131911994                while (length > 0)
 1995                {
 91111996                    length -= 1;
 1997
 91111998                    lookUp = Unsafe.Add(ref searchSpace, offset);
 91111999                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2000                    {
 31202001                        return (int)(offset);
 2002                    }
 2003
 59912004                    offset += 1;
 2005                }
 2006
 40802007                return -1;
 2008            }
 53822009            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2010            {
 80042011                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 40022012                ref TValue currentSearchSpace = ref searchSpace;
 40022013                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                {
 169262018                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 169262019                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 169262020                    if (equals == Vector512<TValue>.Zero)
 2021                    {
 142892022                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 142892023                        continue;
 2024                    }
 2025
 26372026                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2027                }
 142892028                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2029
 2030                // If any elements remain, process the last vector in the search space.
 13652031                if ((uint)length % Vector512<TValue>.Count != 0)
 2032                {
 10112033                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 10112034                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 10112035                    if (equals != Vector512<TValue>.Zero)
 2036                    {
 902037                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2038                    }
 2039                }
 2040            }
 13802041            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2042            {
 13202043                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 6602044                ref TValue currentSearchSpace = ref searchSpace;
 6602045                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                {
 6602050                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 6602051                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 6602052                    if (equals == Vector256<TValue>.Zero)
 2053                    {
 2312054                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 2312055                        continue;
 2056                    }
 2057
 4292058                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2059                }
 2312060                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2061
 2062                // If any elements remain, process the last vector in the search space.
 2312063                if ((uint)length % Vector256<TValue>.Count != 0)
 2064                {
 1442065                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 1442066                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 1442067                    if (equals != Vector256<TValue>.Zero)
 2068                    {
 242069                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2070                    }
 2071                }
 2072            }
 2073            else
 2074            {
 14402075                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 7202076                ref TValue currentSearchSpace = ref searchSpace;
 7202077                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                {
 7202082                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 7202083                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 7202084                    if (equals == Vector128<TValue>.Zero)
 2085                    {
 2852086                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 2852087                        continue;
 2088                    }
 2089
 4352090                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2091                }
 2852092                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2093
 2094                // If any elements remain, process the first vector in the search space.
 2852095                if ((uint)length % Vector128<TValue>.Count != 0)
 2096                {
 1472097                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1472098                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 1472099                    if (equals != Vector128<TValue>.Zero)
 2100                    {
 392101                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2102                    }
 2103                }
 2104            }
 2105
 17282106            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        {
 69762136            Debug.Assert(length >= 0, "Expected non-negative length");
 69762137            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2138
 69762139            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2140            {
 42462141                nuint offset = 0;
 2142                TValue lookUp;
 2143
 42462144                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 2145                {
 27392146                    while (length >= 8)
 2147                    {
 9242148                        length -= 8;
 2149
 9242150                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 9242151                        lookUp = current;
 9242152                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2153                        {
 4622154                            return (int)(offset);
 2155                        }
 2156
 4622157                        lookUp = Unsafe.Add(ref current, 1);
 4622158                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2159                        {
 1232160                            return (int)(offset + 1);
 2161                        }
 2162
 3392163                        lookUp = Unsafe.Add(ref current, 2);
 3392164                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2165                        {
 812166                            return (int)(offset + 2);
 2167                        }
 2168
 2582169                        lookUp = Unsafe.Add(ref current, 3);
 2582170                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2171                        {
 392172                            return (int)(offset + 3);
 2173                        }
 2174
 2192175                        lookUp = Unsafe.Add(ref current, 4);
 2192176                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2177                        {
 422178                            return (int)(offset + 4);
 2179                        }
 2180
 1772181                        lookUp = Unsafe.Add(ref current, 5);
 1772182                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2183                        {
 302184                            return (int)(offset + 5);
 2185                        }
 2186
 1472187                        lookUp = Unsafe.Add(ref current, 6);
 1472188                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2189                        {
 302190                            return (int)(offset + 6);
 2191                        }
 2192
 1172193                        lookUp = Unsafe.Add(ref current, 7);
 1172194                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2195                        {
 422196                            return (int)(offset + 7);
 2197                        }
 2198
 752199                        offset += 8;
 2200                    }
 2201                }
 2202
 35352203                while (length >= 4)
 2204                {
 5462205                    length -= 4;
 2206
 5462207                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 5462208                    lookUp = current;
 5462209                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2210                    {
 2672211                        return (int)(offset);
 2212                    }
 2213
 2792214                    lookUp = Unsafe.Add(ref current, 1);
 2792215                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2216                    {
 902217                        return (int)(offset + 1);
 2218                    }
 2219
 1892220                    lookUp = Unsafe.Add(ref current, 2);
 1892221                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2222                    {
 272223                        return (int)(offset + 2);
 2224                    }
 2225
 1622226                    lookUp = Unsafe.Add(ref current, 3);
 1622227                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2228                    {
 242229                        return (int)(offset + 3);
 2230                    }
 2231
 1382232                    offset += 4;
 2233                }
 2234
 62982235                while (length > 0)
 2236                {
 45712237                    length -= 1;
 2238
 45712239                    lookUp = Unsafe.Add(ref searchSpace, offset);
 45712240                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 2241                    {
 12622242                        return (int)(offset);
 2243                    }
 2244
 33092245                    offset += 1;
 2246                }
 2247
 17272248                return -1;
 2249            }
 27302250            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2251            {
 57422252                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 19142253                ref TValue currentSearchSpace = ref searchSpace;
 19142254                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                {
 68432259                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 68432260                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 68432261                    if (equals == Vector512<TValue>.Zero)
 2262                    {
 55502263                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 55502264                        continue;
 2265                    }
 2266
 12932267                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2268                }
 55502269                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2270
 2271                // If any elements remain, process the last vector in the search space.
 6212272                if ((uint)length % Vector512<TValue>.Count != 0)
 2273                {
 4832274                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 4832275                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 4832276                    if (equals != Vector512<TValue>.Zero)
 2277                    {
 842278                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2279                    }
 2280                }
 2281            }
 8162282            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2283            {
 12062284                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 4022285                ref TValue currentSearchSpace = ref searchSpace;
 4022286                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                {
 4022291                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 4022292                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 4022293                    if (equals == Vector256<TValue>.Zero)
 2294                    {
 1412295                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 1412296                        continue;
 2297                    }
 2298
 2612299                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2300                }
 1412301                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2302
 2303                // If any elements remain, process the last vector in the search space.
 1412304                if ((uint)length % Vector256<TValue>.Count != 0)
 2305                {
 1082306                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 1082307                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 1082308                    if (equals != Vector256<TValue>.Zero)
 2309                    {
 92310                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2311                    }
 2312                }
 2313            }
 2314            else
 2315            {
 12422316                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 4142317                ref TValue currentSearchSpace = ref searchSpace;
 4142318                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                {
 4142323                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 4142324                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 4142325                    if (equals == Vector128<TValue>.Zero)
 2326                    {
 1472327                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1472328                        continue;
 2329                    }
 2330
 2672331                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2332                }
 1472333                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2334
 2335                // If any elements remain, process the first vector in the search space.
 1472336                if ((uint)length % Vector128<TValue>.Count != 0)
 2337                {
 662338                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 662339                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 662340                    if (equals != Vector128<TValue>.Zero)
 2341                    {
 182342                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2343                    }
 2344                }
 2345            }
 2346
 7982347            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
 33442352            => 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 
 33442356            => 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        {
 66882362            Debug.Assert(length >= 0, "Expected non-negative length");
 66882363            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2364
 66882365            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2366            {
 41742367                nuint offset = 0;
 2368                TValue lookUp;
 2369
 56072370                while (length >= 4)
 2371                {
 35802372                    length -= 4;
 2373
 35802374                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 35802375                    lookUp = current;
 35802376                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2377                    {
 16852378                        return (int)(offset);
 2379                    }
 2380
 18952381                    lookUp = Unsafe.Add(ref current, 1);
 18952382                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2383                    {
 1982384                        return (int)(offset + 1);
 2385                    }
 2386
 16972387                    lookUp = Unsafe.Add(ref current, 2);
 16972388                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2389                    {
 1442390                        return (int)(offset + 2);
 2391                    }
 2392
 15532393                    lookUp = Unsafe.Add(ref current, 3);
 15532394                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2395                    {
 1202396                        return (int)(offset + 3);
 2397                    }
 2398
 14332399                    offset += 4;
 2400                }
 2401
 25942402                while (length > 0)
 2403                {
 9572404                    length -= 1;
 2405
 9572406                    lookUp = Unsafe.Add(ref searchSpace, offset);
 9572407                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2408                    {
 3902409                        return (int)(offset);
 2410                    }
 2411
 5672412                    offset += 1;
 2413                }
 2414
 16372415                return -1;
 2416            }
 25142417            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2418            {
 69362419                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 17342420                ref TValue currentSearchSpace = ref searchSpace;
 17342421                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                {
 64652426                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 64652427                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 64652428                        | Vector512.Equals(values2, current) | Vector512.Equals(values3, current));
 64652429                    if (equals == Vector512<TValue>.Zero)
 2430                    {
 51812431                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 51812432                        continue;
 2433                    }
 2434
 12842435                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2436                }
 51812437                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2438
 2439                // If any elements remain, process the last vector in the search space.
 4502440                if ((uint)length % Vector512<TValue>.Count != 0)
 2441                {
 4082442                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 4082443                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 4082444                        | Vector512.Equals(values2, current) | Vector512.Equals(values3, current));
 4082445                    if (equals != Vector512<TValue>.Zero)
 2446                    {
 512447                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2448                    }
 2449                }
 2450            }
 7802451            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2452            {
 13202453                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 3302454                ref TValue currentSearchSpace = ref searchSpace;
 3302455                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                {
 3302460                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 3302461                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 3302462                        | Vector256.Equals(values2, current) | Vector256.Equals(values3, current));
 3302463                    if (equals == Vector256<TValue>.Zero)
 2464                    {
 722465                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 722466                        continue;
 2467                    }
 2468
 2582469                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2470                }
 722471                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2472
 2473                // If any elements remain, process the last vector in the search space.
 722474                if ((uint)length % Vector256<TValue>.Count != 0)
 2475                {
 692476                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 692477                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 692478                        | Vector256.Equals(values2, current) | Vector256.Equals(values3, current));
 692479                    if (equals != Vector256<TValue>.Zero)
 2480                    {
 242481                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2482                    }
 2483                }
 2484            }
 2485            else
 2486            {
 18002487                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 4502488                ref TValue currentSearchSpace = ref searchSpace;
 4502489                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                {
 4502494                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 4502495                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 4502496                        | Vector128.Equals(values2, current) | Vector128.Equals(values3, current));
 4502497                    if (equals == Vector128<TValue>.Zero)
 2498                    {
 1742499                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1742500                        continue;
 2501                    }
 2502
 2762503                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2504                }
 1742505                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2506
 2507                // If any elements remain, process the first vector in the search space.
 1742508                if ((uint)length % Vector128<TValue>.Count != 0)
 2509                {
 1352510                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1352511                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 1352512                        | Vector128.Equals(values2, current) | Vector128.Equals(values3, current));
 1352513                    if (equals != Vector128<TValue>.Zero)
 2514                    {
 242515                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2516                    }
 2517                }
 2518            }
 2519
 5972520            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, 
 45722525            => 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
 45722529            => 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        {
 91442535            Debug.Assert(length >= 0, "Expected non-negative length");
 91442536            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2537
 91442538            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2539            {
 54542540                nuint offset = 0;
 2541                TValue lookUp;
 2542
 76292543                while (length >= 4)
 2544                {
 52022545                    length -= 4;
 2546
 52022547                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 52022548                    lookUp = current;
 52022549                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2550                    {
 23372551                        return (int)(offset);
 2552                    }
 2553
 28652554                    lookUp = Unsafe.Add(ref current, 1);
 28652555                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2556                    {
 3212557                        return (int)(offset + 1);
 2558                    }
 2559
 25442560                    lookUp = Unsafe.Add(ref current, 2);
 25442561                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2562                    {
 2012563                        return (int)(offset + 2);
 2564                    }
 2565
 23432566                    lookUp = Unsafe.Add(ref current, 3);
 23432567                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2568                    {
 1682569                        return (int)(offset + 3);
 2570                    }
 2571
 21752572                    offset += 4;
 2573                }
 2574
 42452575                while (length > 0)
 2576                {
 22322577                    length -= 1;
 2578
 22322579                    lookUp = Unsafe.Add(ref searchSpace, offset);
 22322580                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 2581                    {
 4142582                        return (int)(offset);
 2583                    }
 2584
 18182585                    offset += 1;
 2586                }
 2587
 20132588                return -1;
 2589            }
 36902590            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2591            {
 52562592                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 78842593                    values2 = Vector512.Create(value2), values3 = Vector512.Create(value3), values4 = Vector512.Create(v
 26282594                ref TValue currentSearchSpace = ref searchSpace;
 26282595                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                {
 67142600                    current = Vector512.LoadUnsafe(ref currentSearchSpace);
 67142601                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 67142602                           | Vector512.Equals(values3, current) | Vector512.Equals(values4, current));
 67142603                    if (equals == Vector512<TValue>.Zero)
 2604                    {
 45932605                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector512<TValue>.Count);
 45932606                        continue;
 2607                    }
 2608
 21212609                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2610                }
 45932611                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2612
 2613                // If any elements remain, process the last vector in the search space.
 5072614                if ((uint)length % Vector512<TValue>.Count != 0)
 2615                {
 4412616                    current = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 4412617                    equals = TNegator.NegateIfNeeded(Vector512.Equals(values0, current) | Vector512.Equals(values1, curr
 4412618                           | Vector512.Equals(values3, current) | Vector512.Equals(values4, current));
 4412619                    if (equals != Vector512<TValue>.Zero)
 2620                    {
 1052621                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2622                    }
 2623                }
 2624            }
 10622625            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2626            {
 11282627                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 16922628                    values2 = Vector256.Create(value2), values3 = Vector256.Create(value3), values4 = Vector256.Create(v
 5642629                ref TValue currentSearchSpace = ref searchSpace;
 5642630                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                {
 5642635                    current = Vector256.LoadUnsafe(ref currentSearchSpace);
 5642636                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 5642637                           | Vector256.Equals(values3, current) | Vector256.Equals(values4, current));
 5642638                    if (equals == Vector256<TValue>.Zero)
 2639                    {
 1172640                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<TValue>.Count);
 1172641                        continue;
 2642                    }
 2643
 4472644                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2645                }
 1172646                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2647
 2648                // If any elements remain, process the last vector in the search space.
 1172649                if ((uint)length % Vector256<TValue>.Count != 0)
 2650                {
 1052651                    current = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 1052652                    equals = TNegator.NegateIfNeeded(Vector256.Equals(values0, current) | Vector256.Equals(values1, curr
 1052653                           | Vector256.Equals(values3, current) | Vector256.Equals(values4, current));
 1052654                    if (equals != Vector256<TValue>.Zero)
 2655                    {
 542656                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2657                    }
 2658                }
 2659            }
 2660            else
 2661            {
 9962662                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 14942663                    values2 = Vector128.Create(value2), values3 = Vector128.Create(value3), values4 = Vector128.Create(v
 4982664                ref TValue currentSearchSpace = ref searchSpace;
 4982665                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                {
 4982670                    current = Vector128.LoadUnsafe(ref currentSearchSpace);
 4982671                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 4982672                           | Vector128.Equals(values3, current) | Vector128.Equals(values4, current));
 4982673                    if (equals == Vector128<TValue>.Zero)
 2674                    {
 1442675                        currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<TValue>.Count);
 1442676                        continue;
 2677                    }
 2678
 3542679                    return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, equals);
 2680                }
 1442681                while (Unsafe.IsAddressLessThanOrEqualTo(ref currentSearchSpace, ref oneVectorAwayFromEnd));
 2682
 2683                // If any elements remain, process the first vector in the search space.
 1442684                if ((uint)length % Vector128<TValue>.Count != 0)
 2685                {
 1112686                    current = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1112687                    equals = TNegator.NegateIfNeeded(Vector128.Equals(values0, current) | Vector128.Equals(values1, curr
 1112688                           | Vector128.Equals(values3, current) | Vector128.Equals(values4, current));
 1112689                    if (equals != Vector128<TValue>.Zero)
 2690                    {
 422691                        return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, equals);
 2692                    }
 2693                }
 2694            }
 2695
 5672696            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>
 30462701            => 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
 28882705            => 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        {
 59342711            Debug.Assert(length >= 0, "Expected non-negative length");
 59342712            Debug.Assert(value is byte or short or int or long, "Expected caller to normalize to one of these types");
 2713
 59342714            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2715            {
 29782716                nuint offset = (nuint)length - 1;
 2717
 30042718                while (length >= 8)
 2719                {
 3882720                    length -= 8;
 2721
 3882722                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2723                    {
 1942724                        return (int)(offset);
 2725                    }
 1942726                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 1) == value))
 2727                    {
 502728                        return (int)(offset - 1);
 2729                    }
 1442730                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 2) == value))
 2731                    {
 222732                        return (int)(offset - 2);
 2733                    }
 1222734                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 3) == value))
 2735                    {
 182736                        return (int)(offset - 3);
 2737                    }
 1042738                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 4) == value))
 2739                    {
 262740                        return (int)(offset - 4);
 2741                    }
 782742                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 5) == value))
 2743                    {
 222744                        return (int)(offset - 5);
 2745                    }
 562746                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 6) == value))
 2747                    {
 122748                        return (int)(offset - 6);
 2749                    }
 442750                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 7) == value))
 2751                    {
 182752                        return (int)(offset - 7);
 2753                    }
 2754
 262755                    offset -= 8;
 2756                }
 2757
 26162758                if (length >= 4)
 2759                {
 11662760                    length -= 4;
 2761
 11662762                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2763                    {
 5742764                        return (int)(offset);
 2765                    }
 5922766                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 1) == value))
 2767                    {
 862768                        return (int)(offset - 1);
 2769                    }
 5062770                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 2) == value))
 2771                    {
 622772                        return (int)(offset - 2);
 2773                    }
 4442774                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset - 3) == value))
 2775                    {
 462776                        return (int)(offset - 3);
 2777                    }
 2778
 3982779                    offset -= 4;
 2780                }
 2781
 32182782                while (length > 0)
 2783                {
 19742784                    length -= 1;
 2785
 19742786                    if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value))
 2787                    {
 6042788                        return (int)(offset);
 2789                    }
 2790
 13702791                    offset -= 1;
 2792                }
 2793
 12442794                return -1;
 2795            }
 29562796            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2797            {
 19612798                return SimdImpl<Vector512<TValue>>(ref searchSpace, value, length);
 2799            }
 9952800            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 2801            {
 5032802                return SimdImpl<Vector256<TValue>>(ref searchSpace, value, length);
 2803            }
 2804            else
 2805            {
 4922806                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;
 29562813                TVector values = TVector.Create(value);
 2814
 29562815                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
 103092818                while (offset > 0)
 2819                {
 88572820                    current = TVector.LoadUnsafe(ref searchSpace, (uint)(offset));
 2821
 88572822                    if (TNegator.HasMatch(values, current))
 2823                    {
 15042824                        return offset + TVector.LastIndexOfWhereAllBitsSet(TNegator.GetMatchMask(values, current));
 2825                    }
 2826
 73532827                    offset -= TVector.ElementCount;
 2828                }
 2829
 2830                // Process the first vector in the search space.
 2831
 14522832                current = TVector.LoadUnsafe(ref searchSpace);
 2833
 14522834                if (TNegator.HasMatch(values, current))
 2835                {
 3612836                    return TVector.LastIndexOfWhereAllBitsSet(TNegator.GetMatchMask(values, current));
 2837                }
 2838
 10912839                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
 45092845            => 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 
 45082849            => 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        {
 90172855            Debug.Assert(length >= 0, "Expected non-negative length");
 90172856            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 2857
 90172858            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 2859            {
 41602860                nuint offset = (nuint)length - 1;
 2861                TValue lookUp;
 2862
 41602863                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 2864                {
 8382865                    while (length >= 8)
 2866                    {
 4562867                        length -= 8;
 2868
 4562869                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 4562870                        lookUp = current;
 4562871                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2872                        {
 2282873                            return (int)(offset);
 2874                        }
 2875
 2282876                        lookUp = Unsafe.Add(ref current, -1);
 2282877                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2878                        {
 662879                            return (int)(offset - 1);
 2880                        }
 2881
 1622882                        lookUp = Unsafe.Add(ref current, -2);
 1622883                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2884                        {
 342885                            return (int)(offset - 2);
 2886                        }
 2887
 1282888                        lookUp = Unsafe.Add(ref current, -3);
 1282889                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2890                        {
 142891                            return (int)(offset - 3);
 2892                        }
 2893
 1142894                        lookUp = Unsafe.Add(ref current, -4);
 1142895                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2896                        {
 182897                            return (int)(offset - 4);
 2898                        }
 2899
 962900                        lookUp = Unsafe.Add(ref current, -5);
 962901                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2902                        {
 122903                            return (int)(offset - 5);
 2904                        }
 2905
 842906                        lookUp = Unsafe.Add(ref current, -6);
 842907                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2908                        {
 202909                            return (int)(offset - 6);
 2910                        }
 2911
 642912                        lookUp = Unsafe.Add(ref current, -7);
 642913                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2914                        {
 222915                            return (int)(offset - 7);
 2916                        }
 2917
 422918                        offset -= 8;
 2919                    }
 2920                }
 2921
 41442922                while (length >= 4)
 2923                {
 14582924                    length -= 4;
 2925
 14582926                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 14582927                    lookUp = current;
 14582928                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2929                    {
 7242930                        return (int)(offset);
 2931                    }
 2932
 7342933                    lookUp = Unsafe.Add(ref current, -1);
 7342934                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2935                    {
 1342936                        return (int)(offset - 1);
 2937                    }
 2938
 6002939                    lookUp = Unsafe.Add(ref current, -2);
 6002940                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2941                    {
 1202942                        return (int)(offset - 2);
 2943                    }
 2944
 4802945                    lookUp = Unsafe.Add(ref current, -3);
 4802946                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2947                    {
 822948                        return (int)(offset - 3);
 2949                    }
 2950
 3982951                    offset -= 4;
 2952                }
 2953
 45282954                while (length > 0)
 2955                {
 28382956                    length -= 1;
 2957
 28382958                    lookUp = Unsafe.Add(ref searchSpace, offset);
 28382959                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1))
 2960                    {
 9962961                        return (int)(offset);
 2962                    }
 2963
 18422964                    offset -= 1;
 2965                }
 2966
 16902967                return -1;
 2968            }
 48572969            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 2970            {
 70182971                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 35092972                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.
 146162975                while (offset > 0)
 2976                {
 132882977                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 132882978                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 2979
 132882980                    if (equals == Vector512<TValue>.Zero)
 2981                    {
 111072982                        offset -= Vector512<TValue>.Count;
 111072983                        continue;
 2984                    }
 2985
 21812986                    return ComputeLastIndex(offset, equals);
 2987                }
 2988
 2989                // Process the first vector in the search space.
 2990
 13282991                current = Vector512.LoadUnsafe(ref searchSpace);
 13282992                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 2993
 13282994                if (equals != Vector512<TValue>.Zero)
 2995                {
 2322996                    return ComputeLastIndex(offset: 0, equals);
 2997                }
 2998            }
 13482999            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3000            {
 13043001                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 6523002                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.
 7863005                while (offset > 0)
 3006                {
 4283007                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 4283008                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 3009
 4283010                    if (equals == Vector256<TValue>.Zero)
 3011                    {
 1343012                        offset -= Vector256<TValue>.Count;
 1343013                        continue;
 3014                    }
 3015
 2943016                    return ComputeLastIndex(offset, equals);
 3017                }
 3018
 3019                // Process the first vector in the search space.
 3020
 3583021                current = Vector256.LoadUnsafe(ref searchSpace);
 3583022                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 3023
 3583024                if (equals != Vector256<TValue>.Zero)
 3025                {
 1723026                    return ComputeLastIndex(offset: 0, equals);
 3027                }
 3028            }
 3029            else
 3030            {
 13923031                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 6963032                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.
 8123035                while (offset > 0)
 3036                {
 3643037                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 3643038                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 3643039                    if (equals == Vector128<TValue>.Zero)
 3040                    {
 1163041                        offset -= Vector128<TValue>.Count;
 1163042                        continue;
 3043                    }
 3044
 2483045                    return ComputeLastIndex(offset, equals);
 3046                }
 3047
 3048                // Process the first vector in the search space.
 4483049                current = Vector128.LoadUnsafe(ref searchSpace);
 4483050                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 4483051                if (equals != Vector128<TValue>.Zero)
 3052                {
 2083053                    return ComputeLastIndex(offset: 0, equals);
 3054                }
 3055            }
 3056
 15223057            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
 26663062            => 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
 26663066            => 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        {
 53323072            Debug.Assert(length >= 0, "Expected non-negative length");
 53323073            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3074
 53323075            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3076            {
 22083077                nuint offset = (nuint)length - 1;
 3078                TValue lookUp;
 3079
 22083080                if (typeof(TValue) == typeof(byte)) // this optimization is beneficial only to byte
 3081                {
 11363082                    while (length >= 8)
 3083                    {
 6163084                        length -= 8;
 3085
 6163086                        ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 6163087                        lookUp = current;
 6163088                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3089                        {
 3083090                            return (int)(offset);
 3091                        }
 3092
 3083093                        lookUp = Unsafe.Add(ref current, -1);
 3083094                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3095                        {
 603096                            return (int)(offset - 1);
 3097                        }
 3098
 2483099                        lookUp = Unsafe.Add(ref current, -2);
 2483100                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3101                        {
 503102                            return (int)(offset - 2);
 3103                        }
 3104
 1983105                        lookUp = Unsafe.Add(ref current, -3);
 1983106                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3107                        {
 263108                            return (int)(offset - 3);
 3109                        }
 3110
 1723111                        lookUp = Unsafe.Add(ref current, -4);
 1723112                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3113                        {
 363114                            return (int)(offset - 4);
 3115                        }
 3116
 1363117                        lookUp = Unsafe.Add(ref current, -5);
 1363118                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3119                        {
 283120                            return (int)(offset - 5);
 3121                        }
 3122
 1083123                        lookUp = Unsafe.Add(ref current, -6);
 1083124                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3125                        {
 263126                            return (int)(offset - 6);
 3127                        }
 3128
 823129                        lookUp = Unsafe.Add(ref current, -7);
 823130                        if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3131                        {
 383132                            return (int)(offset - 7);
 3133                        }
 3134
 443135                        offset -= 8;
 3136                    }
 3137                }
 3138
 17823139                while (length >= 4)
 3140                {
 7543141                    length -= 4;
 3142
 7543143                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 7543144                    lookUp = current;
 7543145                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3146                    {
 3683147                        return (int)(offset);
 3148                    }
 3149
 3863150                    lookUp = Unsafe.Add(ref current, -1);
 3863151                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3152                    {
 1203153                        return (int)(offset - 1);
 3154                    }
 3155
 2663156                    lookUp = Unsafe.Add(ref current, -2);
 2663157                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3158                    {
 743159                        return (int)(offset - 2);
 3160                    }
 3161
 1923162                    lookUp = Unsafe.Add(ref current, -3);
 1923163                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3164                    {
 463165                        return (int)(offset - 3);
 3166                    }
 3167
 1463168                    offset -= 4;
 3169                }
 3170
 16063171                while (length > 0)
 3172                {
 9483173                    length -= 1;
 3174
 9483175                    lookUp = Unsafe.Add(ref searchSpace, offset);
 9483176                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2))
 3177                    {
 3703178                        return (int)(offset);
 3179                    }
 3180
 5783181                    offset -= 1;
 3182                }
 3183
 6583184                return -1;
 3185            }
 31243186            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3187            {
 65283188                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 21763189                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.
 76763192                while (offset > 0)
 3193                {
 70063194                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 70063195                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 3196
 70063197                    if (equals == Vector512<TValue>.Zero)
 3198                    {
 55003199                        offset -= Vector512<TValue>.Count;
 55003200                        continue;
 3201                    }
 3202
 15063203                    return ComputeLastIndex(offset, equals);
 3204                }
 3205
 3206                // Process the first vector in the search space.
 6703207                current = Vector512.LoadUnsafe(ref searchSpace);
 6703208                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 3209
 6703210                if (equals != Vector512<TValue>.Zero)
 3211                {
 1383212                    return ComputeLastIndex(offset: 0, equals);
 3213                }
 3214            }
 9483215            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3216            {
 14283217                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 4763218                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.
 5823221                while (offset > 0)
 3222                {
 3643223                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 3643224                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 3225
 3643226                    if (equals == Vector256<TValue>.Zero)
 3227                    {
 1063228                        offset -= Vector256<TValue>.Count;
 1063229                        continue;
 3230                    }
 3231
 2583232                    return ComputeLastIndex(offset, equals);
 3233                }
 3234
 3235                // Process the first vector in the search space.
 2183236                current = Vector256.LoadUnsafe(ref searchSpace);
 2183237                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 3238
 2183239                if (equals != Vector256<TValue>.Zero)
 3240                {
 983241                    return ComputeLastIndex(offset: 0, equals);
 3242                }
 3243            }
 3244            else
 3245            {
 14163246                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 4723247                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.
 5263250                while (offset > 0)
 3251                {
 2403252                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2403253                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 3254
 2403255                    if (equals == Vector128<TValue>.Zero)
 3256                    {
 543257                        offset -= Vector128<TValue>.Count;
 543258                        continue;
 3259                    }
 3260
 1863261                    return ComputeLastIndex(offset, equals);
 3262                }
 3263
 3264                // Process the first vector in the search space.
 2863265                current = Vector128.LoadUnsafe(ref searchSpace);
 2863266                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 3267
 2863268                if (equals != Vector128<TValue>.Zero)
 3269                {
 1643270                    return ComputeLastIndex(offset: 0, equals);
 3271                }
 3272            }
 3273
 7743274            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
 16723279            => 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, 
 16723283            => 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        {
 33443289            Debug.Assert(length >= 0, "Expected non-negative length");
 33443290            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3291
 33443292            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3293            {
 16683294                nuint offset = (nuint)length - 1;
 3295                TValue lookUp;
 3296
 20443297                while (length >= 4)
 3298                {
 12643299                    length -= 4;
 3300
 12643301                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 12643302                    lookUp = current;
 12643303                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3304                    {
 5643305                        return (int)(offset);
 3306                    }
 3307
 7003308                    lookUp = Unsafe.Add(ref current, -1);
 7003309                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3310                    {
 1823311                        return (int)(offset - 1);
 3312                    }
 3313
 5183314                    lookUp = Unsafe.Add(ref current, -2);
 5183315                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3316                    {
 923317                        return (int)(offset - 2);
 3318                    }
 3319
 4263320                    lookUp = Unsafe.Add(ref current, -3);
 4263321                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3322                    {
 503323                        return (int)(offset - 3);
 3324                    }
 3325
 3763326                    offset -= 4;
 3327                }
 3328
 11463329                while (length > 0)
 3330                {
 6123331                    length -= 1;
 3332
 6123333                    lookUp = Unsafe.Add(ref searchSpace, offset);
 6123334                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3335                    {
 2463336                        return (int)(offset);
 3337                    }
 3338
 3663339                    offset -= 1;
 3340                }
 3341
 5343342                return -1;
 3343            }
 16763344            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3345            {
 46243346                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 11563347                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.
 45323350                while (offset > 0)
 3351                {
 42083352                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 42083353                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 42083354                                            | Vector512.Equals(current, values2) | Vector512.Equals(current, values3));
 42083355                    if (equals == Vector512<TValue>.Zero)
 3356                    {
 33763357                        offset -= Vector512<TValue>.Count;
 33763358                        continue;
 3359                    }
 3360
 8323361                    return ComputeLastIndex(offset, equals);
 3362                }
 3363
 3364                // Process the first vector in the search space.
 3243365                current = Vector512.LoadUnsafe(ref searchSpace);
 3243366                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 3367
 3243368                if (equals != Vector512<TValue>.Zero)
 3369                {
 583370                    return ComputeLastIndex(offset: 0, equals);
 3371                }
 3372            }
 5203373            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3374            {
 8803375                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 2203376                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.
 2663379                while (offset > 0)
 3380                {
 1683381                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 1683382                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 1683383                                            | Vector256.Equals(current, values2) | Vector256.Equals(current, values3));
 1683384                    if (equals == Vector256<TValue>.Zero)
 3385                    {
 463386                        offset -= Vector256<TValue>.Count;
 463387                        continue;
 3388                    }
 3389
 1223390                    return ComputeLastIndex(offset, equals);
 3391                }
 3392
 3393                // Process the first vector in the search space.
 983394                current = Vector256.LoadUnsafe(ref searchSpace);
 983395                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 983396                if (equals != Vector256<TValue>.Zero)
 3397                {
 663398                    return ComputeLastIndex(offset: 0, equals);
 3399                }
 3400            }
 3401            else
 3402            {
 12003403                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 3003404                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.
 3803407                while (offset > 0)
 3408                {
 2323409                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2323410                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 2323411                    if (equals == Vector128<TValue>.Zero)
 3412                    {
 803413                        offset -= Vector128<TValue>.Count;
 803414                        continue;
 3415                    }
 3416
 1523417                    return ComputeLastIndex(offset, equals);
 3418                }
 3419
 3420                // Process the first vector in the search space.
 1483421                current = Vector128.LoadUnsafe(ref searchSpace);
 1483422                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 1483423                if (equals != Vector128<TValue>.Zero)
 3424                {
 483425                    return ComputeLastIndex(offset: 0, equals);
 3426                }
 3427            }
 3428
 3983429            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
 22863551            => 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, 
 22863555            => 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        {
 45723561            Debug.Assert(length >= 0, "Expected non-negative length");
 45723562            Debug.Assert(value0 is byte or short or int or long, "Expected caller to normalize to one of these types");
 3563
 45723564            if (!Vector128.IsHardwareAccelerated || length < Vector128<TValue>.Count)
 3565            {
 21123566                nuint offset = (nuint)length - 1;
 3567                TValue lookUp;
 3568
 27123569                while (length >= 4)
 3570                {
 18963571                    length -= 4;
 3572
 18963573                    ref TValue current = ref Unsafe.Add(ref searchSpace, offset);
 18963574                    lookUp = current;
 18963575                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3576                    {
 8163577                        return (int)offset;
 3578                    }
 3579
 10803580                    lookUp = Unsafe.Add(ref current, -1);
 10803581                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3582                    {
 2763583                        return (int)offset - 1;
 3584                    }
 3585
 8043586                    lookUp = Unsafe.Add(ref current, -2);
 8043587                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3588                    {
 1143589                        return (int)offset - 2;
 3590                    }
 3591
 6903592                    lookUp = Unsafe.Add(ref current, -3);
 6903593                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3594                    {
 903595                        return (int)offset - 3;
 3596                    }
 3597
 6003598                    offset -= 4;
 3599                }
 3600
 12323601                while (length > 0)
 3602                {
 6523603                    length -= 1;
 3604
 6523605                    lookUp = Unsafe.Add(ref searchSpace, offset);
 6523606                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2 || lookUp == va
 3607                    {
 2363608                        return (int)offset;
 3609                    }
 3610
 4163611                    offset -= 1;
 3612                }
 3613            }
 24603614            else if (Vector512.IsHardwareAccelerated && length >= Vector512<TValue>.Count)
 3615            {
 35043616                Vector512<TValue> equals, current, values0 = Vector512.Create(value0), values1 = Vector512.Create(value1
 52563617                    values2 = Vector512.Create(value2), values3 = Vector512.Create(value3), values4 = Vector512.Create(v
 17523618                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.
 51843621                while (offset > 0)
 3622                {
 48063623                    current = Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset));
 48063624                    equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, valu
 48063625                        | Vector512.Equals(current, values3) | Vector512.Equals(current, values4));
 48063626                    if (equals == Vector512<TValue>.Zero)
 3627                    {
 34323628                        offset -= Vector512<TValue>.Count;
 34323629                        continue;
 3630                    }
 3631
 13743632                    return ComputeLastIndex(offset, equals);
 3633                }
 3634
 3635                // Process the first vector in the search space.
 3783636                current = Vector512.LoadUnsafe(ref searchSpace);
 3783637                equals = TNegator.NegateIfNeeded(Vector512.Equals(current, values0) | Vector512.Equals(current, values1)
 3783638                    | Vector512.Equals(current, values3) | Vector512.Equals(current, values4));
 3639
 3783640                if (equals != Vector512<TValue>.Zero)
 3641                {
 1103642                    return ComputeLastIndex(offset: 0, equals);
 3643                }
 3644            }
 7083645            else if (Vector256.IsHardwareAccelerated && length >= Vector256<TValue>.Count)
 3646            {
 7523647                Vector256<TValue> equals, current, values0 = Vector256.Create(value0), values1 = Vector256.Create(value1
 11283648                    values2 = Vector256.Create(value2), values3 = Vector256.Create(value3), values4 = Vector256.Create(v
 3763649                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.
 4163652                while (offset > 0)
 3653                {
 3123654                    current = Vector256.LoadUnsafe(ref searchSpace, (nuint)(offset));
 3123655                    equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, valu
 3123656                        | Vector256.Equals(current, values3) | Vector256.Equals(current, values4));
 3123657                    if (equals == Vector256<TValue>.Zero)
 3658                    {
 403659                        offset -= Vector256<TValue>.Count;
 403660                        continue;
 3661                    }
 3662
 2723663                    return ComputeLastIndex(offset, equals);
 3664                }
 3665
 3666                // Process the first vector in the search space.
 1043667                current = Vector256.LoadUnsafe(ref searchSpace);
 1043668                equals = TNegator.NegateIfNeeded(Vector256.Equals(current, values0) | Vector256.Equals(current, values1)
 1043669                    | Vector256.Equals(current, values3) | Vector256.Equals(current, values4));
 3670
 1043671                if (equals != Vector256<TValue>.Zero)
 3672                {
 623673                    return ComputeLastIndex(offset: 0, equals);
 3674                }
 3675            }
 3676            else
 3677            {
 6643678                Vector128<TValue> equals, current, values0 = Vector128.Create(value0), values1 = Vector128.Create(value1
 9963679                    values2 = Vector128.Create(value2), values3 = Vector128.Create(value3), values4 = Vector128.Create(v
 3323680                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.
 4003683                while (offset > 0)
 3684                {
 2483685                    current = Vector128.LoadUnsafe(ref searchSpace, (nuint)(offset));
 2483686                    equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, valu
 2483687                        | Vector128.Equals(current, values3) | Vector128.Equals(current, values4));
 3688
 2483689                    if (equals == Vector128<TValue>.Zero)
 3690                    {
 683691                        offset -= Vector128<TValue>.Count;
 683692                        continue;
 3693                    }
 3694
 1803695                    return ComputeLastIndex(offset, equals);
 3696                }
 3697
 3698                // Process the first vector in the search space.
 3699
 1523700                current = Vector128.LoadUnsafe(ref searchSpace);
 1523701                equals = TNegator.NegateIfNeeded(Vector128.Equals(current, values0) | Vector128.Equals(current, values1)
 1523702                    | Vector128.Equals(current, values3) | Vector128.Equals(current, values4));
 3703
 1523704                if (equals != Vector128<TValue>.Zero)
 3705                {
 843706                    return ComputeLastIndex(offset: 0, equals);
 3707                }
 3708            }
 3709
 9583710            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        {
 49983716            uint notEqualsElements = equals.ExtractMostSignificantBits();
 49983717            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 49983718            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        {
 41553724            uint notEqualsElements = equals.ExtractMostSignificantBits();
 41553725            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 41553726            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        {
 172893732            ulong notEqualsElements = equals.ExtractMostSignificantBits();
 172893733            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 172893734            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        {
 22023740            uint notEqualsElements = equals.ExtractMostSignificantBits();
 22023741            int index = 31 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 22023742            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        {
 22783748            uint notEqualsElements = equals.ExtractMostSignificantBits();
 22783749            int index = 31 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 22783750            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        {
 95173756            ulong notEqualsElements = equals.ExtractMostSignificantBits();
 95173757            int index = 63 - BitOperations.LeadingZeroCount(notEqualsElements); // 31 = 32 (bits in Int32) - 1 (indexing
 95173758            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        {
 667903781            public static bool NegateIfNeeded(bool equals) => equals;
 69593782            public static Vector128<T> NegateIfNeeded(Vector128<T> equals) => equals;
 62183783            public static Vector256<T> NegateIfNeeded(Vector256<T> equals) => equals;
 647573784            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            {
 250353792                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            {
 54153798                return TVector.Equals(left, right);
 3799            }
 3800        }
 3801
 3802        internal readonly struct Negate<T> : INegator<T>
 3803            where T : struct
 3804        {
 591423805            public static bool NegateIfNeeded(bool equals) => !equals;
 45183806            public static Vector128<T> NegateIfNeeded(Vector128<T> equals) => ~equals;
 44063807            public static Vector256<T> NegateIfNeeded(Vector256<T> equals) => ~equals;
 349563808            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            {
 72413816                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            {
 21073822                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> =>
 60973858            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> =>
 39963862            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        {
 100933869            if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePac
 3870            {
 13871                ref char charSearchSpace = ref Unsafe.As<T, char>(ref searchSpace);
 13872                char charLowInclusive = Unsafe.BitCast<T, char>(lowInclusive);
 13873                char charRange = (char)(Unsafe.BitCast<T, char>(highInclusive) - charLowInclusive);
 3874
 13875                return typeof(TNegator) == typeof(DontNegate<ushort>)
 13876                    ? PackedSpanHelpers.IndexOfAnyInRange(ref charSearchSpace, charLowInclusive, charRange, length)
 13877                    : PackedSpanHelpers.IndexOfAnyExceptInRange(ref charSearchSpace, charLowInclusive, charRange, length
 3878            }
 3879
 100923880            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
 148603889            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 3890            {
 56503891                T rangeInclusive = highInclusive - lowInclusive;
 323603892                for (int i = 0; i < length; i++)
 3893                {
 135583894                    ref T current = ref Unsafe.Add(ref searchSpace, i);
 135583895                    if (TNegator.NegateIfNeeded((current - lowInclusive) <= rangeInclusive))
 3896                    {
 30283897                        return i;
 3898                    }
 3899                }
 3900            }
 92103901            else if (!Vector256.IsHardwareAccelerated || length < Vector256<T>.Count)
 3902            {
 23453903                Vector128<T> lowVector = Vector128.Create(lowInclusive);
 23453904                Vector128<T> rangeVector = Vector128.Create(highInclusive - lowInclusive);
 3905                Vector128<T> inRangeVector;
 3906
 23453907                ref T current = ref searchSpace;
 23453908                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                {
 23453913                    inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref current) 
 23453914                    if (inRangeVector != Vector128<T>.Zero)
 3915                    {
 15193916                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3917                    }
 3918
 8263919                    current = ref Unsafe.Add(ref current, Vector128<T>.Count);
 3920                }
 8263921                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3922
 3923                // Process the last vector in the search space (which might overlap with already processed elements).
 8263924                inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref oneVectorAway
 8263925                if (inRangeVector != Vector128<T>.Zero)
 3926                {
 2263927                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3928                }
 3929            }
 68653930            else if (!Vector512.IsHardwareAccelerated || length < (uint)Vector512<T>.Count)
 3931            {
 17573932                Vector256<T> lowVector = Vector256.Create(lowInclusive);
 17573933                Vector256<T> rangeVector = Vector256.Create(highInclusive - lowInclusive);
 3934                Vector256<T> inRangeVector;
 3935
 17573936                ref T current = ref searchSpace;
 17573937                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                {
 17573942                    inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref current) 
 17573943                    if (inRangeVector != Vector256<T>.Zero)
 3944                    {
 11473945                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3946                    }
 3947
 6103948                    current = ref Unsafe.Add(ref current, Vector256<T>.Count);
 3949                }
 6103950                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3951
 3952                // Process the last vector in the search space (which might overlap with already processed elements).
 6103953                inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref oneVectorAway
 6103954                if (inRangeVector != Vector256<T>.Zero)
 3955                {
 1773956                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3957                }
 3958            }
 3959            else
 3960            {
 51083961                Vector512<T> lowVector = Vector512.Create(lowInclusive);
 51083962                Vector512<T> rangeVector = Vector512.Create(highInclusive - lowInclusive);
 3963                Vector512<T> inRangeVector;
 3964
 51083965                ref T current = ref searchSpace;
 51083966                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                {
 137543971                    inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref current) 
 137543972                    if (inRangeVector != Vector512<T>.Zero)
 3973                    {
 35243974                        return ComputeFirstIndex(ref searchSpace, ref current, inRangeVector);
 3975                    }
 3976
 102303977                    current = ref Unsafe.Add(ref current, Vector512<T>.Count);
 3978                }
 102303979                while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEnd));
 3980
 3981                // Process the last vector in the search space (which might overlap with already processed elements).
 15843982                inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref oneVectorAway
 15843983                if (inRangeVector != Vector512<T>.Zero)
 3984                {
 4433985                    return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, inRangeVector);
 3986                }
 3987            }
 3988
 47963989            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> =>
 40644024            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> =>
 40644028            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
 81284036            if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
 4037            {
 17884038                T rangeInclusive = highInclusive - lowInclusive;
 84084039                for (int i = length - 1; i >= 0; i--)
 4040                {
 34344041                    ref T current = ref Unsafe.Add(ref searchSpace, i);
 34344042                    if (TNegator.NegateIfNeeded((current - lowInclusive) <= rangeInclusive))
 4043                    {
 10184044                        return i;
 4045                    }
 4046                }
 4047            }
 63404048            else if (!Vector256.IsHardwareAccelerated || length < Vector256<T>.Count)
 4049            {
 11404050                Vector128<T> lowVector = Vector128.Create(lowInclusive);
 11404051                Vector128<T> rangeVector = Vector128.Create(highInclusive - lowInclusive);
 4052                Vector128<T> inRangeVector;
 4053
 11404054                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.
 12844057                while (offset > 0)
 4058                {
 7764059                    inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref searchSpa
 7764060                    if (inRangeVector != Vector128<T>.Zero)
 4061                    {
 6324062                        return ComputeLastIndex(offset, inRangeVector);
 4063                    }
 4064
 1444065                    offset -= Vector128<T>.Count;
 4066                }
 4067
 4068                // Process the first vector in the search space.
 5084069                inRangeVector = TNegator.NegateIfNeeded(Vector128.LessThanOrEqual(Vector128.LoadUnsafe(ref searchSpace) 
 5084070                if (inRangeVector != Vector128<T>.Zero)
 4071                {
 3004072                    return ComputeLastIndex(offset: 0, inRangeVector);
 4073                }
 4074            }
 52004075            else if (!Vector512.IsHardwareAccelerated || length < Vector512<T>.Count)
 4076            {
 12084077                Vector256<T> lowVector = Vector256.Create(lowInclusive);
 12084078                Vector256<T> rangeVector = Vector256.Create(highInclusive - lowInclusive);
 4079                Vector256<T> inRangeVector;
 4080
 12084081                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.
 14364084                while (offset > 0)
 4085                {
 9764086                    inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref searchSpa
 9764087                    if (inRangeVector != Vector256<T>.Zero)
 4088                    {
 7484089                        return ComputeLastIndex(offset, inRangeVector);
 4090                    }
 4091
 2284092                    offset -= Vector256<T>.Count;
 4093                }
 4094
 4095                // Process the first vector in the search space.
 4604096                inRangeVector = TNegator.NegateIfNeeded(Vector256.LessThanOrEqual(Vector256.LoadUnsafe(ref searchSpace) 
 4604097                if (inRangeVector != Vector256<T>.Zero)
 4098                {
 1864099                    return ComputeLastIndex(offset: 0, inRangeVector);
 4100                }
 4101            }
 4102            else
 4103            {
 39924104                Vector512<T> lowVector = Vector512.Create(lowInclusive);
 39924105                Vector512<T> rangeVector = Vector512.Create(highInclusive - lowInclusive);
 4106                Vector512<T> inRangeVector;
 4107
 39924108                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.
 130764111                while (offset > 0)
 4112                {
 118744113                    inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref searchSpa
 118744114                    if (inRangeVector != Vector512<T>.Zero)
 4115                    {
 27904116                        return ComputeLastIndex(offset, inRangeVector);
 4117                    }
 4118
 90844119                    offset -= Vector512<T>.Count;
 4120                }
 4121
 4122                // Process the first vector in the search space.
 12024123                inRangeVector = TNegator.NegateIfNeeded(Vector512.LessThanOrEqual(Vector512.LoadUnsafe(ref searchSpace) 
 12024124                if (inRangeVector != Vector512<T>.Zero)
 4125                {
 2964126                    return ComputeLastIndex(offset: 0, inRangeVector);
 4127                }
 4128            }
 4129
 21584130            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}
 4236

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)