Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ public void WritePropertyName(System.ReadOnlySpan<byte> utf8PropertyName) { }
public void WritePropertyName(System.ReadOnlySpan<char> propertyName) { }
public void WritePropertyName(string propertyName) { }
public void WritePropertyName(System.Text.Json.JsonEncodedText propertyName) { }
public void WriteRawValue(string json, bool skipInputValidation = false) { }
public void WriteRawValue(System.ReadOnlySpan<byte> utf8Json, bool skipInputValidation = false) { }
public void WriteRawValue(System.ReadOnlySpan<char> json, bool skipInputValidation = false) { }
public void WriteStartArray() { }
public void WriteStartArray(System.ReadOnlySpan<byte> utf8PropertyName) { }
public void WriteStartArray(System.ReadOnlySpan<char> propertyName) { }
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 @@ -257,6 +257,7 @@
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.Guid.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.Helpers.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.Literal.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.Raw.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.SignedNumber.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.String.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.UnsignedNumber.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ internal static class JsonConstants
// All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes.
public const int MaxExpansionFactorWhileTranscoding = 3;

// When transcoding from UTF8 -> UTF16, the byte count threshold where we rent from the array pool before performing a normal alloc.
public const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;

public const int MaxEscapedTokenSize = 1_000_000_000; // Max size for already escaped value.
public const int MaxUnescapedTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 bytes
public const int MaxBase64ValueTokenSize = (MaxEscapedTokenSize >> 2) * 3 / MaxExpansionFactorWhileEscaping; // 125_000_000 bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,10 @@ public static partial class JsonSerializer

private static TValue? ReadUsingMetadata<TValue>(ReadOnlySpan<char> json, JsonTypeInfo jsonTypeInfo)
{
const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024;

byte[]? tempArray = null;

// For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
Span<byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
Span<byte> utf8 = json.Length <= (JsonConstants.ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
// Use a pooled alloc.
tempArray = ArrayPool<byte>.Shared.Rent(json.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) :
// Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// 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;

namespace System.Text.Json
{
public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the input as JSON content.
/// </summary>
/// <param name="json">The raw JSON content to write.</param>
/// <param name="skipInputValidation">Whether to skip validation of the input JSON content.</param>
public void WriteRawValue(string json, bool skipInputValidation = false)
{
if (json == null)
{
throw new ArgumentNullException(nameof(json));
}

WriteRawValue(json.AsSpan(), skipInputValidation);
}

/// <summary>
/// Writes the input as JSON content.
/// </summary>
/// <param name="json">The raw JSON content to write.</param>
/// <param name="skipInputValidation">Whether to skip validation of the input JSON content.</param>
public void WriteRawValue(ReadOnlySpan<char> json, bool skipInputValidation = false)
{
byte[]? tempArray = null;

// For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
Span<byte> utf8Json = json.Length <= (JsonConstants.ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ?
// Use a pooled alloc.
tempArray = ArrayPool<byte>.Shared.Rent(json.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) :
// Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation)
// and by using a normal alloc we can avoid the Clear().
new byte[JsonReaderHelper.GetUtf8ByteCount(json)];

try
{
int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8Json);
utf8Json = utf8Json.Slice(0, actualByteCount);
WriteRawValue(utf8Json, skipInputValidation);
}
finally
{
if (tempArray != null)
{
utf8Json.Clear();
ArrayPool<byte>.Shared.Return(tempArray);
}
}
}

/// <summary>
/// Writes the input as JSON content.
/// </summary>
/// <param name="utf8Json">The raw JSON content to write.</param>
/// <param name="skipInputValidation">Whether to skip validation of the input JSON content.</param>
public void WriteRawValue(ReadOnlySpan<byte> utf8Json, bool skipInputValidation = false)
{
if (utf8Json.Length == 0)
{
ThrowHelper.ThrowArgumentException(SR.ExpectedJsonTokens);
}

if (!skipInputValidation)
{
Utf8JsonReader reader = new Utf8JsonReader(utf8Json);

try
{
while (reader.Read());
}
catch (JsonReaderException ex)
{
ThrowHelper.ThrowArgumentException(ex.Message);
}
}

int maxRequired = utf8Json.Length + 1; // Optionally, 1 list separator

if (_memory.Length - BytesPending < maxRequired)
{
Grow(maxRequired);
}

Span<byte> output = _memory.Span;

if (_currentDepth < 0)
{
output[BytesPending++] = JsonConstants.ListSeparator;
}

utf8Json.CopyTo(output.Slice(BytesPending));
BytesPending += utf8Json.Length;

SetFlagToAddListSeparatorBeforeNextItem();

// Treat all raw JSON value writes as string.
_tokenType = JsonTokenType.String;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\src\System\Text\Json\BitStack.cs" Link="BitStack.cs" />
<Compile Include="Utf8JsonWriterTests.WriteRaw.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)'">
<Compile Include="$(CommonPath)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
Expand Down
Loading