From 87d1dfe2664a6f7ab0387c91600dc95aa09f7534 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 23 Apr 2024 10:51:54 -0400 Subject: [PATCH] Add ReadType extensions --- .../Extensions/BinaryReaderExtensionsTests.cs | 40 ++++++++++++++++++- .../Extensions/ByteArrayExtensionsTests.cs | 38 +++++++++++++++++- .../Extensions/StreamExtensionsTests.cs | 38 +++++++++++++++++- .../Extensions/TestStructExplicit.cs | 20 ++++++++++ .../Extensions/TestStructSequential.cs | 16 ++++++++ .../Extensions/BinaryReaderExtensions.cs | 14 +++++++ .../Extensions/ByteArrayExtensions.cs | 14 +++++++ SabreTools.IO/Extensions/StreamExtensions.cs | 14 +++++++ 8 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 SabreTools.IO.Test/Extensions/TestStructExplicit.cs create mode 100644 SabreTools.IO.Test/Extensions/TestStructSequential.cs diff --git a/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs b/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs index cf9e948..f45beae 100644 --- a/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs @@ -173,7 +173,7 @@ namespace SabreTools.IO.Test.Extensions } [Fact] - public void ReadGuidBigEndian() + public void ReadGuidBigEndianTest() { var stream = new MemoryStream(_bytes); var br = new BinaryReader(stream); @@ -226,6 +226,44 @@ namespace SabreTools.IO.Test.Extensions } #endif + [Fact] + public void ReadTypeExplicitTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = new TestStructExplicit + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0504, + FourthValue = 0x0706, + }; + var read = br.ReadType(); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + + [Fact] + public void ReadTypeSequentialTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = new TestStructSequential + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0908, + FourthValue = 0x0B0A, + }; + var read = br.ReadType(); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + // TODO: Add byte[], char[] tests // TODO: Add decimal tests // TODO: Add string reading tests diff --git a/SabreTools.IO.Test/Extensions/ByteArrayExtensionsTests.cs b/SabreTools.IO.Test/Extensions/ByteArrayExtensionsTests.cs index 2777d8c..cbaa90a 100644 --- a/SabreTools.IO.Test/Extensions/ByteArrayExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/ByteArrayExtensionsTests.cs @@ -196,7 +196,7 @@ namespace SabreTools.IO.Test.Extensions } [Fact] - public void ReadGuidBigEndian() + public void ReadGuidBigEndianTest() { int offset = 0; var expected = new Guid(_bytes.Reverse().ToArray()); @@ -244,6 +244,42 @@ namespace SabreTools.IO.Test.Extensions } #endif + [Fact] + public void ReadTypeExplicitTest() + { + int offset = 0; + var expected = new TestStructExplicit + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0504, + FourthValue = 0x0706, + }; + var read = _bytes.ReadType(ref offset); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + + [Fact] + public void ReadTypeSequentialTest() + { + int offset = 0; + var expected = new TestStructSequential + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0908, + FourthValue = 0x0B0A, + }; + var read = _bytes.ReadType(ref offset); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + // TODO: Add decimal tests // TODO: Add string reading tests } diff --git a/SabreTools.IO.Test/Extensions/StreamExtensionsTests.cs b/SabreTools.IO.Test/Extensions/StreamExtensionsTests.cs index 2754b05..eb2084f 100644 --- a/SabreTools.IO.Test/Extensions/StreamExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/StreamExtensionsTests.cs @@ -190,7 +190,7 @@ namespace SabreTools.IO.Test.Extensions } [Fact] - public void ReadGuidBigEndian() + public void ReadGuidBigEndianTest() { var stream = new MemoryStream(_bytes); var expected = new Guid(_bytes.Reverse().ToArray()); @@ -238,6 +238,42 @@ namespace SabreTools.IO.Test.Extensions } #endif + [Fact] + public void ReadTypeExplicitTest() + { + var stream = new MemoryStream(_bytes); + var expected = new TestStructExplicit + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0504, + FourthValue = 0x0706, + }; + var read = stream.ReadType(); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + + [Fact] + public void ReadTypeSequentialTest() + { + var stream = new MemoryStream(_bytes); + var expected = new TestStructSequential + { + FirstValue = 0x03020100, + SecondValue = 0x07060504, + ThirdValue = 0x0908, + FourthValue = 0x0B0A, + }; + var read = stream.ReadType(); + Assert.Equal(expected.FirstValue, read.FirstValue); + Assert.Equal(expected.SecondValue, read.SecondValue); + Assert.Equal(expected.ThirdValue, read.ThirdValue); + Assert.Equal(expected.FourthValue, read.FourthValue); + } + // TODO: Add decimal tests // TODO: Add string reading tests } diff --git a/SabreTools.IO.Test/Extensions/TestStructExplicit.cs b/SabreTools.IO.Test/Extensions/TestStructExplicit.cs new file mode 100644 index 0000000..33617c9 --- /dev/null +++ b/SabreTools.IO.Test/Extensions/TestStructExplicit.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.IO.Test.Extensions +{ + [StructLayout(LayoutKind.Explicit)] + internal struct TestStructExplicit + { + [FieldOffset(0)] + public int FirstValue; + + [FieldOffset(4)] + public int SecondValue; + + [FieldOffset(4)] + public ushort ThirdValue; + + [FieldOffset(6)] + public short FourthValue; + } +} diff --git a/SabreTools.IO.Test/Extensions/TestStructSequential.cs b/SabreTools.IO.Test/Extensions/TestStructSequential.cs new file mode 100644 index 0000000..50016e0 --- /dev/null +++ b/SabreTools.IO.Test/Extensions/TestStructSequential.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace SabreTools.IO.Test.Extensions +{ + [StructLayout(LayoutKind.Sequential)] + internal struct TestStructSequential + { + public int FirstValue; + + public int SecondValue; + + public ushort ThirdValue; + + public short FourthValue; + } +} diff --git a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs index c9e4623..79a8651 100644 --- a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs +++ b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; namespace SabreTools.IO.Extensions { @@ -193,5 +194,18 @@ namespace SabreTools.IO.Extensions return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8)); } #endif + + /// + /// Read a from the underlying stream + /// + public static T? ReadType(this BinaryReader reader) + { + int typeSize = Marshal.SizeOf(typeof(T)); + byte[] buffer = reader.ReadBytes(typeSize); + var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + return data; + } } } diff --git a/SabreTools.IO/Extensions/ByteArrayExtensions.cs b/SabreTools.IO/Extensions/ByteArrayExtensions.cs index 808350b..b44826c 100644 --- a/SabreTools.IO/Extensions/ByteArrayExtensions.cs +++ b/SabreTools.IO/Extensions/ByteArrayExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; namespace SabreTools.IO.Extensions @@ -385,6 +386,19 @@ namespace SabreTools.IO.Extensions return new string([.. keyChars]).TrimEnd(); } + /// + /// Read a from the underlying stream + /// + public static T? ReadType(this byte[] content, ref int offset) + { + int typeSize = Marshal.SizeOf(typeof(T)); + byte[] buffer = ReadToBuffer(content, ref offset, typeSize); + var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + return data; + } + /// /// Read a number of bytes from the current byte array to a buffer /// diff --git a/SabreTools.IO/Extensions/StreamExtensions.cs b/SabreTools.IO/Extensions/StreamExtensions.cs index 7b78b49..35eb865 100644 --- a/SabreTools.IO/Extensions/StreamExtensions.cs +++ b/SabreTools.IO/Extensions/StreamExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace SabreTools.IO.Extensions @@ -367,6 +368,19 @@ namespace SabreTools.IO.Extensions return line.TrimEnd(); } + /// + /// Read a from the stream + /// + public static T? ReadType(this Stream stream) + { + int typeSize = Marshal.SizeOf(typeof(T)); + byte[] buffer = ReadToBuffer(stream, typeSize); + var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + return data; + } + /// /// Seek to a specific point in the stream, if possible ///