< 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
 165520        public ProbabilisticWithAsciiCharSearchValues(ReadOnlySpan<char> values, int maxInclusive)
 21        {
 165522            Debug.Assert(IndexOfAnyAsciiSearcher.IsVectorizationSupported);
 165523            Debug.Assert(values.ContainsAnyInRange((char)0, (char)127));
 24
 165525            IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _asciiState);
 165526            _inverseAsciiState = _asciiState.CreateInverse();
 27
 165528            _map = new ProbabilisticMapState(values, maxInclusive);
 165529        }
 30
 31        internal override char[] GetValues() =>
 032            _map.GetValues();
 33
 34        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 35        internal override bool ContainsCore(char value) =>
 18523536            _map.FastContains(value);
 37
 38        internal override int IndexOfAny(ReadOnlySpan<char> span)
 39        {
 662040            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.
 662044            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
 156853                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 54
 156855                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 56                {
 46157                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 58
 46159                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 46160                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 46161                        span.Length,
 46162                        ref _inverseAsciiState);
 63                }
 64                else
 65                {
 110766                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 110767                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 68
 110769                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 110770                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 110771                        span.Length,
 110772                        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.
 156876                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 77                {
 117278                    return offset;
 79                }
 80
 81                // Fall back to using the ProbabilisticMap.
 39682                span = span.Slice(offset);
 83            }
 84
 544885            int index = ProbabilisticMap.IndexOfAny<SearchValues.TrueConst>(
 544886                ref MemoryMarshal.GetReference(span),
 544887                span.Length,
 544888                ref _map);
 89
 544890            if (index >= 0)
 91            {
 92                // We found a match. Account for the number of ASCII characters we've skipped previously.
 243393                index += offset;
 94            }
 95
 544896            return index;
 97        }
 98
 99        internal override int IndexOfAnyExcept(ReadOnlySpan<char> span)
 100        {
 6620101            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.
 6620105            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
 1568108                offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, SearchValues
 1568109                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 1568110                    span.Length,
 1568111                    ref _asciiState);
 112
 113                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 1568114                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 115                {
 741116                    return offset;
 117                }
 118
 119                // Fall back to a simple char-by-char search.
 827120                span = span.Slice(offset);
 121            }
 122
 5879123            int index = ProbabilisticMapState.IndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negat
 5879124                ref MemoryMarshal.GetReference(span),
 5879125                span.Length,
 5879126                ref _map);
 127
 5879128            if (index >= 0)
 129            {
 130                // We found a match. Account for the number of ASCII characters we've skipped previously.
 2313131                index += offset;
 132            }
 133
 5879134            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.
 3310141            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
 578150                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 151
 152                int offset;
 153
 578154                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 155                {
 172156                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 157
 172158                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 172159                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 172160                        span.Length,
 172161                        ref _inverseAsciiState);
 162                }
 163                else
 164                {
 406165                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 406166                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 167
 406168                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 406169                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 406170                        span.Length,
 406171                        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.
 578175                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 176                {
 416177                    return offset;
 178                }
 179
 180                // Fall back to using the ProbabilisticMap.
 162181                span = span.Slice(0, offset + 1);
 182            }
 183
 2894184            return ProbabilisticMap.LastIndexOfAny<SearchValues.TrueConst>(
 2894185                ref MemoryMarshal.GetReference(span),
 2894186                span.Length,
 2894187                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.
 3310194            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
 578197                int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, Sear
 578198                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 578199                    span.Length,
 578200                    ref _asciiState);
 201
 202                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 578203                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 204                {
 412205                    return offset;
 206                }
 207
 208                // Fall back to a simple char-by-char search.
 166209                span = span.Slice(0, offset + 1);
 210            }
 211
 2898212            return ProbabilisticMapState.LastIndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negate
 2898213                ref MemoryMarshal.GetReference(span),
 2898214                span.Length,
 2898215                ref _map);
 216        }
 217    }
 218}