diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
index ea0c1280f3e95e..f14bab0f7c58b5 100644
--- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj
+++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
@@ -348,11 +348,14 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
-
-
-
+
+
+
+
+
+
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs
index 0486afb939ff3b..141d23f5b45999 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs
@@ -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 span)
- {
- return IndexOfOrLessThan(
- ref MemoryMarshal.GetReference(span),
- JsonConstants.Quote,
- JsonConstants.BackSlash,
- lessThan: 32, // Space ' '
- span.Length);
- }
-
public static bool TryGetEscapedDateTime(ReadOnlySpan source, out DateTime value)
{
Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.net8.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.net8.cs
new file mode 100644
index 00000000000000..6aebc26faf2728
--- /dev/null
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.net8.cs
@@ -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
+ {
+ /// '"', '\', or any control characters (i.e. 0 to 31).
+ /// https://tools.ietf.org/html/rfc8259
+ private static readonly IndexOfAnyValues 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 span) =>
+ span.IndexOfAny(s_controlQuoteBackslash);
+ }
+}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sn.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.netstandard.cs
similarity index 75%
rename from src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sn.cs
rename to src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.netstandard.cs
index 59728a4c01b1ce..1db19a3d70adec 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sn.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.netstandard.cs
@@ -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)
+ /// IndexOfAny('"', '\', less than 32)
+ /// https://tools.ietf.org/html/rfc8259
+ public static unsafe int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan 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.Count - 1));
// Get comparison Vector
- Vector values0 = new Vector(value0);
- Vector values1 = new Vector(value1);
- Vector valuesLessThan = new Vector(lessThan);
+ Vector values0 = new Vector(Value0);
+ Vector values1 = new Vector(Value1);
+ Vector valuesLessThan = new Vector(LessThan);
while ((byte*)nLength > (byte*)index)
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sri.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sri.cs
deleted file mode 100644
index ed6abe7ecd6739..00000000000000
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.sri.cs
+++ /dev/null
@@ -1,216 +0,0 @@
-// 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.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.Count)
- {
- // Get comparison Vectors
- Vector256 values0 = Vector256.Create(value0);
- Vector256 values1 = Vector256.Create(value1);
- Vector256 valuesLessThan = Vector256.Create(lessThan);
-
- ref byte currentSearchSpace = ref searchSpace;
- ref byte oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector256.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.Zero)
- {
- currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256.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.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.Zero)
- {
- return ComputeFirstIndex(ref searchSpace, ref oneVectorAwayFromEnd, vMatches);
- }
- }
- }
- else
- {
- // Get comparison Vectors
- Vector128 values0 = Vector128.Create(value0);
- Vector128 values1 = Vector128.Create(value1);
- Vector128 valuesLessThan = Vector128.Create(lessThan);
-
- ref byte currentSearchSpace = ref searchSpace;
- ref byte oneVectorAwayFromEnd = ref Unsafe.Add(ref searchSpace, length - Vector128.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.Zero)
- {
- currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128.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.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.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 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 equals)
- {
- uint notEqualsElements = equals.ExtractMostSignificantBits();
- int index = BitOperations.TrailingZeroCount(notEqualsElements);
- return index + (int)(Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(byte));
- }
- }
-}