Browse Source

Merge pull request #1223 from SixLabors/af/FixedCapacityPooledMemoryStream-use-allocator

Use MemoryAllocator in FixedCapacityPooledMemoryStream
pull/1226/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
bbf323374b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 89
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 81
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  3. 38
      src/ImageSharp/Formats/IImageDecoderInternals.cs
  4. 55
      src/ImageSharp/Formats/ImageDecoderUtilities.cs
  5. 85
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  6. 95
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  7. 90
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  8. 20
      src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs
  9. 5
      src/ImageSharp/Image.FromStream.cs
  10. 5
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

89
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <remarks> /// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/> /// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks> /// </remarks>
internal sealed class BmpDecoderCore internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps. /// The default mask for the red part of the color for 16 bit rgb bitmaps.
@ -89,11 +89,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
private BmpInfoHeader infoHeader; private BmpInfoHeader infoHeader;
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.
/// </summary> /// </summary>
@ -111,67 +106,28 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{ {
this.configuration = configuration; this.Configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = configuration.MemoryAllocator;
this.options = options; this.options = options;
} }
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the dimensions of the image. /// Gets the dimensions of the image.
/// </summary> /// </summary>
public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height); public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height);
/// <summary> /// <inheritdoc />
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
// if we can seek then we arn't in a context that errors on async operations
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
// cheat for now do async copy of the stream into memory stream and use the sync version
// we should use an array pool backed memorystream implementation
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
try try
{ {
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); var image = new Image<TPixel>(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
@ -242,30 +198,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
} }
/// <summary> /// <inheritdoc />
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
this.ReadImageHeaders(stream, out _, out _); this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata);
} }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
/// <summary> /// <summary>
/// Returns the y- value based on the given height. /// Returns the y- value based on the given height.
/// </summary> /// </summary>
@ -999,7 +938,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY); Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgr24Bytes( PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.configuration, this.Configuration,
row.GetSpan(), row.GetSpan(),
pixelSpan, pixelSpan,
width); width);
@ -1028,7 +967,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY); Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes( PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration, this.Configuration,
row.GetSpan(), row.GetSpan(),
pixelSpan, pixelSpan,
width); width);
@ -1065,7 +1004,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row); this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes( PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration, this.Configuration,
row.GetSpan(), row.GetSpan(),
bgraRowSpan, bgraRowSpan,
width); width);
@ -1101,7 +1040,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY); Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes( PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration, this.Configuration,
row.GetSpan(), row.GetSpan(),
pixelSpan, pixelSpan,
width); width);
@ -1115,7 +1054,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
this.stream.Read(row); this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes( PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration, this.Configuration,
row.GetSpan(), row.GetSpan(),
bgraRowSpan, bgraRowSpan,
width); width);

81
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -17,18 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
internal sealed class GifDecoderCore internal sealed class GifDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// The temp buffer used to reduce allocations.
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[16]; private readonly byte[] buffer = new byte[16];
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary> /// <summary>
/// The currently loaded stream. /// The currently loaded stream.
/// </summary> /// </summary>
@ -78,9 +73,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
this.IgnoreMetadata = options.IgnoreMetadata; this.IgnoreMetadata = options.IgnoreMetadata;
this.DecodingMode = options.DecodingMode; this.DecodingMode = options.DecodingMode;
this.configuration = configuration ?? Configuration.Default; this.Configuration = configuration ?? Configuration.Default;
} }
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary> /// </summary>
@ -96,40 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
public Size Dimensions => new Size(this.imageDescriptor.Width, this.imageDescriptor.Height); public Size Dimensions => new Size(this.imageDescriptor.Width, this.imageDescriptor.Height);
private MemoryAllocator MemoryAllocator => this.configuration.MemoryAllocator; private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <returns>The decoded image</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
/// <summary> /// <inheritdoc />
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <returns>The decoded image</returns>
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Image<TPixel> image = null; Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null; ImageFrame<TPixel> previousFrame = null;
@ -188,31 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
return image; return image;
} }
/// <summary> /// <inheritdoc />
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
if (stream.CanSeek)
{
return this.Identify(stream);
}
else
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
try try
@ -410,11 +355,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.imageDescriptor.LocalColorTableFlag) if (this.imageDescriptor.LocalColorTableFlag)
{ {
int length = this.imageDescriptor.LocalColorTableSize * 3; int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); localColorTable = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
this.stream.Read(localColorTable.Array, 0, length); this.stream.Read(localColorTable.Array, 0, length);
} }
indices = this.configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); indices = this.Configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
this.ReadFrameIndices(indices); this.ReadFrameIndices(indices);
ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan()); ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan());
@ -438,7 +383,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameIndices(Buffer2D<byte> indices) private void ReadFrameIndices(Buffer2D<byte> indices)
{ {
int dataSize = this.stream.ReadByte(); int dataSize = this.stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream);
lzwDecoder.DecodePixels(dataSize, indices); lzwDecoder.DecodePixels(dataSize, indices);
} }
@ -464,7 +409,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame is null) if (previousFrame is null)
{ {
// This initializes the image to become fully transparent because the alpha channel is zero. // This initializes the image to become fully transparent because the alpha channel is zero.
image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metadata); image = new Image<TPixel>(this.Configuration, imageWidth, imageHeight, this.metadata);
this.SetFrameMetadata(image.Frames.RootFrame.Metadata); this.SetFrameMetadata(image.Frames.RootFrame.Metadata);

38
src/ImageSharp/Formats/IImageDecoderInternals.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Abstraction for shared internals for ***DecoderCore implementations to be used with <see cref="ImageDecoderUtilities"/>.
/// </summary>
internal interface IImageDecoderInternals
{
/// <summary>
/// Gets the associated configuration.
/// </summary>
Configuration Configuration { get; }
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>;
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="IImageInfo"/>.</returns>
IImageInfo Identify(Stream stream);
}
}

