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);
}
}
}
}
}