Make SharpLib more friendly

This commit is contained in:
Marc Hernandez 2026-02-13 15:49:56 -08:00
parent 3a3a634841
commit ce168985a8
36 changed files with 1756 additions and 175 deletions

View File

@ -11,9 +11,12 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>14.0</LangVersion> <LangVersion>14.0</LangVersion>
<Copyright>Copyright 2003..2025 Marc Hernandez</Copyright>
<Description>A base set of functionality</Description>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Nullable>disable</Nullable>
<Nullability>disable</Nullability>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
<!-- <!--
I Want to turn this on, but cant yet. Implementing nullability 1 file at a gime I Want to turn this on, but cant yet. Implementing nullability 1 file at a gime
@ -22,6 +25,16 @@
<NoWarn>$(NoWarn);SYSLIB0050;CS8981; CS8632</NoWarn> <NoWarn>$(NoWarn);SYSLIB0050;CS8981; CS8632</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0'">
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net10.0'">
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="Optional" Version="4.0.0" /> <PackageReference Include="Optional" Version="4.0.0" />

View File

@ -25,6 +25,8 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#if UNSAFE
namespace lib namespace lib
{ {
/// <summary> /// <summary>
@ -163,3 +165,5 @@ namespace lib
} }
} }
} }
#endif // UNSAFE

View File

@ -35,6 +35,7 @@ using System.Security;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace lib namespace lib
{ {
/// <summary> /// <summary>
@ -43,7 +44,6 @@ namespace lib
public static class Util public static class Util
{ {
/*
public static string StringJoin( string separator, IEnumerable<object> collectionToConvert ) public static string StringJoin( string separator, IEnumerable<object> collectionToConvert )
{ {
return String.Join(separator, collectionToConvert.Select(o => o.ToString())); return String.Join(separator, collectionToConvert.Select(o => o.ToString()));
@ -60,7 +60,9 @@ namespace lib
return type.Name; return type.Name;
} }
*/
/*
public static string ToString<T>( this IEnumerable<T> collectionToConvert, string separator ) public static string ToString<T>( this IEnumerable<T> collectionToConvert, string separator )
@ -79,34 +81,35 @@ namespace lib
return type.Name; return type.Name;
} }
*/
/* /*
#if XENKO_PLATFORM_UWP #if XENKO_PLATFORM_UWP
public static unsafe void CopyMemory(IntPtr dest, IntPtr src, int sizeInBytesToCopy) public static unsafe void CopyMemory(IntPtr dest, IntPtr src, int sizeInBytesToCopy)
{ {
Interop.memcpy((void*)dest, (void*)src, sizeInBytesToCopy); Interop.memcpy((void*)dest, (void*)src, sizeInBytesToCopy);
} }
#else #else
#if XENKO_PLATFORM_WINDOWS_DESKTOP #if XENKO_PLATFORM_WINDOWS_DESKTOP
private const string MemcpyDll = "msvcrt.dll"; private const string MemcpyDll = "msvcrt.dll";
#elif XENKO_PLATFORM_ANDROID #elif XENKO_PLATFORM_ANDROID
private const string MemcpyDll = "libc.so"; private const string MemcpyDll = "libc.so";
#elif XENKO_PLATFORM_UNIX #elif XENKO_PLATFORM_UNIX
// We do not specifiy the .so extension as libc.so on Linux // We do not specifiy the .so extension as libc.so on Linux
// is actually not a .so files but a script. Using just libc // is actually not a .so files but a script. Using just libc
// will automatically find the corresponding .so. // will automatically find the corresponding .so.
private const string MemcpyDll = "libc"; private const string MemcpyDll = "libc";
#elif XENKO_PLATFORM_IOS #elif XENKO_PLATFORM_IOS
private const string MemcpyDll = ObjCRuntime.Constants.SystemLibrary; private const string MemcpyDll = ObjCRuntime.Constants.SystemLibrary;
#else #else
# error Unsupported platform #error Unsupported platform
#endif #endif
[DllImport(MemcpyDll, EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] [DllImport(MemcpyDll, EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
#if !XENKO_RUNTIME_CORECLR #if !XENKO_RUNTIME_CORECLR
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
#endif #endif
private static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, ulong sizeInBytesToCopy); private static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, ulong sizeInBytesToCopy);
/// <summary> /// <summary>
@ -119,7 +122,7 @@ namespace lib
{ {
CopyMemory(dest, src, (ulong)sizeInBytesToCopy); CopyMemory(dest, src, (ulong)sizeInBytesToCopy);
} }
#endif #endif
*/ */
@ -139,6 +142,7 @@ namespace lib
} }
#if UNSAFE
/// <summary> /// <summary>
/// Compares two block of memory. /// Compares two block of memory.
@ -465,6 +469,47 @@ namespace lib
Marshal.FreeHGlobal( ( (IntPtr*)alignedBuffer )[-1] ); Marshal.FreeHGlobal( ( (IntPtr*)alignedBuffer )[-1] );
} }
/// <summary>
/// Writes the specified T data to a memory location.
/// </summary>
/// <typeparam name="T">Type of a data to write</typeparam>
/// <param name="destination">Memory location to write to.</param>
/// <param name="data">The data to write.</param>
internal static void UnsafeWrite<T>( IntPtr destination, ref T data )
{
unsafe
{
Interop.CopyInline( (void*)destination, ref data );
}
}
/// <summary>
/// Reads the specified T data from a memory location.
/// </summary>
/// <typeparam name="T">Type of a data to read</typeparam>
/// <param name="source">Memory location to read from.</param>
/// <param name="data">The data write to.</param>
internal static void UnsafeReadOut<T>( IntPtr source, out T data )
{
unsafe
{
Interop.CopyInlineOut( out data, (void*)source );
}
}
/// <summary>
/// Return the sizeof a struct from a CLR. Equivalent to sizeof operator but works on generics too.
/// </summary>
/// <typeparam name="T">a struct to evaluate</typeparam>
/// <returns>sizeof this struct</returns>
internal static int UnsafeSizeOf<T>()
{
return Interop.SizeOf<T>();
}
#endif
/// <summary> /// <summary>
/// If non-null, disposes the specified object and set it to null, otherwise do nothing. /// If non-null, disposes the specified object and set it to null, otherwise do nothing.
/// </summary> /// </summary>
@ -820,43 +865,6 @@ namespace lib
Thread.Sleep( sleepTimeInMillis ); Thread.Sleep( sleepTimeInMillis );
} }
/// <summary>
/// Writes the specified T data to a memory location.
/// </summary>
/// <typeparam name="T">Type of a data to write</typeparam>
/// <param name="destination">Memory location to write to.</param>
/// <param name="data">The data to write.</param>
internal static void UnsafeWrite<T>( IntPtr destination, ref T data )
{
unsafe
{
Interop.CopyInline( (void*)destination, ref data );
}
}
/// <summary>
/// Reads the specified T data from a memory location.
/// </summary>
/// <typeparam name="T">Type of a data to read</typeparam>
/// <param name="source">Memory location to read from.</param>
/// <param name="data">The data write to.</param>
internal static void UnsafeReadOut<T>( IntPtr source, out T data )
{
unsafe
{
Interop.CopyInlineOut( out data, (void*)source );
}
}
/// <summary>
/// Return the sizeof a struct from a CLR. Equivalent to sizeof operator but works on generics too.
/// </summary>
/// <typeparam name="T">a struct to evaluate</typeparam>
/// <returns>sizeof this struct</returns>
internal static int UnsafeSizeOf<T>()
{
return Interop.SizeOf<T>();
}
/// <summary> /// <summary>
/// Linq assisted full tree iteration and collection in a single line. /// Linq assisted full tree iteration and collection in a single line.

View File

