Browse Source

Add optimized Read(Span) API

pull/1574/head
James Jackson-South 6 years ago
parent
commit
94613d68b3
  1. 24
      src/ImageSharp/IO/ChunkedMemoryStream.cs
  2. 1
      src/ImageSharp/Memory/MemoryOwnerExtensions.cs
  3. 47
      tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs

24
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -236,11 +236,18 @@ namespace SixLabors.ImageSharp.IO
Guard.NotNull(buffer, nameof(buffer)); Guard.NotNull(buffer, nameof(buffer));
Guard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset)); Guard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset));
Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count));
if (buffer.Length - offset < count) Guard.IsFalse(buffer.Length - offset < count, nameof(buffer), $"{offset} subtracted from the buffer length is less than {count}");
{
throw new ArgumentException($"{offset} subtracted from the buffer length is less than {count}"); return this.ReadImpl(buffer.AsSpan().Slice(offset, count));
} }
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/>
public override int Read(Span<byte> buffer) => this.ReadImpl(buffer);
#endif
private int ReadImpl(Span<byte> buffer)
{
this.EnsureNotDisposed(); this.EnsureNotDisposed();
if (this.readChunk is null) if (this.readChunk is null)
@ -254,7 +261,7 @@ namespace SixLabors.ImageSharp.IO
this.readOffset = 0; this.readOffset = 0;
} }
byte[] chunkBuffer = this.readChunk.Buffer.Array; Span<byte> chunkBuffer = this.readChunk.Buffer.GetSpan();
int chunkSize = this.readChunk.Length; int chunkSize = this.readChunk.Length;
if (this.readChunk.Next is null) if (this.readChunk.Next is null)
{ {
@ -262,7 +269,8 @@ namespace SixLabors.ImageSharp.IO
} }
int bytesRead = 0; int bytesRead = 0;
int offset = 0;
int count = buffer.Length;
while (count > 0) while (count > 0)
{ {
if (this.readOffset == chunkSize) if (this.readOffset == chunkSize)
@ -275,7 +283,7 @@ namespace SixLabors.ImageSharp.IO
this.readChunk = this.readChunk.Next; this.readChunk = this.readChunk.Next;
this.readOffset = 0; this.readOffset = 0;
chunkBuffer = this.readChunk.Buffer.Array; chunkBuffer = this.readChunk.Buffer.GetSpan();
chunkSize = this.readChunk.Length; chunkSize = this.readChunk.Length;
if (this.readChunk.Next is null) if (this.readChunk.Next is null)
{ {
@ -284,7 +292,7 @@ namespace SixLabors.ImageSharp.IO
} }
int readCount = Math.Min(count, chunkSize - this.readOffset); 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; offset += readCount;
count -= readCount; count -= readCount;
this.readOffset += readCount; this.readOffset += readCount;

1
src/ImageSharp/Memory/MemoryOwnerExtensions.cs

@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
internal static class MemoryOwnerExtensions internal static class MemoryOwnerExtensions
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetSpan<T>(this IMemoryOwner<T> buffer) public static Span<T> GetSpan<T>(this IMemoryOwner<T> buffer)
=> buffer.Memory.Span; => buffer.Memory.Span;

47
tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs

@ -49,8 +49,8 @@ namespace SixLabors.ImageSharp.Tests.IO
Assert.Throws<ArgumentNullException>(() => ms2.Read(null, 0, 0)); Assert.Throws<ArgumentNullException>(() => ms2.Read(null, 0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, -1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, -1, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, 0, -1)); Assert.Throws<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, 0, -1));
Assert.Throws<ArgumentException>(null, () => ms2.Read(new byte[] { 1 }, 2, 0)); Assert.Throws<ArgumentException>(() => ms2.Read(new byte[] { 1 }, 2, 0));
Assert.Throws<ArgumentException>(null, () => ms2.Read(new byte[] { 1 }, 0, 2)); Assert.Throws<ArgumentException>(() => ms2.Read(new byte[] { 1 }, 0, 2));
ms2.Dispose(); ms2.Dispose();
@ -58,10 +58,11 @@ namespace SixLabors.ImageSharp.Tests.IO
} }
[Theory] [Theory]
[InlineData(1024)] [InlineData(ChunkedMemoryStream.DefaultBufferLength)]
[InlineData(1024 * 4)] [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 1.5))]
[InlineData(1024 * 6)] [InlineData(ChunkedMemoryStream.DefaultBufferLength * 4)]
[InlineData(1024 * 8)] [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 5.5))]
[InlineData(ChunkedMemoryStream.DefaultBufferLength * 8)]
public void MemoryStream_ReadByteTest(int length) public void MemoryStream_ReadByteTest(int length)
{ {
using MemoryStream ms = this.CreateTestStream(length); using MemoryStream ms = this.CreateTestStream(length);
@ -78,10 +79,11 @@ namespace SixLabors.ImageSharp.Tests.IO
} }
[Theory] [Theory]
[InlineData(1024)] [InlineData(ChunkedMemoryStream.DefaultBufferLength)]
[InlineData(1024 * 4)] [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 1.5))]
[InlineData(1024 * 6)] [InlineData(ChunkedMemoryStream.DefaultBufferLength * 4)]
[InlineData(1024 * 8)] [InlineData((int)(ChunkedMemoryStream.DefaultBufferLength * 5.5))]
[InlineData(ChunkedMemoryStream.DefaultBufferLength * 8)]
public void MemoryStream_ReadByteBufferTest(int length) public void MemoryStream_ReadByteBufferTest(int length)
{ {
using MemoryStream ms = this.CreateTestStream(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<byte> 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] [Fact]
public void MemoryStream_WriteToTests() public void MemoryStream_WriteToTests()
{ {
@ -107,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.IO
byte[] bytArrRet; byte[] bytArrRet;
byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 }; 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); ms2.Write(bytArr, 0, bytArr.Length);
using var readonlyStream = new ChunkedMemoryStream(this.allocator); using var readonlyStream = new ChunkedMemoryStream(this.allocator);

Loading…
Cancel
Save