diff --git a/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs b/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs index fc5bd9cf..57837f84 100644 --- a/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/AsyncMarkingBinaryReader.cs @@ -47,14 +47,12 @@ internal class AsyncMarkingBinaryReader public async ValueTask ReadUInt16Async(CancellationToken cancellationToken = default) { - CurrentReadByteCount += 2; var bytes = await ReadBytesAsync(2, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt16LittleEndian(bytes); } public async ValueTask ReadUInt32Async(CancellationToken cancellationToken = default) { - CurrentReadByteCount += 4; var bytes = await ReadBytesAsync(4, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt32LittleEndian(bytes); } @@ -63,7 +61,6 @@ internal class AsyncMarkingBinaryReader CancellationToken cancellationToken = default ) { - CurrentReadByteCount += 8; var bytes = await ReadBytesAsync(8, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadUInt64LittleEndian(bytes); } @@ -72,7 +69,6 @@ internal class AsyncMarkingBinaryReader CancellationToken cancellationToken = default ) { - CurrentReadByteCount += 2; var bytes = await ReadBytesAsync(2, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt16LittleEndian(bytes); } @@ -81,7 +77,6 @@ internal class AsyncMarkingBinaryReader CancellationToken cancellationToken = default ) { - CurrentReadByteCount += 4; var bytes = await ReadBytesAsync(4, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt32LittleEndian(bytes); } @@ -90,7 +85,6 @@ internal class AsyncMarkingBinaryReader CancellationToken cancellationToken = default ) { - CurrentReadByteCount += 8; var bytes = await ReadBytesAsync(8, cancellationToken).ConfigureAwait(false); return BinaryPrimitives.ReadInt64LittleEndian(bytes); } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.Async.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.Async.cs index 845da53f..f753eb03 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.Async.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.Async.cs @@ -119,7 +119,9 @@ internal partial class FileHeader { case FHEXTRA_CRYPT: { - Rar5CryptoInfo = await Rar5CryptoInfo.CreateAsync(reader, true); + Rar5CryptoInfo = await Rar5CryptoInfo + .CreateAsync(reader, true) + .ConfigureAwait(false); if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0)) { Rar5CryptoInfo = null; diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.Async.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.Async.cs index c36687c9..126e855a 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.Async.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.Async.cs @@ -72,7 +72,7 @@ internal sealed partial class StreamingZipHeaderFactory ) { _headerFactory = headerFactory; - _rewindableStream = RewindableStream.EnsureSeekable(stream); + _rewindableStream = new RewindableStream(stream); _reader = new AsyncBinaryReader(_rewindableStream, leaveOpen: true); _cancellationToken = cancellationToken; } diff --git a/tests/SharpCompress.Test/BinaryReaderParityTests.cs b/tests/SharpCompress.Test/BinaryReaderParityTests.cs new file mode 100644 index 00000000..72a7f166 --- /dev/null +++ b/tests/SharpCompress.Test/BinaryReaderParityTests.cs @@ -0,0 +1,188 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Common; +using SharpCompress.IO; +using Xunit; + +namespace SharpCompress.Test; + +public class BinaryReaderParityTests : TestBase +{ + private readonly byte[] _testData; + + public BinaryReaderParityTests() + { + // Create test data with various patterns + _testData = new byte[256]; + for (int i = 0; i < 256; i++) + { + _testData[i] = (byte)i; + } + } + + [Fact] + public async Task ReadByte_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + for (int i = 0; i < 10; i++) + { + byte syncByte = syncReader.ReadByte(); + byte asyncByte = await asyncReader.ReadByteAsync(); + Assert.Equal(syncByte, asyncByte); + } + } + + [Fact] + public async Task ReadBytes_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + var syncBytes = syncReader.ReadBytes(32); + var asyncBuffer = new byte[32]; + await asyncReader.ReadBytesAsync(asyncBuffer, 0, 32); + + Assert.Equal(syncBytes, asyncBuffer); + } + + [Fact] + public async Task ReadUInt16_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + ushort syncValue = syncReader.ReadUInt16(); + ushort asyncValue = await asyncReader.ReadUInt16Async(); + + Assert.Equal(syncValue, asyncValue); + } + + [Fact] + public async Task ReadUInt32_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + uint syncValue = syncReader.ReadUInt32(); + uint asyncValue = await asyncReader.ReadUInt32Async(); + + Assert.Equal(syncValue, asyncValue); + } + + [Fact] + public async Task ReadUInt64_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + ulong syncValue = syncReader.ReadUInt64(); + ulong asyncValue = await asyncReader.ReadUInt64Async(); + + Assert.Equal(syncValue, asyncValue); + } + + [Fact] + public void Position_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + // Read some bytes + syncReader.ReadBytes(10); + var asyncBuffer = new byte[10]; + asyncReader.ReadBytesAsync(asyncBuffer, 0, 10).AsTask().Wait(); + + Assert.Equal(syncStream.Position, asyncStream.Position); + Assert.Equal(syncReader.BaseStream.Position, asyncReader.BaseStream.Position); + } + + [Fact] + public async Task MultipleReads_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + using var syncReader = new BinaryReader(syncStream); + using var asyncReader = new AsyncBinaryReader(asyncStream); + + // Mix of different read operations + Assert.Equal(syncReader.ReadByte(), await asyncReader.ReadByteAsync()); + + var syncBytes = new byte[16]; + var asyncBytes = new byte[16]; + syncStream.Read(syncBytes, 0, 16); + await asyncReader.ReadBytesAsync(asyncBytes, 0, 16); + Assert.Equal(syncBytes, asyncBytes); + + Assert.Equal(syncReader.ReadUInt16(), await asyncReader.ReadUInt16Async()); + Assert.Equal(syncReader.ReadUInt32(), await asyncReader.ReadUInt32Async()); + Assert.Equal(syncReader.ReadUInt64(), await asyncReader.ReadUInt64Async()); + } + + [Fact] + public async Task ReadByte_Async_Properly_Async() + { + using var stream = new MemoryStream(_testData); + using var reader = new AsyncBinaryReader(stream); + + var bytes = new byte[10]; + for (int i = 0; i < 10; i++) + { + bytes[i] = await reader.ReadByteAsync(); + } + + Assert.Equal(_testData.Take(10).ToArray(), bytes); + } + + [Fact] + public async Task ReadBytes_Async_Properly_Async() + { + using var stream = new MemoryStream(_testData); + using var reader = new AsyncBinaryReader(stream); + + var buffer = new byte[64]; + await reader.ReadBytesAsync(buffer, 0, 64); + + Assert.Equal(_testData.Take(64).ToArray(), buffer); + } + + [Fact] + public async Task SkipAsync_Moves_Position_Correctly() + { + using var stream = new MemoryStream(_testData); + using var reader = new AsyncBinaryReader(stream); + + await reader.SkipAsync(10); + + Assert.Equal(10, stream.Position); + Assert.Equal(10, reader.BaseStream.Position); + } + + [Fact] + public async Task SkipAsync_Then_Read_Works_Correctly() + { + using var stream = new MemoryStream(_testData); + using var reader = new AsyncBinaryReader(stream); + + await reader.SkipAsync(10); + byte b = await reader.ReadByteAsync(); + + Assert.Equal(_testData[10], b); + } +} diff --git a/tests/SharpCompress.Test/MarkingBinaryReaderParityTests.cs b/tests/SharpCompress.Test/MarkingBinaryReaderParityTests.cs new file mode 100644 index 00000000..5dfeb5b8 --- /dev/null +++ b/tests/SharpCompress.Test/MarkingBinaryReaderParityTests.cs @@ -0,0 +1,258 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using SharpCompress.Common.Rar; +using SharpCompress.IO; +using Xunit; + +namespace SharpCompress.Test; + +public class MarkingBinaryReaderParityTests : TestBase +{ + private readonly byte[] _testData; + + public MarkingBinaryReaderParityTests() + { + // Create test data with various patterns + _testData = new byte[256]; + for (int i = 0; i < 256; i++) + { + _testData[i] = (byte)i; + } + } + + [Fact] + public void Mark_Resets_ByteCount() + { + using var stream = new MemoryStream(_testData); + var reader = new MarkingBinaryReader(stream); + + reader.ReadBytes(10); + Assert.Equal(10, reader.CurrentReadByteCount); + + reader.Mark(); + Assert.Equal(0, reader.CurrentReadByteCount); + + reader.ReadBytes(5); + Assert.Equal(5, reader.CurrentReadByteCount); + } + + [Fact] + public async Task Mark_Resets_ByteCount_Async() + { + using var stream = new MemoryStream(_testData); + var reader = new AsyncMarkingBinaryReader(stream); + + await reader.ReadBytesAsync(10); + Assert.Equal(10, reader.CurrentReadByteCount); + + reader.Mark(); + Assert.Equal(0, reader.CurrentReadByteCount); + + await reader.ReadBytesAsync(5); + Assert.Equal(5, reader.CurrentReadByteCount); + } + + [Fact] + public void ReadByte_Updates_ByteCount() + { + using var stream = new MemoryStream(_testData); + var reader = new MarkingBinaryReader(stream); + + reader.Mark(); + reader.ReadByte(); + Assert.Equal(1, reader.CurrentReadByteCount); + + reader.ReadByte(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public async Task ReadByte_Updates_ByteCount_Async() + { + using var stream = new MemoryStream(_testData); + var reader = new AsyncMarkingBinaryReader(stream); + + reader.Mark(); + await reader.ReadByteAsync(); + Assert.Equal(1, reader.CurrentReadByteCount); + + await reader.ReadByteAsync(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public void ReadBytes_Updates_ByteCount() + { + using var stream = new MemoryStream(_testData); + var reader = new MarkingBinaryReader(stream); + + reader.Mark(); + reader.ReadBytes(16); + Assert.Equal(16, reader.CurrentReadByteCount); + + reader.ReadBytes(8); + Assert.Equal(24, reader.CurrentReadByteCount); + } + + [Fact] + public async Task ReadBytes_Updates_ByteCount_Async() + { + using var stream = new MemoryStream(_testData); + var reader = new AsyncMarkingBinaryReader(stream); + + reader.Mark(); + await reader.ReadBytesAsync(16); + Assert.Equal(16, reader.CurrentReadByteCount); + + await reader.ReadBytesAsync(8); + Assert.Equal(24, reader.CurrentReadByteCount); + } + + [Fact] + public void ReadUInt16_Updates_ByteCount() + { + using var stream = new MemoryStream(_testData); + var reader = new MarkingBinaryReader(stream); + + reader.Mark(); + reader.ReadUInt16(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public async Task ReadUInt16_Updates_ByteCount_Async() + { + using var stream = new MemoryStream(_testData); + var reader = new AsyncMarkingBinaryReader(stream); + + reader.Mark(); + await reader.ReadUInt16Async(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public void ReadUInt32_Updates_ByteCount() + { + using var stream = new MemoryStream(_testData); + var reader = new MarkingBinaryReader(stream); + + reader.Mark(); + reader.ReadUInt32(); + Assert.Equal(4, reader.CurrentReadByteCount); + } + + [Fact] + public async Task ReadUInt32_Updates_ByteCount_Async() + { + using var stream = new MemoryStream(_testData); + var reader = new AsyncMarkingBinaryReader(stream); + + reader.Mark(); + await reader.ReadUInt32Async(); + Assert.Equal(4, reader.CurrentReadByteCount); + } + + [Fact] + public void ReadRarVInt_Updates_ByteCount() + { + // Create valid RAR v-int data: 0x05 (value 5, no continuation bit) + var data = new byte[] { 0x05, 0x85, 0x01, 0x00 }; // 0x05, then 0x85 0x01 (value 5 + 128 = 133) + using var stream = new MemoryStream(data); + var reader = new MarkingBinaryReader(stream); + + reader.Mark(); + // Read a single-byte v-int (value 5, no continuation bit) + reader.ReadRarVInt(); + Assert.Equal(1, reader.CurrentReadByteCount); + + reader.Mark(); + // Read a two-byte v-int (0x85 means continuation, then 0x01) + reader.ReadRarVInt(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public async Task ReadRarVInt_Updates_ByteCount_Async() + { + // Create valid RAR v-int data: 0x05 (value 5, no continuation bit) + var data = new byte[] { 0x05, 0x85, 0x01, 0x00 }; + using var stream = new MemoryStream(data); + var reader = new AsyncMarkingBinaryReader(stream); + + reader.Mark(); + // Read a single-byte v-int (value 5, no continuation bit) + await reader.ReadRarVIntAsync(); + Assert.Equal(1, reader.CurrentReadByteCount); + + reader.Mark(); + // Read a two-byte v-int (0x85 means continuation, then 0x01) + await reader.ReadRarVIntAsync(); + Assert.Equal(2, reader.CurrentReadByteCount); + } + + [Fact] + public async Task Sync_Async_ByteCount_Parity() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + var syncReader = new MarkingBinaryReader(syncStream); + var asyncReader = new AsyncMarkingBinaryReader(asyncStream); + + syncReader.Mark(); + asyncReader.Mark(); + + // Read bytes with sync + syncReader.ReadByte(); + syncReader.ReadByte(); + syncReader.ReadUInt16(); + syncReader.ReadUInt32(); + syncReader.ReadBytes(8); + var syncCount = syncReader.CurrentReadByteCount; + + // Read bytes with async + await asyncReader.ReadByteAsync(); + await asyncReader.ReadByteAsync(); + await asyncReader.ReadUInt16Async(); + await asyncReader.ReadUInt32Async(); + await asyncReader.ReadBytesAsync(8); + var asyncCount = asyncReader.CurrentReadByteCount; + + Assert.Equal(syncCount, asyncCount); + Assert.Equal(16, syncCount); + Assert.Equal(16, asyncCount); + } + + [Fact] + public async Task Sync_Async_ByteCount_Parity_Alt() + { + using var syncStream = new MemoryStream(_testData); + using var asyncStream = new MemoryStream(_testData); + var syncReader = new MarkingBinaryReader(syncStream); + var asyncReader = new AsyncMarkingBinaryReader(asyncStream); + + syncReader.Mark(); + asyncReader.Mark(); + + // Read bytes with sync + syncReader.ReadByte(); + syncReader.ReadByte(); + syncReader.ReadUInt16(); + syncReader.ReadUInt32(); + syncReader.ReadBytes(8); + var syncCount = syncReader.CurrentReadByteCount; + + // Read bytes with async + await asyncReader.ReadByteAsync(); + await asyncReader.ReadByteAsync(); + await asyncReader.ReadUInt16Async(); + await asyncReader.ReadUInt32Async(); + await asyncReader.ReadBytesAsync(8); + var asyncCount = asyncReader.CurrentReadByteCount; + + Assert.Equal(syncCount, asyncCount); + Assert.Equal(16, syncCount); + Assert.Equal(16, asyncCount); + } +}