@ -5,6 +5,7 @@ using System.Numerics;
static public class bit static public class bit
{ {
#if UNSAFE
public static unsafe TInt ByPointers_DirectInt<TInt, TEnum>( TEnum enumValue ) public static unsafe TInt ByPointers_DirectInt<TInt, TEnum>( TEnum enumValue )
where TInt : IBinaryInteger<TInt> where TInt : IBinaryInteger<TInt>
where TEnum : unmanaged, System.Enum where TEnum : unmanaged, System.Enum
@ -14,8 +15,6 @@ static public class bit
#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
} }
static public bool On<T, E>( T bitfield, E e ) static public bool On<T, E>( T bitfield, E e )
where T : IBinaryInteger<T> where T : IBinaryInteger<T>
where E : unmanaged, System.Enum where E : unmanaged, System.Enum
@ -26,4 +25,19 @@ static public class bit
} }
#endif
static public bool On<T, E>( T bitfield, E e )
where T : IBinaryInteger<T>
where E : unmanaged, System.Enum
{
// FIX FIX FIX
// FIX FIX FIX
// FIX FIX FIX
// FIX FIX FIX
return false;
}
} }

View File

@ -23,7 +23,7 @@ namespace lib
// TODO PERF Make span versions of all these functions // TODO PERF Make span versions of all these functions
unsafe public static Id<T> Generate() public static Id<T> Generate()
{ {
var buf = new byte[8]; var buf = new byte[8];

View File

@ -92,7 +92,7 @@ namespace lib
public ser.Types TypesDefault = ser.Types.Fields; public ser.Types TypesDefault = ser.Types.Fields;
} }
public class XmlFormatter2 : IFormatter public class XmlFormatter2
{ {

View File

@ -9,7 +9,6 @@ using System.Reflection;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using lib.Net;
using System.Text; using System.Text;
//using System.Threading.Tasks; //using System.Threading.Tasks;
@ -141,14 +140,14 @@ static public class log
// It would be better to use a ManualResetEvent here if waiting is truly needed. // It would be better to use a ManualResetEvent here if waiting is truly needed.
Thread.Sleep( 500 ); // Simple wait Thread.Sleep( 500 ); // Simple wait
var processId = Process.GetCurrentProcess().Id; var processId = Process.GetCurrentProcess().Id;
LogGC.PrintRuntimeGCEvents( processId ); //LogGC.PrintRuntimeGCEvents( processId );
} }
static void StartTracing() static void StartTracing()
{ {
// See above comment // See above comment
Thread.Sleep( 500 ); Thread.Sleep( 500 );
Tracing.TraceLogMonitor.Run(); //Tracing.TraceLogMonitor.Run();
} }
#endregion // CLR Logging #endregion // CLR Logging
@ -825,7 +824,7 @@ static public class log
StartThread(); StartThread();
LogGC.RegisterObjectId( s_lock ); //LogGC.RegisterObjectId( s_lock );
info( $"startup END", cat: "log.startup" ); info( $"startup END", cat: "log.startup" );
} }

View File

@ -87,6 +87,7 @@ namespace math
Vec3.TransformCoordinate( ref center, ref world, out Center ); Vec3.TransformCoordinate( ref center, ref world, out Center );
#if UNSAFE
// Update world matrix into absolute form // Update world matrix into absolute form
unsafe unsafe
{ {
@ -99,7 +100,7 @@ namespace math
++matrixData; ++matrixData;
} }
} }
#endif
Vec3.TransformNormal( ref extent, ref world, out Extent ); Vec3.TransformNormal( ref extent, ref world, out Extent );
} }

View File

@ -185,6 +185,7 @@ namespace math
return CollisionHelper.SphereContainsSphere( ref this, ref sphere ); return CollisionHelper.SphereContainsSphere( ref this, ref sphere );
} }
#if UNSAFE
/// <summary> /// <summary>
/// Constructs a <see cref="math.BoundingSphere"/> that fully contains the given points. /// Constructs a <see cref="math.BoundingSphere"/> that fully contains the given points.
/// </summary> /// </summary>
@ -199,7 +200,6 @@ namespace math
FromPoints( (IntPtr)pointsPtr, 0, points.Length, lib.Util.SizeOf<Vec3>(), out result ); FromPoints( (IntPtr)pointsPtr, 0, points.Length, lib.Util.SizeOf<Vec3>(), out result );
} }
} }
/// <summary> /// <summary>
/// Constructs a <see cref="math.BoundingSphere" /> that fully contains the given unmanaged points. /// Constructs a <see cref="math.BoundingSphere" /> that fully contains the given unmanaged points.
/// </summary> /// </summary>
@ -251,7 +251,6 @@ namespace math
result.Center = center; result.Center = center;
result.Radius = radius; result.Radius = radius;
} }
/// <summary> /// <summary>
/// Constructs a <see cref="math.BoundingSphere"/> that fully contains the given points. /// Constructs a <see cref="math.BoundingSphere"/> that fully contains the given points.
/// </summary> /// </summary>
@ -263,6 +262,7 @@ namespace math
FromPoints( points, out result ); FromPoints( points, out result );
return result; return result;
} }
#endif
/// <summary> /// <summary>
/// Constructs a <see cref="math.BoundingSphere"/> from a given box. /// Constructs a <see cref="math.BoundingSphere"/> from a given box.

View File

@ -1474,13 +1474,23 @@ namespace math
return ContainmentType.Contains; return ContainmentType.Contains;
} }
/// <summary> /// <summary>
/// Determines whether a <see cref="BoundingFrustum" /> intersects or contains an AABB determined by its center and extent. /// Determines whether a <see cref="BoundingFrustum" /> intersects or contains an AABB determined by its center and extent.
/// Faster variant specific for frustum culling. /// Faster variant specific for frustum culling.
/// </summary> /// </summary>
/// <param name="frustum">The frustum.</param> /// <param name="frustum">The frustum.</param>
/// <param name="boundingBoxExt">The bounding box ext.</param> /// <param name="boundingBoxExt">The bounding box ext.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
public static bool FrustumContainsBox( ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt )
{
throw new NotImplementedException();
throw new NotImplementedException();
return false;
}
#if UNSAFE
public static bool FrustumContainsBox( ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt ) public static bool FrustumContainsBox( ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt )
{ {
unsafe unsafe
@ -1547,5 +1557,7 @@ namespace math
} }
*/ */
} }
} #endif
}
} }

View File

@ -18,10 +18,12 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Double2 : IEquatable<Double2>, IFormattable public struct Double2 : IEquatable<Double2>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Double2"/> type, in bytes. /// The size of the <see cref="math.Double2"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Double2>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Double2>();
#endif
/// <summary> /// <summary>
/// A <see cref="math.Double2"/> with all of its components set to zero. /// A <see cref="math.Double2"/> with all of its components set to zero.

View File

@ -17,16 +17,20 @@ namespace math
[DataStyle( DataStyle.Compact )] [DataStyle( DataStyle.Compact )]
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Double3 : IEquatable<Double3>, IFormattable public struct Double3 : IEquatable<Double3>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Double3"/> type, in bytes. /// The size of the <see cref="math.Double3"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Double3>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Double3>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="math.Double3"/> with all of its components set to zero. /// A <see cref="math.Double3"/> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Double3 Zero = new Double3(); public static readonly Double3 Zero = new Double3();
/// <summary> /// <summary>
/// The X unit <see cref="math.Double3"/> (1, 0, 0). /// The X unit <see cref="math.Double3"/> (1, 0, 0).

View File

@ -18,15 +18,17 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Double4 : IEquatable<Double4>, IFormattable public struct Double4 : IEquatable<Double4>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Double4"/> type, in bytes. /// The size of the <see cref="math.Double4"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Double4>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Double4>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="math.Double4"/> with all of its components set to zero. /// A <see cref="math.Double4"/> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Double4 Zero = new Double4(); public static readonly Double4 Zero = new Double4();
/// <summary> /// <summary>
/// The X unit <see cref="math.Double4"/> (1, 0, 0, 0). /// The X unit <see cref="math.Double4"/> (1, 0, 0, 0).

View File

@ -41,15 +41,18 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Int2 : IEquatable<Int2>, IFormattable public struct Int2 : IEquatable<Int2>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Int2"/> type, in bytes. /// The size of the <see cref="math.Int2"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Int2>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Int2>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="math.Int2"/> with all of its components set to zero. /// A <see cref="math.Int2"/> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Int2 Zero = new Int2(); public static readonly Int2 Zero = new Int2();
/// <summary> /// <summary>
/// The X unit <see cref="math.Int2"/> (1, 0, 0). /// The X unit <see cref="math.Int2"/> (1, 0, 0).