55
src/ImageSharp/Formats/ImageDecoderUtilities.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
{
internal static class ImageDecoderUtilities
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public static async Task<IImageInfo> IdentifyAsync(this IImageDecoderInternals decoder, Stream stream)
{
if (stream.CanSeek)
{
return decoder.Identify(stream);
}
using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return decoder.Identify(ms);
}
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="decoder">The decoder.</param>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public static async Task<Image<TPixel>> DecodeAsync<TPixel>(this IImageDecoderInternals decoder, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return decoder.Decode<TPixel>(stream);
}
using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length);
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return decoder.Decode<TPixel>(ms);
}
}
}

85
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -25,18 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/> /// Originally ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/>
/// with additional fixes for both performance and common encoding errors. /// with additional fixes for both performance and common encoding errors.
/// </summary> /// </summary>
internal sealed class JpegDecoderCore : IRawJpegData internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// The only supported precision /// The only supported precision
/// </summary> /// </summary>
private readonly int[] supportedPrecisions = { 8, 12 }; private readonly int[] supportedPrecisions = { 8, 12 };
/// <summary>
/// The global configuration
/// </summary>
private readonly Configuration configuration;
/// <summary> /// <summary>
/// The buffer used to temporarily store bytes read from the stream. /// The buffer used to temporarily store bytes read from the stream.
/// </summary> /// </summary>
@ -109,10 +104,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
{ {
this.configuration = configuration ?? Configuration.Default; this.Configuration = configuration ?? Configuration.Default;
this.IgnoreMetadata = options.IgnoreMetadata; this.IgnoreMetadata = options.IgnoreMetadata;
} }
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the frame /// Gets the frame
/// </summary> /// </summary>
@ -213,38 +211,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return new JpegFileMarker(marker[1], stream.Position - 2, true); return new JpegFileMarker(marker[1], stream.Position - 2, true);
} }
/// <summary> /// <inheritdoc/>
/// Decodes the image from the specified <see cref="Stream"/> and sets the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be.</param>
/// <returns>The decoded image.</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
/// <summary>
/// Decodes the image from the specified <see cref="Stream"/> and sets the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be.</param>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
this.ParseStream(stream); this.ParseStream(stream);
this.InitExifProfile(); this.InitExifProfile();
@ -254,31 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return this.PostProcessIntoImage<TPixel>(); return this.PostProcessIntoImage<TPixel>();
} }
/// <summary> /// <inheritdoc/>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
if (stream.CanSeek)
{
return this.Identify(stream);
}
else
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
this.ParseStream(stream, true); this.ParseStream(stream, true);
@ -298,7 +243,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public void ParseStream(Stream stream, bool metadataOnly = false) public void ParseStream(Stream stream, bool metadataOnly = false)
{ {
this.Metadata = new ImageMetadata(); this.Metadata = new ImageMetadata();
this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream); this.InputStream = new DoubleBufferedStreamReader(this.Configuration.MemoryAllocator, stream);
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
this.InputStream.Read(this.markerBuffer, 0, 2); this.InputStream.Read(this.markerBuffer, 0, 2);
@ -937,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
maxV = v; maxV = v;
} }
var component = new JpegComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component; this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id; this.Frame.ComponentIds[i] = component.Id;
@ -962,7 +907,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
int length = remaining; int length = remaining;
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) using (IManagedByteBuffer huffmanData = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{ {
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan()); ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
for (int i = 2; i < remaining;) for (int i = 2; i < remaining;)
@ -985,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(huffmanData.Array, 0, 16); this.InputStream.Read(huffmanData.Array, 0, 16);
using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean)) using (IManagedByteBuffer codeLengths = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean))
{ {
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.GetSpan()); ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.GetSpan());
int codeLengthSum = 0; int codeLengthSum = 0;
@ -1002,7 +947,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
} }
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) using (IManagedByteBuffer huffmanValues = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{ {
this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
@ -1129,12 +1074,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
var image = Image.CreateUninitialized<TPixel>( var image = Image.CreateUninitialized<TPixel>(
this.configuration, this.Configuration,
this.ImageWidth, this.ImageWidth,
this.ImageHeight, this.ImageHeight,
this.Metadata); this.Metadata);
using (var postProcessor = new JpegImagePostProcessor(this.configuration, this)) using (var postProcessor = new JpegImagePostProcessor(this.Configuration, this))
{ {
postProcessor.PostProcess(image.Frames.RootFrame); postProcessor.PostProcess(image.Frames.RootFrame);
} }

95
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -24,18 +24,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Performs the png decoding operation. /// Performs the png decoding operation.
/// </summary> /// </summary>
internal sealed class PngDecoderCore internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// Reusable buffer. /// Reusable buffer.
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[4]; private readonly byte[] buffer = new byte[4];
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary> /// </summary>
@ -123,60 +118,22 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) public PngDecoderCore(Configuration configuration, IPngDecoderOptions options)
{ {
this.configuration = configuration ?? Configuration.Default; this.Configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.configuration.MemoryAllocator; this.memoryAllocator = this.Configuration.MemoryAllocator;
this.ignoreMetadata = options.IgnoreMetadata; this.ignoreMetadata = options.IgnoreMetadata;
} }
/// <inheritdoc/>
public Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the dimensions of the image. /// Gets the dimensions of the image.
/// </summary> /// </summary>
public Size Dimensions => new Size(this.header.Width, this.header.Height); public Size Dimensions => new Size(this.header.Width, this.header.Height);
/// <summary> /// <inheritdoc/>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
/// <returns>The decoded image.</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var metadata = new ImageMetadata(); var metadata = new ImageMetadata();
PngMetadata pngMetadata = metadata.GetPngMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata();
@ -266,31 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
} }
/// <summary> /// <inheritdoc/>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
if (stream.CanSeek)
{
return this.Identify(stream);
}
else
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
var metadata = new ImageMetadata(); var metadata = new ImageMetadata();
@ -446,7 +379,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
image = Image.CreateUninitialized<TPixel>( image = Image.CreateUninitialized<TPixel>(
this.configuration, this.Configuration,
this.header.Width, this.header.Width,
this.header.Height, this.header.Height,
metadata); metadata);
@ -460,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); this.scanline = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean);
} }
/// <summary> /// <summary>
@ -759,7 +692,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb: case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline( PngScanlineProcessor.ProcessRgbScanline(
this.configuration, this.Configuration,
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
@ -773,7 +706,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline( PngScanlineProcessor.ProcessRgbaScanline(
this.configuration, this.Configuration,
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
@ -1258,7 +1191,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private IManagedByteBuffer ReadChunkData(int length) private IManagedByteBuffer ReadChunkData(int length)
{ {
// We rent the buffer here to return it afterwards in Decode() // We rent the buffer here to return it afterwards in Decode()
IManagedByteBuffer buffer = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); IManagedByteBuffer buffer = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
this.currentStream.Read(buffer.Array, 0, length); this.currentStream.Read(buffer.Array, 0, length);

90
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary> /// <summary>
/// Performs the tga decoding operation. /// Performs the tga decoding operation.
/// </summary> /// </summary>
internal sealed class TgaDecoderCore internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// A scratch buffer to reduce allocations. /// A scratch buffer to reduce allocations.
@ -38,11 +37,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary> /// </summary>
private TgaFileHeader fileHeader; private TgaFileHeader fileHeader;
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.
/// </summary> /// </summary>
@ -70,54 +64,22 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options)
{ {
this.configuration = configuration; this.Configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = configuration.MemoryAllocator;
this.options = options; this.options = options;
} }
/// <inheritdoc />
public Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the dimensions of the image. /// Gets the dimensions of the image.
/// </summary> /// </summary>
public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height); public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height);
/// <summary> /// <inheritdoc />
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
if (stream.CanSeek)
{
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
/// <summary>
/// Decodes the image from the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
try try
{ {
@ -135,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
throw new UnknownImageFormatException("Width or height cannot be 0"); throw new UnknownImageFormatException("Width or height cannot be 0");
} }
var image = Image.CreateUninitialized<TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); var image = Image.CreateUninitialized<TPixel>(this.Configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1) if (this.fileHeader.ColorMapType == 1)
@ -489,11 +451,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
{ {
PixelOperations<TPixel>.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width); PixelOperations<TPixel>.Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width);
} }
else else
{ {
PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width);
} }
} }
} }
@ -678,31 +640,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
} }
} }
/// <summary> /// <inheritdoc />
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
if (stream.CanSeek)
{
return this.Identify(stream);
}
else
{
using (var ms = new FixedCapacityPooledMemoryStream(stream.Length))
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
this.ReadFileHeader(stream); this.ReadFileHeader(stream);
@ -719,7 +657,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{ {
this.currentStream.Read(row); this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); PixelOperations<TPixel>.Instance.FromL8Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -746,7 +684,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{ {
this.currentStream.Read(row); this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -765,7 +703,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{ {
this.currentStream.Read(row); this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.Configuration, row.GetSpan(), pixelSpan, width);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

20
src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs

@ -3,6 +3,7 @@
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.IO namespace SixLabors.ImageSharp.IO
{ {
@ -11,18 +12,19 @@ namespace SixLabors.ImageSharp.IO
/// </summary> /// </summary>
internal sealed class FixedCapacityPooledMemoryStream : MemoryStream internal sealed class FixedCapacityPooledMemoryStream : MemoryStream
{ {
private readonly byte[] buffer; private readonly IManagedByteBuffer buffer;
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityPooledMemoryStream"/> class. /// Initializes a new instance of the <see cref="FixedCapacityPooledMemoryStream"/> class.
/// </summary> /// </summary>
/// <param name="length">The length of the stream buffer to rent.</param> /// <param name="length">The length of the stream buffer to rent.</param>
public FixedCapacityPooledMemoryStream(long length) /// <param name="allocator">The allocator to rent the buffer from.</param>
: this(RentBuffer(length)) => this.Length = length; public FixedCapacityPooledMemoryStream(long length, MemoryAllocator allocator)
: this(RentBuffer(length, allocator)) => this.Length = length;
private FixedCapacityPooledMemoryStream(byte[] buffer) private FixedCapacityPooledMemoryStream(IManagedByteBuffer buffer)
: base(buffer) => this.buffer = buffer; : base(buffer.Array) => this.buffer = buffer;
/// <inheritdoc/> /// <inheritdoc/>
public override long Length { get; } public override long Length { get; }
@ -36,7 +38,7 @@ namespace SixLabors.ImageSharp.IO
if (disposing) if (disposing)
{ {
ArrayPool<byte>.Shared.Return(this.buffer); this.buffer.Dispose();
} }
base.Dispose(disposing); base.Dispose(disposing);
@ -45,6 +47,10 @@ namespace SixLabors.ImageSharp.IO
// In the extrememly unlikely event someone ever gives us a stream // In the extrememly unlikely event someone ever gives us a stream
// with length longer than int.MaxValue then we'll use something else. // with length longer than int.MaxValue then we'll use something else.
private static byte[] RentBuffer(long length) => ArrayPool<byte>.Shared.Rent((int)length); private static IManagedByteBuffer RentBuffer(long length, MemoryAllocator allocator)
{
Guard.MustBeBetweenOrEqualTo(length, 0, int.MaxValue, nameof(length));
return allocator.AllocateManagedByteBuffer((int)length);
}
} }
} }

5
src/ImageSharp/Image.FromStream.cs

@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -675,7 +676,7 @@ namespace SixLabors.ImageSharp
} }
// We want to be able to load images from things like HttpContext.Request.Body // We want to be able to load images from things like HttpContext.Request.Body
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length)) using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
{ {
stream.CopyTo(memoryStream); stream.CopyTo(memoryStream);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -711,7 +712,7 @@ namespace SixLabors.ImageSharp
return await action(stream).ConfigureAwait(false); return await action(stream).ConfigureAwait(false);
} }
using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length)) using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length))
{ {
await stream.CopyToAsync(memoryStream).ConfigureAwait(false); await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;

5
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers; using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.Memory namespace SixLabors.ImageSharp.Memory
{ {
@ -98,5 +100,8 @@ namespace SixLabors.ImageSharp.Memory
AllocationOptions options = AllocationOptions.None) AllocationOptions options = AllocationOptions.None)
where T : struct where T : struct
=> MemoryGroup<T>.Allocate(memoryAllocator, totalLength, bufferAlignment, options); => MemoryGroup<T>.Allocate(memoryAllocator, totalLength, bufferAlignment, options);
internal static MemoryStream AllocateFixedCapacityMemoryStream(this MemoryAllocator allocator, long length) =>
new FixedCapacityPooledMemoryStream(length, allocator);
} }
} }

Loading…
Cancel
Save