--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written.
+ /// </summary>
+#if USE_ABW_INTERNALLY
+ internal
+#else
+ public
+#endif
+ sealed class ArrayBufferWriter<T> : IBufferWriter<T>
+ {
+ private T[] _buffer;
+ private int _index;
+
+ private const int MinimumBufferSize = 256;
+
+ /// <summary>
+ /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
+ /// with the default initial capacity.
+ /// </summary>
+ public ArrayBufferWriter()
+ {
+ _buffer = new T[MinimumBufferSize];
+ _index = 0;
+ }
+
+ /// <summary>
+ /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
+ /// with an initial capacity specified.
+ /// </summary>
+ /// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
+ /// <exception cref="ArgumentException">
+ /// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0).
+ /// </exception>
+ public ArrayBufferWriter(int initialCapacity)
+ {
+ if (initialCapacity <= 0)
+ throw new ArgumentException(nameof(initialCapacity));
+
+ _buffer = new T[initialCapacity];
+ _index = 0;
+ }
+
+ /// <summary>
+ /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>.
+ /// </summary>
+ public ReadOnlyMemory<T> WrittenMemory => _buffer.AsMemory(0, _index);
+
+ /// <summary>
+ /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
+ /// </summary>
+ public ReadOnlySpan<T> WrittenSpan => _buffer.AsSpan(0, _index);
+
+ /// <summary>
+ /// Returns the amount of data written to the underlying buffer so far.
+ /// </summary>
+ public int WrittenCount => _index;
+
+ /// <summary>
+ /// Returns the total amount of space within the underlying buffer.
+ /// </summary>
+ public int Capacity => _buffer.Length;
+
+ /// <summary>
+ /// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow.
+ /// </summary>
+ public int FreeCapacity => _buffer.Length - _index;
+
+ /// <summary>
+ /// Clears the data written to the underlying buffer.
+ /// </summary>
+ /// <remarks>
+ /// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it.
+ /// </remarks>
+ public void Clear()
+ {
+ Debug.Assert(_buffer.Length >= _index);
+ _buffer.AsSpan(0, _index).Clear();
+ _index = 0;
+ }
+
+ /// <summary>
+ /// Notifies <see cref="IBufferWriter{T}"/> that <paramref name="count"/> amount of data was written to the output <see cref="Span{T}"/>/<see cref="Memory{T}"/>
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when <paramref name="count"/> is negative.
+ /// </exception>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when attempting to advance past the end of the underlying buffer.
+ /// </exception>
+ /// <remarks>
+ /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+ /// </remarks>
+ public void Advance(int count)
+ {
+ if (count < 0)
+ throw new ArgumentException(nameof(count));
+
+ if (_index > _buffer.Length - count)
+ ThrowInvalidOperationException(_buffer.Length);
+
+ _index += count;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
+ /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when <paramref name="sizeHint"/> is negative.
+ /// </exception>
+ /// <remarks>
+ /// This will never return an empty <see cref="Memory{T}"/>.
+ /// </remarks>
+ /// <remarks>
+ /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
+ /// </remarks>
+ /// <remarks>
+ /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+ /// </remarks>
+ public Memory<T> GetMemory(int sizeHint = 0)
+ {
+ CheckAndResizeBuffer(sizeHint);
+ Debug.Assert(_buffer.Length > _index);
+ return _buffer.AsMemory(_index);
+ }
+
+ /// <summary>
+ /// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
+ /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when <paramref name="sizeHint"/> is negative.
+ /// </exception>
+ /// <remarks>
+ /// This will never return an empty <see cref="Span{T}"/>.
+ /// </remarks>
+ /// <remarks>
+ /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
+ /// </remarks>
+ /// <remarks>
+ /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
+ /// </remarks>
+ public Span<T> GetSpan(int sizeHint = 0)
+ {
+ CheckAndResizeBuffer(sizeHint);
+ Debug.Assert(_buffer.Length > _index);
+ return _buffer.AsSpan(_index);
+ }
+
+ private void CheckAndResizeBuffer(int sizeHint)
+ {
+ if (sizeHint < 0)
+ throw new ArgumentException(nameof(sizeHint));
+
+ if (sizeHint == 0)
+ {
+ sizeHint = MinimumBufferSize;
+ }
+
+ if (sizeHint > FreeCapacity)
+ {
+ int growBy = Math.Max(sizeHint, _buffer.Length);
+
+ int newSize = checked(_buffer.Length + growBy);
+
+ Array.Resize(ref _buffer, newSize);
+ }
+
+ Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint);
+ }
+
+ private static void ThrowInvalidOperationException(int capacity)
+ {
+ throw new InvalidOperationException(SR.Format(SR.BufferWriterAdvancedTooFar, capacity));
+ }
+ }
+}
}
namespace System.Buffers
{
+ public sealed partial class ArrayBufferWriter<T> : System.Buffers.IBufferWriter<T>
+ {
+ public ArrayBufferWriter() { }
+ public ArrayBufferWriter(int initialCapacity) { }
+ public int Capacity { get { throw null; } }
+ public int FreeCapacity { get { throw null; } }
+ public int WrittenCount { get { throw null; } }
+ public System.ReadOnlyMemory<T> WrittenMemory { get { throw null; } }
+ public System.ReadOnlySpan<T> WrittenSpan { get { throw null; } }
+ public void Advance(int count) { }
+ public void Clear() { }
+ public System.Memory<T> GetMemory(int sizeHint = 0) { throw null; }
+ public System.Span<T> GetSpan(int sizeHint = 0) { throw null; }
+ }
public static partial class BuffersExtensions
{
public static void CopyTo<T>(this in System.Buffers.ReadOnlySequence<T> source, System.Span<T> destination) { }
<data name="UnexpectedSegmentType" xml:space="preserve">
<value>Unexpected segment type.</value>
</data>
+ <data name="BufferWriterAdvancedTooFar" xml:space="preserve">
+ <value>Cannot advance past the end of the buffer, which has a size of {0}.</value>
+ </data>
</root>
<Compile Include="$(CommonPath)\CoreLib\System\Numerics\Hashing\HashHelpers.cs">
<Link>Common\System\Collections\HashHelpers.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Buffers\ArrayBufferWriter.cs">
+ <Link>Common\System\Buffers\ArrayBufferWriter.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup>
<ReferenceFromRuntime Include="System.Private.CoreLib" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Buffers.Tests
+{
+ public static partial class ArrayBufferWriterTests_Byte
+ {
+ [Fact]
+ public static void ArrayBufferWriter_Ctor()
+ {
+ {
+ var output = new ArrayBufferWriter<byte>();
+ Assert.True(output.FreeCapacity > 0);
+ Assert.True(output.Capacity > 0);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>(200);
+ Assert.True(output.FreeCapacity >= 200);
+ Assert.True(output.Capacity >= 200);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ ArrayBufferWriter<byte> output = default;
+ Assert.Equal(null, output);
+ }
+ }
+
+ [Fact]
+ public static void Invalid_Ctor()
+ {
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<byte>(0));
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<byte>(-1));
+ Assert.Throws<OutOfMemoryException>(() => new ArrayBufferWriter<byte>(int.MaxValue));
+ }
+
+ [Fact]
+ public static void Clear()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ int previousAvailable = output.FreeCapacity;
+ WriteData(output, 2);
+ Assert.True(output.FreeCapacity < previousAvailable);
+ Assert.True(output.WrittenCount > 0);
+ Assert.False(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.False(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ output.Clear();
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+
+ [Fact]
+ public static void Advance()
+ {
+ {
+ var output = new ArrayBufferWriter<byte>();
+ int capacity = output.Capacity;
+ Assert.Equal(capacity, output.FreeCapacity);
+ output.Advance(output.FreeCapacity);
+ Assert.Equal(capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>();
+ output.Advance(output.Capacity);
+ Assert.Equal(output.Capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ int previousCapacity = output.Capacity;
+ Span<byte> _ = output.GetSpan();
+ Assert.True(output.Capacity > previousCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 2);
+ ReadOnlyMemory<byte> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<byte> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(10);
+ Assert.False(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.False(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+ }
+
+ [Fact]
+ public static void AdvanceZero()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 2);
+ Assert.Equal(2, output.WrittenCount);
+ ReadOnlyMemory<byte> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<byte> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(0);
+ Assert.Equal(2, output.WrittenCount);
+ Assert.True(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ [Fact]
+ public static void InvalidAdvance()
+ {
+ {
+ var output = new ArrayBufferWriter<byte>();
+ Assert.Throws<ArgumentException>(() => output.Advance(-1));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.Capacity + 1));
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 100);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.FreeCapacity + 1));
+ }
+ }
+
+ public static bool IsX64 { get; } = IntPtr.Size >= 8;
+
+ [ConditionalFact(nameof(IsX64))]
+ [OuterLoop]
+ public static void InvalidAdvance_Large()
+ {
+ try
+ {
+ {
+ var output = new ArrayBufferWriter<byte>(2_000_000_000);
+ WriteData(output, 1_000);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(int.MaxValue));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(2_000_000_000 - 1_000 + 1));
+ }
+ }
+ catch (OutOfMemoryException) { }
+ }
+
+ [Fact]
+ public static void GetMemoryAndSpan()
+ {
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 2);
+ Span<byte> span = output.GetSpan();
+ Memory<byte> memory = output.GetMemory();
+ Span<byte> memorySpan = memory.Span;
+ Assert.True(span.Length > 0);
+ Assert.True(memorySpan.Length > 0);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 2);
+ ReadOnlyMemory<byte> writtenSoFarMemory = output.WrittenMemory;
+ ReadOnlySpan<byte> writtenSoFar = output.WrittenSpan;
+ Assert.True(writtenSoFarMemory.Span.SequenceEqual(writtenSoFar));
+ int previousAvailable = output.FreeCapacity;
+ Span<byte> span = output.GetSpan(500);
+ Assert.True(span.Length >= 500);
+ Assert.True(output.FreeCapacity >= 500);
+ Assert.True(output.FreeCapacity > previousAvailable);
+
+ Assert.Equal(writtenSoFar.Length, output.WrittenCount);
+ Assert.False(writtenSoFar.SequenceEqual(span.Slice(0, output.WrittenCount)));
+
+ Memory<byte> memory = output.GetMemory();
+ Span<byte> memorySpan = memory.Span;
+ Assert.True(span.Length >= 500);
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+
+ memory = output.GetMemory(500);
+ memorySpan = memory.Span;
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < memorySpan.Length; i++)
+ {
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+ }
+
+ [Fact]
+ public static void GetSpanShouldAtleastDoubleWhenGrowing()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 100);
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ _ = output.GetSpan(previousAvailable + 1);
+ Assert.True(output.FreeCapacity >= previousAvailable * 2);
+ }
+
+ [Fact]
+ public static void GetSpanOnlyGrowsAboveThreshold()
+ {
+ {
+ var output = new ArrayBufferWriter<byte>();
+ int previousAvailable = output.FreeCapacity;
+
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<byte>();
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ previousAvailable = output.FreeCapacity;
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+ }
+
+ [Fact]
+ public static void InvalidGetMemoryAndSpan()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 2);
+ Assert.Throws<ArgumentException>(() => output.GetSpan(-1));
+ Assert.Throws<ArgumentException>(() => output.GetMemory(-1));
+ }
+
+ [Fact]
+ public static void MultipleCallsToGetSpan()
+ {
+ var output = new ArrayBufferWriter<byte>(300);
+ int previousAvailable = output.FreeCapacity;
+ Assert.True(previousAvailable >= 300);
+ Assert.True(output.Capacity >= 300);
+ Assert.Equal(previousAvailable, output.Capacity);
+ Span<byte> span = output.GetSpan();
+ Assert.True(span.Length >= previousAvailable);
+ Assert.True(span.Length >= 256);
+ Span<byte> newSpan = output.GetSpan();
+ Assert.Equal(span.Length, newSpan.Length);
+
+ unsafe
+ {
+ void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
+ void* pNewSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(newSpan));
+ Assert.Equal((IntPtr)pSpan, (IntPtr)pNewSpan);
+ }
+
+ Assert.Equal(span.Length, output.GetSpan().Length);
+ }
+
+ [Fact]
+ public static void WriteAndCopyToStream()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 100);
+ using (var memStream = new MemoryStream(100))
+ {
+ Assert.Equal(100, output.WrittenCount);
+
+ ReadOnlySpan<byte> outputSpan = output.WrittenMemory.ToArray();
+
+ ReadOnlyMemory<byte> transientMemory = output.WrittenMemory;
+ ReadOnlySpan<byte> transientSpan = output.WrittenSpan;
+
+ Assert.True(transientSpan.SequenceEqual(transientMemory.Span));
+
+ Assert.True(transientSpan[0] != 0);
+
+ memStream.Write(transientSpan.ToArray(), 0, transientSpan.Length);
+ output.Clear();
+
+ Assert.True(transientSpan[0] == 0);
+ Assert.True(transientMemory.Span[0] == 0);
+
+ Assert.Equal(0, output.WrittenCount);
+ byte[] streamOutput = memStream.ToArray();
+
+ Assert.True(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+
+ Assert.Equal(outputSpan.Length, streamOutput.Length);
+ Assert.True(outputSpan.SequenceEqual(streamOutput));
+ }
+ }
+
+ [Fact]
+ public static async Task WriteAndCopyToStreamAsync()
+ {
+ var output = new ArrayBufferWriter<byte>();
+ WriteData(output, 100);
+ using (var memStream = new MemoryStream(100))
+ {
+ Assert.Equal(100, output.WrittenCount);
+
+ ReadOnlyMemory<byte> outputMemory = output.WrittenMemory.ToArray();
+
+ ReadOnlyMemory<byte> transient = output.WrittenMemory;
+
+ Assert.True(transient.Span[0] != 0);
+
+ await memStream.WriteAsync(transient.ToArray(), 0, transient.Length);
+ output.Clear();
+
+ Assert.True(transient.Span[0] == 0);
+
+ Assert.Equal(0, output.WrittenCount);
+ byte[] streamOutput = memStream.ToArray();
+
+ Assert.True(ReadOnlyMemory<byte>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(ReadOnlySpan<byte>.Empty.SequenceEqual(output.WrittenMemory.Span));
+
+ Assert.Equal(outputMemory.Length, streamOutput.Length);
+ Assert.True(outputMemory.Span.SequenceEqual(streamOutput));
+ }
+ }
+
+ private static void WriteData(IBufferWriter<byte> bufferWriter, int numBytes)
+ {
+ Span<byte> outputSpan = bufferWriter.GetSpan(numBytes);
+ Debug.Assert(outputSpan.Length >= numBytes);
+ var random = new Random(42);
+
+ var data = new byte[numBytes];
+ random.NextBytes(data);
+ data.CopyTo(outputSpan);
+
+ bufferWriter.Advance(numBytes);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace System.Buffers.Tests
+{
+ public static partial class ArrayBufferWriterTests_Char
+ {
+ [Fact]
+ public static void ArrayBufferWriter_Ctor()
+ {
+ {
+ var output = new ArrayBufferWriter<char>();
+ Assert.True(output.FreeCapacity > 0);
+ Assert.True(output.Capacity > 0);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<char>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<char>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>(200);
+ Assert.True(output.FreeCapacity >= 200);
+ Assert.True(output.Capacity >= 200);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<char>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<char>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ ArrayBufferWriter<char> output = default;
+ Assert.Equal(null, output);
+ }
+ }
+
+ [Fact]
+ public static void Invalid_Ctor()
+ {
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<char>(0));
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<char>(-1));
+ Assert.Throws<OutOfMemoryException>(() => new ArrayBufferWriter<char>(int.MaxValue));
+ }
+
+ [Fact]
+ public static void Clear()
+ {
+ var output = new ArrayBufferWriter<char>();
+ int previousAvailable = output.FreeCapacity;
+ WriteData(output, 2);
+ Assert.True(output.FreeCapacity < previousAvailable);
+ Assert.True(output.WrittenCount > 0);
+ Assert.False(ReadOnlySpan<char>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.False(ReadOnlyMemory<char>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ output.Clear();
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<char>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<char>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+
+ [Fact]
+ public static void Advance()
+ {
+ {
+ var output = new ArrayBufferWriter<char>();
+ int capacity = output.Capacity;
+ Assert.Equal(capacity, output.FreeCapacity);
+ output.Advance(output.FreeCapacity);
+ Assert.Equal(capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>();
+ output.Advance(output.Capacity);
+ Assert.Equal(output.Capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ int previousCapacity = output.Capacity;
+ _ = output.GetSpan();
+ Assert.True(output.Capacity > previousCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 2);
+ ReadOnlyMemory<char> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<char> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(10);
+ Assert.False(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.False(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+ }
+
+ [Fact]
+ public static void AdvanceZero()
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 2);
+ Assert.Equal(2, output.WrittenCount);
+ ReadOnlyMemory<char> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<char> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(0);
+ Assert.Equal(2, output.WrittenCount);
+ Assert.True(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ [Fact]
+ public static void InvalidAdvance()
+ {
+ {
+ var output = new ArrayBufferWriter<char>();
+ Assert.Throws<ArgumentException>(() => output.Advance(-1));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.Capacity + 1));
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 100);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.FreeCapacity + 1));
+ }
+ }
+
+ public static bool IsX64 { get; } = IntPtr.Size >= 8;
+
+ [ConditionalFact(nameof(IsX64))]
+ [OuterLoop]
+ public static void InvalidAdvance_Large()
+ {
+ try
+ {
+ {
+ var output = new ArrayBufferWriter<char>(2_000_000_000);
+ WriteData(output, 1_000);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(int.MaxValue));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(2_000_000_000 - 1_000 + 1));
+ }
+ }
+ catch (OutOfMemoryException) { }
+ }
+
+ [Fact]
+ public static void GetMemoryAndSpan()
+ {
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 2);
+ Span<char> span = output.GetSpan();
+ Memory<char> memory = output.GetMemory();
+ Span<char> memorySpan = memory.Span;
+ Assert.True(span.Length > 0);
+ Assert.True(memorySpan.Length > 0);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 2);
+ ReadOnlyMemory<char> writtenSoFarMemory = output.WrittenMemory;
+ ReadOnlySpan<char> writtenSoFar = output.WrittenSpan;
+ Assert.True(writtenSoFarMemory.Span.SequenceEqual(writtenSoFar));
+ int previousAvailable = output.FreeCapacity;
+ Span<char> span = output.GetSpan(500);
+ Assert.True(span.Length >= 500);
+ Assert.True(output.FreeCapacity >= 500);
+ Assert.True(output.FreeCapacity > previousAvailable);
+
+ Assert.Equal(writtenSoFar.Length, output.WrittenCount);
+ Assert.False(writtenSoFar.SequenceEqual(span.Slice(0, output.WrittenCount)));
+
+ Memory<char> memory = output.GetMemory();
+ Span<char> memorySpan = memory.Span;
+ Assert.True(span.Length >= 500);
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+
+ memory = output.GetMemory(500);
+ memorySpan = memory.Span;
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < memorySpan.Length; i++)
+ {
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+ }
+
+ [Fact]
+ public static void GetSpanShouldAtleastDoubleWhenGrowing()
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 100);
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ _ = output.GetSpan(previousAvailable + 1);
+ Assert.True(output.FreeCapacity >= previousAvailable * 2);
+ }
+
+ [Fact]
+ public static void GetSpanOnlyGrowsAboveThreshold()
+ {
+ {
+ var output = new ArrayBufferWriter<char>();
+ int previousAvailable = output.FreeCapacity;
+
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<char>();
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ previousAvailable = output.FreeCapacity;
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+ }
+
+ [Fact]
+ public static void InvalidGetMemoryAndSpan()
+ {
+ var output = new ArrayBufferWriter<char>();
+ WriteData(output, 2);
+ Assert.Throws<ArgumentException>(() => output.GetSpan(-1));
+ Assert.Throws<ArgumentException>(() => output.GetMemory(-1));
+ }
+
+ [Fact]
+ public static void MultipleCallsToGetSpan()
+ {
+ var output = new ArrayBufferWriter<char>(300);
+ int previousAvailable = output.FreeCapacity;
+ Assert.True(previousAvailable >= 300);
+ Assert.True(output.Capacity >= 300);
+ Assert.Equal(previousAvailable, output.Capacity);
+ Span<char> span = output.GetSpan();
+ Assert.True(span.Length >= previousAvailable);
+ Assert.True(span.Length >= 256);
+ Span<char> newSpan = output.GetSpan();
+ Assert.Equal(span.Length, newSpan.Length);
+
+ unsafe
+ {
+ void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
+ void* pNewSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(newSpan));
+ Assert.Equal((IntPtr)pSpan, (IntPtr)pNewSpan);
+ }
+
+ Assert.Equal(span.Length, output.GetSpan().Length);
+ }
+
+ private static void WriteData(IBufferWriter<char> bufferWriter, int numChars)
+ {
+ Span<char> outputSpan = bufferWriter.GetSpan(numChars);
+ Debug.Assert(outputSpan.Length >= numChars);
+ var random = new Random(42);
+
+ var data = new char[numChars];
+
+ for (int i = 0; i < numChars; i++)
+ {
+ data[i] = (char)random.Next(0, char.MaxValue);
+ }
+
+ data.CopyTo(outputSpan);
+
+ bufferWriter.Advance(numChars);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Xunit;
+
+namespace System.Buffers.Tests
+{
+ public static partial class ArrayBufferWriterTests_String
+ {
+ [Fact]
+ public static void ArrayBufferWriter_Ctor()
+ {
+ {
+ var output = new ArrayBufferWriter<string>();
+ Assert.True(output.FreeCapacity > 0);
+ Assert.True(output.Capacity > 0);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<string>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<string>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>(200);
+ Assert.True(output.FreeCapacity >= 200);
+ Assert.True(output.Capacity >= 200);
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<string>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<string>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ {
+ ArrayBufferWriter<string> output = default;
+ Assert.Equal(null, output);
+ }
+ }
+
+ [Fact]
+ public static void Invalid_Ctor()
+ {
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<string>(0));
+ Assert.Throws<ArgumentException>(() => new ArrayBufferWriter<string>(-1));
+ Assert.Throws<OutOfMemoryException>(() => new ArrayBufferWriter<string>(int.MaxValue));
+ }
+
+ [Fact]
+ public static void Clear()
+ {
+ var output = new ArrayBufferWriter<string>();
+ int previousAvailable = output.FreeCapacity;
+ WriteData(output, 2);
+ Assert.True(output.FreeCapacity < previousAvailable);
+ Assert.True(output.WrittenCount > 0);
+ Assert.False(ReadOnlySpan<string>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.False(ReadOnlyMemory<string>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ output.Clear();
+ Assert.Equal(0, output.WrittenCount);
+ Assert.True(ReadOnlySpan<string>.Empty.SequenceEqual(output.WrittenSpan));
+ Assert.True(ReadOnlyMemory<string>.Empty.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+
+ [Fact]
+ public static void Advance()
+ {
+ {
+ var output = new ArrayBufferWriter<string>();
+ int capacity = output.Capacity;
+ Assert.Equal(capacity, output.FreeCapacity);
+ output.Advance(output.FreeCapacity);
+ Assert.Equal(capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>();
+ output.Advance(output.Capacity);
+ Assert.Equal(output.Capacity, output.WrittenCount);
+ Assert.Equal(0, output.FreeCapacity);
+ int previousCapacity = output.Capacity;
+ _ = output.GetSpan();
+ Assert.True(output.Capacity > previousCapacity);
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 2);
+ ReadOnlyMemory<string> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<string> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(10);
+ Assert.False(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.False(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+ }
+
+ [Fact]
+ public static void AdvanceZero()
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 2);
+ Assert.Equal(2, output.WrittenCount);
+ ReadOnlyMemory<string> previousMemory = output.WrittenMemory;
+ ReadOnlySpan<string> previousSpan = output.WrittenSpan;
+ Assert.True(previousSpan.SequenceEqual(previousMemory.Span));
+ output.Advance(0);
+ Assert.Equal(2, output.WrittenCount);
+ Assert.True(previousMemory.Span.SequenceEqual(output.WrittenMemory.Span));
+ Assert.True(previousSpan.SequenceEqual(output.WrittenSpan));
+ Assert.True(output.WrittenSpan.SequenceEqual(output.WrittenMemory.Span));
+ }
+
+ [Fact]
+ public static void InvalidAdvance()
+ {
+ {
+ var output = new ArrayBufferWriter<string>();
+ Assert.Throws<ArgumentException>(() => output.Advance(-1));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.Capacity + 1));
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 100);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(output.FreeCapacity + 1));
+ }
+ }
+
+ public static bool IsX64 { get; } = IntPtr.Size >= 8;
+
+ [ConditionalFact(nameof(IsX64))]
+ [OuterLoop]
+ public static void InvalidAdvance_Large()
+ {
+ try
+ {
+ {
+ var output = new ArrayBufferWriter<string>(2_000_000_000);
+ WriteData(output, 1_000);
+ Assert.Throws<InvalidOperationException>(() => output.Advance(int.MaxValue));
+ Assert.Throws<InvalidOperationException>(() => output.Advance(2_000_000_000 - 1_000 + 1));
+ }
+ }
+ catch (OutOfMemoryException) { }
+ }
+
+ [Fact]
+ public static void GetMemoryAndSpan()
+ {
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 2);
+ Span<string> span = output.GetSpan();
+ Memory<string> memory = output.GetMemory();
+ Span<string> memorySpan = memory.Span;
+ Assert.True(span.Length > 0);
+ Assert.True(memorySpan.Length > 0);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 2);
+ ReadOnlyMemory<string> writtenSoFarMemory = output.WrittenMemory;
+ ReadOnlySpan<string> writtenSoFar = output.WrittenSpan;
+ Assert.True(writtenSoFarMemory.Span.SequenceEqual(writtenSoFar));
+ int previousAvailable = output.FreeCapacity;
+ Span<string> span = output.GetSpan(500);
+ Assert.True(span.Length >= 500);
+ Assert.True(output.FreeCapacity >= 500);
+ Assert.True(output.FreeCapacity > previousAvailable);
+
+ Assert.Equal(writtenSoFar.Length, output.WrittenCount);
+ Assert.False(writtenSoFar.SequenceEqual(span.Slice(0, output.WrittenCount)));
+
+ Memory<string> memory = output.GetMemory();
+ Span<string> memorySpan = memory.Span;
+ Assert.True(span.Length >= 500);
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.Equal(default, span[i]);
+ Assert.Equal(default, memorySpan[i]);
+ }
+
+ memory = output.GetMemory(500);
+ memorySpan = memory.Span;
+ Assert.True(memorySpan.Length >= 500);
+ Assert.Equal(span.Length, memorySpan.Length);
+ for (int i = 0; i < memorySpan.Length; i++)
+ {
+ Assert.Equal(default, memorySpan[i]);
+ }
+ }
+ }
+
+ [Fact]
+ public static void GetSpanShouldAtleastDoubleWhenGrowing()
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 100);
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ _ = output.GetSpan(previousAvailable + 1);
+ Assert.True(output.FreeCapacity >= previousAvailable * 2);
+ }
+
+ [Fact]
+ public static void GetSpanOnlyGrowsAboveThreshold()
+ {
+ {
+ var output = new ArrayBufferWriter<string>();
+ int previousAvailable = output.FreeCapacity;
+
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+
+ {
+ var output = new ArrayBufferWriter<string>();
+ int previousAvailable = output.FreeCapacity;
+
+ _ = output.GetSpan(previousAvailable);
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+
+ previousAvailable = output.FreeCapacity;
+ for (int i = 0; i < 10; i++)
+ {
+ _ = output.GetSpan();
+ Assert.Equal(previousAvailable, output.FreeCapacity);
+ }
+ }
+ }
+
+ [Fact]
+ public static void InvalidGetMemoryAndSpan()
+ {
+ var output = new ArrayBufferWriter<string>();
+ WriteData(output, 2);
+ Assert.Throws<ArgumentException>(() => output.GetSpan(-1));
+ Assert.Throws<ArgumentException>(() => output.GetMemory(-1));
+ }
+
+ [Fact]
+ public static void MultipleCallsToGetSpan()
+ {
+ var output = new ArrayBufferWriter<string>(300);
+ int previousAvailable = output.FreeCapacity;
+ Assert.True(previousAvailable >= 300);
+ Assert.True(output.Capacity >= 300);
+ Assert.Equal(previousAvailable, output.Capacity);
+ Span<string> span = output.GetSpan();
+ Assert.True(span.Length >= previousAvailable);
+ Assert.True(span.Length >= 256);
+ Span<string> newSpan = output.GetSpan();
+ Assert.Equal(span.Length, newSpan.Length);
+
+ unsafe
+ {
+ void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
+ void* pNewSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(newSpan));
+ Assert.Equal((IntPtr)pSpan, (IntPtr)pNewSpan);
+ }
+
+ Assert.Equal(span.Length, output.GetSpan().Length);
+ }
+
+ private static void WriteData(IBufferWriter<string> bufferWriter, int numStrings)
+ {
+ Span<string> outputSpan = bufferWriter.GetSpan(numStrings);
+ Debug.Assert(outputSpan.Length >= numStrings);
+ var random = new Random(42);
+
+ var data = new string[numStrings];
+
+ for (int i = 0; i < numStrings; i++)
+ {
+ int length = random.Next(5, 10);
+ data[i] = GetRandomString(random, length, 32, 127);
+ }
+
+ data.CopyTo(outputSpan);
+
+ bufferWriter.Advance(numStrings);
+ }
+
+ private static string GetRandomString(Random r, int length, int minCodePoint, int maxCodePoint)
+ {
+ StringBuilder sb = new StringBuilder(length);
+ while (length-- != 0)
+ {
+ sb.Append((char)r.Next(minCodePoint, maxCodePoint));
+ }
+ return sb.ToString();
+ }
+ }
+}
</PropertyGroup>
<ItemGroup Condition="'$(IncludePartialFacadeTests)' == 'true'">
<!-- Tests specific to netcoreapp and uap -->
+ <Compile Include="ArrayBufferWriter\ArrayBufferWriterTests.Byte.cs" />
+ <Compile Include="ArrayBufferWriter\ArrayBufferWriterTests.Char.cs" />
+ <Compile Include="ArrayBufferWriter\ArrayBufferWriterTests.String.cs" />
<Compile Include="MemoryMarshal\AsRef.cs" />
<Compile Include="MemoryMarshal\AsReadOnlyRef.cs" />
<Compile Include="MemoryMarshal\CreateSpan.cs" />
the Common SR.cs into SourcePackageFiles. -->
<Target Name="GetSourcesToPackage" BeforeTargets="ExpandProjectReferences">
<ItemGroup>
- <_ProjectsToBuild Include="../src/System.Text.Json.csproj" UndefineProperties="Configuration" />
+ <_ProjectsToBuild Include="../src/System.Text.Json.csproj" UndefineProperties="Configuration" AdditionalProperties="TargetGroup=netstandard"/>
</ItemGroup>
<MSBuild Projects="@(_ProjectsToBuild)"
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ------------------------------------------------------------------------------
+// Changes to this file must follow the http://aka.ms/api-review process.
+// ------------------------------------------------------------------------------
+
+namespace System.Text.Json
+{
+ public sealed partial class Utf8JsonWriter : System.IAsyncDisposable
+ {
+ public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
+ }
+}
public bool TryGetUInt32(out uint value) { throw null; }
[System.CLSCompliantAttribute(false)]
public bool TryGetUInt64(out ulong value) { throw null; }
- public void WriteAsProperty(System.ReadOnlySpan<byte> utf8PropertyName, ref System.Text.Json.Utf8JsonWriter writer) { }
- public void WriteAsProperty(System.ReadOnlySpan<char> propertyName, ref System.Text.Json.Utf8JsonWriter writer) { }
- public void WriteAsValue(ref System.Text.Json.Utf8JsonWriter writer) { }
+ public void WriteAsProperty(System.ReadOnlySpan<byte> utf8PropertyName, System.Text.Json.Utf8JsonWriter writer) { }
+ public void WriteAsProperty(System.ReadOnlySpan<char> propertyName, System.Text.Json.Utf8JsonWriter writer) { }
+ public void WriteAsValue(System.Text.Json.Utf8JsonWriter writer) { }
public partial struct ArrayEnumerator : System.Collections.Generic.IEnumerable<System.Text.Json.JsonElement>, System.Collections.Generic.IEnumerator<System.Text.Json.JsonElement>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
{
private object _dummy;
public bool Indented { get { throw null; } set { } }
public bool SkipValidation { get { throw null; } set { } }
}
- public partial struct JsonWriterState
- {
- private object _dummy;
- private int _dummyPrimitive;
- public JsonWriterState(System.Text.Json.JsonWriterOptions options = default(System.Text.Json.JsonWriterOptions)) { throw null; }
- public long BytesCommitted { get { throw null; } }
- public long BytesWritten { get { throw null; } }
- public System.Text.Json.JsonWriterOptions Options { get { throw null; } }
- }
public ref partial struct Utf8JsonReader
{
private object _dummy;
[System.CLSCompliantAttribute(false)]
public bool TryGetUInt64(out ulong value) { throw null; }
}
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter : System.IDisposable
{
- private object _dummy;
- private int _dummyPrimitive;
- public Utf8JsonWriter(System.Buffers.IBufferWriter<byte> bufferWriter, System.Text.Json.JsonWriterState state = default(System.Text.Json.JsonWriterState)) { throw null; }
+ public Utf8JsonWriter(System.Buffers.IBufferWriter<byte> bufferWriter, System.Text.Json.JsonWriterOptions options = default(System.Text.Json.JsonWriterOptions)) { }
+ public Utf8JsonWriter(System.IO.Stream utf8Json, System.Text.Json.JsonWriterOptions options = default(System.Text.Json.JsonWriterOptions)) { }
public long BytesCommitted { get { throw null; } }
- public long BytesWritten { get { throw null; } }
+ public int BytesPending { get { throw null; } }
public int CurrentDepth { get { throw null; } }
- public void Flush(bool isFinalBlock = true) { }
- public System.Text.Json.JsonWriterState GetCurrentState() { throw null; }
- public void WriteBoolean(System.ReadOnlySpan<byte> utf8PropertyName, bool value, bool escape = true) { }
- public void WriteBoolean(System.ReadOnlySpan<char> propertyName, bool value, bool escape = true) { }
- public void WriteBoolean(string propertyName, bool value, bool escape = true) { }
+ public System.Text.Json.JsonWriterOptions Options { get { throw null; } }
+ public void Dispose() { }
+ 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.IO.Stream utf8Json) { }
+ public void WriteBoolean(System.ReadOnlySpan<byte> utf8PropertyName, bool value) { }
+ public void WriteBoolean(System.ReadOnlySpan<char> propertyName, bool value) { }
+ public void WriteBoolean(string propertyName, bool value) { }
public void WriteBooleanValue(bool value) { }
- public void WriteCommentValue(System.ReadOnlySpan<byte> utf8Value, bool escape = true) { }
- public void WriteCommentValue(System.ReadOnlySpan<char> value, bool escape = true) { }
- public void WriteCommentValue(string value, bool escape = true) { }
+ public void WriteCommentValue(System.ReadOnlySpan<byte> utf8Value) { }
+ public void WriteCommentValue(System.ReadOnlySpan<char> value) { }
+ public void WriteCommentValue(string value) { }
public void WriteEndArray() { }
public void WriteEndObject() { }
- public void WriteNull(System.ReadOnlySpan<byte> utf8PropertyName, bool escape = true) { }
- public void WriteNull(System.ReadOnlySpan<char> propertyName, bool escape = true) { }
- public void WriteNull(string propertyName, bool escape = true) { }
+ public void WriteNull(System.ReadOnlySpan<byte> utf8PropertyName) { }
+ public void WriteNull(System.ReadOnlySpan<char> propertyName) { }
+ public void WriteNull(string propertyName) { }
public void WriteNullValue() { }
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, decimal value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, double value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, int value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, long value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, float value, bool escape = true) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, decimal value) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, double value) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, int value) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, long value) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, float value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, uint value, bool escape = true) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, uint value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, ulong value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, decimal value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, double value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, int value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, long value, bool escape = true) { }
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, float value, bool escape = true) { }
+ public void WriteNumber(System.ReadOnlySpan<byte> utf8PropertyName, ulong value) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, decimal value) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, double value) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, int value) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, long value) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, float value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, uint value, bool escape = true) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, uint value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(System.ReadOnlySpan<char> propertyName, ulong value, bool escape = true) { }
- public void WriteNumber(string propertyName, decimal value, bool escape = true) { }
- public void WriteNumber(string propertyName, double value, bool escape = true) { }
- public void WriteNumber(string propertyName, int value, bool escape = true) { }
- public void WriteNumber(string propertyName, long value, bool escape = true) { }
- public void WriteNumber(string propertyName, float value, bool escape = true) { }
+ public void WriteNumber(System.ReadOnlySpan<char> propertyName, ulong value) { }
+ public void WriteNumber(string propertyName, decimal value) { }
+ public void WriteNumber(string propertyName, double value) { }
+ public void WriteNumber(string propertyName, int value) { }
+ public void WriteNumber(string propertyName, long value) { }
+ public void WriteNumber(string propertyName, float value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(string propertyName, uint value, bool escape = true) { }
+ public void WriteNumber(string propertyName, uint value) { }
[System.CLSCompliantAttribute(false)]
- public void WriteNumber(string propertyName, ulong value, bool escape = true) { }
+ public void WriteNumber(string propertyName, ulong value) { }
public void WriteNumberValue(decimal value) { }
public void WriteNumberValue(double value) { }
public void WriteNumberValue(int value) { }
[System.CLSCompliantAttribute(false)]
public void WriteNumberValue(ulong value) { }
public void WriteStartArray() { }
- public void WriteStartArray(System.ReadOnlySpan<byte> utf8PropertyName, bool escape = true) { }
- public void WriteStartArray(System.ReadOnlySpan<char> propertyName, bool escape = true) { }
- public void WriteStartArray(string propertyName, bool escape = true) { }
+ public void WriteStartArray(System.ReadOnlySpan<byte> utf8PropertyName) { }
+ public void WriteStartArray(System.ReadOnlySpan<char> propertyName) { }
+ public void WriteStartArray(string propertyName) { }
public void WriteStartObject() { }
- public void WriteStartObject(System.ReadOnlySpan<byte> utf8PropertyName, bool escape = true) { }
- public void WriteStartObject(System.ReadOnlySpan<char> propertyName, bool escape = true) { }
- public void WriteStartObject(string propertyName, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.DateTime value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.DateTimeOffset value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.Guid value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.ReadOnlySpan<byte> utf8Value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.ReadOnlySpan<char> value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, string value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, System.DateTime value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, System.DateTimeOffset value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, System.Guid value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, System.ReadOnlySpan<byte> utf8Value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, System.ReadOnlySpan<char> value, bool escape = true) { }
- public void WriteString(System.ReadOnlySpan<char> propertyName, string value, bool escape = true) { }
- public void WriteString(string propertyName, System.DateTime value, bool escape = true) { }
- public void WriteString(string propertyName, System.DateTimeOffset value, bool escape = true) { }
- public void WriteString(string propertyName, System.Guid value, bool escape = true) { }
- public void WriteString(string propertyName, System.ReadOnlySpan<byte> utf8Value, bool escape = true) { }
- public void WriteString(string propertyName, System.ReadOnlySpan<char> value, bool escape = true) { }
- public void WriteString(string propertyName, string value, bool escape = true) { }
+ public void WriteStartObject(System.ReadOnlySpan<byte> utf8PropertyName) { }
+ public void WriteStartObject(System.ReadOnlySpan<char> propertyName) { }
+ public void WriteStartObject(string propertyName) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.DateTime value) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.DateTimeOffset value) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.Guid value) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.ReadOnlySpan<byte> utf8Value) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, System.ReadOnlySpan<char> value) { }
+ public void WriteString(System.ReadOnlySpan<byte> utf8PropertyName, string value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, System.DateTime value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, System.DateTimeOffset value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, System.Guid value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, System.ReadOnlySpan<byte> utf8Value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, System.ReadOnlySpan<char> value) { }
+ public void WriteString(System.ReadOnlySpan<char> propertyName, string value) { }
+ public void WriteString(string propertyName, System.DateTime value) { }
+ public void WriteString(string propertyName, System.DateTimeOffset value) { }
+ public void WriteString(string propertyName, System.Guid value) { }
+ public void WriteString(string propertyName, System.ReadOnlySpan<byte> utf8Value) { }
+ public void WriteString(string propertyName, System.ReadOnlySpan<char> value) { }
+ public void WriteString(string propertyName, string value) { }
public void WriteStringValue(System.DateTime value) { }
public void WriteStringValue(System.DateTimeOffset value) { }
public void WriteStringValue(System.Guid value) { }
- public void WriteStringValue(System.ReadOnlySpan<byte> utf8Value, bool escape = true) { }
- public void WriteStringValue(System.ReadOnlySpan<char> value, bool escape = true) { }
- public void WriteStringValue(string value, bool escape = true) { }
+ public void WriteStringValue(System.ReadOnlySpan<byte> utf8Value) { }
+ public void WriteStringValue(System.ReadOnlySpan<char> value) { }
+ public void WriteStringValue(string value) { }
}
}
namespace System.Text.Json.Serialization
<Compile Include="System.Text.Json.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' != 'netstandard'">
+ <Compile Include="System.Text.Json.Manual.cs" />
<ProjectReference Include="..\..\System.Memory\ref\System.Memory.csproj" />
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
</ItemGroup>
<data name="FailedToGetLargerSpan" xml:space="preserve">
<value>The 'IBufferWriter' could not provide an output buffer that is large enough to continue writing.</value>
</data>
- <data name="FailedToGetMinimumSizeSpan" xml:space="preserve">
- <value>The 'IBufferWriter' could not provide an output buffer that is large enough to continue writing. Need at least {0} bytes.</value>
- </data>
<data name="FoundInvalidCharacter" xml:space="preserve">
<value>'{0}' is invalid after a value. Expected either ',', '}}', or ']'.</value>
</data>
<data name="SerializerOptionsImmutable" xml:space="preserve">
<value>Serializer options cannot be changed once serialization or deserialization has occurred.</value>
</data>
+ <data name="StreamNotWritable" xml:space="preserve">
+ <value>Stream was not writable.</value>
+ </data>
+ <data name="CannotWriteCommentWithEmbeddedDelimiter" xml:space="preserve">
+ <value>Cannot write a comment value which contains the end of comment delimiter.</value>
+ </data>
<data name="SerializerPropertyNameConflict" xml:space="preserve">
<value>The property '{0}.{1}' has the same name as a previous property based on naming or casing policies.</value>
</data>
<!-- BUILDING_INBOX_LIBRARY is only false when building for netstandard to validate that the sources are netstandard compatible. -->
<!-- This is meant to help with producing a source package and not to ship a netstandard compatible binary. -->
<DefineConstants Condition="'$(TargetsNETStandard)' != 'true'">$(DefineConstants);BUILDING_INBOX_LIBRARY</DefineConstants>
+ <DefineConstants Condition="'$(TargetsNETStandard)' == 'true'">$(DefineConstants);USE_ABW_INTERNALLY</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Text\Json\BitStack.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.MultiSegment.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.TryGet.cs" />
- <Compile Include="System\Text\Json\Serialization\ArrayBufferWriter.cs" />
<Compile Include="System\Text\Json\Serialization\ClassMaterializer.cs" />
<Compile Include="System\Text\Json\Serialization\ClassType.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\DefaultArrayConverter.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleEnumerable.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleObject.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.String.cs" />
- <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleValue.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.cs" />
<Compile Include="System\Text\Json\Serialization\Policies\JsonValueConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\PooledBufferWriter.cs" />
<Compile Include="System\Text\Json\Serialization\PropertyRef.cs" />
<Compile Include="System\Text\Json\Serialization\ReadStack.cs" />
<Compile Include="System\Text\Json\Serialization\ReadStackFrame.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterHelper.Escaping.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterHelper.Transcoding.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterOptions.cs" />
- <Compile Include="System\Text\Json\Writer\JsonWriterState.cs" />
<Compile Include="System\Text\Json\Writer\SequenceValidity.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteProperties.DateTime.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.String.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.UnsignedNumber.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetsNETStandard)' == 'true'">
+ <!-- Common or Common-branched source files -->
+ <Compile Include="$(CommonPath)\System\Buffers\ArrayBufferWriter.cs">
+ <Link>Common\System\Buffers\ArrayBufferWriter.cs</Link>
+ </Compile>
+ </ItemGroup>
<ItemGroup Condition="'$(TargetsNETStandard)' != 'true'">
<Reference Include="System.Collections" />
<Reference Include="System.Diagnostics.Debug" />
/// </summary>
internal void WriteElementTo(
int index,
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
ReadOnlySpan<char> propertyName)
{
CheckNotDisposed();
{
case JsonTokenType.StartObject:
writer.WriteStartObject(propertyName);
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.StartArray:
writer.WriteStartArray(propertyName);
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.String:
- WriteString(propertyName, row, ref writer);
+ WriteString(propertyName, row, writer);
return;
case JsonTokenType.True:
writer.WriteBoolean(propertyName, value: true);
/// </summary>
internal void WriteElementTo(
int index,
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
ReadOnlySpan<byte> propertyName)
{
CheckNotDisposed();
{
case JsonTokenType.StartObject:
writer.WriteStartObject(propertyName);
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.StartArray:
writer.WriteStartArray(propertyName);
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.String:
- WriteString(propertyName, row, ref writer);
+ WriteString(propertyName, row, writer);
return;
case JsonTokenType.True:
writer.WriteBoolean(propertyName, value: true);
/// </summary>
internal void WriteElementTo(
int index,
- ref Utf8JsonWriter writer)
+ Utf8JsonWriter writer)
{
CheckNotDisposed();
{
case JsonTokenType.StartObject:
writer.WriteStartObject();
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.StartArray:
writer.WriteStartArray();
- WriteComplexElement(index, ref writer);
+ WriteComplexElement(index, writer);
return;
case JsonTokenType.String:
- WriteString(row, ref writer);
+ WriteString(row, writer);
return;
case JsonTokenType.Number:
writer.WriteNumberValue(_utf8Json.Slice(row.Location, row.SizeOrLength).Span);
Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
}
- private void WriteComplexElement(int index, ref Utf8JsonWriter writer)
+ private void WriteComplexElement(int index, Utf8JsonWriter writer)
{
int endIndex = GetEndIndex(index, true);
switch (row.TokenType)
{
case JsonTokenType.String:
- WriteString(row, ref writer);
+ WriteString(row, writer);
continue;
case JsonTokenType.Number:
writer.WriteNumberValue(_utf8Json.Slice(row.Location, row.SizeOrLength).Span);
switch (propertyValue.TokenType)
{
case JsonTokenType.String:
- WriteString(propertyName, propertyValue, ref writer);
+ WriteString(propertyName, propertyValue, writer);
continue;
case JsonTokenType.Number:
writer.WriteNumber(
}
}
- private void WriteString(ReadOnlySpan<byte> propertyName, in DbRow row, ref Utf8JsonWriter writer)
+ private void WriteString(ReadOnlySpan<byte> propertyName, in DbRow row, Utf8JsonWriter writer)
{
ArraySegment<byte> rented = default;
}
}
- private void WriteString(ReadOnlySpan<char> propertyName, in DbRow row, ref Utf8JsonWriter writer)
+ private void WriteString(ReadOnlySpan<char> propertyName, in DbRow row, Utf8JsonWriter writer)
{
ArraySegment<byte> rented = default;
}
}
- private void WriteString(in DbRow row, ref Utf8JsonWriter writer)
+ private void WriteString(in DbRow row, Utf8JsonWriter writer)
{
ArraySegment<byte> rented = default;
/// <exception cref="ObjectDisposedException">
/// The parent <see cref="JsonDocument"/> has been disposed.
/// </exception>
- public void WriteAsProperty(ReadOnlySpan<char> propertyName, ref Utf8JsonWriter writer)
+ public void WriteAsProperty(ReadOnlySpan<char> propertyName, Utf8JsonWriter writer)
{
CheckValidInstance();
- _parent.WriteElementTo(_idx, ref writer, propertyName);
+ _parent.WriteElementTo(_idx, writer, propertyName);
}
/// <summary>
/// <exception cref="ObjectDisposedException">
/// The parent <see cref="JsonDocument"/> has been disposed.
/// </exception>
- public void WriteAsProperty(ReadOnlySpan<byte> utf8PropertyName, ref Utf8JsonWriter writer)
+ public void WriteAsProperty(ReadOnlySpan<byte> utf8PropertyName, Utf8JsonWriter writer)
{
CheckValidInstance();
- _parent.WriteElementTo(_idx, ref writer, utf8PropertyName);
+ _parent.WriteElementTo(_idx, writer, utf8PropertyName);
}
/// <summary>
/// <exception cref="ObjectDisposedException">
/// The parent <see cref="JsonDocument"/> has been disposed.
/// </exception>
- public void WriteAsValue(ref Utf8JsonWriter writer)
+ public void WriteAsValue(Utf8JsonWriter writer)
{
CheckValidInstance();
- _parent.WriteElementTo(_idx, ref writer);
+ _parent.WriteElementTo(_idx, writer);
}
/// <summary>
return true;
}
- public override void Write(bool value, ref Utf8JsonWriter writer)
+ public override void Write(bool value, Utf8JsonWriter writer)
{
writer.WriteBooleanValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, bool value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, bool value, Utf8JsonWriter writer)
{
writer.WriteBoolean(escapedPropertyName, value);
}
return true;
}
- public override void Write(byte value, ref Utf8JsonWriter writer)
+ public override void Write(byte value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, byte value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, byte value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return true;
}
- public override void Write(char value, ref Utf8JsonWriter writer)
+ public override void Write(char value, Utf8JsonWriter writer)
{
#if BUILDING_INBOX_LIBRARY
- Span<char> temp = MemoryMarshal.CreateSpan<char>(ref value, 1);
+ Span<char> temp = MemoryMarshal.CreateSpan(ref value, 1);
writer.WriteStringValue(temp);
#else
writer.WriteStringValue(value.ToString());
#endif
}
- public override void Write(Span<byte> escapedPropertyName, char value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, char value, Utf8JsonWriter writer)
{
#if BUILDING_INBOX_LIBRARY
- Span<char> temp = MemoryMarshal.CreateSpan<char>(ref value, 1);
+ Span<char> temp = MemoryMarshal.CreateSpan(ref value, 1);
writer.WriteString(escapedPropertyName, temp);
#else
writer.WriteString(escapedPropertyName, value.ToString());
return reader.TryGetDateTime(out value);
}
- public override void Write(DateTime value, ref Utf8JsonWriter writer)
+ public override void Write(DateTime value, Utf8JsonWriter writer)
{
writer.WriteStringValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, DateTime value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, DateTime value, Utf8JsonWriter writer)
{
writer.WriteString(escapedPropertyName, value);
}
return reader.TryGetDateTimeOffset(out value);
}
- public override void Write(DateTimeOffset value, ref Utf8JsonWriter writer)
+ public override void Write(DateTimeOffset value, Utf8JsonWriter writer)
{
writer.WriteStringValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, DateTimeOffset value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, DateTimeOffset value, Utf8JsonWriter writer)
{
writer.WriteString(escapedPropertyName, value);
}
return reader.TryGetDecimal(out value);
}
- public override void Write(decimal value, ref Utf8JsonWriter writer)
+ public override void Write(decimal value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, decimal value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, decimal value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return reader.TryGetDouble(out value);
}
- public override void Write(double value, ref Utf8JsonWriter writer)
+ public override void Write(double value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, double value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, double value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return true;
}
- public override void Write(TValue value, ref Utf8JsonWriter writer)
+ public override void Write(TValue value, Utf8JsonWriter writer)
{
if (TreatAsString)
{
}
}
- public override void Write(Span<byte> escapedPropertyName, TValue value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, TValue value, Utf8JsonWriter writer)
{
if (TreatAsString)
{
return true;
}
- public override void Write(short value, ref Utf8JsonWriter writer)
+ public override void Write(short value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, short value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, short value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return reader.TryGetInt32(out value);
}
- public override void Write(int value, ref Utf8JsonWriter writer)
+ public override void Write(int value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, int value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, int value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return reader.TryGetInt64(out value);
}
- public override void Write(long value, ref Utf8JsonWriter writer)
+ public override void Write(long value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, long value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, long value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return true;
}
- public override void Write(sbyte value, ref Utf8JsonWriter writer)
+ public override void Write(sbyte value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, sbyte value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, sbyte value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return true;
}
- public override void Write(float value, ref Utf8JsonWriter writer)
+ public override void Write(float value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, float value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, float value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return true;
}
- public override void Write(string value, ref Utf8JsonWriter writer)
+ public override void Write(string value, Utf8JsonWriter writer)
{
writer.WriteStringValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, string value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, string value, Utf8JsonWriter writer)
{
writer.WriteString(escapedPropertyName, value);
}
return true;
}
- public override void Write(ushort value, ref Utf8JsonWriter writer)
+ public override void Write(ushort value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, ushort value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, ushort value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return reader.TryGetUInt32(out value);
}
- public override void Write(uint value, ref Utf8JsonWriter writer)
+ public override void Write(uint value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, uint value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, uint value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
return reader.TryGetUInt64(out value);
}
- public override void Write(ulong value, ref Utf8JsonWriter writer)
+ public override void Write(ulong value, Utf8JsonWriter writer)
{
writer.WriteNumberValue(value);
}
- public override void Write(Span<byte> escapedPropertyName, ulong value, ref Utf8JsonWriter writer)
+ public override void Write(Span<byte> escapedPropertyName, ulong value, Utf8JsonWriter writer)
{
writer.WriteNumber(escapedPropertyName, value);
}
internal abstract void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
internal abstract void SetValueAsObject(object obj, object value, JsonSerializerOptions options);
- internal abstract void Write(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer);
- internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer);
+ internal abstract void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
+
+ internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
}
}
}
// todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call.
- internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (current.Enumerator != null)
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
- propertyInfo.WriteEnumerable(options, ref current, ref writer);
+ propertyInfo.WriteEnumerable(options, ref current, writer);
}
else if (ShouldSerialize)
{
{
if (_escapedName != null)
{
- ValueConverter.Write(_escapedName, value, ref writer);
+ ValueConverter.Write(_escapedName, value, writer);
}
else
{
- ValueConverter.Write(value, ref writer);
+ ValueConverter.Write(value, writer);
}
}
}
}
- internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (ValueConverter != null)
{
}
else
{
- ValueConverter.Write(value, ref writer);
+ ValueConverter.Write(value, writer);
}
}
}
}
// todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call.
- internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (current.Enumerator != null)
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
- propertyInfo.WriteEnumerable(options, ref current, ref writer);
+ propertyInfo.WriteEnumerable(options, ref current, writer);
}
else if (ShouldSerialize)
{
{
if (_escapedName != null)
{
- ValueConverter.Write(_escapedName, value.GetValueOrDefault(), ref writer);
+ ValueConverter.Write(_escapedName, value.GetValueOrDefault(), writer);
}
else
{
- ValueConverter.Write(value.GetValueOrDefault(), ref writer);
+ ValueConverter.Write(value.GetValueOrDefault(), writer);
}
}
}
}
- internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer)
{
if (ValueConverter != null)
{
}
else
{
- ValueConverter.Write(value.GetValueOrDefault(), ref writer);
+ ValueConverter.Write(value.GetValueOrDefault(), writer);
}
}
}
{
public static partial class JsonSerializer
{
- private static bool WriteEnumerable(
- JsonSerializerOptions options,
- ref Utf8JsonWriter writer,
- ref WriteStack state)
- {
- return HandleEnumerable(state.Current.JsonClassInfo.ElementClassInfo, options, ref writer, ref state);
- }
-
private static bool HandleEnumerable(
JsonClassInfo elementClassInfo,
JsonSerializerOptions options,
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
ref WriteStack state)
{
Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable);
if (elementClassInfo.ClassType == ClassType.Value)
{
- elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer);
+ elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, writer);
}
else if (state.Current.Enumerator.Current == null)
{
{
private static bool WriteObject(
JsonSerializerOptions options,
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
ref WriteStack state)
{
JsonClassInfo classInfo = state.Current.JsonClassInfo;
// Determine if we are done enumerating properties.
if (state.Current.PropertyIndex != classInfo.PropertyCount)
{
- HandleObject(options, ref writer, ref state);
+ HandleObject(options, writer, ref state);
return false;
}
private static bool HandleObject(
JsonSerializerOptions options,
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
ref WriteStack state)
{
Debug.Assert(
if (jsonPropertyInfo.ClassType == ClassType.Value)
{
- jsonPropertyInfo.Write(options, ref state.Current, ref writer);
+ jsonPropertyInfo.Write(options, ref state.Current, writer);
state.Current.NextProperty();
return true;
}
// A property that returns an enumerator keeps the same stack frame.
if (jsonPropertyInfo.ClassType == ClassType.Enumerable)
{
- bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, ref writer, ref state);
+ bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, writer, ref state);
if (endOfEnumerable)
{
state.Current.NextProperty();
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-
-namespace System.Text.Json.Serialization
-{
- public static partial class JsonSerializer
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool WriteValue(
- JsonSerializerOptions options,
- ref Utf8JsonWriter writer,
- ref WriteStackFrame current)
- {
- Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value);
-
- current.JsonPropertyInfo.Write(options, ref current, ref writer);
- return true;
- }
- }
-}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Buffers;
using System.Diagnostics;
namespace System.Text.Json.Serialization
}
}
- private static void WriteNull(
- ref JsonWriterState writerState,
- IBufferWriter<byte> bufferWriter)
- {
- Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState);
- writer.WriteNullValue();
- writer.Flush(true);
- }
-
private static byte[] WriteCoreBytes(object value, Type type, JsonSerializerOptions options)
{
if (options == null)
byte[] result;
- using (var output = new ArrayBufferWriter<byte>(options.DefaultBufferSize))
+ using (var output = new PooledBufferWriter<byte>(options.DefaultBufferSize))
{
WriteCore(output, value, type, options);
result = output.WrittenMemory.ToArray();
string result;
- using (var output = new ArrayBufferWriter<byte>(options.DefaultBufferSize))
+ using (var output = new PooledBufferWriter<byte>(options.DefaultBufferSize))
{
WriteCore(output, value, type, options);
result = JsonReaderHelper.TranscodeHelper(output.WrittenMemory.Span);
return result;
}
- private static void WriteCore(ArrayBufferWriter<byte> output, object value, Type type, JsonSerializerOptions options)
+ private static void WriteCore(PooledBufferWriter<byte> output, object value, Type type, JsonSerializerOptions options)
{
Debug.Assert(type != null || value == null);
- var writerState = new JsonWriterState(options.GetWriterOptions());
- var writer = new Utf8JsonWriter(output, writerState);
+ var writer = new Utf8JsonWriter(output, options.GetWriterOptions());
if (value == null)
{
state.Current.Initialize(type, options);
state.Current.CurrentValue = value;
- Write(ref writer, -1, options, ref state);
+ Write(writer, -1, options, ref state);
}
- writer.Flush(isFinalBlock: true);
+ writer.Flush();
}
}
}
options = JsonSerializerOptions.s_defaultOptions;
}
- var writerState = new JsonWriterState(options.GetWriterOptions());
+ JsonWriterOptions writerOptions = options.GetWriterOptions();
- using (var bufferWriter = new ArrayBufferWriter<byte>(options.DefaultBufferSize))
+ using (var bufferWriter = new PooledBufferWriter<byte>(options.DefaultBufferSize))
+ using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions))
{
if (value == null)
{
- WriteNull(ref writerState, bufferWriter);
+ writer.WriteNullValue();
+ writer.Flush();
+
#if BUILDING_INBOX_LIBRARY
await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
#else
{
flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here
- isFinalBlock = Write(ref writerState, bufferWriter, flushThreshold, options, ref state);
+ isFinalBlock = Write(writer, flushThreshold, options, ref state);
+ writer.Flush();
+
#if BUILDING_INBOX_LIBRARY
await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
#else
{
public static partial class JsonSerializer
{
- private static bool Write(
- ref JsonWriterState writerState,
- IBufferWriter<byte> bufferWriter,
- int flushThreshold,
- JsonSerializerOptions options,
- ref WriteStack state)
- {
- Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState);
-
- bool isFinalBlock = Write(
- ref writer,
- flushThreshold,
- options,
- ref state);
-
- writer.Flush(isFinalBlock: isFinalBlock);
- writerState = writer.GetCurrentState();
-
- return isFinalBlock;
- }
-
// There are three conditions to consider for an object (primitive value, enumerable or object) being processed here:
// 1) The object type was specified as the root-level return type to a Parse\Read method.
// 2) The object is property on a parent object.
// 3) The object is an element in an enumerable.
private static bool Write(
- ref Utf8JsonWriter writer,
+ Utf8JsonWriter writer,
int flushThreshold,
JsonSerializerOptions options,
ref WriteStack state)
bool finishedSerializing;
do
{
- switch (state.Current.JsonClassInfo.ClassType)
+ WriteStackFrame current = state.Current;
+ switch (current.JsonClassInfo.ClassType)
{
case ClassType.Enumerable:
- finishedSerializing = WriteEnumerable(options, ref writer, ref state);
+ finishedSerializing = HandleEnumerable(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
break;
case ClassType.Value:
- finishedSerializing = WriteValue(options, ref writer, ref state.Current);
+ Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value);
+ current.JsonPropertyInfo.Write(options, ref current, writer);
+ finishedSerializing = true;
break;
case ClassType.Object:
- finishedSerializing = WriteObject(options, ref writer, ref state);
+ finishedSerializing = WriteObject(options, writer, ref state);
break;
default:
Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Unknown);
// Treat typeof(object) as an empty object.
- finishedSerializing = WriteObject(options, ref writer, ref state);
+ finishedSerializing = WriteObject(options, writer, ref state);
break;
}
- if (flushThreshold >= 0 && writer.BytesWritten > flushThreshold)
+ if (flushThreshold >= 0 && writer.BytesPending > flushThreshold)
{
return false;
}
internal abstract class JsonValueConverter<TValue>
{
public abstract bool TryRead(Type valueType, ref Utf8JsonReader reader, out TValue value);
- public abstract void Write(TValue value, ref Utf8JsonWriter writer);
- public abstract void Write(Span<byte> escapedPropertyName, TValue value, ref Utf8JsonWriter writer);
+ public abstract void Write(TValue value, Utf8JsonWriter writer);
+ public abstract void Write(Span<byte> escapedPropertyName, TValue value, Utf8JsonWriter writer);
}
}
namespace System.Text.Json.Serialization
{
- // Note: this is currently an internal class that will be replaced with a shared version.
- internal sealed class ArrayBufferWriter<T> : IBufferWriter<T>, IDisposable
+ /// <summary>
+ /// This is an implementation detail and MUST NOT be called by source-package consumers.
+ /// </summary>
+ internal sealed class PooledBufferWriter<T> : IBufferWriter<T>, IDisposable
{
private T[] _rentedBuffer;
private int _index;
private const int MinimumBufferSize = 256;
- public ArrayBufferWriter()
+ public PooledBufferWriter()
{
_rentedBuffer = ArrayPool<T>.Shared.Rent(MinimumBufferSize);
_index = 0;
}
- public ArrayBufferWriter(int initialCapacity)
+ public PooledBufferWriter(int initialCapacity)
{
if (initialCapacity <= 0)
throw new ArgumentException(nameof(initialCapacity));
private void CheckIfDisposed()
{
if (_rentedBuffer == null)
- ThrowHelper.ThrowObjectDisposedException(nameof(ArrayBufferWriter<T>));
+ ThrowHelper.ThrowObjectDisposedException(nameof(PooledBufferWriter<T>));
}
public void Advance(int count)
throw GetArgumentException(SR.SpecialNumberValuesNotSupported);
}
- public static void ThrowArgumentException(ExceptionResource resource, int minimumSize = 0)
+ public static void ThrowInvalidOperationException_NeedLargerSpan()
{
- if (resource == ExceptionResource.FailedToGetLargerSpan)
- {
- throw GetArgumentException(SR.FailedToGetLargerSpan);
- }
- else
- {
- Debug.Assert(resource == ExceptionResource.FailedToGetMinimumSizeSpan);
- throw GetArgumentException(SR.Format(SR.FailedToGetMinimumSizeSpan, minimumSize));
- }
+ throw GetInvalidOperationException(SR.FailedToGetLargerSpan);
}
public static void ThrowArgumentException(ReadOnlySpan<byte> propertyName, ReadOnlySpan<byte> value)
throw GetInvalidOperationException(resource, currentDepth, token, tokenType);
}
+ public static void ThrowArgumentException_InvalidCommentValue()
+ {
+ throw new ArgumentException(SR.CannotWriteCommentWithEmbeddedDelimiter);
+ }
+
public static void ThrowArgumentException_InvalidUTF8(ReadOnlySpan<byte> value)
{
var builder = new StringBuilder();
CannotStartObjectArrayAfterPrimitiveOrClose,
CannotWriteValueWithinObject,
CannotWriteValueAfterPrimitive,
- FailedToGetMinimumSizeSpan,
- FailedToGetLargerSpan,
CannotWritePropertyWithinArray,
ExpectedJsonTokens,
TrailingCommaNotAllowedBeforeArrayEnd,
{
internal static partial class JsonWriterHelper
{
- public static bool TryWriteIndentation(Span<byte> buffer, int indent, out int bytesWritten)
+ public static void WriteIndentation(Span<byte> buffer, int indent)
{
Debug.Assert(indent % JsonConstants.SpacesPerIndent == 0);
+ Debug.Assert(buffer.Length >= indent);
- if (buffer.Length >= indent)
- {
- // Based on perf tests, the break-even point where vectorized Fill is faster
- // than explicitly writing the space in a loop is 8.
- if (indent < 8)
- {
- int i = 0;
- while (i < indent)
- {
- buffer[i++] = JsonConstants.Space;
- buffer[i++] = JsonConstants.Space;
- }
- }
- else
- {
- buffer.Slice(0, indent).Fill(JsonConstants.Space);
- }
- bytesWritten = indent;
- return true;
- }
- else
+ // Based on perf tests, the break-even point where vectorized Fill is faster
+ // than explicitly writing the space in a loop is 8.
+ if (indent < 8)
{
int i = 0;
- while (i < buffer.Length - 1)
+ while (i < indent)
{
buffer[i++] = JsonConstants.Space;
buffer[i++] = JsonConstants.Space;
}
- bytesWritten = i;
- return false;
+ }
+ else
+ {
+ buffer.Slice(0, indent).Fill(JsonConstants.Space);
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.Text.Json
-{
- /// <summary>
- /// Defines an opaque type that holds and saves all the relevant state information which must be provided
- /// to the <see cref="Utf8JsonWriter"/> to continue writing after completing a partial write.
- /// </summary>
- /// <remarks>
- /// This type is required to support reentrancy when writing incomplete data, and to continue
- /// writing in chunks. Unlike the <see cref="Utf8JsonWriter"/>, which is a ref struct,
- /// this type can survive across async/await boundaries and hence this type is required to provide
- /// support for writing more JSON text asynchronously before continuing with a new instance of the <see cref="Utf8JsonWriter"/>.
- /// </remarks>
- public struct JsonWriterState
- {
- internal long _bytesWritten;
- internal long _bytesCommitted;
- internal bool _inObject;
- internal bool _isNotPrimitive;
- internal JsonTokenType _tokenType;
- internal int _currentDepth;
- internal JsonWriterOptions _writerOptions;
- internal BitStack _bitStack;
-
- /// <summary>
- /// Returns the total amount of bytes written by the <see cref="Utf8JsonWriter"/> so far.
- /// This includes data that has been written beyond what has already been committed.
- /// </summary>
- public long BytesWritten => _bytesWritten;
-
- /// <summary>
- /// Returns the total amount of bytes committed to the output by the <see cref="Utf8JsonWriter"/> so far.
- /// This is how much the IBufferWriter has advanced.
- /// </summary>
- public long BytesCommitted => _bytesCommitted;
-
- /// <summary>
- /// Constructs a new <see cref="JsonWriterState"/> instance.
- /// </summary>
- /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
- /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (i.e. with no extra whitespace)
- /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
- /// <remarks>
- /// An instance of this state must be passed to the <see cref="Utf8JsonWriter"/> ctor with the output destination.
- /// Unlike the <see cref="Utf8JsonWriter"/>, which is a ref struct, the state can survive
- /// across async/await boundaries and hence this type is required to provide support for reading
- /// in more data asynchronously before continuing with a new instance of the <see cref="Utf8JsonWriter"/>.
- /// </remarks>
- public JsonWriterState(JsonWriterOptions options = default)
- {
- _bytesWritten = default;
- _bytesCommitted = default;
- _inObject = default;
- _isNotPrimitive = default;
- _tokenType = default;
- _currentDepth = default;
- _writerOptions = options;
-
- // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
- // This way we avoid allocations in the common, default cases, and allocate lazily.
- _bitStack = default;
- }
-
- /// <summary>
- /// Gets the custom behavior when writing JSON using
- /// the <see cref="Utf8JsonWriter"/> which indicates whether to format the output
- /// while writing and whether to skip structural JSON validation or not.
- /// </summary>
- public JsonWriterOptions Options => _writerOptions;
- }
-}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="DateTime"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTime"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000.
/// </remarks>
- public void WriteString(string propertyName, DateTime value, bool escape = true)
- => WriteString(propertyName.AsSpan(), value, escape);
+ public void WriteString(string propertyName, DateTime value)
+ => WriteString(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="DateTime"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTime"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000.
/// </remarks>
- public void WriteString(ReadOnlySpan<char> propertyName, DateTime value, bool escape = true)
+ public void WriteString(ReadOnlySpan<char> propertyName, DateTime value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteStringEscape(propertyName, value);
- }
- else
- {
- WriteStringByOptions(propertyName, value);
- }
+ WriteStringEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTime"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000.
/// </remarks>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, DateTime value, bool escape = true)
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, DateTime value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteStringEscape(utf8PropertyName, value);
- }
- else
- {
- WriteStringByOptions(utf8PropertyName, value);
- }
+ WriteStringEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteStringByOptions(ReadOnlySpan<char> propertyName, DateTime value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(propertyName, value);
}
private void WriteStringByOptions(ReadOnlySpan<byte> utf8PropertyName, DateTime value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8PropertyName, value);
}
private void WriteStringMinimized(ReadOnlySpan<char> escapedPropertyName, DateTime value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatDateTimeOffsetLength - 6);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDateTimeOffsetLength + 6;
- WriteStringValue(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringMinimized(ReadOnlySpan<byte> escapedPropertyName, DateTime value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatDateTimeOffsetLength - 6);
- WriteStringValue(value, ref idx);
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 5; // 2 quotes for property name, 2 quotes for date, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
- Advance(idx);
- }
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, DateTime value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ Span<byte> output = _memory.Span;
- WriteStringValue(value, ref idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
- }
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, DateTime value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- WriteStringValue(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
- private void WriteStringValue(DateTime value, ref int idx)
+ private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, DateTime value)
{
- if (_buffer.Length <= idx)
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDateTimeOffsetLength + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- FormatLoop(value, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
- }
- private void FormatLoop(DateTime value, ref int idx)
- {
- Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
Debug.Assert(result);
-
JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ }
+
+ private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, DateTime value)
+ {
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength);
- if (_buffer.Length - idx < bytesWritten)
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 6; // 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, bytesWritten);
+ Grow(maxRequired);
}
- tempSpan.Slice(0, bytesWritten).CopyTo(_buffer.Slice(idx));
+ Span<byte> output = _memory.Span;
- idx += bytesWritten;
- }
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- private static readonly StandardFormat s_dateTimeStandardFormat = new StandardFormat('O');
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ }
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="DateTimeOffset"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTimeOffset"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000-07:00.
/// </remarks>
- public void WriteString(string propertyName, DateTimeOffset value, bool escape = true)
- => WriteString(propertyName.AsSpan(), value, escape);
+ public void WriteString(string propertyName, DateTimeOffset value)
+ => WriteString(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="DateTimeOffset"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTimeOffset"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000-07:00.
/// </remarks>
- public void WriteString(ReadOnlySpan<char> propertyName, DateTimeOffset value, bool escape = true)
+ public void WriteString(ReadOnlySpan<char> propertyName, DateTimeOffset value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteStringEscape(propertyName, value);
- }
- else
- {
- WriteStringByOptions(propertyName, value);
- }
+ WriteStringEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="DateTimeOffset"/> using the round-trippable ('O') <see cref="StandardFormat"/> , for example: 2017-06-12T05:30:45.7680000-07:00.
/// </remarks>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, DateTimeOffset value, bool escape = true)
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, DateTimeOffset value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteStringEscape(utf8PropertyName, value);
- }
- else
- {
- WriteStringByOptions(utf8PropertyName, value);
- }
+ WriteStringEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteStringByOptions(ReadOnlySpan<char> propertyName, DateTimeOffset value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(propertyName, value);
}
private void WriteStringByOptions(ReadOnlySpan<byte> utf8PropertyName, DateTimeOffset value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8PropertyName, value);
}
private void WriteStringMinimized(ReadOnlySpan<char> escapedPropertyName, DateTimeOffset value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatDateTimeOffsetLength - 6);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDateTimeOffsetLength + 6;
- WriteStringValue(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringMinimized(ReadOnlySpan<byte> escapedPropertyName, DateTimeOffset value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatDateTimeOffsetLength - 6);
- WriteStringValue(value, ref idx);
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 5; // 2 quotes for property name, 2 quotes for date, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
- Advance(idx);
- }
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, DateTimeOffset value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ Span<byte> output = _memory.Span;
- WriteStringValue(value, ref idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
- }
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, DateTimeOffset value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- WriteStringValue(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
- private void WriteStringValue(DateTimeOffset value, ref int idx)
+ private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, DateTimeOffset value)
{
- if (_buffer.Length <= idx)
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDateTimeOffsetLength + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- FormatLoop(value, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
- }
- private void FormatLoop(DateTimeOffset value, ref int idx)
- {
- Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
Debug.Assert(result);
-
JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ }
+
+ private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, DateTimeOffset value)
+ {
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength);
+
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatDateTimeOffsetLength + 6; // 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- if (_buffer.Length - idx < bytesWritten)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx, bytesWritten);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- tempSpan.Slice(0, bytesWritten).CopyTo(_buffer.Slice(idx));
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
- idx += bytesWritten;
+ output[BytesPending++] = JsonConstants.Quote;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="decimal"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="decimal"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(string propertyName, decimal value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), value, escape);
+ public void WriteNumber(string propertyName, decimal value)
+ => WriteNumber(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="decimal"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="decimal"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<char> propertyName, decimal value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<char> propertyName, decimal value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteNumberEscape(propertyName, value);
- }
- else
- {
- WriteNumberByOptions(propertyName, value);
- }
+ WriteNumberEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="decimal"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, decimal value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, decimal value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteNumberEscape(utf8PropertyName, value);
- }
- else
- {
- WriteNumberByOptions(utf8PropertyName, value);
- }
+ WriteNumberEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, decimal value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(propertyName, value);
}
private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, decimal value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(utf8PropertyName, value);
}
private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, decimal value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatDecimalLength - 4);
+
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatDecimalLength + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDecimalLength + 4;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- WriteNumberValueFormatLoop(value, ref idx);
+ TranscodeAndWrite(escapedPropertyName, output);
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, decimal value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatDecimalLength - 4);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatDecimalLength + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- WriteNumberValueFormatLoop(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, decimal value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDecimalLength - 5 - s_newLineLength);
- Advance(idx);
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatDecimalLength + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDecimalLength + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, decimal value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDecimalLength - 5 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatDecimalLength + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteNumberValueFormatLoop(decimal value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatDecimalLength);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ Grow(maxRequired);
}
- idx += bytesWritten;
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="double"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="double"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(string propertyName, double value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), value, escape);
+ public void WriteNumber(string propertyName, double value)
+ => WriteNumber(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="double"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="double"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<char> propertyName, double value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<char> propertyName, double value)
{
JsonWriterHelper.ValidateProperty(propertyName);
JsonWriterHelper.ValidateDouble(value);
- if (escape)
- {
- WriteNumberEscape(propertyName, value);
- }
- else
- {
- WriteNumberByOptions(propertyName, value);
- }
+ WriteNumberEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="double"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, double value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, double value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
JsonWriterHelper.ValidateDouble(value);
- if (escape)
- {
- WriteNumberEscape(utf8PropertyName, value);
- }
- else
- {
- WriteNumberByOptions(utf8PropertyName, value);
- }
+ WriteNumberEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, double value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(propertyName, value);
}
private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, double value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(utf8PropertyName, value);
}
private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, double value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatDoubleLength - 4);
+
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatDoubleLength + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDoubleLength + 4;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- WriteNumberValueFormatLoop(value, ref idx);
+ TranscodeAndWrite(escapedPropertyName, output);
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, double value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatDoubleLength - 4);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatDoubleLength + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- WriteNumberValueFormatLoop(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, double value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDoubleLength - 5 - s_newLineLength);
- Advance(idx);
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatDoubleLength + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatDoubleLength + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, double value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDoubleLength - 5 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatDoubleLength + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteNumberValueFormatLoop(double value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatDoubleLength);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ Grow(maxRequired);
}
- idx += bytesWritten;
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="float"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="float"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(string propertyName, float value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), value, escape);
+ public void WriteNumber(string propertyName, float value)
+ => WriteNumber(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="float"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="float"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<char> propertyName, float value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<char> propertyName, float value)
{
JsonWriterHelper.ValidateProperty(propertyName);
JsonWriterHelper.ValidateSingle(value);
- if (escape)
- {
- WriteNumberEscape(propertyName, value);
- }
- else
- {
- WriteNumberByOptions(propertyName, value);
- }
+ WriteNumberEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="float"/> using the default <see cref="StandardFormat"/> (i.e. 'G').
/// </remarks>
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, float value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, float value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
JsonWriterHelper.ValidateSingle(value);
- if (escape)
- {
- WriteNumberEscape(utf8PropertyName, value);
- }
- else
- {
- WriteNumberByOptions(utf8PropertyName, value);
- }
+ WriteNumberEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, float value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(propertyName, value);
}
}
}
- private void WriteNumberByOptions(ReadOnlySpan<byte> propertyName, float value)
+ private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, float value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
- WriteNumberIndented(propertyName, value);
+ WriteNumberIndented(utf8PropertyName, value);
}
else
{
- WriteNumberMinimized(propertyName, value);
+ WriteNumberMinimized(utf8PropertyName, value);
}
}
private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, float value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatSingleLength - 4);
+
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatSingleLength + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatSingleLength + 4;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, float value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatSingleLength - 4);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatSingleLength + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- WriteNumberValueFormatLoop(value, ref idx);
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, float value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatSingleLength - 5 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatSingleLength + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatSingleLength + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, float value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatSingleLength - 5 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatSingleLength + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteNumberValueFormatLoop(float value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatSingleLength);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ WriteNewLine(output);
}
- idx += bytesWritten;
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
-using System.Buffers.Text;
using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="utf8FormattedNumber">The value to be written as a JSON number as part of the name/value pair.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
internal void WriteNumber(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> utf8FormattedNumber)
{
JsonWriterHelper.ValidateProperty(propertyName);
+ JsonWriterHelper.ValidateValue(utf8FormattedNumber);
JsonWriterHelper.ValidateNumber(utf8FormattedNumber);
WriteNumberEscape(propertyName, utf8FormattedNumber);
+
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
}
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="utf8FormattedNumber">The value to be written as a JSON number as part of the name/value pair.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
internal void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> utf8FormattedNumber)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
+ JsonWriterHelper.ValidateValue(utf8FormattedNumber);
JsonWriterHelper.ValidateNumber(utf8FormattedNumber);
WriteNumberEscape(utf8PropertyName, utf8FormattedNumber);
+
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
}
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
- WriteNumberIndented(propertyName, value);
+ WriteLiteralIndented(propertyName, value);
}
else
{
- WriteNumberMinimized(propertyName, value);
+ WriteLiteralMinimized(propertyName, value);
}
}
private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
- WriteNumberIndented(utf8PropertyName, value);
+ WriteLiteralIndented(utf8PropertyName, value);
}
else
{
- WriteNumberMinimized(utf8PropertyName, value);
- }
- }
-
- private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> value)
- {
- int idx = WritePropertyNameMinimized(escapedPropertyName);
-
- WriteNumberValueFormatLoop(value, ref idx);
-
- Advance(idx);
- }
-
- private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> value)
- {
- int idx = WritePropertyNameMinimized(escapedPropertyName);
-
- WriteNumberValueFormatLoop(value, ref idx);
-
- Advance(idx);
- }
-
- private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
-
- WriteNumberValueFormatLoop(value, ref idx);
-
- Advance(idx);
- }
-
- private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> value)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
-
- WriteNumberValueFormatLoop(value, ref idx);
-
- Advance(idx);
- }
-
- private void WriteNumberValueFormatLoop(ReadOnlySpan<byte> value, ref int idx)
- {
- if (_buffer.Length - idx - value.Length < 0)
- {
- AdvanceAndGrow(ref idx, value.Length);
+ WriteLiteralMinimized(utf8PropertyName, value);
}
-
- value.CopyTo(_buffer.Slice(idx));
- idx += value.Length;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="Guid"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="Guid"/> using the default <see cref="StandardFormat"/> (i.e. 'D'), as the form: nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn.
/// </remarks>
- public void WriteString(string propertyName, Guid value, bool escape = true)
- => WriteString(propertyName.AsSpan(), value, escape);
+ public void WriteString(string propertyName, Guid value)
+ => WriteString(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="Guid"/> value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="Guid"/> using the default <see cref="StandardFormat"/> (i.e. 'D'), as the form: nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn.
/// </remarks>
- public void WriteString(ReadOnlySpan<char> propertyName, Guid value, bool escape = true)
+ public void WriteString(ReadOnlySpan<char> propertyName, Guid value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteStringEscape(propertyName, value);
- }
- else
- {
- WriteStringByOptions(propertyName, value);
- }
+ WriteStringEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="Guid"/> using the default <see cref="StandardFormat"/> (i.e. 'D'), as the form: nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn.
/// </remarks>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, Guid value, bool escape = true)
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, Guid value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteStringEscape(utf8PropertyName, value);
- }
- else
- {
- WriteStringByOptions(utf8PropertyName, value);
- }
+ WriteStringEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStringByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteStringByOptions(ReadOnlySpan<char> propertyName, Guid value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(propertyName, value);
}
private void WriteStringByOptions(ReadOnlySpan<byte> utf8PropertyName, Guid value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8PropertyName, value);
}
private void WriteStringMinimized(ReadOnlySpan<char> escapedPropertyName, Guid value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatGuidLength - 6);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatGuidLength + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatGuidLength + 6;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
- WriteStringValue(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringMinimized(ReadOnlySpan<byte> escapedPropertyName, Guid value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatGuidLength - 6);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatGuidLength + 5; // 2 quotes for property name, 2 quotes for date, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- WriteStringValue(value, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, Guid value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatGuidLength - 7 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatGuidLength + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatGuidLength + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- WriteStringValue(value, ref idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- Advance(idx);
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, Guid value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteStringValue(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatGuidLength - 7 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatGuidLength + 6; // 2 quotes for property name, 2 quotes for date, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteStringValue(Guid value, ref int idx)
- {
- if (_buffer.Length <= idx)
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- FormatLoop(value, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
- }
- private void FormatLoop(Guid value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatGuidLength);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ WriteNewLine(output);
}
- idx += bytesWritten;
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidatePropertyNameAndDepth(ReadOnlySpan<char> propertyName)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidateWritingProperty()
{
- if (!_writerOptions.SkipValidation)
+ if (!Options.SkipValidation)
{
if (!_inObject)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidateWritingProperty(byte token)
{
- if (!_writerOptions.SkipValidation)
+ if (!Options.SkipValidation)
{
if (!_inObject)
{
}
}
- private int WritePropertyNameMinimized(ReadOnlySpan<byte> escapedPropertyName)
+ private void WritePropertyNameMinimized(ReadOnlySpan<byte> escapedPropertyName, byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
- {
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
- }
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - 5);
- if (_buffer.Length <= idx)
+ int minRequired = escapedPropertyName.Length + 4; // 2 quotes, 1 colon, and 1 start token
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- CopyLoop(escapedPropertyName, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.Quote;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.KeyValueSeperator;
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- return idx;
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = token;
}
- private int WritePropertyNameIndented(ReadOnlySpan<byte> escapedPropertyName)
+ private void WritePropertyNameIndented(ReadOnlySpan<byte> escapedPropertyName, byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
- {
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
- }
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- if (_tokenType != JsonTokenType.None)
- WriteNewLine(ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - 6 - s_newLineLength);
- int indent = Indentation;
- while (true)
- {
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + 5; // 2 quotes, 1 colon, 1 space, and 1 start token
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- if (_buffer.Length <= idx)
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- CopyLoop(escapedPropertyName, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
- if (_buffer.Length <= idx)
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx);
+ WriteNewLine(output);
}
- _buffer[idx++] = JsonConstants.KeyValueSeperator;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Space;
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- return idx;
+ output[BytesPending++] = JsonConstants.Quote;
+
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+ output[BytesPending++] = token;
}
- private int WritePropertyNameMinimized(ReadOnlySpan<char> escapedPropertyName)
+ private void WritePropertyNameMinimized(ReadOnlySpan<char> escapedPropertyName, byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
- {
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
- }
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - 5);
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Quote;
+ // All ASCII, 2 quotes, 1 colon, and 1 start token => escapedPropertyName.Length + 4
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 5;
- ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(escapedPropertyName);
- int partialConsumed = 0;
- while (true)
+ if (_memory.Length - BytesPending < maxRequired)
{
- OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan.Slice(partialConsumed), _buffer.Slice(idx), out int consumed, out int written);
- idx += written;
- if (status == OperationStatus.Done)
- {
- break;
- }
- partialConsumed += consumed;
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Quote;
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Quote;
- return idx;
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = token;
}
- private int WritePropertyNameIndented(ReadOnlySpan<char> escapedPropertyName)
+ private void WritePropertyNameIndented(ReadOnlySpan<char> escapedPropertyName, byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
- {
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
- }
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- if (_tokenType != JsonTokenType.None)
- WriteNewLine(ref idx);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 6 - s_newLineLength);
- int indent = Indentation;
- while (true)
- {
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
- }
+ // All ASCII, 2 quotes, 1 colon, 1 space, and 1 start token => indent + escapedPropertyName.Length + 5
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 6 + s_newLineLength;
- if (_buffer.Length <= idx)
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(escapedPropertyName);
- int partialConsumed = 0;
- while (true)
- {
- OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan.Slice(partialConsumed), _buffer.Slice(idx), out int consumed, out int written);
- idx += written;
- if (status == OperationStatus.Done)
- {
- break;
- }
- partialConsumed += consumed;
- AdvanceAndGrow(ref idx);
- }
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
- if (_buffer.Length <= idx)
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx);
+ WriteNewLine(output);
}
- _buffer[idx++] = JsonConstants.KeyValueSeperator;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Space;
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- return idx;
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+ output[BytesPending++] = token;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void TranscodeAndWrite(ReadOnlySpan<char> escapedPropertyName, Span<byte> output)
+ {
+ ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(escapedPropertyName);
+ OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan, output.Slice(BytesPending), out int consumed, out int written);
+ Debug.Assert(status == OperationStatus.Done);
+ Debug.Assert(consumed == byteSpan.Length);
+ BytesPending += written;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and the JSON literal "null" as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteNull(string propertyName, bool escape = true)
- => WriteNull(propertyName.AsSpan(), escape);
+ public void WriteNull(string propertyName)
+ => WriteNull(propertyName.AsSpan());
/// <summary>
/// Writes the property name and the JSON literal "null" as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteNull(ReadOnlySpan<char> propertyName, bool escape = true)
+ public void WriteNull(ReadOnlySpan<char> propertyName)
{
JsonWriterHelper.ValidateProperty(propertyName);
ReadOnlySpan<byte> span = JsonConstants.NullValue;
- if (escape)
- {
- WriteLiteralEscape(propertyName, span);
- }
- else
- {
- WriteLiteralByOptions(propertyName, span);
- }
+ WriteLiteralEscape(propertyName, span);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Null;
/// Writes the property name and the JSON literal "null" as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteNull(ReadOnlySpan<byte> utf8PropertyName, bool escape = true)
+ public void WriteNull(ReadOnlySpan<byte> utf8PropertyName)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
ReadOnlySpan<byte> span = JsonConstants.NullValue;
- if (escape)
- {
- WriteLiteralEscape(utf8PropertyName, span);
- }
- else
- {
- WriteLiteralByOptions(utf8PropertyName, span);
- }
+ WriteLiteralEscape(utf8PropertyName, span);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Null;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON literal "true" or "false" as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteBoolean(string propertyName, bool value, bool escape = true)
- => WriteBoolean(propertyName.AsSpan(), value, escape);
+ public void WriteBoolean(string propertyName, bool value)
+ => WriteBoolean(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="bool"/> value (as a JSON literal "true" or "false") as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON literal "true" or "false" as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteBoolean(ReadOnlySpan<char> propertyName, bool value, bool escape = true)
+ public void WriteBoolean(ReadOnlySpan<char> propertyName, bool value)
{
JsonWriterHelper.ValidateProperty(propertyName);
ReadOnlySpan<byte> span = value ? JsonConstants.TrueValue : JsonConstants.FalseValue;
- if (escape)
- {
- WriteLiteralEscape(propertyName, span);
- }
- else
- {
- WriteLiteralByOptions(propertyName, span);
- }
+ WriteLiteralEscape(propertyName, span);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = value ? JsonTokenType.True : JsonTokenType.False;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON literal "true" or "false" as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteBoolean(ReadOnlySpan<byte> utf8PropertyName, bool value, bool escape = true)
+ public void WriteBoolean(ReadOnlySpan<byte> utf8PropertyName, bool value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
ReadOnlySpan<byte> span = value ? JsonConstants.TrueValue : JsonConstants.FalseValue;
- if (escape)
- {
- WriteLiteralEscape(utf8PropertyName, span);
- }
- else
- {
- WriteLiteralByOptions(utf8PropertyName, span);
- }
+ WriteLiteralEscape(utf8PropertyName, span);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = value ? JsonTokenType.True : JsonTokenType.False;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteLiteralByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteLiteralByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteLiteralByOptions(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> value)
{
ValidateWritingProperty();
- int idx;
- if (_writerOptions.Indented)
+ if (Options.Indented)
+ {
+ WriteLiteralIndented(propertyName, value);
+ }
+ else
+ {
+ WriteLiteralMinimized(propertyName, value);
+ }
+ }
+
+ private void WriteLiteralByOptions(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> value)
+ {
+ ValidateWritingProperty();
+ if (Options.Indented)
{
- idx = WritePropertyNameIndented(propertyName);
+ WriteLiteralIndented(utf8PropertyName, value);
}
else
{
- idx = WritePropertyNameMinimized(propertyName);
+ WriteLiteralMinimized(utf8PropertyName, value);
+ }
+ }
+
+ private void WriteLiteralMinimized(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> value)
+ {
+ Debug.Assert(value.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - value.Length - 4);
+
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + value.Length + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + value.Length + 4;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
}
- if (value.Length > _buffer.Length - idx)
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx, value.Length);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
- value.CopyTo(_buffer.Slice(idx));
- idx += value.Length;
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ value.CopyTo(output.Slice(BytesPending));
+ BytesPending += value.Length;
}
- private void WriteLiteralByOptions(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> value)
+ private void WriteLiteralMinimized(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> value)
{
- ValidateWritingProperty();
- int idx;
- if (_writerOptions.Indented)
+ Debug.Assert(value.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - value.Length - 4);
+
+ int minRequired = escapedPropertyName.Length + value.Length + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- idx = WritePropertyNameIndented(utf8PropertyName);
+ Grow(maxRequired);
}
- else
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
{
- idx = WritePropertyNameMinimized(utf8PropertyName);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- if (value.Length > _buffer.Length - idx)
+ value.CopyTo(output.Slice(BytesPending));
+ BytesPending += value.Length;
+ }
+
+ private void WriteLiteralIndented(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> value)
+ {
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(value.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - value.Length - 5 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + value.Length + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + value.Length + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, value.Length);
+ Grow(maxRequired);
}
- value.CopyTo(_buffer.Slice(idx));
- idx += value.Length;
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ value.CopyTo(output.Slice(BytesPending));
+ BytesPending += value.Length;
+ }
+
+ private void WriteLiteralIndented(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> value)
+ {
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(value.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - value.Length - 5 - s_newLineLength);
+
+ int minRequired = indent + escapedPropertyName.Length + value.Length + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
- Advance(idx);
+ value.CopyTo(output.Slice(BytesPending));
+ BytesPending += value.Length;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="long"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="long"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(string propertyName, long value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), value, escape);
+ public void WriteNumber(string propertyName, long value)
+ => WriteNumber(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="long"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="long"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(ReadOnlySpan<char> propertyName, long value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<char> propertyName, long value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteNumberEscape(propertyName, value);
- }
- else
- {
- WriteNumberByOptions(propertyName, value);
- }
+ WriteNumberEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="long"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, long value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, long value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteNumberEscape(utf8PropertyName, value);
- }
- else
- {
- WriteNumberByOptions(utf8PropertyName, value);
- }
+ WriteNumberEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="int"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(string propertyName, int value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), (long)value, escape);
+ public void WriteNumber(string propertyName, int value)
+ => WriteNumber(propertyName.AsSpan(), (long)value);
/// <summary>
/// Writes the property name and <see cref="int"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="int"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(ReadOnlySpan<char> propertyName, int value, bool escape = true)
- => WriteNumber(propertyName, (long)value, escape);
+ public void WriteNumber(ReadOnlySpan<char> propertyName, int value)
+ => WriteNumber(propertyName, (long)value);
/// <summary>
/// Writes the property name and <see cref="int"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// <remarks>
/// Writes the <see cref="int"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, int value, bool escape = true)
- => WriteNumber(utf8PropertyName, (long)value, escape);
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, int value)
+ => WriteNumber(utf8PropertyName, (long)value);
private void WriteNumberEscape(ReadOnlySpan<char> propertyName, long value)
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, long value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(propertyName, value);
}
private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, long value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(utf8PropertyName, value);
}
private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, long value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatInt64Length - 4);
- WriteNumberValueFormatLoop(value, ref idx);
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatInt64Length + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatInt64Length + 4;
- Advance(idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, long value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatInt64Length - 4);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatInt64Length + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, long value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatInt64Length - 5 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatInt64Length + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatInt64Length + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
- WriteNumberValueFormatLoop(value, ref idx);
+ TranscodeAndWrite(escapedPropertyName, output);
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, long value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatInt64Length - 5 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatInt64Length + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteNumberValueFormatLoop(long value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatInt64Length);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ Grow(maxRequired);
}
- idx += bytesWritten;
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and string text value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(string propertyName, string value, bool escape = true)
- => WriteString(propertyName.AsSpan(), value.AsSpan(), escape);
+ public void WriteString(string propertyName, string value)
+ => WriteString(propertyName.AsSpan(), value.AsSpan());
/// <summary>
/// Writes the UTF-16 property name and UTF-16 text value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<char> propertyName, ReadOnlySpan<char> value, bool escape = true)
+ public void WriteString(ReadOnlySpan<char> propertyName, ReadOnlySpan<char> value)
{
JsonWriterHelper.ValidatePropertyAndValue(propertyName, value);
- if (escape)
- {
- WriteStringEscape(propertyName, value);
- }
- else
- {
- WriteStringDontEscape(propertyName, value);
- }
+ WriteStringEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="utf8Value">The UTF-8 encoded value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> utf8Value, bool escape = true)
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> utf8Value)
{
JsonWriterHelper.ValidatePropertyAndValue(utf8PropertyName, utf8Value);
- if (escape)
- {
- WriteStringEscape(utf8PropertyName, utf8Value);
- }
- else
- {
- WriteStringDontEscape(utf8PropertyName, utf8Value);
- }
+ WriteStringEscape(utf8PropertyName, utf8Value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(string propertyName, ReadOnlySpan<char> value, bool escape = true)
- => WriteString(propertyName.AsSpan(), value, escape);
+ public void WriteString(string propertyName, ReadOnlySpan<char> value)
+ => WriteString(propertyName.AsSpan(), value);
/// <summary>
/// Writes the UTF-8 property name and UTF-16 text value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<char> value, bool escape = true)
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<char> value)
{
JsonWriterHelper.ValidatePropertyAndValue(utf8PropertyName, value);
- if (escape)
- {
- WriteStringEscape(utf8PropertyName, value);
- }
- else
- {
- WriteStringDontEscape(utf8PropertyName, value);
- }
+ WriteStringEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="utf8Value">The UTF-8 encoded value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(string propertyName, ReadOnlySpan<byte> utf8Value, bool escape = true)
- => WriteString(propertyName.AsSpan(), utf8Value, escape);
+ public void WriteString(string propertyName, ReadOnlySpan<byte> utf8Value)
+ => WriteString(propertyName.AsSpan(), utf8Value);
/// <summary>
/// Writes the UTF-16 property name and UTF-8 text value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="utf8Value">The UTF-8 encoded value to be written as a JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> utf8Value, bool escape = true)
+ public void WriteString(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> utf8Value)
{
JsonWriterHelper.ValidatePropertyAndValue(propertyName, utf8Value);
- if (escape)
- {
- WriteStringEscape(propertyName, utf8Value);
- }
- else
- {
- WriteStringDontEscape(propertyName, utf8Value);
- }
+ WriteStringEscape(propertyName, utf8Value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<char> propertyName, string value, bool escape = true)
- => WriteString(propertyName, value.AsSpan(), escape);
+ public void WriteString(ReadOnlySpan<char> propertyName, string value)
+ => WriteString(propertyName, value.AsSpan());
/// <summary>
/// Writes the UTF-8 property name and string text value (as a JSON string) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.
- /// The value is always escaped</param>
+ /// <remarks>
+ /// The property name and value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name or value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteString(ReadOnlySpan<byte> utf8PropertyName, string value, bool escape = true)
- => WriteString(utf8PropertyName, value.AsSpan(), escape);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteStringDontEscape(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<char> value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(value);
- if (valueIdx != -1)
- {
- WriteStringEscapeValueOnly(escapedPropertyName, value, valueIdx);
- }
- else
- {
- WriteStringByOptions(escapedPropertyName, value);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteStringDontEscape(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> utf8Value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
- if (valueIdx != -1)
- {
- WriteStringEscapeValueOnly(escapedPropertyName, utf8Value, valueIdx);
- }
- else
- {
- WriteStringByOptions(escapedPropertyName, utf8Value);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteStringDontEscape(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> utf8Value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
- if (valueIdx != -1)
- {
- WriteStringEscapeValueOnly(escapedPropertyName, utf8Value, valueIdx);
- }
- else
- {
- WriteStringByOptions(escapedPropertyName, utf8Value);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteStringDontEscape(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<char> value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(value);
- if (valueIdx != -1)
- {
- WriteStringEscapeValueOnly(escapedPropertyName, value, valueIdx);
- }
- else
- {
- WriteStringByOptions(escapedPropertyName, value);
- }
- }
+ public void WriteString(ReadOnlySpan<byte> utf8PropertyName, string value)
+ => WriteString(utf8PropertyName, value.AsSpan());
private void WriteStringEscapeValueOnly(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<char> value, int firstEscapeIndex)
{
int valueIdx = JsonWriterHelper.NeedsEscaping(value);
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < value.Length && valueIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length && propertyIdx < int.MaxValue / 2);
// Equivalent to: valueIdx != -1 || propertyIdx != -1
if (valueIdx + propertyIdx != -2)
int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < utf8Value.Length && valueIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length && propertyIdx < int.MaxValue / 2);
// Equivalent to: valueIdx != -1 || propertyIdx != -1
if (valueIdx + propertyIdx != -2)
int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < utf8Value.Length && valueIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length && propertyIdx < int.MaxValue / 2);
// Equivalent to: valueIdx != -1 || propertyIdx != -1
if (valueIdx + propertyIdx != -2)
int valueIdx = JsonWriterHelper.NeedsEscaping(value);
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < value.Length && valueIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length && propertyIdx < int.MaxValue / 2);
// Equivalent to: valueIdx != -1 || propertyIdx != -1
if (valueIdx + propertyIdx != -2)
int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal);
Span<char> escapedValue;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
valueArray = ArrayPool<char>.Shared.Rent(length);
escapedValue = valueArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
char* ptr = stackalloc char[length];
escapedValue = new Span<char>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndexVal, out int written);
value = escapedValue.Slice(0, written);
}
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
propertyArray = ArrayPool<char>.Shared.Rent(length);
escapedPropertyName = propertyArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
char* ptr = stackalloc char[length];
escapedPropertyName = new Span<char>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
propertyName = escapedPropertyName.Slice(0, written);
}
int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);
Span<byte> escapedValue;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
valueArray = ArrayPool<byte>.Shared.Rent(length);
escapedValue = valueArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
byte* ptr = stackalloc byte[length];
escapedValue = new Span<byte>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, out int written);
utf8Value = escapedValue.Slice(0, written);
}
if (firstEscapeIndexProp != -1)
{
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
+
Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
propertyArray = ArrayPool<byte>.Shared.Rent(length);
escapedPropertyName = propertyArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
byte* ptr = stackalloc byte[length];
escapedPropertyName = new Span<byte>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
utf8PropertyName = escapedPropertyName.Slice(0, written);
}
int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);
Span<byte> escapedValue;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
valueArray = ArrayPool<byte>.Shared.Rent(length);
escapedValue = valueArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
byte* ptr = stackalloc byte[length];
escapedValue = new Span<byte>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, out int written);
utf8Value = escapedValue.Slice(0, written);
}
if (firstEscapeIndexProp != -1)
{
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
+
Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
propertyArray = ArrayPool<char>.Shared.Rent(length);
escapedPropertyName = propertyArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
char* ptr = stackalloc char[length];
escapedPropertyName = new Span<char>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
propertyName = escapedPropertyName.Slice(0, written);
}
int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal);
Span<char> escapedValue;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
valueArray = ArrayPool<char>.Shared.Rent(length);
escapedValue = valueArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
char* ptr = stackalloc char[length];
escapedValue = new Span<char>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndexVal, out int written);
value = escapedValue.Slice(0, written);
}
if (firstEscapeIndexProp != -1)
{
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
+
Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
+ if (length > JsonConstants.StackallocThreshold)
{
propertyArray = ArrayPool<byte>.Shared.Rent(length);
escapedPropertyName = propertyArray;
}
else
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
+ // Cannot create a span directly since it gets assigned to parameter and passed down.
unsafe
{
byte* ptr = stackalloc byte[length];
escapedPropertyName = new Span<byte>(ptr, length);
}
}
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
utf8PropertyName = escapedPropertyName.Slice(0, written);
}
private void WriteStringByOptions(ReadOnlySpan<char> propertyName, ReadOnlySpan<char> value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(propertyName, value);
}
private void WriteStringByOptions(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<byte> utf8Value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8PropertyName, utf8Value);
}
private void WriteStringByOptions(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> utf8Value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(propertyName, utf8Value);
}
private void WriteStringByOptions(ReadOnlySpan<byte> utf8PropertyName, ReadOnlySpan<char> value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8PropertyName, value);
}
}
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<char> escapedValue)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < ((int.MaxValue - 6) / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for value, and 1 colon => escapedPropertyName.Length + escapedValue.Length + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = ((escapedPropertyName.Length + escapedValue.Length) * JsonConstants.MaxExpansionFactorWhileTranscoding) + 6;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- WriteStringValue(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> escapedValue)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - escapedValue.Length - 6);
+
+ int minRequired = escapedPropertyName.Length + escapedValue.Length + 5; // 2 quotes for property name, 2 quotes for value, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- WriteStringValue(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> escapedValue)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 6);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for value, and 1 colon => escapedPropertyName.Length + escapedValue.Length + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + escapedValue.Length + 6;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
- WriteStringValue(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<char> escapedValue)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 6);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for value, and 1 colon => escapedPropertyName.Length + escapedValue.Length + 5
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedValue.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + escapedPropertyName.Length + 6;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
- WriteStringValue(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<char> escapedValue)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < ((int.MaxValue - 7 - indent - s_newLineLength) / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length);
- WriteStringValue(escapedValue, ref idx);
+ // All ASCII, 2 quotes for property name, 2 quotes for value, 1 colon, and 1 space => escapedPropertyName.Length + escapedValue.Length + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + ((escapedPropertyName.Length + escapedValue.Length) * JsonConstants.MaxExpansionFactorWhileTranscoding) + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- Advance(idx);
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<byte> escapedValue)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteStringValue(escapedValue, ref idx);
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - escapedValue.Length - 7 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + escapedValue.Length + 6; // 2 quotes for property name, 2 quotes for value, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> escapedValue)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- WriteStringValue(escapedValue, ref idx);
+ Span<byte> output = _memory.Span;
- Advance(idx);
- }
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<char> escapedValue)
- {
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- WriteStringValue(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
- private void WriteStringValue(ReadOnlySpan<char> escapedValue, ref int idx)
+ // TODO: https://github.com/dotnet/corefx/issues/36958
+ private void WriteStringIndented(ReadOnlySpan<char> escapedPropertyName, ReadOnlySpan<byte> escapedValue)
{
- if (_buffer.Length <= idx)
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 7 - indent - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for value, 1 colon, and 1 space => escapedPropertyName.Length + escapedValue.Length + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + escapedValue.Length + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(escapedValue);
- int partialConsumed = 0;
- while (true)
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
{
- OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan.Slice(partialConsumed), _buffer.Slice(idx), out int consumed, out int written);
- idx += written;
- if (status == OperationStatus.Done)
- {
- break;
- }
- partialConsumed += consumed;
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- if (_buffer.Length <= idx)
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx);
+ WriteNewLine(output);
}
- _buffer[idx++] = JsonConstants.Quote;
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
- private void WriteStringValue(ReadOnlySpan<byte> escapedValue, ref int idx)
+ // TODO: https://github.com/dotnet/corefx/issues/36958
+ private void WriteStringIndented(ReadOnlySpan<byte> escapedPropertyName, ReadOnlySpan<char> escapedValue)
{
- if (_buffer.Length <= idx)
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedValue.Length <= JsonConstants.MaxTokenSize);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 7 - indent - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 2 quotes for value, 1 colon, and 1 space => escapedPropertyName.Length + escapedValue.Length + 6
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedValue.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + escapedPropertyName.Length + 7 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Quote;
- CopyLoop(escapedValue, ref idx);
+ Span<byte> output = _memory.Span;
- if (_buffer.Length <= idx)
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = JsonConstants.Quote;
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the property name and <see cref="ulong"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="ulong"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(string propertyName, ulong value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), value, escape);
+ public void WriteNumber(string propertyName, ulong value)
+ => WriteNumber(propertyName.AsSpan(), value);
/// <summary>
/// Writes the property name and <see cref="ulong"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="ulong"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(ReadOnlySpan<char> propertyName, ulong value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<char> propertyName, ulong value)
{
JsonWriterHelper.ValidateProperty(propertyName);
- if (escape)
- {
- WriteNumberEscape(propertyName, value);
- }
- else
- {
- WriteNumberByOptions(propertyName, value);
- }
+ WriteNumberEscape(propertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="ulong"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, ulong value, bool escape = true)
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, ulong value)
{
JsonWriterHelper.ValidateProperty(utf8PropertyName);
- if (escape)
- {
- WriteNumberEscape(utf8PropertyName, value);
- }
- else
- {
- WriteNumberByOptions(utf8PropertyName, value);
- }
+ WriteNumberEscape(utf8PropertyName, value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.Number;
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="uint"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(string propertyName, uint value, bool escape = true)
- => WriteNumber(propertyName.AsSpan(), (ulong)value, escape);
+ public void WriteNumber(string propertyName, uint value)
+ => WriteNumber(propertyName.AsSpan(), (ulong)value);
/// <summary>
/// Writes the property name and <see cref="uint"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="uint"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(ReadOnlySpan<char> propertyName, uint value, bool escape = true)
- => WriteNumber(propertyName, (ulong)value, escape);
+ public void WriteNumber(ReadOnlySpan<char> propertyName, uint value)
+ => WriteNumber(propertyName, (ulong)value);
/// <summary>
/// Writes the property name and <see cref="uint"/> value (as a JSON number) as part of a name/value pair of a JSON object.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
/// <param name="value">The value to be written as a JSON number as part of the name/value pair.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Writes the <see cref="uint"/> using the default <see cref="StandardFormat"/> (i.e. 'G'), for example: 32767.
/// </remarks>
[CLSCompliant(false)]
- public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, uint value, bool escape = true)
- => WriteNumber(utf8PropertyName, (ulong)value, escape);
+ public void WriteNumber(ReadOnlySpan<byte> utf8PropertyName, uint value)
+ => WriteNumber(utf8PropertyName, (ulong)value);
private void WriteNumberEscape(ReadOnlySpan<char> propertyName, ulong value)
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteNumberByOptions(escapedPropertyName.Slice(0, written), value);
private void WriteNumberByOptions(ReadOnlySpan<char> propertyName, ulong value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(propertyName, value);
}
private void WriteNumberByOptions(ReadOnlySpan<byte> utf8PropertyName, ulong value)
{
ValidateWritingProperty();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberIndented(utf8PropertyName, value);
}
private void WriteNumberMinimized(ReadOnlySpan<char> escapedPropertyName, ulong value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - JsonConstants.MaximumFormatUInt64Length - 4);
- WriteNumberValueFormatLoop(value, ref idx);
+ // All ASCII, 2 quotes for property name, and 1 colon => escapedPropertyName.Length + JsonConstants.MaximumFormatUInt64Length + 3
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatUInt64Length + 4;
- Advance(idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedPropertyName, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberMinimized(ReadOnlySpan<byte> escapedPropertyName, ulong value)
{
- int idx = WritePropertyNameMinimized(escapedPropertyName);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - JsonConstants.MaximumFormatUInt64Length - 4);
+
+ int minRequired = escapedPropertyName.Length + JsonConstants.MaximumFormatUInt64Length + 3; // 2 quotes for property name, and 1 colon
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
- Advance(idx);
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<char> escapedPropertyName, ulong value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatUInt64Length - 5 - s_newLineLength);
+
+ // All ASCII, 2 quotes for property name, 1 colon, and 1 space => escapedPropertyName.Length + JsonConstants.MaximumFormatUInt64Length + 4
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + JsonConstants.MaximumFormatUInt64Length + 5 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
- WriteNumberValueFormatLoop(value, ref idx);
+ TranscodeAndWrite(escapedPropertyName, output);
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberIndented(ReadOnlySpan<byte> escapedPropertyName, ulong value)
{
- int idx = WritePropertyNameIndented(escapedPropertyName);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- WriteNumberValueFormatLoop(value, ref idx);
+ Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatUInt64Length - 5 - s_newLineLength);
- Advance(idx);
- }
+ int minRequired = indent + escapedPropertyName.Length + JsonConstants.MaximumFormatUInt64Length + 4; // 2 quotes for property name, 1 colon, and 1 space
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
- private void WriteNumberValueFormatLoop(ulong value, ref int idx)
- {
- if (!Utf8Formatter.TryFormat(value, _buffer.Slice(idx), out int bytesWritten))
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx, JsonConstants.MaximumFormatUInt64Length);
- bool result = Utf8Formatter.TryFormat(value, _buffer, out bytesWritten);
- Debug.Assert(result);
+ Grow(maxRequired);
}
- idx += bytesWritten;
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedPropertyName.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedPropertyName.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
+ output[BytesPending++] = JsonConstants.KeyValueSeperator;
+ output[BytesPending++] = JsonConstants.Space;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
+ private static char[] s_singleLineCommentDelimiter = new char[2] { '*', '/' };
+ private static ReadOnlySpan<byte> SingleLineCommentDelimiterUtf8 => new byte[2] { (byte)'*', (byte)'/' };
+
/// <summary>
/// Writes the string text value (as a JSON comment).
/// </summary>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON comment within /*..*/.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The comment value is not escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
- /// Thrown when the specified value is too large.
+ /// Thrown when the specified value is too large OR if the given string text value contains a comment delimiter (i.e. */).
/// </exception>
- public void WriteCommentValue(string value, bool escape = true)
- => WriteCommentValue(value.AsSpan(), escape);
+ public void WriteCommentValue(string value)
+ => WriteCommentValue(value.AsSpan());
/// <summary>
/// Writes the UTF-16 text value (as a JSON comment).
/// </summary>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON comment within /*..*/.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The comment value is not escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
- /// Thrown when the specified value is too large.
+ /// Thrown when the specified value is too large OR if the given UTF-16 text value contains a comment delimiter (i.e. */).
/// </exception>
- public void WriteCommentValue(ReadOnlySpan<char> value, bool escape = true)
+ public void WriteCommentValue(ReadOnlySpan<char> value)
{
JsonWriterHelper.ValidateValue(value);
- if (escape)
+ if (value.IndexOf(s_singleLineCommentDelimiter) != -1)
{
- WriteCommentEscape(value);
+ ThrowHelper.ThrowArgumentException_InvalidCommentValue();
}
- else
- {
- WriteCommentByOptions(value);
- }
- }
- private void WriteCommentEscape(ReadOnlySpan<char> value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(value);
-
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
-
- if (valueIdx != -1)
- {
- WriteCommentEscapeValue(value, valueIdx);
- }
- else
- {
- WriteCommentByOptions(value);
- }
+ WriteCommentByOptions(value);
}
private void WriteCommentByOptions(ReadOnlySpan<char> value)
{
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteCommentIndented(value);
}
}
}
- private void WriteCommentMinimized(ReadOnlySpan<char> escapedValue)
- {
- int idx = 0;
-
- WriteCommentValue(escapedValue, ref idx);
-
- Advance(idx);
- }
-
- private void WriteCommentIndented(ReadOnlySpan<char> escapedValue)
+ private void WriteCommentMinimized(ReadOnlySpan<char> value)
{
- int idx = 0;
+ Debug.Assert(value.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - 4);
- if (_tokenType != JsonTokenType.None)
- WriteNewLine(ref idx);
+ // All ASCII, /*...*/ => escapedValue.Length + 4
+ // Optionally, up to 3x growth when transcoding
+ int maxRequired = (value.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 4;
- int indent = Indentation;
- while (true)
+ if (_memory.Length - BytesPending < maxRequired)
{
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- WriteCommentValue(escapedValue, ref idx);
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Slash;
+ output[BytesPending++] = JsonConstants.Asterisk;
+
+ ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(value);
+ OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan, output.Slice(BytesPending), out int _, out int written);
+ Debug.Assert(status != OperationStatus.DestinationTooSmall);
+ BytesPending += written;
+
+ output[BytesPending++] = JsonConstants.Asterisk;
+ output[BytesPending++] = JsonConstants.Slash;
}
- private void WriteCommentEscapeValue(ReadOnlySpan<char> value, int firstEscapeIndexVal)
+ private void WriteCommentIndented(ReadOnlySpan<char> value)
{
- Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= value.Length);
- Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < value.Length);
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- char[] valueArray = null;
+ Debug.Assert(value.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 4 - s_newLineLength);
- int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal);
+ // All ASCII, /*...*/ => escapedValue.Length + 4
+ // Optionally, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (value.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 4 + s_newLineLength;
- Span<char> escapedValue;
- if (length > StackallocThreshold)
- {
- valueArray = ArrayPool<char>.Shared.Rent(length);
- escapedValue = valueArray;
- }
- else
+ if (_memory.Length - BytesPending < maxRequired)
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedValue = new Span<char>(ptr, length);
- }
+ Grow(maxRequired);
}
- JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndexVal, out int written);
- WriteCommentByOptions(escapedValue.Slice(0, written));
+ Span<byte> output = _memory.Span;
- if (valueArray != null)
+ if (_tokenType != JsonTokenType.None)
{
- ArrayPool<char>.Shared.Return(valueArray);
+ WriteNewLine(output);
}
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Slash;
+ output[BytesPending++] = JsonConstants.Asterisk;
+
+ ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(value);
+ OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan, output.Slice(BytesPending), out int _, out int written);
+ Debug.Assert(status != OperationStatus.DestinationTooSmall);
+ BytesPending += written;
+
+ output[BytesPending++] = JsonConstants.Asterisk;
+ output[BytesPending++] = JsonConstants.Slash;
}
/// <summary>
/// Writes the UTF-8 text value (as a JSON comment).
/// </summary>
/// <param name="utf8Value">The UTF-8 encoded value to be written as a JSON comment within /*..*/.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The comment value is not escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
- /// Thrown when the specified value is too large.
+ /// Thrown when the specified value is too large OR if the given UTF-8 text value contains a comment delimiter (i.e. */).
/// </exception>
- public void WriteCommentValue(ReadOnlySpan<byte> utf8Value, bool escape = true)
+ public void WriteCommentValue(ReadOnlySpan<byte> utf8Value)
{
JsonWriterHelper.ValidateValue(utf8Value);
- if (escape)
+ if (utf8Value.IndexOf(SingleLineCommentDelimiterUtf8) != -1)
{
- WriteCommentEscape(utf8Value);
+ ThrowHelper.ThrowArgumentException_InvalidCommentValue();
}
- else
- {
- WriteCommentByOptions(utf8Value);
- }
- }
- private void WriteCommentEscape(ReadOnlySpan<byte> utf8Value)
- {
- int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
-
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
-
- if (valueIdx != -1)
- {
- WriteCommentEscapeValue(utf8Value, valueIdx);
- }
- else
- {
- WriteCommentByOptions(utf8Value);
- }
+ WriteCommentByOptions(utf8Value);
}
private void WriteCommentByOptions(ReadOnlySpan<byte> utf8Value)
{
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteCommentIndented(utf8Value);
}
}
}
- private void WriteCommentMinimized(ReadOnlySpan<byte> escapedValue)
- {
- int idx = 0;
-
- WriteCommentValue(escapedValue, ref idx);
-
- Advance(idx);
- }
-
- private void WriteCommentIndented(ReadOnlySpan<byte> escapedValue)
- {
- int idx = 0;
- WriteFormattingPreamble(ref idx);
-
- WriteCommentValue(escapedValue, ref idx);
-
- Advance(idx);
- }
-
- private void WriteCommentEscapeValue(ReadOnlySpan<byte> utf8Value, int firstEscapeIndexVal)
+ private void WriteCommentMinimized(ReadOnlySpan<byte> utf8Value)
{
- Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
- Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < utf8Value.Length);
+ Debug.Assert(utf8Value.Length < int.MaxValue - 4);
- byte[] valueArray = null;
+ int maxRequired = utf8Value.Length + 4; // /*...*/
- int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);
-
- Span<byte> escapedValue;
- if (length > StackallocThreshold)
- {
- valueArray = ArrayPool<byte>.Shared.Rent(length);
- escapedValue = valueArray;
- }
- else
+ if (_memory.Length - BytesPending < maxRequired)
{
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedValue = new Span<byte>(ptr, length);
- }
+ Grow(maxRequired);
}
- JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, out int written);
- WriteCommentByOptions(escapedValue.Slice(0, written));
+ Span<byte> output = _memory.Span;
- if (valueArray != null)
- {
- ArrayPool<byte>.Shared.Return(valueArray);
- }
+ output[BytesPending++] = JsonConstants.Slash;
+ output[BytesPending++] = JsonConstants.Asterisk;
+
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
+
+ output[BytesPending++] = JsonConstants.Asterisk;
+ output[BytesPending++] = JsonConstants.Slash;
}
- private void WriteCommentValue(ReadOnlySpan<char> escapedValue, ref int idx)
+ private void WriteCommentIndented(ReadOnlySpan<byte> utf8Value)
{
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Slash;
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Asterisk;
+ Debug.Assert(utf8Value.Length < int.MaxValue - indent - 4 - s_newLineLength);
- ReadOnlySpan<byte> byteSpan = MemoryMarshal.AsBytes(escapedValue);
- int partialConsumed = 0;
- while (true)
- {
- OperationStatus status = JsonWriterHelper.ToUtf8(byteSpan.Slice(partialConsumed), _buffer.Slice(idx), out int consumed, out int written);
- idx += written;
- if (status == OperationStatus.Done)
- {
- break;
- }
- partialConsumed += consumed;
- AdvanceAndGrow(ref idx);
- }
+ int minRequired = indent + utf8Value.Length + 4; // /*...*/
+ int maxRequired = minRequired + s_newLineLength; // Optionally, 1-2 bytes for new line
- if (_buffer.Length <= idx)
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = JsonConstants.Asterisk;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Slash;
- }
+ Span<byte> output = _memory.Span;
- private void WriteCommentValue(ReadOnlySpan<byte> escapedValue, ref int idx)
- {
- if (_buffer.Length <= idx)
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx);
+ WriteNewLine(output);
}
- _buffer[idx++] = JsonConstants.Slash;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Asterisk;
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- CopyLoop(escapedValue, ref idx);
+ output[BytesPending++] = JsonConstants.Slash;
+ output[BytesPending++] = JsonConstants.Asterisk;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Asterisk;
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.Slash;
+ output[BytesPending++] = JsonConstants.Asterisk;
+ output[BytesPending++] = JsonConstants.Slash;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="DateTime"/> value (as a JSON string) as an element of a JSON array.
public void WriteStringValue(DateTime value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringValueIndented(value);
}
private void WriteStringValueMinimized(DateTime value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatDateTimeOffsetLength + 3; // 2 quotes, and optionally, 1 list separator
- WriteStringValue(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringValueIndented(DateTime value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line
+ int maxRequired = indent + JsonConstants.MaximumFormatDateTimeOffsetLength + 3 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- WriteStringValue(value, ref idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- Advance(idx);
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+
+ private static readonly StandardFormat s_dateTimeStandardFormat = new StandardFormat('O');
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
+
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="DateTimeOffset"/> value (as a JSON string) as an element of a JSON array.
public void WriteStringValue(DateTimeOffset value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringValueIndented(value);
}
private void WriteStringValueMinimized(DateTimeOffset value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatDateTimeOffsetLength + 3; // 2 quotes, and optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- WriteStringValue(value, ref idx);
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ output[BytesPending++] = JsonConstants.Quote;
+
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringValueIndented(DateTimeOffset value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line
+ int maxRequired = indent + JsonConstants.MaximumFormatDateTimeOffsetLength + 3 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
- WriteStringValue(value, ref idx);
+ Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
+ bool result = Utf8Formatter.TryFormat(value, tempSpan, out int bytesWritten, s_dateTimeStandardFormat);
+ Debug.Assert(result);
+ JsonWriterHelper.TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
+ tempSpan.Slice(0, bytesWritten).CopyTo(output.Slice(BytesPending));
+ BytesPending += bytesWritten;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="decimal"/> value (as a JSON number) as an element of a JSON array.
public void WriteNumberValue(decimal value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberValueIndented(value);
}
private void WriteNumberValueMinimized(decimal value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatDecimalLength + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberValueIndented(decimal value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int maxRequired = indent + JsonConstants.MaximumFormatDecimalLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="double"/> value (as a JSON number) as an element of a JSON array.
JsonWriterHelper.ValidateDouble(value);
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberValueIndented(value);
}
private void WriteNumberValueMinimized(double value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatDoubleLength + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberValueIndented(double value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int maxRequired = indent + JsonConstants.MaximumFormatDoubleLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="float"/> value (as a JSON number) as an element of a JSON array.
JsonWriterHelper.ValidateSingle(value);
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberValueIndented(value);
}
private void WriteNumberValueMinimized(float value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatSingleLength + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberValueIndented(float value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int maxRequired = indent + JsonConstants.MaximumFormatSingleLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the value (as a JSON number) as an element of a JSON array.
/// </remarks>
internal void WriteNumberValue(ReadOnlySpan<byte> utf8FormattedNumber)
{
+ JsonWriterHelper.ValidateValue(utf8FormattedNumber);
JsonWriterHelper.ValidateNumber(utf8FormattedNumber);
-
ValidateWritingValue();
- if (_writerOptions.Indented)
+
+ if (Options.Indented)
{
WriteNumberValueIndented(utf8FormattedNumber);
}
_tokenType = JsonTokenType.Number;
}
- private void WriteNumberValueMinimized(ReadOnlySpan<byte> value)
+ private void WriteNumberValueMinimized(ReadOnlySpan<byte> utf8Value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = utf8Value.Length + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
}
- private void WriteNumberValueIndented(ReadOnlySpan<byte> value)
+ private void WriteNumberValueIndented(ReadOnlySpan<byte> utf8Value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(utf8Value.Length < int.MaxValue - indent - 1 - s_newLineLength);
+
+ int maxRequired = indent + utf8Value.Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="Guid"/> value (as a JSON string) as an element of a JSON array.
public void WriteStringValue(Guid value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringValueIndented(value);
}
private void WriteStringValueMinimized(Guid value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatGuidLength + 3; // 2 quotes, and optionally, 1 list separator
- WriteStringValue(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringValueIndented(Guid value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line
+ int maxRequired = indent + JsonConstants.MaximumFormatGuidLength + 3 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = JsonConstants.Quote;
- WriteStringValue(value, ref idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
private void ValidateWritingValue()
{
- if (!_writerOptions.SkipValidation)
+ if (!Options.SkipValidation)
{
if (_inObject)
{
}
}
}
-
- private int WriteCommaAndFormattingPreamble()
- {
- int idx = 0;
- WriteListSeparator(ref idx);
- WriteFormattingPreamble(ref idx);
- return idx;
- }
-
- private void WriteFormattingPreamble(ref int idx)
- {
- if (_tokenType != JsonTokenType.None)
- WriteNewLine(ref idx);
-
- int indent = Indentation;
- while (true)
- {
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
- }
- }
-
- private void WriteListSeparator(ref int idx)
- {
- if (_currentDepth < 0)
- {
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
- }
- }
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics;
+
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the JSON literal "null" as an element of a JSON array.
private void WriteLiteralByOptions(ReadOnlySpan<byte> utf8Value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteLiteralIndented(utf8Value);
}
private void WriteLiteralMinimized(ReadOnlySpan<byte> utf8Value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ Debug.Assert(utf8Value.Length <= 5);
+
+ int maxRequired = utf8Value.Length + 1; // Optionally, 1 list separator
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- CopyLoop(utf8Value, ref idx);
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
}
private void WriteLiteralIndented(ReadOnlySpan<byte> utf8Value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+ Debug.Assert(utf8Value.Length <= 5);
+
+ int maxRequired = indent + utf8Value.Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- CopyLoop(utf8Value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ utf8Value.CopyTo(output.Slice(BytesPending));
+ BytesPending += utf8Value.Length;
}
}
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="int"/> value (as a JSON number) as an element of a JSON array.
public void WriteNumberValue(long value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberValueIndented(value);
}
private void WriteNumberValueMinimized(long value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatInt64Length + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberValueIndented(long value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int maxRequired = indent + JsonConstants.MaximumFormatInt64Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the string text value (as a JSON string) as an element of a JSON array.
/// </summary>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string element of a JSON array.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStringValue(string value, bool escape = true)
- => WriteStringValue(value.AsSpan(), escape);
+ public void WriteStringValue(string value)
+ => WriteStringValue(value.AsSpan());
/// <summary>
/// Writes the UTF-16 text value (as a JSON string) as an element of a JSON array.
/// </summary>
/// <param name="value">The UTF-16 encoded value to be written as a UTF-8 transcoded JSON string element of a JSON array.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStringValue(ReadOnlySpan<char> value, bool escape = true)
+ public void WriteStringValue(ReadOnlySpan<char> value)
{
JsonWriterHelper.ValidateValue(value);
- if (escape)
- {
- WriteStringEscape(value);
- }
- else
- {
- WriteStringByOptions(value);
- }
+ WriteStringEscape(value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
{
int valueIdx = JsonWriterHelper.NeedsEscaping(value);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < value.Length);
if (valueIdx != -1)
{
private void WriteStringByOptions(ReadOnlySpan<char> value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(value);
}
}
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<char> escapedValue)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ Debug.Assert(escapedValue.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - 3);
+
+ // All ASCII, 2 quotes => escapedValue.Length + 2
+ // Optionally, 1 list separator, and up to 3x growth when transcoding
+ int maxRequired = (escapedValue.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 3;
- WriteStringValue(escapedValue, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- Advance(idx);
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringIndented(ReadOnlySpan<char> escapedValue)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedValue.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 3 - s_newLineLength);
+
+ // All ASCII, 2 quotes => indent + escapedValue.Length + 2
+ // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding
+ int maxRequired = indent + (escapedValue.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + 3 + s_newLineLength;
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteStringValue(escapedValue, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ TranscodeAndWrite(escapedValue, output);
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringEscapeValue(ReadOnlySpan<char> value, int firstEscapeIndexVal)
int length = JsonWriterHelper.GetMaxEscapedLength(value.Length, firstEscapeIndexVal);
- Span<char> escapedValue;
- if (length > StackallocThreshold)
- {
- valueArray = ArrayPool<char>.Shared.Rent(length);
- escapedValue = valueArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedValue = new Span<char>(ptr, length);
- }
- }
+ Span<char> escapedValue = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (valueArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(value, escapedValue, firstEscapeIndexVal, out int written);
WriteStringByOptions(escapedValue.Slice(0, written));
/// Writes the UTF-8 text value (as a JSON string) as an element of a JSON array.
/// </summary>
/// <param name="utf8Value">The UTF-8 encoded value to be written as a JSON string element of a JSON array.</param>
- /// <param name="escape">If this is set to false, the writer assumes the value is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The value is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified value is too large.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStringValue(ReadOnlySpan<byte> utf8Value, bool escape = true)
+ public void WriteStringValue(ReadOnlySpan<byte> utf8Value)
{
JsonWriterHelper.ValidateValue(utf8Value);
- if (escape)
- {
- WriteStringEscape(utf8Value);
- }
- else
- {
- WriteStringByOptions(utf8Value);
- }
+ WriteStringEscape(utf8Value);
SetFlagToAddListSeparatorBeforeNextItem();
_tokenType = JsonTokenType.String;
{
int valueIdx = JsonWriterHelper.NeedsEscaping(utf8Value);
- Debug.Assert(valueIdx >= -1 && valueIdx < int.MaxValue / 2);
+ Debug.Assert(valueIdx >= -1 && valueIdx < utf8Value.Length);
if (valueIdx != -1)
{
private void WriteStringByOptions(ReadOnlySpan<byte> utf8Value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteStringIndented(utf8Value);
}
}
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringMinimized(ReadOnlySpan<byte> escapedValue)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ Debug.Assert(escapedValue.Length < int.MaxValue - 3);
+
+ int minRequired = escapedValue.Length + 2; // 2 quotes
+ int maxRequired = minRequired + 1; // Optionally, 1 list separator
- WriteStringValue(escapedValue, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
- Advance(idx);
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
+ // TODO: https://github.com/dotnet/corefx/issues/36958
private void WriteStringIndented(ReadOnlySpan<byte> escapedValue)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ Debug.Assert(escapedValue.Length < int.MaxValue - indent - 3 - s_newLineLength);
+
+ int minRequired = indent + escapedValue.Length + 2; // 2 quotes
+ int maxRequired = minRequired + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteStringValue(escapedValue, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ output[BytesPending++] = JsonConstants.Quote;
+
+ escapedValue.CopyTo(output.Slice(BytesPending));
+ BytesPending += escapedValue.Length;
+
+ output[BytesPending++] = JsonConstants.Quote;
}
private void WriteStringEscapeValue(ReadOnlySpan<byte> utf8Value, int firstEscapeIndexVal)
int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);
- Span<byte> escapedValue;
- if (length > StackallocThreshold)
- {
- valueArray = ArrayPool<byte>.Shared.Rent(length);
- escapedValue = valueArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedValue = new Span<byte>(ptr, length);
- }
- }
+ Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (valueArray = ArrayPool<byte>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, out int written);
WriteStringByOptions(escapedValue.Slice(0, written));
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
namespace System.Text.Json
{
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter
{
/// <summary>
/// Writes the <see cref="uint"/> value (as a JSON number) as an element of a JSON array.
public void WriteNumberValue(ulong value)
{
ValidateWritingValue();
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
WriteNumberValueIndented(value);
}
private void WriteNumberValueMinimized(ulong value)
{
- int idx = 0;
- WriteListSeparator(ref idx);
+ int maxRequired = JsonConstants.MaximumFormatUInt64Length + 1; // Optionally, 1 list separator
- WriteNumberValueFormatLoop(value, ref idx);
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
- Advance(idx);
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
private void WriteNumberValueIndented(ulong value)
{
- int idx = WriteCommaAndFormattingPreamble();
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int maxRequired = indent + JsonConstants.MaximumFormatUInt64Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
+ {
+ Grow(maxRequired);
+ }
+
+ Span<byte> output = _memory.Span;
+
+ if (_currentDepth < 0)
+ {
+ output[BytesPending++] = JsonConstants.ListSeparator;
+ }
+
+ if (_tokenType != JsonTokenType.None)
+ {
+ WriteNewLine(output);
+ }
- WriteNumberValueFormatLoop(value, ref idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
- Advance(idx);
+ bool result = Utf8Formatter.TryFormat(value, output.Slice(BytesPending), out int bytesWritten);
+ Debug.Assert(result);
+ BytesPending += bytesWritten;
}
}
}
using System.Buffers;
using System.Diagnostics;
+using System.IO;
using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
namespace System.Text.Json
{
/// <summary>
/// Provides a high-performance API for forward-only, non-cached writing of UTF-8 encoded JSON text.
+ /// </summary>
+ /// <remarks>
/// It writes the text sequentially with no caching and adheres to the JSON RFC
/// by default (https://tools.ietf.org/html/rfc8259), with the exception of writing comments.
- /// </summary>
+ /// </remarks>
/// <remarks>
/// When the user attempts to write invalid JSON and validation is enabled, it throws
- /// a <see cref="InvalidOperationException"/> with a context specific error message.
- /// Since this type is a ref struct, it does not directly support async. However, it does provide
- /// support for reentrancy to write partial data, and continue writing in chunks.
+ /// an <see cref="InvalidOperationException"/> with a context specific error message.
+ /// </remarks>
+ /// <remarks>
/// To be able to format the output with indentation and whitespace OR to skip validation, create an instance of
- /// <see cref="JsonWriterState"/> and pass that in to the writer.
+ /// <see cref="JsonWriterOptions"/> and pass that in to the writer.
/// </remarks>
- public ref partial struct Utf8JsonWriter
+ public sealed partial class Utf8JsonWriter : IDisposable
+#if BUILDING_INBOX_LIBRARY
+ , IAsyncDisposable
+#endif
{
- private const int StackallocThreshold = 256;
- private const int DefaultGrowthSize = 4096;
+ // Depending on OS, either '\r\n' OR '\n'
+ private static readonly int s_newLineLength = Environment.NewLine.Length;
- private readonly IBufferWriter<byte> _output;
- private int _buffered;
- private Span<byte> _buffer;
+ private const int DefaultGrowthSize = 4096;
- /// <summary>
- /// Returns the total amount of bytes written by the <see cref="Utf8JsonWriter"/> so far
- /// for the current instance of the <see cref="Utf8JsonWriter"/>.
- /// This includes data that has been written beyond what has already been committed.
- /// </summary>
- public long BytesWritten
- {
- get
- {
- Debug.Assert(BytesCommitted <= long.MaxValue - _buffered);
- return BytesCommitted + _buffered;
- }
- }
+ private IBufferWriter<byte> _output;
+ private Stream _stream;
+ private ArrayBufferWriter<byte> _arrayBufferWriter;
- /// <summary>
- /// Returns the total amount of bytes committed to the output by the <see cref="Utf8JsonWriter"/> so far
- /// for the current instance of the <see cref="Utf8JsonWriter"/>.
- /// This is how much the IBufferWriter has advanced.
- /// </summary>
- public long BytesCommitted { get; private set; }
+ private Memory<byte> _memory;
private bool _inObject;
private bool _isNotPrimitive;
private JsonTokenType _tokenType;
- private readonly JsonWriterOptions _writerOptions;
private BitStack _bitStack;
// The highest order bit of _currentDepth is used to discern whether we are writing the first item in a list or not.
// else, no list separator is needed since we are writing the first item.
private int _currentDepth;
+ /// <summary>
+ /// Returns the amount of bytes written by the <see cref="Utf8JsonWriter"/> so far
+ /// that have not yet been flushed to the output and committed.
+ /// </summary>
+ public int BytesPending { get; private set; }
+
+ /// <summary>
+ /// Returns the amount of bytes committed to the output by the <see cref="Utf8JsonWriter"/> so far.
+ /// </summary>
+ /// <remarks>
+ /// In the case of IBufferwriter, this is how much the IBufferWriter has advanced.
+ /// In the case of Stream, this is how much data has been written to the stream.
+ /// </remarks>
+ public long BytesCommitted { get; private set; }
+
+ /// <summary>
+ /// Gets the custom behavior when writing JSON using
+ /// the <see cref="Utf8JsonWriter"/> which indicates whether to format the output
+ /// while writing and whether to skip structural JSON validation or not.
+ /// </summary>
+ public JsonWriterOptions Options { get; }
+
private int Indentation => CurrentDepth * JsonConstants.SpacesPerIndent;
/// <summary>
public int CurrentDepth => _currentDepth & JsonConstants.RemoveFlagsBitMask;
/// <summary>
- /// Returns the current snapshot of the <see cref="Utf8JsonWriter"/> state which must
- /// be captured by the caller and passed back in to the <see cref="Utf8JsonWriter"/> ctor with more data.
+ /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
/// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when there is JSON data that has been written and buffered but not yet flushed to the <see cref="IBufferWriter{Byte}" />.
- /// Getting the state for creating a new <see cref="Utf8JsonWriter"/> without first committing the data that has been written
- /// would result in an inconsistent state. Call Flush before getting the current state.
+ /// <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"/>
+ /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (i.e. with no extra whitespace)
+ /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
+ /// </exception>
+ public Utf8JsonWriter(IBufferWriter<byte> bufferWriter, JsonWriterOptions options = default)
+ {
+ _output = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
+ _stream = default;
+ _arrayBufferWriter = default;
+
+ BytesPending = default;
+ BytesCommitted = default;
+ _memory = default;
+
+ _inObject = default;
+ _isNotPrimitive = default;
+ _tokenType = default;
+ _currentDepth = default;
+ Options = options;
+
+ // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
+ // This way we avoid allocations in the common, default cases, and allocate lazily.
+ _bitStack = default;
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="utf8Json"/>.
+ /// </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"/>
+ /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (i.e. with no extra whitespace)
+ /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
/// </exception>
+ public Utf8JsonWriter(Stream utf8Json, JsonWriterOptions options = default)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+ if (!utf8Json.CanWrite)
+ throw new ArgumentException(SR.StreamNotWritable);
+
+ _stream = utf8Json;
+ _arrayBufferWriter = new ArrayBufferWriter<byte>();
+ _output = _arrayBufferWriter;
+
+ BytesPending = default;
+ BytesCommitted = default;
+ _memory = default;
+
+ _inObject = default;
+ _isNotPrimitive = default;
+ _tokenType = default;
+ _currentDepth = default;
+ Options = options;
+
+ // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
+ // This way we avoid allocations in the common, default cases, and allocate lazily.
+ _bitStack = default;
+ }
+
+ /// <summary>
+ /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used.
+ /// </summary>
/// <remarks>
- /// Unlike the <see cref="Utf8JsonWriter"/>, which is a ref struct, the state can survive
- /// across async/await boundaries and hence this type is required to provide support for reading
- /// in more data asynchronously before continuing with a new instance of the <see cref="Utf8JsonWriter"/>.
+ /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options
+ /// and the original output as the destination (either <see cref="IBufferWriter{Byte}" /> or <see cref="Stream" />).
/// </remarks>
- public JsonWriterState GetCurrentState()
+ public void Reset()
{
- if (_buffered != 0)
+ if (_arrayBufferWriter != null)
{
- throw ThrowHelper.GetInvalidOperationException_CallFlushFirst(_buffered);
+ _arrayBufferWriter.Clear();
}
- return new JsonWriterState
+ ResetHelper();
+ }
+
+ /// <summary>
+ /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="Stream" />.
+ /// </summary>
+ /// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param>
+ /// <remarks>
+ /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options
+ /// but now write to the passed in <see cref="Stream" /> as the new destination.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">
+ /// The instance of <see cref="Utf8JsonWriter"/> has been disposed.
+ /// </exception>
+ 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)
+ {
+ _arrayBufferWriter = new ArrayBufferWriter<byte>();
+ }
+ else
{
- _bytesWritten = BytesWritten,
- _bytesCommitted = BytesCommitted,
- _inObject = _inObject,
- _isNotPrimitive = _isNotPrimitive,
- _tokenType = _tokenType,
- _currentDepth = _currentDepth,
- _writerOptions = _writerOptions,
- _bitStack = _bitStack,
- };
+ _arrayBufferWriter.Clear();
+ }
+ _output = _arrayBufferWriter;
+
+ ResetHelper();
}
/// <summary>
- /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
+ /// Resets the <see cref="Utf8JsonWriter"/> internal state so that it can be re-used with the new instance of <see cref="IBufferWriter{Byte}" />.
/// </summary>
/// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
- /// <param name="state">If this is the first call to the ctor, pass in a default state. Otherwise,
- /// capture the state from the previous instance of the <see cref="Utf8JsonWriter"/> and pass that back.</param>
+ /// <remarks>
+ /// The <see cref="Utf8JsonWriter"/> will continue to use the original writer options
+ /// but now write to the passed in <see cref="IBufferWriter{Byte}" /> as the new destination.
+ /// </remarks>
/// <exception cref="ArgumentNullException">
/// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
/// </exception>
- /// <remarks>
- /// Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to it.
- /// This is the reason why the ctor accepts a <see cref="JsonWriterState"/>.
- /// </remarks>
- public Utf8JsonWriter(IBufferWriter<byte> bufferWriter, JsonWriterState state = default)
+ /// <exception cref="ObjectDisposedException">
+ /// The instance of <see cref="Utf8JsonWriter"/> has been disposed.
+ /// </exception>
+ public void Reset(IBufferWriter<byte> bufferWriter)
{
+ CheckNotDisposed();
+
_output = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
- _buffered = 0;
- BytesCommitted = 0;
- _buffer = _output.GetSpan();
+ _stream = default;
+
+ if (_arrayBufferWriter != null)
+ {
+ _arrayBufferWriter.Clear();
+ }
- _inObject = state._inObject;
- _isNotPrimitive = state._isNotPrimitive;
- _tokenType = state._tokenType;
- _writerOptions = state._writerOptions;
- _bitStack = state._bitStack;
+ _arrayBufferWriter = default;
- _currentDepth = state._currentDepth;
+ ResetHelper();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void Advance(int count)
+ private void ResetHelper()
{
- Debug.Assert(count >= 0 && _buffered <= int.MaxValue - count);
+ BytesPending = default;
+ BytesCommitted = default;
+ _memory = default;
+
+ _inObject = default;
+ _isNotPrimitive = default;
+ _tokenType = default;
+ _currentDepth = default;
+
+ // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
+ // This way we avoid allocations in the common, default cases, and allocate lazily.
+ _bitStack = default;
+ }
- _buffered += count;
- _buffer = _buffer.Slice(count);
+ private void CheckNotDisposed()
+ {
+ if (_output == null)
+ {
+ throw new ObjectDisposedException(nameof(Utf8JsonWriter));
+ }
}
/// <summary>
- /// Advances the underlying <see cref="IBufferWriter{Byte}" /> based on what has been written so far.
+ /// Commits the JSON text written so far which makes it visible to the output destination.
/// </summary>
- /// <param name="isFinalBlock">Let's the writer know whether more data will be written. This is used to validate
- /// that the JSON written so far is structurally valid if no more data is to follow.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when incomplete JSON has been written and <paramref name="isFinalBlock"/> is true.
- /// (for example when an open object or array needs to be closed).
- /// </exception>
- public void Flush(bool isFinalBlock = true)
+ /// <remarks>
+ /// In the case of IBufferWriter, this advances the underlying <see cref="IBufferWriter{Byte}" /> based on what has been written so far.
+ /// In the case of Stream, this writes the data to the stream and flushes it.
+ /// </remarks>
+ public void Flush()
{
- if (isFinalBlock && !_writerOptions.SkipValidation && (CurrentDepth != 0 || _tokenType == JsonTokenType.None))
- ThrowHelper.ThrowInvalidOperationException_DepthNonZeroOrEmptyJson(_currentDepth);
+ FlushHelper();
+
+ if (_stream != null)
+ {
+ FlushHelperStream();
+ _stream.Flush();
+ }
+
+ _memory = default;
+ BytesCommitted += BytesPending;
+ BytesPending = 0;
+ }
+
+ /// <summary>
+ /// Commits any left over JSON text that has not yet been flushed and releases all resources used by the current instance.
+ /// </summary>
+ /// <remarks>
+ /// In the case of IBufferWriter, this advances the underlying <see cref="IBufferWriter{Byte}" /> based on what has been written so far.
+ /// In the case of Stream, this writes the data to the stream and flushes it.
+ /// </remarks>
+ /// <remarks>
+ /// The <see cref="Utf8JsonWriter"/> instance cannot be re-used after disposing.
+ /// </remarks>
+ public void Dispose()
+ {
+ if (_output == null)
+ {
+ return;
+ }
Flush();
+ ResetHelper();
+
+ _stream = null;
+ _arrayBufferWriter = null;
+ _output = null;
+ }
+
+#if BUILDING_INBOX_LIBRARY
+ /// <summary>
+ /// Asynchronously commits any left over JSON text that has not yet been flushed and releases all resources used by the current instance.
+ /// </summary>
+ /// <remarks>
+ /// In the case of IBufferWriter, this advances the underlying <see cref="IBufferWriter{Byte}" /> based on what has been written so far.
+ /// In the case of Stream, this writes the data to the stream and flushes it.
+ /// </remarks>
+ /// <remarks>
+ /// The <see cref="Utf8JsonWriter"/> instance cannot be re-used after disposing.
+ /// </remarks>
+ public async ValueTask DisposeAsync()
+ {
+ if (_output == null)
+ {
+ return;
+ }
+
+ await FlushAsync().ConfigureAwait(false);
+ ResetHelper();
+
+ _stream = null;
+ _arrayBufferWriter = null;
+ _output = null;
+ }
+#endif
+
+ /// <summary>
+ /// Asynchronously commits the JSON text written so far which makes it visible to the output destination.
+ /// </summary>
+ /// <remarks>
+ /// In the case of IBufferWriter, this advances the underlying <see cref="IBufferWriter{Byte}" /> based on what has been written so far.
+ /// In the case of Stream, this writes the data to the stream and flushes it asynchronously, while monitoring cancellation requests.
+ /// </remarks>
+ public async Task FlushAsync(CancellationToken cancellationToken = default)
+ {
+ FlushHelper();
+
+ if (_stream != null)
+ {
+ Debug.Assert(_arrayBufferWriter != null);
+ Debug.Assert(BytesPending == _arrayBufferWriter.WrittenCount);
+ if (BytesPending != 0)
+ {
+#if BUILDING_INBOX_LIBRARY
+ await _stream.WriteAsync(_arrayBufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
+#else
+ Debug.Assert(_arrayBufferWriter.WrittenMemory.Length == _arrayBufferWriter.WrittenCount);
+ await _stream.WriteAsync(_arrayBufferWriter.WrittenMemory.ToArray(), 0, _arrayBufferWriter.WrittenCount, cancellationToken).ConfigureAwait(false);
+#endif
+ _arrayBufferWriter.Clear();
+ }
+ await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ _memory = default;
+ BytesCommitted += BytesPending;
+ BytesPending = 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void FlushHelperStream()
+ {
+ Debug.Assert(_arrayBufferWriter != null);
+ Debug.Assert(BytesPending == _arrayBufferWriter.WrittenCount);
+ if (BytesPending != 0)
+ {
+#if BUILDING_INBOX_LIBRARY
+ _stream.Write(_arrayBufferWriter.WrittenSpan);
+#else
+ Debug.Assert(_arrayBufferWriter.WrittenSpan.Length == _arrayBufferWriter.WrittenCount);
+ _stream.Write(_arrayBufferWriter.WrittenSpan.ToArray(), 0, _arrayBufferWriter.WrittenCount);
+#endif
+ _arrayBufferWriter.Clear();
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void Flush()
+ private void FlushHelper()
{
- _output.Advance(_buffered);
- BytesCommitted += _buffered;
- _buffered = 0;
+ _output.Advance(BytesPending);
}
/// <summary>
if (CurrentDepth >= JsonConstants.MaxWriterDepth)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.DepthTooLarge, _currentDepth, token: default, tokenType: default);
- if (_writerOptions.IndentedOrNotSkipValidation)
+ if (Options.IndentedOrNotSkipValidation)
{
WriteStartSlow(token);
}
private void WriteStartMinimized(byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
+ if (_memory.Length - BytesPending < 2) // 1 start token, and optionally, 1 list separator
{
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
+ Grow(2);
}
- if (_buffer.Length <= idx)
+ Span<byte> output = _memory.Span;
+ if (_currentDepth < 0)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- _buffer[idx++] = token;
-
- Advance(idx);
+ output[BytesPending++] = token;
}
private void WriteStartSlow(byte token)
{
- Debug.Assert(_writerOptions.Indented || !_writerOptions.SkipValidation);
+ Debug.Assert(Options.Indented || !Options.SkipValidation);
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
- if (!_writerOptions.SkipValidation)
+ if (!Options.SkipValidation)
{
ValidateStart();
UpdateBitStackOnStart(token);
}
else
{
- Debug.Assert(!_writerOptions.SkipValidation);
+ Debug.Assert(!Options.SkipValidation);
ValidateStart();
UpdateBitStackOnStart(token);
WriteStartMinimized(token);
private void WriteStartIndented(byte token)
{
- int idx = 0;
- if (_currentDepth < 0)
+ int indent = Indentation;
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+
+ int minRequired = indent + 1; // 1 start token
+ int maxRequired = minRequired + 3; // Optionally, 1 list separator and 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- if (_buffer.Length <= idx)
- {
- GrowAndEnsure();
- }
- _buffer[idx++] = JsonConstants.ListSeparator;
+ Grow(maxRequired);
}
- if (_tokenType != JsonTokenType.None)
- WriteNewLine(ref idx);
+ Span<byte> output = _memory.Span;
- int indent = Indentation;
- while (true)
+ if (_currentDepth < 0)
{
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.ListSeparator;
}
- if (_buffer.Length <= idx)
+ if (_tokenType != JsonTokenType.None)
{
- AdvanceAndGrow(ref idx);
+ WriteNewLine(output);
}
- _buffer[idx++] = token;
- Advance(idx);
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = token;
}
/// <summary>
/// Writes the beginning of a JSON array with a property name as the key.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON array to be written.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartArray(ReadOnlySpan<byte> utf8PropertyName, bool escape = true)
+ public void WriteStartArray(ReadOnlySpan<byte> utf8PropertyName)
{
ValidatePropertyNameAndDepth(utf8PropertyName);
- if (escape)
- {
- WriteStartEscape(utf8PropertyName, JsonConstants.OpenBracket);
- }
- else
- {
- WriteStartByOptions(utf8PropertyName, JsonConstants.OpenBracket);
- }
+ WriteStartEscape(utf8PropertyName, JsonConstants.OpenBracket);
_currentDepth &= JsonConstants.RemoveFlagsBitMask;
_currentDepth++;
/// Writes the beginning of a JSON object with a property name as the key.
/// </summary>
/// <param name="utf8PropertyName">The UTF-8 encoded property name of the JSON object to be written.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartObject(ReadOnlySpan<byte> utf8PropertyName, bool escape = true)
+ public void WriteStartObject(ReadOnlySpan<byte> utf8PropertyName)
{
ValidatePropertyNameAndDepth(utf8PropertyName);
- if (escape)
- {
- WriteStartEscape(utf8PropertyName, JsonConstants.OpenBrace);
- }
- else
- {
- WriteStartByOptions(utf8PropertyName, JsonConstants.OpenBrace);
- }
+ WriteStartEscape(utf8PropertyName, JsonConstants.OpenBrace);
_currentDepth &= JsonConstants.RemoveFlagsBitMask;
_currentDepth++;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(utf8PropertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < utf8PropertyName.Length);
if (propertyIdx != -1)
{
private void WriteStartByOptions(ReadOnlySpan<byte> utf8PropertyName, byte token)
{
ValidateWritingProperty(token);
- int idx;
- if (_writerOptions.Indented)
+
+ if (Options.Indented)
{
- idx = WritePropertyNameIndented(utf8PropertyName);
+ WritePropertyNameIndented(utf8PropertyName, token);
}
else
{
- idx = WritePropertyNameMinimized(utf8PropertyName);
- }
-
- if (1 > _buffer.Length - idx)
- {
- AdvanceAndGrow(ref idx, 1);
+ WritePropertyNameMinimized(utf8PropertyName, token);
}
-
- _buffer[idx++] = token;
-
- Advance(idx);
}
private void WriteStartEscapeProperty(ReadOnlySpan<byte> utf8PropertyName, byte token, int firstEscapeIndexProp)
byte[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(utf8PropertyName.Length, firstEscapeIndexProp);
- Span<byte> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<byte>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- byte* ptr = stackalloc byte[length];
- escapedPropertyName = new Span<byte>(ptr, length);
- }
- }
+
+ Span<byte> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[length] :
+ (propertyArray = ArrayPool<byte>.Shared.Rent(length));
JsonWriterHelper.EscapeString(utf8PropertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
/// Writes the beginning of a JSON array with a property name as the key.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON array to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartArray(string propertyName, bool escape = true)
- => WriteStartArray(propertyName.AsSpan(), escape);
+ public void WriteStartArray(string propertyName)
+ => WriteStartArray(propertyName.AsSpan());
/// <summary>
/// Writes the beginning of a JSON object with a property name as the key.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartObject(string propertyName, bool escape = true)
- => WriteStartObject(propertyName.AsSpan(), escape);
+ public void WriteStartObject(string propertyName)
+ => WriteStartObject(propertyName.AsSpan());
/// <summary>
/// Writes the beginning of a JSON array with a property name as the key.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON array to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartArray(ReadOnlySpan<char> propertyName, bool escape = true)
+ public void WriteStartArray(ReadOnlySpan<char> propertyName)
{
ValidatePropertyNameAndDepth(propertyName);
- if (escape)
- {
- WriteStartEscape(propertyName, JsonConstants.OpenBracket);
- }
- else
- {
- WriteStartByOptions(propertyName, JsonConstants.OpenBracket);
- }
+ WriteStartEscape(propertyName, JsonConstants.OpenBracket);
_currentDepth &= JsonConstants.RemoveFlagsBitMask;
_currentDepth++;
/// Writes the beginning of a JSON object with a property name as the key.
/// </summary>
/// <param name="propertyName">The UTF-16 encoded property name of the JSON object to be transcoded and written as UTF-8.</param>
- /// <param name="escape">If this is set to false, the writer assumes the property name is properly escaped and skips the escaping step.</param>
+ /// <remarks>
+ /// The property name is escaped before writing.
+ /// </remarks>
/// <exception cref="ArgumentException">
/// Thrown when the specified property name is too large.
/// </exception>
/// Thrown when the depth of the JSON has exceeded the maximum depth of 1000
/// OR if this would result in an invalid JSON to be written (while validation is enabled).
/// </exception>
- public void WriteStartObject(ReadOnlySpan<char> propertyName, bool escape = true)
+ public void WriteStartObject(ReadOnlySpan<char> propertyName)
{
ValidatePropertyNameAndDepth(propertyName);
- if (escape)
- {
- WriteStartEscape(propertyName, JsonConstants.OpenBrace);
- }
- else
- {
- WriteStartByOptions(propertyName, JsonConstants.OpenBrace);
- }
+ WriteStartEscape(propertyName, JsonConstants.OpenBrace);
_currentDepth &= JsonConstants.RemoveFlagsBitMask;
_currentDepth++;
{
int propertyIdx = JsonWriterHelper.NeedsEscaping(propertyName);
- Debug.Assert(propertyIdx >= -1 && propertyIdx < int.MaxValue / 2);
+ Debug.Assert(propertyIdx >= -1 && propertyIdx < propertyName.Length);
if (propertyIdx != -1)
{
private void WriteStartByOptions(ReadOnlySpan<char> propertyName, byte token)
{
ValidateWritingProperty(token);
- int idx;
- if (_writerOptions.Indented)
+
+ if (Options.Indented)
{
- idx = WritePropertyNameIndented(propertyName);
+ WritePropertyNameIndented(propertyName, token);
}
else
{
- idx = WritePropertyNameMinimized(propertyName);
- }
-
- if (1 > _buffer.Length - idx)
- {
- AdvanceAndGrow(ref idx, 1);
+ WritePropertyNameMinimized(propertyName, token);
}
-
- _buffer[idx++] = token;
-
- Advance(idx);
}
private void WriteStartEscapeProperty(ReadOnlySpan<char> propertyName, byte token, int firstEscapeIndexProp)
char[] propertyArray = null;
int length = JsonWriterHelper.GetMaxEscapedLength(propertyName.Length, firstEscapeIndexProp);
- Span<char> escapedPropertyName;
- if (length > StackallocThreshold)
- {
- propertyArray = ArrayPool<char>.Shared.Rent(length);
- escapedPropertyName = propertyArray;
- }
- else
- {
- // Cannot create a span directly since it gets passed to instance methods on a ref struct.
- unsafe
- {
- char* ptr = stackalloc char[length];
- escapedPropertyName = new Span<char>(ptr, length);
- }
- }
+
+ Span<char> escapedPropertyName = length <= JsonConstants.StackallocThreshold ?
+ stackalloc char[length] :
+ (propertyArray = ArrayPool<char>.Shared.Rent(length));
+
JsonWriterHelper.EscapeString(propertyName, escapedPropertyName, firstEscapeIndexProp, out int written);
WriteStartByOptions(escapedPropertyName.Slice(0, written), token);
private void WriteEnd(byte token)
{
- if (_writerOptions.IndentedOrNotSkipValidation)
+ if (Options.IndentedOrNotSkipValidation)
{
WriteEndSlow(token);
}
private void WriteEndMinimized(byte token)
{
- if (_buffer.Length < 1)
+ if (_memory.Length - BytesPending < 1) // 1 end token
{
- GrowAndEnsure();
+ Grow(1);
}
- _buffer[0] = token;
- Advance(1);
+ Span<byte> output = _memory.Span;
+ output[BytesPending++] = token;
}
private void WriteEndSlow(byte token)
{
- Debug.Assert(_writerOptions.Indented || !_writerOptions.SkipValidation);
+ Debug.Assert(Options.Indented || !Options.SkipValidation);
- if (_writerOptions.Indented)
+ if (Options.Indented)
{
- if (!_writerOptions.SkipValidation)
+ if (!Options.SkipValidation)
{
ValidateEnd(token);
}
}
else
{
- Debug.Assert(!_writerOptions.SkipValidation);
+ Debug.Assert(!Options.SkipValidation);
ValidateEnd(token);
WriteEndMinimized(token);
}
}
else
{
- int idx = 0;
- WriteNewLine(ref idx);
-
int indent = Indentation;
+
// Necessary if WriteEndX is called without a corresponding WriteStartX first.
if (indent != 0)
{
// current depth yet, explicitly subtract here.
indent -= JsonConstants.SpacesPerIndent;
}
- while (true)
- {
- bool result = JsonWriterHelper.TryWriteIndentation(_buffer.Slice(idx), indent, out int bytesWritten);
- idx += bytesWritten;
- if (result)
- {
- break;
- }
- indent -= bytesWritten;
- AdvanceAndGrow(ref idx);
- }
- if (_buffer.Length <= idx)
+ Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth);
+ Debug.Assert(Options.SkipValidation || _tokenType != JsonTokenType.None);
+
+ int maxRequired = indent + 3; // 1 end token, 1-2 bytes for new line
+
+ if (_memory.Length - BytesPending < maxRequired)
{
- AdvanceAndGrow(ref idx);
+ Grow(maxRequired);
}
- _buffer[idx++] = token;
- Advance(idx);
+ Span<byte> output = _memory.Span;
+
+ WriteNewLine(output);
+
+ JsonWriterHelper.WriteIndentation(output.Slice(BytesPending), indent);
+ BytesPending += indent;
+
+ output[BytesPending++] = token;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteNewLine(ref int idx)
+ private void WriteNewLine(Span<byte> output)
{
// Write '\r\n' OR '\n', depending on OS
- if (Environment.NewLine.Length == 2)
- {
- if (_buffer.Length <= idx)
- {
- AdvanceAndGrow(ref idx);
- }
- _buffer[idx++] = JsonConstants.CarriageReturn;
- }
-
- if (_buffer.Length <= idx)
+ if (s_newLineLength == 2)
{
- AdvanceAndGrow(ref idx);
+ output[BytesPending++] = JsonConstants.CarriageReturn;
}
- _buffer[idx++] = JsonConstants.LineFeed;
+ output[BytesPending++] = JsonConstants.LineFeed;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
}
}
- private void GrowAndEnsure()
+ private void Grow(int requiredSize)
{
- Flush();
- int previousSpanLength = _buffer.Length;
- Debug.Assert(previousSpanLength < DefaultGrowthSize);
- _buffer = _output.GetSpan(DefaultGrowthSize);
- if (_buffer.Length <= previousSpanLength)
- {
- ThrowHelper.ThrowArgumentException(ExceptionResource.FailedToGetLargerSpan);
- }
- }
+ Debug.Assert(requiredSize > 0);
- private void GrowAndEnsure(int minimumSize)
- {
- Flush();
- Debug.Assert(minimumSize < DefaultGrowthSize);
- _buffer = _output.GetSpan(DefaultGrowthSize);
- if (_buffer.Length < minimumSize)
+ if (_memory.Length == 0)
{
- ThrowHelper.ThrowArgumentException(ExceptionResource.FailedToGetMinimumSizeSpan, minimumSize);
+ Debug.Assert(BytesPending == 0);
+ _memory = _output.GetMemory();
+ if (_memory.Length >= requiredSize)
+ {
+ return;
+ }
}
- }
- private void AdvanceAndGrow(ref int alreadyWritten)
- {
- Debug.Assert(alreadyWritten >= 0);
- Advance(alreadyWritten);
- GrowAndEnsure();
- alreadyWritten = 0;
- }
+ FlushHelper();
- private void AdvanceAndGrow(ref int alreadyWritten, int minimumSize)
- {
- Debug.Assert(minimumSize >= 1 && minimumSize <= 128);
- Advance(alreadyWritten);
- GrowAndEnsure(minimumSize);
- alreadyWritten = 0;
- }
+ int sizeHint = Math.Max(DefaultGrowthSize, requiredSize);
- private void CopyLoop(ReadOnlySpan<byte> span, ref int idx)
- {
- while (true)
+ if (_stream != null)
{
- if (span.Length <= _buffer.Length - idx)
+ FlushHelperStream();
+ _memory = _output.GetMemory(sizeHint);
+
+ Debug.Assert(_memory.Length >= sizeHint);
+ }
+ else
+ {
+ _memory = _output.GetMemory(sizeHint);
+
+ if (_memory.Length < sizeHint)
{
- span.CopyTo(_buffer.Slice(idx));
- idx += span.Length;
- break;
+ ThrowHelper.ThrowInvalidOperationException_NeedLargerSpan();
}
-
- span.Slice(0, _buffer.Length - idx).CopyTo(_buffer.Slice(idx));
- span = span.Slice(_buffer.Length - idx);
- idx = _buffer.Length;
- AdvanceAndGrow(ref idx);
}
+
+ BytesCommitted += BytesPending;
+ BytesPending = 0;
}
private void SetFlagToAddListSeparatorBeforeNextItem()
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-
-namespace System.Text.Json.Tests
-{
- internal class ArrayBufferWriter : IBufferWriter<byte>, IDisposable
- {
- private ResizableArray<byte> _buffer;
-
- public ArrayBufferWriter(int capacity)
- {
- _buffer = new ResizableArray<byte>(ArrayPool<byte>.Shared.Rent(capacity));
- }
-
- public int CommitedByteCount => _buffer.Count;
-
- public void Clear()
- {
- _buffer.Count = 0;
- }
-
- public ArraySegment<byte> Free => _buffer.Free;
-
- public ArraySegment<byte> Formatted => _buffer.Full;
-
- public Memory<byte> GetMemory(int minimumLength = 0)
- {
- if (minimumLength < 1)
- {
- minimumLength = 1;
- }
-
- if (minimumLength > _buffer.FreeCount)
- {
- int doubleCount = _buffer.FreeCount * 2;
- int newSize = minimumLength > doubleCount ? minimumLength : doubleCount;
- byte[] newArray = ArrayPool<byte>.Shared.Rent(newSize + _buffer.Count);
- byte[] oldArray = _buffer.Resize(newArray);
- ArrayPool<byte>.Shared.Return(oldArray);
- }
-
- return _buffer.FreeMemory;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span<byte> GetSpan(int minimumLength = 0)
- {
- if (minimumLength < 1)
- {
- minimumLength = 1;
- }
-
- if (minimumLength > _buffer.FreeCount)
- {
- int doubleCount = _buffer.FreeCount * 2;
- int newSize = minimumLength > doubleCount ? minimumLength : doubleCount;
- byte[] newArray = ArrayPool<byte>.Shared.Rent(newSize + _buffer.Count);
- byte[] oldArray = _buffer.Resize(newArray);
- ArrayPool<byte>.Shared.Return(oldArray);
- }
-
- return _buffer.FreeSpan;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Advance(int bytes)
- {
- _buffer.Count += bytes;
- if (_buffer.Count > _buffer.Capacity)
- {
- throw new InvalidOperationException("More bytes commited than returned from FreeBuffer");
- }
- }
-
- public void Dispose()
- {
- byte[] array = _buffer.Array;
- _buffer.Array = null;
- ArrayPool<byte>.Shared.Return(array);
- }
- }
-}
public byte[] Formatted => _buffer.AsSpan(0, _count).ToArray();
+ public int FormattedCount => _count;
+
public Memory<byte> GetMemory(int minimumLength = 0) => _buffer.AsMemory(_count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int bytes)
{
- _count += bytes;
- if (_count > _buffer.Length)
+ if (_count > _buffer.Length - bytes)
{
throw new InvalidOperationException("Cannot advance past the end of the buffer.");
}
+ _count += bytes;
}
}
}
Assert.Throws<ObjectDisposedException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsValue(ref writer);
+ root.WriteAsValue(writer);
});
Assert.Throws<ObjectDisposedException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsProperty(ReadOnlySpan<char>.Empty, ref writer);
+ root.WriteAsProperty(ReadOnlySpan<char>.Empty, writer);
});
Assert.Throws<ObjectDisposedException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsProperty(ReadOnlySpan<byte>.Empty, ref writer);
+ root.WriteAsProperty(ReadOnlySpan<byte>.Empty, writer);
});
}
}
Assert.Throws<InvalidOperationException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsValue(ref writer);
+ root.WriteAsValue(writer);
});
Assert.Throws<InvalidOperationException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsProperty(ReadOnlySpan<char>.Empty, ref writer);
+ root.WriteAsProperty(ReadOnlySpan<char>.Empty, writer);
});
Assert.Throws<InvalidOperationException>(() =>
{
Utf8JsonWriter writer = default;
- root.WriteAsProperty(ReadOnlySpan<byte>.Empty, ref writer);
+ root.WriteAsProperty(ReadOnlySpan<byte>.Empty, writer);
});
}
// See the LICENSE file in the project root for more information.
using Xunit;
+using System.Buffers;
namespace System.Text.Json.Tests
{
{
WriteSimpleValue(indented, "false");
}
-
+
[Theory]
[InlineData(false)]
[InlineData(true)]
[Theory]
[InlineData(false)]
[InlineData(true)]
+ public static void WriteNumberAsPropertyWithLargeName(bool indented)
+ {
+ var charArray = new char[300];
+ charArray.AsSpan().Fill('a');
+ charArray[0] = (char)0xEA;
+ var propertyName = new string(charArray);
+
+ WritePropertyValueBothForms(
+ indented,
+ propertyName,
+ "42",
+ @"{
+ ""\u00ea" + propertyName.Substring(1) + @""": 42
+}",
+ $"{{\"\\u00ea{propertyName.Substring(1)}\":42}}");
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
public static void WriteNumberScientificAsProperty(bool indented)
{
WritePropertyValueBothForms(
closeBrackets.Fill((byte)']');
jsonIn.AsSpan(SpacesPre + TargetDepth + SpacesSplit + TargetDepth).Fill((byte)' ');
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(jsonIn.Length))
+ var buffer = new ArrayBufferWriter<byte>(jsonIn.Length);
using (JsonDocument doc = JsonDocument.Parse(jsonIn, optionsCopy))
{
var writer = new Utf8JsonWriter(buffer);
- doc.RootElement.WriteAsValue(ref writer);
+ doc.RootElement.WriteAsValue(writer);
writer.Flush();
- ArraySegment<byte> formattedSegment = buffer.Formatted;
- ReadOnlySpan<byte> formatted = formattedSegment;
+ ReadOnlySpan<byte> formatted = buffer.WrittenSpan;
Assert.Equal(TargetDepth + TargetDepth, formatted.Length);
Assert.True(formatted.Slice(0, TargetDepth).SequenceEqual(openBrackets), "OpenBrackets match");
[InlineData(true)]
public static void WritePropertyOutsideObject(bool skipValidation)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (var doc = JsonDocument.Parse("[ null, false, true, \"hi\", 5, {}, [] ]", s_readerOptions))
{
JsonElement root = doc.RootElement;
- JsonWriterState state = new JsonWriterState(
- new JsonWriterOptions
- {
- SkipValidation = skipValidation,
- });
+ var options = new JsonWriterOptions
+ {
+ SkipValidation = skipValidation,
+ };
const string CharLabel = "char";
byte[] byteUtf8 = Encoding.UTF8.GetBytes("byte");
- Utf8JsonWriter writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
if (skipValidation)
{
foreach (JsonElement val in root.EnumerateArray())
{
- val.WriteAsProperty(CharLabel.AsSpan(), ref writer);
- val.WriteAsProperty(byteUtf8, ref writer);
+ val.WriteAsProperty(CharLabel.AsSpan(), writer);
+ val.WriteAsProperty(byteUtf8, writer);
}
writer.Flush();
{
JsonTestHelper.AssertThrows<InvalidOperationException>(
ref writer,
- (ref Utf8JsonWriter w) => val.WriteAsProperty(CharLabel.AsSpan(), ref w));
+ (ref Utf8JsonWriter w) => val.WriteAsProperty(CharLabel.AsSpan(), w));
JsonTestHelper.AssertThrows<InvalidOperationException>(
ref writer,
- (ref Utf8JsonWriter w) => val.WriteAsProperty(byteUtf8, ref w));
+ (ref Utf8JsonWriter w) => val.WriteAsProperty(byteUtf8, w));
}
- JsonTestHelper.AssertThrows<InvalidOperationException>(
- ref writer,
- (ref Utf8JsonWriter w) => w.Flush());
-
- writer.Flush(isFinalBlock: false);
+ writer.Flush();
AssertContents("", buffer);
}
[InlineData(true)]
public static void WriteValueInsideObject(bool skipValidation)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (var doc = JsonDocument.Parse("[ null, false, true, \"hi\", 5, {}, [] ]", s_readerOptions))
{
JsonElement root = doc.RootElement;
- JsonWriterState state = new JsonWriterState(
- new JsonWriterOptions
- {
- SkipValidation = skipValidation,
- });
+ var options = new JsonWriterOptions
+ {
+ SkipValidation = skipValidation,
+ };
- Utf8JsonWriter writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
writer.WriteStartObject();
if (skipValidation)
{
foreach (JsonElement val in root.EnumerateArray())
{
- val.WriteAsValue(ref writer);
+ val.WriteAsValue(writer);
}
writer.WriteEndObject();
{
JsonTestHelper.AssertThrows<InvalidOperationException>(
ref writer,
- (ref Utf8JsonWriter w) => val.WriteAsValue(ref w));
+ (ref Utf8JsonWriter w) => val.WriteAsValue(w));
}
writer.WriteEndObject();
private static void WriteSimpleValue(bool indented, string jsonIn, string jsonOut = null)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (JsonDocument doc = JsonDocument.Parse($" [ {jsonIn} ]", s_readerOptions))
{
JsonElement target = doc.RootElement[0];
- var state = new JsonWriterState(
- new JsonWriterOptions
- {
- Indented = indented,
- });
+ var options = new JsonWriterOptions
+ {
+ Indented = indented,
+ };
- var writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
- target.WriteAsValue(ref writer);
+ target.WriteAsValue(writer);
writer.Flush();
AssertContents(jsonOut ?? jsonIn, buffer);
string expectedIndent,
string expectedMinimal)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (JsonDocument doc = JsonDocument.Parse($" [ {jsonIn} ]", s_readerOptions))
{
JsonElement target = doc.RootElement[0];
- var state = new JsonWriterState(
- new JsonWriterOptions
- {
- Indented = indented,
- });
+ var options = new JsonWriterOptions
+ {
+ Indented = indented,
+ };
- var writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
- target.WriteAsValue(ref writer);
+ target.WriteAsValue(writer);
writer.Flush();
if (indented && s_replaceNewlines)
string expectedIndent,
string expectedMinimal)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (JsonDocument doc = JsonDocument.Parse($" [ {jsonIn} ]", s_readerOptions))
{
JsonElement target = doc.RootElement[0];
- var state = new JsonWriterState(
- new JsonWriterOptions
- {
- Indented = indented,
- });
+ var options = new JsonWriterOptions
+ {
+ Indented = indented,
+ };
- var writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
writer.WriteStartObject();
- target.WriteAsProperty(propertyName, ref writer);
+ target.WriteAsProperty(propertyName, writer);
writer.WriteEndObject();
writer.Flush();
string expectedIndent,
string expectedMinimal)
{
- using (ArrayBufferWriter buffer = new ArrayBufferWriter(1024))
+ var buffer = new ArrayBufferWriter<byte>(1024);
using (JsonDocument doc = JsonDocument.Parse($" [ {jsonIn} ]", s_readerOptions))
{
JsonElement target = doc.RootElement[0];
- var state = new JsonWriterState(
- new JsonWriterOptions
- {
- Indented = indented,
- });
+ var options = new JsonWriterOptions
+ {
+ Indented = indented,
+ };
- var writer = new Utf8JsonWriter(buffer, state);
+ var writer = new Utf8JsonWriter(buffer, options);
writer.WriteStartObject();
- target.WriteAsProperty(propertyName, ref writer);
+ target.WriteAsProperty(propertyName, writer);
writer.WriteEndObject();
writer.Flush();
}
}
- private static void AssertContents(string expectedValue, ArrayBufferWriter buffer)
+ private static void AssertContents(string expectedValue, ArrayBufferWriter<byte> buffer)
{
Assert.Equal(
expectedValue,
Encoding.UTF8.GetString(
- buffer.Formatted
+ buffer.WrittenSpan
#if netstandard
- .AsSpan().ToArray()
+ .ToArray()
#endif
));
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Tests
+{
+ public static partial class JsonWriterOptionsTests
+ {
+ [Fact]
+ public static void JsonWriterOptionsDefaultCtor()
+ {
+ JsonWriterOptions options = default;
+
+ var expectedOption = new JsonWriterOptions
+ {
+ Indented = false,
+ SkipValidation = false
+ };
+ Assert.Equal(expectedOption, options);
+ }
+
+ [Fact]
+ public static void JsonWriterOptionsCtor()
+ {
+ var options = new JsonWriterOptions();
+
+ var expectedOption = new JsonWriterOptions
+ {
+ Indented = false,
+ SkipValidation = false
+ };
+ Assert.Equal(expectedOption, options);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public static void JsonWriterOptions(bool indented, bool skipValidation)
+ {
+ var options = new JsonWriterOptions();
+ options.Indented = indented;
+ options.SkipValidation = skipValidation;
+
+ var expectedOption = new JsonWriterOptions
+ {
+ Indented = indented,
+ SkipValidation = skipValidation
+ };
+ Assert.Equal(expectedOption, options);
+ }
+ }
+}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Xunit;
-
-namespace System.Text.Json.Tests
-{
- public static partial class JsonWriterStateTests
- {
- [Fact]
- public static void DefaultJsonWriterState()
- {
- JsonWriterState state = default;
- Assert.Equal(0, state.BytesCommitted);
- Assert.Equal(0, state.BytesWritten);
-
- var expectedOption = new JsonWriterOptions
- {
- Indented = false,
- SkipValidation = false
- };
- Assert.Equal(expectedOption, state.Options);
- }
-
- [Fact]
- public static void JsonWriterStateDefaultCtor()
- {
- var state = new JsonWriterState();
- Assert.Equal(0, state.BytesCommitted);
- Assert.Equal(0, state.BytesWritten);
-
- var expectedOption = new JsonWriterOptions
- {
- Indented = false,
- SkipValidation = false
- };
- Assert.Equal(expectedOption, state.Options);
- }
-
- [Fact]
- public static void JsonWriterStateCtor()
- {
- var state = new JsonWriterState(options: default);
- Assert.Equal(0, state.BytesCommitted);
- Assert.Equal(0, state.BytesWritten);
-
- var expectedOption = new JsonWriterOptions
- {
- Indented = false,
- SkipValidation = false
- };
- Assert.Equal(expectedOption, state.Options);
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Buffers;
-using System.Runtime.CompilerServices;
-
-namespace System.Text.Json.Tests
-{
- // a List<T> like type designed to be embeded in other types
- internal struct ResizableArray<T>
- {
- public ResizableArray(T[] array, int count = 0)
- {
- Array = array;
- Count = count;
- }
-
- public T[] Array { get; set; }
-
- public int Count { get; set; }
-
- public int Capacity => Array.Length;
-
- public T[] Resize(T[] newArray)
- {
- T[] oldArray = Array;
- Array.AsSpan(0, Count).CopyTo(newArray); // CopyTo will throw if newArray.Length < _count
- Array = newArray;
- return oldArray;
- }
-
- public ArraySegment<T> Full => new ArraySegment<T>(Array, 0, Count);
-
- public ArraySegment<T> Free => new ArraySegment<T>(Array, Count, Array.Length - Count);
-
- public Span<T> FreeSpan => new Span<T>(Array, Count, Array.Length - Count);
-
- public Memory<T> FreeMemory => new Memory<T>(Array, Count, Array.Length - Count);
-
- public int FreeCount => Array.Length - Count;
- }
-}
}
}</value>
</data>
+ <data name="BufferWriterAdvancedTooFar" xml:space="preserve">
+ <value>Cannot advance past the end of the buffer, which has a size of {0}.</value>
+ </data>
</root>
\ No newline at end of file
<Compile Include="$(CommonTestPath)\System\IO\WrappedMemoryStream.cs">
<Link>CommonTest\System\IO\WrappedMemoryStream.cs</Link>
</Compile>
- <Compile Include="ArrayBufferWriter.cs" />
<Compile Include="BitStackTests.cs" />
<Compile Include="BufferFactory.cs" />
<Compile Include="BufferSegment.cs" />
<Compile Include="JsonNumberTestData.cs" />
<Compile Include="JsonReaderStateAndOptionsTests.cs" />
<Compile Include="JsonTestHelper.cs" />
- <Compile Include="JsonWriterStateTests.cs" />
- <Compile Include="ResizableArray.cs" />
+ <Compile Include="JsonWriterOptionsTests.cs" />
<Compile Include="Serialization\Array.ReadTests.cs" />
<Compile Include="Serialization\Array.WriteTests.cs" />
<Compile Include="Serialization\CacheTests.cs" />
<ItemGroup>
<Compile Include="..\src\System\Text\Json\BitStack.cs" Link="BitStack.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetsNETStandard)' == 'true'">
+ <Compile Include="$(CommonPath)\System\Buffers\ArrayBufferWriter.cs">
+ <Link>CommonTest\System\Buffers\ArrayBufferWriter.cs</Link>
+ </Compile>
+ </ItemGroup>
<ItemGroup>
<ReferenceFromRuntime Include="Newtonsoft.Json" />
<!-- Copy external dependency into the test output directory. -->
// See the LICENSE file in the project root for more information.
using Xunit;
+using System.Buffers;
using System.IO;
using Newtonsoft.Json;
using System.Globalization;
+using System.Threading.Tasks;
namespace System.Text.Json.Tests
{
[InlineData(false, false)]
public void NullCtor(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- try
- {
- var jsonUtf8 = new Utf8JsonWriter(null);
- Assert.True(false, "Expected ArgumentNullException to be thrown when null IBufferWriter is passed in.");
- }
- catch (ArgumentNullException) { }
+ Assert.Throws<ArgumentNullException>(() => new Utf8JsonWriter((Stream)null));
+ Assert.Throws<ArgumentNullException>(() => new Utf8JsonWriter((IBufferWriter<byte>)null));
+ Assert.Throws<ArgumentNullException>(() => new Utf8JsonWriter((Stream)null, options));
+ Assert.Throws<ArgumentNullException>(() => new Utf8JsonWriter((IBufferWriter<byte>)null, options));
+ }
- try
- {
- var jsonUtf8 = new Utf8JsonWriter(null, state);
- Assert.True(false, "Expected ArgumentNullException to be thrown when null IBufferWriter is passed in.");
- }
- catch (ArgumentNullException) { }
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void CantWriteToNonWritableStream(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var stream = new MemoryStream();
+ stream.Dispose();
+
+ Assert.Throws<ArgumentException>(() => new Utf8JsonWriter(stream));
+ Assert.Throws<ArgumentException>(() => new Utf8JsonWriter(stream, options));
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void InitialState(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var stream = new MemoryStream();
+ var writer = new Utf8JsonWriter(stream, options);
+ Assert.Equal(0, writer.BytesCommitted);
+ Assert.Equal(0, writer.BytesPending);
+ Assert.Equal(0, writer.CurrentDepth);
+ Assert.Equal(formatted, writer.Options.Indented);
+ Assert.Equal(skipValidation, writer.Options.SkipValidation);
+ Assert.Equal(0, stream.Position);
+
+ var output = new FixedSizedBufferWriter(0);
+ writer = new Utf8JsonWriter(output, options);
+ Assert.Equal(0, writer.BytesCommitted);
+ Assert.Equal(0, writer.BytesPending);
+ Assert.Equal(0, writer.CurrentDepth);
+ Assert.Equal(formatted, writer.Options.Indented);
+ Assert.Equal(skipValidation, writer.Options.SkipValidation);
+ Assert.Equal(0, output.FormattedCount);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void Reset(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteNumberValue(1);
+ writeToStream.Flush();
+
+ Assert.True(writeToStream.BytesCommitted != 0);
+
+ writeToStream.Reset();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(0, writeToStream.CurrentDepth);
+ Assert.Equal(formatted, writeToStream.Options.Indented);
+ Assert.Equal(skipValidation, writeToStream.Options.SkipValidation);
+ Assert.True(stream.Position != 0);
+
+ long previousWritten = stream.Position;
+ writeToStream.Flush();
+ Assert.Equal(previousWritten, stream.Position);
+
+ var output = new FixedSizedBufferWriter(32);
+ var writeToIBW = new Utf8JsonWriter(output, options);
+ writeToIBW.WriteNumberValue(1);
+ writeToIBW.Flush();
+
+ Assert.True(writeToIBW.BytesCommitted != 0);
+
+ writeToIBW.Reset();
+ Assert.Equal(0, writeToIBW.BytesCommitted);
+ Assert.Equal(0, writeToIBW.BytesPending);
+ Assert.Equal(0, writeToIBW.CurrentDepth);
+ Assert.Equal(formatted, writeToIBW.Options.Indented);
+ Assert.Equal(skipValidation, writeToIBW.Options.SkipValidation);
+ Assert.True(output.FormattedCount != 0);
+
+ previousWritten = output.FormattedCount;
+ writeToIBW.Flush();
+ Assert.Equal(previousWritten, output.FormattedCount);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void ResetWithSameOutput(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteNumberValue(1);
+ writeToStream.Flush();
+
+ Assert.True(writeToStream.BytesCommitted != 0);
+
+ writeToStream.Reset(stream);
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(0, writeToStream.CurrentDepth);
+ Assert.Equal(formatted, writeToStream.Options.Indented);
+ Assert.Equal(skipValidation, writeToStream.Options.SkipValidation);
+ Assert.True(stream.Position != 0);
+
+ long previousWritten = stream.Position;
+ writeToStream.Flush();
+ Assert.Equal(previousWritten, stream.Position);
+
+ writeToStream.WriteNumberValue(1);
+ writeToStream.Flush();
+
+ Assert.NotEqual(previousWritten, stream.Position);
+ Assert.Equal("11", Encoding.UTF8.GetString(stream.ToArray()));
+
+ var output = new FixedSizedBufferWriter(32);
+ var writeToIBW = new Utf8JsonWriter(output, options);
+ writeToIBW.WriteNumberValue(1);
+ writeToIBW.Flush();
+
+ Assert.True(writeToIBW.BytesCommitted != 0);
+
+ writeToIBW.Reset(output);
+ Assert.Equal(0, writeToIBW.BytesCommitted);
+ Assert.Equal(0, writeToIBW.BytesPending);
+ Assert.Equal(0, writeToIBW.CurrentDepth);
+ Assert.Equal(formatted, writeToIBW.Options.Indented);
+ Assert.Equal(skipValidation, writeToIBW.Options.SkipValidation);
+ Assert.True(output.FormattedCount != 0);
+
+ previousWritten = output.FormattedCount;
+ writeToIBW.Flush();
+ Assert.Equal(previousWritten, output.FormattedCount);
+
+ writeToIBW.WriteNumberValue(1);
+ writeToIBW.Flush();
+
+ Assert.NotEqual(previousWritten, output.FormattedCount);
+ Assert.Equal("11", Encoding.UTF8.GetString(output.Formatted));
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void ResetChangeOutputMode(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteNumberValue(1);
+ writeToStream.Flush();
+
+ Assert.True(writeToStream.BytesCommitted != 0);
+
+ var output = new FixedSizedBufferWriter(32);
+ writeToStream.Reset(output);
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(0, writeToStream.CurrentDepth);
+ Assert.Equal(formatted, writeToStream.Options.Indented);
+ Assert.Equal(skipValidation, writeToStream.Options.SkipValidation);
+ Assert.True(stream.Position != 0);
+
+ long previousWrittenStream = stream.Position;
+ long previousWrittenIBW = output.FormattedCount;
+ Assert.Equal(0, previousWrittenIBW);
+ writeToStream.Flush();
+ Assert.Equal(previousWrittenStream, stream.Position);
+ Assert.Equal(previousWrittenIBW, output.FormattedCount);
+
+ writeToStream.WriteNumberValue(1);
+ writeToStream.Flush();
+
+ Assert.True(writeToStream.BytesCommitted != 0);
+ Assert.Equal(previousWrittenStream, stream.Position);
+ Assert.True(output.FormattedCount != 0);
+
+ output = new FixedSizedBufferWriter(32);
+ var writeToIBW = new Utf8JsonWriter(output, options);
+ writeToIBW.WriteNumberValue(1);
+ writeToIBW.Flush();
+
+ Assert.True(writeToIBW.BytesCommitted != 0);
+
+ stream = new MemoryStream();
+ writeToIBW.Reset(stream);
+ Assert.Equal(0, writeToIBW.BytesCommitted);
+ Assert.Equal(0, writeToIBW.BytesPending);
+ Assert.Equal(0, writeToIBW.CurrentDepth);
+ Assert.Equal(formatted, writeToIBW.Options.Indented);
+ Assert.Equal(skipValidation, writeToIBW.Options.SkipValidation);
+ Assert.True(output.FormattedCount != 0);
+
+ previousWrittenStream = stream.Position;
+ previousWrittenIBW = output.FormattedCount;
+ Assert.Equal(0, previousWrittenStream);
+ writeToIBW.Flush();
+ Assert.Equal(previousWrittenStream, stream.Position);
+ Assert.Equal(previousWrittenIBW, output.FormattedCount);
+
+ writeToIBW.WriteNumberValue(1);
+ writeToIBW.Flush();
+
+ Assert.True(writeToIBW.BytesCommitted != 0);
+ Assert.Equal(previousWrittenIBW, output.FormattedCount);
+ Assert.True(stream.Position != 0);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void InvalidReset(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+
+ Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((Stream)null));
+ Assert.Throws<ArgumentNullException>(() => writeToStream.Reset((IBufferWriter<byte>)null));
+
+ stream.Dispose();
+
+ Assert.Throws<ArgumentException>(() => writeToStream.Reset(stream));
+
+ var output = new FixedSizedBufferWriter(32);
+ var writeToIBW = new Utf8JsonWriter(output, options);
+
+ Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((Stream)null));
+ Assert.Throws<ArgumentNullException>(() => writeToIBW.Reset((IBufferWriter<byte>)null));
+
+ Assert.Throws<ArgumentException>(() => writeToIBW.Reset(stream));
}
[Theory]
[InlineData(false, false)]
public void FlushEmpty(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new FixedSizedBufferWriter(0);
- try
- {
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- jsonUtf8.Flush();
- WriterDidNotThrow(skipValidation, "Expected InvalidOperationException to be thrown when calling Flush on an empty JSON payload.");
- }
- catch (InvalidOperationException) { }
- output = new FixedSizedBufferWriter(10);
- try
- {
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- jsonUtf8.WriteCommentValue("hi");
- jsonUtf8.Flush();
- WriterDidNotThrow(skipValidation, "Expected InvalidOperationException to be thrown when calling Flush on an empty JSON payload.");
- }
- catch (InvalidOperationException) { }
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.Flush();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.Flush();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, stream.Position);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public async Task FlushEmptyAsync(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(0);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ await jsonUtf8.FlushAsync();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ await writeToStream.FlushAsync();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, stream.Position);
}
[Theory]
[InlineData(false, false)]
public void FlushMultipleTimes(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new FixedSizedBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(2, jsonUtf8.BytesPending);
+ Assert.Equal(0, output.FormattedCount);
jsonUtf8.Flush();
Assert.Equal(2, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(2, output.FormattedCount);
jsonUtf8.Flush();
Assert.Equal(2, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(2, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ writeToStream.WriteEndObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(2, writeToStream.BytesPending);
+ Assert.Equal(0, stream.Position);
+ writeToStream.Flush();
+ Assert.Equal(2, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(2, stream.Position);
+ writeToStream.Flush();
+ Assert.Equal(2, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(2, stream.Position);
}
[Theory]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
- public void InvalidBufferWriter(bool formatted, bool skipValidation)
+ public async Task FlushMultipleTimesAsync(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(10);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteEndObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(2, jsonUtf8.BytesPending);
+ Assert.Equal(0, output.FormattedCount);
+ await jsonUtf8.FlushAsync();
+ Assert.Equal(2, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(2, output.FormattedCount);
+ await jsonUtf8.FlushAsync();
+ Assert.Equal(2, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(2, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ writeToStream.WriteEndObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(2, writeToStream.BytesPending);
+ Assert.Equal(0, stream.Position);
+ await writeToStream.FlushAsync();
+ Assert.Equal(2, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(2, stream.Position);
+ await writeToStream.FlushAsync();
+ Assert.Equal(2, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(2, stream.Position);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void DisposeAutoFlushes(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteEndObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, output.FormattedCount);
+ jsonUtf8.Dispose();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(2, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ writeToStream.WriteEndObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, stream.Position);
+ writeToStream.Dispose();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(2, stream.Position);
+ }
+
+#if !netstandard
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public async Task DisposeAutoFlushesAsync(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(10);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteEndObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, output.FormattedCount);
+ await jsonUtf8.DisposeAsync();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(2, output.FormattedCount);
+
+ var stream = new MemoryStream();
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ writeToStream.WriteEndObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, stream.Position);
+ await writeToStream.DisposeAsync();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(2, stream.Position);
+ }
+#endif
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void UseAfterDisposeInvalid(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(10);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(1, jsonUtf8.BytesPending);
+ Assert.Equal(0, output.FormattedCount);
+ jsonUtf8.Dispose();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(1, output.FormattedCount);
+ Assert.Throws<NullReferenceException>(() => jsonUtf8.Flush());
+ jsonUtf8.Dispose();
+ Assert.Throws<NullReferenceException>(() => jsonUtf8.Flush());
+
+ jsonUtf8.Reset();
+
+ var stream = new MemoryStream();
+ Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream));
+
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(1, writeToStream.BytesPending);
+ Assert.Equal(0, stream.Position);
+ writeToStream.Dispose();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(1, stream.Position);
+ Assert.Throws<NullReferenceException>(() => writeToStream.Flush());
+ writeToStream.Dispose();
+ Assert.Throws<NullReferenceException>(() => writeToStream.Flush());
+
+ writeToStream.Reset();
+
+ Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output));
+ }
+
+#if !netstandard
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public async Task UseAfterDisposeInvalidAsync(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new FixedSizedBufferWriter(10);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(1, jsonUtf8.BytesPending);
+ Assert.Equal(0, output.FormattedCount);
+ await jsonUtf8.DisposeAsync();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
+ Assert.Equal(1, output.FormattedCount);
+ await Assert.ThrowsAsync<NullReferenceException>(() => jsonUtf8.FlushAsync());
+ await jsonUtf8.DisposeAsync();
+ await Assert.ThrowsAsync<NullReferenceException>(() => jsonUtf8.FlushAsync());
+
+ jsonUtf8.Reset();
+
+ var stream = new MemoryStream();
+ Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(stream));
+
+ var writeToStream = new Utf8JsonWriter(stream, options);
+ writeToStream.WriteStartObject();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(1, writeToStream.BytesPending);
+ Assert.Equal(0, stream.Position);
+ await writeToStream.DisposeAsync();
+ Assert.Equal(0, writeToStream.BytesCommitted);
+ Assert.Equal(0, writeToStream.BytesPending);
+ Assert.Equal(1, stream.Position);
+ await Assert.ThrowsAsync<NullReferenceException>(() => writeToStream.FlushAsync());
+ await writeToStream.DisposeAsync();
+ await Assert.ThrowsAsync<NullReferenceException>(() => writeToStream.FlushAsync());
+
+ writeToStream.Reset();
+
+ Assert.Throws<ObjectDisposedException>(() => jsonUtf8.Reset(output));
+ }
+#endif
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void InvalidBufferWriter(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new InvalidBufferWriter();
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
- try
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteNumberValue((ulong)12345678901));
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public async Task WriteLargeToStream(bool formatted, bool skipValidation)
+ {
+ var stream = new MemoryStream();
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ await WriteLargeToStreamHelper(stream, options);
+
+ string expectedString = GetExpectedLargeString(formatted);
+ string actualString = Encoding.UTF8.GetString(stream.ToArray());
+
+ Assert.Equal(expectedString, actualString);
+ }
+
+ private static async Task WriteLargeToStreamHelper(Stream stream, JsonWriterOptions options)
+ {
+ const int SyncWriteThreshold = 25_000;
+
+#if !netstandard
+ await
+#endif
+ using var jsonUtf8 = new Utf8JsonWriter(stream, options);
+
+ byte[] utf8String = Encoding.UTF8.GetBytes("some string 1234");
+
+ jsonUtf8.WriteStartArray();
+ for (int i = 0; i < 10_000; i++)
{
- jsonUtf8.WriteNumberValue((ulong)12345678901);
- Assert.True(false, "Expected ArgumentException to be thrown when IBufferWriter doesn't have enough space.");
+ jsonUtf8.WriteStringValue(utf8String);
+ if (jsonUtf8.BytesPending > SyncWriteThreshold)
+ {
+ await jsonUtf8.FlushAsync();
+ }
}
- catch (ArgumentException) { }
+ jsonUtf8.WriteEndArray();
+ }
+
+ private static string GetExpectedLargeString(bool prettyPrint)
+ {
+ var ms = new MemoryStream();
+ TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
+
+ var json = new JsonTextWriter(streamWriter)
+ {
+ Formatting = prettyPrint ? Formatting.Indented : Formatting.None
+ };
+
+ json.WriteStartArray();
+ for (int i = 0; i < 10_000; i++)
+ {
+ json.WriteValue("some string 1234");
+ }
+ json.WriteEnd();
+
+ json.Flush();
+
+ return Encoding.UTF8.GetString(ms.ToArray());
}
[Theory]
[InlineData(false, false)]
public void FixedSizeBufferWriter_Guid(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new FixedSizedBufferWriter(37);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
Guid guid = Guid.NewGuid();
- try
- {
- jsonUtf8.WriteStringValue(guid);
- Assert.True(false, "Expected ArgumentException to be thrown when IBufferWriter doesn't have enough space.");
- }
- catch (ArgumentException) { }
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStringValue(guid));
- output = new FixedSizedBufferWriter(39);
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ output = new FixedSizedBufferWriter(41);
+ jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStringValue(guid);
jsonUtf8.Flush();
string actualStr = Encoding.UTF8.GetString(output.Formatted);
[InlineData(false, false)]
public void FixedSizeBufferWriter_DateTime(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new FixedSizedBufferWriter(20);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
var date = new DateTime(2019, 1, 1);
- try
- {
- jsonUtf8.WriteStringValue(date);
- Assert.True(false, "Expected ArgumentException to be thrown when IBufferWriter doesn't have enough space.");
- }
- catch (ArgumentException) { }
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStringValue(date));
- output = new FixedSizedBufferWriter(21);
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ output = new FixedSizedBufferWriter(38);
+ jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStringValue(date);
jsonUtf8.Flush();
string actualStr = Encoding.UTF8.GetString(output.Formatted);
[InlineData(false, false)]
public void FixedSizeBufferWriter_DateTimeOffset(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var output = new FixedSizedBufferWriter(26);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
DateTimeOffset date = new DateTime(2019, 1, 1);
- try
- {
- jsonUtf8.WriteStringValue(date);
- Assert.True(false, "Expected ArgumentException to be thrown when IBufferWriter doesn't have enough space.");
- }
- catch (ArgumentException) { }
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStringValue(date));
- output = new FixedSizedBufferWriter(27);
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ output = new FixedSizedBufferWriter(38);
+ jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStringValue(date);
jsonUtf8.Flush();
string actualStr = Encoding.UTF8.GetString(output.Formatted);
[InlineData(false, false)]
public void FixedSizeBufferWriter_Decimal(bool formatted, bool skipValidation)
{
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
var random = new Random(42);
for (int i = 0; i < 1_000; i++)
{
- var output = new FixedSizedBufferWriter(31);
+ var output = new FixedSizedBufferWriter(34);
decimal value = JsonTestHelper.NextDecimal(random, 78E14, -78E14);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteNumberValue(value);
jsonUtf8.Flush();
for (int i = 0; i < 1_000; i++)
{
- var output = new FixedSizedBufferWriter(31);
+ var output = new FixedSizedBufferWriter(34);
decimal value = JsonTestHelper.NextDecimal(random, 1_000_000, -1_000_000);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteNumberValue(value);
jsonUtf8.Flush();
}
{
- var output = new FixedSizedBufferWriter(31);
+ var output = new FixedSizedBufferWriter(34);
decimal value = 9999999999999999999999999999m;
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteNumberValue(value);
jsonUtf8.Flush();
}
{
- var output = new FixedSizedBufferWriter(31);
+ var output = new FixedSizedBufferWriter(34);
decimal value = -9999999999999999999999999999m;
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteNumberValue(value);
jsonUtf8.Flush();
{
var output = new FixedSizedBufferWriter(30);
decimal value = -0.9999999999999999999999999999m;
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
- try
- {
- jsonUtf8.WriteNumberValue(value);
- Assert.True(false, "Expected ArgumentException to be thrown when IBufferWriter doesn't have enough space.");
- }
- catch (ArgumentException) { }
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteNumberValue(value));
- output = new FixedSizedBufferWriter(31);
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ output = new FixedSizedBufferWriter(34);
+ jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteNumberValue(value);
jsonUtf8.Flush();
[InlineData(false, false)]
public void InvalidJsonMismatch(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ if (skipValidation)
{
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ if (skipValidation)
{
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ if (skipValidation)
+ {
+ jsonUtf8.WriteStartArray("property at start");
+ }
+ else
{
- jsonUtf8.WriteStartArray("property at start", escape: false);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray("property at start"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject("property at start", escape: false);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteStartObject("property at start");
+ }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject("property at start"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartArray("property inside array", escape: false);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteStartArray("property inside array");
+ }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray("property inside array"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ if (skipValidation)
{
jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
jsonUtf8.WriteStringValue("key");
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStringValue("key"));
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
jsonUtf8.WriteString("key", "value");
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteString("key", "value"));
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteEndArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteEndArray();
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteStartObject("some object");
+ jsonUtf8.WriteEndObject();
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartObject("some object", escape: false);
- jsonUtf8.WriteEndObject();
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartObject("some object", escape: false);
- jsonUtf8.WriteEndObject();
+ jsonUtf8.WriteStartObject("some object");
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject("some object"));
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteStartArray("test array");
+ jsonUtf8.WriteEndArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartArray("test array", escape: false);
- jsonUtf8.WriteEndArray();
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteEndArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteEndArray();
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteEndObject();
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndObject();
- jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteStartArray();
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartArray();
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteStartObject("test object");
+ if (skipValidation)
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartObject("test object", escape: false);
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void InvalidJsonIncomplete(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
+
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ Assert.True(jsonUtf8.CurrentDepth != 0);
+
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.True(jsonUtf8.CurrentDepth != 0);
+
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteEndArray();
+ Assert.True(jsonUtf8.CurrentDepth != 0);
+
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteStartObject("some object");
+ Assert.True(jsonUtf8.CurrentDepth != 0);
+
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartArray();
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteEndObject();
+ Assert.True(jsonUtf8.CurrentDepth != 0);
- output.Dispose();
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteStartArray("test array");
+ jsonUtf8.WriteEndArray();
+ Assert.True(jsonUtf8.CurrentDepth != 0);
}
[Theory]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
- public void InvalidJsonIncomplete(bool formatted, bool skipValidation)
+ public void InvalidJsonPrimitive(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var output = new ArrayBufferWriter(1024);
-
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteNumberValue(12345);
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteNumberValue(12345));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteEndArray();
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartObject("some object", escape: false);
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray());
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStartObject("some object", escape: false);
- jsonUtf8.WriteEndObject();
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteStartObject();
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartArray("test array", escape: false);
- jsonUtf8.WriteEndArray();
- jsonUtf8.Flush(isFinalBlock: true);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject());
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
- }
-
- [Theory]
- [InlineData(true, true)]
- [InlineData(true, false)]
- [InlineData(false, true)]
- [InlineData(false, false)]
- public void InvalidJsonPrimitive(bool formatted, bool skipValidation)
- {
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteNumberValue(12345);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteStartArray("property name");
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteStartArray();
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray("property name"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteStartObject();
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteStartObject("property name");
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteStartArray("property name", escape: false);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject("property name"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteStartObject("property name", escape: false);
- WriterDidNotThrow(skipValidation);
+ jsonUtf8.WriteString("property name", "value");
}
- catch (InvalidOperationException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ else
{
- jsonUtf8.WriteNumberValue(12345);
- jsonUtf8.WriteString("property name", "value", escape: false);
- WriterDidNotThrow(skipValidation);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteString("property name", "value"));
}
- catch (InvalidOperationException) { }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteNumberValue(12345);
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
+ }
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteNumberValue(12345);
+ if (skipValidation)
{
- jsonUtf8.WriteNumberValue(12345);
jsonUtf8.WriteEndObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndObject());
+ }
}
[Theory]
[InlineData(false, false)]
public void InvalidNumbersJson(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(double.NegativeInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(double.PositiveInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(double.NegativeInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(double.NaN);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(double.PositiveInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(float.PositiveInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(double.NaN));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(float.NegativeInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(float.PositiveInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteNumberValue(float.NaN);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(float.NegativeInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", double.NegativeInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumberValue(float.NaN));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", double.PositiveInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", double.NegativeInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", double.NaN);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", double.PositiveInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", float.PositiveInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", double.NaN));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", float.NegativeInfinity);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", float.PositiveInfinity));
- jsonUtf8 = new Utf8JsonWriter(output, state);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteNumber("name", float.NaN);
- Assert.True(false, "Expected ArgumentException to be thrown for unsupported number values.");
- }
- catch (ArgumentException) { }
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", float.NegativeInfinity));
- output.Dispose();
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteNumber("name", float.NaN));
}
[Theory]
[InlineData(false)]
public void InvalidJsonContinueShouldSucceed(bool formatted)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = true });
-
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = true };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
for (int i = 0; i < 100; i++)
+ {
jsonUtf8.WriteEndArray();
+ }
jsonUtf8.WriteStartArray();
jsonUtf8.WriteEndArray();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
var sb = new StringBuilder();
for (int i = 0; i < 100; i++)
{
sb.Append(Environment.NewLine);
sb.Append("[]");
- Assert.Equal(sb.ToString(), actualStr);
-
- output.Dispose();
+ AssertContents(sb.ToString(), output);
}
[Theory]
[InlineData(false, false)]
public void WritingTooDeep(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var output = new ArrayBufferWriter(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
- try
+ for (int i = 0; i < 1000; i++)
{
- for (int i = 0; i < 1001; i++)
- {
- jsonUtf8.WriteStartArray();
- }
- Assert.True(false, "Expected InvalidOperationException to be thrown for depth >= 1000.");
+ jsonUtf8.WriteStartArray();
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
+ Assert.Equal(1000, jsonUtf8.CurrentDepth);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray());
}
[Theory]
[InlineData(false, false)]
public void WritingTooDeepProperty(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
- try
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ for (int i = 0; i < 999; i++)
{
- jsonUtf8.WriteStartObject();
- for (int i = 0; i < 1000; i++)
- {
- jsonUtf8.WriteStartArray("name");
- }
- Assert.True(false, "Expected InvalidOperationException to be thrown for depth >= 1000.");
+ jsonUtf8.WriteStartObject("name");
}
- catch (InvalidOperationException) { }
+ Assert.Equal(1000, jsonUtf8.CurrentDepth);
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray("name"));
- jsonUtf8 = new Utf8JsonWriter(output, state);
-
- try
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ for (int i = 0; i < 999; i++)
{
- jsonUtf8.WriteStartObject();
- for (int i = 0; i < 1000; i++)
- {
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("name"));
- }
- Assert.True(false, "Expected InvalidOperationException to be thrown for depth >= 1000.");
+ jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes("name"));
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("name")));
}
[ConditionalTheory(nameof(IsX64))]
[InlineData(false, false)]
public void WritingTooLargeProperty(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(1024);
-
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
- Span<byte> key;
- Span<char> keyChars;
+ byte[] key;
+ char[] keyChars;
try
{
return;
}
- key.Fill((byte)'a');
- keyChars.Fill('a');
-
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartArray(keyChars);
- Assert.True(false, $"Expected ArgumentException for property too large wasn't thrown. PropertyLength: {keyChars.Length}");
- }
- catch (ArgumentException) { }
+ key.AsSpan().Fill((byte)'a');
+ keyChars.AsSpan().Fill('a');
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- try
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteStartArray(key);
- Assert.True(false, $"Expected ArgumentException for property too large wasn't thrown. PropertyLength: {key.Length}");
- }
- catch (ArgumentException) { }
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteStartArray(keyChars));
- output.Dispose();
+ jsonUtf8 = new Utf8JsonWriter(output, options);
+ jsonUtf8.WriteStartObject();
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteStartArray(key));
}
[Theory]
{
string expectedStr = "123456789012345";
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 3; i++)
- {
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
- jsonUtf8.WriteNumberValue(123456789012345);
-
- jsonUtf8.Flush();
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+ jsonUtf8.WriteNumberValue(123456789012345);
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
+ jsonUtf8.Flush();
- output.Dispose();
- }
+ AssertContents(expectedStr, output);
}
[Theory]
[InlineData(false, false)]
public void WriteHelloWorld(bool formatted, bool skipValidation)
{
- string expectedStr = GetHelloWorldExpectedString(prettyPrint: formatted);
+ string propertyName = "message";
+ string value = "Hello, World!";
+ string expectedStr = GetHelloWorldExpectedString(prettyPrint: formatted, propertyName, value);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
for (int i = 0; i < 9; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(32);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteString("message", "Hello, World!", escape: false);
+ jsonUtf8.WriteString("message", "Hello, World!");
+ jsonUtf8.WriteString("message", "Hello, World!");
break;
case 1:
- jsonUtf8.WriteString("message", "Hello, World!".AsSpan(), escape: false);
+ jsonUtf8.WriteString("message", "Hello, World!".AsSpan());
+ jsonUtf8.WriteString("message", "Hello, World!".AsSpan());
break;
case 2:
- jsonUtf8.WriteString("message", Encoding.UTF8.GetBytes("Hello, World!"), escape: false);
+ jsonUtf8.WriteString("message", Encoding.UTF8.GetBytes("Hello, World!"));
+ jsonUtf8.WriteString("message", Encoding.UTF8.GetBytes("Hello, World!"));
break;
case 3:
- jsonUtf8.WriteString("message".AsSpan(), "Hello, World!", escape: false);
+ jsonUtf8.WriteString("message".AsSpan(), "Hello, World!");
+ jsonUtf8.WriteString("message".AsSpan(), "Hello, World!");
break;
case 4:
- jsonUtf8.WriteString("message".AsSpan(), "Hello, World!".AsSpan(), escape: false);
+ jsonUtf8.WriteString("message".AsSpan(), "Hello, World!".AsSpan());
+ jsonUtf8.WriteString("message".AsSpan(), "Hello, World!".AsSpan());
break;
case 5:
- jsonUtf8.WriteString("message".AsSpan(), Encoding.UTF8.GetBytes("Hello, World!"), escape: false);
+ jsonUtf8.WriteString("message".AsSpan(), Encoding.UTF8.GetBytes("Hello, World!"));
+ jsonUtf8.WriteString("message".AsSpan(), Encoding.UTF8.GetBytes("Hello, World!"));
break;
case 6:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!", escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!");
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!");
break;
case 7:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!".AsSpan(), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!".AsSpan());
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), "Hello, World!".AsSpan());
break;
case 8:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), Encoding.UTF8.GetBytes("Hello, World!"), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), Encoding.UTF8.GetBytes("Hello, World!"));
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes("message"), Encoding.UTF8.GetBytes("Hello, World!"));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+ AssertContents(expectedStr, output);
+ }
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteHelloWorldEscaped(bool formatted, bool skipValidation)
+ {
+ string propertyName = "mess><age";
+ string value = "Hello,>< World!";
+ string expectedStr = GetHelloWorldExpectedString(prettyPrint: formatted, propertyName, value);
+
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ ReadOnlySpan<char> propertyNameSpan = propertyName.AsSpan();
+ ReadOnlySpan<char> valueSpan = value.AsSpan();
+ ReadOnlySpan<byte> propertyNameSpanUtf8 = Encoding.UTF8.GetBytes(propertyName);
+ ReadOnlySpan<byte> valueSpanUtf8 = Encoding.UTF8.GetBytes(value);
+
+ for (int i = 0; i < 9; i++)
+ {
+ var output = new ArrayBufferWriter<byte>(32);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ jsonUtf8.WriteStartObject();
+
+ switch (i)
+ {
+ case 0:
+ jsonUtf8.WriteString(propertyName, value);
+ jsonUtf8.WriteString(propertyName, value);
+ break;
+ case 1:
+ jsonUtf8.WriteString(propertyName, valueSpan);
+ jsonUtf8.WriteString(propertyName, valueSpan);
+ break;
+ case 2:
+ jsonUtf8.WriteString(propertyName, valueSpanUtf8);
+ jsonUtf8.WriteString(propertyName, valueSpanUtf8);
+ break;
+ case 3:
+ jsonUtf8.WriteString(propertyNameSpan, value);
+ jsonUtf8.WriteString(propertyNameSpan, value);
+ break;
+ case 4:
+ jsonUtf8.WriteString(propertyNameSpan, valueSpan);
+ jsonUtf8.WriteString(propertyNameSpan, valueSpan);
+ break;
+ case 5:
+ jsonUtf8.WriteString(propertyNameSpan, valueSpanUtf8);
+ jsonUtf8.WriteString(propertyNameSpan, valueSpanUtf8);
+ break;
+ case 6:
+ jsonUtf8.WriteString(propertyNameSpanUtf8, value);
+ jsonUtf8.WriteString(propertyNameSpanUtf8, value);
+ break;
+ case 7:
+ jsonUtf8.WriteString(propertyNameSpanUtf8, valueSpan);
+ jsonUtf8.WriteString(propertyNameSpanUtf8, valueSpan);
+ break;
+ case 8:
+ jsonUtf8.WriteString(propertyNameSpanUtf8, valueSpanUtf8);
+ jsonUtf8.WriteString(propertyNameSpanUtf8, valueSpanUtf8);
+ break;
+ }
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
+ jsonUtf8.WriteEndObject();
+ jsonUtf8.Flush();
- output.Dispose();
+ AssertContents(expectedStr, output);
}
+
+ // Verify that escaping does not change the input strings/spans.
+ Assert.Equal("mess><age", propertyName);
+ Assert.Equal("Hello,>< World!", value);
+ Assert.True(propertyName.AsSpan().SequenceEqual(propertyNameSpan));
+ Assert.True(value.AsSpan().SequenceEqual(valueSpan));
+ Assert.True(Encoding.UTF8.GetBytes(propertyName).AsSpan().SequenceEqual(propertyNameSpanUtf8));
+ Assert.True(Encoding.UTF8.GetBytes(value).AsSpan().SequenceEqual(valueSpanUtf8));
}
[Theory]
[InlineData(false, false)]
public void WritePartialHelloWorld(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
Assert.Equal(0, jsonUtf8.BytesCommitted);
- Assert.Equal(1, jsonUtf8.BytesWritten);
+ Assert.Equal(1, jsonUtf8.BytesPending);
jsonUtf8.WriteString("message", "Hello, World!");
- Assert.Equal(16, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
if (formatted)
- Assert.Equal(26 + 2 + Environment.NewLine.Length + 1, jsonUtf8.BytesWritten);
+ Assert.Equal(26 + 2 + Environment.NewLine.Length + 1, jsonUtf8.BytesPending); // new lines, indentation, white space
else
- Assert.Equal(26, jsonUtf8.BytesWritten);
+ Assert.Equal(26, jsonUtf8.BytesPending);
- jsonUtf8.Flush(isFinalBlock: false);
+ jsonUtf8.Flush();
if (formatted)
Assert.Equal(26 + 2 + Environment.NewLine.Length + 1, jsonUtf8.BytesCommitted); // new lines, indentation, white space
else
Assert.Equal(26, jsonUtf8.BytesCommitted);
- Assert.Equal(jsonUtf8.BytesCommitted, jsonUtf8.BytesWritten);
+ Assert.Equal(0, jsonUtf8.BytesPending);
jsonUtf8.WriteString("message", "Hello, World!");
jsonUtf8.WriteEndObject();
Assert.Equal(26, jsonUtf8.BytesCommitted);
if (formatted)
- Assert.Equal(53 + (2 * 2) + (3 * Environment.NewLine.Length) + (1 * 2), jsonUtf8.BytesWritten); // new lines, indentation, white space
+ Assert.Equal(27 + 2 + (2 * Environment.NewLine.Length) + 1, jsonUtf8.BytesPending); // new lines, indentation, white space
else
- Assert.Equal(53, jsonUtf8.BytesWritten);
+ Assert.Equal(27, jsonUtf8.BytesPending);
- jsonUtf8.Flush(isFinalBlock: true);
+ jsonUtf8.Flush();
if (formatted)
Assert.Equal(53 + (2 * 2) + (3 * Environment.NewLine.Length) + (1 * 2), jsonUtf8.BytesCommitted); // new lines, indentation, white space
else
Assert.Equal(53, jsonUtf8.BytesCommitted);
- Assert.Equal(jsonUtf8.BytesCommitted, jsonUtf8.BytesWritten);
-
- Assert.Equal(0, state.BytesCommitted);
- Assert.Equal(0, state.BytesWritten);
-
- state = jsonUtf8.GetCurrentState();
- Assert.Equal(jsonUtf8.BytesCommitted, state.BytesCommitted);
- Assert.Equal(jsonUtf8.BytesWritten, state.BytesWritten);
-
- output.Dispose();
- }
-
- [Theory]
- [InlineData(true, true)]
- [InlineData(true, false)]
- [InlineData(false, true)]
- [InlineData(false, false)]
- public void WritePartialHelloWorldSaveState(bool formatted, bool skipValidation)
- {
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
- Assert.Equal(0, jsonUtf8.CurrentDepth);
- jsonUtf8.WriteStartObject();
- Assert.Equal(1, jsonUtf8.CurrentDepth);
- jsonUtf8.Flush(isFinalBlock: false);
-
- state = jsonUtf8.GetCurrentState();
-
- Assert.Equal(1, state.BytesCommitted);
- Assert.Equal(1, state.BytesWritten);
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
-
- Assert.Equal(1, jsonUtf8.CurrentDepth);
-
- jsonUtf8.WriteString("message", "Hello, World!");
- jsonUtf8.WriteEndObject();
- jsonUtf8.Flush();
-
- Assert.Equal(jsonUtf8.BytesCommitted, jsonUtf8.BytesWritten);
-
- if (formatted)
- Assert.Equal(26 + 2 + (2 * Environment.NewLine.Length) + 1, jsonUtf8.BytesCommitted);
- else
- Assert.Equal(26, jsonUtf8.BytesCommitted);
-
- Assert.Equal(1, state.BytesCommitted);
- Assert.Equal(1, state.BytesWritten);
-
- state = jsonUtf8.GetCurrentState();
-
- if (formatted)
- {
- Assert.Equal(26 + 2 + (2 * Environment.NewLine.Length) + 1, state.BytesCommitted);
- Assert.Equal(26 + 2 + (2 * Environment.NewLine.Length) + 1, state.BytesWritten);
- }
- else
- {
- Assert.Equal(26, state.BytesCommitted);
- Assert.Equal(26, state.BytesWritten);
- }
-
- output.Dispose();
+ Assert.Equal(0, jsonUtf8.BytesPending);
}
[Theory]
[InlineData(false, false)]
public void WriteInvalidPartialJson(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
- Assert.Equal(0, state.BytesCommitted);
- Assert.Equal(0, state.BytesWritten);
-
- jsonUtf8.Flush(isFinalBlock: false);
-
- state = jsonUtf8.GetCurrentState();
+ Assert.Equal(0, jsonUtf8.BytesCommitted);
+ Assert.Equal(1, jsonUtf8.BytesPending);
- Assert.Equal(1, state.BytesCommitted);
- Assert.Equal(1, state.BytesWritten);
+ jsonUtf8.Flush();
- jsonUtf8 = new Utf8JsonWriter(output, state);
+ Assert.Equal(1, jsonUtf8.BytesCommitted);
+ Assert.Equal(0, jsonUtf8.BytesPending);
- try
+ if (skipValidation)
{
jsonUtf8.WriteStringValue("Hello, World!");
- WriterDidNotThrow(skipValidation);
- }
- catch (InvalidOperationException) { }
- try
- {
jsonUtf8.WriteEndArray();
- WriterDidNotThrow(skipValidation);
- }
- catch (InvalidOperationException) { }
-
- output.Dispose();
- }
-
- [Theory]
- [InlineData(true, true)]
- [InlineData(true, false)]
- [InlineData(false, true)]
- [InlineData(false, false)]
- public void WritePartialJsonSkipFlush(bool formatted, bool skipValidation)
- {
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
-
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteString("message", "Hello, World!");
-
- Assert.Equal(1, jsonUtf8.CurrentDepth);
-
- try
- {
- state = jsonUtf8.GetCurrentState();
- Assert.True(false, "Expected InvalidOperationException when trying to get current state without flushing first.");
}
- catch (InvalidOperationException)
- {
-
- }
- finally
+ else
{
- jsonUtf8.Flush(isFinalBlock: false);
- state = jsonUtf8.GetCurrentState();
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStringValue("Hello, World!"));
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteEndArray());
}
-
- if (formatted)
- Assert.Equal(26 + 2 + Environment.NewLine.Length + 1, state.BytesWritten);
- else
- Assert.Equal(26, state.BytesWritten);
-
- Assert.Equal(jsonUtf8.BytesWritten, jsonUtf8.BytesCommitted);
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
- Assert.Equal(1, jsonUtf8.CurrentDepth);
- Assert.Equal(0, jsonUtf8.BytesWritten);
- Assert.Equal(0, jsonUtf8.BytesCommitted);
- jsonUtf8.WriteEndObject();
- jsonUtf8.Flush();
-
- output.Dispose();
}
[Theory]
public void WriteInvalidDepthPartial(bool formatted, bool skipValidation)
{
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndObject();
Assert.Equal(0, jsonUtf8.CurrentDepth);
- state = jsonUtf8.GetCurrentState();
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
-
- try
+ if (skipValidation)
{
jsonUtf8.WriteStartObject();
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject());
+ }
}
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- Assert.Equal(0, jsonUtf8.CurrentDepth);
- state = jsonUtf8.GetCurrentState();
-
- jsonUtf8 = new Utf8JsonWriter(output, state);
-
- try
+ if (skipValidation)
{
jsonUtf8.WriteStartObject("name");
- WriterDidNotThrow(skipValidation);
}
- catch (InvalidOperationException) { }
-
- output.Dispose();
+ else
+ {
+ Assert.Throws<InvalidOperationException>(() => jsonUtf8.WriteStartObject("name"));
+ }
}
}
{
string expectedStr = GetCommentExpectedString(prettyPrint: formatted, comment);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(32);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartArray();
for (int j = 0; j < 10; j++)
{
- WriteCommentValue(ref jsonUtf8, i, comment);
+ WriteCommentValue(jsonUtf8, i, comment);
}
switch (i)
break;
}
- WriteCommentValue(ref jsonUtf8, i, comment);
+ WriteCommentValue(jsonUtf8, i, comment);
jsonUtf8.WriteEndArray();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
- private static void WriteCommentValue(ref Utf8JsonWriter jsonUtf8, int i, string comment)
+ private static void WriteCommentValue(Utf8JsonWriter jsonUtf8, int i, string comment)
{
switch (i)
{
case 0:
- jsonUtf8.WriteCommentValue(comment, escape: false);
+ jsonUtf8.WriteCommentValue(comment);
break;
case 1:
- jsonUtf8.WriteCommentValue(comment.AsSpan(), escape: false);
+ jsonUtf8.WriteCommentValue(comment.AsSpan());
break;
case 2:
- jsonUtf8.WriteCommentValue(Encoding.UTF8.GetBytes(comment), escape: false);
+ jsonUtf8.WriteCommentValue(Encoding.UTF8.GetBytes(comment));
break;
}
}
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
+ public void WriteInvalidComment(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>(32);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ string comment = "comment is */ invalid";
+
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteCommentValue(comment));
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteCommentValue(comment.AsSpan()));
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteCommentValue(Encoding.UTF8.GetBytes(comment)));
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteCommentsInvalidTextAllowed(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>(32);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ string comment = "comment is * / valid";
+ jsonUtf8.WriteCommentValue(comment);
+ jsonUtf8.WriteCommentValue(comment.AsSpan());
+ jsonUtf8.WriteCommentValue(Encoding.UTF8.GetBytes(comment));
+
+ comment = "comment is /* valid";
+ jsonUtf8.WriteCommentValue(comment);
+ jsonUtf8.WriteCommentValue(comment.AsSpan());
+ jsonUtf8.WriteCommentValue(Encoding.UTF8.GetBytes(comment));
+
+ comment = "comment is / * valid even with unpaired surrogate \udc00 this part no longer visible";
+ jsonUtf8.WriteCommentValue(comment);
+ jsonUtf8.WriteCommentValue(comment.AsSpan());
+
+ jsonUtf8.Flush();
+
+ // Explicitly skipping flushing here
+ var invalidUtf8 = new byte[2] { 0xc3, 0x28 };
+ jsonUtf8.WriteCommentValue(invalidUtf8);
+
+ string expectedStr = GetCommentExpectedString(prettyPrint: formatted);
+ AssertContents(expectedStr, output);
+ }
+
+ private static string GetCommentExpectedString(bool prettyPrint)
+ {
+ var ms = new MemoryStream();
+ TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
+
+ var json = new JsonTextWriter(streamWriter)
+ {
+ Formatting = prettyPrint ? Formatting.Indented : Formatting.None,
+ StringEscapeHandling = StringEscapeHandling.EscapeHtml,
+ };
+
+ string comment = "comment is * / valid";
+ json.WriteComment(comment);
+ json.WriteComment(comment);
+ json.WriteComment(comment);
+
+ comment = "comment is /* valid";
+ json.WriteComment(comment);
+ json.WriteComment(comment);
+ json.WriteComment(comment);
+
+ comment = "comment is / * valid even with unpaired surrogate ";
+ json.WriteComment(comment);
+ json.WriteComment(comment);
+
+ json.Flush();
+
+ return Encoding.UTF8.GetString(ms.ToArray());
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
public void WriteStrings(bool formatted, bool skipValidation)
{
string value = "temp";
string expectedStr = GetStringsExpectedString(prettyPrint: formatted, value);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartArray();
case 2:
jsonUtf8.WriteStringValue(Encoding.UTF8.GetBytes(value));
break;
- case 3:
- jsonUtf8.WriteStringValue(value, escape: false);
- break;
- case 4:
- jsonUtf8.WriteStringValue(value.AsSpan(), escape: false);
- break;
- case 5:
- jsonUtf8.WriteStringValue(Encoding.UTF8.GetBytes(value), escape: false);
- break;
}
}
jsonUtf8.WriteEndArray();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
public void WriteHelloWorldEscaped(bool formatted, bool skipValidation, string key, string value)
{
string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, key, value, StringEscapeHandling.EscapeHtml);
- string expectedStrNoEscape = GetEscapedExpectedString(prettyPrint: formatted, key, value, StringEscapeHandling.EscapeHtml, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 18; i++)
+ for (int i = 0; i < 9; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteString(key, value, escape: true);
+ jsonUtf8.WriteString(key, value);
break;
case 1:
- jsonUtf8.WriteString(key.AsSpan(), value.AsSpan(), escape: true);
+ jsonUtf8.WriteString(key.AsSpan(), value.AsSpan());
break;
case 2:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(value), escape: true);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(value));
break;
case 3:
- jsonUtf8.WriteString(key, value.AsSpan(), escape: true);
+ jsonUtf8.WriteString(key, value.AsSpan());
break;
case 4:
- jsonUtf8.WriteString(key, Encoding.UTF8.GetBytes(value), escape: true);
+ jsonUtf8.WriteString(key, Encoding.UTF8.GetBytes(value));
break;
case 5:
- jsonUtf8.WriteString(key.AsSpan(), value, escape: true);
+ jsonUtf8.WriteString(key.AsSpan(), value);
break;
case 6:
- jsonUtf8.WriteString(key.AsSpan(), Encoding.UTF8.GetBytes(value), escape: true);
+ jsonUtf8.WriteString(key.AsSpan(), Encoding.UTF8.GetBytes(value));
break;
case 7:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value, escape: true);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value);
break;
case 8:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value.AsSpan(), escape: true);
- break;
- case 9:
- jsonUtf8.WriteString(key, value, escape: false);
- break;
- case 10:
- jsonUtf8.WriteString(key.AsSpan(), value.AsSpan(), escape: false);
- break;
- case 11:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(value), escape: false);
- break;
- case 12:
- jsonUtf8.WriteString(key, value.AsSpan(), escape: false);
- break;
- case 13:
- jsonUtf8.WriteString(key, Encoding.UTF8.GetBytes(value), escape: false);
- break;
- case 14:
- jsonUtf8.WriteString(key.AsSpan(), value, escape: false);
- break;
- case 15:
- jsonUtf8.WriteString(key.AsSpan(), Encoding.UTF8.GetBytes(value), escape: false);
- break;
- case 16:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value, escape: false);
- break;
- case 17:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value.AsSpan(), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(key), value.AsSpan());
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i >= 9)
- Assert.True(expectedStrNoEscape == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
- else
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeHtml);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- for (int i = 0; i < 4; i++)
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ for (int i = 0; i < 2; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteString(propertyName, value, escape: true);
+ jsonUtf8.WriteString(propertyName, value);
break;
case 1:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: true);
- break;
- case 2:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeHtml, escape: false);
- jsonUtf8.WriteString(propertyName, value, escape: false);
- break;
- case 3:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeHtml, escape: false);
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- for (int i = 0; i < 4; i++)
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ for (int i = 0; i < 2; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteString(propertyName, value, escape: true);
+ jsonUtf8.WriteString(propertyName, value);
break;
case 1:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: true);
- break;
- case 2:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii, escape: false);
- jsonUtf8.WriteString(propertyName, value, escape: false);
- break;
- case 3:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii, escape: false);
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
string expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
- for (int i = 0; i < 4; i++)
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ for (int i = 0; i < 2; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteString(propertyName, value, escape: true);
+ jsonUtf8.WriteString(propertyName, value);
break;
case 1:
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: true);
- break;
- case 2:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii, escape: false);
- jsonUtf8.WriteString(propertyName, value, escape: false);
- break;
- case 3:
- expectedStr = GetEscapedExpectedString(prettyPrint: formatted, propertyName, value, StringEscapeHandling.EscapeNonAscii, escape: false);
- jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value), escape: false);
+ jsonUtf8.WriteString(Encoding.UTF8.GetBytes(propertyName), Encoding.UTF8.GetBytes(value));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
[InlineData(false, false)]
public void InvalidUTF8(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ var validUtf8 = new byte[2] { 0xc3, 0xb1 }; // 0xF1
+ var invalidUtf8 = new byte[2] { 0xc3, 0x28 };
jsonUtf8.WriteStartObject();
- for (int i = 0; i < 8; i++)
+ for (int i = 0; i < 4; i++)
{
- try
+ switch (i)
{
- switch (i)
- {
- case 0:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0x28 }, new byte[2] { 0xc3, 0x28 }, escape: false);
- AssertWriterThrow(noThrow: false);
- break;
- case 1:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0x28 }, new byte[2] { 0xc3, 0xb1 }, escape: false);
- AssertWriterThrow(noThrow: true);
- break;
- case 2:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0xb1 }, new byte[2] { 0xc3, 0x28 }, escape: false);
- AssertWriterThrow(noThrow: false);
- break;
- case 3:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0xb1 }, new byte[2] { 0xc3, 0xb1 }, escape: false);
- AssertWriterThrow(noThrow: true);
- break;
- case 4:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0x28 }, new byte[2] { 0xc3, 0x28 }, escape: true);
- AssertWriterThrow(noThrow: false);
- break;
- case 5:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0x28 }, new byte[2] { 0xc3, 0xb1 }, escape: true);
- AssertWriterThrow(noThrow: false);
- break;
- case 6:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0xb1 }, new byte[2] { 0xc3, 0x28 }, escape: true);
- AssertWriterThrow(noThrow: false);
- break;
- case 7:
- jsonUtf8.WriteString(new byte[2] { 0xc3, 0xb1 }, new byte[2] { 0xc3, 0xb1 }, escape: true);
- AssertWriterThrow(noThrow: true);
- break;
- }
+ case 0:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(invalidUtf8, invalidUtf8));
+ break;
+ case 1:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(invalidUtf8, validUtf8));
+ break;
+ case 2:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(validUtf8, invalidUtf8));
+ break;
+ case 3:
+ jsonUtf8.WriteString(validUtf8, validUtf8);
+ break;
}
- catch (ArgumentException) { }
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
+ }
- output.Dispose();
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void InvalidUTF16(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ var validUtf16 = new char[2] { (char)0xD801, (char)0xDC37 }; // 0x10437
+ var invalidUtf16 = new char[2] { (char)0xD801, 'a' };
+
+ jsonUtf8.WriteStartObject();
+ for (int i = 0; i < 4; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(invalidUtf16, invalidUtf16));
+ break;
+ case 1:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(invalidUtf16, validUtf16));
+ break;
+ case 2:
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(validUtf16, invalidUtf16));
+ break;
+ case 3:
+ jsonUtf8.WriteString(validUtf16, validUtf16);
+ break;
+ }
+ }
+ jsonUtf8.WriteEndObject();
+ jsonUtf8.Flush();
}
[Theory]
[InlineData(false, false)]
public void WriteCustomStrings(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- var output = new ArrayBufferWriter(10);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(10);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
for (int i = 0; i < 1_000; i++)
- jsonUtf8.WriteString("message", "Hello, World!", escape: false);
+ {
+ jsonUtf8.WriteString("message", "Hello, World!");
+ }
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(GetCustomExpectedString(formatted), actualStr);
-
- output.Dispose();
+ AssertContents(GetCustomExpectedString(formatted), output);
}
[Theory]
{
string expectedStr = GetStartEndExpectedString(prettyPrint: formatted);
- var output = new ArrayBufferWriter(1024);
-
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartArray();
jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndArray();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
[Theory]
{
string expectedStr = "[}";
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = true };
+ var output = new ArrayBufferWriter<byte>(1024);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = true });
-
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartArray();
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
{
string expectedStr = "{]";
- var output = new ArrayBufferWriter(1024);
-
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = true });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = true };
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
jsonUtf8.WriteEndArray();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
{
string expectedStr = GetStartEndWithPropertyArrayExpectedString(prettyPrint: formatted);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteStartArray("property name", escape: false);
+ jsonUtf8.WriteStartArray("property name");
break;
case 1:
- jsonUtf8.WriteStartArray("property name".AsSpan(), escape: false);
+ jsonUtf8.WriteStartArray("property name".AsSpan());
break;
case 2:
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("property name"), escape: false);
- break;
- case 3:
- jsonUtf8.WriteStartArray("property name", escape: true);
- break;
- case 4:
- jsonUtf8.WriteStartArray("property name".AsSpan(), escape: true);
- break;
- case 5:
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("property name"), escape: true);
+ jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("property name"));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
var key = new string(keyChars);
string expectedStr = GetStartEndWithPropertyArrayExpectedString(key, prettyPrint: formatted, escape: true);
- string expectedStrNoEscape = GetStartEndWithPropertyArrayExpectedString(key, prettyPrint: formatted, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteStartArray(key, escape: false);
+ jsonUtf8.WriteStartArray(key);
break;
case 1:
- jsonUtf8.WriteStartArray(key.AsSpan(), escape: false);
+ jsonUtf8.WriteStartArray(key.AsSpan());
break;
case 2:
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes(key), escape: false);
- break;
- case 3:
- jsonUtf8.WriteStartArray(key, escape: true);
- break;
- case 4:
- jsonUtf8.WriteStartArray(key.AsSpan(), escape: true);
- break;
- case 5:
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes(key), escape: true);
+ jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes(key));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.Equal(expectedStrNoEscape, actualStr);
- else
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
{
string expectedStr = GetStartEndWithPropertyObjectExpectedString(prettyPrint: formatted);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteStartObject("property name", escape: false);
+ jsonUtf8.WriteStartObject("property name");
break;
case 1:
- jsonUtf8.WriteStartObject("property name".AsSpan(), escape: false);
+ jsonUtf8.WriteStartObject("property name".AsSpan());
break;
case 2:
- jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes("property name"), escape: false);
- break;
- case 3:
- jsonUtf8.WriteStartObject("property name", escape: true);
- break;
- case 4:
- jsonUtf8.WriteStartObject("property name".AsSpan(), escape: true);
- break;
- case 5:
- jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes("property name"), escape: true);
+ jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes("property name"));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
var key = new string(keyChars);
string expectedStr = GetStartEndWithPropertyObjectExpectedString(key, prettyPrint: formatted, escape: true);
- string expectedStrNoEscape = GetStartEndWithPropertyObjectExpectedString(key, prettyPrint: formatted, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteStartObject(key, escape: false);
+ jsonUtf8.WriteStartObject(key);
break;
case 1:
- jsonUtf8.WriteStartObject(key.AsSpan(), escape: false);
+ jsonUtf8.WriteStartObject(key.AsSpan());
break;
case 2:
- jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes(key), escape: false);
- break;
- case 3:
- jsonUtf8.WriteStartObject(key, escape: true);
- break;
- case 4:
- jsonUtf8.WriteStartObject(key.AsSpan(), escape: true);
- break;
- case 5:
- jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes(key), escape: true);
+ jsonUtf8.WriteStartObject(Encoding.UTF8.GetBytes(key));
break;
}
jsonUtf8.WriteEndObject();
- jsonUtf8.WriteEndObject();
- jsonUtf8.Flush();
-
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.Equal(expectedStrNoEscape, actualStr);
- else
- Assert.Equal(expectedStr, actualStr);
+ jsonUtf8.WriteEndObject();
+ jsonUtf8.Flush();
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
{
string expectedStr = GetArrayWithPropertyExpectedString(prettyPrint: formatted);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
+ var output = new ArrayBufferWriter<byte>(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteStartArray("message", escape: false);
+ jsonUtf8.WriteStartArray("message");
break;
case 1:
- jsonUtf8.WriteStartArray("message".AsSpan(), escape: false);
+ jsonUtf8.WriteStartArray("message".AsSpan());
break;
case 2:
- jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("message"), escape: false);
+ jsonUtf8.WriteStartArray(Encoding.UTF8.GetBytes("message"));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
public void WriteBooleanValue(bool formatted, bool skipValidation, bool value, string keyString)
{
string expectedStr = GetBooleanExpectedString(prettyPrint: formatted, keyString, value, escape: true);
- string expectedStrNoEscape = GetBooleanExpectedString(prettyPrint: formatted, keyString, value, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteBoolean(keyString, value, escape: false);
+ jsonUtf8.WriteBoolean(keyString, value);
break;
case 1:
- jsonUtf8.WriteBoolean(keyString.AsSpan(), value, escape: false);
+ jsonUtf8.WriteBoolean(keyString.AsSpan(), value);
break;
case 2:
- jsonUtf8.WriteBoolean(Encoding.UTF8.GetBytes(keyString), value, escape: false);
- break;
- case 3:
- jsonUtf8.WriteBoolean(keyString, value, escape: true);
- break;
- case 4:
- jsonUtf8.WriteBoolean(keyString.AsSpan(), value, escape: true);
- break;
- case 5:
- jsonUtf8.WriteBoolean(Encoding.UTF8.GetBytes(keyString), value, escape: true);
+ jsonUtf8.WriteBoolean(Encoding.UTF8.GetBytes(keyString), value);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.True(expectedStrNoEscape == actualStr, $"Case: {i}, | Expected: {expectedStrNoEscape}, | Actual: {actualStr}, | Value: {value}");
- else
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}, | Value: {value}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
public void WriteNullValue(bool formatted, bool skipValidation, string keyString)
{
string expectedStr = GetNullExpectedString(prettyPrint: formatted, keyString, escape: true);
- string expectedStrNoEscape = GetNullExpectedString(prettyPrint: formatted, keyString, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(16);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteNull(keyString, escape: false);
+ jsonUtf8.WriteNull(keyString);
+ jsonUtf8.WriteNull(keyString);
break;
case 1:
- jsonUtf8.WriteNull(keyString.AsSpan(), escape: false);
+ jsonUtf8.WriteNull(keyString.AsSpan());
+ jsonUtf8.WriteNull(keyString.AsSpan());
break;
case 2:
- jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString), escape: false);
- break;
- case 3:
- jsonUtf8.WriteNull(keyString, escape: true);
- break;
- case 4:
- jsonUtf8.WriteNull(keyString.AsSpan(), escape: true);
- break;
- case 5:
- jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString), escape: true);
+ jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString));
+ jsonUtf8.WriteNull(Encoding.UTF8.GetBytes(keyString));
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.True(expectedStrNoEscape == actualStr, $"Case: {i}, | Expected: {expectedStrNoEscape}, | Actual: {actualStr}");
- else
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
{
string expectedStr = GetIntegerExpectedString(prettyPrint: formatted, value);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
switch (i)
{
case 0:
- jsonUtf8.WriteNumber("message", value, escape: false);
+ jsonUtf8.WriteNumber("message", value);
break;
case 1:
- jsonUtf8.WriteNumber("message".AsSpan(), value, escape: false);
+ jsonUtf8.WriteNumber("message".AsSpan(), value);
break;
case 2:
- jsonUtf8.WriteNumber(Encoding.UTF8.GetBytes("message"), value, escape: false);
+ jsonUtf8.WriteNumber(Encoding.UTF8.GetBytes("message"), value);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- Assert.True(expectedStr == actualStr, $"Case: {i}, | Expected: {expectedStr}, | Actual: {actualStr}, | Value: {value}");
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
}
string expectedStr = GetNumbersExpectedString(prettyPrint: formatted, keyString, ints, uints, longs, ulongs, floats, doubles, decimals, escape: false);
- string expectedStrNoEscape = GetNumbersExpectedString(prettyPrint: formatted, keyString, ints, uints, longs, ulongs, floats, doubles, decimals, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- for (int j = 0; j < 6; j++)
+ for (int j = 0; j < 3; j++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
ReadOnlySpan<char> keyUtf16 = keyString.AsSpan();
ReadOnlySpan<byte> keyUtf8 = Encoding.UTF8.GetBytes(keyString);
{
case 0:
for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyString, floats[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, floats[i]);
for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyString, ints[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, ints[i]);
for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyString, uints[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, uints[i]);
for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyString, doubles[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, doubles[i]);
for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyString, longs[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, longs[i]);
for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyString, ulongs[i], escape: false);
+ jsonUtf8.WriteNumber(keyString, ulongs[i]);
for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyString, decimals[i], escape: false);
- jsonUtf8.WriteStartArray(keyString, escape: false);
+ jsonUtf8.WriteNumber(keyString, decimals[i]);
+ jsonUtf8.WriteStartArray(keyString);
break;
case 1:
for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, floats[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, floats[i]);
for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, ints[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, ints[i]);
for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, uints[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, uints[i]);
for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, doubles[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, doubles[i]);
for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, longs[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, longs[i]);
for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, ulongs[i], escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, ulongs[i]);
for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, decimals[i], escape: false);
- jsonUtf8.WriteStartArray(keyUtf16, escape: false);
+ jsonUtf8.WriteNumber(keyUtf16, decimals[i]);
+ jsonUtf8.WriteStartArray(keyUtf16);
break;
case 2:
for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, floats[i], escape: false);
- for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, ints[i], escape: false);
- for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, uints[i], escape: false);
- for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, doubles[i], escape: false);
- for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, longs[i], escape: false);
- for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, ulongs[i], escape: false);
- for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, decimals[i], escape: false);
- jsonUtf8.WriteStartArray(keyUtf8, escape: false);
- break;
- case 3:
- for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyString, floats[i], escape: true);
- for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyString, ints[i], escape: true);
- for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyString, uints[i], escape: true);
- for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyString, doubles[i], escape: true);
- for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyString, longs[i], escape: true);
- for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyString, ulongs[i], escape: true);
- for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyString, decimals[i], escape: true);
- jsonUtf8.WriteStartArray(keyString, escape: true);
- break;
- case 4:
- for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, floats[i], escape: true);
- for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, ints[i], escape: true);
- for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, uints[i], escape: true);
- for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, doubles[i], escape: true);
- for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, longs[i], escape: true);
- for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, ulongs[i], escape: true);
- for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyUtf16, decimals[i], escape: true);
- jsonUtf8.WriteStartArray(keyUtf16, escape: true);
- break;
- case 5:
- for (int i = 0; i < floats.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, floats[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, floats[i]);
for (int i = 0; i < ints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, ints[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, ints[i]);
for (int i = 0; i < uints.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, uints[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, uints[i]);
for (int i = 0; i < doubles.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, doubles[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, doubles[i]);
for (int i = 0; i < longs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, longs[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, longs[i]);
for (int i = 0; i < ulongs.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, ulongs[i], escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, ulongs[i]);
for (int i = 0; i < decimals.Length; i++)
- jsonUtf8.WriteNumber(keyUtf8, decimals[i], escape: true);
- jsonUtf8.WriteStartArray(keyUtf8, escape: true);
+ jsonUtf8.WriteNumber(keyUtf8, decimals[i]);
+ jsonUtf8.WriteStartArray(keyUtf8);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
// TODO: The output doesn't match what JSON.NET does (different rounding/e-notation).
- //if (j < 3)
- // Assert.Equal(expectedStrNoEscape, actualStr);
- //else
- // Assert.Equal(expectedStr, actualStr);
+ // AssertContents(expectedStr, output);
+ }
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueInt32(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue(1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueInt64(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((long)1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueUInt32(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((uint)1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueUInt64(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((ulong)1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueSingle(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
- output.Dispose();
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((float)1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueDouble(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((double)1234567);
+ numberOfElements++;
+ }
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public void WriteNumberValueDecimal(bool formatted, bool skipValidation)
+ {
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+
+ var output = new ArrayBufferWriter<byte>();
+ int initialCapacity = output.Capacity;
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
+
+ int numberOfElements = 0;
+ jsonUtf8.WriteStartArray();
+ while (initialCapacity == output.Capacity)
+ {
+ jsonUtf8.WriteNumberValue((decimal)1234567);
+ numberOfElements++;
}
+ Assert.Equal(initialCapacity + 4096, output.Capacity);
+ jsonUtf8.WriteEndArray();
+ jsonUtf8.Flush();
+
+ string expectedStr = GetNumbersExpectedString(formatted, numberOfElements);
+ AssertContents(expectedStr, output);
}
[Theory]
var guids = new Guid[numberOfItems];
for (int i = 0; i < numberOfItems; i++)
+ {
guids[i] = Guid.NewGuid();
+ }
string expectedStr = GetGuidsExpectedString(prettyPrint: formatted, keyString, guids, escape: true);
- string expectedStrNoEscape = GetGuidsExpectedString(prettyPrint: formatted, keyString, guids, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
ReadOnlySpan<char> keyUtf16 = keyString.AsSpan();
ReadOnlySpan<byte> keyUtf8 = Encoding.UTF8.GetBytes(keyString);
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
{
case 0:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, guids[j], escape: false);
- jsonUtf8.WriteStartArray(keyString, escape: false);
+ jsonUtf8.WriteString(keyString, guids[j]);
+ jsonUtf8.WriteStartArray(keyString);
break;
case 1:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, guids[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf16, escape: false);
+ jsonUtf8.WriteString(keyUtf16, guids[j]);
+ jsonUtf8.WriteStartArray(keyUtf16);
break;
case 2:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, guids[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf8, escape: false);
- break;
- case 3:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, guids[j], escape: true);
- jsonUtf8.WriteStartArray(keyString, escape: true);
- break;
- case 4:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, guids[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf16, escape: true);
- break;
- case 5:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, guids[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf8, escape: true);
+ jsonUtf8.WriteString(keyUtf8, guids[j]);
+ jsonUtf8.WriteStartArray(keyUtf8);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.Equal(expectedStrNoEscape, actualStr);
- else
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
dates[i] = start.AddDays(random.Next(range));
string expectedStr = GetDatesExpectedString(prettyPrint: formatted, keyString, dates, escape: true);
- string expectedStrNoEscape = GetDatesExpectedString(prettyPrint: formatted, keyString, dates, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
ReadOnlySpan<char> keyUtf16 = keyString.AsSpan();
ReadOnlySpan<byte> keyUtf8 = Encoding.UTF8.GetBytes(keyString);
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
{
case 0:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyString, escape: false);
+ jsonUtf8.WriteString(keyString, dates[j]);
+ jsonUtf8.WriteStartArray(keyString);
break;
case 1:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf16, escape: false);
+ jsonUtf8.WriteString(keyUtf16, dates[j]);
+ jsonUtf8.WriteStartArray(keyUtf16);
break;
case 2:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf8, escape: false);
- break;
- case 3:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyString, escape: true);
- break;
- case 4:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf16, escape: true);
- break;
- case 5:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf8, escape: true);
+ jsonUtf8.WriteString(keyUtf8, dates[j]);
+ jsonUtf8.WriteStartArray(keyUtf8);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.Equal(expectedStrNoEscape, actualStr);
- else
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
dates[i] = new DateTimeOffset(start.AddDays(random.Next(range)));
string expectedStr = GetDatesExpectedString(prettyPrint: formatted, keyString, dates, escape: true);
- string expectedStrNoEscape = GetDatesExpectedString(prettyPrint: formatted, keyString, dates, escape: false);
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
ReadOnlySpan<char> keyUtf16 = keyString.AsSpan();
ReadOnlySpan<byte> keyUtf8 = Encoding.UTF8.GetBytes(keyString);
- for (int i = 0; i < 6; i++)
+ for (int i = 0; i < 3; i++)
{
- var output = new ArrayBufferWriter(1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
{
case 0:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyString, escape: false);
+ jsonUtf8.WriteString(keyString, dates[j]);
+ jsonUtf8.WriteStartArray(keyString);
break;
case 1:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf16, escape: false);
+ jsonUtf8.WriteString(keyUtf16, dates[j]);
+ jsonUtf8.WriteStartArray(keyUtf16);
break;
case 2:
for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, dates[j], escape: false);
- jsonUtf8.WriteStartArray(keyUtf8, escape: false);
- break;
- case 3:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyString, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyString, escape: true);
- break;
- case 4:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf16, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf16, escape: true);
- break;
- case 5:
- for (int j = 0; j < numberOfItems; j++)
- jsonUtf8.WriteString(keyUtf8, dates[j], escape: true);
- jsonUtf8.WriteStartArray(keyUtf8, escape: true);
+ jsonUtf8.WriteString(keyUtf8, dates[j]);
+ jsonUtf8.WriteStartArray(keyUtf8);
break;
}
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
- ArraySegment<byte> arraySegment = output.Formatted;
- string actualStr = Encoding.UTF8.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- if (i < 3)
- Assert.Equal(expectedStrNoEscape, actualStr);
- else
- Assert.Equal(expectedStr, actualStr);
-
- output.Dispose();
+ AssertContents(expectedStr, output);
}
}
[InlineData(false, false)]
public void WriteLargeKeyOrValue(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
-
- Span<byte> key;
- Span<byte> value;
+ byte[] key;
+ byte[] value;
try
{
return;
}
- key.Fill((byte)'a');
- value.Fill((byte)'b');
+ key.AsSpan().Fill((byte)'a');
+ value.AsSpan().Fill((byte)'b');
- var output = new ArrayBufferWriter(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- try
{
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
- jsonUtf8.WriteString(key, DateTime.Now, escape: false);
- Assert.True(false, $"Expected ArgumentException for data too large wasn't thrown. KeyLength: {key.Length}");
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteString(key, DateTime.Now));
+ Assert.Equal(0, output.WrittenCount);
}
- catch (ArgumentException) { }
- try
{
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartArray();
- jsonUtf8.WriteStringValue(value, escape: false);
- Assert.True(false, $"Expected ArgumentException for data too large wasn't thrown. ValueLength: {value.Length}");
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteStringValue(value));
+ Assert.Equal(0, output.WrittenCount);
}
- catch (ArgumentException) { }
-
- output.Dispose();
}
[ConditionalTheory(nameof(IsX64))]
[InlineData(false, false)]
public void WriteLargeKeyValue(bool formatted, bool skipValidation)
{
- var state = new JsonWriterState(options: new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation });
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
Span<byte> key;
Span<byte> value;
return;
}
- WriteTooLargeHelper(state, key, value);
- WriteTooLargeHelper(state, key.Slice(0, 1_000_000_000), value);
- WriteTooLargeHelper(state, key, value.Slice(0, 1_000_000_000));
- WriteTooLargeHelper(state, key.Slice(0, 10_000_000 / 3), value.Slice(0, 10_000_000 / 3), noThrow: true);
+ WriteTooLargeHelper(options, key, value);
+ WriteTooLargeHelper(options, key.Slice(0, 1_000_000_000), value);
+ WriteTooLargeHelper(options, key, value.Slice(0, 1_000_000_000));
+ WriteTooLargeHelper(options, key.Slice(0, 10_000_000 / 3), value.Slice(0, 10_000_000 / 3), noThrow: true);
}
- private static void WriteTooLargeHelper(JsonWriterState state, ReadOnlySpan<byte> key, ReadOnlySpan<byte> value, bool noThrow = false)
+ private static void WriteTooLargeHelper(JsonWriterOptions options, ReadOnlySpan<byte> key, ReadOnlySpan<byte> value, bool noThrow = false)
{
// Resizing is too slow, even for outerloop tests, so initialize to a large output size up front.
- var output = new ArrayBufferWriter(noThrow ? 40_000_000 : 1024);
- var jsonUtf8 = new Utf8JsonWriter(output, state);
+ var output = new ArrayBufferWriter<byte>(noThrow ? 40_000_000 : 1024);
+ var jsonUtf8 = new Utf8JsonWriter(output, options);
jsonUtf8.WriteStartObject();
try
{
- jsonUtf8.WriteString(key, value, escape: false);
+ jsonUtf8.WriteString(key, value);
if (!noThrow)
{
jsonUtf8.WriteEndObject();
jsonUtf8.Flush();
-
- output.Dispose();
- }
-
- private static void WriterDidNotThrow(bool skipValidation)
- {
- if (skipValidation)
- Assert.True(true, "Did not expect InvalidOperationException to be thrown since validation was skipped.");
- else
- Assert.True(false, "Expected InvalidOperationException to be thrown when validation is enabled.");
- }
-
- private static void WriterDidNotThrow(bool skipValidation, string message)
- {
- if (skipValidation)
- Assert.True(true, message);
- else
- Assert.True(false, message);
- }
-
- private static void AssertWriterThrow(bool noThrow)
- {
- if (noThrow)
- Assert.True(true, "Did not expect InvalidOperationException to be thrown since input was valid (or suppressEscaping was true).");
- else
- Assert.True(false, "Expected InvalidOperationException to be thrown when user passes invalid UTF-8.");
}
- private static string GetHelloWorldExpectedString(bool prettyPrint)
+ private static string GetHelloWorldExpectedString(bool prettyPrint, string propertyName, string value)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
{
- Formatting = prettyPrint ? Formatting.Indented : Formatting.None
+ Formatting = prettyPrint ? Formatting.Indented : Formatting.None,
+ StringEscapeHandling = StringEscapeHandling.EscapeHtml
};
json.WriteStartObject();
- json.WritePropertyName("message");
- json.WriteValue("Hello, World!");
+ json.WritePropertyName(propertyName);
+ json.WriteValue(value);
+ json.WritePropertyName(propertyName);
+ json.WriteValue(value);
json.WriteEnd();
json.Flush();
private static string GetCommentExpectedString(bool prettyPrint, string comment)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStringsExpectedString(bool prettyPrint, string value)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetEscapedExpectedString(bool prettyPrint, string propertyName, string value, StringEscapeHandling escaping, bool escape = true)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetCustomExpectedString(bool prettyPrint)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStartEndExpectedString(bool prettyPrint)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStartEndWithPropertyArrayExpectedString(bool prettyPrint)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStartEndWithPropertyArrayExpectedString(string key, bool prettyPrint, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStartEndWithPropertyObjectExpectedString(bool prettyPrint)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetStartEndWithPropertyObjectExpectedString(string key, bool prettyPrint, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetArrayWithPropertyExpectedString(bool prettyPrint)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetBooleanExpectedString(bool prettyPrint, string keyString, bool value, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetNullExpectedString(bool prettyPrint, string keyString, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
json.WriteStartObject();
json.WritePropertyName(keyString, escape);
json.WriteNull();
+ json.WritePropertyName(keyString, escape);
+ json.WriteNull();
json.WritePropertyName("temp");
json.WriteStartArray();
private static string GetIntegerExpectedString(bool prettyPrint, int value)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetNumbersExpectedString(bool prettyPrint, string keyString, int[] ints, uint[] uints, long[] longs, ulong[] ulongs, float[] floats, double[] doubles, decimal[] decimals, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetGuidsExpectedString(bool prettyPrint, string keyString, Guid[] guids, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
return Encoding.UTF8.GetString(ms.ToArray());
}
+ private static string GetNumbersExpectedString(bool prettyPrint, int numberOfElements)
+ {
+ var ms = new MemoryStream();
+ TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
+
+ var json = new JsonTextWriter(streamWriter)
+ {
+ Formatting = prettyPrint ? Formatting.Indented : Formatting.None,
+ };
+
+ json.WriteStartArray();
+ for (int i = 0; i < numberOfElements; i++)
+ {
+ json.WriteValue(1234567);
+ }
+ json.WriteEnd();
+
+ json.Flush();
+
+ return Encoding.UTF8.GetString(ms.ToArray());
+ }
+
private static string GetDatesExpectedString(bool prettyPrint, string keyString, DateTime[] dates, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
private static string GetDatesExpectedString(bool prettyPrint, string keyString, DateTimeOffset[] dates, bool escape = false)
{
- MemoryStream ms = new MemoryStream();
+ var ms = new MemoryStream();
TextWriter streamWriter = new StreamWriter(ms, new UTF8Encoding(false), 1024, true);
var json = new JsonTextWriter(streamWriter)
return Encoding.UTF8.GetString(ms.ToArray());
}
+
+ private static void AssertContents(string expectedValue, ArrayBufferWriter<byte> buffer)
+ {
+ Assert.Equal(
+ expectedValue,
+ Encoding.UTF8.GetString(
+ buffer.WrittenSpan
+#if netstandard
+ .ToArray()
+#endif
+ ));
+ }
}
}