diff --git a/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStreamBaseTests.cs b/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStreamBaseTests.cs index 3241584..1d61599 100644 --- a/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStreamBaseTests.cs +++ b/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStreamBaseTests.cs @@ -1,97 +1,115 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; using Kaitai.Async; using Xunit; namespace Kaitai.Struct.Runtime.Async.Tests { - public class KaitaiAsyncStreamBaseTests + public class StreamKaitaiAsyncStreamBaseTests : KaitaiAsyncStreamBaseTests { - [Fact] - public async Task AlignToByte_Test() - { - //Arrange - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[]{0b_1000_0000}); - - var read = await kaitaiStreamSUT.ReadBitsIntAsync(1); - Assert.Equal(1u, read); - - //Act - kaitaiStreamSUT.AlignToByte(); - //Assert - Assert.Equal(1, kaitaiStreamSUT.Pos); - } - - [Theory] - [InlineData(true, 0, 0)] - [InlineData(false, 1, 0)] - [InlineData(false, 1, 1)] - [InlineData(false, 1, 2)] - [InlineData(false, 1, 3)] - [InlineData(false, 1, 4)] - [InlineData(false, 1, 5)] - [InlineData(false, 1, 6)] - [InlineData(false, 1, 7)] - [InlineData(true, 1, 8)] - public async Task Eof_Test(bool shouldBeEof, int streamSize, int readBitsAmount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[streamSize]); - - await kaitaiStreamSUT.ReadBitsIntAsync(readBitsAmount); - - if (shouldBeEof) - Assert.True(kaitaiStreamSUT.IsEof); - else - Assert.False(kaitaiStreamSUT.IsEof); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(1, 1)] - public async Task Pos_ByRead_Test(int expectedPos, int readBitsAmount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[1]); - - await kaitaiStreamSUT.ReadBytesAsync(readBitsAmount); - - Assert.Equal(expectedPos, kaitaiStreamSUT.Pos); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(1, 1)] - public async Task Pos_BySeek_Test(int expectedPos, int position) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[1]); - - await kaitaiStreamSUT.SeekAsync(position); - - Assert.Equal(expectedPos, kaitaiStreamSUT.Pos); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void Size_Test(int streamSize) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[streamSize]); - - Assert.Equal(streamSize, kaitaiStreamSUT.Size); - } - - [Fact] - public void EmptyStream_NoRead_NoSeek_IsEof_ShouldBe_True() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - Assert.True(kaitaiStreamSUT.IsEof); - } - - [Fact] - public void EmptyStream_NoRead_NoSeek_Pos_ShouldBe_0() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - Assert.Equal(0, kaitaiStreamSUT.Pos); - } + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderKaitaiAsyncStreamBaseTests : KaitaiAsyncStreamBaseTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class KaitaiAsyncStreamBaseTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + [Theory] + [InlineData(true, 0, 0)] + [InlineData(false, 1, 0)] + [InlineData(false, 1, 1)] + [InlineData(false, 1, 2)] + [InlineData(false, 1, 3)] + [InlineData(false, 1, 4)] + [InlineData(false, 1, 5)] + [InlineData(false, 1, 6)] + [InlineData(false, 1, 7)] + [InlineData(true, 1, 8)] + public async Task Eof_Test(bool shouldBeEof, int streamSize, int readBitsAmount) + { + var kaitaiStreamSUT = Create(new byte[streamSize]); + + await kaitaiStreamSUT.ReadBitsIntAsync(readBitsAmount); + + if (shouldBeEof) + { + Assert.True(kaitaiStreamSUT.IsEof); + } + else + { + Assert.False(kaitaiStreamSUT.IsEof); + } + } + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + public async Task Pos_ByRead_Test(int expectedPos, int readBitsAmount) + { + var kaitaiStreamSUT = Create(new byte[1]); + + await kaitaiStreamSUT.ReadBytesAsync(readBitsAmount); + + Assert.Equal(expectedPos, kaitaiStreamSUT.Pos); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 1)] + public async Task Pos_BySeek_Test(int expectedPos, int position) + { + var kaitaiStreamSUT = Create(new byte[1]); + + await kaitaiStreamSUT.SeekAsync(position); + + Assert.Equal(expectedPos, kaitaiStreamSUT.Pos); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + public void Size_Test(int streamSize) + { + var kaitaiStreamSUT = Create(new byte[streamSize]); + + Assert.Equal(streamSize, kaitaiStreamSUT.Size); + } + + [Fact] + public async Task AlignToByte_Test() + { + //Arrange + var kaitaiStreamSUT = Create(new byte[] {0b_1000_0000}); + + ulong read = await kaitaiStreamSUT.ReadBitsIntAsync(1); + Assert.Equal(1u, read); + + //Act + kaitaiStreamSUT.AlignToByte(); + //Assert + Assert.Equal(1, kaitaiStreamSUT.Pos); + } + + [Fact] + public void EmptyStream_NoRead_NoSeek_IsEof_ShouldBe_True() + { + var kaitaiStreamSUT = Create(new byte[0]); + + Assert.True(kaitaiStreamSUT.IsEof); + } + + [Fact] + public void EmptyStream_NoRead_NoSeek_Pos_ShouldBe_0() + { + var kaitaiStreamSUT = Create(new byte[0]); + + Assert.Equal(0, kaitaiStreamSUT.Pos); } + } } \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStructTests.cs b/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStructTests.cs index 9803a62..f64dc20 100644 --- a/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStructTests.cs +++ b/Kaitai.Struct.Runtime.Async.Tests/KaitaiAsyncStructTests.cs @@ -1,28 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.IO; using Kaitai.Async; using Xunit; namespace Kaitai.Struct.Runtime.Async.Tests { - - public class KaitaiAsyncStructTests + public class StreamKaitaiAsyncStructTests : KaitaiAsyncStructTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderKaitaiAsyncStructTests : KaitaiAsyncStructTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class KaitaiAsyncStructTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + private class FooKaitaiAsyncStruct : KaitaiAsyncStruct { - [Fact] - public void M_Io_IsSet() - { - var kaitaiAsyncStream = new KaitaiAsyncStream(new byte[0]); - var kaitaiAsyncStructSUT = new FooKaitaiAsyncStruct(kaitaiAsyncStream); + public FooKaitaiAsyncStruct(KaitaiAsyncStream kaitaiStream) : base(kaitaiStream) + { + } + } - Assert.Equal(kaitaiAsyncStream, kaitaiAsyncStructSUT.M_Io); - } + [Fact] + public void M_Io_IsSet() + { + var kaitaiAsyncStream = Create(new byte[0]); + var kaitaiAsyncStructSUT = new FooKaitaiAsyncStruct(kaitaiAsyncStream); - private class FooKaitaiAsyncStruct : KaitaiAsyncStruct - { - public FooKaitaiAsyncStruct(KaitaiAsyncStream kaitaiStream) : base(kaitaiStream) - { - } - } + Assert.Equal(kaitaiAsyncStream, kaitaiAsyncStructSUT.M_Io); } -} + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadBits.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadBits.cs deleted file mode 100644 index bb89c64..0000000 --- a/Kaitai.Struct.Runtime.Async.Tests/ReadBits.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kaitai.Async; -using Xunit; - -namespace Kaitai.Struct.Runtime.Async.Tests -{ - public class ReadBits - { - [Theory] - [MemberData(nameof(BitsData.BitsBeData), MemberType = typeof(BitsData))] - public async Task ReadBitsIntAsync_Test(ulong expected, byte[] streamContent, int bitsCount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadBitsIntAsync(bitsCount)); - } - - [Theory] - [MemberData(nameof(BitsData.BitsLeData), MemberType = typeof(BitsData))] - public async Task ReadBitsIntLeAsync_Test(ulong expected, byte[] streamContent, int bitsCount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadBitsIntLeAsync(bitsCount)); - } - } -} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadBitsAsyncTests.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadBitsAsyncTests.cs new file mode 100644 index 0000000..baa4b56 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async.Tests/ReadBitsAsyncTests.cs @@ -0,0 +1,41 @@ +using System.IO; +using System.Threading.Tasks; +using Kaitai.Async; +using Xunit; + +namespace Kaitai.Struct.Runtime.Async.Tests +{ + public class StreamKaitaiReadBitsAsyncTests : ReadBitsAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderReadBitsAsyncTests : ReadBitsAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class ReadBitsAsyncTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + [Theory] + [MemberData(nameof(BitsData.BitsBeData), MemberType = typeof(BitsData))] + public async Task ReadBitsIntAsync_Test(ulong expected, byte[] streamContent, int bitsCount) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadBitsIntAsync(bitsCount)); + } + + [Theory] + [MemberData(nameof(BitsData.BitsLeData), MemberType = typeof(BitsData))] + public async Task ReadBitsIntLeAsync_Test(ulong expected, byte[] streamContent, int bitsCount) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadBitsIntLeAsync(bitsCount)); + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsync.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsync.cs deleted file mode 100644 index f1fad8f..0000000 --- a/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsync.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Kaitai.Async; -using Xunit; - -namespace Kaitai.Struct.Runtime.Async.Tests -{ - public class ReadBytesAsync - { - public static IEnumerable BytesData => - new List<(byte[] streamContent, int bytesCount)> - { - (new byte[] {0b_1101_0101}, 0), - (new byte[] {0b_1101_0101}, 1), - (new byte[] {0b_1101_0101, 0b_1101_0101}, 1), - (new byte[] {0b_1101_0101, 0b_1101_0101}, 2), - }.Select(t => new object[] { t.streamContent, t.bytesCount }); - - - [Theory] - [MemberData(nameof(BytesData))] - public async Task ReadBytesAsync_long_Test(byte[] streamContent, long bytesCount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(streamContent.Take((int)bytesCount), await kaitaiStreamSUT.ReadBytesAsync(bytesCount)); - } - - [Theory] - [MemberData(nameof(BytesData))] - public async Task ReadBytesAsync_ulong_Test(byte[] streamContent, ulong bytesCount) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(streamContent.Take((int)bytesCount), await kaitaiStreamSUT.ReadBytesAsync(bytesCount)); - } - - [Fact] - public async Task ReadBytesAsyncLong_NegativeInvoke_ThrowsArgumentOutOfRangeException() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - await Assert.ThrowsAsync(async () => - await kaitaiStreamSUT.ReadBytesAsync((long) -1)); - } - - [Fact] - public async Task ReadBytesAsyncLong_LargerThanInt32Invoke_ThrowsArgumentOutOfRangeException() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - await Assert.ThrowsAsync(async () => - await kaitaiStreamSUT.ReadBytesAsync((long)Int32.MaxValue+1)); - } - - [Fact] - public async Task ReadBytesAsyncLong_LargerThanBufferInvoke_ThrowsArgumentOutOfRangeException() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - await Assert.ThrowsAsync(async () => - await kaitaiStreamSUT.ReadBytesAsync((long)1)); - } - - [Fact] - public async Task ReadBytesAsyncULong_LargerThanInt32Invoke_ThrowsArgumentOutOfRangeException() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - await Assert.ThrowsAsync(async () => - await kaitaiStreamSUT.ReadBytesAsync((ulong)Int32.MaxValue + 1)); - } - - [Fact] - public async Task ReadBytesAsyncULong_LargerThanBufferInvoke_ThrowsArgumentOutOfRangeException() - { - var kaitaiStreamSUT = new KaitaiAsyncStream(new byte[0]); - - await Assert.ThrowsAsync(async () => - await kaitaiStreamSUT.ReadBytesAsync((ulong)1)); - } - - public static IEnumerable StringData => - new List - { - "", - "ABC", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - }.Select(t => new []{ Encoding.ASCII.GetBytes(t)}); - - - [Theory] - [MemberData(nameof(StringData))] - public async Task ReadBytesFullAsync_Test(byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(streamContent, await kaitaiStreamSUT.ReadBytesFullAsync()); - } - - [Theory] - [MemberData(nameof(StringData))] - public async Task EnsureFixedContentsAsync_Test(byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(streamContent, await kaitaiStreamSUT.EnsureFixedContentsAsync(streamContent)); - } - - [Theory] - [MemberData(nameof(StringData))] - public async Task EnsureFixedContentsAsync_ThrowsIfByteIsChanged(byte[] streamContent) - { - if(streamContent.Length == 0) return; - - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - var expected = streamContent.ToArray(); - expected[0] = (byte)~expected[0]; - - await Assert.ThrowsAsync(async () => await kaitaiStreamSUT.EnsureFixedContentsAsync(expected)); - } - - public static IEnumerable StringWithTerminatorsData => - new List<(string streamContent, string expected, char terminator, bool isPresent, bool shouldInclude)> - { - ("", "", '\0', false, false), - ("", "", '\0', false, true), - - ("ABC", "ABC", '\0', false, false), - ("ABC", "ABC", '\0', false, true), - - ("ABC", "", 'A', true, false), - ("ABC", "A", 'A', true, true), - - ("ABC", "A", 'B', true, false), - ("ABC", "AB", 'B', true, true), - - ("ABC", "AB", 'C', true, false), - ("ABC", "ABC", 'C', true, true), - }.Select(t => new[] { Encoding.ASCII.GetBytes(t.streamContent), Encoding.ASCII.GetBytes(t.expected), (object)(byte)t.terminator, t.isPresent, t.shouldInclude}); - - [Theory] - [MemberData(nameof(StringWithTerminatorsData))] - public async Task ReadBytesTermAsync(byte[] streamContent, byte[] expected, byte terminator, bool _, bool shouldInclude) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, false)); - } - - [Theory] - [MemberData(nameof(StringWithTerminatorsData))] - public async Task ReadBytesTermAsync_ThrowsIsTerminatorNotPresent(byte[] streamContent, byte[] expected, byte terminator, bool terminatorIsPresent, bool shouldInclude) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - if(terminatorIsPresent) return; - - await Assert.ThrowsAsync(async()=> await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, true)); - } - - [Theory] - [MemberData(nameof(StringWithTerminatorsData))] - public async Task ReadBytesTermAsync_ShouldNotConsumeTerminator(byte[] streamContent, byte[] expected, byte terminator, bool terminatorIsPresent, bool shouldInclude) - { - //Arrange - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - //Act - await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, false); - - //Assert - var amountToConsume = expected.Length; - if (expected.Length > 0 && shouldInclude && terminatorIsPresent) - { - amountToConsume--; - } - - Assert.Equal(amountToConsume, kaitaiStreamSUT.Pos); - } - - [Theory] - [MemberData(nameof(StringWithTerminatorsData))] - public async Task ReadBytesTermAsync_ShouldConsumeTerminator(byte[] streamContent, byte[] expected, byte terminator, bool terminatorIsPresent, bool shouldInclude) - { - //Arrange - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - //Act - await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, true, false); - - //Assert - var amountToConsume = expected.Length; - if (!shouldInclude && terminatorIsPresent) - { - amountToConsume++; - } - - Assert.Equal(amountToConsume, kaitaiStreamSUT.Pos); - } - - } -} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsyncTests.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsyncTests.cs new file mode 100644 index 0000000..1de942b --- /dev/null +++ b/Kaitai.Struct.Runtime.Async.Tests/ReadBytesAsyncTests.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Kaitai.Async; +using Xunit; + +namespace Kaitai.Struct.Runtime.Async.Tests +{ + public class StreamReadBytesAsyncTests : ReadBytesAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderReadBytesAsyncTests : ReadBytesAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class ReadBytesAsyncTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + public static IEnumerable BytesData => + new List<(byte[] streamContent, int bytesCount)> + { + (new byte[] {0b_1101_0101}, 0), + (new byte[] {0b_1101_0101}, 1), + (new byte[] {0b_1101_0101, 0b_1101_0101}, 1), + (new byte[] {0b_1101_0101, 0b_1101_0101}, 2) + }.Select(t => new object[] {t.streamContent, t.bytesCount}); + + + [Theory] + [MemberData(nameof(BytesData))] + public async Task ReadBytesAsync_long_Test(byte[] streamContent, long bytesCount) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(streamContent.Take((int) bytesCount), await kaitaiStreamSUT.ReadBytesAsync(bytesCount)); + } + + [Theory] + [MemberData(nameof(BytesData))] + public async Task ReadBytesAsync_ulong_Test(byte[] streamContent, ulong bytesCount) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(streamContent.Take((int) bytesCount), await kaitaiStreamSUT.ReadBytesAsync(bytesCount)); + } + + public static IEnumerable StringData => + new List + { + "", + "ABC", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + }.Select(t => new[] {Encoding.ASCII.GetBytes(t)}); + + + [Theory] + [MemberData(nameof(StringData))] + public async Task ReadBytesFullAsync_Test(byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(streamContent, await kaitaiStreamSUT.ReadBytesFullAsync()); + } + + [Theory] + [MemberData(nameof(StringData))] + public async Task EnsureFixedContentsAsync_Test(byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(streamContent, await kaitaiStreamSUT.EnsureFixedContentsAsync(streamContent)); + } + + [Theory] + [MemberData(nameof(StringData))] + public async Task EnsureFixedContentsAsync_ThrowsIfByteIsChanged(byte[] streamContent) + { + if (streamContent.Length == 0) + { + return; + } + + var kaitaiStreamSUT = Create(streamContent); + + var expected = streamContent.ToArray(); + expected[0] = (byte) ~expected[0]; + + await Assert.ThrowsAsync(async () => await kaitaiStreamSUT.EnsureFixedContentsAsync(expected)); + } + + public static IEnumerable StringWithTerminatorsData => + new List<(string streamContent, string expected, char terminator, bool isPresent, bool shouldInclude)> + { + ("", "", '\0', false, false), + ("", "", '\0', false, true), + + ("ABC", "ABC", '\0', false, false), + ("ABC", "ABC", '\0', false, true), + + ("ABC", "", 'A', true, false), + ("ABC", "A", 'A', true, true), + + ("ABC", "A", 'B', true, false), + ("ABC", "AB", 'B', true, true), + + ("ABC", "AB", 'C', true, false), + ("ABC", "ABC", 'C', true, true) + }.Select(t => new[] + { + Encoding.ASCII.GetBytes(t.streamContent), Encoding.ASCII.GetBytes(t.expected), (object) (byte) t.terminator, + t.isPresent, t.shouldInclude + }); + + [Theory] + [MemberData(nameof(StringWithTerminatorsData))] + public async Task ReadBytesTermAsync(byte[] streamContent, + byte[] expected, + byte terminator, + bool _, + bool shouldInclude) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, false)); + } + + [Theory] + [MemberData(nameof(StringWithTerminatorsData))] + public async Task ReadBytesTermAsync_ThrowsIsTerminatorNotPresent(byte[] streamContent, + byte[] expected, + byte terminator, + bool terminatorIsPresent, + bool shouldInclude) + { + var kaitaiStreamSUT = Create(streamContent); + + if (terminatorIsPresent) + { + return; + } + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, true)); + } + + [Theory] + [MemberData(nameof(StringWithTerminatorsData))] + public async Task ReadBytesTermAsync_ShouldNotConsumeTerminator(byte[] streamContent, + byte[] expected, + byte terminator, + bool terminatorIsPresent, + bool shouldInclude) + { + //Arrange + var kaitaiStreamSUT = Create(streamContent); + + //Act + await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, false, false); + + //Assert + int amountToConsume = expected.Length; + if (expected.Length > 0 && shouldInclude && terminatorIsPresent) + { + amountToConsume--; + } + + Assert.Equal(amountToConsume, kaitaiStreamSUT.Pos); + } + + [Theory] + [MemberData(nameof(StringWithTerminatorsData))] + public async Task ReadBytesTermAsync_ShouldConsumeTerminator(byte[] streamContent, + byte[] expected, + byte terminator, + bool terminatorIsPresent, + bool shouldInclude) + { + //Arrange + var kaitaiStreamSUT = Create(streamContent); + + //Act + await kaitaiStreamSUT.ReadBytesTermAsync(terminator, shouldInclude, true, false); + + //Assert + int amountToConsume = expected.Length; + if (!shouldInclude && terminatorIsPresent) + { + amountToConsume++; + } + + Assert.Equal(amountToConsume, kaitaiStreamSUT.Pos); + } + + [Fact] + public async Task ReadBytesAsyncLong_LargerThanBufferInvoke_ThrowsArgumentOutOfRangeException() + { + var kaitaiStreamSUT = Create(new byte[0]); + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesAsync(1)); + } + + [Fact] + public async Task ReadBytesAsyncLong_LargerThanInt32Invoke_ThrowsArgumentOutOfRangeException() + { + var kaitaiStreamSUT = Create(new byte[0]); + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesAsync((long) int.MaxValue + 1)); + } + + [Fact] + public async Task ReadBytesAsyncLong_NegativeInvoke_ThrowsArgumentOutOfRangeException() + { + var kaitaiStreamSUT = Create(new byte[0]); + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesAsync(-1)); + } + + [Fact] + public async Task ReadBytesAsyncULong_LargerThanBufferInvoke_ThrowsArgumentOutOfRangeException() + { + var kaitaiStreamSUT = Create(new byte[0]); + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesAsync((ulong) 1)); + } + + [Fact] + public async Task ReadBytesAsyncULong_LargerThanInt32Invoke_ThrowsArgumentOutOfRangeException() + { + var kaitaiStreamSUT = Create(new byte[0]); + + await Assert.ThrowsAsync(async () => + await kaitaiStreamSUT.ReadBytesAsync((ulong) int.MaxValue + 1)); + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadDecimal.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadDecimal.cs deleted file mode 100644 index 9a9487c..0000000 --- a/Kaitai.Struct.Runtime.Async.Tests/ReadDecimal.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Kaitai.Async; -using Xunit; - -namespace Kaitai.Struct.Runtime.Async.Tests -{ - public class ReadDecimal - { - [Theory] - [MemberData(nameof(DecimalData.Decimal4Data), MemberType = typeof(DecimalData))] - public async Task ReadF4beAsync_Test(float expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadF4beAsync()); - } - - [Theory] - [MemberData(nameof(DecimalData.Decimal4Data), MemberType = typeof(DecimalData))] - public async Task ReadF4leAsync_Test(float expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadF4leAsync()); - } - - [Theory] - [MemberData(nameof(DecimalData.Decimal8Data), MemberType = typeof(DecimalData))] - public async Task ReadF8beAsync_Test(double expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadF8beAsync()); - } - - [Theory] - [MemberData(nameof(DecimalData.Decimal8Data), MemberType = typeof(DecimalData))] - public async Task ReadF8leAsync_Test(double expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadF8leAsync()); - } - } -} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadDecimalAsyncTests.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadDecimalAsyncTests.cs new file mode 100644 index 0000000..dc4f652 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async.Tests/ReadDecimalAsyncTests.cs @@ -0,0 +1,60 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kaitai.Async; +using Xunit; + +namespace Kaitai.Struct.Runtime.Async.Tests +{ + public class StreamReadDecimalAsyncTests : ReadDecimalAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderReadDecimalAsyncTests : ReadDecimalAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class ReadDecimalAsyncTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + [Theory] + [MemberData(nameof(DecimalData.Decimal4Data), MemberType = typeof(DecimalData))] + public async Task ReadF4beAsync_Test(float expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadF4beAsync()); + } + + [Theory] + [MemberData(nameof(DecimalData.Decimal4Data), MemberType = typeof(DecimalData))] + public async Task ReadF4leAsync_Test(float expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadF4leAsync()); + } + + [Theory] + [MemberData(nameof(DecimalData.Decimal8Data), MemberType = typeof(DecimalData))] + public async Task ReadF8beAsync_Test(double expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadF8beAsync()); + } + + [Theory] + [MemberData(nameof(DecimalData.Decimal8Data), MemberType = typeof(DecimalData))] + public async Task ReadF8leAsync_Test(double expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadF8leAsync()); + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadSigned.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadSigned.cs deleted file mode 100644 index 0207c42..0000000 --- a/Kaitai.Struct.Runtime.Async.Tests/ReadSigned.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Kaitai.Async; -using Xunit; - -namespace Kaitai.Struct.Runtime.Async.Tests -{ - public class ReadSigned - { - [Theory] - [MemberData(nameof(IntegralData.Integral1Data), MemberType = typeof(IntegralData))] - public async Task ReadS1Async_Test(sbyte expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS1Async()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] - public async Task ReadS2beAsync_Test(short expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS2beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] - public async Task ReadS4beAsync_Test(int expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS4beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] - public async Task ReadS8beAsync_Test(long expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS8beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] - public async Task ReadS2leAsync_Test(short expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS2leAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] - public async Task ReadS4leAsync_Test(int expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS4leAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] - public async Task ReadS8leAsync_Test(long expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal(expected, await kaitaiStreamSUT.ReadS8leAsync()); - } - } -} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadSignedAsyncTests.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadSignedAsyncTests.cs new file mode 100644 index 0000000..64ba08b --- /dev/null +++ b/Kaitai.Struct.Runtime.Async.Tests/ReadSignedAsyncTests.cs @@ -0,0 +1,87 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kaitai.Async; +using Xunit; + +namespace Kaitai.Struct.Runtime.Async.Tests +{ + public class StreamReadSignedAsyncTests : ReadSignedAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderReadSignedAsyncTests : ReadSignedAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class ReadSignedAsyncTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + [Theory] + [MemberData(nameof(IntegralData.Integral1Data), MemberType = typeof(IntegralData))] + public async Task ReadS1Async_Test(sbyte expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS1Async()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] + public async Task ReadS2beAsync_Test(short expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS2beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] + public async Task ReadS4beAsync_Test(int expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS4beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] + public async Task ReadS8beAsync_Test(long expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS8beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] + public async Task ReadS2leAsync_Test(short expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS2leAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] + public async Task ReadS4leAsync_Test(int expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS4leAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] + public async Task ReadS8leAsync_Test(long expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal(expected, await kaitaiStreamSUT.ReadS8leAsync()); + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadUnSigned.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadUnSigned.cs deleted file mode 100644 index 9428724..0000000 --- a/Kaitai.Struct.Runtime.Async.Tests/ReadUnSigned.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Kaitai.Async; -using Xunit; - -namespace Kaitai.Struct.Runtime.Async.Tests -{ - public class ReadUnSigned - { - [Theory] - [MemberData(nameof(IntegralData.Integral1Data), MemberType = typeof(IntegralData))] - public async Task ReadU1Async_Test( /*u*/ sbyte expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal((byte) expected, await kaitaiStreamSUT.ReadU1Async()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] - public async Task ReadU2beAsync_Test( /*u*/ short expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal((ushort) expected, await kaitaiStreamSUT.ReadU2beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] - public async Task ReadU4beAsync_Test( /*u*/ int expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal((uint) expected, await kaitaiStreamSUT.ReadU4beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] - public async Task ReadU8beAsync_Test( /*u*/ long expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent); - - Assert.Equal((ulong) expected, await kaitaiStreamSUT.ReadU8beAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] - public async Task ReadU2leAsync_Test( /*u*/ short expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal((ushort) expected, await kaitaiStreamSUT.ReadU2leAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] - public async Task ReadU4leAsync_Test( /*u*/ int expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal((uint) expected, await kaitaiStreamSUT.ReadU4leAsync()); - } - - [Theory] - [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] - public async Task ReadU8leAsync_Test( /*u*/ long expected, byte[] streamContent) - { - var kaitaiStreamSUT = new KaitaiAsyncStream(streamContent.Reverse().ToArray()); - - Assert.Equal((ulong) expected, await kaitaiStreamSUT.ReadU8leAsync()); - } - } -} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async.Tests/ReadUnSignedAsyncTests.cs b/Kaitai.Struct.Runtime.Async.Tests/ReadUnSignedAsyncTests.cs new file mode 100644 index 0000000..e593961 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async.Tests/ReadUnSignedAsyncTests.cs @@ -0,0 +1,87 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kaitai.Async; +using Xunit; + +namespace Kaitai.Struct.Runtime.Async.Tests +{ + public class StreamReadUnSignedAsyncTests : ReadUnSignedAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => new KaitaiAsyncStream(data); + } + + public class PipeReaderReadUnSignedAsyncTests : ReadUnSignedAsyncTests + { + protected override KaitaiAsyncStream Create(byte[] data) => + new KaitaiAsyncStream(System.IO.Pipelines.PipeReader.Create(new MemoryStream(data))); + } + + public abstract class ReadUnSignedAsyncTests + { + protected abstract KaitaiAsyncStream Create(byte[] data); + + [Theory] + [MemberData(nameof(IntegralData.Integral1Data), MemberType = typeof(IntegralData))] + public async Task ReadU1Async_Test( /*u*/ sbyte expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal((byte) expected, await kaitaiStreamSUT.ReadU1Async()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] + public async Task ReadU2beAsync_Test( /*u*/ short expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal((ushort) expected, await kaitaiStreamSUT.ReadU2beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] + public async Task ReadU4beAsync_Test( /*u*/ int expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal((uint) expected, await kaitaiStreamSUT.ReadU4beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] + public async Task ReadU8beAsync_Test( /*u*/ long expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent); + + Assert.Equal((ulong) expected, await kaitaiStreamSUT.ReadU8beAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral2Data), MemberType = typeof(IntegralData))] + public async Task ReadU2leAsync_Test( /*u*/ short expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal((ushort) expected, await kaitaiStreamSUT.ReadU2leAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral4Data), MemberType = typeof(IntegralData))] + public async Task ReadU4leAsync_Test( /*u*/ int expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal((uint) expected, await kaitaiStreamSUT.ReadU4leAsync()); + } + + [Theory] + [MemberData(nameof(IntegralData.Integral8Data), MemberType = typeof(IntegralData))] + public async Task ReadU8leAsync_Test( /*u*/ long expected, byte[] streamContent) + { + var kaitaiStreamSUT = Create(streamContent.Reverse().ToArray()); + + Assert.Equal((ulong) expected, await kaitaiStreamSUT.ReadU8leAsync()); + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async/Interface/IReaderContext.cs b/Kaitai.Struct.Runtime.Async/Interface/IReaderContext.cs new file mode 100644 index 0000000..1f954d6 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async/Interface/IReaderContext.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Kaitai.Async +{ + public interface IReaderContext + { + long Position { get; } + ValueTask GetSize(); + ValueTask IsEof(); + ValueTask SeekAsync(long position); + ValueTask ReadByteAsync(); + ValueTask ReadBytesAsync(long count); + ValueTask ReadBytesFullAsync(); + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj b/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj index 848a9b2..a842e82 100644 --- a/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj +++ b/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj @@ -11,6 +11,7 @@ + diff --git a/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj.DotSettings b/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj.DotSettings new file mode 100644 index 0000000..ad21594 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async/Kaitai.Struct.Runtime.Async.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async/KaitaiAsyncStream.cs b/Kaitai.Struct.Runtime.Async/KaitaiAsyncStream.cs index 9227593..804a11d 100644 --- a/Kaitai.Struct.Runtime.Async/KaitaiAsyncStream.cs +++ b/Kaitai.Struct.Runtime.Async/KaitaiAsyncStream.cs @@ -1,325 +1,349 @@ using System; using System.Collections.Generic; using System.IO; -using System.IO.Compression; +using System.IO.Pipelines; using System.Threading.Tasks; -using Overby.Extensions.AsyncBinaryReaderWriter; namespace Kaitai.Async { - public partial class KaitaiAsyncStream : KaitaiStreamBase, IKaitaiAsyncStream - { - protected Stream BaseStream; + public class KaitaiAsyncStream : KaitaiStreamBase, IKaitaiAsyncStream + { + private readonly IReaderContext _readerContext; + private ulong _bits; + private int _bitsLeft; - private ulong _bits = 0; - private int _bitsLeft = 0; + #region Constructors - protected AsyncBinaryReader AsyncBinaryReader { get; } + public KaitaiAsyncStream(IReaderContext readerContext) + { + _readerContext = readerContext; + } - #region Constructors + public KaitaiAsyncStream(PipeReader pipeReader) + { + _readerContext = new PipeReaderContext(pipeReader); + } - public KaitaiAsyncStream(Stream stream) - { - BaseStream = stream; - AsyncBinaryReader = new AsyncBinaryReader(BaseStream); + public KaitaiAsyncStream(Stream stream) + { + _readerContext = new StreamReaderContext(stream); + } - } + /// + /// Creates a IKaitaiAsyncStream backed by a file (RO) + /// + public KaitaiAsyncStream(string file) : this(File.Open(file, + FileMode.Open, + FileAccess.Read, + FileShare.Read)) + { + } - /// - /// Creates a IKaitaiAsyncStream backed by a file (RO) - /// - public KaitaiAsyncStream(string file) : this(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - } + /// + ///Creates a IKaitaiAsyncStream backed by a byte buffer + /// + public KaitaiAsyncStream(byte[] bytes) : this(new MemoryStream(bytes)) + { + } - /// - ///Creates a IKaitaiAsyncStream backed by a byte buffer - /// - public KaitaiAsyncStream(byte[] bytes) : this(new MemoryStream(bytes)) - { - } + #endregion - #endregion + #region Stream positioning - #region Stream positioning + public override bool IsEof => + _readerContext.IsEof().GetAwaiter().GetResult() && _bitsLeft == 0; //TODO should be async method - public override bool IsEof => BaseStream.Position >= BaseStream.Length && _bitsLeft == 0; + public virtual async Task SeekAsync(long position) => await _readerContext.SeekAsync(position); - public virtual Task SeekAsync(long position) - { - BaseStream.Seek(position, SeekOrigin.Begin); - return Task.CompletedTask; - } + public override long Pos => _readerContext.Position; - public override long Pos => BaseStream.Position; + public override long Size => ReadBytesFullAsync().GetAwaiter().GetResult().Length; //TODO should be async method - public override long Size => BaseStream.Length; + #endregion - #endregion + #region Integer types - #region Integer types + #region Signed - #region Signed + public async Task ReadS1Async() => (sbyte) await ReadU1Async(); - public async Task ReadS1Async() => await AsyncBinaryReader.ReadSByteAsync(); + #region Big-endian - #region Big-endian + public async Task ReadS2beAsync() => BitConverter.ToInt16(await ReadBytesNormalisedBigEndianAsync(2), 0); - public async Task ReadS2beAsync() => BitConverter.ToInt16(await ReadBytesNormalisedBigEndianAsync(2), 0); + public async Task ReadS4beAsync() => BitConverter.ToInt32(await ReadBytesNormalisedBigEndianAsync(4), 0); - public async Task ReadS4beAsync() => BitConverter.ToInt32(await ReadBytesNormalisedBigEndianAsync(4), 0); + public async Task ReadS8beAsync() => BitConverter.ToInt64(await ReadBytesNormalisedBigEndianAsync(8), 0); - public async Task ReadS8beAsync() => BitConverter.ToInt64(await ReadBytesNormalisedBigEndianAsync(8), 0); + #endregion - #endregion + #region Little-endian - #region Little-endian + public async Task ReadS2leAsync() => BitConverter.ToInt16(await ReadBytesNormalisedLittleEndianAsync(2), 0); - public async Task ReadS2leAsync() => BitConverter.ToInt16(await ReadBytesNormalisedLittleEndianAsync(2), 0); + public async Task ReadS4leAsync() => BitConverter.ToInt32(await ReadBytesNormalisedLittleEndianAsync(4), 0); - public async Task ReadS4leAsync() => BitConverter.ToInt32(await ReadBytesNormalisedLittleEndianAsync(4), 0); + public async Task ReadS8leAsync() => BitConverter.ToInt64(await ReadBytesNormalisedLittleEndianAsync(8), 0); - public async Task ReadS8leAsync() => BitConverter.ToInt64(await ReadBytesNormalisedLittleEndianAsync(8), 0); + #endregion - #endregion + #endregion - #endregion + #region Unsigned - #region Unsigned + public async Task ReadU1Async() => await _readerContext.ReadByteAsync(); - public async Task ReadU1Async() => await AsyncBinaryReader.ReadByteAsync(); + #region Big-endian - #region Big-endian + public async Task ReadU2beAsync() => BitConverter.ToUInt16(await ReadBytesNormalisedBigEndianAsync(2), 0); - public async Task ReadU2beAsync() => BitConverter.ToUInt16(await ReadBytesNormalisedBigEndianAsync(2), 0); + public async Task ReadU4beAsync() => BitConverter.ToUInt32(await ReadBytesNormalisedBigEndianAsync(4), 0); - public async Task ReadU4beAsync() => BitConverter.ToUInt32(await ReadBytesNormalisedBigEndianAsync(4), 0); + public async Task ReadU8beAsync() => BitConverter.ToUInt64(await ReadBytesNormalisedBigEndianAsync(8), 0); - public async Task ReadU8beAsync() => BitConverter.ToUInt64(await ReadBytesNormalisedBigEndianAsync(8), 0); + #endregion - #endregion + #region Little-endian - #region Little-endian + public async Task ReadU2leAsync() => + BitConverter.ToUInt16(await ReadBytesNormalisedLittleEndianAsync(2), 0); - public async Task ReadU2leAsync() => BitConverter.ToUInt16(await ReadBytesNormalisedLittleEndianAsync(2), 0); + public async Task ReadU4leAsync() => BitConverter.ToUInt32(await ReadBytesNormalisedLittleEndianAsync(4), 0); - public async Task ReadU4leAsync() => BitConverter.ToUInt32(await ReadBytesNormalisedLittleEndianAsync(4), 0); + public async Task ReadU8leAsync() => BitConverter.ToUInt64(await ReadBytesNormalisedLittleEndianAsync(8), 0); - public async Task ReadU8leAsync() => BitConverter.ToUInt64(await ReadBytesNormalisedLittleEndianAsync(8), 0); + #endregion - #endregion + #endregion - #endregion + #endregion - #endregion + #region Floating point types - #region Floating point types + #region Big-endian - #region Big-endian + public async Task ReadF4beAsync() => BitConverter.ToSingle(await ReadBytesNormalisedBigEndianAsync(4), 0); - public async Task ReadF4beAsync() => BitConverter.ToSingle(await ReadBytesNormalisedBigEndianAsync(4), 0); + public async Task ReadF8beAsync() => BitConverter.ToDouble(await ReadBytesNormalisedBigEndianAsync(8), 0); - public async Task ReadF8beAsync() => BitConverter.ToDouble(await ReadBytesNormalisedBigEndianAsync(8), 0); + #endregion - #endregion + #region Little-endian - #region Little-endian + public async Task ReadF4leAsync() => BitConverter.ToSingle(await ReadBytesNormalisedLittleEndianAsync(4), 0); - public async Task ReadF4leAsync() => BitConverter.ToSingle(await ReadBytesNormalisedLittleEndianAsync(4), 0); + public async Task ReadF8leAsync() => + BitConverter.ToDouble(await ReadBytesNormalisedLittleEndianAsync(8), 0); - public async Task ReadF8leAsync() => BitConverter.ToDouble(await ReadBytesNormalisedLittleEndianAsync(8), 0); + #endregion - #endregion + #endregion - #endregion + #region Unaligned bit values - #region Unaligned bit values + public override void AlignToByte() + { + _bits = 0; + _bitsLeft = 0; + } - public override void AlignToByte() + public async Task ReadBitsIntAsync(int n) + { + int bitsNeeded = n - _bitsLeft; + if (bitsNeeded > 0) + { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + int bytesNeeded = (bitsNeeded - 1) / 8 + 1; + var buf = await ReadBytesAsync(bytesNeeded); + for (var i = 0; i < buf.Length; i++) { - _bits = 0; - _bitsLeft = 0; + _bits <<= 8; + _bits |= buf[i]; + _bitsLeft += 8; } + } + + // raw mask with required number of 1s, starting from lowest bit + ulong mask = GetMaskOnes(n); + // shift mask to align with highest bits available in "bits" + int shiftBits = _bitsLeft - n; + mask = mask << shiftBits; + // derive reading result + ulong res = (_bits & mask) >> shiftBits; + // clear top bits that we've just read => AND with 1s + _bitsLeft -= n; + mask = GetMaskOnes(_bitsLeft); + _bits &= mask; + + return res; + } - public async Task ReadBitsIntAsync(int n) + //Method ported from algorithm specified @ issue#155 + public async Task ReadBitsIntLeAsync(int n) + { + int bitsNeeded = n - _bitsLeft; + + if (bitsNeeded > 0) + { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + int bytesNeeded = (bitsNeeded - 1) / 8 + 1; + var buf = await ReadBytesAsync(bytesNeeded); + for (var i = 0; i < buf.Length; i++) { - int bitsNeeded = n - _bitsLeft; - if (bitsNeeded > 0) - { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - int bytesNeeded = ((bitsNeeded - 1) / 8) + 1; - byte[] buf = await ReadBytesAsync(bytesNeeded); - for (int i = 0; i < buf.Length; i++) - { - _bits <<= 8; - _bits |= buf[i]; - _bitsLeft += 8; - } - } - - // raw mask with required number of 1s, starting from lowest bit - ulong mask = GetMaskOnes(n); - // shift mask to align with highest bits available in "bits" - int shiftBits = _bitsLeft - n; - mask = mask << shiftBits; - // derive reading result - ulong res = (_bits & mask) >> shiftBits; - // clear top bits that we've just read => AND with 1s - _bitsLeft -= n; - mask = GetMaskOnes(_bitsLeft); - _bits &= mask; - - return res; + ulong v = (ulong) buf[i] << _bitsLeft; + _bits |= v; + _bitsLeft += 8; } + } - //Method ported from algorithm specified @ issue#155 - public async Task ReadBitsIntLeAsync(int n) - { - int bitsNeeded = n - _bitsLeft; - - if (bitsNeeded > 0) - { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - int bytesNeeded = ((bitsNeeded - 1) / 8) + 1; - byte[] buf = await ReadBytesAsync(bytesNeeded); - for (int i = 0; i < buf.Length; i++) - { - ulong v = (ulong)((ulong)buf[i] << _bitsLeft); - _bits |= v; - _bitsLeft += 8; - } - } - - // raw mask with required number of 1s, starting from lowest bit - ulong mask = GetMaskOnes(n); - - // derive reading result - ulong res = (_bits & mask); - - // remove bottom bits that we've just read by shifting - _bits >>= n; - _bitsLeft -= n; - - return res; - } + // raw mask with required number of 1s, starting from lowest bit + ulong mask = GetMaskOnes(n); - #endregion + // derive reading result + ulong res = _bits & mask; - #region Byte arrays + // remove bottom bits that we've just read by shifting + _bits >>= n; + _bitsLeft -= n; - public async Task ReadBytesAsync(long count) - { - if (count < 0 || count > Int32.MaxValue) - throw new ArgumentOutOfRangeException("requested " + count + " bytes, while only non-negative int32 amount of bytes possible"); - byte[] bytes = await AsyncBinaryReader.ReadBytesAsync((int)count); - if (bytes.Length < count) - throw new EndOfStreamException("requested " + count + " bytes, but got only " + bytes.Length + " bytes"); - return bytes; - } + return res; + } - public async Task ReadBytesAsync(ulong count) - { - if (count > Int32.MaxValue) - throw new ArgumentOutOfRangeException("requested " + count + " bytes, while only non-negative int32 amount of bytes possible"); - byte[] bytes = await AsyncBinaryReader.ReadBytesAsync((int)count); - if (bytes.Length < (int)count) - throw new EndOfStreamException("requested " + count + " bytes, but got only " + bytes.Length + " bytes"); - return bytes; - } + #endregion - /// - /// Read bytes from the stream in little endian format and convert them to the endianness of the current platform - /// - /// The number of bytes to read - /// An array of bytes that matches the endianness of the current platform - protected async Task ReadBytesNormalisedLittleEndianAsync(int count) - { - byte[] bytes = await ReadBytesAsync(count); - if (!IsLittleEndian) Array.Reverse(bytes); - return bytes; - } + #region Byte arrays + + public async Task ReadBytesAsync(long count) => await _readerContext.ReadBytesAsync(count); + + public async Task ReadBytesAsync(ulong count) + { + if (count > int.MaxValue) + { + throw new ArgumentOutOfRangeException( + $"requested {count} bytes, while only non-negative int32 amount of bytes possible"); + } + + return await ReadBytesAsync((long) count); + } + + /// + /// Read bytes from the stream in little endian format and convert them to the endianness of the current platform + /// + /// The number of bytes to read + /// An array of bytes that matches the endianness of the current platform + protected async Task ReadBytesNormalisedLittleEndianAsync(int count) + { + var bytes = await ReadBytesAsync(count); + if (!IsLittleEndian) + { + Array.Reverse(bytes); + } + + return bytes; + } + + /// + /// Read bytes from the stream in big endian format and convert them to the endianness of the current platform + /// + /// The number of bytes to read + /// An array of bytes that matches the endianness of the current platform + protected async Task ReadBytesNormalisedBigEndianAsync(int count) + { + var bytes = await ReadBytesAsync(count); + if (IsLittleEndian) + { + Array.Reverse(bytes); + } + + return bytes; + } - /// - /// Read bytes from the stream in big endian format and convert them to the endianness of the current platform - /// - /// The number of bytes to read - /// An array of bytes that matches the endianness of the current platform - protected async Task ReadBytesNormalisedBigEndianAsync(int count) + /// + /// Read all the remaining bytes from the stream until the end is reached + /// + /// + public virtual async Task ReadBytesFullAsync() => await _readerContext.ReadBytesFullAsync(); + + /// + /// Read a terminated string from the stream + /// + /// The string terminator value + /// True to include the terminator in the returned string + /// True to consume the terminator byte before returning + /// True to throw an error when the EOS was reached before the terminator + /// + public async Task ReadBytesTermAsync(byte terminator, + bool includeTerminator, + bool consumeTerminator, + bool eosError) + { + var bytes = new List(); + while (true) + { + if (IsEof) { - byte[] bytes = await ReadBytesAsync(count); - if (IsLittleEndian) Array.Reverse(bytes); - return bytes; + if (eosError) + { + throw new EndOfStreamException( + $"End of stream reached, but no terminator `{terminator}` found"); + } + + break; } - /// - /// Read all the remaining bytes from the stream until the end is reached - /// - /// - /// //TODO Handle asynchronously, BaseStream.Length is troublesome - public virtual async Task ReadBytesFullAsync() => await ReadBytesAsync(BaseStream.Length - BaseStream.Position); - - /// - /// Read a terminated string from the stream - /// - /// The string terminator value - /// True to include the terminator in the returned string - /// True to consume the terminator byte before returning - /// True to throw an error when the EOS was reached before the terminator - /// - public async Task ReadBytesTermAsync(byte terminator, bool includeTerminator, bool consumeTerminator, bool eosError) + byte b = await ReadU1Async(); + if (b == terminator) { - List bytes = new List(); - while (true) - { - if (IsEof) - { - if (eosError) throw new EndOfStreamException( - $"End of stream reached, but no terminator `{terminator}` found"); - break; - } - - byte b = await AsyncBinaryReader.ReadByteAsync(); - if (b == terminator) - { - if (includeTerminator) bytes.Add(b); - if (!consumeTerminator) await SeekAsync(Pos - 1); - break; - } - bytes.Add(b); - } - return bytes.ToArray(); + if (includeTerminator) + { + bytes.Add(b); + } + + if (!consumeTerminator) + { + await SeekAsync(Pos - 1); + } + + break; } - /// - /// Read a specific set of bytes and assert that they are the same as an expected result - /// - /// The expected result - /// - public async Task EnsureFixedContentsAsync(byte[] expected) + bytes.Add(b); + } + + return bytes.ToArray(); + } + + /// + /// Read a specific set of bytes and assert that they are the same as an expected result + /// + /// The expected result + /// + public async Task EnsureFixedContentsAsync(byte[] expected) + { + var bytes = await ReadBytesAsync(expected.Length); + + if (bytes.Length != expected.Length) //TODO Is this necessary? + { + throw new Exception( + $"Expected bytes: {Convert.ToBase64String(expected)} ({expected.Length} bytes), Instead got: {Convert.ToBase64String(bytes)} ({bytes.Length} bytes)"); + } + + for (var i = 0; i < bytes.Length; i++) + { + if (bytes[i] != expected[i]) { - byte[] bytes = await ReadBytesAsync(expected.Length); - - if (bytes.Length != expected.Length) //TODO Is this necessary? - { - throw new Exception( - $"Expected bytes: {Convert.ToBase64String(expected)} ({expected.Length} bytes), Instead got: {Convert.ToBase64String(bytes)} ({bytes.Length} bytes)"); - } - for (int i = 0; i < bytes.Length; i++) - { - if (bytes[i] != expected[i]) - { - throw new Exception( - $"Expected bytes: {Convert.ToBase64String(expected)} ({expected.Length} bytes), Instead got: {Convert.ToBase64String(bytes)} ({bytes.Length} bytes)"); - } - } - - return bytes; + throw new Exception( + $"Expected bytes: {Convert.ToBase64String(expected)} ({expected.Length} bytes), Instead got: {Convert.ToBase64String(bytes)} ({bytes.Length} bytes)"); } + } - #endregion + return bytes; } -} + + #endregion + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async/ReaderContext/PipeReaderContext.cs b/Kaitai.Struct.Runtime.Async/ReaderContext/PipeReaderContext.cs new file mode 100644 index 0000000..5522c73 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async/ReaderContext/PipeReaderContext.cs @@ -0,0 +1,148 @@ +using System; +using System.Buffers; +using System.IO; +using System.IO.Pipelines; +using System.Threading.Tasks; + +namespace Kaitai.Async +{ + internal class PipeReaderContext : IReaderContext + { + private readonly PipeReader _pipeReader; + private ReadResult _readResult; + + public PipeReaderContext(PipeReader pipeReader) + { + _pipeReader = pipeReader; + } + + private long RemainingBytesInReadResult => _readResult.Buffer.Length - Position; + + public long Position { get; private set; } + + public async ValueTask GetSize() + { + await FillReadResultBufferToTheEnd(); + + return _readResult.Buffer.Length; + } + + public async ValueTask IsEof() + { + if (_readResult.Equals(default(ReadResult)) || + Position >= _readResult.Buffer.Length && !_readResult.IsCompleted) + { + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.GetPosition(Position)); + _readResult = await _pipeReader.ReadAsync(); + } + + return Position >= _readResult.Buffer.Length && _readResult.IsCompleted; + } + + + public async ValueTask SeekAsync(long position) + { + if (position <= Position) + { + Position = position; + } + else + { + while (_readResult.Buffer.Length < position && !_readResult.IsCompleted) + { + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.End); + _readResult = await _pipeReader.ReadAsync(); + } + + if (_readResult.Buffer.Length <= position) + { + Position = position; + return; + } + + if (_readResult.IsCompleted) + { + throw new EndOfStreamException( + $"requested {position} bytes, but got only {RemainingBytesInReadResult} bytes"); + } + } + } + + public async ValueTask ReadByteAsync() + { + var value = byte.MinValue; + while (!TryReadByte(out value) && !_readResult.IsCompleted) + { + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.GetPosition(Position)); + _readResult = await _pipeReader.ReadAsync(); + } + + Position += 1; + return value; + + bool TryReadByte(out byte readValue) + { + var sequenceReader = new SequenceReader(_readResult.Buffer); + sequenceReader.Advance(Position); + return sequenceReader.TryRead(out readValue); + } + } + + public async ValueTask ReadBytesAsync(long count) + { + if (count < 0 || count > int.MaxValue) + { + throw new ArgumentOutOfRangeException( + $"requested {count} bytes, while only non-negative int32 amount of bytes possible"); + } + + byte[] value = null; + + while (!TryRead(out value, count)) + { + if (_readResult.IsCompleted) + { + throw new EndOfStreamException( + $"requested {count} bytes, but got only {RemainingBytesInReadResult} bytes"); + } + + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.GetPosition(Position)); + _readResult = await _pipeReader.ReadAsync(); + } + + Position += count; + return value; + + bool TryRead(out byte[] readBytes, long readBytesCount) + { + if (RemainingBytesInReadResult < readBytesCount) + { + readBytes = null; + return false; + } + + readBytes = _readResult.Buffer.Slice(Position, readBytesCount).ToArray(); + return true; + } + } + + public virtual async ValueTask ReadBytesFullAsync() + { + await FillReadResultBufferToTheEnd(); + + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.End); + var value = _readResult.Buffer.Slice(Position, _readResult.Buffer.End).ToArray(); + Position += value.Length; + return value; + } + + private async Task FillReadResultBufferToTheEnd() + { + while (!_readResult.IsCompleted) + { + _pipeReader.AdvanceTo(_readResult.Buffer.Start, _readResult.Buffer.End); + _readResult = await _pipeReader.ReadAsync(); + } + } + } +} \ No newline at end of file diff --git a/Kaitai.Struct.Runtime.Async/ReaderContext/StreamReaderContext.cs b/Kaitai.Struct.Runtime.Async/ReaderContext/StreamReaderContext.cs new file mode 100644 index 0000000..2a0cd05 --- /dev/null +++ b/Kaitai.Struct.Runtime.Async/ReaderContext/StreamReaderContext.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Overby.Extensions.AsyncBinaryReaderWriter; + +namespace Kaitai.Async +{ + internal class StreamReaderContext : IReaderContext + { + private readonly Stream _baseStream; + + public StreamReaderContext(Stream stream) + { + _baseStream = stream; + AsyncBinaryReader = new AsyncBinaryReader(_baseStream); + } + + protected AsyncBinaryReader AsyncBinaryReader { get; } + public long Position => _baseStream.Position; + public ValueTask GetSize() => new ValueTask(_baseStream.Length); + + public ValueTask IsEof() => + new ValueTask(_baseStream.Position >= _baseStream.Length); + + public ValueTask SeekAsync(long position) + { + _baseStream.Seek(position, SeekOrigin.Begin); + return new ValueTask(); + } + + public async ValueTask ReadByteAsync() => (byte) await AsyncBinaryReader.ReadSByteAsync(); + + public async ValueTask ReadBytesAsync(long count) + { + if (count < 0 || count > int.MaxValue) + { + throw new ArgumentOutOfRangeException( + $"requested {count} bytes, while only non-negative int32 amount of bytes possible"); + } + + var bytes = await AsyncBinaryReader.ReadBytesAsync((int) count); + if (bytes.Length < count) + { + throw new EndOfStreamException($"requested {count} bytes, but got only {bytes.Length} bytes"); + } + + return bytes; + } + + public async ValueTask ReadBytesFullAsync() => + await ReadBytesAsync(_baseStream.Length - _baseStream.Position); + } +} \ No newline at end of file