Skip to content

Commit 2b301cc

Browse files
MihaZupansirntar
authored andcommitted
Add a SearchValues implementation for values with unique low nibbles (dotnet#106900)
* Add SearchValues implementation for values with unique low nibbles * More generics * Tweak comment * Remove extra empty line * Update comment
1 parent 2d24698 commit 2b301cc

File tree

9 files changed

+310
-144
lines changed

9 files changed

+310
-144
lines changed

src/libraries/System.Memory/tests/Span/SearchValues.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public static IEnumerable<object[]> Values_MemberData()
3535
"abcd",
3636
"aeio",
3737
"aeiou",
38+
"Aabc",
39+
"Aabcd",
3840
"abceiou",
3941
"123456789",
4042
"123456789123",
@@ -82,6 +84,11 @@ public static IEnumerable<object[]> Values_MemberData()
8284
{
8385
yield return Pair(value);
8486
yield return Pair('a' + value);
87+
yield return Pair('\0' + value);
88+
yield return Pair('\u0001' + value);
89+
yield return Pair('\u00FE' + value);
90+
yield return Pair('\u00FF' + value);
91+
yield return Pair('\uFF00' + value);
8592

8693
// Test some more duplicates
8794
if (value.Length > 0)

src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedIgnoreCaseSearchValues.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
5353
[CompExactlyDependsOn(typeof(AdvSimd))]
5454
[CompExactlyDependsOn(typeof(PackedSimd))]
5555
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
56-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, IndexOfAnyAsciiSearcher.Default>(
56+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, IndexOfAnyAsciiSearcher.Default, SearchValues.FalseConst>(
5757
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
5858

5959
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6060
[CompExactlyDependsOn(typeof(Ssse3))]
6161
[CompExactlyDependsOn(typeof(AdvSimd))]
6262
[CompExactlyDependsOn(typeof(PackedSimd))]
6363
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
64-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.Default>(
64+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.Default, SearchValues.FalseConst>(
6565
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
6666
}
6767
}
Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Runtime.CompilerServices;
56
using System.Runtime.InteropServices;
67
using System.Runtime.Intrinsics.Arm;
78
using System.Runtime.Intrinsics.Wasm;
89
using System.Runtime.Intrinsics.X86;
10+
using System.Text;
911

1012
namespace System.Buffers
1113
{
12-
internal sealed class AsciiByteSearchValues : SearchValues<byte>
14+
internal sealed class AsciiByteSearchValues<TUniqueLowNibble> : SearchValues<byte>
15+
where TUniqueLowNibble : struct, SearchValues.IRuntimeConst
1316
{
1417
private IndexOfAnyAsciiSearcher.AsciiState _state;
1518

16-
public AsciiByteSearchValues(ReadOnlySpan<byte> values) =>
17-
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
19+
public AsciiByteSearchValues(ReadOnlySpan<byte> values)
20+
{
21+
// Despite the name being Ascii, this type may be used with non-ASCII values on ARM.
22+
// See IndexOfAnyAsciiSearcher.CanUseUniqueLowNibbleSearch.
23+
Debug.Assert(Ascii.IsValid(values) || (AdvSimd.IsSupported && TUniqueLowNibble.Value));
24+
25+
if (TUniqueLowNibble.Value)
26+
{
27+
IndexOfAnyAsciiSearcher.ComputeUniqueLowNibbleState(values, out _state);
28+
}
29+
else
30+
{
31+
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
32+
}
33+
}
1834

1935
internal override byte[] GetValues() =>
2036
_state.Lookup.GetByteValues();
@@ -28,47 +44,47 @@ internal override bool ContainsCore(byte value) =>
2844
[CompExactlyDependsOn(typeof(PackedSimd))]
2945
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3046
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
31-
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(
47+
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TUniqueLowNibble>(
3248
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
3349

3450
[CompExactlyDependsOn(typeof(Ssse3))]
3551
[CompExactlyDependsOn(typeof(AdvSimd))]
3652
[CompExactlyDependsOn(typeof(PackedSimd))]
3753
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3854
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
39-
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate>(
55+
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
4056
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
4157

4258
[CompExactlyDependsOn(typeof(Ssse3))]
4359
[CompExactlyDependsOn(typeof(AdvSimd))]
4460
[CompExactlyDependsOn(typeof(PackedSimd))]
4561
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4662
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
47-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(
63+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TUniqueLowNibble>(
4864
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
4965

5066
[CompExactlyDependsOn(typeof(Ssse3))]
5167
[CompExactlyDependsOn(typeof(AdvSimd))]
5268
[CompExactlyDependsOn(typeof(PackedSimd))]
5369
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5470
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
55-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate>(
71+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
5672
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
5773

5874
[CompExactlyDependsOn(typeof(Ssse3))]
5975
[CompExactlyDependsOn(typeof(AdvSimd))]
6076
[CompExactlyDependsOn(typeof(PackedSimd))]
6177
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6278
internal override bool ContainsAny(ReadOnlySpan<byte> span) =>
63-
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate>(
79+
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TUniqueLowNibble>(
6480
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
6581

6682
[CompExactlyDependsOn(typeof(Ssse3))]
6783
[CompExactlyDependsOn(typeof(AdvSimd))]
6884
[CompExactlyDependsOn(typeof(PackedSimd))]
6985
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7086
internal override bool ContainsAnyExcept(ReadOnlySpan<byte> span) =>
71-
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate>(
87+
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
7288
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
7389
}
7490
}
Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,91 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Runtime.CompilerServices;
56
using System.Runtime.InteropServices;
67
using System.Runtime.Intrinsics.Arm;
78
using System.Runtime.Intrinsics.Wasm;
89
using System.Runtime.Intrinsics.X86;
10+
using System.Text;
911

1012
namespace System.Buffers
1113
{
12-
internal sealed class AsciiCharSearchValues<TOptimizations> : SearchValues<char>
14+
internal sealed class AsciiCharSearchValues<TOptimizations, TUniqueLowNibble> : SearchValues<char>
1315
where TOptimizations : struct, IndexOfAnyAsciiSearcher.IOptimizations
16+
where TUniqueLowNibble : struct, SearchValues.IRuntimeConst
1417
{
1518
private IndexOfAnyAsciiSearcher.AsciiState _state;
1619

17-
public AsciiCharSearchValues(ReadOnlySpan<char> values) =>
18-
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
20+
public AsciiCharSearchValues(ReadOnlySpan<char> values)
21+
{
22+
// Despite the name being Ascii, this type may be used with non-ASCII values on ARM.
23+
// See IndexOfAnyAsciiSearcher.CanUseUniqueLowNibbleSearch.
24+
Debug.Assert(Ascii.IsValid(values) || (AdvSimd.IsSupported && TUniqueLowNibble.Value));
25+
26+
if (TUniqueLowNibble.Value)
27+
{
28+
IndexOfAnyAsciiSearcher.ComputeUniqueLowNibbleState(values, out _state);
29+
}
30+
else
31+
{
32+
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
33+
}
34+
}
1935

2036
internal override char[] GetValues() =>
2137
_state.Lookup.GetCharValues();
2238

2339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2440
internal override bool ContainsCore(char value) =>
25-
_state.Lookup.Contains128(value);
41+
_state.Lookup.Contains256(value);
2642

2743
[CompExactlyDependsOn(typeof(Ssse3))]
2844
[CompExactlyDependsOn(typeof(AdvSimd))]
2945
[CompExactlyDependsOn(typeof(PackedSimd))]
3046
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3147
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
32-
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
48+
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
3349
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
3450

3551
[CompExactlyDependsOn(typeof(Ssse3))]
3652
[CompExactlyDependsOn(typeof(AdvSimd))]
3753
[CompExactlyDependsOn(typeof(PackedSimd))]
3854
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3955
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
40-
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
56+
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
4157
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
4258

4359
[CompExactlyDependsOn(typeof(Ssse3))]
4460
[CompExactlyDependsOn(typeof(AdvSimd))]
4561
[CompExactlyDependsOn(typeof(PackedSimd))]
4662
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4763
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
48-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
64+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
4965
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
5066

5167
[CompExactlyDependsOn(typeof(Ssse3))]
5268
[CompExactlyDependsOn(typeof(AdvSimd))]
5369
[CompExactlyDependsOn(typeof(PackedSimd))]
5470
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5571
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
56-
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
72+
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
5773
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
5874

5975
[CompExactlyDependsOn(typeof(Ssse3))]
6076
[CompExactlyDependsOn(typeof(AdvSimd))]
6177
[CompExactlyDependsOn(typeof(PackedSimd))]
6278
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6379
internal override bool ContainsAny(ReadOnlySpan<char> span) =>
64-
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
80+
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
6581
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
6682

6783
[CompExactlyDependsOn(typeof(Ssse3))]
6884
[CompExactlyDependsOn(typeof(AdvSimd))]
6985
[CompExactlyDependsOn(typeof(PackedSimd))]
7086
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7187
internal override bool ContainsAnyExcept(ReadOnlySpan<char> span) =>
72-
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
88+
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
7389
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
7490
}
7591
}

src/libraries/System.Private.CoreLib/src/System/SearchValues/BitVector256.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ public void Set(int c)
3131
_values[offset] |= significantBit;
3232
}
3333

34-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
35-
public readonly bool Contains128(char c) =>
36-
c < 128 && ContainsUnchecked(c);
37-
3834
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3935
public readonly bool Contains256(char c) =>
4036
c < 256 && ContainsUnchecked(c);

0 commit comments

Comments
 (0)