diff options
Diffstat (limited to 'src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs')
-rw-r--r-- | src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs | 28 |
1 files changed, 26 insertions, 2 deletions
diff --git a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs index b899951ba7..76e6beb7a6 100644 --- a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs +++ b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs @@ -484,7 +484,22 @@ namespace System.IO try { - return new ValueTask<int>(Read(destination.Span)); + // ReadAsync(Memory<byte>,...) needs to delegate to an existing virtual to do the work, in case an existing derived type + // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to + // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate + // a Task<int> for the return value, so we want to delegate to one of the synchronous methods. We could always + // delegate to the Read(Span<byte>) method, and that's the most efficient solution when dealing with a concrete + // UnmanagedMemoryStream, but if we're dealing with a type derived from UnmanagedMemoryStream, Read(Span<byte>) will end up delegating + // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the + // very common case of the Memory<byte> wrapping an array: if it does, we delegate to Read(byte[], ...) with it, + // as that will be efficient in both cases, and we fall back to Read(Span<byte>) if the Memory<byte> wrapped something + // else; if this is a concrete UnmanagedMemoryStream, that'll be efficient, and only in the case where the Memory<byte> wrapped + // something other than an array and this is an UnmanagedMemoryStream-derived type that doesn't override Read(Span<byte>) will + // it then fall back to doing the ArrayPool/copy behavior. + return new ValueTask<int>( + destination.TryGetArray(out ArraySegment<byte> destinationArray) ? + Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) : + Read(destination.Span)); } catch (Exception ex) { @@ -764,7 +779,16 @@ namespace System.IO try { - Write(source.Span); + // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan<byte>). + // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency. + if (source.DangerousTryGetArray(out ArraySegment<byte> sourceArray)) + { + Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count); + } + else + { + Write(source.Span); + } return Task.CompletedTask; } catch (Exception ex) |