View File

@ -41,10 +41,13 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Int3 : IEquatable<Int3>, IFormattable public struct Int3 : IEquatable<Int3>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="Int3"/> type, in bytes. /// The size of the <see cref="Int3"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Int3>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Int3>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="Int3"/> with all of its components set to zero. /// A <see cref="Int3"/> with all of its components set to zero.

View File

@ -35,15 +35,18 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Int4 : IEquatable<Int4>, IFormattable public struct Int4 : IEquatable<Int4>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref = "Int4" /> type, in bytes. /// The size of the <see cref = "Int4" /> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Int4>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Int4>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref = "Int4" /> with all of its components set to zero. /// A <see cref = "Int4" /> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Int4 Zero = new Int4(); public static readonly Int4 Zero = new Int4();
/// <summary> /// <summary>
/// The X unit <see cref = "Int4" /> (1, 0, 0, 0). /// The X unit <see cref = "Int4" /> (1, 0, 0, 0).

View File

@ -79,6 +79,14 @@ namespace math
/// The code is using the technique described by Bruce Dawson in /// The code is using the technique described by Bruce Dawson in
/// <a href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">Comparing Floating point numbers 2012 edition</a>. /// <a href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">Comparing Floating point numbers 2012 edition</a>.
/// </remarks> /// </remarks>
public static bool NearEqual( float a, float b )
{
return Math.Abs( a - b ) < 0.00001f;
}
#if UNSAFE
public static unsafe bool NearEqual( float a, float b ) public static unsafe bool NearEqual( float a, float b )
{ {
// Check if the numbers are really close -- needed // Check if the numbers are really close -- needed
@ -102,7 +110,7 @@ namespace math
const int maxUlp = 4; const int maxUlp = 4;
return ( ulp <= maxUlp ); return ( ulp <= maxUlp );
} }
#endif
/// <summary> /// <summary>
/// Determines whether the specified value is close to zero (0.0f). /// Determines whether the specified value is close to zero (0.0f).
/// </summary> /// </summary>

View File

@ -44,15 +44,18 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Matrix : IEquatable<Matrix>, IFormattable public struct Matrix : IEquatable<Matrix>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Matrix"/> type, in bytes. /// The size of the <see cref="math.Matrix"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Matrix>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Matrix>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="math.Matrix"/> with all of its components set to zero. /// A <see cref="math.Matrix"/> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Matrix Zero = new Matrix(); public static readonly Matrix Zero = new Matrix();
/// <summary> /// <summary>
/// The identity <see cref="math.Matrix"/>. /// The identity <see cref="math.Matrix"/>.
@ -3168,6 +3171,7 @@ namespace math
return result; return result;
} }
#if UNSAFE
/// <summary> /// <summary>
/// Copies a nxm matrix to this instance. /// Copies a nxm matrix to this instance.
/// </summary> /// </summary>
@ -3214,7 +3218,7 @@ namespace math
} }
} }
} }
#endif
/// <summary> /// <summary>
/// Adds two matricies. /// Adds two matricies.
/// </summary> /// </summary>

View File

@ -41,15 +41,18 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Quaternion : IEquatable<Quaternion>, IFormattable public struct Quaternion : IEquatable<Quaternion>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Quaternion"/> type, in bytes. /// The size of the <see cref="math.Quaternion"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Quaternion>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Quaternion>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref="math.Quaternion"/> with all of its components set to zero. /// A <see cref="math.Quaternion"/> with all of its components set to zero.
/// </summary> /// </summary>
public static readonly Quaternion Zero = new Quaternion(); public static readonly Quaternion Zero = new Quaternion();
/// <summary> /// <summary>
/// A <see cref="math.Quaternion"/> with all of its components set to one. /// A <see cref="math.Quaternion"/> with all of its components set to one.

View File

@ -34,10 +34,13 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct UInt4 : IEquatable<UInt4>, IFormattable public struct UInt4 : IEquatable<UInt4>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref = "UInt4" /> type, in bytes. /// The size of the <see cref = "UInt4" /> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<UInt4>(); public static readonly int SizeInBytes = lib.Util.SizeOf<UInt4>();
#endif // UNSAFE
/// <summary> /// <summary>
/// A <see cref = "UInt4" /> with all of its components set to zero. /// A <see cref = "UInt4" /> with all of its components set to zero.

View File

@ -42,11 +42,12 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Vec2 : IEquatable<Vec2>, IFormattable public struct Vec2 : IEquatable<Vec2>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Vec2"/> type, in bytes. /// The size of the <see cref="math.Vec2"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Vec2>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Vec2>();
#endif
/// <summary> /// <summary>
/// A <see cref="math.Vec2"/> with all of its components set to zero. /// A <see cref="math.Vec2"/> with all of its components set to zero.
/// </summary> /// </summary>

View File

@ -43,11 +43,12 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Vec3 : IEquatable<Vec3>, IFormattable public struct Vec3 : IEquatable<Vec3>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Vec3"/> type, in bytes. /// The size of the <see cref="math.Vec3"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Vec3>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Vec3>();
#endif
/// <summary> /// <summary>
/// A <see cref="math.Vec3"/> with all of its components set to zero. /// A <see cref="math.Vec3"/> with all of its components set to zero.
/// </summary> /// </summary>

View File

