< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 83
Coverable lines: 83
Total lines: 219
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 64
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
GetValues()100%110%
ContainsCore(...)100%110%
IndexOfAny(...)0%22220%
IndexOfAnyExcept(...)0%12120%
LastIndexOfAny(...)0%20200%
LastIndexOfAnyExcept(...)0%10100%

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
 020        public ProbabilisticWithAsciiCharSearchValues(ReadOnlySpan<char> values, int maxInclusive)
 21        {
 022            Debug.Assert(IndexOfAnyAsciiSearcher.IsVectorizationSupported);
 023            Debug.Assert(values.ContainsAnyInRange((char)0, (char)127));
 24
 025            IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _asciiState);
 026            _inverseAsciiState = _asciiState.CreateInverse();
 27
 028            _map = new ProbabilisticMapState(values, maxInclusive);
 029        }
 30
 31        internal override char[] GetValues() =>
 032            _map.GetValues();
 33
 34        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 35        internal override bool ContainsCore(char value) =>
 036            _map.FastContains(value);
 37
 38        internal override int IndexOfAny(ReadOnlySpan<char> span)
 39        {
 040            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.
 044            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
 053                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 54
 055                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 56                {
 057                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 58
 059                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 060                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 061                        span.Length,
 062                        ref _inverseAsciiState);
 63                }
 64                else
 65                {
 066                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 067                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 68
 069                    offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.
 070                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 071                        span.Length,
 072                        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.
 076                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 77                {
 078                    return offset;
 79                }
 80
 81                // Fall back to using the ProbabilisticMap.
 082                span = span.Slice(offset);
 83            }
 84
 085            int index = ProbabilisticMap.IndexOfAny<SearchValues.TrueConst>(
 086                ref MemoryMarshal.GetReference(span),
 087                span.Length,
 088                ref _map);
 89
 090            if (index >= 0)
 91            {
 92                // We found a match. Account for the number of ASCII characters we've skipped previously.
 093                index += offset;
 94            }
 95
 096            return index;
 97        }
 98
 99        internal override int IndexOfAnyExcept(ReadOnlySpan<char> span)
 100        {
 0101            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.
 0105            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
 0108                offset = IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, SearchValues
 0109                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 0110                    span.Length,
 0111                    ref _asciiState);
 112
 113                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 0114                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 115                {
 0116                    return offset;
 117                }
 118
 119                // Fall back to a simple char-by-char search.
 0120                span = span.Slice(offset);
 121            }
 122
 0123            int index = ProbabilisticMapState.IndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negat
 0124                ref MemoryMarshal.GetReference(span),
 0125                span.Length,
 0126                ref _map);
 127
 0128            if (index >= 0)
 129            {
 130                // We found a match. Account for the number of ASCII characters we've skipped previously.
 0131                index += offset;
 132            }
 133
 0134            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.
 0141            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
 0150                Debug.Assert(_asciiState.Lookup.Contains(0) != _inverseAsciiState.Lookup.Contains(0));
 151
 152                int offset;
 153
 0154                if ((Ssse3.IsSupported || PackedSimd.IsSupported) && typeof(TOptimizations) == typeof(IndexOfAnyAsciiSea
 155                {
 0156                    Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0.");
 157
 0158                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 0159                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 0160                        span.Length,
 0161                        ref _inverseAsciiState);
 162                }
 163                else
 164                {
 0165                    Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0
 0166                        "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle.");
 167
 0168                    offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearc
 0169                        ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 0170                        span.Length,
 0171                        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.
 0175                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 176                {
 0177                    return offset;
 178                }
 179
 180                // Fall back to using the ProbabilisticMap.
 0181                span = span.Slice(0, offset + 1);
 182            }
 183
 0184            return ProbabilisticMap.LastIndexOfAny<SearchValues.TrueConst>(
 0185                ref MemoryMarshal.GetReference(span),
 0186                span.Length,
 0187                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.
 0194            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
 0197                int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, Sear
 0198                    ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)),
 0199                    span.Length,
 0200                    ref _asciiState);
 201
 202                // If we've reached the end of the span or stopped at an ASCII character, we've found the result.
 0203                if ((uint)offset >= (uint)span.Length || char.IsAscii(span[offset]))
 204                {
 0205                    return offset;
 206                }
 207
 208                // Fall back to a simple char-by-char search.
 0209                span = span.Slice(0, offset + 1);
 210            }
 211
 0212            return ProbabilisticMapState.LastIndexOfAnySimpleLoop<SearchValues.TrueConst, IndexOfAnyAsciiSearcher.Negate
 0213                ref MemoryMarshal.GetReference(span),
 0214                span.Length,
 0215                ref _map);
 216        }
 217    }
 218}
 219