Avoid .ToArray() in JsonSerializer .NET Standard implementation (dotnet/corefx#37976)
authorAdam Sitnik <adam.sitnik@gmail.com>
Wed, 29 May 2019 16:58:07 +0000 (09:58 -0700)
committerGitHub <noreply@github.com>
Wed, 29 May 2019 16:58:07 +0000 (09:58 -0700)
* PooledBufferWriter<T> is always used as byte buffer, make it PooledByteBufferWriter to be able to call stream.Write(bytes)

* avoid expensive .ToArray in .NET Standard implementation

* rename

Commit migrated from https://github.com/dotnet/corefx/commit/e3a4edabd12b5f7dc9be20136cc867bbe67f1f7c

src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Helpers.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Stream.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PooledByteBufferWriter.cs [moved from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PooledBufferWriter.cs with 73% similarity]

index 585c7e4..fc3df9a 100644 (file)
     <Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.cs" />
     <Compile Include="System\Text\Json\Serialization\MemberAccessor.cs" />
     <Compile Include="System\Text\Json\Serialization\Policies\JsonValueConverter.cs" />
-    <Compile Include="System\Text\Json\Serialization\PooledBufferWriter.cs" />
+    <Compile Include="System\Text\Json\Serialization\PooledByteBufferWriter.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" />
index 573f008..7fd8aca 100644 (file)
@@ -63,7 +63,7 @@ namespace System.Text.Json.Serialization
 
             byte[] result;
 
-            using (var output = new PooledBufferWriter<byte>(options.DefaultBufferSize))
+            using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
             {
                 WriteCore(output, value, type, options);
                 result = output.WrittenMemory.ToArray();
@@ -81,7 +81,7 @@ namespace System.Text.Json.Serialization
 
             string result;
 
-            using (var output = new PooledBufferWriter<byte>(options.DefaultBufferSize))
+            using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
             {
                 WriteCore(output, value, type, options);
                 result = JsonReaderHelper.TranscodeHelper(output.WrittenMemory.Span);
@@ -90,7 +90,7 @@ namespace System.Text.Json.Serialization
             return result;
         }
 
-        private static void WriteCore(PooledBufferWriter<byte> output, object value, Type type, JsonSerializerOptions options)
+        private static void WriteCore(PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
         {
             Debug.Assert(type != null || value == null);
 
index 9aa8ece..c001565 100644 (file)
@@ -2,7 +2,6 @@
 // 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.Collections.Generic;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
@@ -52,7 +51,7 @@ namespace System.Text.Json.Serialization
 
             JsonWriterOptions writerOptions = options.GetWriterOptions();
 
-            using (var bufferWriter = new PooledBufferWriter<byte>(options.DefaultBufferSize))
+            using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize))
             using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions))
             {
                 if (value == null)
@@ -60,12 +59,8 @@ namespace System.Text.Json.Serialization
                     writer.WriteNullValue();
                     writer.Flush();
 
-#if BUILDING_INBOX_LIBRARY
-                    await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
-#else
-                    // todo: stackalloc or pool here?
-                    await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false);
-#endif
+                    await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false);
+
                     return;
                 }
 
@@ -88,12 +83,8 @@ namespace System.Text.Json.Serialization
                     isFinalBlock = Write(writer, flushThreshold, options, ref state);
                     writer.Flush();
 
-#if BUILDING_INBOX_LIBRARY
-                    await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
-#else
-                    // todo: use pool here to avod extra alloc?
-                    await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false);
-#endif
+                    await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false);
+
                     bufferWriter.Clear();
                 } while (!isFinalBlock);
             }
@@ -4,28 +4,31 @@
 
 using System.Buffers;
 using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace System.Text.Json.Serialization
 {
     /// <summary>
     ///   This is an implementation detail and MUST NOT be called by source-package consumers.
     /// </summary>
-    internal sealed class PooledBufferWriter<T> : IBufferWriter<T>, IDisposable
+    internal sealed class PooledByteBufferWriter : IBufferWriter<byte>, IDisposable
     {
-        private T[] _rentedBuffer;
+        private byte[] _rentedBuffer;
         private int _index;
 
         private const int MinimumBufferSize = 256;
 
-        public PooledBufferWriter(int initialCapacity)
+        public PooledByteBufferWriter(int initialCapacity)
         {
             Debug.Assert(initialCapacity > 0);
 
-            _rentedBuffer = ArrayPool<T>.Shared.Rent(initialCapacity);
+            _rentedBuffer = ArrayPool<byte>.Shared.Rent(initialCapacity);
             _index = 0;
         }
 
-        public ReadOnlyMemory<T> WrittenMemory
+        public ReadOnlyMemory<byte> WrittenMemory
         {
             get
             {
@@ -85,7 +88,7 @@ namespace System.Text.Json.Serialization
             }
 
             ClearHelper();
-            ArrayPool<T>.Shared.Return(_rentedBuffer);
+            ArrayPool<byte>.Shared.Return(_rentedBuffer);
             _rentedBuffer = null;
         }
 
@@ -98,18 +101,30 @@ namespace System.Text.Json.Serialization
             _index += count;
         }
 
-        public Memory<T> GetMemory(int sizeHint = 0)
+        public Memory<byte> GetMemory(int sizeHint = 0)
         {
             CheckAndResizeBuffer(sizeHint);
             return _rentedBuffer.AsMemory(_index);
         }
 
-        public Span<T> GetSpan(int sizeHint = 0)
+        public Span<byte> GetSpan(int sizeHint = 0)
         {
             CheckAndResizeBuffer(sizeHint);
             return _rentedBuffer.AsSpan(_index);
         }
 
+#if BUILDING_INBOX_LIBRARY
+        internal ValueTask WriteToStreamAsync(Stream destination, CancellationToken cancellationToken)
+        {
+            return destination.WriteAsync(WrittenMemory, cancellationToken);
+        }
+#else
+        internal Task WriteToStreamAsync(Stream destination, CancellationToken cancellationToken)
+        {
+            return destination.WriteAsync(_rentedBuffer, 0, _index, cancellationToken);
+        }
+#endif
+
         private void CheckAndResizeBuffer(int sizeHint)
         {
             Debug.Assert(_rentedBuffer != null);
@@ -128,17 +143,17 @@ namespace System.Text.Json.Serialization
 
                 int newSize = checked(_rentedBuffer.Length + growBy);
 
-                T[] oldBuffer = _rentedBuffer;
+                byte[] oldBuffer = _rentedBuffer;
 
-                _rentedBuffer = ArrayPool<T>.Shared.Rent(newSize);
+                _rentedBuffer = ArrayPool<byte>.Shared.Rent(newSize);
 
                 Debug.Assert(oldBuffer.Length >= _index);
                 Debug.Assert(_rentedBuffer.Length >= _index);
 
-                Span<T> previousBuffer = oldBuffer.AsSpan(0, _index);
+                Span<byte> previousBuffer = oldBuffer.AsSpan(0, _index);
                 previousBuffer.CopyTo(_rentedBuffer);
                 previousBuffer.Clear();
-                ArrayPool<T>.Shared.Return(oldBuffer);
+                ArrayPool<byte>.Shared.Return(oldBuffer);
             }
 
             Debug.Assert(_rentedBuffer.Length - _index > 0);