@ -42,11 +42,12 @@ namespace math
[StructLayout( LayoutKind.Sequential, Pack = 4 )] [StructLayout( LayoutKind.Sequential, Pack = 4 )]
public struct Vec4 : IEquatable<Vec4>, IFormattable public struct Vec4 : IEquatable<Vec4>, IFormattable
{ {
#if UNSAFE
/// <summary> /// <summary>
/// The size of the <see cref="math.Vec4"/> type, in bytes. /// The size of the <see cref="math.Vec4"/> type, in bytes.
/// </summary> /// </summary>
public static readonly int SizeInBytes = lib.Util.SizeOf<Vec4>(); public static readonly int SizeInBytes = lib.Util.SizeOf<Vec4>();
#endif
/// <summary> /// <summary>
/// A <see cref="math.Vec4"/> with all of its components set to zero. /// A <see cref="math.Vec4"/> with all of its components set to zero.
/// </summary> /// </summary>

View File

@ -1,4 +1,3 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System; using System;
using System.ComponentModel; using System.ComponentModel;

View File

@ -196,7 +196,7 @@ static public class refl
return first; return first;
} }
LogGC.RegisterObjectId( t ); //LogGC.RegisterObjectId( t );
lock( t ) lock( t )
{ {

View File

@ -6,7 +6,6 @@ using System.Reflection;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; // Note: This was in the original, but seems unused. Keep for now.
using System.Linq; using System.Linq;
#nullable enable #nullable enable

View File

@ -3,7 +3,7 @@
#nullable enable #nullable enable
#if SCRIPTS
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Emit;
@ -20,43 +20,64 @@ using System.Threading;
public class MemorySourceText : SourceText public class MemorySourceText : SourceText
{ {
public override char this[int position] => ' '; private readonly string _content;
public MemorySourceText(string content = "")
{
_content = content ?? string.Empty;
}
public override char this[int position] => _content[position];
public override Encoding? Encoding => Encoding.UTF8; public override Encoding? Encoding => Encoding.UTF8;
public override int Length => 0; public override int Length => _content.Length;
public override void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count ) public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
{ {
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (sourceIndex < 0 || sourceIndex >= _content.Length)
throw new ArgumentOutOfRangeException(nameof(sourceIndex));
if (destinationIndex < 0 || destinationIndex >= destination.Length)
throw new ArgumentOutOfRangeException(nameof(destinationIndex));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
if (sourceIndex + count > _content.Length || destinationIndex + count > destination.Length)
throw new ArgumentException("Source or destination range is out of bounds.");
_content.CopyTo(sourceIndex, destination, destinationIndex, count);
} }
} }
public class MemoryRefResolver : SourceReferenceResolver public class MemoryRefResolver : SourceReferenceResolver
{ {
public override bool Equals( object? other ) public override bool Equals(object? other)
{ {
return false; return other is MemoryRefResolver;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return 0; return typeof(MemoryRefResolver).GetHashCode();
} }
private MemoryStream _fakeMS = new MemoryStream(); private readonly MemoryStream _fakeMS = new MemoryStream();
public override string? NormalizePath( string? path, string? baseFilePath ) => path; public override string? NormalizePath(string? path, string? baseFilePath) => path;
public override Stream OpenRead( string resolvedPath ) => _fakeMS; public override Stream OpenRead(string resolvedPath) => _fakeMS;
private MemorySourceText _fakeMST = new(); private readonly MemorySourceText _fakeMST = new();
public override SourceText ReadText( string resolvedPath ) => _fakeMST; public override SourceText ReadText(string resolvedPath) => _fakeMST;
public override string? ResolveReference( string? path, string? baseFilePath ) => path;
public override string? ResolveReference(string? path, string? baseFilePath) => path;
} }
@ -204,7 +225,7 @@ public static class scr
CompileFile( filename, ( ass ) => { s_fnAss( ass ); }, ( diags ) => { } ); CompileFile( filename, ( ass ) => { s_fnAss( ass ); }, ( diags ) => { } );
} }
public static void CompileFile( string filename, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 ) public static void CompileFile( string filename, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure/* PORT , Platform platform = Platform.X86*/ )
{ {
var fullpath = Path.GetFullPath( filename ); var fullpath = Path.GetFullPath( filename );
@ -215,7 +236,7 @@ public static class scr
Compile( sourceText, fullpath, onSuccess, onFailure ); Compile( sourceText, fullpath, onSuccess, onFailure );
} }
public static void Compile( string str, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 ) public static void Compile( string str, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure /*, Platform platform = Platform.X86*/ )
{ {
var sourceText = SourceText.From( str ); var sourceText = SourceText.From( str );
@ -223,7 +244,7 @@ public static class scr
} }
public static void Compile( SourceText sourceText, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 ) public static void Compile( SourceText sourceText, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure /*, Platform platform = Platform.X86*/ )
{ {
string assemblyName = Path.GetRandomFileName(); string assemblyName = Path.GetRandomFileName();
@ -237,7 +258,7 @@ public static class scr
using MemoryStream ms = new(); using MemoryStream ms = new();
using MemoryStream pdb = new(); using MemoryStream pdb = new();
var result = CompileAndEmit( assemblyName, new[] { syntaxTree }, ms, pdb, platform ); var result = CompileAndEmit( assemblyName, new[] { syntaxTree }, ms, pdb );
if( !result.Success ) if( !result.Success )
{ {
@ -269,7 +290,7 @@ public static class scr
} }
} }
private static EmitResult CompileAndEmit( string assemblyName, SyntaxTree[] syntaxTrees, MemoryStream ms, MemoryStream pdb, Platform platform ) private static EmitResult CompileAndEmit( string assemblyName, SyntaxTree[] syntaxTrees, MemoryStream ms, MemoryStream pdb /*, Platform platform*/ )
{ {
MemoryRefResolver memRef = new(); MemoryRefResolver memRef = new();
@ -281,7 +302,7 @@ public static class scr
options: new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, options: new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary,
sourceReferenceResolver: memRef, sourceReferenceResolver: memRef,
optimizationLevel: OptimizationLevel.Release, optimizationLevel: OptimizationLevel.Release,
platform: platform, //platform: platform,
specificDiagnosticOptions: new Dictionary<string, ReportDiagnostic> specificDiagnosticOptions: new Dictionary<string, ReportDiagnostic>
{ {
{ "CS1701", ReportDiagnostic.Suppress } { "CS1701", ReportDiagnostic.Suppress }
@ -358,3 +379,4 @@ public static class scr
} }
} }
#endif

838
ser/XmlSer.cs Normal file
View File

