Skip to content
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: 2 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 @@ -587,7 +587,9 @@ public void Flush() { }
public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public void Reset() { }
public void Reset(System.Buffers.IBufferWriter<byte> bufferWriter) { }
public void Reset(System.Buffers.IBufferWriter<byte> bufferWriter, System.Text.Json.JsonWriterOptions options) { }
public void Reset(System.IO.Stream utf8Json) { }
public void Reset(System.IO.Stream utf8Json, System.Text.Json.JsonWriterOptions options) { }
public void WriteBase64String(System.ReadOnlySpan<byte> utf8PropertyName, System.ReadOnlySpan<byte> bytes) { }
public void WriteBase64String(System.ReadOnlySpan<char> propertyName, System.ReadOnlySpan<byte> bytes) { }
public void WriteBase64String(string propertyName, System.ReadOnlySpan<byte> bytes) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ public void Reset()
/// <exception cref="ArgumentNullException">
/// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the instance of <see cref="Stream" /> that is passed in does not support writing.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The instance of <see cref="Utf8JsonWriter"/> has been disposed.
/// </exception>
Expand All @@ -297,9 +300,14 @@ public void Reset(Stream utf8Json)
CheckNotDisposed();

if (utf8Json == null)
{
throw new ArgumentNullException(nameof(utf8Json));
}

if (!utf8Json.CanWrite)
{
throw new ArgumentException(SR.StreamNotWritable);
}

_stream = utf8Json;
if (_arrayBufferWriter == null)
Expand All @@ -310,11 +318,32 @@ public void Reset(Stream utf8Json)
{
_arrayBufferWriter.Clear();
}
_output = null;

_output = null;
ResetHelper();
}

/// <summary>
/// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="Stream" />
/// and the specified <see cref="JsonWriterOptions"/>.
/// </summary>
/// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param>
/// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the instance of <see cref="Stream" /> that is passed in does not support writing.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The instance of <see cref="Utf8JsonWriter"/> has been disposed.
/// </exception>
public void Reset(Stream utf8Json, JsonWriterOptions options)
{
Reset(utf8Json);
SetOptions(options);
}
Comment thread
eiriktsarpalis marked this conversation as resolved.

/// <summary>
/// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="IBufferWriter{Byte}" />.
/// </summary>
Expand All @@ -340,6 +369,24 @@ public void Reset(IBufferWriter<byte> bufferWriter)
ResetHelper();
}

/// <summary>
/// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="IBufferWriter{Byte}" />
/// and the specified <see cref="JsonWriterOptions"/>.
/// </summary>
/// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
/// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The instance of <see cref="Utf8JsonWriter"/> has been disposed.
/// </exception>
public void Reset(IBufferWriter<byte> bufferWriter, JsonWriterOptions options)
{
Reset(bufferWriter);
SetOptions(options);
}

internal void ResetAllStateForCacheReuse()
{
ResetHelper();
Expand All @@ -349,7 +396,7 @@ internal void ResetAllStateForCacheReuse()
_output = null;
}

internal void Reset(IBufferWriter<byte> bufferWriter, JsonWriterOptions options)
internal void ConfigureForCacheReuse(IBufferWriter<byte> bufferWriter, JsonWriterOptions options)
{
Debug.Assert(_output is null && _stream is null && _arrayBufferWriter is null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static Utf8JsonWriter RentWriterAndBuffer(JsonWriterOptions options, int
writer = state.Writer;

bufferWriter.InitializeEmptyInstance(defaultBufferSize);
writer.Reset(bufferWriter, options);
writer.ConfigureForCacheReuse(bufferWriter, options);
}
else
{
Expand All @@ -50,7 +50,7 @@ public static Utf8JsonWriter RentWriter(JsonSerializerOptions options, IBufferWr
{
// First JsonSerializer call in the stack -- initialize & return the cached instance.
writer = state.Writer;
writer.Reset(bufferWriter, options.GetWriterOptions());
writer.ConfigureForCacheReuse(bufferWriter, options.GetWriterOptions());
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1180,18 +1180,190 @@ public void InvalidReset(JsonWriterOptions options)

Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((Stream)null));
Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((IBufferWriter<byte>)null));
Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((Stream)null, options));
Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((IBufferWriter<byte>)null, options));

stream.Dispose();

Assert.Throws<ArgumentException>(() => writeToStream.Reset(stream));
Assert.Throws<ArgumentException>(() => writeToStream.Reset(stream, options));

var output = new FixedSizedBufferWriter(256);
using var writeToIBW = new Utf8JsonWriter(output, options);

Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((Stream)null));
Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((IBufferWriter<byte>)null));
Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((Stream)null, options));
Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((IBufferWriter<byte>)null, options));

