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

Improve throughput / allocations of JsonNode.GetPath #92284

Merged
merged 3 commits into from
Oct 3, 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
2 changes: 1 addition & 1 deletion src/libraries/Common/src/System/Text/ValueStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public unsafe void Append(char* value, int length)
_pos += length;
}

public void Append(ReadOnlySpan<char> value)
public void Append(scoped ReadOnlySpan<char> value)
{
int pos = _pos;
if (pos > _chars.Length - value.Length)
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET

<ItemGroup>
<Compile Include="$(CommonPath)System\HexConverter.cs" Link="Common\System\HexConverter.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs" Link="Common\ValueStringBuilder.cs" />
<Compile Include="$(CommonPath)System\Text\Json\PooledByteBufferWriter.cs" Link="Common\System\Text\Json\PooledByteBufferWriter.cs" />
<Compile Include="..\Common\JsonCamelCaseNamingPolicy.cs" Link="Common\System\Text\Json\JsonCamelCaseNamingPolicy.cs" />
<Compile Include="..\Common\JsonNamingPolicy.cs" Link="Common\System\Text\Json\JsonNamingPolicy.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ internal static partial class JsonConstants

public const int MaximumFormatBooleanLength = 5;
public const int MaximumFormatInt64Length = 20; // 19 + sign (i.e. -9223372036854775808)
public const int MaximumFormatUInt32Length = 10; // i.e. 4294967295
public const int MaximumFormatUInt64Length = 20; // i.e. 18446744073709551615
public const int MaximumFormatDoubleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
public const int MaximumFormatSingleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,26 @@ internal void SetItem(int index, JsonNode? value)
List[index] = value;
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Parent?.GetPath(ref path, this);

if (child != null)
{
int index = List.IndexOf(child);
path.Add($"[{index}]");
Debug.Assert(index >= 0);

path.Append('[');
#if NETCOREAPP
Span<char> chars = stackalloc char[JsonConstants.MaximumFormatUInt32Length];
bool formatted = ((uint)index).TryFormat(chars, out int charsWritten);
Debug.Assert(formatted);
path.Append(chars.Slice(0, charsWritten));
#else
path.Append(index.ToString());
#endif
path.Append(']');
}

Parent?.GetPath(path, this);
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,13 @@ public string GetPath()
return "$";
}

var path = new List<string>();
GetPath(path, null);

var sb = new StringBuilder("$");
for (int i = path.Count - 1; i >= 0; i--)
{
sb.Append(path[i]);
}

return sb.ToString();
var path = new ValueStringBuilder(stackalloc char[JsonConstants.StackallocCharThreshold]);
path.Append('$');
GetPath(ref path, null);
return path.ToString();
}

internal abstract void GetPath(List<string> path, JsonNode? child);
internal abstract void GetPath(ref ValueStringBuilder path, JsonNode? child);

/// <summary>
/// Gets the root <see cref="JsonNode"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,22 +196,25 @@ internal override bool DeepEqualsCore(JsonNode? node)
return null;
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Parent?.GetPath(ref path, this);

if (child != null)
{
string propertyName = Dictionary.FindValue(child)!.Value.Key;
if (propertyName.AsSpan().ContainsSpecialCharacters())
{
path.Add($"['{propertyName}']");
path.Append("['");
path.Append(propertyName);
path.Append("']");
}
else
{
path.Add($".{propertyName}");
path.Append('.');
path.Append(propertyName);
}
}

Parent?.GetPath(path, this);
}

internal void SetItem(string propertyName, JsonNode? value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ private protected JsonValue(JsonNodeOptions? options = null) : base(options) { }
return new JsonValueCustomized<T>(value, jsonTypeInfo, options);
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Debug.Assert(child == null);

Parent?.GetPath(path, this);
Parent?.GetPath(ref path, this);
}

/// <summary>
Expand Down