< Summary

Information
Class: System.Buffers.ProbabilisticWithAsciiCharSearchValues<T>
Assembly: System.Private.CoreLib
File(s): D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SearchValues\ProbabilisticWithAsciiCharSearchValues.cs
Line coverage
98%
Covered lines: 82
Uncovered lines: 1
Coverable lines: 83
Total lines: 218
Line coverage: 98.7%
Branch coverage
78%
Covered branches: 50
Total branches: 64
Branch coverage: 78.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
GetValues()100%110%
ContainsCore(...)100%11100%
IndexOfAny(...)72.72%2222100%
IndexOfAnyExcept(...)91.66%1212100%
LastIndexOfAny(...)70%2020100%
LastIndexOfAnyExcept(...)90%1010100%

File(s)

D:\runner\runtime\src\libraries\System.Private.CoreLib\src\System\SearchValues\ProbabilisticWithAsciiCharSearchValues.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7using System.Runtime.Intrinsics;
 8using System.Runtime.Intrinsics.Wasm;
 9using System.Runtime.Intrinsics.X86;
 10
 11namespace System.Buffers
 12{
 13    internal sealed class ProbabilisticWithAsciiCharSearchValues<TOptimizations> : SearchValues<char>
 14        where TOptimizations : struct, IndexOfAnyAsciiSearcher.IOptimizations
 15    {
 16        private IndexOfAnyAsciiSearcher.AsciiState _asciiState;
 17        private IndexOfAnyAsciiSearcher.AsciiState _inverseAsciiState;
 18        private ProbabilisticMapState _map;
 19
 162120        public ProbabilisticWithAsciiCharSearchValues(ReadOnlySpan<char> values, int maxInclusive)
 21        {
 162122            Debug.Assert(IndexOfAnyAsciiSearcher.IsVectorizationSupported);
 162123            Debug.Assert(values.ContainsAnyInRange((char)0, (char)127));
 24
 162125            IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _asciiState);
 162126            _inverseAsciiState = _asciiState.CreateInverse();
 27
 162128            _map = new ProbabilisticMapState(values, maxInclusive);
 162129        }
 30
 31        internal override char[] GetValues() =>
 032            _map.GetValues();
 33
 34        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 35        internal override bool ContainsCore(char value) =>
 18312436            _map.FastContains(value);
 37
 38        internal override int IndexOfAny(ReadOnlySpan<char> span)
 39        {
 648440            int offset = 0;
 41
 42            // We check whether the first character is ASCII before calling into IndexOfAnyAsciiSearcher
 43            // in order to minimize the overhead this fast-path has on non-ASCII texts.
 648444            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128<short>.Count && char.IsAsci
 45            {
 46                // We are using IndexOfAnyAsciiSearcher to search for the first ASCII character in the set, or any non-A
 47                // We do this by inverting the bitmap and using the opposite search function (Negate instead of DontNega
 48
 49                // If the bitmap we're using contains a 0, we have to use 'Ssse3AndWasmHandleZeroInNeedle' when running 
 50                // Everything else should use 'Default'. 'TOptimizations' specifies whether '_asciiState' contains a 0.
 51                // Since we're using the inverse bitmap in this case, we have to use 'Ssse3AndWasmHandleZeroInNeedle' if
 52                // running on X86/WASM and 'TOptimizations' is 'Default' (as that means that the inverse bitmap definite
 154653                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 54
 154655                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 56                {
 45557                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 58
 45559                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 45560                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 45561                        span.Length,
 45562                        ref _inverseAsciiState);
 63                }
 64                else
 65                {
 109166                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 109167                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 68
 109169                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 109170                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 109171                        span.Length,
 109172                        ref _inverseAsciiState);
 73                }
 74
 75                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 154676                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 77                {
 115678                    return offset;
 79                }
 80
 81                // Fall back to using the ProbabilisticMap.
 39082                span = span.Slice(offset);
 83            }
 84
 532885            int index = ProbabilisticMap.IndexOfAny<SearchValues.TrueConst>(
 532886                ref MemoryMarshal.GetReference(span),
 532887                span.Length,
 532888                ref _map);
 89
 532890            if (index >= 0)
 91            {
 92                // We found a match. Account for the number of ASCII characters we've skipped previously.
 238893                index += offset;
 94            }
 95
 532896            return index;
 97        }
 98
 99        internal override int IndexOfAnyExcept(ReadOnlySpan<char> span)
 100        {
 6484101            int offset = 0;
 102
 103            // We check whether the first character is ASCII before calling into IndexOfAnyAsciiSearcher
 104            // in order to minimize the overhead this fast-path has on non-ASCII texts.
 6484105            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128<short>.Count && char.IsAsci
 106            {
 107                // Do a regular IndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a non-AS
 1546108                offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, SearchValues
 1546109                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 1546110                    span.Length,
 1546111                    ref _asciiState);
 112
 113                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 1546114                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 115                {
 735116                    return offset;
 117                }
 118
 119                // Fall back to a simple char-by-char search.
 811120                span = span.Slice(offset);
 121            }
 122
 5749123            int index = ProbabilisticMapState.IndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negat
 5749124                ref MemoryMarshal.GetReference(span),
 5749125                span.Length,
 5749126                ref _map);
 127
 5749128            if (index >= 0)
 129            {
 130                // We found a match. Account for the number of ASCII characters we've skipped previously.
 2259131                index += offset;
 132            }
 133
 5749134            return index;
 135        }
 136
 137        internal override int LastIndexOfAny(ReadOnlySpan<char> span)
 138        {
 139            // We check whether the last character is ASCII before calling into IndexOfAnyAsciiSearcher
 140            // in order to minimize the overhead this fast-path has on non-ASCII texts.
 3242141            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128<short>.Count && char.IsAsci
 142            {
 143                // We are using IndexOfAnyAsciiSearcher to search for the last ASCII character in the set, or any non-AS
 144                // We do this by inverting the bitmap and using the opposite search function (Negate instead of DontNega
 145
 146                // If the bitmap we're using contains a 0, we have to use 'Ssse3AndWasmHandleZeroInNeedle' when running 
 147                // Everything else should use 'Default'. 'TOptimizations' specifies whether '_asciiState' contains a 0.
 148                // Since we're using the inverse bitmap in this case, we have to use 'Ssse3AndWasmHandleZeroInNeedle' if
 149                // running on X86/WASM and 'TOptimizations' is 'Default' (as that means that the inverse bitmap definite
 568150                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 151
 152                int offset;
 153
 568154                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 155                {
 170156                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 157
 170158                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 170159                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 170160                        span.Length,
 170161                        ref _inverseAsciiState);
 162                }
 163                else
 164                {
 398165                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 398166                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 167
 398168                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 398169                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 398170                        span.Length,
 398171                        ref _inverseAsciiState);
 172                }
 173
 174                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 568175                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 176                {
 410177                    return offset;
 178                }
 179
 180                // Fall back to using the ProbabilisticMap.
 158181                span = span.Slice(0, offset + 1);
 182            }
 183
 2832184            return ProbabilisticMap.LastIndexOfAny<SearchValues.TrueConst>(
 2832185                ref MemoryMarshal.GetReference(span),
 2832186                span.Length,
 2832187                ref _map);
 188        }
 189
 190        internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span)
 191        {
 192            // We check whether the last character is ASCII before calling into IndexOfAnyAsciiSearcher
 193            // in order to minimize the overhead this fast-path has on non-ASCII texts.
 3242194            if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128<short>.Count && char.IsAsci
 195            {
 196                // Do a regular LastIndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a no
 568197                int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, Sear
 568198                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 568199                    span.Length,
 568200                    ref _asciiState);
 201
 202                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 568203                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 204                {
 408205                    return offset;
 206                }
 207
 208                // Fall back to a simple char-by-char search.
 160209                span = span.Slice(0, offset + 1);
 210            }
 211
 2834212            return ProbabilisticMapState.LastIndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negate
 2834213                ref MemoryMarshal.GetReference(span),
 2834214                span.Length,
 2834215                ref _map);
 216        }
 217    }
 218}