Assert.Throws<ArgumentException>(() => writeToIBW.Reset(stream));
Assert.Throws<ArgumentException>(() => writeToIBW.Reset(stream, options));
}

[Fact]
public static void JsonSerializerCacheNotPoisonedByDisposedWriter()
{
var options = new JsonSerializerOptions();
options.Converters.Add(new DisposingInt32Converter());

Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(1, options));
Assert.Equal("2", JsonSerializer.Serialize(2));
}

[Theory]
[MemberData(nameof(JsonOptions_TestData))]
public void ResetWithNewOptions(JsonWriterOptions options)
{
var newOptions = new JsonWriterOptions
{
Indented = !options.Indented,
SkipValidation = !options.SkipValidation,
MaxDepth = 500,
IndentCharacter = '\t',
IndentSize = 4,
};

var stream = new MemoryStream();
using var writeToStream = new Utf8JsonWriter(stream, options);
writeToStream.WriteNumberValue(1);
writeToStream.Flush();

Assert.True(writeToStream.BytesCommitted != 0);

writeToStream.Reset(stream, newOptions);
Assert.Equal(0, writeToStream.BytesCommitted);
Assert.Equal(0, writeToStream.BytesPending);
Assert.Equal(0, writeToStream.CurrentDepth);
Assert.Equal(newOptions.Indented, writeToStream.Options.Indented);
Assert.Equal(newOptions.SkipValidation, writeToStream.Options.SkipValidation);
Assert.Equal(500, writeToStream.Options.MaxDepth);
Assert.Equal('\t', writeToStream.Options.IndentCharacter);
Assert.Equal(4, writeToStream.Options.IndentSize);

long previousWritten = stream.Position;
writeToStream.Flush();
Assert.Equal(previousWritten, stream.Position);

writeToStream.WriteNumberValue(1);
writeToStream.Flush();

Assert.NotEqual(previousWritten, stream.Position);

var output = new FixedSizedBufferWriter(257);
using var writeToIBW = new Utf8JsonWriter(output, options);
writeToIBW.WriteNumberValue(1);
writeToIBW.Flush();

Assert.True(writeToIBW.BytesCommitted != 0);

writeToIBW.Reset(output, newOptions);
Assert.Equal(0, writeToIBW.BytesCommitted);
Assert.Equal(0, writeToIBW.BytesPending);
Assert.Equal(0, writeToIBW.CurrentDepth);
Assert.Equal(newOptions.Indented, writeToIBW.Options.Indented);
Assert.Equal(newOptions.SkipValidation, writeToIBW.Options.SkipValidation);
Assert.Equal(500, writeToIBW.Options.MaxDepth);
Assert.Equal('\t', writeToIBW.Options.IndentCharacter);
Assert.Equal(4, writeToIBW.Options.IndentSize);

previousWritten = output.FormattedCount;
writeToIBW.Flush();
Assert.Equal(previousWritten, output.FormattedCount);

writeToIBW.WriteNumberValue(1);
writeToIBW.Flush();

Assert.NotEqual(previousWritten, output.FormattedCount);
}

private sealed class DisposingInt32Converter : System.Text.Json.Serialization.JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
throw new NotSupportedException();

public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
writer.Dispose();
throw new InvalidOperationException();
}
}

