diff --git a/src/ImageSharp/IO/BufferedReadStream.cs b/src/ImageSharp/IO/BufferedReadStream.cs
index e5fe6f807..0f6e9da1e 100644
--- a/src/ImageSharp/IO/BufferedReadStream.cs
+++ b/src/ImageSharp/IO/BufferedReadStream.cs
@@ -66,20 +66,16 @@ namespace SixLabors.ImageSharp.IO
this.readBufferIndex = BufferLength;
}
- ///
- /// Gets the length, in bytes, of the stream.
- ///
+ ///
public override long Length { get; }
- ///
- /// Gets or sets the current position within the stream.
- ///
+ ///
public override long Position
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.readerPosition;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(MethodImplOptions.NoInlining)]
set
{
// Only reset readBufferIndex if we are out of bounds of our working buffer
@@ -185,12 +181,16 @@ namespace SixLabors.ImageSharp.IO
}
///
- /// This operation is not supported in .
+ ///
+ /// This operation is not supported in .
+ ///
public override void SetLength(long value)
=> throw new NotSupportedException();
///
- /// This operation is not supported in .
+ ///
+ /// This operation is not supported in .
+ ///
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
@@ -239,7 +239,7 @@ namespace SixLabors.ImageSharp.IO
return n;
}
- [MethodImpl(MethodImplOptions.NoInlining)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count)
{
// Refill our buffer then copy.
diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs
deleted file mode 100644
index 0345717d2..000000000
--- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Buffers;
-using System.IO;
-using System.Runtime.CompilerServices;
-
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.IO
-{
- ///
- /// A stream reader that add a secondary level buffer in addition to native stream buffered reading
- /// to reduce the overhead of small incremental reads.
- ///
- internal sealed unsafe class DoubleBufferedStreamReader : IDisposable
- {
- ///
- /// The length, in bytes, of the buffering chunk.
- ///
- public const int ChunkLength = 8192;
-
- private const int MaxChunkIndex = ChunkLength - 1;
-
- private readonly Stream stream;
-
- private readonly IManagedByteBuffer managedBuffer;
-
- private MemoryHandle handle;
-
- private readonly byte* pinnedChunk;
-
- private readonly byte[] bufferChunk;
-
- private readonly int length;
-
- private int chunkIndex;
-
- private int position;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The to use for buffer allocations.
- /// The input stream.
- public DoubleBufferedStreamReader(MemoryAllocator memoryAllocator, Stream stream)
- {
- this.stream = stream;
- this.Position = (int)stream.Position;
- this.length = (int)stream.Length;
- this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(ChunkLength);
- this.bufferChunk = this.managedBuffer.Array;
- this.handle = this.managedBuffer.Memory.Pin();
- this.pinnedChunk = (byte*)this.handle.Pointer;
- this.chunkIndex = ChunkLength;
- }
-
- ///
- /// Gets the length, in bytes, of the stream.
- ///
- public long Length => this.length;
-
- ///
- /// Gets or sets the current position within the stream.
- ///
- public long Position
- {
- get => this.position;
-
- set
- {
- // Only reset chunkIndex if we are out of bounds of our working chunk
- // otherwise we should simply move the value by the diff.
- int v = (int)value;
- if (this.IsInChunk(v, out int index))
- {
- this.chunkIndex = index;
- this.position = v;
- }
- else
- {
- this.position = v;
- this.stream.Seek(value, SeekOrigin.Begin);
- this.chunkIndex = ChunkLength;
- }
- }
- }
-
- ///
- /// Reads a byte from the stream and advances the position within the stream by one
- /// byte, or returns -1 if at the end of the stream.
- ///
- /// The unsigned byte cast to an , or -1 if at the end of the stream.
- [MethodImpl(InliningOptions.ShortMethod)]
- public int ReadByte()
- {
- if (this.position >= this.length)
- {
- return -1;
- }
-
- if (this.chunkIndex > MaxChunkIndex)
- {
- this.FillChunk();
- }
-
- this.position++;
- return this.pinnedChunk[this.chunkIndex++];
- }
-
- ///
- /// Skips the number of bytes in the stream
- ///
- /// The number of bytes to skip.
- [MethodImpl(InliningOptions.ShortMethod)]
- public void Skip(int count) => this.Position += count;
-
- ///
- /// Reads a sequence of bytes from the current stream and advances the position within the stream
- /// by the number of bytes read.
- ///
- ///
- /// An array of bytes. When this method returns, the buffer contains the specified
- /// byte array with the values between offset and (offset + count - 1) replaced by
- /// the bytes read from the current source.
- ///
- ///
- /// The zero-based byte offset in buffer at which to begin storing the data read
- /// from the current stream.
- ///
- /// The maximum number of bytes to be read from the current stream.
- ///
- /// The total number of bytes read into the buffer. This can be less than the number
- /// of bytes requested if that many bytes are not currently available, or zero (0)
- /// if the end of the stream has been reached.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public int Read(byte[] buffer, int offset, int count)
- {
- if (count > ChunkLength)
- {
- return this.ReadToBufferSlow(buffer, offset, count);
- }
-
- if (count + this.chunkIndex > ChunkLength)
- {
- return this.ReadToChunkSlow(buffer, offset, count);
- }
-
- int n = this.GetCopyCount(count);
- this.CopyBytes(buffer, offset, n);
-
- this.position += n;
- this.chunkIndex += n;
- return n;
- }
-
- ///
- public void Dispose()
- {
- this.handle.Dispose();
- this.managedBuffer?.Dispose();
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private int GetPositionDifference(int p) => p - this.position;
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private bool IsInChunk(int p, out int index)
- {
- index = this.GetPositionDifference(p) + this.chunkIndex;
- return index > -1 && index < ChunkLength;
- }
-
- [MethodImpl(InliningOptions.ColdPath)]
- private void FillChunk()
- {
- if (this.position != this.stream.Position)
- {
- this.stream.Seek(this.position, SeekOrigin.Begin);
- }
-
- this.stream.Read(this.bufferChunk, 0, ChunkLength);
- this.chunkIndex = 0;
- }
-
- [MethodImpl(InliningOptions.ColdPath)]
- private int ReadToChunkSlow(byte[] buffer, int offset, int count)
- {
- // Refill our buffer then copy.
- this.FillChunk();
-
- int n = this.GetCopyCount(count);
- this.CopyBytes(buffer, offset, n);
-
- this.position += n;
- this.chunkIndex += n;
-
- return n;
- }
-
- [MethodImpl(InliningOptions.ColdPath)]
- private int ReadToBufferSlow(byte[] buffer, int offset, int count)
- {
- // Read to target but don't copy to our chunk.
- if (this.position != this.stream.Position)
- {
- this.stream.Seek(this.position, SeekOrigin.Begin);
- }
-
- int n = this.stream.Read(buffer, offset, count);
- this.Position += n;
-
- return n;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private int GetCopyCount(int count)
- {
- int n = this.length - this.position;
- if (n > count)
- {
- n = count;
- }
-
- if (n < 0)
- {
- n = 0;
- }
-
- return n;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private void CopyBytes(byte[] buffer, int offset, int count)
- {
- if (count < 9)
- {
- int byteCount = count;
- int read = this.chunkIndex;
- byte* pinned = this.pinnedChunk;
-
- while (--byteCount > -1)
- {
- buffer[offset + byteCount] = pinned[read + byteCount];
- }
- }
- else
- {
- Buffer.BlockCopy(this.bufferChunk, this.chunkIndex, buffer, offset, count);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
index 1696623ef..8345d863e 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
public ShortClr()
{
// Job.Default.With(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3),
- this.Add(Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3));
+ this.Add(Job.Default.With(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3));
}
}
}
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
- #pragma warning disable SA1115
+#pragma warning disable SA1115
[Params(
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
diff --git a/src/ImageSharp/IO/BufferedReadStream2.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs
similarity index 67%
rename from src/ImageSharp/IO/BufferedReadStream2.cs
rename to tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs
index a35804ce2..76a48af21 100644
--- a/src/ImageSharp/IO/BufferedReadStream2.cs
+++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedReadStreamWrapper.cs
@@ -6,13 +6,13 @@ using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
-namespace SixLabors.ImageSharp.IO
+namespace SixLabors.ImageSharp.Benchmarks.IO
{
///
- /// A readonly stream that add a secondary level buffer in addition to native stream
+ /// A readonly stream wrapper that add a secondary level buffer in addition to native stream
/// buffered reading to reduce the overhead of small incremental reads.
///
- internal sealed unsafe class BufferedReadStream2 : IDisposable
+ internal sealed unsafe class BufferedReadStreamWrapper : IDisposable
{
///
/// The length, in bytes, of the underlying buffer.
@@ -29,19 +29,19 @@ namespace SixLabors.ImageSharp.IO
private readonly byte* pinnedReadBuffer;
+ // Index within our buffer, not reader position.
private int readBufferIndex;
- private readonly int length;
-
- private int position;
+ // Matches what the stream position would be without buffering
+ private long readerPosition;
private bool isDisposed;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The input stream.
- public BufferedReadStream2(Stream stream)
+ public BufferedReadStreamWrapper(Stream stream)
{
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream must be readable.");
Guard.IsTrue(stream.CanSeek, nameof(stream), "Stream must be seekable.");
@@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.IO
this.stream = stream;
this.Position = (int)stream.Position;
- this.length = (int)stream.Length;
+ this.Length = stream.Length;
this.readBuffer = ArrayPool.Shared.Rent(BufferLength);
this.readBufferHandle = new Memory(this.readBuffer).Pin();
@@ -69,113 +69,109 @@ namespace SixLabors.ImageSharp.IO
///
/// Gets the length, in bytes, of the stream.
///
- public long Length => this.length;
+ public long Length { get; }
///
/// Gets or sets the current position within the stream.
///
public long Position
{
- get => this.position;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.readerPosition;
+ [MethodImpl(MethodImplOptions.NoInlining)]
set
{
- // Only reset readIndex if we are out of bounds of our working buffer
+ // Only reset readBufferIndex if we are out of bounds of our working buffer
// otherwise we should simply move the value by the diff.
- int v = (int)value;
- if (this.IsInReadBuffer(v, out int index))
+ if (this.IsInReadBuffer(value, out long index))
{
- this.readBufferIndex = index;
- this.position = v;
+ this.readBufferIndex = (int)index;
+ this.readerPosition = value;
}
else
{
- this.position = v;
+ // Base stream seek will throw for us if invalid.
this.stream.Seek(value, SeekOrigin.Begin);
+ this.readerPosition = value;
this.readBufferIndex = BufferLength;
}
}
}
- public bool CanRead { get; } = true;
-
- public bool CanSeek { get; } = true;
-
- public bool CanWrite { get; } = false;
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadByte()
{
- if (this.position >= this.length)
+ if (this.readerPosition >= this.Length)
{
return -1;
}
+ // Our buffer has been read.
+ // We need to refill and start again.
if (this.readBufferIndex > MaxBufferIndex)
{
this.FillReadBuffer();
}
- this.position++;
+ this.readerPosition++;
return this.pinnedReadBuffer[this.readBufferIndex++];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Read(byte[] buffer, int offset, int count)
{
+ // Too big for our buffer. Read directly from the stream.
if (count > BufferLength)
{
return this.ReadToBufferDirectSlow(buffer, offset, count);
}
+ // Too big for remaining buffer but less than entire buffer length
+ // Copy to buffer then read from there.
if (count + this.readBufferIndex > BufferLength)
{
return this.ReadToBufferViaCopySlow(buffer, offset, count);
}
- // return this.ReadToBufferViaCopyFast(buffer, offset, count);
- int n = this.GetCopyCount(count);
- this.CopyBytes(buffer, offset, n);
-
- this.position += n;
- this.readBufferIndex += n;
-
- return n;
+ return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
public void Flush()
{
- // Reset the stream position.
- if (this.position != this.stream.Position)
+ // Reset the stream position to match reader position.
+ if (this.readerPosition != this.stream.Position)
{
- this.stream.Seek(this.position, SeekOrigin.Begin);
- this.position = (int)this.stream.Position;
+ this.stream.Seek(this.readerPosition, SeekOrigin.Begin);
+ this.readerPosition = (int)this.stream.Position;
}
+ // Reset to trigger full read on next attempt.
this.readBufferIndex = BufferLength;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long Seek(long offset, SeekOrigin origin)
{
- if (origin == SeekOrigin.Begin)
+ switch (origin)
{
- this.Position = offset;
- }
- else
- {
- this.Position += offset;
- }
+ case SeekOrigin.Begin:
+ this.Position = offset;
+ break;
- return this.position;
- }
+ case SeekOrigin.Current:
+ this.Position += offset;
+ break;
- public void SetLength(long value)
- => throw new NotSupportedException();
+ case SeekOrigin.End:
+ this.Position = this.Length - offset;
+ break;
+ }
- public void Write(byte[] buffer, int offset, int count)
- => throw new NotSupportedException();
+ return this.readerPosition;
+ }
+ ///
public void Dispose()
{
if (!this.isDisposed)
@@ -188,21 +184,18 @@ namespace SixLabors.ImageSharp.IO
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int GetPositionDifference(int p) => p - this.position;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool IsInReadBuffer(int p, out int index)
+ private bool IsInReadBuffer(long newPosition, out long index)
{
- index = this.GetPositionDifference(p) + this.readBufferIndex;
+ index = newPosition - this.readerPosition + this.readBufferIndex;
return index > -1 && index < BufferLength;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void FillReadBuffer()
{
- if (this.position != this.stream.Position)
+ if (this.readerPosition != this.stream.Position)
{
- this.stream.Seek(this.position, SeekOrigin.Begin);
+ this.stream.Seek(this.readerPosition, SeekOrigin.Begin);
}
this.stream.Read(this.readBuffer, 0, BufferLength);
@@ -215,35 +208,28 @@ namespace SixLabors.ImageSharp.IO
int n = this.GetCopyCount(count);
this.CopyBytes(buffer, offset, n);
- this.position += n;
+ this.readerPosition += n;
this.readBufferIndex += n;
return n;
}
- [MethodImpl(MethodImplOptions.NoInlining)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count)
{
// Refill our buffer then copy.
this.FillReadBuffer();
- // return this.ReadToBufferViaCopyFast(buffer, offset, count);
- int n = this.GetCopyCount(count);
- this.CopyBytes(buffer, offset, n);
-
- this.position += n;
- this.readBufferIndex += n;
-
- return n;
+ return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count)
{
// Read to target but don't copy to our read buffer.
- if (this.position != this.stream.Position)
+ if (this.readerPosition != this.stream.Position)
{
- this.stream.Seek(this.position, SeekOrigin.Begin);
+ this.stream.Seek(this.readerPosition, SeekOrigin.Begin);
}
int n = this.stream.Read(buffer, offset, count);
@@ -255,18 +241,18 @@ namespace SixLabors.ImageSharp.IO
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetCopyCount(int count)
{
- int n = this.length - this.position;
+ long n = this.Length - this.readerPosition;
if (n > count)
{
- n = count;
+ return count;
}
if (n < 0)
{
- n = 0;
+ return 0;
}
- return n;
+ return (int)n;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs
similarity index 73%
rename from tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs
rename to tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs
index 389a74326..c5064aeea 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs
+++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs
@@ -6,10 +6,10 @@ using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.IO;
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+namespace SixLabors.ImageSharp.Benchmarks.IO
{
[Config(typeof(Config.ShortClr))]
- public class DoubleBufferedStreams
+ public class BufferedStreams
{
private readonly byte[] buffer = CreateTestBytes();
private readonly byte[] chunk1 = new byte[2];
@@ -21,14 +21,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private MemoryStream stream4;
private MemoryStream stream5;
private MemoryStream stream6;
- private MemoryStream stream7;
- private MemoryStream stream8;
- private DoubleBufferedStreamReader reader1;
- private DoubleBufferedStreamReader reader2;
private BufferedReadStream bufferedStream1;
private BufferedReadStream bufferedStream2;
- private BufferedReadStream2 bufferedStream3;
- private BufferedReadStream2 bufferedStream4;
+ private BufferedReadStreamWrapper bufferedStreamWrap1;
+ private BufferedReadStreamWrapper bufferedStreamWrap2;
[GlobalSetup]
public void CreateStreams()
@@ -39,31 +35,25 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
this.stream4 = new MemoryStream(this.buffer);
this.stream5 = new MemoryStream(this.buffer);
this.stream6 = new MemoryStream(this.buffer);
- this.stream7 = new MemoryStream(this.buffer);
- this.stream8 = new MemoryStream(this.buffer);
- this.reader1 = new DoubleBufferedStreamReader(Configuration.Default.MemoryAllocator, this.stream2);
- this.reader2 = new DoubleBufferedStreamReader(Configuration.Default.MemoryAllocator, this.stream2);
- this.bufferedStream1 = new BufferedReadStream(this.stream5);
- this.bufferedStream2 = new BufferedReadStream(this.stream6);
- this.bufferedStream3 = new BufferedReadStream2(this.stream7);
- this.bufferedStream4 = new BufferedReadStream2(this.stream8);
+ this.bufferedStream1 = new BufferedReadStream(this.stream3);
+ this.bufferedStream2 = new BufferedReadStream(this.stream4);
+ this.bufferedStreamWrap1 = new BufferedReadStreamWrapper(this.stream5);
+ this.bufferedStreamWrap2 = new BufferedReadStreamWrapper(this.stream6);
}
[GlobalCleanup]
public void DestroyStreams()
{
+ this.bufferedStream1?.Dispose();
+ this.bufferedStream2?.Dispose();
+ this.bufferedStreamWrap1?.Dispose();
+ this.bufferedStreamWrap2?.Dispose();
this.stream1?.Dispose();
this.stream2?.Dispose();
this.stream3?.Dispose();
this.stream4?.Dispose();
this.stream5?.Dispose();
this.stream6?.Dispose();
- this.reader1?.Dispose();
- this.reader2?.Dispose();
- this.bufferedStream1?.Dispose();
- this.bufferedStream2?.Dispose();
- this.bufferedStream3?.Dispose();
- this.bufferedStream4?.Dispose();
}
[Benchmark]
@@ -82,10 +72,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
}
[Benchmark]
- public int DoubleBufferedStreamRead()
+ public int BufferedReadStreamRead()
{
int r = 0;
- DoubleBufferedStreamReader reader = this.reader2;
+ BufferedReadStream reader = this.bufferedStream1;
byte[] b = this.chunk2;
for (int i = 0; i < reader.Length / 2; i++)
@@ -97,25 +87,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
}
[Benchmark]
- public int BufferedStreamRead()
+ public int BufferedReadStreamWrapRead()
{
int r = 0;
- BufferedReadStream reader = this.bufferedStream2;
- byte[] b = this.chunk2;
-
- for (int i = 0; i < reader.Length / 2; i++)
- {
- r += reader.Read(b, 0, 2);
- }
-
- return r;
- }
-
- [Benchmark]
- public int BufferedStreamWrapRead()
- {
- int r = 0;
- BufferedReadStream2 reader = this.bufferedStream3;
+ BufferedReadStreamWrapper reader = this.bufferedStreamWrap1;
byte[] b = this.chunk2;
for (int i = 0; i < reader.Length / 2; i++)
@@ -130,7 +105,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
public int StandardStreamReadByte()
{
int r = 0;
- Stream stream = this.stream1;
+ Stream stream = this.stream2;
for (int i = 0; i < stream.Length; i++)
{
@@ -141,21 +116,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
}
[Benchmark]
- public int DoubleBufferedStreamReadByte()
- {
- int r = 0;
- DoubleBufferedStreamReader reader = this.reader1;
-
- for (int i = 0; i < reader.Length; i++)
- {
- r += reader.ReadByte();
- }
-
- return r;
- }
-
- [Benchmark]
- public int BufferedStreamReadByte()
+ public int BufferedReadStreamReadByte()
{
int r = 0;
BufferedReadStream reader = this.bufferedStream2;
@@ -169,10 +130,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
}
[Benchmark]
- public int BufferedStreamWrapReadByte()
+ public int BufferedReadStreamWrapReadByte()
{
int r = 0;
- BufferedReadStream2 reader = this.bufferedStream4;
+ BufferedReadStreamWrapper reader = this.bufferedStreamWrap2;
for (int i = 0; i < reader.Length; i++)
{
@@ -197,7 +158,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private static byte[] CreateTestBytes()
{
- var buffer = new byte[DoubleBufferedStreamReader.ChunkLength * 3];
+ var buffer = new byte[BufferedReadStream.BufferLength * 3];
var random = new Random();
random.NextBytes(buffer);
diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
index f380d0a6a..e26fba627 100644
--- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
+++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
@@ -31,7 +31,6 @@
-
diff --git a/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs b/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
index 992e2536d..748550a54 100644
--- a/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
+++ b/tests/ImageSharp.Tests/IO/BufferedReadStreamTests.cs
@@ -199,6 +199,22 @@ namespace SixLabors.ImageSharp.Tests.IO
}
}
+ [Fact]
+ public void BufferedStreamReadsCanReadAllAsSingleByteFromOrigin()
+ {
+ using (MemoryStream stream = this.CreateTestStream())
+ {
+ byte[] expected = stream.ToArray();
+ using (var reader = new BufferedReadStream(stream))
+ {
+ for (int i = 0; i < expected.Length; i++)
+ {
+ Assert.Equal(expected[i], reader.ReadByte());
+ }
+ }
+ }
+ }
+
private MemoryStream CreateTestStream(int length = BufferedReadStream.BufferLength * 3)
{
var buffer = new byte[length];
diff --git a/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs b/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs
deleted file mode 100644
index 62e204843..000000000
--- a/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.IO;
-using SixLabors.ImageSharp.IO;
-using SixLabors.ImageSharp.Memory;
-using Xunit;
-
-namespace SixLabors.ImageSharp.Tests.IO
-{
- public class DoubleBufferedStreamReaderTests
- {
- private readonly MemoryAllocator allocator = Configuration.Default.MemoryAllocator;
-
- [Fact]
- public void DoubleBufferedStreamReaderCanReadSingleByteFromOrigin()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- byte[] expected = stream.ToArray();
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
-
- Assert.Equal(expected[0], reader.ReadByte());
-
- // We've read a whole chunk but increment by 1 in our reader.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength);
- Assert.Equal(1, reader.Position);
- }
- }
-
- [Fact]
- public void DoubleBufferedStreamReaderCanReadSingleByteFromOffset()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- byte[] expected = stream.ToArray();
- const int offset = 5;
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
- reader.Position = offset;
-
- Assert.Equal(expected[offset], reader.ReadByte());
-
- // We've read a whole chunk but increment by 1 in our reader.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength + offset);
- Assert.Equal(offset + 1, reader.Position);
- }
- }
-
- [Fact]
- public void DoubleBufferedStreamReaderCanReadSubsequentSingleByteCorrectly()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- byte[] expected = stream.ToArray();
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
-
- for (int i = 0; i < expected.Length; i++)
- {
- Assert.Equal(expected[i], reader.ReadByte());
- Assert.Equal(i + 1, reader.Position);
-
- if (i < DoubleBufferedStreamReader.ChunkLength)
- {
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength);
- }
- else if (i >= DoubleBufferedStreamReader.ChunkLength && i < DoubleBufferedStreamReader.ChunkLength * 2)
- {
- // We should have advanced to the second chunk now.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength * 2);
- }
- else
- {
- // We should have advanced to the third chunk now.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength * 3);
- }
- }
- }
- }
-
- [Fact]
- public void DoubleBufferedStreamReaderCanReadMultipleBytesFromOrigin()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- var buffer = new byte[2];
- byte[] expected = stream.ToArray();
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
-
- Assert.Equal(2, reader.Read(buffer, 0, 2));
- Assert.Equal(expected[0], buffer[0]);
- Assert.Equal(expected[1], buffer[1]);
-
- // We've read a whole chunk but increment by the buffer length in our reader.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength);
- Assert.Equal(buffer.Length, reader.Position);
- }
- }
-
- [Fact]
- public void DoubleBufferedStreamReaderCanReadSubsequentMultipleByteCorrectly()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- var buffer = new byte[2];
- byte[] expected = stream.ToArray();
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
-
- for (int i = 0, o = 0; i < expected.Length / 2; i++, o += 2)
- {
- Assert.Equal(2, reader.Read(buffer, 0, 2));
- Assert.Equal(expected[o], buffer[0]);
- Assert.Equal(expected[o + 1], buffer[1]);
- Assert.Equal(o + 2, reader.Position);
-
- int offset = i * 2;
- if (offset < DoubleBufferedStreamReader.ChunkLength)
- {
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength);
- }
- else if (offset >= DoubleBufferedStreamReader.ChunkLength && offset < DoubleBufferedStreamReader.ChunkLength * 2)
- {
- // We should have advanced to the second chunk now.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength * 2);
- }
- else
- {
- // We should have advanced to the third chunk now.
- Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength * 3);
- }
- }
- }
- }
-
- [Fact]
- public void DoubleBufferedStreamReaderCanSkip()
- {
- using (MemoryStream stream = this.CreateTestStream())
- {
- byte[] expected = stream.ToArray();
- var reader = new DoubleBufferedStreamReader(this.allocator, stream);
-
- int skip = 50;
- int plusOne = 1;
- int skip2 = DoubleBufferedStreamReader.ChunkLength;
-
- // Skip
- reader.Skip(skip);
- Assert.Equal(skip, reader.Position);
- Assert.Equal(stream.Position, reader.Position);
-
- // Read
- Assert.Equal(expected[skip], reader.ReadByte());
-
- // Skip Again
- reader.Skip(skip2);
-
- // First Skip + First Read + Second Skip
- int position = skip + plusOne + skip2;
-
- Assert.Equal(position, reader.Position);
- Assert.Equal(stream.Position, reader.Position);
- Assert.Equal(expected[position], reader.ReadByte());
- }
- }
-
- private MemoryStream CreateTestStream()
- {
- var buffer = new byte[DoubleBufferedStreamReader.ChunkLength * 3];
- var random = new Random();
- random.NextBytes(buffer);
-
- return new MemoryStream(buffer);
- }
- }
-}