@ -0,0 +1,838 @@
using System;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Collections.Immutable;
using System.Net.Sockets;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
namespace ser;
#region Attributes & Enums (Mostly unchanged, ensure these exist)
/*
public CoolClass
{
private string Rare = "Rare Change";
private float TestF = 1.0f;
}
public class Bag
{
public string Owner { get; set; }
public CoolClass Cool { get; set; } = new();
}
public class BasicTest
{
public Bag MyBag { get; set; } = new();
}
<root>
<MyBag Owner="John" Cool.Rare="Changed" >
<Cool TestF="2.5" />
</MyBag>
</root>
public abstract class Item
{
public int Id { get; set; }
}
public class Key : Item
{
public int Code { get; set; }
}
public class Orb : Item
{
private float Power = 1.0f;
public bool IsDark = true;
}
*/
public interface I_Serialize
{
void OnSerialize() { }
object OnDeserialize( object enclosing ) => this;
}
[Flags]
public enum Types
{
Fields = 0b_0001,
Props = 0b_0010,
Implied = 0b_0100,
Explicit = 0b_1000,
None = 0b_0000,
Default = Fields,
All = Fields | Props,
}
public class Ser : Attribute
{
public Types Types { get; set; } = Types.Default;
}
public class Do : Attribute
{
}
public class Dont : Attribute
{
}
public class ChildAttribute : Attribute
{
public string[] Values { get; private set; }
public ChildAttribute( params string[] values )
{
this.Values = values;
}
}
public class ChildFieldsAttribute : ChildAttribute
{
public ChildFieldsAttribute( params string[] values ) : base( values ) { }
}
public class ChildPropsAttribute : ChildAttribute
{
public ChildPropsAttribute( params string[] values ) : base( values ) { }
}
public interface ITypeHandler
{
bool CanHandle( TypeInfo typeInfo, XmlElement? elem = null ); // Elem needed for Deser
void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType );
object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing );
}
// --- Enums & Records (Slightly adjusted/renamed) ---
public enum Datastructure { Tree, Graph }
public enum BackingFieldNaming { Short, Regular }
public enum POD { Attributes, Elements }
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
public record XmlCfg : io.Recorded<XmlCfg>
{
public bool Verbose { get; init; } = false;
public Datastructure Structure { get; init; } = Datastructure.Tree;
public int Version { get; init; } = 2;
public ImmutableDictionary<Type, TypeProxy> Proxies { get; init; } = ImmutableDictionary<Type, TypeProxy>.Empty;
public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Short;
public POD POD { get; init; } = POD.Attributes;
public ser.Types TypesDefault { get; init; } = ser.Types.Fields;
public static XmlCfg Default { get; } = new XmlCfg();
}
#endregion
#region Reflection & Metadata Cache
public record DependentMember(
string Name, //Prop or field name
TypeInfo Enclosing,
MemberInfo EnclosingMember
);
public enum MemberMetaType
{
Invalid,
Simple,
Composite,
}
public record MemberMeta(
//Base type.
Type Type,
MemberInfo Info,
string XmlName,
Func<object, object?> GetValue,
Action<object, object?> SetValue,
bool IsPodAttribute,
bool HasDo,
bool HasDont
);
public record TypeInfo(
Type Type,
List<MemberMeta> Members,
bool IsISerializable,
bool IsImm,
bool IsProxy,
TypeProxy? ProxyDef
);
public class TypeMetaCache
{
private readonly ConcurrentDictionary<Type, TypeInfo> _cache = new();
private readonly XmlCfg _cfg;
public TypeMetaCache( XmlCfg cfg ) => _cfg = cfg;
public TypeInfo Get( Type type )
{
// Expanded in anticpation of more complex ways to specify saves/loads
if( _cache.TryGetValue( type, out var ti ) )
return ti;
var children = new HashSet<string>();
var tiNew = BuildTypeInfo( type, children );
_cache.AddOrUpdate( type, tiNew, ( t, ti ) => tiNew );
return tiNew;
}
// Helper to create accessors (using standard reflection - can be optimized)
private static Func<object, object?> CreateGetter( MemberInfo mi )
{
if( mi is FieldInfo fi )
return fi.GetValue;
if( mi is PropertyInfo pi && pi.CanRead )
return pi.GetValue;
return _ => null;
}
private static Action<object, object?> CreateSetter( MemberInfo mi )
{
if( mi is FieldInfo fi )
return fi.SetValue;
if( mi is PropertyInfo pi && pi.CanWrite )
return pi.SetValue;
return ( _, _ ) => { };
}
// Helper to create accessors (using standard reflection - can be optimized)
private static Func<object, object?> CreateGetter( MemberInfo mi, Func<object, object?> getter )
{
if( mi is FieldInfo fi )
{
var innerGet = fi.GetValue;
return obj => innerGet( getter( obj ) );
}
if( mi is PropertyInfo pi && pi.CanRead )
{
Func<object, object?> innerGet = pi.GetValue;
return obj => innerGet( getter( obj ) );
}
// return pi.GetValue;
return _ => null;
}
private static Action<object, object?> CreateSetter( MemberInfo mi, Func<object, object?> getter )
{
if( mi is FieldInfo fi )
{
Action<object, object?> innerSet = fi.SetValue;
return ( obj, value ) => innerSet( getter( obj ), value );
}
if( mi is PropertyInfo pi && pi.CanWrite )
{
Action<object, object?> innerSet = pi.SetValue;
//return innerSet;
//var innerSetType = innerSet.GetType();
//var isArgs = innerSetType.Metho
return ( obj, value ) =>
{
var leaf = getter( obj );
innerSet( leaf, value );
};
}
return ( _, _ ) => { };
}
public void AddType( Type type, params string[] children )
{
var hashChildren = new HashSet<string>( children );
BuildTypeInfo( type, hashChildren );
}
private TypeInfo BuildTypeInfo( Type type, HashSet<string> children )
{
if( _cfg.Verbose )
log.info( $"Building TypeInfo for {type.Name}" );
var members = new List<MemberMeta>();
bool doImpls, doFields, doProps;
GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps );
var isImm = typeof( io.Obj ).IsAssignableFrom( type );
var typesAtt = type.GetCustomAttribute<ser.Ser>( true );
var serTypes = typesAtt?.Types ?? ser.Types.None;
if( doFields || doImpls )
{
foreach( var fi in refl.GetAllFields( type ) )
{
ProcessMember( fi, serTypes.HasFlag( ser.Types.Fields ), children, doImpls, isImm, members );
}
}
if( doProps || doImpls )
{
foreach( var pi in refl.GetAllProperties( type ) )
{
ProcessMember( pi, serTypes.HasFlag( ser.Types.Props ), children, doImpls, isImm, members );
}
}
var (isProxy, proxyDef) = FindProxy( type );
//Dont need to sort since we just run through the attributes first, then the nodes
//members.Sort( ( a, b ) => ( a.IsPodAttribute.Int < b.IsPodAttribute.Int ) ? 1 : -1);
return new TypeInfo(
type,
members,
typeof( ISerializable ).IsAssignableFrom( type ) && !typeof( Delegate ).IsAssignableFrom( type ), // Exclude Delegates
isImm,
isProxy,
proxyDef
);
}
private (bool, TypeProxy?) FindProxy( Type type )
{
var tryType = type;
while( tryType != null && tryType != typeof( object ) )
{
if( _cfg.Proxies.TryGetValue( tryType, out var proxy ) )
{
return (true, proxy);
}
tryType = tryType.BaseType;
}
return (false, null);
}
private void ProcessMember( MemberInfo mi, bool doMemberType, HashSet<string> childrenOverridden, bool doImpls, bool isImm, List<MemberMeta> members )
{
List<string> children = new( childrenOverridden );
var (hasDo, hasDont, hasImpl, propName, serTypess) = GetMemberAttributes( mi, out var actualMiForAtts, children );
if( hasDont )
return;
// TODO MH Change this to a configurable query(s)
if( isImm && ( mi.Name == "MetaStorage" || mi.Name == "Fn" ) )
return;
if( mi.GetCustomAttribute<NonSerializedAttribute>( true ) != null )
return;
if( !(
hasDo |
doMemberType |
( hasImpl & children.Any() )
) )
return;
var miType = ( mi is FieldInfo fi ) ? fi.FieldType : ( (PropertyInfo)mi ).PropertyType; // CHANGED (moved up)
string name = mi.Name;
string finalName = name;
if( !string.IsNullOrEmpty( propName ) )
{
finalName = ( _cfg.Naming == BackingFieldNaming.Short ) ? propName : name;
}
finalName = refl.TypeToIdentifier( finalName ); // Ensure XML-safe name
var overiddenName = false;
var getter = CreateGetter( mi );
var setter = CreateSetter( mi );
var blankHashSet = new HashSet<string>();
if( hasImpl && children.Any() )
{
//List<MemberMeta> specialMembers = new();
foreach( var childName in children )
{
var memberInfoArr = miType.GetMember( childName );
var miFinal = memberInfoArr?.FirstOrDefault();
if( miFinal == null )
continue;
var dependentType = miFinal is FieldInfo fidd ? fidd.FieldType : ( miFinal as PropertyInfo ).PropertyType;
bool isPod = Type.GetTypeCode( dependentType ) != TypeCode.Object;
//ProcessMember( miFinal, blankHashSet, doImpls, isImm, specialMembers );
//First this one. We need the old getter for the setter.
setter = CreateSetter( miFinal, getter );
//Now wrap the getter itself
getter = CreateGetter( miFinal, getter );
var depName = $"{finalName}.{childName}";
var memberMeta = new MemberMeta(
dependentType,
miFinal,
depName,
getter,
setter,
isPod && _cfg.POD == POD.Attributes,
hasDo,
hasDont
);
members.Add( memberMeta );
if( _cfg.Verbose )
{
log.info( $"{depName} ({mi.Name}) -> {finalName} ({dependentType.Name}) PodAtt: {isPod && _cfg.POD == POD.Attributes}" );
}
}
return;
/*
foreach( var childName in children )
{
var memberInfoArr = miType.GetMember( childName );
var miFinal = memberInfoArr?.FirstOrDefault();
if( miFinal != null )
{
realMemberType = ( miFinal is FieldInfo fin ) ? fin.FieldType : ( miFinal as PropertyInfo ).PropertyType;
getter = CreateGetter( miFinal, getter );
setter = CreateSetter( miFinal, setter );
}
}
//*/
}
{
// Simplified POD check
bool isPod = Type.GetTypeCode( miType ) != TypeCode.Object && !typeof( IEnumerable ).IsAssignableFrom( miType ) || overiddenName;
members.Add( new MemberMeta(
miType,
mi,
finalName,
getter,
setter,
isPod && _cfg.POD == POD.Attributes,
hasDo,
hasDont
) );
if( _cfg.Verbose )
{
log.info( $"{mi.Name} ({miType.Name}) -> {finalName} ({miType.Name}) PodAtt: {isPod && _cfg.POD == POD.Attributes}" );
}
}
}
private void ProcessDepedentMember( Type type, HashSet<string> children, List<MemberMeta> members )
{
}
private (bool hasDo, bool hasDont, bool hasImpl, string propName, ser.Types serTypess) GetMemberAttributes( MemberInfo mi, out MemberInfo actualMi, List<string> children )
{
actualMi = mi;
string propName = "";
bool isBacking = mi.Name.StartsWith( "<" ) && mi.Name.EndsWith( "BackingField" );
var typesAtt = mi.DeclaringType.GetCustomAttribute<ser.Ser>( true );
var serTypes = typesAtt?.Types ?? ser.Types.None;
var doImpls = serTypes.HasFlag( ser.Types.Implied );
var attDo = actualMi.GetCustomAttribute<ser.Do>() != null;
var attDont = actualMi.GetCustomAttribute<ser.Dont>() != null;
if( isBacking && mi is FieldInfo )
{
var gtIndex = mi.Name.IndexOf( '>' );
propName = mi.Name.Substring( 1, gtIndex - 1 );
var propInfo = mi.DeclaringType?.GetProperty( propName );
if( propInfo != null )
actualMi = propInfo;
}
var attChildren = actualMi.GetCustomAttribute<ser.ChildAttribute>();
if( attChildren != null )
{
children.AddRange( attChildren.Values );
}
return (
attDo,
attDont,
doImpls,
propName,
serTypes
);
}
// --- These helpers are copied/adapted from XmlFormatter2 ---
private static void GetFilters( ser.Types typesDefault, Type type, out bool doImpls, out bool doFields, out bool doProps )
{
var typesTodo = type.GetCustomAttribute<ser.Ser>( true )?.Types ?? typesDefault;
doImpls = typesTodo.HasFlag( ser.Types.Implied );
doFields = typesTodo.HasFlag( ser.Types.Fields );
doProps = typesTodo.HasFlag( ser.Types.Props );
}
}
public class TypeResolver
{
private readonly ConcurrentDictionary<string, Type?> _cache = new();
private readonly Assembly[] _assemblies;
private static readonly FormatterConverter _conv = new();
public TypeResolver()
{
_assemblies = AppDomain.CurrentDomain.GetAssemblies();
}
public Type Resolve( XmlElement elem, Type? expectedType )
{
if( elem.HasAttribute( "_.t" ) )
{
var typeName = elem.GetAttribute( "_.t" );
var resolved = FindType( typeName );
if( resolved != null )
return resolved;
}
return expectedType ?? typeof( object ); // Fallback needed
}
public Type? FindType( string typeName )
{
return _cache.GetOrAdd( typeName, tn =>
{
// Try direct lookup first (might work for fully qualified)
var t = Type.GetType( tn );
if( t != null )
return t;
// Then search assemblies
foreach( Assembly a in _assemblies )
{
t = a.GetType( tn );
if( t != null )
return t;
}
log.warn( $"Could not resolve type: {tn}" );
return null;
} );
}
public object ConvertSimple( string value, Type type )
{
if( type.IsEnum )
return Enum.Parse( type, value );
try
{
return _conv.Convert( value, type );
}
catch( Exception ex )
{
object defaultVal = type.IsValueType ? Activator.CreateInstance( type )! : null!;
log.warn( $"Conversion failed for '{value}' to {type.Name}: {ex.Message}. Returning default of {defaultVal}({defaultVal.GetType().Name})." );
return defaultVal;
}
}
}
#endregion
#region XmlSer (Coordinator)
public class XmlSer // : IFormatter
{
internal readonly XmlCfg _cfg;
internal readonly TypeMetaCache _meta;
internal readonly TypeResolver _resolver;
private readonly List<ITypeHandler> _handlers;
// Per-operation state
internal ObjectIDGenerator _idGen = new();
internal Dictionary<long, object> _processed = new();
private string _streamSource = "";
public XmlSer( XmlCfg? cfg = null, TypeMetaCache metaCache = null )
{
var isCustomConfig = cfg != null;
_cfg = cfg ?? XmlCfg.Default;
if( _cfg.Verbose )
{
log.info( $"Config:" );
log.info( $" {log.var( _cfg.Verbose )}" );
log.info( $" {log.var( _cfg.Structure )}" );
log.info( $" {log.var( _cfg.Version )}" );
log.info( $" {log.var( _cfg.Naming )}" );
log.info( $" {log.var( _cfg.POD )}" );
log.info( $" {log.var( _cfg.TypesDefault )}" );
}
_meta = metaCache ?? new TypeMetaCache( _cfg );
_resolver = new TypeResolver();
_handlers = new List<ITypeHandler>
{
new ProxyHandler(),
new ISerializableHandler(),
new PrimitiveHandler(),
new CollectionHandler(),
new ObjectHandler() // Must be last
};
if( _cfg.Verbose )
{
log.info( $"Handlers in importance..." );
foreach( var h in _handlers )
{
log.info( $" {h.GetType().Name}" );
}
log.high( "XmlSer Initialized." );
}
}
internal ITypeHandler GetHandler( Type t, XmlElement? elem = null )
{
var ti = _meta.Get( t );
return _handlers.First( h => h.CanHandle( ti, elem ) );
}
// --- Context Helpers ---
internal void WriteTypeAttr( XmlWriter writer, Type memberType, Type actualType )
{
if( memberType != actualType )
{
writer.WriteAttributeString( "_.t", actualType.FullName );
}
}
internal bool HandleGraphWrite( XmlWriter writer, object obj, out bool first )
{
first = true;
if( _cfg.Structure == Datastructure.Graph )
{
long id = _idGen.GetId( obj, out first );
writer.WriteAttributeString( "ref", id.ToString() );
if( first )
_processed[id] = obj;
}
return first || _cfg.Structure == Datastructure.Tree; // Write if first or if Tree
}
internal long TrackIfGraph( object obj, XmlElement elem )
{
long id = -1;
bool first;
if( _cfg.Structure == Datastructure.Graph )
{
id = _idGen.GetId( obj, out first );
if( elem.HasAttribute( "ref" ) )
{
id = long.Parse( elem.GetAttribute( "ref" ) );
}
if( !_processed.ContainsKey( id ) )
{
_processed[id] = obj;
}
}
return id;
}
// --- Deserialization ---
public T? Deserialize<T>( Stream stream ) => (T?)Deserialize( stream, typeof( T ) );
public object? Deserialize( Stream stream, Type? type = null )
{
_streamSource = stream.ToString() ?? "{null}"; // Basic source, improve as needed
_processed.Clear();
_idGen = new ObjectIDGenerator();
using var reader = XmlReader.Create( stream, new XmlReaderSettings { IgnoreWhitespace = true } );
XmlDocument doc = new XmlDocument();
try
{ doc.Load( reader ); }
catch( Exception ex ) { log.exception( ex, $"XML Load failed: {ex.Message}" ); return null; }
if( doc.DocumentElement == null )
return null;
return ReadNode( doc.DocumentElement, type ?? typeof( object ), null );
}
public void DeserializeInto<T>( Stream stream, T obj ) where T : class
{
_streamSource = stream.ToString() ?? "{null}";
_processed.Clear();
_idGen = new ObjectIDGenerator();
using var reader = XmlReader.Create( stream, new XmlReaderSettings { IgnoreWhitespace = true } );
XmlDocument doc = new XmlDocument();
try
{
doc.Load( reader );
}
catch( Exception ex )
{
log.exception( ex, $"XML Load failed: {ex.Message}" );
return;
}
if( doc.DocumentElement == null )
return;
ReadNode( doc.DocumentElement, typeof( T ), obj );
}
internal object? ReadNode( XmlElement elem, Type expectedType, object? existing )
{
if( elem.HasAttribute( "v" ) && elem.GetAttribute( "v" ) == "null" )
return null;
// 1. Handle refs (if Graph)
if( _cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) )
{
long id = long.Parse( elem.GetAttribute( "ref" ) );
if( _processed.TryGetValue( id, out var obj ) )
return obj;
}
// 2. Determine Type & Select Handler
var actualType = _resolver.Resolve( elem, expectedType );
var ti = _meta.Get( actualType );
var handler = _handlers.First( h => h.CanHandle( ti, elem ) );
// 3. Delegate
return handler.ReadXml( this, elem, actualType, existing );
}
// --- Serialization ---
public void Serialize( Stream stream, object root )
{
_processed.Clear();
_idGen = new ObjectIDGenerator();
var settings = new XmlWriterSettings
{
Indent = true,
Encoding = System.Text.Encoding.UTF8, // Use UTF8 for better compatibility
OmitXmlDeclaration = true // Often preferred for fragments/storage
};
using var writer = XmlWriter.Create( stream, settings );
writer.WriteStartDocument();
WriteNode( writer, root, "root", root?.GetType() ?? typeof( object ), true ); // Force type on root
writer.WriteEndDocument();
writer.Flush();
}
internal void WriteNode( XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( _cfg.Verbose )
log.info( $"Writing {name} ({memberType}) force: {forceType}" );
if( obj == null )
{
writer.WriteStartElement( name );
writer.WriteAttributeString( "v", "null" );
writer.WriteEndElement();
return;
}
var actualType = obj.GetType();
var ti = _meta.Get( actualType );
var handler = _handlers.First( h => h.CanHandle( ti ) );
try
{
handler.WriteXml( this, writer, obj, name, memberType, forceType || memberType != actualType );
}
catch( Exception ex )
{
log.exception( ex, $"{name}({memberType.Name}) forceType: {forceType}" );
}
}
}
#endregion

