-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathExtensions.Stream.cs
54 lines (49 loc) · 1.47 KB
/
Extensions.Stream.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Open.Collections;
public static partial class Extensions
{
/// <summary>
/// Copies the source stream to the target.
/// </summary>
public static async ValueTask DualBufferCopyToAsync(
this Stream source,
Stream target,
int bufferSize = 4096,
bool clearBufferAfter = false,
CancellationToken cancellationToken = default)
{
if (source is null) throw new ArgumentNullException(nameof(source));
if (target is null) throw new ArgumentNullException(nameof(target));
ArrayPool<byte>? pool = ArrayPool<byte>.Shared;
byte[]? cNext = pool.Rent(bufferSize);
byte[]? cCurrent = pool.Rent(bufferSize);
try
{
Task<int>? next = source.ReadAsync(cNext, 0, bufferSize, cancellationToken);
while (true)
{
int n = await next.ConfigureAwait(false);
if (n == 0) break;
// Preemptive request before yielding.
Task<int> current = source.ReadAsync(cCurrent, 0, bufferSize, cancellationToken);
#if NETSTANDARD2_0
await target.WriteAsync(cNext, 0, n, cancellationToken).ConfigureAwait(false);
#else
await target.WriteAsync(cNext.AsMemory(0, n), cancellationToken).ConfigureAwait(false);
#endif
if (current is null) throw new OperationCanceledException();
(cCurrent, cNext) = (cNext, cCurrent);
next = current;
}
}
finally
{
pool.Return(cNext, clearBufferAfter);
pool.Return(cCurrent, clearBufferAfter);
}
}
}