< Summary

Line coverage
98%
Covered lines: 82
Uncovered lines: 1
Coverable lines: 83
Total lines: 219
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)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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
 139520        public ProbabilisticWithAsciiCharSearchValues(ReadOnlySpan<char> values, int maxInclusive)
 21        {
 139522            Debug.Assert(IndexOfAnyAsciiSearcher.IsVectorizationSupported);
 139523            Debug.Assert(values.ContainsAnyInRange((char)0, (char)127));
 24
 139525            IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _asciiState);
 139526            _inverseAsciiState = _asciiState.CreateInverse();
 27
 139528            _map = new ProbabilisticMapState(values, maxInclusive);
 139529        }
 30
 31        internal override char[] GetValues() =>
 032            _map.GetValues();
 33
 34        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 35        internal override bool ContainsCore(char value) =>
 13378536            _map.FastContains(value);
 37
 38        internal override int IndexOfAny(ReadOnlySpan<char> span)
 39        {
 558040            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.
 558044            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
 105653                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 54
 105655                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 56                {
 27157                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 58
 27159                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 27160                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 27161                        span.Length,
 27162                        ref _inverseAsciiState);
 63                }
 64                else
 65                {
 78566                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 78567                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 68
 78569                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 78570                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 78571                        span.Length,
 78572                        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.
 105676                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 77                {
 86778                    return offset;
 79                }
 80
 81                // Fall back to using the ProbabilisticMap.
 18982                span = span.Slice(offset);
 83            }
 84
 471385            int index = ProbabilisticMap.IndexOfAny<SearchValues.TrueConst>(
 471386                ref MemoryMarshal.GetReference(span),
 471387                span.Length,
 471388                ref _map);
 89
 471390            if (index >= 0)
 91            {
 92                // We found a match. Account for the number of ASCII characters we've skipped previously.
 228093                index += offset;
 94            }
 95
 471396            return index;
 97        }
 98
 99        internal override int IndexOfAnyExcept(ReadOnlySpan<char> span)
 100        {
 5580101            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.
 5580105            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
 1056108                offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, SearchValues
 1056109                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 1056110                    span.Length,
 1056111                    ref _asciiState);
 112
 113                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 1056114                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 115                {
 477116                    return offset;
 117                }
 118
 119                // Fall back to a simple char-by-char search.
 579120                span = span.Slice(offset);
 121            }
 122
 5103123            int index = ProbabilisticMapState.IndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negat
 5103124                ref MemoryMarshal.GetReference(span),
 5103125                span.Length,
 5103126                ref _map);
 127
 5103128            if (index >= 0)
 129            {
 130                // We found a match. Account for the number of ASCII characters we've skipped previously.
 2346131                index += offset;
 132            }
 133
 5103134            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.
 2790141            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
 398150                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 151
 152                int offset;
 153
 398154                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 155                {
 144156                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 157
 144158                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 144159                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 144160                        span.Length,
 144161                        ref _inverseAsciiState);
 162                }
 163                else
 164                {
 254165                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 254166                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 167
 254168                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 254169                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 254170                        span.Length,
 254171                        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.
 398175                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 176                {
 264177                    return offset;
 178                }
 179
 180                // Fall back to using the ProbabilisticMap.
 134181                span = span.Slice(0, offset + 1);
 182            }
 183
 2526184            return ProbabilisticMap.LastIndexOfAny<SearchValues.TrueConst>(
 2526185                ref MemoryMarshal.GetReference(span),
 2526186                span.Length,
 2526187                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.
 2790194            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
 398197                int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, Sear
 398198                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 398199                    span.Length,
 398200                    ref _asciiState);
 201
 202                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 398203                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 204                {
 298205                    return offset;
 206                }
 207
 208                // Fall back to a simple char-by-char search.
 100209                span = span.Slice(0, offset + 1);
 210            }
 211
 2492212            return ProbabilisticMapState.LastIndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negate
 2492213                ref MemoryMarshal.GetReference(span),
 2492214                span.Length,
 2492215                ref _map);
 216        }
 217    }
 218}
 219