From cc1c36591fdf87e5cacd755d41dd35fe9415e7dd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 26 Mar 2021 12:47:48 +0100 Subject: [PATCH 1/3] add failing test --- .../FileStream/FileStreamConformanceTests.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs index 64b44242450ef4..271cb2833c94ae 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.IO.Pipes; using System.Linq; using System.Threading.Tasks; @@ -98,6 +99,40 @@ public async Task WriteAsyncStartsWherePreviousReadAsyncHasFinished(ReadWriteMod byte[] allBytes = File.ReadAllBytes(filePath); Assert.Equal(initialData.Concat(writeBuffer), allBytes); } + + [Theory] + [MemberData(nameof(AllReadWriteModes))] + public async Task NoDataIsLostWhenWritingToFile(ReadWriteMode mode) + { + string filePath; + List writtenBytes = new List(); + + using (FileStream stream = (FileStream)await CreateWriteOnlyStreamCore(Array.Empty())) + { + filePath = stream.Name; + + // the following buffer fits into internal FileStream buffer + byte[] small = Enumerable.Repeat(byte.MinValue, BufferSize - 1).ToArray(); + // the following buffer does not fit into internal FileStream buffer + byte[] big = Enumerable.Repeat(byte.MaxValue, BufferSize + 1).ToArray(); + // in this test we are selecting a random buffer and write it to file + // the goal is to cover all possible scenarios for the internal buffering logic + Random random = new Random(12345); + for (int i = 0; i < 1000; i++) + { + byte[] bytes = random.Next() % 2 == 0 ? small : big; + + await WriteAsync(mode, stream, bytes, 0, bytes.Length); + + writtenBytes.AddRange(bytes); + Assert.Equal(writtenBytes.Count, stream.Length); + Assert.Equal(stream.Length, stream.Position); + } + } + + byte[] allBytes = File.ReadAllBytes(filePath); + Assert.Equal(writtenBytes.ToArray(), allBytes); + } } public class UnbufferedSyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests From 27f7aefd3b1e04384e96b0184c80caba2ad8d618 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 26 Mar 2021 12:52:28 +0100 Subject: [PATCH 2/3] fix the bug --- .../src/System/IO/Strategies/BufferedFileStreamStrategy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index b2a239b20b73bc..3c1229303bbe7a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -539,6 +539,7 @@ private void WriteSpan(ReadOnlySpan source, ArraySegment arraySegmen source.Slice(0, numBytes).CopyTo(_buffer!.AsSpan(_writePos)); _writePos += numBytes; source = source.Slice(numBytes); + arraySegment = arraySegment.Array != null ? arraySegment.Slice(numBytes) : arraySegment; } } From d140b3ef13da5dceb53524c2fb9788363aba639f Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 26 Mar 2021 17:03:36 +0100 Subject: [PATCH 3/3] Update src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs Co-authored-by: Stephen Toub --- .../src/System/IO/Strategies/BufferedFileStreamStrategy.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs index 3c1229303bbe7a..2b914e7bc0aec1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -539,7 +539,10 @@ private void WriteSpan(ReadOnlySpan source, ArraySegment arraySegmen source.Slice(0, numBytes).CopyTo(_buffer!.AsSpan(_writePos)); _writePos += numBytes; source = source.Slice(numBytes); - arraySegment = arraySegment.Array != null ? arraySegment.Slice(numBytes) : arraySegment; + if (arraySegment.Array != null) + { + arraySegment = arraySegment.Slice(numBytes); + } } }