// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Marshal.cs // Author(s) : Natalia Portillo // // Component : Helpers. // // --[ Description ] ---------------------------------------------------------- // // Provides marshalling for binary data. // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Aaru.Helpers; /// Provides methods to marshal binary data into C# structs [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class Marshal { /// Returns the size of an unmanaged type in bytes. /// The type whose size is to be returned. /// The size, in bytes, of the type that is specified by the generic type parameter. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SizeOf() => System.Runtime.InteropServices.Marshal.SizeOf(); /// Marshal little-endian binary data to a structure /// Byte array containing the binary data /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructureLittleEndian(byte[] bytes) where T : struct { var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); var str = (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ?? default(T)); ptr.Free(); return str; } /// Marshal little-endian binary data to a structure /// Byte array containing the binary data /// Start on the array where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructureLittleEndian(byte[] bytes, int start, int length) where T : struct { Span span = bytes; return ByteArrayToStructureLittleEndian(span.Slice(start, length).ToArray()); } /// /// Marshal big-endian binary data to a structure using compile-time generated swap method. /// /// Byte array containing the binary data /// Type of the structure to marshal (must be marked with [SwapEndian] attribute) /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct, ISwapEndian { T str = ByteArrayToStructureLittleEndian(bytes); return str.SwapEndian(); } /// /// Marshal big-endian binary data to a structure using compile-time generated swap method. /// /// Byte array containing the binary data /// Start on the array where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal (must be marked with [SwapEndian] attribute) /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructureBigEndian(byte[] bytes, int start, int length) where T : struct, ISwapEndian { Span span = bytes; return ByteArrayToStructureBigEndian(span.Slice(start, length).ToArray()); } /// Marshal PDP-11 binary data to a structure /// Byte array containing the binary data /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructurePdpEndian(byte[] bytes) where T : struct, ISwapPdpEndian { T str = ByteArrayToStructureLittleEndian(bytes); return str.SwapPdpEndian(); } /// Marshal PDP-11 binary data to a structure /// Byte array containing the binary data /// Start on the array where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ByteArrayToStructurePdpEndian(byte[] bytes, int start, int length) where T : struct, ISwapPdpEndian { Span span = bytes; return ByteArrayToStructurePdpEndian(span.Slice(start, length).ToArray()); } /// /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this /// method will crash. /// /// Byte array containing the binary data /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructureLittleEndian(ReadOnlySpan bytes) where T : struct => MemoryMarshal.Read(bytes); /// /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this /// method will crash. /// /// Byte span containing the binary data /// Start on the span where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructureLittleEndian(ReadOnlySpan bytes, int start, int length) where T : struct => MemoryMarshal.Read(bytes.Slice(start, length)); /// /// Marshal big-endian binary data to a structure using compile-time generated swap method. /// If the structure type contains any non value type, this method will crash. /// /// Byte array containing the binary data /// Type of the structure to marshal (must be marked with [SwapEndian] attribute) /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructureBigEndian(ReadOnlySpan bytes) where T : struct, ISwapEndian { T str = SpanToStructureLittleEndian(bytes); return str.SwapEndian(); } /// /// Marshal big-endian binary data to a structure using compile-time generated swap method. /// If the structure type contains any non value type, this method will crash. /// /// Byte span containing the binary data /// Start on the span where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal (must be marked with [SwapEndian] attribute) /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructureBigEndian(ReadOnlySpan bytes, int start, int length) where T : struct, ISwapEndian { T str = SpanToStructureLittleEndian(bytes.Slice(start, length)); return str.SwapEndian(); } /// /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will /// crash. /// /// Byte array containing the binary data /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructurePdpEndian(ReadOnlySpan bytes) where T : struct, ISwapPdpEndian { T str = SpanToStructureLittleEndian(bytes); return str.SwapPdpEndian(); } /// /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will /// crash. /// /// Byte array containing the binary data /// Start on the span where the structure begins /// Length of the structure in bytes /// Type of the structure to marshal /// The binary data marshalled in a structure with the specified type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T SpanToStructurePdpEndian(ReadOnlySpan bytes, int start, int length) where T : struct, ISwapPdpEndian { T str = SpanToStructureLittleEndian(bytes.Slice(start, length)); return str.SwapPdpEndian(); } /// Marshal a structure to little-endian binary data /// The structure you want to marshal to binary /// Type of the structure to marshal /// The byte array representing the given structure [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] StructureToByteArrayLittleEndian(T str) where T : struct { var buf = new byte[SizeOf()]; var ptr = GCHandle.Alloc(buf, GCHandleType.Pinned); System.Runtime.InteropServices.Marshal.StructureToPtr(str, ptr.AddrOfPinnedObject(), false); ptr.Free(); return buf; } /// Marshal a structure to little-endian binary data /// The structure you want to marshal to binary /// Type of the structure to marshal /// The byte array representing the given structure [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] StructureToByteArrayBigEndian(T str) where T : struct, ISwapEndian => StructureToByteArrayLittleEndian(str.SwapEndian()); /// Converts a hexadecimal string into a byte array /// Hexadecimal string /// Resulting byte array /// Number of output bytes processed public static int ConvertFromHexAscii(string hex, out byte[] outBuf) { outBuf = null; if(hex is null or "") return -1; var off = 0; if(hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) off = 2; outBuf = new byte[(hex.Length - off) / 2]; var count = 0; for(int i = off; i < hex.Length; i += 2) { char c = hex[i]; if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f') break; c -= c switch { >= 'a' and <= 'f' => '\u0057', >= 'A' and <= 'F' => '\u0037', _ => '\u0030' }; outBuf[(i - off) / 2] = (byte)(c << 4); c = hex[i + 1]; if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f') break; c -= c switch { >= 'a' and <= 'f' => '\u0057', >= 'A' and <= 'F' => '\u0037', _ => '\u0030' }; outBuf[(i - off) / 2] += (byte)c; count++; } return count; } }