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

Switch JsonReaderHelper.IndexOfQuoteOrAnyControlOrBackSlash to use IndexOfAnyValues #82789

Merged
merged 3 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 6 additions & 3 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,6 @@ public static bool IsTokenTypePrimitive(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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 +
MihaZupan marked this conversation as resolved.
Show resolved Hide resolved
// Quote
"\""u8 +
// Backslash
"\\"u8);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span) =>
span.IndexOfAny(s_controlQuoteBackslash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down
Loading