Skip to content

Commit

Permalink
Add a SearchValues implementation for values with unique low nibbles (d…
Browse files Browse the repository at this point in the history
…otnet#106900)

* Add SearchValues implementation for values with unique low nibbles

* More generics

* Tweak comment

* Remove extra empty line

* Update comment
  • Loading branch information
MihaZupan authored and jtschuster committed Sep 17, 2024
1 parent 4924fc2 commit 057be76
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 144 deletions.
7 changes: 7 additions & 0 deletions src/libraries/System.Memory/tests/Span/SearchValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public static IEnumerable<object[]> Values_MemberData()
"abcd",
"aeio",
"aeiou",
"Aabc",
"Aabcd",
"abceiou",
"123456789",
"123456789123",
Expand Down Expand Up @@ -82,6 +84,11 @@ public static IEnumerable<object[]> Values_MemberData()
{
yield return Pair(value);
yield return Pair('a' + value);
yield return Pair('\0' + value);
yield return Pair('\u0001' + value);
yield return Pair('\u00FE' + value);
yield return Pair('\u00FF' + value);
yield return Pair('\uFF00' + value);

// Test some more duplicates
if (value.Length > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, IndexOfAnyAsciiSearcher.Default>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, IndexOfAnyAsciiSearcher.Default, SearchValues.FalseConst>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.Default>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, IndexOfAnyAsciiSearcher.Default, SearchValues.FalseConst>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.Wasm;
using System.Runtime.Intrinsics.X86;
using System.Text;

namespace System.Buffers
{
internal sealed class AsciiByteSearchValues : SearchValues<byte>
internal sealed class AsciiByteSearchValues<TUniqueLowNibble> : SearchValues<byte>
where TUniqueLowNibble : struct, SearchValues.IRuntimeConst
{
private IndexOfAnyAsciiSearcher.AsciiState _state;

public AsciiByteSearchValues(ReadOnlySpan<byte> values) =>
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
public AsciiByteSearchValues(ReadOnlySpan<byte> values)
{
// Despite the name being Ascii, this type may be used with non-ASCII values on ARM.
// See IndexOfAnyAsciiSearcher.CanUseUniqueLowNibbleSearch.
Debug.Assert(Ascii.IsValid(values) || (AdvSimd.IsSupported && TUniqueLowNibble.Value));

if (TUniqueLowNibble.Value)
{
IndexOfAnyAsciiSearcher.ComputeUniqueLowNibbleState(values, out _state);
}
else
{
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
}
}

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

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<byte> span) =>
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate>(
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
ref MemoryMarshal.GetReference(span), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<byte> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TUniqueLowNibble>(
ref MemoryMarshal.GetReference(span), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<byte> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
ref MemoryMarshal.GetReference(span), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsAny(ReadOnlySpan<byte> span) =>
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate>(
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TUniqueLowNibble>(
ref MemoryMarshal.GetReference(span), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsAnyExcept(ReadOnlySpan<byte> span) =>
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate>(
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TUniqueLowNibble>(
ref MemoryMarshal.GetReference(span), span.Length, ref _state);
}
}
Original file line number Diff line number Diff line change
@@ -1,75 +1,91 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.Wasm;
using System.Runtime.Intrinsics.X86;
using System.Text;

namespace System.Buffers
{
internal sealed class AsciiCharSearchValues<TOptimizations> : SearchValues<char>
internal sealed class AsciiCharSearchValues<TOptimizations, TUniqueLowNibble> : SearchValues<char>
where TOptimizations : struct, IndexOfAnyAsciiSearcher.IOptimizations
where TUniqueLowNibble : struct, SearchValues.IRuntimeConst
{
private IndexOfAnyAsciiSearcher.AsciiState _state;

public AsciiCharSearchValues(ReadOnlySpan<char> values) =>
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
public AsciiCharSearchValues(ReadOnlySpan<char> values)
{
// Despite the name being Ascii, this type may be used with non-ASCII values on ARM.
// See IndexOfAnyAsciiSearcher.CanUseUniqueLowNibbleSearch.
Debug.Assert(Ascii.IsValid(values) || (AdvSimd.IsSupported && TUniqueLowNibble.Value));

if (TUniqueLowNibble.Value)
{
IndexOfAnyAsciiSearcher.ComputeUniqueLowNibbleState(values, out _state);
}
else
{
IndexOfAnyAsciiSearcher.ComputeAsciiState(values, out _state);
}
}

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsCore(char value) =>
_state.Lookup.Contains128(value);
_state.Lookup.Contains256(value);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAnyExcept(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
IndexOfAnyAsciiSearcher.IndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAny(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int LastIndexOfAnyExcept(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
IndexOfAnyAsciiSearcher.LastIndexOfAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsAny(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations>(
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.DontNegate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);

[CompExactlyDependsOn(typeof(Ssse3))]
[CompExactlyDependsOn(typeof(AdvSimd))]
[CompExactlyDependsOn(typeof(PackedSimd))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override bool ContainsAnyExcept(ReadOnlySpan<char> span) =>
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations>(
IndexOfAnyAsciiSearcher.ContainsAny<IndexOfAnyAsciiSearcher.Negate, TOptimizations, TUniqueLowNibble>(
ref Unsafe.As<char, short>(ref MemoryMarshal.GetReference(span)), span.Length, ref _state);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ public void Set(int c)
_values[offset] |= significantBit;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Contains128(char c) =>
c < 128 && ContainsUnchecked(c);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Contains256(char c) =>
c < 256 && ContainsUnchecked(c);
Expand Down
Loading

0 comments on commit 057be76

Please sign in to comment.