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

System.Text.Json: stackalloc constants + misc PR feedback #55350

Merged
merged 4 commits into from
Jul 18, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ internal bool TryGetNamedPropertyValue(int index, ReadOnlySpan<char> propertyNam
int startIndex = index + DbRow.Size;
int endIndex = checked(row.NumberOfRows * DbRow.Size + index);

if (maxBytes < JsonConstants.StackallocThreshold)
if (maxBytes < JsonConstants.StackallocByteThreshold)
{
Span<byte> utf8Name = stackalloc byte[JsonConstants.StackallocThreshold];
Span<byte> utf8Name = stackalloc byte[JsonConstants.StackallocByteThreshold];
int len = JsonReaderHelper.GetUtf8FromText(propertyName, utf8Name);
utf8Name = utf8Name.Slice(0, len);

Expand Down Expand Up @@ -139,7 +139,7 @@ private bool TryGetNamedPropertyValue(
out JsonElement value)
{
ReadOnlySpan<byte> documentSpan = _utf8Json.Span;
Span<byte> utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocThreshold];
Span<byte> utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold];

// Move to the row before the EndObject
int index = endIndex - DbRow.Size;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ internal bool TextEquals(int index, ReadOnlySpan<char> otherText, bool isPropert
byte[]? otherUtf8TextArray = null;

int length = checked(otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);
Span<byte> otherUtf8Text = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[JsonConstants.StackallocThreshold] :
Span<byte> otherUtf8Text = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(otherUtf8TextArray = ArrayPool<byte>.Shared.Rent(length));

ReadOnlySpan<byte> utf16Text = MemoryMarshal.AsBytes(otherText);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ internal static class JsonConstants
public const int MaxWriterDepth = 1_000;
public const int RemoveFlagsBitMask = 0x7FFFFFFF;

public const int StackallocThreshold = 256;
public const int StackallocByteThreshold = 256;
public const int StackallocCharThreshold = StackallocByteThreshold / 2;

// In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped.
// For example: '+' becomes '\u0043'
Expand All @@ -60,7 +61,7 @@ internal static class JsonConstants
public const int MaxExpansionFactorWhileEscaping = 6;

// In the worst case, a single UTF-16 character could be expanded to 3 UTF-8 bytes.
// Only surrogate pairs expand to 4 UTF-8 bytes but that is a transformation of 2 UTF-16 characters goign to 4 UTF-8 bytes (factor of 2).
// Only surrogate pairs expand to 4 UTF-8 bytes but that is a transformation of 2 UTF-16 characters going to 4 UTF-8 bytes (factor of 2).
// All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes.
public const int MaxExpansionFactorWhileTranscoding = 3;

Expand Down Expand Up @@ -95,6 +96,8 @@ internal static class JsonConstants
public const int MinimumDateTimeParseLength = 10; // YYYY-MM-DD
public const int MaximumEscapedDateTimeOffsetParseLength = MaxExpansionFactorWhileEscaping * MaximumDateTimeOffsetParseLength;

public const int MaximumLiteralLength = 5; // Must be able to fit null, true, & false.

// Encoding Helpers
public const char HighSurrogateStart = '\ud800';
public const char HighSurrogateEnd = '\udbff';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public static bool TryParseAsISO(ReadOnlySpan<char> source, out DateTime value)

int maxLength = checked(source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);

Span<byte> bytes = maxLength <= JsonConstants.StackallocThreshold
? stackalloc byte[JsonConstants.StackallocThreshold]
Span<byte> bytes = maxLength <= JsonConstants.StackallocByteThreshold
? stackalloc byte[JsonConstants.StackallocByteThreshold]
: new byte[maxLength];

int length = JsonReaderHelper.GetUtf8FromText(source, bytes);
Expand Down Expand Up @@ -86,8 +86,8 @@ public static bool TryParseAsISO(ReadOnlySpan<char> source, out DateTimeOffset v

int maxLength = checked(source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);

Span<byte> bytes = maxLength <= JsonConstants.StackallocThreshold
? stackalloc byte[JsonConstants.StackallocThreshold]
Span<byte> bytes = maxLength <= JsonConstants.StackallocByteThreshold
? stackalloc byte[JsonConstants.StackallocByteThreshold]
: new byte[maxLength];

int length = JsonReaderHelper.GetUtf8FromText(source, bytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public static byte[] EscapeValue(

int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedValue = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(valueArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);
Expand All @@ -65,8 +65,8 @@ private static byte[] GetEscapedPropertyNameSection(

int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedValue = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(valueArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public static bool TryGetUnescapedBase64Bytes(ReadOnlySpan<byte> utf8Source, int
{
byte[]? unescapedArray = null;

Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocThreshold ?
stackalloc byte[utf8Source.Length] :
Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(unescapedArray = ArrayPool<byte>.Shared.Rent(utf8Source.Length));

Unescape(utf8Source, utf8Unescaped, idx, out int written);
Expand Down Expand Up @@ -44,8 +44,8 @@ public static string GetUnescapedString(ReadOnlySpan<byte> utf8Source, int idx)
int length = utf8Source.Length;
byte[]? pooledName = null;

Span<byte> utf8Unescaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(pooledName = ArrayPool<byte>.Shared.Rent(length));

Unescape(utf8Source, utf8Unescaped, idx, out int written);
Expand All @@ -71,8 +71,8 @@ public static ReadOnlySpan<byte> GetUnescapedSpan(ReadOnlySpan<byte> utf8Source,
int length = utf8Source.Length;
byte[]? pooledName = null;

Span<byte> utf8Unescaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(pooledName = ArrayPool<byte>.Shared.Rent(length));

Unescape(utf8Source, utf8Unescaped, idx, out int written);
Expand All @@ -96,8 +96,8 @@ public static bool UnescapeAndCompare(ReadOnlySpan<byte> utf8Source, ReadOnlySpa

byte[]? unescapedArray = null;

Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocThreshold ?
stackalloc byte[utf8Source.Length] :
Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(unescapedArray = ArrayPool<byte>.Shared.Rent(utf8Source.Length));

Unescape(utf8Source, utf8Unescaped, 0, out int written);
Expand Down Expand Up @@ -127,12 +127,12 @@ public static bool UnescapeAndCompare(ReadOnlySequence<byte> utf8Source, ReadOnl

int length = checked((int)utf8Source.Length);

Span<byte> utf8Unescaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(unescapedArray = ArrayPool<byte>.Shared.Rent(length));

Span<byte> utf8Escaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> utf8Escaped = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(escapedArray = ArrayPool<byte>.Shared.Rent(length));

utf8Source.CopyTo(utf8Escaped);
Expand Down Expand Up @@ -174,8 +174,8 @@ public static bool TryDecodeBase64(ReadOnlySpan<byte> utf8Unescaped, [NotNullWhe
{
byte[]? pooledArray = null;

Span<byte> byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocThreshold ?
stackalloc byte[utf8Unescaped.Length] :
Span<byte> byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(pooledArray = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length));

OperationStatus status = Base64.DecodeFromUtf8(utf8Unescaped, byteSpan, out int bytesConsumed, out int bytesWritten);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public static bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime
Debug.Assert(backslash != -1);

Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
Span<byte> sourceUnescaped = stackalloc byte[source.Length];
Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];

Unescape(source, sourceUnescaped, backslash, out int written);
Debug.Assert(written > 0);
Expand All @@ -277,7 +277,7 @@ public static bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out Da
Debug.Assert(backslash != -1);

Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
Span<byte> sourceUnescaped = stackalloc byte[source.Length];
Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];

Unescape(source, sourceUnescaped, backslash, out int written);
Debug.Assert(written > 0);
Expand All @@ -303,7 +303,7 @@ public static bool TryGetEscapedGuid(ReadOnlySpan<byte> source, out Guid value)
int idx = source.IndexOf(JsonConstants.BackSlash);
Debug.Assert(idx != -1);

Span<byte> utf8Unescaped = stackalloc byte[source.Length];
Span<byte> utf8Unescaped = stackalloc byte[JsonConstants.MaximumEscapedGuidLength];

Unescape(source, utf8Unescaped, idx, out int written);
Debug.Assert(written > 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,9 @@ private bool ConsumeLiteralMultiSegment(ReadOnlySpan<byte> literal, JsonTokenTyp

private bool CheckLiteralMultiSegment(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal, out int consumed)
{
Debug.Assert(span.Length > 0 && span[0] == literal[0]);
Debug.Assert(span.Length > 0 && span[0] == literal[0] && literal.Length <= JsonConstants.MaximumLiteralLength);

Span<byte> readSoFar = stackalloc byte[literal.Length];
Span<byte> readSoFar = stackalloc byte[JsonConstants.MaximumLiteralLength];
int written = 0;

long prevTotalConsumed = _totalConsumed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ public bool ValueTextEquals(ReadOnlySpan<char> text)

int length = checked(text.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);

if (length > JsonConstants.StackallocThreshold)
if (length > JsonConstants.StackallocByteThreshold)
{
otherUtf8TextArray = ArrayPool<byte>.Shared.Rent(length);
otherUtf8Text = otherUtf8TextArray;
Expand All @@ -523,8 +523,8 @@ public bool ValueTextEquals(ReadOnlySpan<char> text)
// Cannot create a span directly since it gets passed to instance methods on a ref struct.
unsafe
{
byte* ptr = stackalloc byte[JsonConstants.StackallocThreshold];
otherUtf8Text = new Span<byte>(ptr, JsonConstants.StackallocThreshold);
byte* ptr = stackalloc byte[JsonConstants.StackallocByteThreshold];
otherUtf8Text = new Span<byte>(ptr, JsonConstants.StackallocByteThreshold);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, Jso
int backslash = source.IndexOf(JsonConstants.BackSlash);
Debug.Assert(backslash != -1);

Span<byte> sourceUnescaped = stackalloc byte[source.Length];
Span<byte> sourceUnescaped = stackalloc byte[MaximumEscapedTimeSpanFormatLength];
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved

JsonReaderHelper.Unescape(source, sourceUnescaped, backslash, out int written);
Debug.Assert(written > 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ private void WriteBase64EscapeProperty(ReadOnlySpan<char> propertyName, ReadOnly

int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);

Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc char[length] :
Span<char> escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ?
stackalloc char[JsonConstants.StackallocCharThreshold] :
(propertyArray = ArrayPool<char>.Shared.Rent(length));

JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand All @@ -162,8 +162,8 @@ private void WriteBase64EscapeProperty(ReadOnlySpan<byte> utf8PropertyName, Read

int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);

Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(propertyArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ private void WriteStringEscapeProperty(ReadOnlySpan<char> propertyName, DateTime

int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);

Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc char[length] :
Span<char> escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ?
stackalloc char[JsonConstants.StackallocCharThreshold] :
(propertyArray = ArrayPool<char>.Shared.Rent(length));

JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand All @@ -167,8 +167,8 @@ private void WriteStringEscapeProperty(ReadOnlySpan<byte> utf8PropertyName, Date

int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);

Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(propertyArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ private void WriteStringEscapeProperty(ReadOnlySpan<char> propertyName, DateTime

int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);

Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc char[length] :
Span<char> escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ?
stackalloc char[JsonConstants.StackallocCharThreshold] :
(propertyArray = ArrayPool<char>.Shared.Rent(length));

JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand All @@ -166,8 +166,8 @@ private void WriteStringEscapeProperty(ReadOnlySpan<byte> utf8PropertyName, Date

int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);

Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(propertyArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ private void WriteNumberEscapeProperty(ReadOnlySpan<char> propertyName, decimal

int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);

Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc char[length] :
Span<char> escapedPropertyName = length <= JsonConstants.StackallocCharThreshold ?
stackalloc char[JsonConstants.StackallocCharThreshold] :
(propertyArray = ArrayPool<char>.Shared.Rent(length));

JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand All @@ -166,8 +166,8 @@ private void WriteNumberEscapeProperty(ReadOnlySpan<byte> utf8PropertyName, deci

int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);

Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
Span<byte> escapedPropertyName = length <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(propertyArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, _options.Encoder, out int written);
Expand Down
Loading