Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a SearchValues implementation for values with unique low nibbles #106900

Merged
merged 5 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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