[Theory]
[MemberData(nameof(JsonOptions_TestData))]
public void ResetChangeOutputModeWithNewOptions(JsonWriterOptions options)
{
var newOptions = new JsonWriterOptions
{
Indented = !options.Indented,
SkipValidation = !options.SkipValidation,
};

var stream = new MemoryStream();
using var writeToStream = new Utf8JsonWriter(stream, options);
writeToStream.WriteNumberValue(1);
writeToStream.Flush();

Assert.True(writeToStream.BytesCommitted != 0);

var output = new FixedSizedBufferWriter(256);
writeToStream.Reset(output, newOptions);
Assert.Equal(0, writeToStream.BytesCommitted);
Assert.Equal(0, writeToStream.BytesPending);
Assert.Equal(0, writeToStream.CurrentDepth);
Assert.Equal(newOptions.Indented, writeToStream.Options.Indented);
Assert.Equal(newOptions.SkipValidation, writeToStream.Options.SkipValidation);

writeToStream.WriteNumberValue(1);
writeToStream.Flush();
Assert.True(writeToStream.BytesCommitted != 0);
Assert.True(output.FormattedCount != 0);

output = new FixedSizedBufferWriter(256);
using var writeToIBW = new Utf8JsonWriter(output, options);
writeToIBW.WriteNumberValue(1);
writeToIBW.Flush();

Assert.True(writeToIBW.BytesCommitted != 0);

stream = new MemoryStream();
writeToIBW.Reset(stream, newOptions);
Assert.Equal(0, writeToIBW.BytesCommitted);
Assert.Equal(0, writeToIBW.BytesPending);
Assert.Equal(0, writeToIBW.CurrentDepth);
Assert.Equal(newOptions.Indented, writeToIBW.Options.Indented);
Assert.Equal(newOptions.SkipValidation, writeToIBW.Options.SkipValidation);

writeToIBW.WriteNumberValue(1);
writeToIBW.Flush();
Assert.True(writeToIBW.BytesCommitted != 0);
Assert.True(stream.Position != 0);
}

[Fact]
public void ResetWithNewOptions_ProducesExpectedOutput()
{
var output = new ArrayBufferWriter<byte>();
using var writer = new Utf8JsonWriter(output, new JsonWriterOptions { Indented = false });

writer.WriteStartObject();
writer.WriteNumber("x", 1);
writer.WriteEndObject();
writer.Flush();

string compact = Encoding.UTF8.GetString(output.WrittenSpan.ToArray());
Assert.Equal("""{"x":1}""", compact);

output.Clear();
writer.Reset(output, new JsonWriterOptions { Indented = true });

writer.WriteStartObject();
writer.WriteNumber("x", 1);
writer.WriteEndObject();
writer.Flush();

string indented = Encoding.UTF8.GetString(output.WrittenSpan.ToArray());
Assert.Contains("\n", indented);
Assert.Contains("\"x\": 1", indented);
}

[Theory]
Expand Down Expand Up @@ -1381,6 +1553,7 @@ public void UseAfterDisposeInvalid(JsonWriterOptions options)

var stream = new MemoryStream();
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream));
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream, options));

using var writeToStream = new Utf8JsonWriter(stream, options);
writeToStream.WriteStartObject();
Expand All @@ -1398,6 +1571,7 @@ public void UseAfterDisposeInvalid(JsonWriterOptions options)
Assert.Throws<ObjectDisposedException>(() => writeToStream.Reset());

Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output));
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output, options));
}

[Theory]
Expand All @@ -1423,6 +1597,7 @@ public async Task UseAfterDisposeInvalidAsync(JsonWriterOptions options)

var stream = new MemoryStream();
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream));
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream, options));

using var writeToStream = new Utf8JsonWriter(stream, options);
writeToStream.WriteStartObject();
Expand All @@ -1440,6 +1615,7 @@ public async Task UseAfterDisposeInvalidAsync(JsonWriterOptions options)
Assert.Throws<ObjectDisposedException>(() => writeToStream.Reset());

Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output));
Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output, options));
}

[Theory]
Expand Down