Skip to content

Commit 143c442

Browse files
committed
Add File.Append/WriteAllText/Bytes{Async} overloads for span/memory
1 parent 29f3110 commit 143c442

File tree

10 files changed

+488
-71
lines changed

10 files changed

+488
-71
lines changed

src/libraries/System.Private.CoreLib/src/System/IO/File.cs

Lines changed: 282 additions & 55 deletions
Large diffs are not rendered by default.

src/libraries/System.Runtime/ref/System.Runtime.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10061,15 +10061,21 @@ public EnumerationOptions() { }
1006110061
public static partial class File
1006210062
{
1006310063
public static void AppendAllBytes(string path, byte[] bytes) { }
10064+
public static void AppendAllBytes(string path, System.ReadOnlySpan<byte> bytes) { }
1006410065
public static System.Threading.Tasks.Task AppendAllBytesAsync(string path, byte[] bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
10066+
public static System.Threading.Tasks.Task AppendAllBytesAsync(string path, System.ReadOnlyMemory<byte> bytes, System.Threading.CancellationToken cancellationToken = default) { throw null; }
1006510067
public static void AppendAllLines(string path, System.Collections.Generic.IEnumerable<string> contents) { }
1006610068
public static void AppendAllLines(string path, System.Collections.Generic.IEnumerable<string> contents, System.Text.Encoding encoding) { }
1006710069
public static System.Threading.Tasks.Task AppendAllLinesAsync(string path, System.Collections.Generic.IEnumerable<string> contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1006810070
public static System.Threading.Tasks.Task AppendAllLinesAsync(string path, System.Collections.Generic.IEnumerable<string> contents, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1006910071
public static void AppendAllText(string path, string? contents) { }
1007010072
public static void AppendAllText(string path, string? contents, System.Text.Encoding encoding) { }
10073+
public static void AppendAllText(string path, System.ReadOnlySpan<char> contents) { }
10074+
public static void AppendAllText(string path, System.ReadOnlySpan<char> contents, System.Text.Encoding encoding) { }
1007110075
public static System.Threading.Tasks.Task AppendAllTextAsync(string path, string? contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1007210076
public static System.Threading.Tasks.Task AppendAllTextAsync(string path, string? contents, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
10077+
public static System.Threading.Tasks.Task AppendAllTextAsync(string path, System.ReadOnlyMemory<char> contents, System.Threading.CancellationToken cancellationToken = default) { throw null; }
10078+
public static System.Threading.Tasks.Task AppendAllTextAsync(string path, System.ReadOnlyMemory<char> contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default) { throw null; }
1007310079
public static System.IO.StreamWriter AppendText(string path) { throw null; }
1007410080
public static void Copy(string sourceFileName, string destFileName) { }
1007510081
public static void Copy(string sourceFileName, string destFileName, bool overwrite) { }
@@ -10148,7 +10154,9 @@ public static void SetUnixFileMode(Microsoft.Win32.SafeHandles.SafeFileHandle fi
1014810154
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
1014910155
public static void SetUnixFileMode(string path, System.IO.UnixFileMode mode) { }
1015010156
public static void WriteAllBytes(string path, byte[] bytes) { }
10157+
public static void WriteAllBytes(string path, System.ReadOnlySpan<byte> bytes) { }
1015110158
public static System.Threading.Tasks.Task WriteAllBytesAsync(string path, byte[] bytes, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
10159+
public static System.Threading.Tasks.Task WriteAllBytesAsync(string path, System.ReadOnlyMemory<byte> bytes, System.Threading.CancellationToken cancellationToken = default) { throw null; }
1015210160
public static void WriteAllLines(string path, System.Collections.Generic.IEnumerable<string> contents) { }
1015310161
public static void WriteAllLines(string path, System.Collections.Generic.IEnumerable<string> contents, System.Text.Encoding encoding) { }
1015410162
public static void WriteAllLines(string path, string[] contents) { }
@@ -10157,8 +10165,12 @@ public static void WriteAllLines(string path, string[] contents, System.Text.Enc
1015710165
public static System.Threading.Tasks.Task WriteAllLinesAsync(string path, System.Collections.Generic.IEnumerable<string> contents, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1015810166
public static void WriteAllText(string path, string? contents) { }
1015910167
public static void WriteAllText(string path, string? contents, System.Text.Encoding encoding) { }
10168+
public static void WriteAllText(string path, System.ReadOnlySpan<char> contents) { }
10169+
public static void WriteAllText(string path, System.ReadOnlySpan<char> contents, System.Text.Encoding encoding) { }
1016010170
public static System.Threading.Tasks.Task WriteAllTextAsync(string path, string? contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
1016110171
public static System.Threading.Tasks.Task WriteAllTextAsync(string path, string? contents, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
10172+
public static System.Threading.Tasks.Task WriteAllTextAsync(string path, System.ReadOnlyMemory<char> contents, System.Threading.CancellationToken cancellationToken = default) { throw null; }
10173+
public static System.Threading.Tasks.Task WriteAllTextAsync(string path, System.ReadOnlyMemory<char> contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default) { throw null; }
1016210174
}
1016310175
[System.FlagsAttribute]
1016410176
public enum FileAccess

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/Append.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ protected override void Write(string path, string content, Encoding encoding)
4040
}
4141
}
4242

43+
public class File_AppendAllText_Span : File_ReadWriteAllText
44+
{
45+
protected override bool IsAppend => true;
46+
47+
protected override void Write(string path, string content)
48+
{
49+
File.AppendAllText(path, content.AsSpan());
50+
}
51+
52+
protected override void Write(string path, string content, Encoding encoding)
53+
{
54+
File.AppendAllText(path, content.AsSpan(), encoding);
55+
}
56+
}
57+
4358
public class File_AppendAllText_Encoded : File_AppendAllText
4459
{
4560
protected override void Write(string path, string content)
@@ -54,6 +69,20 @@ public void NullEncoding()
5469
}
5570
}
5671

72+
public class File_AppendAllText_Span_Encoded : File_AppendAllText
73+
{
74+
protected override void Write(string path, string content)
75+
{
76+
File.AppendAllText(path, content.AsSpan(), new UTF8Encoding(false));
77+
}
78+
79+
[Fact]
80+
public void NullEncoding()
81+
{
82+
Assert.Throws<ArgumentNullException>(() => File.AppendAllText(GetTestFilePath(), "Text".AsSpan(), null));
83+
}
84+
}
85+
5786
public class File_AppendAllLines : File_ReadWriteAllLines_Enumerable
5887
{
5988
protected override bool IsAppend => true;

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/AppendAllBytes.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.IO.Tests;
54
using System.Linq;
65
using System.Text;
76
using Xunit;
@@ -10,26 +9,28 @@ namespace System.IO.Tests
109
{
1110
public class File_AppendAllBytes : FileSystemTest
1211
{
13-
1412
[Fact]
1513
public void NullParameters()
1614
{
1715
string path = GetTestFilePath();
1816

1917
Assert.Throws<ArgumentNullException>(() => File.AppendAllBytes(null, new byte[0]));
18+
Assert.Throws<ArgumentNullException>(() => File.AppendAllBytes(null, ReadOnlySpan<byte>.Empty));
2019
Assert.Throws<ArgumentNullException>(() => File.AppendAllBytes(path, null));
2120
}
2221

2322
[Fact]
2423
public void NonExistentPath()
2524
{
2625
Assert.Throws<DirectoryNotFoundException>(() => File.AppendAllBytes(Path.Combine(TestDirectory, GetTestFileName(), GetTestFileName()), new byte[0]));
26+
Assert.Throws<DirectoryNotFoundException>(() => File.AppendAllBytes(Path.Combine(TestDirectory, GetTestFileName(), GetTestFileName()), ReadOnlySpan<byte>.Empty));
2727
}
2828

2929
[Fact]
3030
public void InvalidParameters()
3131
{
3232
Assert.Throws<ArgumentException>(() => File.AppendAllBytes(string.Empty, new byte[0]));
33+
Assert.Throws<ArgumentException>(() => File.AppendAllBytes(string.Empty, ReadOnlySpan<byte>.Empty));
3334
}
3435

3536

@@ -43,10 +44,11 @@ public void AppendAllBytes_WithValidInput_AppendsBytes()
4344

4445
File.WriteAllBytes(path, initialBytes);
4546
File.AppendAllBytes(path, additionalBytes);
47+
File.AppendAllBytes(path, additionalBytes.AsSpan());
4648

4749
byte[] result = File.ReadAllBytes(path);
4850

49-
byte[] expectedBytes = initialBytes.Concat(additionalBytes).ToArray();
51+
byte[] expectedBytes = initialBytes.Concat(additionalBytes).Concat(additionalBytes).ToArray();
5052

5153
Assert.True(result.SequenceEqual(expectedBytes));
5254
}
@@ -58,6 +60,7 @@ public void EmptyContentCreatesFile()
5860
string path = GetTestFilePath();
5961
Assert.False(File.Exists(path));
6062
File.AppendAllBytes(path, new byte[0]);
63+
File.AppendAllBytes(path, ReadOnlySpan<byte>.Empty);
6164
Assert.True(File.Exists(path));
6265
Assert.Empty(File.ReadAllBytes(path));
6366
}
@@ -71,6 +74,7 @@ public void OpenFile_ThrowsIOException()
7174
using (File.Create(path))
7275
{
7376
Assert.Throws<IOException>(() => File.AppendAllBytes(path, bytes));
77+
Assert.Throws<IOException>(() => File.AppendAllBytes(path, bytes.AsSpan()));
7478
}
7579
}
7680

@@ -97,6 +101,7 @@ public void AppendToReadOnlyFileAsync()
97101
else
98102
{
99103
Assert.Throws<UnauthorizedAccessException>(() => File.AppendAllBytes(path, dataToAppend));
104+
Assert.Throws<UnauthorizedAccessException>(() => File.AppendAllBytes(path, dataToAppend.AsSpan()));
100105
}
101106
}
102107
finally

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/AppendAllBytesAsync.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ public async Task NullParametersAsync()
1919
string path = GetTestFilePath();
2020

2121
await Assert.ThrowsAsync<ArgumentNullException>("path", async () => await File.AppendAllBytesAsync(null, new byte[0]));
22+
await Assert.ThrowsAsync<ArgumentNullException>("path", async () => await File.AppendAllBytesAsync(null, ReadOnlyMemory<byte>.Empty));
2223
await Assert.ThrowsAsync<ArgumentNullException>("bytes", async () => await File.AppendAllBytesAsync(path, null));
2324
}
2425

2526
[Fact]
2627
public void NonExistentPathAsync()
2728
{
2829
Assert.ThrowsAsync<DirectoryNotFoundException>(() => File.AppendAllBytesAsync(Path.Combine(TestDirectory, GetTestFileName(), GetTestFileName()), new byte[0]));
30+
Assert.ThrowsAsync<DirectoryNotFoundException>(() => File.AppendAllBytesAsync(Path.Combine(TestDirectory, GetTestFileName(), GetTestFileName()), ReadOnlyMemory<byte>.Empty));
2931
}
3032

3133
[Fact]
3234
public async Task InvalidParametersAsync()
3335
{
3436
await Assert.ThrowsAsync<ArgumentException>("path", async () => await File.AppendAllBytesAsync(string.Empty, new byte[0]));
37+
await Assert.ThrowsAsync<ArgumentException>("path", async () => await File.AppendAllBytesAsync(string.Empty, ReadOnlyMemory<byte>.Empty));
3538
}
3639

3740
[Fact]
@@ -44,10 +47,11 @@ public async Task AppendAllBytesAsync_WithValidInput_AppendsBytes()
4447

4548
await File.WriteAllBytesAsync(path, initialBytes);
4649
await File.AppendAllBytesAsync(path, additionalBytes);
50+
await File.AppendAllBytesAsync(path, additionalBytes.AsMemory());
4751

4852
byte[] result = await File.ReadAllBytesAsync(path);
4953

50-
byte[] expectedBytes = initialBytes.Concat(additionalBytes).ToArray();
54+
byte[] expectedBytes = initialBytes.Concat(additionalBytes).Concat(additionalBytes).ToArray();
5155

5256
Assert.True(result.SequenceEqual(expectedBytes));
5357
}
@@ -57,6 +61,7 @@ public async Task EmptyContentCreatesFileAsync()
5761
{
5862
string path = GetTestFilePath();
5963
await File.AppendAllBytesAsync(path, new byte[0]);
64+
await File.AppendAllBytesAsync(path, ReadOnlyMemory<byte>.Empty);
6065
Assert.True(File.Exists(path));
6166
Assert.Empty(await File.ReadAllBytesAsync(path));
6267
}
@@ -70,6 +75,7 @@ public async Task OpenFile_ThrowsIOExceptionAsync()
7075
using (File.Create(path))
7176
{
7277
await Assert.ThrowsAsync<IOException>(async () => await File.AppendAllBytesAsync(path, bytes));
78+
await Assert.ThrowsAsync<IOException>(async () => await File.AppendAllBytesAsync(path, bytes.AsMemory()));
7379
}
7480
}
7581

@@ -91,11 +97,13 @@ public async Task AppendToReadOnlyFileAsync()
9197
if (PlatformDetection.IsNotWindows && PlatformDetection.IsPrivilegedProcess)
9298
{
9399
await File.AppendAllBytesAsync(path, dataToAppend);
94-
Assert.Equal(dataToAppend, await File.ReadAllBytesAsync(path));
100+
await File.AppendAllBytesAsync(path, dataToAppend.AsMemory());
101+
Assert.Equal(dataToAppend.Concat(dataToAppend), await File.ReadAllBytesAsync(path));
95102
}
96103
else
97104
{
98105
await Assert.ThrowsAsync<UnauthorizedAccessException>(async () => await File.AppendAllBytesAsync(path, dataToAppend));
106+
await Assert.ThrowsAsync<UnauthorizedAccessException>(async () => await File.AppendAllBytesAsync(path, dataToAppend.AsMemory()));
99107
}
100108
}
101109
finally

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/AppendAsync.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ public override Task TaskAlreadyCanceledAsync()
2828
}
2929
}
3030

31+
public class File_AppendAllTextAsync_Memory : File_ReadWriteAllTextAsync
32+
{
33+
protected override bool IsAppend => true;
34+
35+
protected override Task WriteAsync(string path, string content) => File.AppendAllTextAsync(path, content.AsMemory());
36+
37+
protected override Task WriteAsync(string path, string content, Encoding encoding) => File.AppendAllTextAsync(path, content.AsMemory(), encoding);
38+
39+
[Fact]
40+
public override Task TaskAlreadyCanceledAsync()
41+
{
42+
string path = GetTestFilePath();
43+
CancellationTokenSource source = new CancellationTokenSource();
44+
CancellationToken token = source.Token;
45+
source.Cancel();
46+
Assert.True(File.AppendAllTextAsync(path, "".AsMemory(), token).IsCanceled);
47+
return Assert.ThrowsAsync<TaskCanceledException>(async () => await File.AppendAllTextAsync(path, "".AsMemory(), token));
48+
}
49+
}
50+
3151
public class File_AppendAllTextAsync_Encoded : File_AppendAllTextAsync
3252
{
3353
protected override Task WriteAsync(string path, string content) =>
@@ -51,6 +71,29 @@ public override Task TaskAlreadyCanceledAsync()
5171
}
5272
}
5373

