Skip to content

Commit

Permalink
Avoid string allocation in WriteTo when possible.
Browse files Browse the repository at this point in the history
System.Text.Json.JsonProperty.WriteTo uses get_Name, calling
JsonElement.GetPropertyName() which would allocate a string.

Use ReadOnlySpan<byte> from the underlying UTF8 json, when possible,
by adding helper methods into JsonDocument & JsonElement.

Fix dotnet#88767
  • Loading branch information
karakasa committed Aug 5, 2023
1 parent b5ee6a1 commit ad30f54
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,29 @@ private ReadOnlyMemory<byte> GetPropertyRawValue(int valueIndex)
: JsonReaderHelper.TranscodeHelper(segment);
}

internal ReadOnlySpan<byte> GetUtf8Span(int index, JsonTokenType expectedType)
{
CheckNotDisposed();

DbRow row = _parsedData.Get(index);

JsonTokenType tokenType = row.TokenType;

if (tokenType == JsonTokenType.Null)
{
return default;
}

CheckExpectedType(expectedType, tokenType);

ReadOnlySpan<byte> data = _utf8Json.Span;
ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);

return row.HasComplexChildren
? JsonReaderHelper.GetUnescapedSpan(segment)
: segment;
}

internal bool TextEquals(int index, ReadOnlySpan<char> otherText, bool isPropertyName)
{
CheckNotDisposed();
Expand Down Expand Up @@ -363,6 +386,12 @@ internal string GetNameOfPropertyValue(int index)
return GetString(index - DbRow.Size, JsonTokenType.PropertyName)!;
}

internal ReadOnlySpan<byte> GetNameOfPropertyValueAsUtf8Span(int index)
{
// The property name is one row before the property value
return GetUtf8Span(index - DbRow.Size, JsonTokenType.PropertyName)!;
}

internal bool TryGetValue(int index, [NotNullWhen(true)] out byte[]? value)
{
CheckNotDisposed();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,13 @@ internal string GetPropertyName()
return _parent.GetNameOfPropertyValue(_idx);
}

internal ReadOnlySpan<byte> GetPropertyNameAsUtf8Span()
{
CheckValidInstance();

return _parent.GetNameOfPropertyValueAsUtf8Span(_idx);
}

/// <summary>
/// Gets the original input data backing this value, returning it as a <see cref="string"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,15 @@ public void WriteTo(Utf8JsonWriter writer)
ThrowHelper.ThrowArgumentNullException(nameof(writer));
}

writer.WritePropertyName(Name);
if (_name is null)
{
writer.WritePropertyName(Value.GetPropertyNameAsUtf8Span());
}
else
{
writer.WritePropertyName(_name);
}

Value.WriteTo(writer);
}

Expand Down

0 comments on commit ad30f54

Please sign in to comment.