Browse Source

Add optimized Read(Span) API

pull/1316/head
James Jackson-South 6 years ago
parent
commit
399cfc244f
  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.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
/// <inheritdoc/>
public override int Read(Span<byte> buffer) => this.ReadImpl(buffer);
#endif
private int ReadImpl(Span<byte> 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<byte> 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;

1
src/ImageSharp/Memory/MemoryOwnerExtensions.cs

@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
internal static class MemoryOwnerExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetSpan<T>(this IMemoryOwner<T> buffer)
=> 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<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, -1, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => ms2.Read(new byte[] { 1 }, 0, -1));
Assert.Throws<ArgumentException>(null, () => 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 }, 2, 0));
Assert.Throws<ArgumentException>(() => 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<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]
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);

Loading…
Cancel
Save