未验证 提交 ada0379e 编写于 作者: S Stephen Toub 提交者: GitHub

Switch JsonReaderHelper.IndexOfQuoteOrAnyControlOrBackSlash to use IndexOfAnyValues (#82789)

* Switch JsonReaderHelper.IndexOfQuoteOrAnyControlOrBackSlash to use IndexOfAnyValues

* Remove aggressive inlining
上级 6ef2d07f
......@@ -348,11 +348,14 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\StringSyntaxAttribute.cs" />
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\RequiresDynamicCodeAttribute.cs" />
<Compile Include="System\Text\Json\Reader\JsonReaderHelper.sn.cs" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">
<Compile Include="System\Text\Json\Reader\JsonReaderHelper.sri.cs" />
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<Compile Include="System\Text\Json\Reader\JsonReaderHelper.netstandard.cs" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<Compile Include="System\Text\Json\Reader\JsonReaderHelper.net8.cs" />
</ItemGroup>
<!-- Application tfms (.NETCoreApp, .NETFramework) need to use the same or higher version of .NETStandard's dependencies. -->
......
......@@ -77,22 +77,6 @@ internal static JsonValueKind ToValueKind(this JsonTokenType tokenType)
// Otherwise, return false.
public static bool IsHexDigit(byte nextByte) => HexConverter.IsHexChar(nextByte);
// https://tools.ietf.org/html/rfc8259
// Does the span contain '"', '\', or any control characters (i.e. 0 to 31)
// IndexOfAny(34, 92, < 32)
// Borrowed and modified from SpanHelpers.Byte:
// https://github.com/dotnet/corefx/blob/fc169cddedb6820aaabbdb8b7bece2a3df0fd1a5/src/Common/src/CoreLib/System/SpanHelpers.Byte.cs#L473-L604
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span)
{
return IndexOfOrLessThan(
ref MemoryMarshal.GetReference(span),
JsonConstants.Quote,
JsonConstants.BackSlash,
lessThan: 32, // Space ' '
span.Length);
}
public static bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime value)
{
Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers;
using System.Runtime.CompilerServices;
namespace System.Text.Json
{
internal static partial class JsonReaderHelper
{
/// <summary>'"', '\', or any control characters (i.e. 0 to 31).</summary>
/// <remarks>https://tools.ietf.org/html/rfc8259</remarks>
private static readonly IndexOfAnyValues<byte> s_controlQuoteBackslash = IndexOfAnyValues.Create(
// Any Control, < 32 (' ')
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"u8 +
// Quote
"\""u8 +
// Backslash
"\\"u8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span) =>
span.IndexOfAny(s_controlQuoteBackslash);
}
}
......@@ -4,18 +4,31 @@
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.Text.Json
{
internal static partial class JsonReaderHelper
{
private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, byte value1, byte lessThan, int length)
/// <summary>IndexOfAny('"', '\', less than 32)</summary>
/// <remarks>https://tools.ietf.org/html/rfc8259</remarks>
public static unsafe int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span)
{
// Borrowed and modified from SpanHelpers.Byte:
// https://github.com/dotnet/corefx/blob/fc169cddedb6820aaabbdb8b7bece2a3df0fd1a5/src/Common/src/CoreLib/System/SpanHelpers.Byte.cs#L473-L604
ref byte searchSpace = ref MemoryMarshal.GetReference(span);
int length = span.Length;
Debug.Assert(length >= 0);
uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
uint uLessThan = lessThan; // Use uint for comparisons to avoid unnecessary 8->32 extensions
const byte Value0 = JsonConstants.Quote;
const byte Value1 = JsonConstants.BackSlash;
const byte LessThan = JsonConstants.Space;
const uint UValue0 = Value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
const uint UValue1 = Value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
const uint ULessThan = LessThan; // Use uint for comparisons to avoid unnecessary 8->32 extensions
IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
IntPtr nLength = (IntPtr)length;
......@@ -31,28 +44,28 @@ private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, b
nLength -= 8;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found2;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found3;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found4;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found5;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found6;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found7;
index += 8;
......@@ -63,16 +76,16 @@ private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, b
nLength -= 4;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found2;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found3;
index += 4;
......@@ -83,7 +96,7 @@ private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, b
nLength -= 1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
if (UValue0 == lookUp || UValue1 == lookUp || ULessThan > lookUp)
goto Found;
index += 1;
......@@ -94,9 +107,9 @@ private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, b
nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
// Get comparison Vector
Vector<byte> values0 = new Vector<byte>(value0);
Vector<byte> values1 = new Vector<byte>(value1);
Vector<byte> valuesLessThan = new Vector<byte>(lessThan);
Vector<byte> values0 = new Vector<byte>(Value0);
Vector<byte> values1 = new Vector<byte>(Value1);
Vector<byte> valuesLessThan = new Vector<byte>(LessThan);
while ((byte*)nLength > (byte*)index)
{
......
// 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.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.CompilerServices;
namespace System.Text.Json
{
internal static partial class JsonReaderHelper
{
private static unsafe int IndexOfOrLessThan(ref byte searchSpace, byte value0, byte value1, byte lessThan, int length)
{
Debug.Assert(length >= 0);
uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions
uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions
uint uLessThan = lessThan; // Use uint for comparisons to avoid unnecessary 8->32 extensions
IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
IntPtr nLength = (IntPtr)length;
if (!Vector128.IsHardwareAccelerated || length < Vector128<byte>.Count)
{
uint lookUp;
while ((byte*)nLength >= (byte*)8)
{
nLength -= 8;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found2;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found3;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 4);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found4;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 5);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found5;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 6);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found6;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 7);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found7;
index += 8;
}
if ((byte*)nLength >= (byte*)4)
{
nLength -= 4;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 1);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 2);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found2;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index + 3);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found3;
index += 4;
}
while ((byte*)nLength > (byte*)0)
{
nLength -= 1;
lookUp = Unsafe.AddByteOffset(ref searchSpace, index);
if (uValue0 == lookUp || uValue1 == lookUp || uLessThan > lookUp)
goto Found;
index += 1;
}
}
else if (Vector256.IsHardwareAccelerated && length >= Vector256<byte>.Count)
{
// Get comparison Vectors
Vector256<byte> values0 = Vector256.Create(value0);
Vector256<byte> values1 = Vector256.Create(value1);
Vector256<byte> valuesLessThan = Vector256.Create(lessThan);
ref byte currentSearchSpace = ref searchSpace;
ref byte oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256<byte>.Count);
// Loop until either we've finished all elements or there's less than a vector's-worth remaining.
do
{
var vData = Vector256.LoadUnsafe(ref currentSearchSpace);
var vMatches = Vector256.BitwiseOr(
Vector256.BitwiseOr(
Vector256.Equals(vData, values0),
Vector256.Equals(vData, values1)),
Vector256.LessThan(vData, valuesLessThan));
if (vMatches == Vector256<byte>.Zero)
{
currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256<byte>.Count);
continue;
}
return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, vMatches);
}
while (!Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwayFromEnd));
// If any elements remain, process the last vector in the search space.
if ((uint)length % Vector256<byte>.Count != 0)
{
var vData = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd);
var vMatches = Vector256.BitwiseOr(
Vector256.BitwiseOr(
Vector256.Equals(vData, values0),
Vector256.Equals(vData, values1)),
Vector256.LessThan(vData, valuesLessThan));
if (vMatches != Vector256<byte>.Zero)
{
return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, vMatches);
}
}
}
else
{
// Get comparison Vectors
Vector128<byte> values0 = Vector128.Create(value0);
Vector128<byte> values1 = Vector128.Create(value1);
Vector128<byte> valuesLessThan = Vector128.Create(lessThan);
ref byte currentSearchSpace = ref searchSpace;
ref byte oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128<byte>.Count);
// Loop until either we've finished all elements or there's less than a vector's-worth remaining.
do
{
var vData = Vector128.LoadUnsafe(ref currentSearchSpace);
var vMatches = Vector128.BitwiseOr(
Vector128.BitwiseOr(
Vector128.Equals(vData, values0),
Vector128.Equals(vData, values1)),
Vector128.LessThan(vData, valuesLessThan));
if (vMatches == Vector128<byte>.Zero)
{
currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128<byte>.Count);
continue;
}
return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, vMatches);
}
while (!Unsafe.IsAddressGreaterThan(ref currentSearchSpace, ref oneVectorAwayFromEnd));
// If any elements remain, process the last vector in the search space.
if ((uint)length % Vector128<byte>.Count != 0)
{
var vData = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd);
var vMatches = Vector128.BitwiseOr(
Vector128.BitwiseOr(
Vector128.Equals(vData, values0),
Vector128.Equals(vData, values1)),
Vector128.LessThan(vData, valuesLessThan));
if (vMatches != Vector128<byte>.Zero)
{
return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, vMatches);
}
}
}
return -1;
Found: // Workaround for https://github.com/dotnet/runtime/issues/8795
return (int)(byte*)index;
Found1:
return (int)(byte*)(index + 1);
Found2:
return (int)(byte*)(index + 2);
Found3:
return (int)(byte*)(index + 3);
Found4:
return (int)(byte*)(index + 4);
Found5:
return (int)(byte*)(index + 5);
Found6:
return (int)(byte*)(index + 6);
Found7:
return (int)(byte*)(index + 7);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe int ComputeFirstIndex(ref byte searchSpace, ref byte current, Vector256<byte> equals)
{
uint notEqualsElements = equals.ExtractMostSignificantBits();
int index = BitOperations.TrailingZeroCount(notEqualsElements);
return index + (int)(Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(byte));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe int ComputeFirstIndex(ref byte searchSpace, ref byte current, Vector128<byte> equals)
{
uint notEqualsElements = equals.ExtractMostSignificantBits();
int index = BitOperations.TrailingZeroCount(notEqualsElements);
return index + (int)(Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(byte));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册