159
ser/XmlSer_Core.cs Normal file
View File

@ -0,0 +1,159 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
namespace ser;
// --- Primitive Handler ---
public partial class PrimitiveHandler : ser.ITypeHandler
{
public bool CanHandle( TypeInfo ti, XmlElement? elem )
{
var typeCode = Type.GetTypeCode( ti.Type );
var typeNotObject = Type.GetTypeCode( ti.Type ) != TypeCode.Object;
//var isString = ti.Type == typeof( string );
return typeNotObject;
}
}
// --- Proxy Handler ---
public partial class ProxyHandler : ITypeHandler
{
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false );
}
// --- ISerializable Handler ---
public partial class ISerializableHandler : ITypeHandler
{
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsISerializable;
}
// --- Collection Handler ---
public partial class CollectionHandler : ITypeHandler
{
public bool CanHandle( TypeInfo ti, XmlElement? elem ) =>
typeof( IEnumerable ).IsAssignableFrom( ti.Type );
private Type GetElementType( Type collectionType )
{
if( collectionType.IsArray )
return collectionType.GetElementType()!;
if( collectionType.IsGenericType )
{
var args = collectionType.GetGenericArguments();
if( args.Length == 1 )
return args[0];
if( args.Length == 2 )
return typeof( KeyValuePair<,> ).MakeGenericType( args );
}
return typeof( object ); // Fallback
}
private object ConvertToFinalCollection( IList list, Type expectedType, Type elemType )
{
if( expectedType.IsArray )
{
var arr = Array.CreateInstance( elemType, list.Count );
list.CopyTo( arr, 0 );
return arr;
}
if( expectedType.IsGenericType )
{
var genDef = expectedType.GetGenericTypeDefinition();
if( genDef == typeof( ImmutableArray<> ) )
{
var method = typeof( ImmutableArray ).GetMethods()
.First( m => m.Name == "ToImmutableArray" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
.MakeGenericMethod( elemType );
return method.Invoke( null, new object[] { list } )!;
}
if( genDef == typeof( ImmutableList<> ) )
{
var method = typeof( ImmutableList ).GetMethods()
.First( m => m.Name == "ToImmutableList" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
.MakeGenericMethod( elemType );
return method.Invoke( null, new object[] { list } )!;
}
// Add more immutable/dictionary handlers here (using MakeImmutableDictionary etc.)
}
return list; // Default to List<T> if no specific match
}
}
// --- Object Handler (Default/Complex) ---
public partial class ObjectHandler : ITypeHandler
{
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => true; // Fallback
private (XmlNode? source, bool isAttribute) FindValueSource( XmlElement parent, string name )
{
if( parent.HasAttribute( name ) )
{
return (parent.Attributes[name], true);
}
foreach( XmlNode node in parent.ChildNodes )
{
if( node.NodeType == XmlNodeType.Element && node.Name == name )
{
return (node, false);
}
}
return (null, false);
}
private bool ShouldSetValue( MemberMeta member, bool isHydrating )
{
// [Dont] members are filtered out during metadata generation.
// If a member is present in the metadata and a value is found in the XML,
// we should always set it. This handles both new creation and hydration/merge.
return true;
}
private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing )
{
long id = -1;
bool first = true;
// Check existing
if( existing != null && type.IsAssignableFrom( existing.GetType() ) )
{
id = xml._idGen.GetId( existing, out first );
return (existing, id);
}
// Create new
object? newObj = null;
try
{
if( type.GetConstructor( Type.EmptyTypes ) != null )
{
newObj = Activator.CreateInstance( type );
}
else
{
newObj = FormatterServices.GetUninitializedObject( type );
}
}
catch( Exception ex )
{
log.exception( ex, $"Failed to create instance of {type.Name}: {ex.Message}" );
return (null, -1);
}
id = xml._idGen.GetId( newObj, out first );
return (newObj, id);
}
}

188
ser/XmlSer_Read.cs Normal file
View File

@ -0,0 +1,188 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
namespace ser;
// --- Primitive Handler ---
public partial class PrimitiveHandler : ser.ITypeHandler
{
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
{
string val = elem.HasAttribute( "v" ) ? elem.GetAttribute( "v" ) : elem.InnerText;
if( val == "null" )
return null;
// So this is an interesting one. Why not use the expected type? Well, we know we have
// data in the XML, it just wont convert to what we want.
return xml._resolver.ConvertSimple( val, expectedType );
}
}
// --- Proxy Handler ---
public partial class ProxyHandler : ITypeHandler
{
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
{
var ti = xml._meta.Get( expectedType ); // Re-get to ensure we have proxy info
if( !elem.HasAttribute( "proxy" ) || !ti.ProxyDef.HasValue )
{
log.warn( $"Proxy read failed for {expectedType.Name}. Fallback needed." );
return null; // Should fall back or throw
}
var proxyVal = elem.GetAttribute( "proxy" );
return ti.ProxyDef.Value.fnDes( expectedType.FullName, proxyVal );
}
}
// --- ISerializable Handler ---
public partial class ISerializableHandler : ITypeHandler
{
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
{
// Create/Get instance (needs FormatterServices for ISerializable)
object obj = existing ?? FormatterServices.GetUninitializedObject( expectedType );
long id = xml.TrackIfGraph( obj, elem ); // Track it
var serInfo = new SerializationInfo( expectedType, new FormatterConverter() );
foreach( XmlNode objNode in elem.ChildNodes )
{
if( objNode is XmlElement childElem )
{
string childName = childElem.Name;
Type? childType = xml._resolver.FindType( childElem.GetAttribute( "_.t" ) );
if( childType != null )
{
var desValue = xml.ReadNode( childElem, childType, null );
serInfo.AddValue( childName, desValue, childType );
}
}
}
var context = new StreamingContext( StreamingContextStates.All ); // Or use xml.Context
var cons = expectedType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null, new[] { typeof( SerializationInfo ), typeof( StreamingContext ) }, null );
if( cons != null )
{
cons.Invoke( obj, new object[] { serInfo, context } );
}
else
{
log.error( $"ISerializable type {expectedType.Name} lacks the required constructor." );
}
if( obj is IDeserializationCallback cb )
cb.OnDeserialization( obj );
return obj;
}
}
// --- Collection Handler ---
public partial class CollectionHandler : ITypeHandler
{
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
{
// Determine element type
Type elemType = GetElementType( expectedType );
// Create a temporary list
var listType = typeof( List<> ).MakeGenericType( elemType );
var list = (IList)Activator.CreateInstance( listType )!;
xml.TrackIfGraph( list, elem ); // Track list if graph
// Populate the list
foreach( XmlNode node in elem.ChildNodes )
{
if( node is XmlElement childElem )
{
list.Add( xml.ReadNode( childElem, elemType, null ) );
}
}
// Convert to the final expected type (Array, Immutable*, List)
return ConvertToFinalCollection( list, expectedType, elemType );
}
}
// --- Object Handler (Default/Complex) ---
public partial class ObjectHandler : ITypeHandler
{
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
{
var actualType = xml._resolver.Resolve( elem, expectedType );
var ti = xml._meta.Get( actualType );
// 1. Get/Create Instance
var (obj, _) = GetOrCreateInstance( xml, elem, actualType, existing );
if( obj == null )
return null;
// Handle graph refs (if already processed)
if( xml._cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) )
{
long id = long.Parse( elem.GetAttribute( "ref" ) );
if( xml._processed.TryGetValue( id, out var processedObj ) )
return processedObj;
}
// Track if it's new
xml.TrackIfGraph( obj, elem );
// 2. Hydrate
foreach( var memberMeta in ti.Members )
{
{
var (valueSource, isAttribute) = FindValueSource( elem, memberMeta.XmlName );
if( valueSource != null )
{
object? memberValue;
object? currentMemberValue = memberMeta.GetValue( obj );
if( isAttribute )
{
memberValue = xml._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
}
else // Child Element
{
memberValue = xml.ReadNode( (XmlElement)valueSource, memberMeta.Type, currentMemberValue );
}
// Set value, respecting ser.Do/ser.Dont and pre-hydration
if( ShouldSetValue( memberMeta, existing != null ) )
{
memberMeta.SetValue( obj, memberValue );
}
}
}
}
// 3. Post-processing
if( obj is ser.I_Serialize iSer )
obj = iSer.OnDeserialize( null );
if( ti.IsImm && obj is io.Obj immObj )
return immObj.Record( $"From XML {elem.Name}" );
return obj;
}
}

