From 5a2e382792a9aa9d347e478e7105185e9250c78a Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 21 Oct 2025 01:59:58 +0100 Subject: [PATCH] Add big endian structure swapping source generator. --- .../Attributes/SwapEndianAttribute.cs | 39 ++ Aaru.Generators/Aaru.Generators.csproj | 6 + Aaru.Generators/PluginRegisterGenerator.cs | 70 ++-- Aaru.Generators/SwapEndianGenerator.cs | 388 ++++++++++++++++++ Aaru.Helpers/ISwapEndian.cs | 42 ++ 5 files changed, 520 insertions(+), 25 deletions(-) create mode 100644 Aaru.CommonTypes/Attributes/SwapEndianAttribute.cs create mode 100644 Aaru.Generators/SwapEndianGenerator.cs create mode 100644 Aaru.Helpers/ISwapEndian.cs diff --git a/Aaru.CommonTypes/Attributes/SwapEndianAttribute.cs b/Aaru.CommonTypes/Attributes/SwapEndianAttribute.cs new file mode 100644 index 000000000..3e08ee6e1 --- /dev/null +++ b/Aaru.CommonTypes/Attributes/SwapEndianAttribute.cs @@ -0,0 +1,39 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : SwapEndianAttribute.cs +// Author(s) : Natalia Portillo +// +// Component : Aaru.Generators. +// +// --[ 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 +// ****************************************************************************/ + +// ReSharper disable once CheckNamespace + +using System; + +namespace Aaru.CommonTypes.Attributes; + +/// +/// Marks a struct for automatic generation of endianness swapping methods. +/// +[AttributeUsage(AttributeTargets.Struct)] +public sealed class SwapEndianAttribute : Attribute {} \ No newline at end of file diff --git a/Aaru.Generators/Aaru.Generators.csproj b/Aaru.Generators/Aaru.Generators.csproj index 540341b4c..28930aca5 100644 --- a/Aaru.Generators/Aaru.Generators.csproj +++ b/Aaru.Generators/Aaru.Generators.csproj @@ -14,4 +14,10 @@ + + + + LICENSE.LGPL + + diff --git a/Aaru.Generators/PluginRegisterGenerator.cs b/Aaru.Generators/PluginRegisterGenerator.cs index a0a87caae..18352d489 100644 --- a/Aaru.Generators/PluginRegisterGenerator.cs +++ b/Aaru.Generators/PluginRegisterGenerator.cs @@ -1,3 +1,31 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : PluginRegisterGenerator.cs +// Author(s) : Natalia Portillo +// +// Component : Aaru.Generators. +// +// --[ 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.Collections.Generic; using System.Linq; using System.Text; @@ -33,7 +61,7 @@ public class PluginRegisterGenerator : ISourceGenerator if(pluginRegister == null) return; - string @namespace = + var @namespace = (pluginRegister.Ancestors().FirstOrDefault(x => x is FileScopedNamespaceDeclarationSyntax) as FileScopedNamespaceDeclarationSyntax)?.Name.ToString(); @@ -339,49 +367,43 @@ public class PluginRegisterGenerator : ISourceGenerator .ValueText == "IArchive") == true) - { - if(!Archives.Contains(plugin.Identifier.Text)) Archives.Add(plugin.Identifier.Text); - } + if(!Archives.Contains(plugin.Identifier.Text)) + Archives.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IChecksum") == true) - { - if(!Checksums.Contains(plugin.Identifier.Text)) Checksums.Add(plugin.Identifier.Text); - } + if(!Checksums.Contains(plugin.Identifier.Text)) + Checksums.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IFilesystem") == true) - { - if(!FileSystems.Contains(plugin.Identifier.Text)) FileSystems.Add(plugin.Identifier.Text); - } + if(!FileSystems.Contains(plugin.Identifier.Text)) + FileSystems.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IFilter") == true) - { - if(!Filters.Contains(plugin.Identifier.Text)) Filters.Add(plugin.Identifier.Text); - } + if(!Filters.Contains(plugin.Identifier.Text)) + Filters.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IFloppyImage") == true) - { - if(!FloppyImagePlugins.Contains(plugin.Identifier.Text)) FloppyImagePlugins.Add(plugin.Identifier.Text); - } + if(!FloppyImagePlugins.Contains(plugin.Identifier.Text)) + FloppyImagePlugins.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IFluxImage") == true) - { - if(!FluxImagePlugins.Contains(plugin.Identifier.Text)) FluxImagePlugins.Add(plugin.Identifier.Text); - } + if(!FluxImagePlugins.Contains(plugin.Identifier.Text)) + FluxImagePlugins.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier @@ -391,17 +413,15 @@ public class PluginRegisterGenerator : ISourceGenerator or "ITapeImage" or "IFluxImage") == true) - { - if(!MediaImagePlugins.Contains(plugin.Identifier.Text)) MediaImagePlugins.Add(plugin.Identifier.Text); - } + if(!MediaImagePlugins.Contains(plugin.Identifier.Text)) + MediaImagePlugins.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == "IPartition") == true) - { - if(!PartitionPlugins.Contains(plugin.Identifier.Text)) PartitionPlugins.Add(plugin.Identifier.Text); - } + if(!PartitionPlugins.Contains(plugin.Identifier.Text)) + PartitionPlugins.Add(plugin.Identifier.Text); if(plugin.BaseList?.Types.Any(t => ((t as SimpleBaseTypeSyntax)?.Type as IdentifierNameSyntax)?.Identifier .ValueText == diff --git a/Aaru.Generators/SwapEndianGenerator.cs b/Aaru.Generators/SwapEndianGenerator.cs new file mode 100644 index 000000000..6f79d8fa9 --- /dev/null +++ b/Aaru.Generators/SwapEndianGenerator.cs @@ -0,0 +1,388 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : SwapEndianGenerator.cs +// Author(s) : Natalia Portillo +// +// Component : Aaru.Generators. +// +// --[ 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.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Aaru.Generators; + +[Generator] +public class SwapEndianGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Find all structs with the [SwapEndian] attribute and collect semantic info + // The attribute is defined in Aaru.CommonTypes.Attributes namespace + IncrementalValuesProvider<(StructDeclarationSyntax structDecl, + List<(string fieldName, ITypeSymbol typeSymbol, string typeName)> fieldTypes)?> structDeclarations = + context.SyntaxProvider + .CreateSyntaxProvider(static (s, _) => IsSyntaxTargetForGeneration(s), + static (ctx, _) => GetSemanticTargetForGeneration(ctx)) + .Where(static m => m.HasValue); + + context.RegisterSourceOutput(structDeclarations, static (spc, source) => Execute(source.Value, spc)); + } + + private static bool IsSyntaxTargetForGeneration(SyntaxNode node) => + node is StructDeclarationSyntax { AttributeLists.Count: > 0 }; + + private static (StructDeclarationSyntax structDecl, + List<(string fieldName, ITypeSymbol typeSymbol, string typeName)> fieldTypes)? GetSemanticTargetForGeneration( + GeneratorSyntaxContext context) + { + var structDeclaration = (StructDeclarationSyntax)context.Node; + + foreach(AttributeListSyntax attributeList in structDeclaration.AttributeLists) + { + foreach(AttributeSyntax attribute in attributeList.Attributes) + { + ISymbol symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol; + + if(symbol is not IMethodSymbol attributeSymbol) continue; + + INamedTypeSymbol attributeContainingType = attributeSymbol.ContainingType; + string fullName = attributeContainingType.ToDisplayString(); + + if(fullName != "Aaru.CommonTypes.Attributes.SwapEndianAttribute") continue; + + // Collect field type information here where we have access to semantic model + + IEnumerable fields = structDeclaration.Members.OfType(); + + var fieldTypes = (from field in fields + let typeInfo = context.SemanticModel.GetTypeInfo(field.Declaration.Type) + let typeSymbol = typeInfo.Type + let typeName = field.Declaration.Type.ToString() + from variable in field.Declaration.Variables + select (variable.Identifier.Text, typeSymbol, typeName)).ToList(); + + return (structDeclaration, fieldTypes); + } + } + + return null; + } + + private static void Execute( + (StructDeclarationSyntax structDeclaration, List<(string fieldName, ITypeSymbol typeSymbol, string typeName)> + fieldTypes) source, SourceProductionContext context) + { + if(source.structDeclaration == null) return; + + string structName = source.structDeclaration.Identifier.Text; + string namespaceName = GetNamespace(source.structDeclaration); + List<(string Name, string Keyword)> containingTypes = GetContainingTypes(source.structDeclaration); + + string generatedSource = + GenerateSwapEndianMethod(structName, namespaceName, containingTypes, source.fieldTypes); + + context.AddSource($"{structName}_SwapEndian.g.cs", SourceText.From(generatedSource, Encoding.UTF8)); + } + + private static string GetNamespace(SyntaxNode syntax) + { + // Try file-scoped namespace first + FileScopedNamespaceDeclarationSyntax fileScopedNamespace = syntax.Ancestors() + .OfType() + .FirstOrDefault(); + + if(fileScopedNamespace != null) return fileScopedNamespace.Name.ToString(); + + // Try regular namespace + NamespaceDeclarationSyntax namespaceDeclaration = syntax.Ancestors() + .OfType() + .FirstOrDefault(); + + return namespaceDeclaration?.Name.ToString() ?? string.Empty; + } + + private static List<(string Name, string Keyword)> GetContainingTypes(SyntaxNode syntax) + { + var containingTypes = new List<(string Name, string Keyword)>(); + + foreach(SyntaxNode ancestor in syntax.Ancestors()) + { + switch(ancestor) + { + case ClassDeclarationSyntax classDecl: + string classModifiers = GetValidPartialModifiers(classDecl.Modifiers); + containingTypes.Insert(0, (classDecl.Identifier.Text, $"{classModifiers}partial class".Trim())); + + break; + case StructDeclarationSyntax structDecl: + string structModifiers = GetValidPartialModifiers(structDecl.Modifiers); + containingTypes.Insert(0, (structDecl.Identifier.Text, $"{structModifiers}partial struct".Trim())); + + break; + case RecordDeclarationSyntax recordDecl: + string recordModifiers = GetValidPartialModifiers(recordDecl.Modifiers); + containingTypes.Insert(0, (recordDecl.Identifier.Text, $"{recordModifiers}partial record".Trim())); + + break; + } + } + + return containingTypes; + } + + private static string GetValidPartialModifiers(SyntaxTokenList modifiers) + { + var validModifiers = (from modifier in modifiers + select modifier.Text + into modifierText + where modifierText != "partial" + where modifierText is "public" or "internal" or "private" or "protected" or "unsafe" + select modifierText).ToList(); + + return validModifiers.Count > 0 ? string.Join(" ", validModifiers) + " " : string.Empty; + } + + private static string GenerateSwapEndianMethod(string structName, string namespaceName, + List<(string Name, string Keyword)> containingTypes, + List<(string fieldName, ITypeSymbol typeSymbol, string typeName)> + fieldTypes) + { + var sb = new StringBuilder(); + + sb.AppendLine("// "); + sb.AppendLine("#nullable enable"); + sb.AppendLine(); + sb.AppendLine("using System;"); + sb.AppendLine("using System.Runtime.CompilerServices;"); + sb.AppendLine("using Aaru.Helpers;"); + sb.AppendLine(); + + if(!string.IsNullOrEmpty(namespaceName)) + { + sb.AppendLine($"namespace {namespaceName};"); + sb.AppendLine(); + } + + // Generate containing type declarations + foreach((string name, string keyword) in containingTypes) + { + sb.AppendLine($"{keyword} {name}"); + sb.AppendLine("{"); + } + + var hasEnums = false; + sb.AppendLine($" partial struct {structName} : ISwapEndian<{structName}>"); + sb.AppendLine(" {"); + sb.AppendLine(" /// Swaps the endianness of all numeric fields in this structure"); + sb.AppendLine(" /// A new structure with swapped endianness"); + sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]"); + sb.AppendLine($" public {structName} SwapEndian()"); + sb.AppendLine(" {"); + sb.AppendLine(" var result = this;"); + sb.AppendLine(); + + // Process each field + foreach((string fieldName, ITypeSymbol typeSymbol, string typeName) in fieldTypes) + { + (string swapCode, bool usesEnumHelper) = GenerateSwapCode(typeName, fieldName, typeSymbol); + + if(string.IsNullOrEmpty(swapCode)) continue; + sb.AppendLine(swapCode); + if(usesEnumHelper) hasEnums = true; + } + + sb.AppendLine(); + sb.AppendLine(" return result;"); + sb.AppendLine(" }"); + + // Add the SwapEnumValue helper method only if we have enums + if(hasEnums) sb.Append(GenerateSwapEnumValueHelper()); + + sb.AppendLine(" }"); + + // Close containing type declarations + for(var i = 0; i < containingTypes.Count; i++) sb.AppendLine("}"); + + return sb.ToString(); + } + + private static (string code, bool usesEnumHelper) GenerateSwapCode(string typeName, string fieldName, + ITypeSymbol typeSymbol) + { + // Remove array brackets and modifiers for type checking + string cleanTypeName = typeName.TrimEnd('[', ']', '?').Trim(); + + string code = cleanTypeName switch + { + "short" or "Int16" => + $" result.{fieldName} = (short)((result.{fieldName} << 8) | ((result.{fieldName} >> 8) & 0xFF));", + + "ushort" or "UInt16" => + $" result.{fieldName} = (ushort)((result.{fieldName} << 8) | (result.{fieldName} >> 8));", + + "int" or "Int32" => $$""" + var {{fieldName}}_temp = result.{{fieldName}}; + {{fieldName}}_temp = (int)(({{fieldName}}_temp << 8) & 0xFF00FF00 | ((uint){{fieldName}}_temp >> 8) & 0xFF00FF); + result.{{fieldName}} = (int)(((uint){{fieldName}}_temp << 16) | ((uint){{fieldName}}_temp >> 16) & 0xFFFF); + """, + + "uint" or "UInt32" => $$""" + var {{fieldName}}_temp = result.{{fieldName}}; + {{fieldName}}_temp = ({{fieldName}}_temp << 8) & 0xFF00FF00 | ({{fieldName}}_temp >> 8) & 0xFF00FF; + result.{{fieldName}} = ({{fieldName}}_temp << 16) | ({{fieldName}}_temp >> 16); + """, + + "long" or "Int64" => $$""" + var {{fieldName}}_temp = result.{{fieldName}}; + {{fieldName}}_temp = ({{fieldName}}_temp & 0x00000000FFFFFFFF) << 32 | (long)(((ulong){{fieldName}}_temp & 0xFFFFFFFF00000000) >> 32); + {{fieldName}}_temp = ({{fieldName}}_temp & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong){{fieldName}}_temp & 0xFFFF0000FFFF0000) >> 16); + result.{{fieldName}} = ({{fieldName}}_temp & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong){{fieldName}}_temp & 0xFF00FF00FF00FF00) >> 8); + """, + + "ulong" or "UInt64" => $$""" + var {{fieldName}}_temp = result.{{fieldName}}; + {{fieldName}}_temp = ({{fieldName}}_temp & 0x00000000FFFFFFFF) << 32 | ({{fieldName}}_temp & 0xFFFFFFFF00000000) >> 32; + {{fieldName}}_temp = ({{fieldName}}_temp & 0x0000FFFF0000FFFF) << 16 | ({{fieldName}}_temp & 0xFFFF0000FFFF0000) >> 16; + result.{{fieldName}} = ({{fieldName}}_temp & 0x00FF00FF00FF00FF) << 8 | ({{fieldName}}_temp & 0xFF00FF00FF00FF00) >> 8; + """, + + "float" or "Single" => $$""" + { + var bytes = BitConverter.GetBytes(result.{{fieldName}}); + Array.Reverse(bytes); + result.{{fieldName}} = BitConverter.ToSingle(bytes, 0); + } + """, + + "double" or "Double" => $$""" + { + var bytes = BitConverter.GetBytes(result.{{fieldName}}); + Array.Reverse(bytes); + result.{{fieldName}} = BitConverter.ToDouble(bytes, 0); + } + """, + + "byte" or "Byte" or "sbyte" or "SByte" => + $" // {fieldName} - no swap needed for byte types", + + "Guid" => $" // TODO: Implement GUID swap for {fieldName}", + + _ when typeName.Contains("[") => $" // TODO: Implement array swap for {fieldName}", + + _ => null + }; + + if(code != null) return (code, false); + + // Use semantic information to determine if it's an enum or struct + if(typeSymbol == null) return ($" result.{fieldName} = result.{fieldName}.SwapEndian();", false); + + if(typeSymbol.TypeKind == TypeKind.Enum) + { + // It's an enum - swap using runtime helper that will determine underlying type + return ($$""" + result.{{fieldName}} = SwapEnumValue(result.{{fieldName}}); + """, true); + } + + return typeSymbol.TypeKind == TypeKind.Struct + ? + + // It's a struct - call .SwapEndian() + ($" result.{fieldName} = result.{fieldName}.SwapEndian();", false) + : + + // Fallback to assuming it's a nested struct + ($" result.{fieldName} = result.{fieldName}.SwapEndian();", false); + } + + // Helper method to generate at the struct level for enum swapping only + private static string GenerateSwapEnumValueHelper() => """ + + private static T SwapEnumValue(T value) where T : struct + { + var type = typeof(T); + + // Handle enums by converting to underlying type + if (type.IsEnum) + { + var underlyingType = Enum.GetUnderlyingType(type); + + if (underlyingType == typeof(short)) + { + var v = (short)(object)value; + v = (short)((v << 8) | ((v >> 8) & 0xFF)); + return (T)(object)v; + } + if (underlyingType == typeof(ushort)) + { + var v = (ushort)(object)value; + v = (ushort)((v << 8) | (v >> 8)); + return (T)(object)v; + } + if (underlyingType == typeof(int)) + { + var v = (int)(object)value; + v = (int)((v << 8) & 0xFF00FF00 | ((uint)v >> 8) & 0xFF00FF); + v = (int)(((uint)v << 16) | ((uint)v >> 16) & 0xFFFF); + return (T)(object)v; + } + if (underlyingType == typeof(uint)) + { + var v = (uint)(object)value; + v = (v << 8) & 0xFF00FF00 | (v >> 8) & 0xFF00FF; + v = (v << 16) | (v >> 16); + return (T)(object)v; + } + if (underlyingType == typeof(long)) + { + var v = (long)(object)value; + v = (v & 0x00000000FFFFFFFF) << 32 | (long)(((ulong)v & 0xFFFFFFFF00000000) >> 32); + v = (v & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong)v & 0xFFFF0000FFFF0000) >> 16); + v = (v & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong)v & 0xFF00FF00FF00FF00) >> 8); + return (T)(object)v; + } + if (underlyingType == typeof(ulong)) + { + var v = (ulong)(object)value; + v = (v & 0x00000000FFFFFFFF) << 32 | (v & 0xFFFFFFFF00000000) >> 32; + v = (v & 0x0000FFFF0000FFFF) << 16 | (v & 0xFFFF0000FFFF0000) >> 16; + v = (v & 0x00FF00FF00FF00FF) << 8 | (v & 0xFF00FF00FF00FF00) >> 8; + return (T)(object)v; + } + if (underlyingType == typeof(byte) || underlyingType == typeof(sbyte)) + { + return value; // No swap needed + } + } + + // If not an enum or unknown underlying type, return unchanged + return value; + } + """; +} \ No newline at end of file diff --git a/Aaru.Helpers/ISwapEndian.cs b/Aaru.Helpers/ISwapEndian.cs new file mode 100644 index 000000000..f30af8524 --- /dev/null +++ b/Aaru.Helpers/ISwapEndian.cs @@ -0,0 +1,42 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : ISwapEndian.cs +// Author(s) : Natalia Portillo +// +// Component : Helpers. +// +// --[ Description ] ---------------------------------------------------------- +// +// Interface for structs that support endianness swapping. +// +// --[ 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 +// ****************************************************************************/ + +namespace Aaru.Helpers; + +/// Interface for structs that support compile-time endianness swapping +/// The struct type that implements this interface +public interface ISwapEndian where T : struct +{ + /// Swaps the endianness of all numeric fields in this structure + /// A new structure with swapped endianness + T SwapEndian(); +} \ No newline at end of file