Added CopyToAsync overload using default buffer size (#14219)
authorKristian Hellang <kristian@hellang.com>
Fri, 29 Sep 2017 01:12:24 +0000 (03:12 +0200)
committerStephen Toub <stoub@microsoft.com>
Fri, 29 Sep 2017 01:12:24 +0000 (21:12 -0400)
* Added CopyToAsync overload using default buffer size

* Address feedback

* Added comment about overrides and argument validation

* Renamed helper method to GetCopyBufferSize

To convey that this method should only be used for
getting a buffer size to be passed to CopyTo{Async}.

src/mscorlib/src/System/IO/Stream.cs

index 83f327b..b8df5aa 100644 (file)
@@ -111,36 +111,7 @@ namespace System.IO
 
         public Task CopyToAsync(Stream destination)
         {
-            int bufferSize = _DefaultCopyBufferSize;
-
-            if (CanSeek)
-            {
-                long length = Length;
-                long position = Position;
-                if (length <= position) // Handles negative overflows
-                {
-                    // If we go down this branch, it means there are
-                    // no bytes left in this stream.
-
-                    // Ideally we would just return Task.CompletedTask here,
-                    // but CopyToAsync(Stream, int, CancellationToken) was already
-                    // virtual at the time this optimization was introduced. So
-                    // if it does things like argument validation (checking if destination
-                    // is null and throwing an exception), then await fooStream.CopyToAsync(null)
-                    // would no longer throw if there were no bytes left. On the other hand,
-                    // we also can't roll our own argument validation and return Task.CompletedTask,
-                    // because it would be a breaking change if the stream's override didn't throw before,
-                    // or in a different order. So for simplicity, we just set the bufferSize to 1
-                    // (not 0 since the default implementation throws for 0) and forward to the virtual method.
-                    bufferSize = 1;
-                }
-                else
-                {
-                    long remaining = length - position;
-                    if (remaining > 0) // In the case of a positive overflow, stick to the default size
-                        bufferSize = (int)Math.Min(bufferSize, remaining);
-                }
-            }
+            int bufferSize = GetCopyBufferSize();
 
             return CopyToAsync(destination, bufferSize);
         }
@@ -150,6 +121,13 @@ namespace System.IO
             return CopyToAsync(destination, bufferSize, CancellationToken.None);
         }
 
+        public Task CopyToAsync(Stream destination, CancellationToken cancellationToken)
+        {
+            int bufferSize = GetCopyBufferSize();
+
+            return CopyToAsync(destination, bufferSize, cancellationToken);
+        }
+
         public virtual Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
         {
             StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
@@ -188,26 +166,7 @@ namespace System.IO
         // the current position.
         public void CopyTo(Stream destination)
         {
-            int bufferSize = _DefaultCopyBufferSize;
-
-            if (CanSeek)
-            {
-                long length = Length;
-                long position = Position;
-                if (length <= position) // Handles negative overflows
-                {
-                    // No bytes left in stream
-                    // Call the other overload with a bufferSize of 1,
-                    // in case it's made virtual in the future
-                    bufferSize = 1;
-                }
-                else
-                {
-                    long remaining = length - position;
-                    if (remaining > 0) // In the case of a positive overflow, stick to the default size
-                        bufferSize = (int)Math.Min(bufferSize, remaining);
-                }
-            }
+            int bufferSize = GetCopyBufferSize();
 
             CopyTo(destination, bufferSize);
         }
@@ -234,6 +193,36 @@ namespace System.IO
             }
         }
 
+        private int GetCopyBufferSize()
+        {
+            int bufferSize = _DefaultCopyBufferSize;
+
+            if (CanSeek)
+            {
+                long length = Length;
+                long position = Position;
+                if (length <= position) // Handles negative overflows
+                {
+                    // There are no bytes left in the stream to copy.
+                    // However, because CopyTo{Async} is virtual, we need to
+                    // ensure that any override is still invoked to provide its
+                    // own validation, so we use the smallest legal buffer size here.
+                    bufferSize = 1;
+                }
+                else
+                {
+                    long remaining = length - position;
+                    if (remaining > 0)
+                    {
+                        // In the case of a positive overflow, stick to the default size
+                        bufferSize = (int)Math.Min(bufferSize, remaining);
+                    }
+                }
+            }
+
+            return bufferSize;
+        }
+
         // Stream used to require that all cleanup logic went into Close(),
         // which was thought up before we invented IDisposable.  However, we
         // need to follow the IDisposable pattern so that users can write