< Summary

Line coverage
67%
Covered lines: 350
Uncovered lines: 167
Coverable lines: 517
Total lines: 1346
Line coverage: 67.6%
Branch coverage
67%
Covered branches: 224
Total branches: 330
Branch coverage: 67.8%
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/SpanHelpers.Packed.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.CompilerServices;
 7using System.Runtime.Intrinsics;
 8using System.Runtime.Intrinsics.X86;
 9
 10namespace System
 11{
 12    // This is a separate class instead of 'partial SpanHelpers' to hide the private helpers
 13    // included in this file which are specific to the packed implementation.
 14    internal static partial class PackedSpanHelpers
 15    {
 16        // We only do this optimization if we have support for X86 intrinsics (Sse2) as the packing is noticeably cheape
 17        // While the impact on the worst-case (match at the start) is minimal on X86, it's prohibitively large on ARM.
 14877418        public static bool PackedIndexOfIsSupported => Sse2.IsSupported;
 19
 20        // Not all values can benefit from packing the searchSpace. See comments in PackSources below.
 21        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22        public static unsafe bool CanUsePackedIndexOf<T>(T value)
 23        {
 8093524            Debug.Assert(PackedIndexOfIsSupported);
 8093525            Debug.Assert(RuntimeHelpers.IsBitwiseEquatable<T>());
 8093526            Debug.Assert(sizeof(T) == sizeof(ushort));
 27
 8093528            return Unsafe.BitCast<T, ushort>(value) - 1u < 254u;
 29        }
 30
 31        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 32        [CompExactlyDependsOn(typeof(Sse2))]
 33        public static int IndexOf(ref char searchSpace, char value, int length) =>
 1180834            IndexOf<SpanHelpers.DontNegate<short>, NopTransform>(ref Unsafe.As<char, short>(ref searchSpace), (short)val
 35
 36        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 37        [CompExactlyDependsOn(typeof(Sse2))]
 38        public static int IndexOfAnyExcept(ref char searchSpace, char value, int length) =>
 150039            IndexOf<SpanHelpers.Negate<short>, NopTransform>(ref Unsafe.As<char, short>(ref searchSpace), (short)value, 
 40
 41        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 42        [CompExactlyDependsOn(typeof(Sse2))]
 43        public static int IndexOfAny(ref char searchSpace, char value0, char value1, int length) =>
 131244            IndexOfAny<SpanHelpers.DontNegate<short>, NopTransform>(ref Unsafe.As<char, short>(ref searchSpace), (short)
 45
 46        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47        [CompExactlyDependsOn(typeof(Sse2))]
 48        public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, int length) =>
 131249            IndexOfAny<SpanHelpers.Negate<short>, NopTransform>(ref Unsafe.As<char, short>(ref searchSpace), (short)valu
 50
 51        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 52        [CompExactlyDependsOn(typeof(Sse2))]
 53        public static int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, int length) =>
 184454            IndexOfAny<SpanHelpers.DontNegate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)value0, (short
 55
 56        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 57        [CompExactlyDependsOn(typeof(Sse2))]
 58        public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, char value2, int length) =>
 184459            IndexOfAny<SpanHelpers.Negate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)value0, (short)val
 60
 61        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 62        [CompExactlyDependsOn(typeof(Sse2))]
 63        public static int IndexOfAnyIgnoreCase(ref char searchSpace, char value, int length)
 64        {
 63665            Debug.Assert((value | 0x20) == value);
 66
 63667            return IndexOf<SpanHelpers.DontNegate<short>, Or20Transform>(ref Unsafe.As<char, short>(ref searchSpace), (s
 68        }
 69
 70        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 71        [CompExactlyDependsOn(typeof(Sse2))]
 72        public static int IndexOfAnyExceptIgnoreCase(ref char searchSpace, char value, int length)
 73        {
 63674            Debug.Assert((value | 0x20) == value);
 75
 63676            return IndexOf<SpanHelpers.Negate<short>, Or20Transform>(ref Unsafe.As<char, short>(ref searchSpace), (short
 77        }
 78
 79        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 80        [CompExactlyDependsOn(typeof(Sse2))]
 81        public static int IndexOfAnyIgnoreCase(ref char searchSpace, char value0, char value1, int length)
 82        {
 144083            Debug.Assert((value0 | 0x20) == value0);
 144084            Debug.Assert((value1 | 0x20) == value1);
 85
 144086            return IndexOfAny<SpanHelpers.DontNegate<short>, Or20Transform>(ref Unsafe.As<char, short>(ref searchSpace),
 87        }
 88
 89        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 90        [CompExactlyDependsOn(typeof(Sse2))]
 91        public static int IndexOfAnyExceptIgnoreCase(ref char searchSpace, char value0, char value1, int length)
 92        {
 144093            Debug.Assert((value0 | 0x20) == value0);
 144094            Debug.Assert((value1 | 0x20) == value1);
 95
 144096            return IndexOfAny<SpanHelpers.Negate<short>, Or20Transform>(ref Unsafe.As<char, short>(ref searchSpace), (sh
 97        }
 98
 99        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 100        [CompExactlyDependsOn(typeof(Sse2))]
 101        public static int IndexOfAnyInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) =>
 1749102            IndexOfAnyInRange<SpanHelpers.DontNegate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)lowIncl
 103
 104        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 105        [CompExactlyDependsOn(typeof(Sse2))]
 106        public static int IndexOfAnyExceptInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int leng
 1748107            IndexOfAnyInRange<SpanHelpers.Negate<short>>(ref Unsafe.As<char, short>(ref searchSpace), (short)lowInclusiv
 108
 109        [CompExactlyDependsOn(typeof(Sse2))]
 110        public static bool Contains(ref short searchSpace, short value, int length)
 111        {
 0112            Debug.Assert(CanUsePackedIndexOf(value));
 113
 0114            if (length < Vector128<short>.Count)
 115            {
 0116                nuint offset = 0;
 117
 0118                if (length >= 4)
 119                {
 0120                    length -= 4;
 121
 0122                    if (searchSpace == value ||
 0123                        Unsafe.Add(ref searchSpace, 1) == value ||
 0124                        Unsafe.Add(ref searchSpace, 2) == value ||
 0125                        Unsafe.Add(ref searchSpace, 3) == value)
 126                    {
 0127                        return true;
 128                    }
 129
 0130                    offset = 4;
 131                }
 132
 0133                while (length > 0)
 134                {
 0135                    length -= 1;
 136
 0137                    if (Unsafe.Add(ref searchSpace, offset) == value)
 138                    {
 0139                        return true;
 140                    }
 141
 0142                    offset += 1;
 143                }
 144            }
 145            else
 146            {
 0147                ref short currentSearchSpace = ref searchSpace;
 148#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 0149                if (Avx512BW.IsSupported && Vector512.IsHardwareAccelerated && length > Vector512<short>.Count)
 150#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 151                {
 0152                    Vector512<byte> packedValue = Vector512.Create((byte)value);
 153
 0154                    if (length > 2 * Vector512<short>.Count)
 155                    {
 156                        // Process the input in chunks of 64 characters (2 * Vector512<short>).
 157                        // If the input length is a multiple of 64, don't consume the last 16 characters in this loop.
 158                        // Let the fallback below handle it instead. This is why the condition is
 159                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0160                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector512<short>
 161
 162                        do
 163                        {
 0164                            Vector512<short> source0 = Vector512.LoadUnsafe(ref currentSearchSpace);
 0165                            Vector512<short> source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512<sho
 0166                            Vector512<byte> packedSource = PackSources(source0, source1);
 167
 0168                            if (Vector512.EqualsAny(packedValue, packedSource))
 169                            {
 0170                                return true;
 171                            }
 172
 0173                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector512<short>.Count);
 174                        }
 0175                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 176                    }
 177
 178                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 179                    // They may overlap, but we're only interested in whether any value matched.
 180                    {
 0181                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<short>.Count
 182
 0183                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 0184                            ? ref oneVectorAwayFromEnd
 0185                            : ref currentSearchSpace;
 186
 0187                        Vector512<short> source0 = Vector512.LoadUnsafe(ref firstVector);
 0188                        Vector512<short> source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 0189                        Vector512<byte> packedSource = PackSources(source0, source1);
 190
 0191                        if (Vector512.EqualsAny(packedValue, packedSource))
 192                        {
 0193                            return true;
 194                        }
 195                    }
 196                }
 197#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 0198                else if (Avx2.IsSupported && length > Vector256<short>.Count)
 199#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 200                {
 0201                    Vector256<byte> packedValue = Vector256.Create((byte)value);
 202
 0203                    if (length > 2 * Vector256<short>.Count)
 204                    {
 205                        // Process the input in chunks of 32 characters (2 * Vector256<short>).
 206                        // If the input length is a multiple of 32, don't consume the last 16 characters in this loop.
 207                        // Let the fallback below handle it instead. This is why the condition is
 208                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0209                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector256<short>
 210
 211                        do
 212                        {
 0213                            Vector256<short> source0 = Vector256.LoadUnsafe(ref currentSearchSpace);
 0214                            Vector256<short> source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256<sho
 0215                            Vector256<byte> packedSource = PackSources(source0, source1);
 0216                            Vector256<byte> result = Vector256.Equals(packedValue, packedSource);
 217
 0218                            if (result != Vector256<byte>.Zero)
 219                            {
 0220                                return true;
 221                            }
 222
 0223                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256<short>.Count);
 224                        }
 0225                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 226                    }
 227
 228                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 229                    // They may overlap, but we're only interested in whether any value matched.
 230                    {
 0231                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<short>.Count
 232
 0233                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 0234                            ? ref oneVectorAwayFromEnd
 0235                            : ref currentSearchSpace;
 236
 0237                        Vector256<short> source0 = Vector256.LoadUnsafe(ref firstVector);
 0238                        Vector256<short> source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 0239                        Vector256<byte> packedSource = PackSources(source0, source1);
 0240                        Vector256<byte> result = Vector256.Equals(packedValue, packedSource);
 241
 0242                        if (result != Vector256<byte>.Zero)
 243                        {
 0244                            return true;
 245                        }
 246                    }
 247                }
 248                else
 249                {
 0250                    Vector128<byte> packedValue = Vector128.Create((byte)value);
 251
 252#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parsea
 253                                                                         // with the check above of Avx2.IsSupported && 
 254                                                                         // in this if statement dead code when Avx2.IsS
 255                                                                         // not generating dead code.
 256#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check abov
 257                                                                                   // may take a dependence on the JIT c
 258                                                                                   // This logic MUST NOT be extracted t
 0259                    if (!Avx2.IsSupported && length > 2 * Vector128<short>.Count)
 260#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 261#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing
 262                    {
 263                        // Process the input in chunks of 16 characters (2 * Vector128<short>).
 264                        // If the input length is a multiple of 16, don't consume the last 16 characters in this loop.
 265                        // Let the fallback below handle it instead. This is why the condition is
 266                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0267                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector128<short>
 268
 269                        do
 270                        {
 0271                            Vector128<short> source0 = Vector128.LoadUnsafe(ref currentSearchSpace);
 0272                            Vector128<short> source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128<sho
 0273                            Vector128<byte> packedSource = PackSources(source0, source1);
 0274                            Vector128<byte> result = Vector128.Equals(packedValue, packedSource);
 275
 0276                            if (result != Vector128<byte>.Zero)
 277                            {
 0278                                return true;
 279                            }
 280
 0281                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128<short>.Count);
 282                        }
 0283                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 284                    }
 285
 286                    // We have 1-16 characters remaining. Process the first and last vector in the search space.
 287                    // They may overlap, but we're only interested in whether any value matched.
 288                    {
 0289                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<short>.Count
 290
 0291                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 0292                            ? ref oneVectorAwayFromEnd
 0293                            : ref currentSearchSpace;
 294
 0295                        Vector128<short> source0 = Vector128.LoadUnsafe(ref firstVector);
 0296                        Vector128<short> source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 0297                        Vector128<byte> packedSource = PackSources(source0, source1);
 0298                        Vector128<byte> result = Vector128.Equals(packedValue, packedSource);
 299
 0300                        if (result != Vector128<byte>.Zero)
 301                        {
 0302                            return true;
 303                        }
 304                    }
 305                }
 306            }
 307
 0308            return false;
 309        }
 310
 311        [CompExactlyDependsOn(typeof(Sse2))]
 312        private static int IndexOf<TNegator, TTransform>(ref short searchSpace, short value, int length)
 313            where TNegator : struct, SpanHelpers.INegator<short>
 314            where TTransform : struct, ITransform
 315        {
 14580316            Debug.Assert(CanUsePackedIndexOf(value));
 317
 14580318            if (length < Vector128<short>.Count)
 319            {
 2256320                nuint offset = 0;
 321
 2256322                if (length >= 4)
 323                {
 816324                    length -= 4;
 325
 1224326                    if (TNegator.NegateIfNeeded(TTransform.TransformInput(searchSpace) == value)) return 0;
 507327                    if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 1)) == value)) ret
 396328                    if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 2)) == value)) ret
 279329                    if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 3)) == value)) ret
 330
 165331                    offset = 4;
 332                }
 333
 2721334                while (length > 0)
 335                {
 1830336                    length -= 1;
 337
 2544338                    if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, offset)) == value)
 339
 1116340                    offset += 1;
 341                }
 342            }
 343            else
 344            {
 12324345                ref short currentSearchSpace = ref searchSpace;
 346
 347#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 12324348                if (Avx512BW.IsSupported && Vector512.IsHardwareAccelerated && length > Vector512<short>.Count)
 349#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 350                {
 10711351                    Vector512<byte> packedValue = Vector512.Create((byte)value);
 352
 10711353                    if (length > 2 * Vector512<short>.Count)
 354                    {
 355                        // Process the input in chunks of 64 characters (2 * Vector512<short>).
 356                        // If the input length is a multiple of 64, don't consume the last 16 characters in this loop.
 357                        // Let the fallback below handle it instead. This is why the condition is
 358                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 10142359                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector512<short>
 360
 361                        do
 362                        {
 12098363                            Vector512<short> source0 = Vector512.LoadUnsafe(ref currentSearchSpace);
 12098364                            Vector512<short> source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512<sho
 12098365                            Vector512<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 366
 12098367                            if (HasMatch<TNegator>(packedValue, packedSource))
 368                            {
 705369                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, GetMatchMask<TNegator>
 370                            }
 371
 11393372                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector512<short>.Count);
 373                        }
 11393374                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 375                    }
 376
 377                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 378                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 379                    {
 10006380                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<short>.Count
 381
 10006382                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 10006383                            ? ref oneVectorAwayFromEnd
 10006384                            : ref currentSearchSpace;
 385
 10006386                        Vector512<short> source0 = Vector512.LoadUnsafe(ref firstVector);
 10006387                        Vector512<short> source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 10006388                        Vector512<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 389
 10006390                        if (HasMatch<TNegator>(packedValue, packedSource))
 391                        {
 267392                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 393                        }
 394                    }
 395                }
 396#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 1613397                else if (Avx2.IsSupported && length > Vector256<short>.Count)
 398#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 399                {
 240400                    Vector256<byte> packedValue = Vector256.Create((byte)value);
 401
 240402                    if (length > 2 * Vector256<short>.Count)
 403                    {
 404                        // Process the input in chunks of 32 characters (2 * Vector256<short>).
 405                        // If the input length is a multiple of 32, don't consume the last 16 characters in this loop.
 406                        // Let the fallback below handle it instead. This is why the condition is
 407                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0408                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector256<short>
 409
 410                        do
 411                        {
 0412                            Vector256<short> source0 = Vector256.LoadUnsafe(ref currentSearchSpace);
 0413                            Vector256<short> source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256<sho
 0414                            Vector256<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 0415                            Vector256<byte> result = Vector256.Equals(packedValue, packedSource);
 0416                            result = NegateIfNeeded<TNegator>(result);
 417
 0418                            if (result != Vector256<byte>.Zero)
 419                            {
 0420                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 421                            }
 422
 0423                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256<short>.Count);
 424                        }
 0425                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 426                    }
 427
 428                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 429                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 430                    {
 240431                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<short>.Count
 432
 240433                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 240434                            ? ref oneVectorAwayFromEnd
 240435                            : ref currentSearchSpace;
 436
 240437                        Vector256<short> source0 = Vector256.LoadUnsafe(ref firstVector);
 240438                        Vector256<short> source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 240439                        Vector256<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 240440                        Vector256<byte> result = Vector256.Equals(packedValue, packedSource);
 240441                        result = NegateIfNeeded<TNegator>(result);
 442
 240443                        if (result != Vector256<byte>.Zero)
 444                        {
 171445                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 446                        }
 447                    }
 448                }
 449                else
 450                {
 1373451                    Vector128<byte> packedValue = Vector128.Create((byte)value);
 452
 453#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parsea
 454                                                                         // with the check above of Avx2.IsSupported && 
 455                                                                         // in this if statement dead code when Avx2.IsS
 456                                                                         // not generating dead code.
 457#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check abov
 458                                                                                   // may take a dependence on the JIT c
 459                                                                                   // This logic MUST NOT be extracted t
 1373460                    if (!Avx2.IsSupported && length > 2 * Vector128<short>.Count)
 461#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 462#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing
 463                    {
 464                        // Process the input in chunks of 16 characters (2 * Vector128<short>).
 465                        // If the input length is a multiple of 16, don't consume the last 16 characters in this loop.
 466                        // Let the fallback below handle it instead. This is why the condition is
 467                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0468                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector128<short>
 469
 470                        do
 471                        {
 0472                            Vector128<short> source0 = Vector128.LoadUnsafe(ref currentSearchSpace);
 0473                            Vector128<short> source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128<sho
 0474                            Vector128<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 0475                            Vector128<byte> result = Vector128.Equals(packedValue, packedSource);
 0476                            result = NegateIfNeeded<TNegator>(result);
 477
 0478                            if (result != Vector128<byte>.Zero)
 479                            {
 0480                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 481                            }
 482
 0483                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128<short>.Count);
 484                        }
 0485                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 486                    }
 487
 488                    // We have 1-16 characters remaining. Process the first and last vector in the search space.
 489                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 490                    {
 1373491                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<short>.Count
 492
 1373493                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 1373494                            ? ref oneVectorAwayFromEnd
 1373495                            : ref currentSearchSpace;
 496
 1373497                        Vector128<short> source0 = Vector128.LoadUnsafe(ref firstVector);
 1373498                        Vector128<short> source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 1373499                        Vector128<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 1373500                        Vector128<byte> result = Vector128.Equals(packedValue, packedSource);
 1373501                        result = NegateIfNeeded<TNegator>(result);
 502
 1373503                        if (result != Vector128<byte>.Zero)
 504                        {
 270505                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 506                        }
 507                    }
 508                }
 509            }
 510
 11802511            return -1;
 512        }
 513
 514        [CompExactlyDependsOn(typeof(Sse2))]
 515        private static int IndexOfAny<TNegator, TTransform>(ref short searchSpace, short value0, short value1, int lengt
 516            where TNegator : struct, SpanHelpers.INegator<short>
 517            where TTransform : struct, ITransform
 518        {
 5504519            Debug.Assert(CanUsePackedIndexOf(value0));
 5504520            Debug.Assert(CanUsePackedIndexOf(value1));
 521
 5504522            if (length < Vector128<short>.Count)
 523            {
 2822524                nuint offset = 0;
 525                short lookUp;
 526
 2822527                if (length >= 4)
 528                {
 1722529                    length -= 4;
 530
 1722531                    lookUp = TTransform.TransformInput(searchSpace);
 2583532                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 0;
 861533                    lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 1));
 1011534                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 1;
 711535                    lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 2));
 789536                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 2;
 633537                    lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 3));
 726538                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 3;
 539
 540540                    offset = 4;
 541                }
 542
 2734543                while (length > 0)
 544                {
 1701545                    length -= 1;
 546
 1701547                    lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, offset));
 2308548                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return (int)offset;
 549
 1094550                    offset += 1;
 551                }
 552            }
 553            else
 554            {
 2682555                ref short currentSearchSpace = ref searchSpace;
 556#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 2682557                if (Avx512BW.IsSupported && Vector512.IsHardwareAccelerated && length > Vector512<short>.Count)
 558#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 559                {
 1542560                    Vector512<byte> packedValue0 = Vector512.Create((byte)value0);
 1542561                    Vector512<byte> packedValue1 = Vector512.Create((byte)value1);
 562
 1542563                    if (length > 2 * Vector512<short>.Count)
 564                    {
 565                        // Process the input in chunks of 64 characters (2 * Vector512<short>).
 566                        // If the input length is a multiple of 64, don't consume the last 16 characters in this loop.
 567                        // Let the fallback below handle it instead. This is why the condition is
 568                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 1164569                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector512<short>
 570
 571                        do
 572                        {
 2964573                            Vector512<short> source0 = Vector512.LoadUnsafe(ref currentSearchSpace);
 2964574                            Vector512<short> source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512<sho
 2964575                            Vector512<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 2964576                            Vector512<byte> result = NegateIfNeeded<TNegator>(Vector512.Equals(packedValue0, packedSourc
 577
 2964578                            if (result != Vector512<byte>.Zero)
 579                            {
 795580                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 581                            }
 582
 2169583                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector512<short>.Count);
 584                        }
 2169585                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 586                    }
 587
 588                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 589                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 590                    {
 747591                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<short>.Count
 592
 747593                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 747594                            ? ref oneVectorAwayFromEnd
 747595                            : ref currentSearchSpace;
 596
 747597                        Vector512<short> source0 = Vector512.LoadUnsafe(ref firstVector);
 747598                        Vector512<short> source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 747599                        Vector512<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 747600                        Vector512<byte> result = NegateIfNeeded<TNegator>(Vector512.Equals(packedValue0, packedSource) |
 601
 747602                        if (result != Vector512<byte>.Zero)
 603                        {
 378604                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 605                        }
 606                    }
 607                }
 608#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 1140609                else if (Avx2.IsSupported && length > Vector256<short>.Count)
 610#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 611                {
 570612                    Vector256<byte> packedValue0 = Vector256.Create((byte)value0);
 570613                    Vector256<byte> packedValue1 = Vector256.Create((byte)value1);
 614
 570615                    if (length > 2 * Vector256<short>.Count)
 616                    {
 617                        // Process the input in chunks of 32 characters (2 * Vector256<short>).
 618                        // If the input length is a multiple of 32, don't consume the last 16 characters in this loop.
 619                        // Let the fallback below handle it instead. This is why the condition is
 620                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0621                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector256<short>
 622
 623                        do
 624                        {
 0625                            Vector256<short> source0 = Vector256.LoadUnsafe(ref currentSearchSpace);
 0626                            Vector256<short> source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256<sho
 0627                            Vector256<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 0628                            Vector256<byte> result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(pac
 0629                            result = NegateIfNeeded<TNegator>(result);
 630
 0631                            if (result != Vector256<byte>.Zero)
 632                            {
 0633                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 634                            }
 635
 0636                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256<short>.Count);
 637                        }
 0638                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 639                    }
 640
 641                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 642                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 643                    {
 570644                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<short>.Count
 645
 570646                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 570647                            ? ref oneVectorAwayFromEnd
 570648                            : ref currentSearchSpace;
 649
 570650                        Vector256<short> source0 = Vector256.LoadUnsafe(ref firstVector);
 570651                        Vector256<short> source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 570652                        Vector256<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 570653                        Vector256<byte> result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(packedV
 570654                        result = NegateIfNeeded<TNegator>(result);
 655
 570656                        if (result != Vector256<byte>.Zero)
 657                        {
 444658                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 659                        }
 660                    }
 661                }
 662                else
 663                {
 570664                    Vector128<byte> packedValue0 = Vector128.Create((byte)value0);
 570665                    Vector128<byte> packedValue1 = Vector128.Create((byte)value1);
 666
 667#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parsea
 668                                                                         // with the check above of Avx2.IsSupported && 
 669                                                                         // in this if statement dead code when Avx2.IsS
 670                                                                         // not generating dead code.
 671#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check abov
 672                                                                                   // may take a dependence on the JIT c
 673                                                                                   // This logic MUST NOT be extracted t
 570674                    if (!Avx2.IsSupported && length > 2 * Vector128<short>.Count)
 675#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 676#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing
 677                    {
 678                        // Process the input in chunks of 16 characters (2 * Vector128<short>).
 679                        // If the input length is a multiple of 16, don't consume the last 16 characters in this loop.
 680                        // Let the fallback below handle it instead. This is why the condition is
 681                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0682                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector128<short>
 683
 684                        do
 685                        {
 0686                            Vector128<short> source0 = Vector128.LoadUnsafe(ref currentSearchSpace);
 0687                            Vector128<short> source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128<sho
 0688                            Vector128<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 0689                            Vector128<byte> result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(pac
 0690                            result = NegateIfNeeded<TNegator>(result);
 691
 0692                            if (result != Vector128<byte>.Zero)
 693                            {
 0694                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 695                            }
 696
 0697                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128<short>.Count);
 698                        }
 0699                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 700                    }
 701
 702                    // We have 1-16 characters remaining. Process the first and last vector in the search space.
 703                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 704                    {
 570705                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<short>.Count
 706
 570707                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 570708                            ? ref oneVectorAwayFromEnd
 570709                            : ref currentSearchSpace;
 710
 570711                        Vector128<short> source0 = Vector128.LoadUnsafe(ref firstVector);
 570712                        Vector128<short> source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 570713                        Vector128<byte> packedSource = TTransform.TransformInput(PackSources(source0, source1));
 570714                        Vector128<byte> result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(packedV
 570715                        result = NegateIfNeeded<TNegator>(result);
 716
 570717                        if (result != Vector128<byte>.Zero)
 718                        {
 438719                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 720                        }
 721                    }
 722                }
 723            }
 724
 1660725            return -1;
 726        }
 727
 728        [CompExactlyDependsOn(typeof(Sse2))]
 729        private static int IndexOfAny<TNegator>(ref short searchSpace, short value0, short value1, short value2, int len
 730            where TNegator : struct, SpanHelpers.INegator<short>
 731        {
 3688732            Debug.Assert(CanUsePackedIndexOf(value0));
 3688733            Debug.Assert(CanUsePackedIndexOf(value1));
 3688734            Debug.Assert(CanUsePackedIndexOf(value2));
 735
 3688736            if (length < Vector128<short>.Count)
 737            {
 1732738                nuint offset = 0;
 739                short lookUp;
 740
 1732741                if (length >= 4)
 742                {
 594743                    length -= 4;
 744
 594745                    lookUp = searchSpace;
 891746                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2)) return 0;
 297747                    lookUp = Unsafe.Add(ref searchSpace, 1);
 393748                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2)) return 1;
 201749                    lookUp = Unsafe.Add(ref searchSpace, 2);
 282750                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2)) return 2;
 120751                    lookUp = Unsafe.Add(ref searchSpace, 3);
 177752                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2)) return 3;
 753
 63754                    offset = 4;
 755                }
 756
 2755757                while (length > 0)
 758                {
 2162759                    length -= 1;
 760
 2162761                    lookUp = Unsafe.Add(ref searchSpace, offset);
 2770762                    if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1 || lookUp == value2)) return (int)o
 763
 1554764                    offset += 1;
 765                }
 766            }
 767            else
 768            {
 1956769                ref short currentSearchSpace = ref searchSpace;
 770
 771#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 1956772                if (Avx512BW.IsSupported && Vector512.IsHardwareAccelerated && length > Vector512<short>.Count)
 773#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 774                {
 1320775                    Vector512<byte> packedValue0 = Vector512.Create((byte)value0);
 1320776                    Vector512<byte> packedValue1 = Vector512.Create((byte)value1);
 1320777                    Vector512<byte> packedValue2 = Vector512.Create((byte)value2);
 778
 1320779                    if (length > 2 * Vector512<short>.Count)
 780                    {
 781                        // Process the input in chunks of 64 characters (2 * Vector512<short>).
 782                        // If the input length is a multiple of 64, don't consume the last 16 characters in this loop.
 783                        // Let the fallback below handle it instead. This is why the condition is
 784                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 1092785                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector512<short>
 786
 787                        do
 788                        {
 3648789                            Vector512<short> source0 = Vector512.LoadUnsafe(ref currentSearchSpace);
 3648790                            Vector512<short> source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512<sho
 3648791                            Vector512<byte> packedSource = PackSources(source0, source1);
 3648792                            Vector512<byte> result = NegateIfNeeded<TNegator>(Vector512.Equals(packedValue0, packedSourc
 793
 3648794                            if (result != Vector512<byte>.Zero)
 795                            {
 768796                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 797                            }
 798
 2880799                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector512<short>.Count);
 800                        }
 2880801                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 802                    }
 803
 804                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 805                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 806                    {
 552807                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<short>.Count
 808
 552809                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 552810                            ? ref oneVectorAwayFromEnd
 552811                            : ref currentSearchSpace;
 812
 552813                        Vector512<short> source0 = Vector512.LoadUnsafe(ref firstVector);
 552814                        Vector512<short> source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 552815                        Vector512<byte> packedSource = PackSources(source0, source1);
 552816                        Vector512<byte> result = NegateIfNeeded<TNegator>(Vector512.Equals(packedValue0, packedSource) |
 817
 552818                        if (result != Vector512<byte>.Zero)
 819                        {
 297820                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 821                        }
 822                    }
 823                }
 824#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 636825                else if (Avx2.IsSupported && length > Vector256<short>.Count)
 826#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 827                {
 300828                    Vector256<byte> packedValue0 = Vector256.Create((byte)value0);
 300829                    Vector256<byte> packedValue1 = Vector256.Create((byte)value1);
 300830                    Vector256<byte> packedValue2 = Vector256.Create((byte)value2);
 831
 300832                    if (length > 2 * Vector256<short>.Count)
 833                    {
 834                        // Process the input in chunks of 32 characters (2 * Vector256<short>).
 835                        // If the input length is a multiple of 32, don't consume the last 16 characters in this loop.
 836                        // Let the fallback below handle it instead. This is why the condition is
 837                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0838                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector256<short>
 839
 840                        do
 841                        {
 0842                            Vector256<short> source0 = Vector256.LoadUnsafe(ref currentSearchSpace);
 0843                            Vector256<short> source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256<sho
 0844                            Vector256<byte> packedSource = PackSources(source0, source1);
 0845                            Vector256<byte> result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(pac
 0846                            result = NegateIfNeeded<TNegator>(result);
 847
 0848                            if (result != Vector256<byte>.Zero)
 849                            {
 0850                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 851                            }
 852
 0853                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256<short>.Count);
 854                        }
 0855                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 856                    }
 857
 858                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 859                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 860                    {
 300861                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<short>.Count
 862
 300863                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 300864                            ? ref oneVectorAwayFromEnd
 300865                            : ref currentSearchSpace;
 866
 300867                        Vector256<short> source0 = Vector256.LoadUnsafe(ref firstVector);
 300868                        Vector256<short> source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 300869                        Vector256<byte> packedSource = PackSources(source0, source1);
 300870                        Vector256<byte> result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(packedV
 300871                        result = NegateIfNeeded<TNegator>(result);
 872
 300873                        if (result != Vector256<byte>.Zero)
 874                        {
 255875                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 876                        }
 877                    }
 878                }
 879                else
 880                {
 336881                    Vector128<byte> packedValue0 = Vector128.Create((byte)value0);
 336882                    Vector128<byte> packedValue1 = Vector128.Create((byte)value1);
 336883                    Vector128<byte> packedValue2 = Vector128.Create((byte)value2);
 884
 885#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parsea
 886                                                                         // with the check above of Avx2.IsSupported && 
 887                                                                         // in this if statement dead code when Avx2.IsS
 888                                                                         // not generating dead code.
 889#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check abov
 890                                                                                   // may take a dependence on the JIT c
 891                                                                                   // This logic MUST NOT be extracted t
 336892                    if (!Avx2.IsSupported && length > 2 * Vector128<short>.Count)
 893#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 894#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing
 895                    {
 896                        // Process the input in chunks of 16 characters (2 * Vector128<short>).
 897                        // If the input length is a multiple of 16, don't consume the last 16 characters in this loop.
 898                        // Let the fallback below handle it instead. This is why the condition is
 899                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 0900                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector128<short>
 901
 902                        do
 903                        {
 0904                            Vector128<short> source0 = Vector128.LoadUnsafe(ref currentSearchSpace);
 0905                            Vector128<short> source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128<sho
 0906                            Vector128<byte> packedSource = PackSources(source0, source1);
 0907                            Vector128<byte> result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(pac
 0908                            result = NegateIfNeeded<TNegator>(result);
 909
 0910                            if (result != Vector128<byte>.Zero)
 911                            {
 0912                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 913                            }
 914
 0915                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128<short>.Count);
 916                        }
 0917                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 918                    }
 919
 920                    // We have 1-16 characters remaining. Process the first and last vector in the search space.
 921                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 922                    {
 336923                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<short>.Count
 924
 336925                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 336926                            ? ref oneVectorAwayFromEnd
 336927                            : ref currentSearchSpace;
 928
 336929                        Vector128<short> source0 = Vector128.LoadUnsafe(ref firstVector);
 336930                        Vector128<short> source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 336931                        Vector128<byte> packedSource = PackSources(source0, source1);
 336932                        Vector128<byte> result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(packedV
 336933                        result = NegateIfNeeded<TNegator>(result);
 934
 336935                        if (result != Vector128<byte>.Zero)
 936                        {
 273937                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 938                        }
 939                    }
 940                }
 941            }
 942
 956943            return -1;
 944        }
 945
 946        [CompExactlyDependsOn(typeof(Sse2))]
 947        private static int IndexOfAnyInRange<TNegator>(ref short searchSpace, short lowInclusive, short rangeInclusive, 
 948            where TNegator : struct, SpanHelpers.INegator<short>
 949        {
 3497950            Debug.Assert(CanUsePackedIndexOf(lowInclusive));
 3497951            Debug.Assert(CanUsePackedIndexOf((short)(lowInclusive + rangeInclusive)));
 3497952            Debug.Assert(rangeInclusive >= 0);
 953
 3497954            if (length < Vector128<short>.Count)
 955            {
 829956                uint lowInclusiveUint = (uint)lowInclusive;
 829957                uint rangeInclusiveUint = (uint)rangeInclusive;
 4008958                for (int i = 0; i < length; i++)
 959                {
 1653960                    uint current = (uint)Unsafe.Add(ref searchSpace, i);
 1653961                    if (TNegator.NegateIfNeeded((current - lowInclusiveUint) <= rangeInclusiveUint))
 962                    {
 478963                        return i;
 964                    }
 965                }
 966            }
 967            else
 968            {
 2668969                ref short currentSearchSpace = ref searchSpace;
 970
 971#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 2668972                if (Avx512BW.IsSupported && Vector512.IsHardwareAccelerated && length > Vector512<short>.Count)
 973#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 974                {
 1754975                    Vector512<byte> lowVector = Vector512.Create((byte)lowInclusive);
 1754976                    Vector512<byte> rangeVector = Vector512.Create((byte)rangeInclusive);
 977
 1754978                    if (length > 2 * Vector512<short>.Count)
 979                    {
 980                        // Process the input in chunks of 64 characters (2 * Vector512<short>).
 981                        // If the input length is a multiple of 64, don't consume the last 16 characters in this loop.
 982                        // Let the fallback below handle it instead. This is why the condition is
 983                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 1330984                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector512<short>
 985
 986                        do
 987                        {
 3825988                            Vector512<short> source0 = Vector512.LoadUnsafe(ref currentSearchSpace);
 3825989                            Vector512<short> source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512<sho
 3825990                            Vector512<byte> packedSource = PackSources(source0, source1) - lowVector;
 991
 3825992                            if (HasMatchInRange<TNegator>(packedSource, rangeVector))
 993                            {
 827994                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, GetMatchInRangeMask<TN
 995                            }
 996
 2998997                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector512<short>.Count);
 998                        }
 2998999                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 1000                    }
 1001
 1002                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 1003                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 1004                    {
 9271005                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector512<short>.Count
 1006
 9271007                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 9271008                            ? ref oneVectorAwayFromEnd
 9271009                            : ref currentSearchSpace;
 1010
 9271011                        Vector512<short> source0 = Vector512.LoadUnsafe(ref firstVector);
 9271012                        Vector512<short> source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd);
 9271013                        Vector512<byte> packedSource = PackSources(source0, source1) - lowVector;
 1014
 9271015                        if (HasMatchInRange<TNegator>(packedSource, rangeVector))
 1016                        {
 4191017                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 1018                        }
 1019                    }
 1020                }
 1021#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if sta
 9141022                else if (Avx2.IsSupported && length > Vector256<short>.Count)
 1023#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 1024                {
 5701025                    Vector256<byte> lowVector = Vector256.Create((byte)lowInclusive);
 5701026                    Vector256<byte> rangeVector = Vector256.Create((byte)rangeInclusive);
 1027
 5701028                    if (length > 2 * Vector256<short>.Count)
 1029                    {
 1030                        // Process the input in chunks of 32 characters (2 * Vector256<short>).
 1031                        // If the input length is a multiple of 32, don't consume the last 16 characters in this loop.
 1032                        // Let the fallback below handle it instead. This is why the condition is
 1033                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 01034                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector256<short>
 1035
 1036                        do
 1037                        {
 01038                            Vector256<short> source0 = Vector256.LoadUnsafe(ref currentSearchSpace);
 01039                            Vector256<short> source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256<sho
 01040                            Vector256<byte> packedSource = PackSources(source0, source1);
 01041                            Vector256<byte> result = Vector256.LessThanOrEqual(packedSource - lowVector, rangeVector);
 01042                            result = NegateIfNeeded<TNegator>(result);
 1043
 01044                            if (result != Vector256<byte>.Zero)
 1045                            {
 01046                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 1047                            }
 1048
 01049                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256<short>.Count);
 1050                        }
 01051                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 1052                    }
 1053
 1054                    // We have 1-32 characters remaining. Process the first and last vector in the search space.
 1055                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 1056                    {
 5701057                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<short>.Count
 1058
 5701059                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 5701060                            ? ref oneVectorAwayFromEnd
 5701061                            : ref currentSearchSpace;
 1062
 5701063                        Vector256<short> source0 = Vector256.LoadUnsafe(ref firstVector);
 5701064                        Vector256<short> source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
 5701065                        Vector256<byte> packedSource = PackSources(source0, source1);
 5701066                        Vector256<byte> result = Vector256.LessThanOrEqual(packedSource - lowVector, rangeVector);
 5701067                        result = NegateIfNeeded<TNegator>(result);
 1068
 5701069                        if (result != Vector256<byte>.Zero)
 1070                        {
 3961071                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 1072                        }
 1073                    }
 1074                }
 1075                else
 1076                {
 3441077                    Vector128<byte> lowVector = Vector128.Create((byte)lowInclusive);
 3441078                    Vector128<byte> rangeVector = Vector128.Create((byte)rangeInclusive);
 1079
 1080#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parsea
 1081                                                                         // with the check above of Avx2.IsSupported && 
 1082                                                                         // in this if statement dead code when Avx2.IsS
 1083                                                                         // not generating dead code.
 1084#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check abov
 1085                                                                                   // may take a dependence on the JIT c
 1086                                                                                   // This logic MUST NOT be extracted t
 3441087                    if (!Avx2.IsSupported && length > 2 * Vector128<short>.Count)
 1088#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough
 1089#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing
 1090                    {
 1091                        // Process the input in chunks of 16 characters (2 * Vector128<short>).
 1092                        // If the input length is a multiple of 16, don't consume the last 16 characters in this loop.
 1093                        // Let the fallback below handle it instead. This is why the condition is
 1094                        // ">" instead of ">=" above, and why "IsAddressLessThan" is used instead of "IsAddressLessThanO
 01095                        ref short twoVectorsAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - (2 * Vector128<short>
 1096
 1097                        do
 1098                        {
 01099                            Vector128<short> source0 = Vector128.LoadUnsafe(ref currentSearchSpace);
 01100                            Vector128<short> source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128<sho
 01101                            Vector128<byte> packedSource = PackSources(source0, source1);
 01102                            Vector128<byte> result = Vector128.LessThanOrEqual(packedSource - lowVector, rangeVector);
 01103                            result = NegateIfNeeded<TNegator>(result);
 1104
 01105                            if (result != Vector128<byte>.Zero)
 1106                            {
 01107                                return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result);
 1108                            }
 1109
 01110                            currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128<short>.Count);
 1111                        }
 01112                        while (Unsafe.IsAddressLessThan(ref currentSearchSpace, ref twoVectorsAwayFromEnd));
 1113                    }
 1114
 1115                    // We have 1-16 characters remaining. Process the first and last vector in the search space.
 1116                    // They may overlap, but we'll handle that in the index calculation if we do get a match.
 1117                    {
 3441118                        ref short oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<short>.Count
 1119
 3441120                        ref short firstVector = ref Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwa
 3441121                            ? ref oneVectorAwayFromEnd
 3441122                            : ref currentSearchSpace;
 1123
 3441124                        Vector128<short> source0 = Vector128.LoadUnsafe(ref firstVector);
 3441125                        Vector128<short> source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
 3441126                        Vector128<byte> packedSource = PackSources(source0, source1);
 3441127                        Vector128<byte> result = Vector128.LessThanOrEqual(packedSource - lowVector, rangeVector);
 3441128                        result = NegateIfNeeded<TNegator>(result);
 1129
 3441130                        if (result != Vector128<byte>.Zero)
 1131                        {
 1991132                            return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEn
 1133                        }
 1134                    }
 1135                }
 1136            }
 1137
 11781138            return -1;
 1139        }
 1140
 1141        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1142        [CompExactlyDependsOn(typeof(Avx512BW))]
 1143        internal static Vector512<byte> PackSources(Vector512<short> source0, Vector512<short> source1)
 1144        {
 347671145            Debug.Assert(Avx512BW.IsSupported);
 1146            // Pack two vectors of characters into bytes. While the type is Vector256<short>, these are really UInt16 ch
 1147            // X86: Downcast every character using saturation.
 1148            // - Values <= 32767 result in min(value, 255).
 1149            // - Values  > 32767 result in 0. Because of this we can't accept needles that contain 0.
 347671150            return Avx512BW.PackUnsignedSaturate(source0, source1).AsByte();
 1151        }
 1152
 1153        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1154        [CompExactlyDependsOn(typeof(Avx2))]
 1155        internal static Vector256<byte> PackSources(Vector256<short> source0, Vector256<short> source1)
 1156        {
 16801157            Debug.Assert(Avx2.IsSupported);
 1158            // Pack two vectors of characters into bytes. While the type is Vector256<short>, these are really UInt16 ch
 1159            // X86: Downcast every character using saturation.
 1160            // - Values <= 32767 result in min(value, 255).
 1161            // - Values  > 32767 result in 0. Because of this we can't accept needles that contain 0.
 16801162            return Avx2.PackUnsignedSaturate(source0, source1).AsByte();
 1163        }
 1164
 1165        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1166        [CompExactlyDependsOn(typeof(Sse2))]
 1167        internal static Vector128<byte> PackSources(Vector128<short> source0, Vector128<short> source1)
 1168        {
 26231169            Debug.Assert(Sse2.IsSupported);
 1170            // Pack two vectors of characters into bytes. While the type is Vector128<short>, these are really UInt16 ch
 1171            // X86: Downcast every character using saturation.
 1172            // - Values <= 32767 result in min(value, 255).
 1173            // - Values  > 32767 result in 0. Because of this we can't accept needles that contain 0.
 26231174            return Sse2.PackUnsignedSaturate(source0, source1).AsByte();
 1175        }
 1176
 1177        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1178        private static bool NegateIfNeeded<TNegator>(bool result)
 1179            where TNegator : struct, SpanHelpers.INegator<short> =>
 1180            typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>) ? result : !result;
 1181
 1182        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1183        private static Vector128<byte> NegateIfNeeded<TNegator>(Vector128<byte> result)
 1184            where TNegator : struct, SpanHelpers.INegator<short> =>
 26231185            typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>) ? result : ~result;
 1186
 1187        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1188        private static Vector256<byte> NegateIfNeeded<TNegator>(Vector256<byte> result)
 1189            where TNegator : struct, SpanHelpers.INegator<short> =>
 16801190            typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>) ? result : ~result;
 1191
 1192        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1193        private static Vector512<byte> NegateIfNeeded<TNegator>(Vector512<byte> result)
 1194            where TNegator : struct, SpanHelpers.INegator<short> =>
 79111195            typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>) ? result : ~result;
 1196
 1197        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1198        private static bool HasMatch<TNegator>(Vector512<byte> left, Vector512<byte> right)
 1199            where TNegator : struct, SpanHelpers.INegator<short>
 1200        {
 221041201            return (typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>))
 221041202                 ? Vector512.EqualsAny(left, right) : !Vector512.EqualsAll(left, right);
 1203        }
 1204
 1205        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1206        private static Vector512<byte> GetMatchMask<TNegator>(Vector512<byte> left, Vector512<byte> right)
 1207             where TNegator : struct, SpanHelpers.INegator<short>
 1208        {
 9721209            return (typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>))
 9721210                 ? Vector512.Equals(left, right) : ~Vector512.Equals(left, right);
 1211        }
 1212
 1213        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1214        private static bool HasMatchInRange<TNegator>(Vector512<byte> left, Vector512<byte> right)
 1215        where TNegator : struct, SpanHelpers.INegator<short>
 1216        {
 47521217            return (typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>))
 47521218                 ? Vector512.LessThanOrEqualAny(left, right) : !Vector512.LessThanOrEqualAll(left, right);
 1219        }
 1220
 1221        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1222        private static Vector512<byte> GetMatchInRangeMask<TNegator>(Vector512<byte> left, Vector512<byte> right)
 1223            where TNegator : struct, SpanHelpers.INegator<short>
 1224        {
 12461225            return (typeof(TNegator) == typeof(SpanHelpers.DontNegate<short>))
 12461226                 ? Vector512.LessThanOrEqual(left, right) : ~Vector512.LessThanOrEqual(left, right);
 1227        }
 1228
 1229        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1230        private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector128<byte> equals)
 1231        {
 01232            uint notEqualsElements = equals.ExtractMostSignificantBits();
 01233            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 01234            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(short));
 1235        }
 1236
 1237        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1238        [CompExactlyDependsOn(typeof(Avx2))]
 1239        private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector256<byte> equals)
 1240        {
 01241            uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits();
 01242            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 01243            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(short));
 1244        }
 1245
 1246        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1247        [CompExactlyDependsOn(typeof(Avx512F))]
 1248        private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector512<byte> equals)
 1249        {
 30951250            ulong notEqualsElements = FixUpPackedVector512Result(equals).ExtractMostSignificantBits();
 30951251            int index = BitOperations.TrailingZeroCount(notEqualsElements);
 30951252            return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(short));
 1253        }
 1254
 1255        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1256        private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Ve
 1257        {
 11801258            uint notEqualsElements = equals.ExtractMostSignificantBits();
 11801259            int offsetInVector = BitOperations.TrailingZeroCount(notEqualsElements);
 11801260            if (offsetInVector >= Vector128<short>.Count)
 1261            {
 1262                // We matched within the second vector
 421263                current0 = ref current1;
 421264                offsetInVector -= Vector128<short>.Count;
 1265            }
 11801266            return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / sizeof(short));
 1267        }
 1268
 1269        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1270        [CompExactlyDependsOn(typeof(Avx2))]
 1271        private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Ve
 1272        {
 12661273            uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits();
 12661274            int offsetInVector = BitOperations.TrailingZeroCount(notEqualsElements);
 12661275            if (offsetInVector >= Vector256<short>.Count)
 1276            {
 1277                // We matched within the second vector
 571278                current0 = ref current1;
 571279                offsetInVector -= Vector256<short>.Count;
 1280            }
 12661281            return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / sizeof(short));
 1282        }
 1283
 1284        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1285        [CompExactlyDependsOn(typeof(Avx512F))]
 1286        private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Ve
 1287        {
 13611288            ulong notEqualsElements = FixUpPackedVector512Result(equals).ExtractMostSignificantBits();
 13611289            int offsetInVector = BitOperations.TrailingZeroCount(notEqualsElements);
 13611290            if (offsetInVector >= Vector512<short>.Count)
 1291            {
 1292                // We matched within the second vector
 901293                current0 = ref current1;
 901294                offsetInVector -= Vector512<short>.Count;
 1295            }
 13611296            return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / sizeof(short));
 1297        }
 1298
 1299        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1300        [CompExactlyDependsOn(typeof(Avx2))]
 1301        internal static Vector256<byte> FixUpPackedVector256Result(Vector256<byte> result)
 1302        {
 55911303            Debug.Assert(Avx2.IsSupported);
 1304            // Avx2.PackUnsignedSaturate(Vector256<short>.One, Vector256.Create<short>(2)) will result in
 1305            // 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2
 1306            // We want to swap the X and Y bits
 1307            // 1, 1, 1, 1, 1, 1, 1, 1, X, X, X, X, X, X, X, X, Y, Y, Y, Y, Y, Y, Y, Y, 2, 2, 2, 2, 2, 2, 2, 2
 55911308            return Avx2.Permute4x64(result.AsInt64(), 0b_11_01_10_00).AsByte();
 1309        }
 1310
 1311        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1312        [CompExactlyDependsOn(typeof(Avx512F))]
 1313        internal static Vector512<byte> FixUpPackedVector512Result(Vector512<byte> result)
 1314        {
 44561315            Debug.Assert(Avx512F.IsSupported);
 1316            // Avx512BW.PackUnsignedSaturate will interleave the inputs in 8-byte blocks.
 1317            // We want to preserve the order of the two input vectors, so we deinterleave the packed value.
 44561318            return Avx512F.PermuteVar8x64(result.AsInt64(), Vector512.Create(0, 2, 4, 6, 1, 3, 5, 7)).AsByte();
 1319        }
 1320
 1321        private interface ITransform
 1322        {
 1323            static abstract short TransformInput(short input);
 1324            static abstract Vector128<byte> TransformInput(Vector128<byte> input);
 1325            static abstract Vector256<byte> TransformInput(Vector256<byte> input);
 1326            static abstract Vector512<byte> TransformInput(Vector512<byte> input);
 1327        }
 1328
 1329        private readonly struct NopTransform : ITransform
 1330        {
 50521331            public static short TransformInput(short input) => input;
 14391332            public static Vector128<byte> TransformInput(Vector128<byte> input) => input;
 3481333            public static Vector256<byte> TransformInput(Vector256<byte> input) => input;
 233941334            public static Vector512<byte> TransformInput(Vector512<byte> input) => input;
 1335        }
 1336
 1337        private readonly struct Or20Transform : ITransform
 1338        {
 41611339            public static short TransformInput(short input) => (short)(input | 0x20);
 5041340            public static Vector128<byte> TransformInput(Vector128<byte> input) => input | Vector128.Create((byte)0x20);
 4621341            public static Vector256<byte> TransformInput(Vector256<byte> input) => input | Vector256.Create((byte)0x20);
 24211342            public static Vector512<byte> TransformInput(Vector512<byte> input) => input | Vector512.Create((byte)0x20);
 1343        }
 1344    }
 1345}
 1346

