Skip to content

Commit 73fed79

Browse files
Use pooled stream for async decode/identify
1 parent 73cc796 commit 73fed79

File tree

11 files changed

+70
-36
lines changed

11 files changed

+70
-36
lines changed

src/ImageSharp/Formats/Bmp/BmpDecoder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
8383
}
8484

8585
/// <inheritdoc/>
86-
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
86+
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
8787
{
8888
Guard.NotNull(stream, nameof(stream));
8989

90-
return await new BmpDecoderCore(configuration, this).IdentifyAsync(stream).ConfigureAwait(false);
90+
return new BmpDecoderCore(configuration, this).IdentifyAsync(stream);
9191
}
9292
}
9393
}

src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Runtime.CompilerServices;
1010
using System.Threading.Tasks;
1111
using SixLabors.ImageSharp.Common.Helpers;
12+
using SixLabors.ImageSharp.IO;
1213
using SixLabors.ImageSharp.Memory;
1314
using SixLabors.ImageSharp.Metadata;
1415
using SixLabors.ImageSharp.PixelFormats;
@@ -257,7 +258,7 @@ public IImageInfo Identify(Stream stream)
257258
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
258259
public async Task<IImageInfo> IdentifyAsync(Stream stream)
259260
{
260-
using (var ms = new MemoryStream())
261+
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
261262
{
262263
await stream.CopyToAsync(ms).ConfigureAwait(false);
263264
ms.Position = 0;

src/ImageSharp/Formats/Gif/GifDecoder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
7777
}
7878

7979
/// <inheritdoc/>
80-
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
80+
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
8181
{
8282
Guard.NotNull(stream, nameof(stream));
8383

8484
var decoder = new GifDecoderCore(configuration, this);
85-
return await decoder.IdentifyAsync(stream).ConfigureAwait(false);
85+
return decoder.IdentifyAsync(stream);
8686
}
8787

8888
/// <inheritdoc />

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Runtime.InteropServices;
88
using System.Text;
99
using System.Threading.Tasks;
10+
using SixLabors.ImageSharp.IO;
1011
using SixLabors.ImageSharp.Memory;
1112
using SixLabors.ImageSharp.Metadata;
1213
using SixLabors.ImageSharp.PixelFormats;
@@ -199,7 +200,7 @@ public async Task<IImageInfo> IdentifyAsync(Stream stream)
199200
}
200201
else
201202
{
202-
using (var ms = new MemoryStream())
203+
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
203204
{
204205
await stream.CopyToAsync(ms).ConfigureAwait(false);
205206
ms.Position = 0;

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ public async Task<IImageInfo> IdentifyAsync(Stream stream)
266266
}
267267
else
268268
{
269-
using (var ms = new MemoryStream())
269+
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
270270
{
271271
await stream.CopyToAsync(ms).ConfigureAwait(false);
272272
ms.Position = 0;

src/ImageSharp/Formats/Png/PngDecoder.cs

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,8 @@
99
namespace SixLabors.ImageSharp.Formats.Png
1010
{
1111
/// <summary>
12-
/// Encoder for generating an image out of a png encoded stream.
12+
/// Decoder for generating an image out of a png encoded stream.
1313
/// </summary>
14-
/// <remarks>
15-
/// At the moment the following features are supported:
16-
/// <para>
17-
/// <b>Filters:</b> all filters are supported.
18-
/// </para>
19-
/// <para>
20-
/// <b>Pixel formats:</b>
21-
/// <list type="bullet">
22-
/// <item>RGBA (True color) with alpha (8 bit).</item>
23-
/// <item>RGB (True color) without alpha (8 bit).</item>
24-
/// <item>grayscale with alpha (8 bit).</item>
25-
/// <item>grayscale without alpha (8 bit).</item>
26-
/// <item>Palette Index with alpha (8 bit).</item>
27-
/// <item>Palette Index without alpha (8 bit).</item>
28-
/// </list>
29-
/// </para>
30-
/// </remarks>
3114
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
3215
{
3316
/// <summary>
@@ -97,10 +80,10 @@ public IImageInfo Identify(Configuration configuration, Stream stream)
9780
}
9881

9982
/// <inheritdoc/>
100-
public async Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
83+
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream)
10184
{
10285
var decoder = new PngDecoderCore(configuration, this);
103-
return await decoder.IdentifyAsync(stream).ConfigureAwait(false);
86+
return decoder.IdentifyAsync(stream);
10487
}
10588

10689
/// <inheritdoc />

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using SixLabors.ImageSharp.Formats.Png.Chunks;
1414
using SixLabors.ImageSharp.Formats.Png.Filters;
1515
using SixLabors.ImageSharp.Formats.Png.Zlib;
16+
using SixLabors.ImageSharp.IO;
1617
using SixLabors.ImageSharp.Memory;
1718
using SixLabors.ImageSharp.Metadata;
1819
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -277,7 +278,7 @@ public async Task<IImageInfo> IdentifyAsync(Stream stream)
277278
}
278279
else
279280
{
280-
using (var ms = new MemoryStream())
281+
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
281282
{
282283
await stream.CopyToAsync(ms).ConfigureAwait(false);
283284
ms.Position = 0;

src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Runtime.CompilerServices;
88
using System.Threading.Tasks;
9+
using SixLabors.ImageSharp.IO;
910
using SixLabors.ImageSharp.Memory;
1011
using SixLabors.ImageSharp.Metadata;
1112
using SixLabors.ImageSharp.PixelFormats;
@@ -689,7 +690,7 @@ public async Task<IImageInfo> IdentifyAsync(Stream stream)
689690
}
690691
else
691692
{
692-
using (var ms = new MemoryStream())
693+
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
693694
{
694695
await stream.CopyToAsync(ms).ConfigureAwait(false);
695696
ms.Position = 0;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System.Buffers;
5+
using System.IO;
6+
7+
namespace SixLabors.ImageSharp.IO
8+
{
9+
/// <summary>
10+
/// A memory stream constructed from a pooled buffer of known length.
11+
/// </summary>
12+
internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
13+
{
14+
private readonly byte[] buffer;
15+
private bool isDisposed;
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="FixedCapacityPooledMemoryStream"/> class.
19+
/// </summary>
20+
/// <param name="length">The length of the stream buffer to rent.</param>
21+
public FixedCapacityPooledMemoryStream(long length)
22+
: this(RentBuffer(length)) => this.Length = length;
23+
24+
private FixedCapacityPooledMemoryStream(byte[] buffer)
25+
: base(buffer) => this.buffer = buffer;
26+
27+
/// <inheritdoc/>
28+
public override long Length { get; }
29+
30+
/// <inheritdoc/>
31+
protected override void Dispose(bool disposing)
32+
{
33+
if (!this.isDisposed)
34+
{
35+
this.isDisposed = true;
36+
37+
if (disposing)
38+
{
39+
ArrayPool<byte>.Shared.Return(this.buffer);
40+
}
41+
42+
base.Dispose(disposing);
43+
}
44+
}
45+
46+
// In the extrememly unlikely event someone ever gives us a stream
47+
// with length longer than int.MaxValue then we'll use something else.
48+
private static byte[] RentBuffer(long length) => ArrayPool<byte>.Shared.Rent((int)length);
49+
}
50+
}

src/ImageSharp/Image.FromStream.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text;
88
using System.Threading.Tasks;
99
using SixLabors.ImageSharp.Formats;
10+
using SixLabors.ImageSharp.IO;
1011
using SixLabors.ImageSharp.PixelFormats;
1112

1213
namespace SixLabors.ImageSharp
@@ -674,10 +675,7 @@ private static T WithSeekableStream<T>(Configuration configuration, Stream strea
674675
}
675676

676677
// We want to be able to load images from things like HttpContext.Request.Body
677-
// TODO: Should really find a nice way to use a pool for these.
678-
// Investigate readonly version of the linked implementation.
679-
// https://github.com/mgravell/Pipelines.Sockets.Unofficial/compare/mgravell:24482d4...mgravell:6740ea4
680-
using (var memoryStream = new MemoryStream())
678+
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
681679
{
682680
stream.CopyTo(memoryStream);
683681
memoryStream.Position = 0;
@@ -713,8 +711,7 @@ private static async Task<T> WithSeekableStreamAsync<T>(
713711
return await action(stream).ConfigureAwait(false);
714712
}
715713

716-
// TODO: See above comment.
717-
using (var memoryStream = new MemoryStream())
714+
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length))
718715
{
719716
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
720717
memoryStream.Position = 0;

0 commit comments

Comments
 (0)