74+
public class File_AppendAllTextAsync_Memory_Encoded : File_AppendAllTextAsync
75+
{
76+
protected override Task WriteAsync(string path, string content) =>
77+
File.AppendAllTextAsync(path, content.AsMemory(), new UTF8Encoding(false));
78+
79+
[Fact]
80+
public Task NullEncodingAsync() => Assert.ThrowsAsync<ArgumentNullException>(
81+
"encoding",
82+
async () => await File.AppendAllTextAsync(GetTestFilePath(), "Text".AsMemory(), null));
83+
84+
[Fact]
85+
public override Task TaskAlreadyCanceledAsync()
86+
{
87+
string path = GetTestFilePath();
88+
CancellationTokenSource source = new CancellationTokenSource();
89+
CancellationToken token = source.Token;
90+
source.Cancel();
91+
Assert.True(File.AppendAllTextAsync(path, "".AsMemory(), Encoding.UTF8, token).IsCanceled);
92+
return Assert.ThrowsAsync<TaskCanceledException>(
93+
async () => await File.AppendAllTextAsync(path, "".AsMemory(), Encoding.UTF8, token));
94+
}
95+
}
96+
5497
public class File_AppendAllLinesAsync : File_ReadWriteAllLines_EnumerableAsync
5598
{
5699
protected override bool IsAppend => true;

0 commit comments

Comments
 (0)