Methods/Properties

PackedIndexOfIsSupported()
CanUsePackedIndexOf(T)
IndexOf(System.Char&,System.Char,System.Int32)
IndexOfAnyExcept(System.Char&,System.Char,System.Int32)
IndexOfAny(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAnyExcept(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAny(System.Char&,System.Char,System.Char,System.Char,System.Int32)
IndexOfAnyExcept(System.Char&,System.Char,System.Char,System.Char,System.Int32)
IndexOfAnyIgnoreCase(System.Char&,System.Char,System.Int32)
IndexOfAnyExceptIgnoreCase(System.Char&,System.Char,System.Int32)
IndexOfAnyIgnoreCase(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAnyExceptIgnoreCase(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAnyInRange(System.Char&,System.Char,System.Char,System.Int32)
IndexOfAnyExceptInRange(System.Char&,System.Char,System.Char,System.Int32)
Contains(System.Int16&,System.Int16,System.Int32)
IndexOf(System.Int16&,System.Int16,System.Int32)
IndexOfAny(System.Int16&,System.Int16,System.Int16,System.Int32)
IndexOfAny(System.Int16&,System.Int16,System.Int16,System.Int16,System.Int32)
IndexOfAnyInRange(System.Int16&,System.Int16,System.Int16,System.Int32)
PackSources(System.Runtime.Intrinsics.Vector512`1<System.Int16>,System.Runtime.Intrinsics.Vector512`1<System.Int16>)
PackSources(System.Runtime.Intrinsics.Vector256`1<System.Int16>,System.Runtime.Intrinsics.Vector256`1<System.Int16>)
PackSources(System.Runtime.Intrinsics.Vector128`1<System.Int16>,System.Runtime.Intrinsics.Vector128`1<System.Int16>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector128`1<System.Byte>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector256`1<System.Byte>)
NegateIfNeeded(System.Runtime.Intrinsics.Vector512`1<System.Byte>)
HasMatch(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
GetMatchMask(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
HasMatchInRange(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
GetMatchInRangeMask(System.Runtime.Intrinsics.Vector512`1<System.Byte>,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
ComputeFirstIndex(System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
ComputeFirstIndex(System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
ComputeFirstIndex(System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
ComputeFirstIndexOverlapped(System.Int16&,System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
ComputeFirstIndexOverlapped(System.Int16&,System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
ComputeFirstIndexOverlapped(System.Int16&,System.Int16&,System.Int16&,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
FixUpPackedVector256Result(System.Runtime.Intrinsics.Vector256`1<System.Byte>)
FixUpPackedVector512Result(System.Runtime.Intrinsics.Vector512`1<System.Byte>)
TransformInput(System.Int16)
TransformInput(System.Runtime.Intrinsics.Vector128`1<System.Byte>)
TransformInput(System.Runtime.Intrinsics.Vector256`1<System.Byte>)
TransformInput(System.Runtime.Intrinsics.Vector512`1<System.Byte>)
TransformInput(System.Int16)
TransformInput(System.Runtime.Intrinsics.Vector128`1<System.Byte>)
TransformInput(System.Runtime.Intrinsics.Vector256`1<System.Byte>)
TransformInput(System.Runtime.Intrinsics.Vector512`1<System.Byte>)