-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Add async File.ReadAll*, File.AppendAll* and File.WriteAll #15372
Conversation
dcdd69d
to
da20360
Compare
|
||
// UTF-8 without BOM and with error detection. | ||
// Same as the default encoding for StreamWriter. | ||
private static Encoding UTF8NoBOM => _UTF8NoBOM ?? (_UTF8NoBOM = new UTF8Encoding(false, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Would named parameters help here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, they wouldn't hurt.
path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, | ||
FileOptions.Asynchronous | FileOptions.SequentialScan); | ||
|
||
return new StreamReader(stream, encoding, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would named parameters help here?
cancellationToken.ThrowIfCancellationRequested(); | ||
StringBuilder sb = new StringBuilder(); | ||
int bufferSize = sr.CurrentEncoding.GetMaxCharCount(DefaultBufferSize); | ||
char[] buffer = new char[bufferSize]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe StringBuilderCache.Acquire()
and ArrayPool<char>.Shared.Rent()
respectively could be used here.
|
||
return cancellationToken.IsCancellationRequested | ||
? Task.FromCanceled(cancellationToken) | ||
: InternalWriteAllTextAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Named parameters
using (fs) | ||
{ | ||
int index = 0; | ||
byte[] bytes = new byte[count]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use ArrayPool<byte>.Shared.Rent(count)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not so useful here, as that array is returned back to the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed that.
|
||
return cancellationToken.IsCancellationRequested | ||
? Task.FromCanceled(cancellationToken) | ||
: InternalWriteAllLinesAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Named parameters
: InternalWriteAllLinesAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken); | ||
} | ||
|
||
private static async Task InternalWriteAllLinesAsync(TextWriter writer, IEnumerable<String> contents, CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: string
if (!string.IsNullOrEmpty(contents)) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
char[] buffer = new char[DefaultBufferSize]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArrayPool
? Same for the other allocations happening.
|
||
return cancellationToken.IsCancellationRequested | ||
? Task.FromCanceled(cancellationToken) | ||
: InternalWriteAllTextAsync(AsyncStreamWriter(path, true, encoding), contents, cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Named parameters? Same for all of the other places with true, false, null literals being passed directly.
da20360
to
662dc6e
Compare
Thanks @jamesqo. I'm now making use of shared buffers in those cases that don't return the array to the client. I'm using the length of the buffer obtained rather than the size asked for with reading but not for writing. With reading it's up to the stream implementation whether it uses the full size or smaller, but with writing it has to use all of it, so that is more likely to be sub-optimal. |
int read = await sr.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); | ||
if (read == 0) | ||
{ | ||
ArrayPool<char>.Shared.Return(buffer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you need to use a try/finally pattern with ArrayPool
so the buffer is returned in the case of an exception. That is the general usage from what I've seen, at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering about that. It's non-fateful but less than ideal for such a thing to happen. I think I'll replace the using
with a single try/finally
and then there's no further impact.
using (fs) | ||
{ | ||
int index = 0; | ||
byte[] bytes = new byte[count]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed that.
foreach (string line in contents) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
// Note that this working depends on the fix to #8662, and cannot be ported without |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#8662 seems to be a closed issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is. This is a note in case it's ported to desktop. If this is ported and the fix for #8662 isn't then this will have a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darn it. Not 8662, but #14563. 8662 was a related PR number in coreclr.
662dc6e
to
369cc91
Compare
|
||
namespace System.IO | ||
{ | ||
// Class for creating FileStream objects, and some basic file management | ||
// routines such as Delete, etc. | ||
public static class File | ||
{ | ||
private static Encoding _UTF8NoBOM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Static fields should be prefixed with s_
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed they should. Fixed.
2a49b66
to
241fc4e
Compare
File.Delete(path); | ||
} | ||
|
||
public Task AlreadyCanceledAsync() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You forgot a [Fact] on this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also forgot to cancel the token!
public async Task NullParametersAsync() | ||
{ | ||
string path = GetTestFilePath(); | ||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await WriteAsync(null, "Text")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does ThrowsAsync
allow assertion of parameterNames on Argument*Exceptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, thanks.
241fc4e
to
8afa432
Compare
@ianhays is this ready for another review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took another look and this LGTM. Thanks Jon!
Nice! Will this be part of .NET Core/Standard/Framework vNext? |
It will be in .NET Core 2.0. |
Thanks @JonHanna for your help here! |
Add async File.ReadAll*, File.AppendAll* and File.WriteAll Commit migrated from dotnet/corefx@2b4e1e4
Fixes #11220