using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; namespace Packaging.Targets { /// /// Provides extension methods for the class. /// internal static class StreamExtensions { /// /// Reads a struct from the stream. /// /// /// The type of the struct to read. /// /// /// The to read the struct from. /// /// /// A new struct, with the data read /// from the stream. /// public static T ReadStruct(this Stream stream) where T : struct { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } var size = Marshal.SizeOf(); var data = new byte[size]; var totalRead = 0; while (totalRead < size) { var read = stream.Read(data, totalRead, size); if (read == 0) { break; } totalRead += read; } if (totalRead < size) { throw new InvalidOperationException("Not enough data"); } // Convert from network byte order (big endian) to little endian. RespectEndianness(data); var pinnedData = GCHandle.Alloc(data, GCHandleType.Pinned); try { var ptr = pinnedData.AddrOfPinnedObject(); return Marshal.PtrToStructure(ptr); } finally { pinnedData.Free(); } } /// /// Writes a struct to a stream. /// /// /// The type of the struct to write. /// /// /// The stream to which to write the struct. /// /// /// The struct to write to the stram. /// public static int WriteStruct(this Stream stream, T data) where T : struct { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } byte[] bytes = new byte[Marshal.SizeOf()]; GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); try { Marshal.StructureToPtr(data, handle.AddrOfPinnedObject(), true); } finally { handle.Free(); } RespectEndianness(bytes); stream.Write(bytes, 0, bytes.Length); return bytes.Length; } /// /// Writes a to a using big-endian encoding. /// /// /// The to which to write the value. /// /// /// The value to write to the stream. /// public static void WriteBE(this Stream stream, short value) { var data = BitConverter.GetBytes(value); Array.Reverse(data); stream.Write(data, 0, data.Length); } /// /// Writes a to a using big-endian encoding. /// /// /// The to which to write the value. /// /// /// The value to write to the stream. /// public static void WriteBE(this Stream stream, int value) { var data = BitConverter.GetBytes(value); Array.Reverse(data); stream.Write(data, 0, data.Length); } /// /// Writes a to a using big-endian encoding. /// /// /// The to which to write the value. /// /// /// The value to write to the stream. /// public static void WriteBE(this Stream stream, long value) { var data = BitConverter.GetBytes(value); Array.Reverse(data); stream.Write(data, 0, data.Length); } private static void RespectEndianness(byte[] data) { foreach (var field in typeof(T).GetTypeInfo().DeclaredFields) { int length = 0; var type = field.FieldType; if (type.GetTypeInfo().IsEnum) { type = Enum.GetUnderlyingType(type); } if (type == typeof(short) || type == typeof(ushort)) { length = 2; } else if (type == typeof(int) || type == typeof(uint)) { length = 4; } if (length > 0) { var offset = Marshal.OffsetOf(field.Name).ToInt32(); Array.Reverse(data, offset, length); } } } } }