diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs
index d3e6861d2..bd374a3ce 100644
--- a/src/ImageSharp/IO/ChunkedMemoryStream.cs
+++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs
@@ -236,11 +236,18 @@ namespace SixLabors.ImageSharp.IO
Guard.NotNull(buffer, nameof(buffer));
Guard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset));
Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count));
- if (buffer.Length - offset < count)
- {
- throw new ArgumentException($"{offset} subtracted from the buffer length is less than {count}");
- }
+ Guard.IsFalse(buffer.Length - offset < count, nameof(buffer), $"{offset} subtracted from the buffer length is less than {count}");
+
+ return this.ReadImpl(buffer.AsSpan().Slice(offset, count));
+ }
+
+#if SUPPORTS_SPAN_STREAM
+ ///
+ public override int Read(Span buffer) => this.ReadImpl(buffer);
+#endif
+ private int ReadImpl(Span buffer)
+ {
this.EnsureNotDisposed();
if (this.readChunk is null)
@@ -254,7 +261,7 @@ namespace SixLabors.ImageSharp.IO
this.readOffset = 0;
}
- byte[] chunkBuffer = this.readChunk.Buffer.Array;
+ Span chunkBuffer = this.readChunk.Buffer.GetSpan();
int chunkSize = this.readChunk.Length;
if (this.readChunk.Next is null)
{
@@ -262,7 +269,8 @@ namespace SixLabors.ImageSharp.IO
}
int bytesRead = 0;
-
+ int offset = 0;
+ int count = buffer.Length;
while (count > 0)
{
if (this.readOffset == chunkSize)
@@ -275,7 +283,7 @@ namespace SixLabors.ImageSharp.IO
this.readChunk = this.readChunk.Next;
this.readOffset = 0;
- chunkBuffer = this.readChunk.Buffer.Array;
+ chunkBuffer = this.readChunk.Buffer.GetSpan();
chunkSize = this.readChunk.Length;
if (this.readChunk.Next is null)
{
@@ -284,7 +292,7 @@ namespace SixLabors.ImageSharp.IO
}
int readCount = Math.Min(count, chunkSize - this.readOffset);
- Buffer.BlockCopy(chunkBuffer, this.readOffset, buffer, offset, readCount);
+ chunkBuffer.Slice(this.readOffset, count).CopyTo(buffer.Slice(offset));
offset += readCount;
count -= readCount;
this.readOffset += readCount;
diff --git a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
index 98fd40e65..aa475a80f 100644
--- a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
+++ b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
@@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Memory
///
internal static class MemoryOwnerExtensions
{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span GetSpan(this IMemoryOwner buffer)
=> buffer.Memory.Span;
diff --git a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
index 46afc7e50..28f1d336d 100644
--- a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
+++ b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
@@ -49,8 +49,8 @@ namespace SixLabors.ImageSharp.Tests.IO
Assert.Throws(() => ms2.Read(null, 0, 0));
Assert.Throws(() => ms2.Read(new byte[] { 1 }, -1, 0));
Assert.Throws(() => ms2.Read(new byte[] { 1 }, 0, -1));
- Assert.Throws(null, () => ms2.Read(new byte[] { 1 }, 2, 0));
- Assert.Throws(null, () => ms2.Read(new byte[] { 1 }, 0, 2));
+ Assert.Throws(() => ms2.Read(new byte[] { 1 }, 2, 0));
+ Assert.Throws(() => ms2.Read(new byte[] { 1 }, 0, 2));
ms2.Dispose();
@@ -58,10 +58,11 @@ namespace SixLabors.ImageSharp.Tests.IO
}
[Theory]
- [InlineData(1024)]
- [InlineData(1024 * 4)]
- [InlineData(1024 * 6)]
- [InlineData(1024 * 8)]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 1.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 4)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 5.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 8)]
public void MemoryStream_ReadByteTest(int length)
{
using MemoryStream ms = this.CreateTestStream(length);
@@ -78,10 +79,11 @@ namespace SixLabors.ImageSharp.Tests.IO
}
[Theory]
- [InlineData(1024)]
- [InlineData(1024 * 4)]
- [InlineData(1024 * 6)]
- [InlineData(1024 * 8)]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 1.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 4)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 5.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 8)]
public void MemoryStream_ReadByteBufferTest(int length)
{
using MemoryStream ms = this.CreateTestStream(length);
@@ -99,6 +101,29 @@ namespace SixLabors.ImageSharp.Tests.IO
}
}
+ [Theory]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 1.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 4)]
+ [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 5.5))]
+ [InlineData(ChunkedMemoryStream.DefaultBufferLength * 8)]
+ public void MemoryStream_ReadByteBufferSpanTest(int length)
+ {
+ using MemoryStream ms = this.CreateTestStream(length);
+ using var cms = new ChunkedMemoryStream(this.allocator);
+
+ ms.CopyTo(cms);
+ cms.Position = 0;
+ var expected = ms.ToArray();
+ Span buffer = new byte[2];
+ for (int i = 0; i < expected.Length; i += 2)
+ {
+ cms.Read(buffer);
+ Assert.Equal(expected[i], buffer[0]);
+ Assert.Equal(expected[i + 1], buffer[1]);
+ }
+ }
+
[Fact]
public void MemoryStream_WriteToTests()
{
@@ -107,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.IO
byte[] bytArrRet;
byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
- // [] Write to FileStream, check the filestream
+ // [] Write to memoryStream, check the memoryStream
ms2.Write(bytArr, 0, bytArr.Length);
using var readonlyStream = new ChunkedMemoryStream(this.allocator);