133
ser/XmlSer_Tests.cs Normal file
View File

@ -0,0 +1,133 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
//using Org.BouncyCastle.Crypto.IO;
namespace ser;
public record class SimpleImmutable( string Name, int Age ) : io.Timed<SimpleImmutable>;
static public class Test
{
public class ExternalClass
{
public string ActualProperty { get; set; } = "test_ActualProperty_set_inline";
//public string ActualProperty_NotSerialized { get; set; } = "ActualProperty_NotSerialized";
}
[ser.Ser( Types = ser.Types.Implied )]
public partial class LeaveWithExternalClasss
{
//[ser.Do]
//public bool _cccwp_doBool = true;
[ser.ChildPropsAttribute( "ActualProperty" )]
public ExternalClass _leaf_external = new();
//public string _cccwp_doNotSerialize = "test_do_not_serialize";
}
[ser.Ser]
public class TrunkClass
{
public LeaveWithExternalClasss _trunk_leaf = new();
//public int _chf_test = 10;
//private string _chf_priv_string = "test_priv_string";
};
public static void Serialization()
{
ser.XmlCfg cfg = new()
{
Verbose = true,
};
ser.TypeMetaCache metaCache = new( cfg );
//metaCache.AddType( typeof( ClassContainsClassWithProp ), "ActualProperty" );
TrunkClass trunk = new()
{
_trunk_leaf = new()
{
_leaf_external = new()
{
ActualProperty = "ActualProperty_set_in_cons"
}
}
};
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "ActualProperty_set_in_cons" );
var memStream = new MemoryStream();
{
var xml = new ser.XmlSer( cfg, metaCache );
xml.Serialize( memStream, trunk );
}
memStream.Position = 0;
var strXml = System.Text.Encoding.UTF8.GetString( memStream.ToArray() );
var badXml = "<root>\n <prop doBool=\"True\" />\n</root>";
///*
var badXml_02 = @"""<root>
<prop doBool=""True"">
<propHolder>
<ActualProperty ActualProperty=""ActualProperty_set_in_cons"" />
<ActualProperty_NotSerialized ActualProperty_NotSerialized=""ActualProperty_NotSerialized"" />
</propHolder>
<doNotSerialize doNotSerialize=""test_do_not_serialize"" />
</prop>
</root>""";
//*/
Debug.Assert( strXml != badXml );
memStream.Position = 0;
var classHasFields2 = new TrunkClass();
//classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized = "ActualProperty_NotSerialized_set_in_test_01";
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
//Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized" );
{
var xml = new ser.XmlSer( cfg, metaCache );
classHasFields2 = xml.Deserialize<TrunkClass>( memStream );
}
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
//Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
memStream.Position = 0;
var classHasFields3 = new TrunkClass();
{
var xml = new ser.XmlSer( cfg, metaCache );
xml.DeserializeInto( memStream, classHasFields3 );
}
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
//Debug.Assert( classHasFields3._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
}
}

