< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 327
Coverable lines: 327
Total lines: 1025
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 212
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticMap.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.Numerics;
 6using System.Runtime;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using System.Runtime.Intrinsics;
 10using System.Runtime.Intrinsics.Arm;
 11using System.Runtime.Intrinsics.Wasm;
 12using System.Runtime.Intrinsics.X86;
 13
 14namespace System.Buffers
 15{
 16    /// <summary>Data structure used to optimize checks for whether a char is in a set of chars.</summary>
 17    /// <remarks>
 18    /// Like a Bloom filter, the idea is to create a bit map of the characters we are
 19    /// searching for and use this map as a "cheap" check to decide if the current
 20    /// character in the string exists in the array of input characters. There are
 21    /// 256 bits in the map, with each character mapped to 2 bits. Every character is
 22    /// divided into 2 bytes, and then every byte is mapped to 1 bit. The character map
 23    /// is an array of 8 integers acting as map blocks. The 3 lsb in each byte in the
 24    /// character is used to index into this map to get the right block, the value of
 25    /// the remaining 5 msb are used as the bit position inside this block.
 26    /// </remarks>
 27    [StructLayout(LayoutKind.Sequential)]
 28    internal readonly struct ProbabilisticMap
 29    {
 30        // The vectorized algorithm operates on bytes instead of uint32s.
 31        // The index and shift are adjusted so that we represent the structure
 32        // as "32 x uint8" instead of "8 x uint32".
 33        // We use the vectorized implementation when we have access to Sse41 or Arm64 intrinsics.
 34        private const uint VectorizedIndexMask = 31u;
 35        private const int VectorizedIndexShift = 5;
 36
 37        // If we don't support vectorization, use uint32 to speed up
 38        // "IsCharBitSet" checks in scalar loops.
 39        private const uint PortableIndexMask = 7u;
 40        private const int PortableIndexShift = 3;
 41
 42        private readonly uint _e0, _e1, _e2, _e3, _e4, _e5, _e6, _e7;
 43
 44        public ProbabilisticMap(ReadOnlySpan<char> values)
 45        {
 046            bool hasAscii = false;
 047            ref uint charMap = ref _e0;
 48
 049            for (int i = 0; i < values.Length; ++i)
 50            {
 051                int c = values[i];
 52
 53                // Map low bit
 054                SetCharBit(ref charMap, (byte)c);
 55
 56                // Map high bit
 057                c >>= 8;
 58
 059                if (c == 0)
 60                {
 061                    hasAscii = true;
 62                }
 63                else
 64                {
 065                    SetCharBit(ref charMap, (byte)c);
 66                }
 67            }
 68
 069            if (hasAscii)
 70            {
 71                // Common to search for ASCII symbols. Just set the high value once.
 072                SetCharBit(ref charMap, 0);
 73            }
 074        }
 75
 76        // SetCharBit and IsCharBitSet must bypass R2R because the set of supported intrinsics impacts how the type is c
 77        // so which branch is taken must never change during program execution as we're tiering up. Other methods in thi
 78        // intrinsics as a fast path where the fallback path behaves identically, so they are fine to compile R2R.
 79        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 80        [BypassReadyToRun]
 81        private static void SetCharBit(ref uint charMap, byte value)
 82        {
 083            if (Sse41.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported)
 84            {
 085                Unsafe.Add(ref Unsafe.As<uint, byte>(ref charMap), value & VectorizedIndexMask) |= (byte)(1u << (value >
 86            }
 87            else
 88            {
 089                Unsafe.Add(ref charMap, value & PortableIndexMask) |= 1u << (value >> PortableIndexShift);
 90            }
 091        }
 92
 93        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 94        [BypassReadyToRun]
 095        private static bool IsCharBitSet(ref uint charMap, byte value) => Sse41.IsSupported || AdvSimd.Arm64.IsSupported
 096            ? (Unsafe.Add(ref Unsafe.As<uint, byte>(ref charMap), value & VectorizedIndexMask) & (1u << (value >> Vector
 097            : (Unsafe.Add(ref charMap, value & PortableIndexMask) & (1u << (value >> PortableIndexShift))) != 0;
 98
 99        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 100        internal static bool Contains(ref uint charMap, ReadOnlySpan<char> values, int ch) =>
 0101            IsCharBitSet(ref charMap, (byte)ch) &&
 0102            IsCharBitSet(ref charMap, (byte)(ch >> 8)) &&
 0103            Contains(values, (char)ch);
 104
 105        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 106        internal static bool Contains(ReadOnlySpan<char> values, char ch) =>
 0107            SpanHelpers.NonPackedContainsValueType(
 0108                ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(values)),
 0109                (short)ch,
 0110                values.Length);
 111
 112        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 113        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 114        private static Vector512<byte> ContainsMask64CharsAvx512(Vector512<byte> charMap, ref char searchSpace0, ref cha
 115        {
 0116            Vector512<ushort> source0 = Vector512.LoadUnsafe(ref searchSpace0);
 0117            Vector512<ushort> source1 = Vector512.LoadUnsafe(ref searchSpace1);
 118
 0119            Vector512<byte> sourceLower = Avx512Vbmi.PermuteVar64x8x2(source0.AsByte(), Vector512.CreateSequence<byte>(0
 0120            Vector512<byte> sourceUpper = Avx512Vbmi.PermuteVar64x8x2(source0.AsByte(), Vector512.CreateSequence<byte>(1
 121
 0122            Vector512<byte> resultLower = IsCharBitNotSetAvx512(charMap, sourceLower);
 0123            Vector512<byte> resultUpper = IsCharBitNotSetAvx512(charMap, sourceUpper);
 124
 0125            return ~(resultLower | resultUpper);
 126        }
 127
 128        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 129        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 130        private static Vector512<byte> IsCharBitNotSetAvx512(Vector512<byte> charMap, Vector512<byte> values)
 131        {
 132            // X86 does not have an instruction for right shifting 8-bit values, so it's instead emulated
 133            // by using a 32-bit value shift followed by an AND to mask off the bits that should be zeroed.
 134            // We're using PermuteVar64x8, which only looks at the lower 6 bits, so we can skip the AND.
 135            // Bits 4/5/6 will not affect the result as the bit positions vector is duplicated 8 times.
 0136            Vector512<byte> shifted = (values.AsInt32() >>> VectorizedIndexShift).AsByte();
 137
 0138            Vector512<byte> bitPositions = Avx512Vbmi.PermuteVar64x8(Vector512.Create(0x8040201008040201).AsByte(), shif
 139
 140            // We want to select bytes from 'charMap' based on the low 5 bits of 'values' (values & VectorizedIndexMask)
 141            // PermuteVar64x8 will look at the low 6 bits, but the 6th bit will not affect the result as the 'charMap' i
 0142            Vector512<byte> bitMask = Avx512Vbmi.PermuteVar64x8(charMap, values);
 143
 0144            return Vector512.Equals(bitMask & bitPositions, Vector512<byte>.Zero);
 145        }
 146
 147        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 148        [CompExactlyDependsOn(typeof(Avx512Vbmi.VL))]
 149        private static Vector256<byte> ContainsMask32CharsAvx512(Vector256<byte> charMap, ref char searchSpace0, ref cha
 150        {
 0151            Vector256<ushort> source0 = Vector256.LoadUnsafe(ref searchSpace0);
 0152            Vector256<ushort> source1 = Vector256.LoadUnsafe(ref searchSpace1);
 153
 0154            Vector256<byte> sourceLower = Avx512Vbmi.VL.PermuteVar32x8x2(source0.AsByte(), Vector256.CreateSequence<byte
 0155            Vector256<byte> sourceUpper = Avx512Vbmi.VL.PermuteVar32x8x2(source0.AsByte(), Vector256.CreateSequence<byte
 156
 0157            Vector256<byte> resultLower = IsCharBitNotSetAvx512(charMap, sourceLower);
 0158            Vector256<byte> resultUpper = IsCharBitNotSetAvx512(charMap, sourceUpper);
 159
 0160            return ~(resultLower | resultUpper);
 161        }
 162
 163        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 164        [CompExactlyDependsOn(typeof(Avx512Vbmi.VL))]
 165        private static Vector256<byte> IsCharBitNotSetAvx512(Vector256<byte> charMap, Vector256<byte> values)
 166        {
 167            // X86 does not have an instruction for right shifting 8-bit values, so it's instead emulated
 168            // by using a 32-bit value shift followed by an AND to mask off the bits that should be zeroed.
 169            // We're using PermuteVar32x8, which only looks at the lower 5 bits, so we can skip the AND.
 170            // Bits 4/5 will not affect the result as the bit positions vector is duplicated 4 times
 0171            Vector256<byte> shifted = (values.AsInt32() >>> VectorizedIndexShift).AsByte();
 172
 0173            Vector256<byte> bitPositions = Avx512Vbmi.VL.PermuteVar32x8(Vector256.Create(0x8040201008040201).AsByte(), s
 174
 175            // We want to select bytes from 'charMap' based on the low 5 bits of 'values' (values & VectorizedIndexMask)
 176            // PermuteVar32x8 already looks only at the low 5 bits, so we can skip the redundant AND.
 0177            Vector256<byte> bitMask = Avx512Vbmi.VL.PermuteVar32x8(charMap, values);
 178
 0179            return Vector256.Equals(bitMask & bitPositions, Vector256<byte>.Zero);
 180        }
 181
 182        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 183        [CompExactlyDependsOn(typeof(Avx2))]
 184        private static Vector256<byte> ContainsMask32CharsAvx2(Vector256<byte> charMapLower, Vector256<byte> charMapUppe
 185        {
 0186            Vector256<ushort> source0 = Vector256.LoadUnsafe(ref searchSpace);
 0187            Vector256<ushort> source1 = Vector256.LoadUnsafe(ref searchSpace, (nuint)Vector256<ushort>.Count);
 188
 0189            Vector256<byte> sourceLower = Avx2.PackUnsignedSaturate(
 0190                (source0 & Vector256.Create((ushort)255)).AsInt16(),
 0191                (source1 & Vector256.Create((ushort)255)).AsInt16());
 192
 0193            Vector256<byte> sourceUpper = Avx2.PackUnsignedSaturate(
 0194                (source0 >>> 8).AsInt16(),
 0195                (source1 >>> 8).AsInt16());
 196
 0197            Vector256<byte> resultLower = IsCharBitNotSetAvx2(charMapLower, charMapUpper, sourceLower);
 0198            Vector256<byte> resultUpper = IsCharBitNotSetAvx2(charMapLower, charMapUpper, sourceUpper);
 199
 0200            return ~(resultLower | resultUpper);
 201        }
 202
 203        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 204        [CompExactlyDependsOn(typeof(Avx2))]
 205        private static Vector256<byte> IsCharBitNotSetAvx2(Vector256<byte> charMapLower, Vector256<byte> charMapUpper, V
 206        {
 0207            Vector256<byte> shifted = values >>> VectorizedIndexShift;
 208
 0209            Vector256<byte> bitPositions = Avx2.Shuffle(Vector256.Create(0x8040201008040201).AsByte(), shifted);
 210
 0211            Vector256<byte> index = values & Vector256.Create((byte)VectorizedIndexMask);
 0212            Vector256<byte> bitMaskLower = Avx2.Shuffle(charMapLower, index);
 0213            Vector256<byte> bitMaskUpper = Avx2.Shuffle(charMapUpper, index - Vector256.Create((byte)16));
 0214            Vector256<byte> mask = Vector256.GreaterThan(index, Vector256.Create((byte)15));
 0215            Vector256<byte> bitMask = Vector256.ConditionalSelect(mask, bitMaskUpper, bitMaskLower);
 216
 0217            return Vector256.Equals(bitMask & bitPositions, Vector256<byte>.Zero);
 218        }
 219
 220        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 221        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 222        [CompExactlyDependsOn(typeof(Sse2))]
 223        [CompExactlyDependsOn(typeof(PackedSimd))]
 224        private static Vector128<byte> ContainsMask16Chars(Vector128<byte> charMapLower, Vector128<byte> charMapUpper, r
 225        {
 226            Vector128<ushort> source0 = Vector128.LoadUnsafe(ref searchSpace);
 0227            Vector128<ushort> source1 = Vector128.LoadUnsafe(ref searchSpace, (nuint)Vector128<ushort>.Count);
 228
 229            Vector128<byte> sourceLower;
 230            Vector128<byte> sourceUpper;
 231
 0232            if (Sse2.IsSupported)
 233            {
 0234                sourceLower = Sse2.PackUnsignedSaturate((source0 & Vector128.Create((ushort)255)).AsInt16(), (source1 & 
 0235                sourceUpper = Sse2.PackUnsignedSaturate((source0 >>> 8).AsInt16(), (source1 >>> 8).AsInt16());
 236            }
 237            else if (AdvSimd.Arm64.IsSupported)
 238            {
 239                sourceLower = AdvSimd.Arm64.UnzipEven(source0.AsByte(), source1.AsByte());
 240                sourceUpper = AdvSimd.Arm64.UnzipOdd(source0.AsByte(), source1.AsByte());
 241            }
 242            else if (PackedSimd.IsSupported)
 243            {
 244                sourceLower = PackedSimd.ConvertNarrowingSaturateUnsigned((source0 & Vector128.Create((ushort)255)).AsIn
 245                sourceUpper = PackedSimd.ConvertNarrowingSaturateUnsigned((source0 >>> 8).AsInt16(), (source1 >>> 8).AsI
 246            }
 247            else
 248            {
 249                // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are live/
 0250                ThrowHelper.ThrowUnreachableException();
 251
 252                sourceLower = default;
 253                sourceUpper = default;
 254            }
 255
 0256            Vector128<byte> resultLower = IsCharBitNotSet(charMapLower, charMapUpper, sourceLower);
 0257            Vector128<byte> resultUpper = IsCharBitNotSet(charMapLower, charMapUpper, sourceUpper);
 258
 0259            return ~(resultLower | resultUpper);
 260        }
 261
 262        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 263        [CompExactlyDependsOn(typeof(Sse2))]
 264        [CompExactlyDependsOn(typeof(Ssse3))]
 265        [CompExactlyDependsOn(typeof(AdvSimd))]
 266        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 267        [CompExactlyDependsOn(typeof(PackedSimd))]
 268        private static Vector128<byte> IsCharBitNotSet(Vector128<byte> charMapLower, Vector128<byte> charMapUpper, Vecto
 269        {
 270            Vector128<byte> shifted = values >>> VectorizedIndexShift;
 271
 0272            Vector128<byte> bitPositions = Vector128.ShuffleNative(Vector128.Create(0x8040201008040201).AsByte(), shifte
 273
 0274            Vector128<byte> index = values & Vector128.Create((byte)VectorizedIndexMask);
 275            Vector128<byte> bitMask;
 276
 277            if (AdvSimd.Arm64.IsSupported)
 278            {
 279                bitMask = AdvSimd.Arm64.VectorTableLookup((charMapLower, charMapUpper), index);
 280            }
 281            else
 282            {
 0283                Vector128<byte> bitMaskLower = Vector128.ShuffleNative(charMapLower, index);
 0284                Vector128<byte> bitMaskUpper = Vector128.ShuffleNative(charMapUpper, index - Vector128.Create((byte)16))
 0285                Vector128<byte> mask = Vector128.GreaterThan(index, Vector128.Create((byte)15));
 0286                bitMask = Vector128.ConditionalSelect(mask, bitMaskUpper, bitMaskLower);
 287            }
 288
 0289            return Vector128.Equals(bitMask & bitPositions, Vector128<byte>.Zero);
 290        }
 291
 292        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 293        private static bool ShouldUseSimpleLoop(int searchSpaceLength, int valuesLength)
 294        {
 295            // We can perform either
 296            // - a simple O(haystack * needle) search or
 297            // - compute a character map of the values in O(needle), followed by an O(haystack) search
 298            // As the constant factor to compute the character map is relatively high, it's more efficient
 299            // to perform a simple loop search for short inputs.
 300            //
 301            // The following check does an educated guess as to whether computing the bitmap is more expensive.
 302            // The limit of 20 on the haystack length is arbitrary, determined by experimentation.
 0303            return searchSpaceLength < Vector128<short>.Count
 0304                || (searchSpaceLength < 20 && searchSpaceLength < (valuesLength >> 1));
 305        }
 306
 307        public static int IndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
 308        {
 0309            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 310
 311            // If the search space is relatively short compared to the needle, do a simple O(n * m) search.
 0312            if (ShouldUseSimpleLoop(searchSpaceLength, valuesLength))
 313            {
 0314                return IndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.DontNegate>(ref searchSpace, searchSpaceLength, valu
 315            }
 316
 0317            if (IndexOfAnyAsciiSearcher.TryIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index))
 318            {
 0319                return index;
 320            }
 321
 0322            return ProbabilisticIndexOfAny(ref searchSpace, searchSpaceLength, ref values, valuesLength);
 323        }
 324
 325        public static int IndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLengt
 326        {
 0327            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 328
 0329            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported &&
 0330                !ShouldUseSimpleLoop(searchSpaceLength, valuesLength) &&
 0331                IndexOfAnyAsciiSearcher.TryIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out int inde
 332            {
 0333                return index;
 334            }
 335
 0336            return IndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref searchSpace, searchSpaceLength, valuesSpan);
 337        }
 338
 339        public static int LastIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, int valuesLength)
 340        {
 0341            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 342
 343            // If the search space is relatively short compared to the needle, do a simple O(n * m) search.
 0344            if (ShouldUseSimpleLoop(searchSpaceLength, valuesLength))
 345            {
 0346                return LastIndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.DontNegate>(ref searchSpace, searchSpaceLength, 
 347            }
 348
 0349            if (IndexOfAnyAsciiSearcher.TryLastIndexOfAny(ref searchSpace, searchSpaceLength, valuesSpan, out int index)
 350            {
 0351                return index;
 352            }
 353
 0354            return ProbabilisticLastIndexOfAny(ref searchSpace, searchSpaceLength, ref values, valuesLength);
 355        }
 356
 357        public static int LastIndexOfAnyExcept(ref char searchSpace, int searchSpaceLength, ref char values, int valuesL
 358        {
 0359            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 360
 0361            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported &&
 0362                !ShouldUseSimpleLoop(searchSpaceLength, valuesLength) &&
 0363                IndexOfAnyAsciiSearcher.TryLastIndexOfAnyExcept(ref searchSpace, searchSpaceLength, valuesSpan, out int 
 364            {
 0365                return index;
 366            }
 367
 0368            return LastIndexOfAnySimpleLoop<IndexOfAnyAsciiSearcher.Negate>(ref searchSpace, searchSpaceLength, valuesSp
 369        }
 370
 371        [MethodImpl(MethodImplOptions.NoInlining)]
 372        private static unsafe int ProbabilisticIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char values, 
 373        {
 0374            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 375
 376            // ProbabilisticMapState can hold either a precomputed hash table or a pointer to the values.
 377            // Precomputing the table is relatively expensive, so we only do it when using SearchValues where instances 
 0378            var state = new ProbabilisticMapState(&valuesSpan);
 379
 380            // The FalseConst here indicates that we can't use the fast character checks and must instead check the valu
 0381            return IndexOfAny<SearchValues.FalseConst>(ref searchSpace, searchSpaceLength, ref state);
 382        }
 383
 384        [MethodImpl(MethodImplOptions.NoInlining)]
 385        private static unsafe int ProbabilisticLastIndexOfAny(ref char searchSpace, int searchSpaceLength, ref char valu
 386        {
 0387            var valuesSpan = new ReadOnlySpan<char>(ref values, valuesLength);
 388
 389            // ProbabilisticMapState can hold either a precomputed hash table or a pointer to the values.
 390            // Precomputing the table is relatively expensive, so we only do it when using SearchValues where instances 
 0391            var state = new ProbabilisticMapState(&valuesSpan);
 392
 393            // The FalseConst here indicates that we can't use the fast character checks and must instead check the valu
 0394            return LastIndexOfAny<SearchValues.FalseConst>(ref searchSpace, searchSpaceLength, ref state);
 395        }
 396
 397        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 398        internal static int IndexOfAny<TUseFastContains>(ref char searchSpace, int searchSpaceLength, ref ProbabilisticM
 399            where TUseFastContains : struct, SearchValues.IRuntimeConst
 400        {
 0401            if ((Sse41.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && searchSpaceLength >= 16)
 402            {
 0403                return Vector512.IsHardwareAccelerated && Avx512Vbmi.VL.IsSupported
 0404                    ? IndexOfAnyVectorizedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, ref state)
 0405                    : IndexOfAnyVectorized<TUseFastContains>(ref searchSpace, searchSpaceLength, ref state);
 406            }
 407
 0408            return ProbabilisticMapState.IndexOfAnySimpleLoop<TUseFastContains, IndexOfAnyAsciiSearcher.DontNegate>(ref 
 409        }
 410
 411        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 412        internal static int LastIndexOfAny<TUseFastContains>(ref char searchSpace, int searchSpaceLength, ref Probabilis
 413            where TUseFastContains : struct, SearchValues.IRuntimeConst
 414        {
 0415            if ((Sse41.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && searchSpaceLength >= 16)
 416            {
 0417                return Vector512.IsHardwareAccelerated && Avx512Vbmi.VL.IsSupported
 0418                    ? LastIndexOfAnyVectorizedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, ref state)
 0419                    : LastIndexOfAnyVectorized<TUseFastContains>(ref searchSpace, searchSpaceLength, ref state);
 420            }
 421
 0422            return ProbabilisticMapState.LastIndexOfAnySimpleLoop<TUseFastContains, IndexOfAnyAsciiSearcher.DontNegate>(
 423        }
 424
 425        [CompExactlyDependsOn(typeof(Avx512Vbmi.VL))]
 426        private static int IndexOfAnyVectorizedAvx512<TUseFastContains>(ref char searchSpace, int searchSpaceLength, ref
 427            where TUseFastContains : struct, SearchValues.IRuntimeConst
 428        {
 0429            Debug.Assert(Avx512Vbmi.VL.IsSupported);
 0430            Debug.Assert(searchSpaceLength >= 16);
 431
 0432            ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
 433
 0434            Vector256<byte> charMap256 = Vector256.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map));
 435
 0436            if (searchSpaceLength > 32)
 437            {
 0438                Vector512<byte> charMap512 = Vector512.Create(charMap256);
 439
 0440                if (searchSpaceLength > 64)
 441                {
 0442                    ref char cur = ref searchSpace;
 0443                    ref char lastStartVector = ref Unsafe.Subtract(ref searchSpaceEnd, 64);
 444
 0445                    while (true)
 446                    {
 0447                        Vector512<byte> result = ContainsMask64CharsAvx512(charMap512, ref cur, ref Unsafe.Add(ref cur, 
 448
 0449                        if (result != Vector512<byte>.Zero)
 450                        {
 0451                            if (TryFindMatchAvx512<TUseFastContains>(ref cur, result.ExtractMostSignificantBits(), ref s
 452                            {
 0453                                return MatchOffset(ref searchSpace, ref cur) + index;
 454                            }
 455                        }
 456
 0457                        cur = ref Unsafe.Add(ref cur, 64);
 458
 0459                        if (Unsafe.IsAddressGreaterThan(ref cur, ref lastStartVector))
 460                        {
 0461                            if (Unsafe.AreSame(ref cur, ref searchSpaceEnd))
 462                            {
 463                                break;
 464                            }
 465
 466                            // Adjust the current vector and do one last iteration.
 0467                            cur = ref lastStartVector;
 468                        }
 469                    }
 470                }
 471                else
 472                {
 0473                    Debug.Assert(searchSpaceLength is > 32 and <= 64);
 474
 475                    // Process the first and last vector in the search space.
 476                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 0477                    Vector512<byte> result = ContainsMask64CharsAvx512(charMap512, ref searchSpace, ref Unsafe.Subtract(
 478
 0479                    if (result != Vector512<byte>.Zero)
 480                    {
 0481                        if (TryFindMatchOverlappedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, result.Ex
 482                        {
 0483                            return index;
 484                        }
 485                    }
 486                }
 487            }
 488            else
 489            {
 0490                Debug.Assert(searchSpaceLength is >= 16 and <= 32);
 491
 492                // Process the first and last vector in the search space.
 493                // They may overlap, but we'll handle that in the index calculation if we do get a match.
 0494                Vector256<byte> result = ContainsMask32CharsAvx512(charMap256, ref searchSpace, ref Unsafe.Subtract(ref 
 495
 0496                if (result != Vector256<byte>.Zero)
 497                {
 0498                    if (TryFindMatchOverlappedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, result.Extrac
 499                    {
 0500                        return index;
 501                    }
 502                }
 503            }
 504
 0505            return -1;
 506        }
 507
 508        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 509        [CompExactlyDependsOn(typeof(Sse41))]
 510        [CompExactlyDependsOn(typeof(PackedSimd))]
 511        private static int IndexOfAnyVectorized<TUseFastContains>(ref char searchSpace, int searchSpaceLength, ref Proba
 512            where TUseFastContains : struct, SearchValues.IRuntimeConst
 513        {
 0514            Debug.Assert(Sse41.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported);
 0515            Debug.Assert(searchSpaceLength >= 16);
 516
 0517            ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
 0518            ref char cur = ref searchSpace;
 519
 0520            Vector128<byte> charMapLower = Vector128.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map));
 0521            Vector128<byte> charMapUpper = Vector128.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map), (n
 522
 523#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clau
 0524            if (Avx2.IsSupported && searchSpaceLength >= 32)
 525#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 526            {
 0527                Vector256<byte> charMapLower256 = Vector256.Create(charMapLower);
 0528                Vector256<byte> charMapUpper256 = Vector256.Create(charMapUpper);
 529
 0530                ref char lastStartVectorAvx2 = ref Unsafe.Subtract(ref searchSpaceEnd, 32);
 531
 532                while (true)
 533                {
 0534                    Vector256<byte> result = ContainsMask32CharsAvx2(charMapLower256, charMapUpper256, ref cur);
 535
 0536                    if (result != Vector256<byte>.Zero)
 537                    {
 0538                        if (TryFindMatch<TUseFastContains>(ref cur, PackedSpanHelpers.FixUpPackedVector256Result(result)
 539                        {
 0540                            return MatchOffset(ref searchSpace, ref cur) + index;
 541                        }
 542                    }
 543
 0544                    cur = ref Unsafe.Add(ref cur, 32);
 545
 0546                    if (Unsafe.IsAddressGreaterThan(ref cur, ref lastStartVectorAvx2))
 547                    {
 0548                        if (Unsafe.AreSame(ref cur, ref searchSpaceEnd))
 549                        {
 0550                            return -1;
 551                        }
 552
 0553                        if (Unsafe.ByteOffset(ref cur, ref searchSpaceEnd) > 16 * sizeof(char))
 554                        {
 555                            // If we have more than 16 characters left to process, we can
 556                            // adjust the current vector and do one last iteration of Avx2.
 0557                            cur = ref lastStartVectorAvx2;
 558                        }
 559                        else
 560                        {
 561                            // Otherwise adjust the vector such that we'll only need to do a single
 562                            // iteration of ContainsMask16Chars below.
 0563                            cur = ref Unsafe.Subtract(ref searchSpaceEnd, 16);
 564                            break;
 565                        }
 566                    }
 567                }
 568            }
 569
 0570            ref char lastStartVector = ref Unsafe.Subtract(ref searchSpaceEnd, 16);
 571
 0572            while (true)
 573            {
 0574                Vector128<byte> result = ContainsMask16Chars(charMapLower, charMapUpper, ref cur);
 575
 0576                if (result != Vector128<byte>.Zero)
 577                {
 0578                    if (TryFindMatch<TUseFastContains>(ref cur, result.ExtractMostSignificantBits(), ref state, out int 
 579                    {
 0580                        return MatchOffset(ref searchSpace, ref cur) + index;
 581                    }
 582                }
 583
 0584                cur = ref Unsafe.Add(ref cur, 16);
 585
 0586                if (Unsafe.IsAddressGreaterThan(ref cur, ref lastStartVector))
 587                {
 0588                    if (Unsafe.AreSame(ref cur, ref searchSpaceEnd))
 589                    {
 590                        break;
 591                    }
 592
 593                    // Adjust the current vector and do one last iteration.
 0594                    cur = ref lastStartVector;
 595                }
 596            }
 597
 0598            return -1;
 599        }
 600
 601        [CompExactlyDependsOn(typeof(Avx512Vbmi.VL))]
 602        private static int LastIndexOfAnyVectorizedAvx512<TUseFastContains>(ref char searchSpace, int searchSpaceLength,
 603            where TUseFastContains : struct, SearchValues.IRuntimeConst
 604        {
 0605            Debug.Assert(Avx512Vbmi.VL.IsSupported);
 0606            Debug.Assert(searchSpaceLength >= 16);
 607
 0608            ref char cur = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
 609
 0610            Vector256<byte> charMap256 = Vector256.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map));
 611
 0612            if (searchSpaceLength > 32)
 613            {
 0614                Vector512<byte> charMap512 = Vector512.Create(charMap256);
 615
 0616                if (searchSpaceLength > 64)
 617                {
 0618                    ref char lastStartVector = ref Unsafe.Add(ref searchSpace, 64);
 619
 0620                    while (true)
 621                    {
 0622                        Debug.Assert(Unsafe.ByteOffset(ref searchSpace, ref cur) >= 64 * sizeof(char));
 623
 0624                        cur = ref Unsafe.Subtract(ref cur, 64);
 625
 0626                        Vector512<byte> result = ContainsMask64CharsAvx512(charMap512, ref cur, ref Unsafe.Add(ref cur, 
 627
 0628                        if (result != Vector512<byte>.Zero)
 629                        {
 0630                            if (TryFindLastMatchAvx512<TUseFastContains>(ref cur, result.ExtractMostSignificantBits(), r
 631                            {
 0632                                return MatchOffset(ref searchSpace, ref cur) + index;
 633                            }
 634                        }
 635
 0636                        if (Unsafe.IsAddressLessThanOrEqualTo(ref cur, ref lastStartVector))
 637                        {
 0638                            if (Unsafe.AreSame(ref cur, ref searchSpace))
 639                            {
 640                                break;
 641                            }
 642
 643                            // Adjust the current vector and do one last iteration.
 0644                            cur = ref lastStartVector;
 645                        }
 646                    }
 647                }
 648                else
 649                {
 0650                    Debug.Assert(searchSpaceLength is > 32 and <= 64);
 0651                    Debug.Assert(Unsafe.ByteOffset(ref searchSpace, ref cur) >= 32 * sizeof(char));
 652
 653                    // Process the first and last vector in the search space.
 654                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 0655                    Vector512<byte> result = ContainsMask64CharsAvx512(charMap512, ref searchSpace, ref Unsafe.Subtract(
 656
 0657                    if (result != Vector512<byte>.Zero)
 658                    {
 0659                        if (TryFindLastMatchOverlappedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, resul
 660                        {
 0661                            return index;
 662                        }
 663                    }
 664                }
 665            }
 666            else
 667            {
 0668                Debug.Assert(searchSpaceLength is >= 16 and <= 32);
 0669                Debug.Assert(Unsafe.ByteOffset(ref searchSpace, ref cur) >= 16 * sizeof(char));
 670
 671                // Process the first and last vector in the search space.
 672                // They may overlap, but we'll handle that in the index calculation if we do get a match.
 0673                Vector256<byte> result = ContainsMask32CharsAvx512(charMap256, ref searchSpace, ref Unsafe.Subtract(ref 
 674
 0675                if (result != Vector256<byte>.Zero)
 676                {
 0677                    if (TryFindLastMatchOverlappedAvx512<TUseFastContains>(ref searchSpace, searchSpaceLength, result.Ex
 678                    {
 0679                        return index;
 680                    }
 681                }
 682            }
 683
 0684            return -1;
 685        }
 686
 687        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 688        [CompExactlyDependsOn(typeof(Sse41))]
 689        [CompExactlyDependsOn(typeof(PackedSimd))]
 690        private static int LastIndexOfAnyVectorized<TUseFastContains>(ref char searchSpace, int searchSpaceLength, ref P
 691            where TUseFastContains : struct, SearchValues.IRuntimeConst
 692        {
 0693            Debug.Assert(Sse41.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported);
 0694            Debug.Assert(searchSpaceLength >= 16);
 695
 0696            ref char cur = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
 697
 0698            Vector128<byte> charMapLower = Vector128.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map));
 0699            Vector128<byte> charMapUpper = Vector128.LoadUnsafe(ref Unsafe.As<ProbabilisticMap, byte>(ref state.Map), (n
 700
 701#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clau
 0702            if (Avx2.IsSupported && searchSpaceLength >= 32)
 703#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 704            {
 0705                Vector256<byte> charMapLower256 = Vector256.Create(charMapLower);
 0706                Vector256<byte> charMapUpper256 = Vector256.Create(charMapUpper);
 707
 0708                ref char lastStartVectorAvx2 = ref Unsafe.Add(ref searchSpace, 32);
 709
 710                while (true)
 711                {
 0712                    Debug.Assert(Unsafe.ByteOffset(ref searchSpace, ref cur) >= 32 * sizeof(char));
 713
 0714                    cur = ref Unsafe.Subtract(ref cur, 32);
 715
 0716                    Vector256<byte> result = ContainsMask32CharsAvx2(charMapLower256, charMapUpper256, ref cur);
 717
 0718                    if (result != Vector256<byte>.Zero)
 719                    {
 0720                        if (TryFindLastMatch<TUseFastContains>(ref cur, PackedSpanHelpers.FixUpPackedVector256Result(res
 721                        {
 0722                            return MatchOffset(ref searchSpace, ref cur) + index;
 723                        }
 724                    }
 725
 0726                    if (Unsafe.IsAddressLessThanOrEqualTo(ref cur, ref lastStartVectorAvx2))
 727                    {
 0728                        if (Unsafe.AreSame(ref cur, ref searchSpace))
 729                        {
 0730                            return -1;
 731                        }
 732
 0733                        if (Unsafe.ByteOffset(ref searchSpace, ref cur) > 16 * sizeof(char))
 734                        {
 735                            // If we have more than 16 characters left to process, we can
 736                            // adjust the current vector and do one last iteration of Avx2.
 0737                            cur = ref lastStartVectorAvx2;
 738                        }
 739                        else
 740                        {
 741                            // Otherwise adjust the vector such that we'll only need to do a single
 742                            // iteration of ContainsMask16Chars below.
 0743                            cur = ref Unsafe.Add(ref searchSpace, 16);
 744                            break;
 745                        }
 746                    }
 747                }
 748            }
 749
 0750            ref char lastStartVector = ref Unsafe.Add(ref searchSpace, 16);
 751
 0752            while (true)
 753            {
 0754                Debug.Assert(Unsafe.ByteOffset(ref searchSpace, ref cur) >= 16 * sizeof(char));
 755
 0756                cur = ref Unsafe.Subtract(ref cur, 16);
 757
 0758                Vector128<byte> result = ContainsMask16Chars(charMapLower, charMapUpper, ref cur);
 759
 0760                if (result != Vector128<byte>.Zero)
 761                {
 0762                    if (TryFindLastMatch<TUseFastContains>(ref cur, result.ExtractMostSignificantBits(), ref state, out 
 763                    {
 0764                        return MatchOffset(ref searchSpace, ref cur) + index;
 765                    }
 766                }
 767
 0768                if (Unsafe.IsAddressLessThanOrEqualTo(ref cur, ref lastStartVector))
 769                {
 0770                    if (Unsafe.AreSame(ref cur, ref searchSpace))
 771                    {
 772                        break;
 773                    }
 774
 775                    // Adjust the current vector and do one last iteration.
 0776                    cur = ref lastStartVector;
 777                }
 778            }
 779
 0780            return -1;
 781        }
 782
 783        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 784        private static int MatchOffset(ref char searchSpace, ref char cur) =>
 0785            (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref cur) / sizeof(char));
 786
 787        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 788        private static bool TryFindMatch<TUseFastContains>(ref char cur, uint mask, ref ProbabilisticMapState state, out
 789            where TUseFastContains : struct, SearchValues.IRuntimeConst
 790        {
 791            do
 792            {
 0793                index = BitOperations.TrailingZeroCount(mask);
 794
 0795                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 796                {
 0797                    return true;
 798                }
 799
 0800                mask = BitOperations.ResetLowestSetBit(mask);
 801            }
 0802            while (mask != 0);
 803
 0804            index = 0;
 0805            return false;
 806        }
 807
 808        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 809        private static bool TryFindMatchOverlappedAvx512<TUseFastContains>(ref char cur, int searchSpaceLength, uint mas
 810            where TUseFastContains : struct, SearchValues.IRuntimeConst
 811        {
 812            do
 813            {
 0814                index = BitOperations.TrailingZeroCount(mask);
 815
 0816                if (index >= Vector256<ushort>.Count)
 817                {
 818                    // The potential match is in the second vector.
 819                    // Fixup the index to account for how we loaded the second overlapped vector.
 0820                    index += searchSpaceLength - (2 * Vector256<ushort>.Count);
 821                }
 822
 0823                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 824                {
 0825                    return true;
 826                }
 827
 0828                mask = BitOperations.ResetLowestSetBit(mask);
 829            }
 0830            while (mask != 0);
 831
 0832            index = 0;
 0833            return false;
 834        }
 835
 836        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 837        private static bool TryFindMatchAvx512<TUseFastContains>(ref char cur, ulong mask, ref ProbabilisticMapState sta
 838            where TUseFastContains : struct, SearchValues.IRuntimeConst
 839        {
 840            do
 841            {
 0842                index = BitOperations.TrailingZeroCount(mask);
 843
 0844                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 845                {
 0846                    return true;
 847                }
 848
 0849                mask = BitOperations.ResetLowestSetBit(mask);
 850            }
 0851            while (mask != 0);
 852
 0853            index = 0;
 0854            return false;
 855        }
 856
 857        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 858        private static bool TryFindMatchOverlappedAvx512<TUseFastContains>(ref char cur, int searchSpaceLength, ulong ma
 859            where TUseFastContains : struct, SearchValues.IRuntimeConst
 860        {
 861            do
 862            {
 0863                index = BitOperations.TrailingZeroCount(mask);
 864
 0865                if (index >= Vector512<ushort>.Count)
 866                {
 867                    // The potential match is in the second vector.
 868                    // Fixup the index to account for how we loaded the second overlapped vector.
 0869                    index += searchSpaceLength - (2 * Vector512<ushort>.Count);
 870                }
 871
 0872                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 873                {
 0874                    return true;
 875                }
 876
 0877                mask = BitOperations.ResetLowestSetBit(mask);
 878            }
 0879            while (mask != 0);
 880
 0881            index = 0;
 0882            return false;
 883        }
 884
 885        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 886        private static bool TryFindLastMatch<TUseFastContains>(ref char cur, uint mask, ref ProbabilisticMapState state,
 887            where TUseFastContains : struct, SearchValues.IRuntimeConst
 888        {
 889            do
 890            {
 0891                index = 31 - BitOperations.LeadingZeroCount(mask);
 892
 0893                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 894                {
 0895                    return true;
 896                }
 897
 898                // Clear the highest set bit
 0899                mask = BitOperations.FlipBit(mask, index);
 900            }
 0901            while (mask != 0);
 902
 0903            index = 0;
 0904            return false;
 905        }
 906
 907        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 908        private static bool TryFindLastMatchOverlappedAvx512<TUseFastContains>(ref char cur, int searchSpaceLength, uint
 909            where TUseFastContains : struct, SearchValues.IRuntimeConst
 910        {
 911            do
 912            {
 0913                index = 31 - BitOperations.LeadingZeroCount(mask);
 914
 915                // Clear the highest set bit
 0916                mask = BitOperations.FlipBit(mask, index);
 917
 0918                if (index >= Vector256<ushort>.Count)
 919                {
 920                    // The potential match is in the second vector.
 921                    // Fixup the index to account for how we loaded the second overlapped vector.
 0922                    index += searchSpaceLength - (2 * Vector256<ushort>.Count);
 923                }
 924
 0925                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 926                {
 0927                    return true;
 928                }
 929            }
 0930            while (mask != 0);
 931
 0932            index = 0;
 0933            return false;
 934        }
 935
 936        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 937        private static bool TryFindLastMatchAvx512<TUseFastContains>(ref char cur, ulong mask, ref ProbabilisticMapState
 938            where TUseFastContains : struct, SearchValues.IRuntimeConst
 939        {
 940            do
 941            {
 0942                index = 63 - BitOperations.LeadingZeroCount(mask);
 943
 0944                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 945                {
 0946                    return true;
 947                }
 948
 949                // Clear the highest set bit
 0950                mask = BitOperations.FlipBit(mask, index);
 951            }
 0952            while (mask != 0);
 953
 0954            index = 0;
 0955            return false;
 956        }
 957
 958        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 959        private static bool TryFindLastMatchOverlappedAvx512<TUseFastContains>(ref char cur, int searchSpaceLength, ulon
 960            where TUseFastContains : struct, SearchValues.IRuntimeConst
 961        {
 962            do
 963            {
 0964                index = 63 - BitOperations.LeadingZeroCount(mask);
 965
 966                // Clear the highest set bit
 0967                mask = BitOperations.FlipBit(mask, index);
 968
 0969                if (index >= Vector512<ushort>.Count)
 970                {
 971                    // The potential match is in the second vector.
 972                    // Fixup the index to account for how we loaded the second overlapped vector.
 0973                    index += searchSpaceLength - (2 * Vector512<ushort>.Count);
 974                }
 975
 0976                if (state.ConfirmProbabilisticMatch<TUseFastContains>(Unsafe.Add(ref cur, index)))
 977                {
 0978                    return true;
 979                }
 980            }
 0981            while (mask != 0);
 982
 0983            index = 0;
 0984            return false;
 985        }
 986
 987        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 988        internal static int IndexOfAnySimpleLoop<TNegator>(ref char searchSpace, int searchSpaceLength, ReadOnlySpan<cha
 989            where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
 990        {
 0991            ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength);
 0992            ref char cur = ref searchSpace;
 993
 0994            while (!Unsafe.AreSame(ref cur, ref searchSpaceEnd))
 995            {
 0996                char c = cur;
 0997                if (TNegator.NegateIfNeeded(Contains(values, c)))
 998                {
 0999                    return MatchOffset(ref searchSpace, ref cur);
 1000                }
 1001
 01002                cur = ref Unsafe.Add(ref cur, 1);
 1003            }
 1004
 01005            return -1;
 1006        }
 1007
 1008        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1009        internal static int LastIndexOfAnySimpleLoop<TNegator>(ref char searchSpace, int searchSpaceLength, ReadOnlySpan
 1010            where TNegator : struct, IndexOfAnyAsciiSearcher.INegator
 1011        {
 01012            for (int i = searchSpaceLength - 1; i >= 0; i--)
 1013            {
 01014                char c = Unsafe.Add(ref searchSpace, i);
 01015                if (TNegator.NegateIfNeeded(Contains(values, c)))
 1016                {
 01017                    return i;
 1018                }
 1019            }
 1020
 01021            return -1;
 1022        }
 1023    }
 1024}
 1025

Methods/Properties

.ctor(System.ReadOnlySpan`1<System.Char>)
SetCharBit(System.UInt32&,System.Byte)
IsCharBitSet(System.UInt32&,System.Byte)
Contains(System.UInt32&,System.ReadOnlySpan`1<System.Char>,System.Int32)
Contains(System.ReadOnlySpan`1<System.Char>,System.Char)
ContainsMask64CharsAvx512(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Char&,System.Char&)
IsCharBitNotSetAvx512(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
ContainsMask32CharsAvx512(System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Char&,System.Char&)
IsCharBitNotSetAvx512(System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
ContainsMask32CharsAvx2(System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Char&)
IsCharBitNotSetAvx2(System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Runtime.Intrinsics.Vector256`1<System.Byte>,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
ContainsMask16Chars(System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Char&)
IsCharBitNotSet(System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
ShouldUseSimpleLoop(System.Int32,System.Int32)
IndexOfAny(System.Char&,System.Int32,System.Char&,System.Int32)
IndexOfAnyExcept(System.Char&,System.Int32,System.Char&,System.Int32)
LastIndexOfAny(System.Char&,System.Int32,System.Char&,System.Int32)
LastIndexOfAnyExcept(System.Char&,System.Int32,System.Char&,System.Int32)
ProbabilisticIndexOfAny(System.Char&,System.Int32,System.Char&,System.Int32)
ProbabilisticLastIndexOfAny(System.Char&,System.Int32,System.Char&,System.Int32)
IndexOfAny(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
LastIndexOfAny(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
IndexOfAnyVectorizedAvx512(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
IndexOfAnyVectorized(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
LastIndexOfAnyVectorizedAvx512(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
LastIndexOfAnyVectorized(System.Char&,System.Int32,System.Buffers.ProbabilisticMapState&)
MatchOffset(System.Char&,System.Char&)
TryFindMatch(System.Char&,System.UInt32,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindMatchOverlappedAvx512(System.Char&,System.Int32,System.UInt32,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindMatchAvx512(System.Char&,System.UInt64,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindMatchOverlappedAvx512(System.Char&,System.Int32,System.UInt64,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindLastMatch(System.Char&,System.UInt32,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindLastMatchOverlappedAvx512(System.Char&,System.Int32,System.UInt32,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindLastMatchAvx512(System.Char&,System.UInt64,System.Buffers.ProbabilisticMapState&,System.Int32&)
TryFindLastMatchOverlappedAvx512(System.Char&,System.Int32,System.UInt64,System.Buffers.ProbabilisticMapState&,System.Int32&)
IndexOfAnySimpleLoop(System.Char&,System.Int32,System.ReadOnlySpan`1<System.Char>)
LastIndexOfAnySimpleLoop(System.Char&,System.Int32,System.ReadOnlySpan`1<System.Char>)