Skip to content

Commit 36b471a

Browse files
authored
Avoid allocating a closure in CopyToAsync (#205)
* Add SecurityRules to AssemblyInfo. * Avoid allocating a closure in CopyToAsyncImpl * Use GC.AllocateUninitializedArray when allocating pooled large buffers. * Fix merge
1 parent 103ea02 commit 36b471a

File tree

2 files changed

+35
-37
lines changed

2 files changed

+35
-37
lines changed

src/RecyclableMemoryStream.cs

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ namespace Microsoft.IO
2626
using System.Buffers;
2727
using System.Collections.Generic;
2828
using System.Diagnostics;
29-
using System.Globalization;
3029
using System.IO;
3130
using System.Runtime.CompilerServices;
3231
using System.Threading;
@@ -409,7 +408,7 @@ public override int Capacity
409408
this.EnsureCapacity(value);
410409
}
411410
}
412-
411+
413412
/// <summary>
414413
/// Returns a 64-bit version of capacity, for streams larger than <c>int.MaxValue</c> in length.
415414
/// </summary>
@@ -472,7 +471,7 @@ public override long Position
472471
{
473472
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} must be non-negative.");
474473
}
475-
474+
476475
if (this.largeBuffer != null && value > RecyclableMemoryStreamManager.MaxArrayLength)
477476
{
478477
throw new InvalidOperationException($"Once the stream is converted to a single large buffer, position cannot be set past {RecyclableMemoryStreamManager.MaxArrayLength}.");
@@ -590,23 +589,7 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
590589
}
591590
else
592591
{
593-
return CopyToAsyncImpl(cancellationToken, count);
594-
595-
async Task CopyToAsyncImpl(CancellationToken ct, long totalBytesToWrite)
596-
{
597-
var bytesRemaining = totalBytesToWrite;
598-
var blockAndOffset = this.GetBlockAndRelativeOffset(startPos);
599-
int currentBlock = blockAndOffset.Block;
600-
var currentOffset = blockAndOffset.Offset;
601-
while (bytesRemaining > 0)
602-
{
603-
int amountToCopy = (int)Math.Min(this.blocks[currentBlock].Length - currentOffset, bytesRemaining);
604-
await destination.WriteAsync(this.blocks[currentBlock], currentOffset, amountToCopy, ct);
605-
bytesRemaining -= amountToCopy;
606-
++currentBlock;
607-
currentOffset = 0;
608-
}
609-
}
592+
return CopyToAsyncImpl(destination, this.GetBlockAndRelativeOffset(startPos), count, this.blocks, cancellationToken);
610593
}
611594
}
612595
else
@@ -615,6 +598,21 @@ async Task CopyToAsyncImpl(CancellationToken ct, long totalBytesToWrite)
615598
return destination.WriteAsync(this.largeBuffer, (int)startPos, (int)count, cancellationToken);
616599
}
617600
}
601+
602+
static async Task CopyToAsyncImpl(Stream destination, BlockAndOffset blockAndOffset, long count, List<byte[]> blocks, CancellationToken cancellationToken)
603+
{
604+
var bytesRemaining = count;
605+
int currentBlock = blockAndOffset.Block;
606+
var currentOffset = blockAndOffset.Offset;
607+
while (bytesRemaining > 0)
608+
{
609+
int amountToCopy = (int)Math.Min(blocks[currentBlock].Length - currentOffset, bytesRemaining);
610+
await destination.WriteAsync(blocks[currentBlock], currentOffset, amountToCopy, cancellationToken);
611+
bytesRemaining -= amountToCopy;
612+
++currentBlock;
613+
currentOffset = 0;
614+
}
615+
}
618616
}
619617

620618
private byte[] bufferWriterTempBuffer;
@@ -756,7 +754,7 @@ public ReadOnlySequence<byte> GetReadOnlySequence()
756754

757755
var first = new BlockSegment(this.blocks[0]);
758756
var last = first;
759-
757+
760758
for (int blockIdx = 1; last.RunningIndex + last.Memory.Length < this.length; blockIdx++)
761759
{
762760
last = last.Append(this.blocks[blockIdx]);
@@ -1226,21 +1224,21 @@ public override void SetLength(long value)
12261224
public override long Seek(long offset, SeekOrigin loc)
12271225
{
12281226
this.CheckDisposed();
1229-
1227+
12301228
long newPosition;
12311229
switch (loc)
12321230
{
1233-
case SeekOrigin.Begin:
1234-
newPosition = offset;
1235-
break;
1236-
case SeekOrigin.Current:
1237-
newPosition = offset + this.position;
1238-
break;
1239-
case SeekOrigin.End:
1240-
newPosition = offset + this.length;
1241-
break;
1242-
default:
1243-
throw new ArgumentException("Invalid seek origin.", nameof(loc));
1231+
case SeekOrigin.Begin:
1232+
newPosition = offset;
1233+
break;
1234+
case SeekOrigin.Current:
1235+
newPosition = offset + this.position;
1236+
break;
1237+
case SeekOrigin.End:
1238+
newPosition = offset + this.length;
1239+
break;
1240+
default:
1241+
throw new ArgumentException("Invalid seek origin.", nameof(loc));
12441242
}
12451243
if (newPosition < 0)
12461244
{
@@ -1405,7 +1403,7 @@ public void WriteTo(byte[] buffer, long offset, long count, int targetOffset)
14051403
{
14061404
int amountToCopy = (int)Math.Min((long)this.blocks[currentBlock].Length - currentOffset, bytesRemaining);
14071405
Buffer.BlockCopy(this.blocks[currentBlock], currentOffset, buffer, currentTargetOffset, amountToCopy);
1408-
1406+
14091407
bytesRemaining -= amountToCopy;
14101408

14111409
++currentBlock;
@@ -1480,7 +1478,7 @@ private int InternalRead(Span<byte> buffer, long fromPosition)
14801478
{
14811479
return 0;
14821480
}
1483-
1481+
14841482
int amountToCopy;
14851483

14861484
if (this.largeBuffer == null)
@@ -1535,7 +1533,7 @@ private void EnsureCapacity(long newCapacity)
15351533
if (newCapacity > this.memoryManager.MaximumStreamCapacity && this.memoryManager.MaximumStreamCapacity > 0)
15361534
{
15371535
this.memoryManager.ReportStreamOverCapacity(this.id, this.tag, newCapacity, this.AllocationStack);
1538-
1536+
15391537
throw new OutOfMemoryException($"Requested capacity is too large: {newCapacity}. Limit is {this.memoryManager.MaximumStreamCapacity}.");
15401538
}
15411539

src/RecyclableMemoryStreamManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ internal byte[] GetLargeBuffer(long requiredSize, Guid id, string tag)
424424
{
425425
if (!this.largePools[poolIndex].TryPop(out buffer))
426426
{
427-
buffer = new byte[requiredSize];
427+
buffer = AllocateArray(requiredSize);
428428
createdNew = true;
429429
}
430430
else

0 commit comments

Comments
 (0)