201
ser/XmlSer_Write.cs Normal file
View File

@ -0,0 +1,201 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
namespace ser;
// --- Primitive Handler ---
public partial class PrimitiveHandler : ser.ITypeHandler
{
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( obj == null )
{
writer.WriteStartElement( name );
writer.WriteAttributeString( "v", "null" );
writer.WriteEndElement();
return;
}
bool writeElements = xml._cfg.POD == POD.Elements || forceType || !( writer is XmlTextWriter );
if( !writeElements && writer is XmlTextWriter tw )
writeElements = tw.WriteState != WriteState.Element;
if( writeElements )
writer.WriteStartElement( name );
if( forceType || xml._cfg.POD == POD.Elements )
{
if( forceType )
writer.WriteAttributeString( "_.t", obj.GetType().FullName );
writer.WriteAttributeString( "v", obj.ToString() );
}
else
{
writer.WriteAttributeString( name, obj.ToString() );
}
if( writeElements )
writer.WriteEndElement();
}
}
// --- Proxy Handler ---
public partial class ProxyHandler : ITypeHandler
{
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( obj == null )
{ xml.GetHandler( typeof( object ) ).WriteXml( xml, writer, null, name, memberType, forceType ); return; }
var ti = xml._meta.Get( obj.GetType() );
if( !ti.ProxyDef.HasValue )
{ log.error( "Proxy write called without proxy def!" ); return; }
writer.WriteStartElement( name );
var proxyStr = ti.ProxyDef.Value.fnSer( obj );
// TODO: Allow arbitrary writing here
writer.WriteAttributeString( "proxy", proxyStr );
writer.WriteEndElement();
}
}
// --- ISerializable Handler ---
public partial class ISerializableHandler : ITypeHandler
{
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( obj == null )
{ /* Write null */ return; }
if( !( obj is ISerializable serObj ) )
{ /* Error */ return; }
writer.WriteStartElement( name );
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
{
if( first )
{
var serInfo = new SerializationInfo( obj.GetType(), new FormatterConverter() );
var context = new StreamingContext( StreamingContextStates.All );
serObj.GetObjectData( serInfo, context );
foreach( var member in serInfo )
{
xml.WriteNode( writer, member.Value, refl.TypeToIdentifier( member.Name ), member.ObjectType, true ); // Force type for ISer
}
}
}
writer.WriteEndElement();
}
}
// --- Collection Handler ---
public partial class CollectionHandler : ITypeHandler
{
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( obj == null )
{ /* Write null */ return; }
if( !( obj is IEnumerable collection ) )
{ /* Error */ return; }
writer.WriteStartElement( name );
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
{
if( first )
{
Type elemType = GetElementType( obj.GetType() );
int i = 0;
foreach( var item in collection )
{
xml.WriteNode( writer, item, $"i{i++}", elemType, false );
}
}
}
writer.WriteEndElement();
}
}
// --- Object Handler (Default/Complex) ---
public partial class ObjectHandler : ITypeHandler
{
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
{
if( obj == null )
{ /* Write null */ return; }
writer.WriteStartElement( name );
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
var ti = xml._meta.Get( obj.GetType() );
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
{
if( first )
{
foreach( var memberMeta in ti.Members )
{
if( !memberMeta.IsPodAttribute )
continue;
var value = memberMeta.GetValue( obj );
if( value != null )
{
try
{
writer.WriteAttributeString( memberMeta.XmlName, value.ToString() );
}
catch( Exception ex )
{
log.exception( ex, $"Writing Att {memberMeta.XmlName} = [{value}]" );
}
}
}
foreach( var memberMeta in ti.Members )
{
if( memberMeta.IsPodAttribute )
continue;
var value = memberMeta.GetValue( obj );
if( value != null )
{
try
{
xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false );
}
catch( Exception ex )
{
log.exception( ex, $"Writing Node {memberMeta.XmlName} = [{value}]" );
}
}
}
}
}
writer.WriteEndElement();
}
}

View File

@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpLib", "SharpLib.csproj", "{CC1801F8-7270-47A2-AF73-CCE2900549A9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CC1801F8-7270-47A2-AF73-CCE2900549A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC1801F8-7270-47A2-AF73-CCE2900549A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC1801F8-7270-47A2-AF73-CCE2900549A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC1801F8-7270-47A2-AF73-CCE2900549A9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E2F905DE-69F3-48F3-943C-B8544945C2E9}
EndGlobalSection
EndGlobal

View File

@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpLib", "SharpLib.csproj", "{8D6C0DEE-23AB-41CA-99B0-4575ECBB41F6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8D6C0DEE-23AB-41CA-99B0-4575ECBB41F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D6C0DEE-23AB-41CA-99B0-4575ECBB41F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D6C0DEE-23AB-41CA-99B0-4575ECBB41F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D6C0DEE-23AB-41CA-99B0-4575ECBB41F6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2C2EFBD0-C7A0-4D9C-B6CD-68E0112B6786}
EndGlobalSection
EndGlobal

View File

@ -3,6 +3,8 @@
using System;
namespace time; namespace time;

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics; using System.Diagnostics;
namespace lib; namespace lib;