Whitespace updates
This commit is contained in:
parent
c25b9b3b12
commit
2b5940bef6
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9</TargetFrameworks>
|
<TargetFrameworks>net9.0;net8.0;net10.0</TargetFrameworks>
|
||||||
<RootNamespace>lib</RootNamespace>
|
<RootNamespace>lib</RootNamespace>
|
||||||
<AssemblyVersion>0.0.1.0</AssemblyVersion>
|
<AssemblyVersion>0.0.1.0</AssemblyVersion>
|
||||||
<FileVersion>0.0.1.0</FileVersion>
|
<FileVersion>0.0.1.0</FileVersion>
|
||||||
|
|||||||
@ -118,7 +118,8 @@ public sealed class PpmCompress
|
|||||||
}
|
}
|
||||||
// Else write context escape symbol and continue decrementing the order
|
// Else write context escape symbol and continue decrementing the order
|
||||||
enc.write( ctx.frequencies, 256 );
|
enc.write( ctx.frequencies, 256 );
|
||||||
outerContinue:;
|
outerContinue:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
//outerBreak:
|
//outerBreak:
|
||||||
|
|||||||
@ -111,7 +111,8 @@ public sealed class PpmDecompress
|
|||||||
return symbol;
|
return symbol;
|
||||||
}
|
}
|
||||||
// Else we read the context escape symbol, so continue decrementing the order
|
// Else we read the context escape symbol, so continue decrementing the order
|
||||||
outerContinue:;
|
outerContinue:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
//outerBreak:
|
//outerBreak:
|
||||||
|
|||||||
9
db/DB.cs
9
db/DB.cs
@ -61,9 +61,11 @@ public class DB<TID, T> where T : IID<TID>
|
|||||||
|
|
||||||
var v = lookup( id );
|
var v = lookup( id );
|
||||||
|
|
||||||
v.Match( t => {
|
v.Match( t =>
|
||||||
|
{
|
||||||
tx.checkout( id );
|
tx.checkout( id );
|
||||||
}, () => {
|
}, () =>
|
||||||
|
{
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return (tx, v);
|
return (tx, v);
|
||||||
@ -111,7 +113,8 @@ public class DB<TID, T> where T : IID<TID>
|
|||||||
{
|
{
|
||||||
for( int i = start; i < curCommitted.Count; ++i )
|
for( int i = start; i < curCommitted.Count; ++i )
|
||||||
{
|
{
|
||||||
if( !t.id.Equals( curCommitted[i] ) ) { }
|
if( !t.id.Equals( curCommitted[i] ) )
|
||||||
|
{ }
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//collision = true;
|
//collision = true;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using imm;
|
|
||||||
|
|
||||||
namespace exp;
|
namespace exp;
|
||||||
|
|
||||||
|
|
||||||
abstract public record class Exp<T> : imm.Versioned<Exp<T>>
|
abstract public record class Exp<T> : io.Versioned<Exp<T>>
|
||||||
{
|
{
|
||||||
protected Exp()
|
protected Exp()
|
||||||
:
|
:
|
||||||
|
|||||||
@ -9,7 +9,8 @@ namespace fsm;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class Context{
|
public class Context
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
imm/FSM.cs
24
imm/FSM.cs
@ -1,28 +1,28 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base context for an FSM.
|
/// Base context for an FSM.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete Context type.</typeparam>
|
/// <typeparam name="TSelf">The concrete Context type.</typeparam>
|
||||||
public abstract record class FsmContextBase<TSelf> : imm.Recorded<TSelf>
|
public abstract record class FsmContextBase<TSelf> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmContextBase<TSelf>
|
where TSelf : FsmContextBase<TSelf>
|
||||||
{
|
{
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmContextBase(imm.Recorded<TSelf> original) : base(original) { }
|
protected FsmContextBase( io.Recorded<TSelf> original ) : base( original ) { }
|
||||||
protected FsmContextBase() { }
|
protected FsmContextBase() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base state for an FSM.
|
/// Base state for an FSM.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete State type.</typeparam>
|
/// <typeparam name="TSelf">The concrete State type.</typeparam>
|
||||||
/// <typeparam name="TCtx">The concrete Context type (must be based on FsmContextBase).</typeparam>
|
/// <typeparam name="TCtx">The concrete Context type (must be based on FsmContextBase).</typeparam>
|
||||||
public abstract record class FsmStateBase<TSelf, TCtx> : imm.Recorded<TSelf>
|
public abstract record class FsmStateBase<TSelf, TCtx> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmStateBase<TSelf, TCtx>
|
where TSelf : FsmStateBase<TSelf, TCtx>
|
||||||
where TCtx : FsmContextBase<TCtx>
|
where TCtx : FsmContextBase<TCtx>
|
||||||
{
|
{
|
||||||
@ -43,18 +43,18 @@ public abstract record class FsmStateBase<TSelf, TCtx> : imm.Recorded<TSelf>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmStateBase(imm.Recorded<TSelf> original) : base(original) { }
|
protected FsmStateBase( io.Recorded<TSelf> original ) : base( original ) { }
|
||||||
protected FsmStateBase() { }
|
protected FsmStateBase() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An immutable FSM base class.
|
/// An immutable FSM base class.
|
||||||
/// MUST inherit from imm.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
/// MUST inherit from io.Recorded<TSelf> or Timed<TSelf> in your concrete class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSelf">The concrete FSM type.</typeparam>
|
/// <typeparam name="TSelf">The concrete FSM type.</typeparam>
|
||||||
/// <typeparam name="TState">The concrete State type.</typeparam>
|
/// <typeparam name="TState">The concrete State type.</typeparam>
|
||||||
/// <typeparam name="TCtx">The concrete Context type.</typeparam>
|
/// <typeparam name="TCtx">The concrete Context type.</typeparam>
|
||||||
public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
public abstract record class FsmBase<TSelf, TState, TCtx> : io.Recorded<TSelf>
|
||||||
where TSelf : FsmBase<TSelf, TState, TCtx>
|
where TSelf : FsmBase<TSelf, TState, TCtx>
|
||||||
where TState : FsmStateBase<TState, TCtx>
|
where TState : FsmStateBase<TState, TCtx>
|
||||||
where TCtx : FsmContextBase<TCtx>
|
where TCtx : FsmContextBase<TCtx>
|
||||||
@ -69,7 +69,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Required for 'with' expressions.
|
// Required for 'with' expressions.
|
||||||
protected FsmBase(imm.Recorded<TSelf> original) : base(original)
|
protected FsmBase( io.Recorded<TSelf> original ) : base( original )
|
||||||
{
|
{
|
||||||
var o = original as FsmBase<TSelf, TState, TCtx>;
|
var o = original as FsmBase<TSelf, TState, TCtx>;
|
||||||
Context = o!.Context;
|
Context = o!.Context;
|
||||||
@ -78,7 +78,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transitions the FSM. It automatically uses the 'Process'
|
/// Transitions the FSM. It automatically uses the 'Process'
|
||||||
/// method appropriate for imm.Recorded or Timed, thanks to virtual overrides.
|
/// method appropriate for io.ecorded or Timed, thanks to virtual overrides.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TSelf Transition(
|
public TSelf Transition(
|
||||||
TState newState,
|
TState newState,
|
||||||
@ -93,7 +93,7 @@ public abstract record class FsmBase<TSelf, TState, TCtx> : imm.Recorded<TSelf>
|
|||||||
var (ctxAfterExit, stateAfterExit) = State.OnExit( Context, newState );
|
var (ctxAfterExit, stateAfterExit) = State.OnExit( Context, newState );
|
||||||
var (ctxAfterEnter, stateAfterEnter) = newState.OnEnter( ctxAfterExit, stateAfterExit );
|
var (ctxAfterEnter, stateAfterEnter) = newState.OnEnter( ctxAfterExit, stateAfterExit );
|
||||||
|
|
||||||
// Since 'this' is at least 'imm.Recorded<TSelf>', we can call the
|
// Since 'this' is at least 'io.Recorded<TSelf>', we can call the
|
||||||
// detailed 'Process'. If 'this' is actually 'Timed<TSelf>', C#'s
|
// detailed 'Process'. If 'this' is actually 'Timed<TSelf>', C#'s
|
||||||
// virtual dispatch will call the 'Timed' override automatically.
|
// virtual dispatch will call the 'Timed' override automatically.
|
||||||
return Process(
|
return Process(
|
||||||
|
|||||||
372
imm/Imm.cs
372
imm/Imm.cs
@ -1,366 +1,31 @@
|
|||||||
#nullable enable
|
//#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace imm;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the base interface for versioned, immutable objects.
|
/// Helper static class for processing immutable objects using a 'ref' pattern.
|
||||||
/// Provides access to metadata and potentially the previous version.
|
/// Provides different levels of processing based on the type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface Obj
|
public static class imm
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the base metadata associated with this version.
|
/// Processes a 'Versioned' object (Level 1).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Metadata_Versioned Meta { get; }
|
public static T LightProcess<T>(
|
||||||
|
ref T obj,
|
||||||
/// <summary>
|
|
||||||
/// Gets the previous version as a base object, if available.
|
|
||||||
/// Returns null if this is the first version or if history is not tracked.
|
|
||||||
/// </summary>
|
|
||||||
Obj? Old { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new version without functional change.
|
|
||||||
/// Returns the new version as an Obj.
|
|
||||||
/// </summary>
|
|
||||||
Obj Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obj delegate for change notifications.
|
|
||||||
/// </summary>
|
|
||||||
public delegate void ChangeDelegate<T>( T? oldVersion, T newVersion );
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a generic interface for immutable objects,
|
|
||||||
/// providing access to basic processing functions and change notifications.
|
|
||||||
/// </summary>
|
|
||||||
public interface Obj<T> : Obj where T : Obj<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the change delegate associated with this object.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
ChangeDelegate<T> OnChange { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a transformation and creates a new version using basic processing.
|
|
||||||
/// </summary>
|
|
||||||
T Process(
|
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "Processed",
|
string reason = "Processed" // TODO Replace the string with a $"" that cinludes some info
|
||||||
[CallerMemberName] string dbgName = "",
|
) where T : io.Versioned<T>
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new version without a functional change using basic processing.
|
|
||||||
/// Uses 'new' to provide a type-safe return.
|
|
||||||
/// </summary>
|
|
||||||
new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
static public class ObjExtensions
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
obj = obj.Process( fn, reason );
|
||||||
/// Creates a new version of the object with the specified reason.
|
|
||||||
/// </summary>
|
|
||||||
public static T Record<T>( this T obj, string reason = "Recorded" ) where T : Obj
|
|
||||||
{
|
|
||||||
if( obj is Recorded<T> recorded )
|
|
||||||
{
|
|
||||||
return recorded.Record( reason );
|
|
||||||
}
|
|
||||||
else if( obj is Versioned<T> versioned )
|
|
||||||
{
|
|
||||||
return versioned.Record( reason );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Dont care
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --- Metadata Hierarchy ---
|
|
||||||
|
|
||||||
public interface VersionedMeta
|
|
||||||
{
|
|
||||||
public uint Version { get; }
|
|
||||||
public string Reason { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obj metadata for version tracking.
|
/// Processes a 'Recorded' object (Level 2), capturing caller info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record Metadata_Versioned
|
|
||||||
{
|
|
||||||
public uint Version { get; init; } = 1;
|
|
||||||
public string Reason { get; init; } = "Created";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface RecordedMeta : VersionedMeta
|
|
||||||
{
|
|
||||||
public string MemberName { get; }
|
|
||||||
public string FilePath { get; }
|
|
||||||
public int LineNumber { get; }
|
|
||||||
public string Expression { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Metadata for version and recording (debug/caller info, history).
|
|
||||||
/// </summary>
|
|
||||||
public record Metadata_Recorded : Metadata_Versioned, RecordedMeta
|
|
||||||
{
|
|
||||||
internal object? OldObject { get; init; } = null;
|
|
||||||
public string MemberName { get; init; } = "";
|
|
||||||
public string FilePath { get; init; } = "";
|
|
||||||
public int LineNumber { get; init; } = 0;
|
|
||||||
public string Expression { get; init; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TimedMeta : RecordedMeta
|
|
||||||
{
|
|
||||||
public DateTime CreatedAt { get; }
|
|
||||||
public DateTime TouchedAt { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Metadata for version, recording, and timing.
|
|
||||||
/// </summary>
|
|
||||||
public record Metadata_Timed : Metadata_Recorded, TimedMeta
|
|
||||||
{
|
|
||||||
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
|
||||||
public DateTime TouchedAt { get; init; } = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Record Hierarchy ---
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 1: Basic versioning. Implements Obj<T>.
|
|
||||||
/// </summary>
|
|
||||||
public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
|
||||||
{
|
|
||||||
public Metadata_Versioned Meta { get; init; } = new();
|
|
||||||
|
|
||||||
[DebuggerBrowsable( DebuggerBrowsableState.Never )]
|
|
||||||
[JsonIgnore]
|
|
||||||
public ChangeDelegate<T> OnChange { get; set; } = ( o, n ) => { };
|
|
||||||
|
|
||||||
public virtual Obj? Old => null;
|
|
||||||
|
|
||||||
Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
[JsonIgnore]
|
|
||||||
Obj? Obj.Old => this.Old;
|
|
||||||
|
|
||||||
public Versioned() { }
|
|
||||||
protected Versioned( Versioned<T> original )
|
|
||||||
{
|
|
||||||
OnChange = original.OnChange;
|
|
||||||
Meta = original.Meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "Processed",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
var newVersion = next with
|
|
||||||
{
|
|
||||||
Meta = new Metadata_Versioned { /*...*/ },
|
|
||||||
OnChange = current.OnChange
|
|
||||||
};
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Basic Record. Made virtual. Implements Obj<T>.Record.
|
|
||||||
/// </summary>
|
|
||||||
public virtual T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implements Obj.Record by calling the virtual T Record.
|
|
||||||
/// </summary>
|
|
||||||
Obj Obj.Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgName, dbgPath, dbgLine );
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 2: Adds history and caller info.
|
|
||||||
/// </summary>
|
|
||||||
public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
|
||||||
{
|
|
||||||
new public Metadata_Recorded Meta { get; init; } = new();
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
new public T? Old => Meta.OldObject as T;
|
|
||||||
|
|
||||||
//public override Obj? Old => this.Old;
|
|
||||||
//Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
|
|
||||||
public Recorded() { }
|
|
||||||
protected Recorded( Recorded<T> original ) : base( original ) { Meta = original.Meta; }
|
|
||||||
|
|
||||||
public override T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
var newMeta = current.Meta with {
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgName,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = expStr,
|
|
||||||
OldObject = current,
|
|
||||||
};
|
|
||||||
|
|
||||||
var newVersion = next with { Meta = newMeta, OnChange = current.OnChange };
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
|
||||||
{
|
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Level 3: Adds timestamps.
|
|
||||||
/// </summary>
|
|
||||||
public record class Timed<T> : Recorded<T> where T : Timed<T>
|
|
||||||
{
|
|
||||||
new public Metadata_Timed Meta { get; init; } = new();
|
|
||||||
//Metadata_Versioned Obj.Meta => this.Meta;
|
|
||||||
public TimeSpan SinceLastTouch => Meta.TouchedAt - ( Old?.Meta as Metadata_Timed )?.TouchedAt ?? TimeSpan.Zero;
|
|
||||||
public TimeSpan TotalAge => Meta.TouchedAt - Meta.CreatedAt;
|
|
||||||
|
|
||||||
public Timed() { }
|
|
||||||
protected Timed( Timed<T> original ) : base( original ) { Meta = original.Meta; }
|
|
||||||
|
|
||||||
|
|
||||||
public override T Process(
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgMethod = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpStr = "" )
|
|
||||||
{
|
|
||||||
var current = (T)this;
|
|
||||||
var next = fn( current );
|
|
||||||
|
|
||||||
if( ReferenceEquals( current, next ) )
|
|
||||||
return current;
|
|
||||||
|
|
||||||
/*
|
|
||||||
var newMeta = new Metadata_Timed
|
|
||||||
{
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgMethod,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = dbgExpression,
|
|
||||||
OldObject = current,
|
|
||||||
CreatedAt = current.Meta.CreatedAt,
|
|
||||||
TouchedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
var newMeta = current.Meta with
|
|
||||||
{
|
|
||||||
Version = current.Meta.Version + 1,
|
|
||||||
Reason = reason,
|
|
||||||
MemberName = dbgMethod,
|
|
||||||
FilePath = dbgPath,
|
|
||||||
LineNumber = dbgLine,
|
|
||||||
Expression = dbgExpStr,
|
|
||||||
OldObject = current,
|
|
||||||
TouchedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
// Testing: Shouldnt need the OnChange
|
|
||||||
var newVersion = next with { Meta = newMeta };
|
|
||||||
newVersion.OnChange( current, newVersion );
|
|
||||||
return newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public new T Record(
|
|
||||||
string reason = "Recorded",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
|
||||||
{
|
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public static class TimedExt
|
|
||||||
{
|
|
||||||
public static T Process<T>(
|
public static T Process<T>(
|
||||||
ref T obj,
|
ref T obj,
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
@ -368,11 +33,20 @@ public static class TimedExt
|
|||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgName = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
||||||
) where T : imm.Timed<T>
|
where T : io.Recorded<T> // Catches Recorded and Timed
|
||||||
{
|
{
|
||||||
|
// This will call the 'Timed' override if T is Timed,
|
||||||
|
// or the 'Recorded' override if T is Recorded.
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string LogDiff<T>( T cur, T old, [CallerArgumentExpression( "cur" )] string dbgExpCur = "", [CallerArgumentExpression( "old" )] string dbgExpOld = "" )
|
||||||
|
{
|
||||||
|
return $"{dbgExpCur} changed from {old} to {cur}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// No specific Process needed for Timed, as it's caught by Recorded<T>
|
||||||
|
// and its Process override handles the timing.
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
@ -6,12 +6,12 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace imm;
|
namespace io;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An immutable list implementation that tracks history, metadata, and time.
|
/// An immutable list implementation that tracks history, metadata, and time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record class List<T> : Timed<List<T>>, IImmutableList<T>
|
public record class List<T> : io.Timed<List<T>>, IImmutableList<T>
|
||||||
{
|
{
|
||||||
static public List<T> Empty { get; } = new();
|
static public List<T> Empty { get; } = new();
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ public record class List<T> : Timed<List<T>>, IImmutableList<T>
|
|||||||
|
|
||||||
public List() { }
|
public List() { }
|
||||||
// Required for 'with' expressions to work with the base class hierarchy
|
// Required for 'with' expressions to work with the base class hierarchy
|
||||||
protected List(Timed<List<T>> original) : base(original) { }
|
protected List( io.Timed<List<T>> original ) : base( original ) { }
|
||||||
|
|
||||||
// Helper to apply changes using the Process method
|
// Helper to apply changes using the Process method
|
||||||
private List<T> Change(
|
private List<T> Change(
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace imm;
|
|
||||||
|
|||||||
379
imm/io.cs
Normal file
379
imm/io.cs
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace io;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base interface for versioned, immutable objects.
|
||||||
|
/// Provides access to metadata and potentially the previous version.
|
||||||
|
/// </summary>
|
||||||
|
public interface Obj
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the base metadata associated with this version.
|
||||||
|
/// </summary>
|
||||||
|
Metadata_Versioned Meta { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the previous version as a base object, if available.
|
||||||
|
/// Returns null if this is the first version or if history is not tracked.
|
||||||
|
/// </summary>
|
||||||
|
Obj? DebugOld { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version without functional change.
|
||||||
|
/// Returns the new version as an Obj.
|
||||||
|
/// </summary>
|
||||||
|
Obj Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obj delegate for change notifications.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void ChangeDelegate<T>( T? oldVersion, T newVersion );
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a generic interface for immutable objects,
|
||||||
|
/// providing access to basic processing functions and change notifications.
|
||||||
|
/// </summary>
|
||||||
|
public interface Obj<T> : Obj where T : Obj<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the change delegate associated with this object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
ChangeDelegate<T> OnChange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a transformation and creates a new version using basic processing.
|
||||||
|
/// </summary>
|
||||||
|
T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "Processed",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version without a functional change using basic processing.
|
||||||
|
/// Uses 'new' to provide a type-safe return.
|
||||||
|
/// </summary>
|
||||||
|
new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
static public class ObjExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new version of the object with the specified reason.
|
||||||
|
/// </summary>
|
||||||
|
public static T Record<T>( this T obj, string reason = "Recorded" ) where T : Obj
|
||||||
|
{
|
||||||
|
if( obj is Recorded<T> recorded )
|
||||||
|
{
|
||||||
|
return recorded.Record( reason );
|
||||||
|
}
|
||||||
|
else if( obj is Versioned<T> versioned )
|
||||||
|
{
|
||||||
|
return versioned.Record( reason );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Dont care
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// --- Metadata Hierarchy ---
|
||||||
|
|
||||||
|
public interface VersionedMeta
|
||||||
|
{
|
||||||
|
public uint Version { get; }
|
||||||
|
public string Reason { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obj metadata for version tracking.
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Versioned
|
||||||
|
{
|
||||||
|
public uint Version { get; init; } = 1;
|
||||||
|
public string Reason { get; init; } = "Created";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface RecordedMeta : VersionedMeta
|
||||||
|
{
|
||||||
|
public string MemberName { get; }
|
||||||
|
public string FilePath { get; }
|
||||||
|
public int LineNumber { get; }
|
||||||
|
public string Expression { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata for version and recording (debug/caller info, history).
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Recorded : Metadata_Versioned, RecordedMeta
|
||||||
|
{
|
||||||
|
internal object? OldObject { get; init; } = null;
|
||||||
|
public string MemberName { get; init; } = "";
|
||||||
|
public string FilePath { get; init; } = "";
|
||||||
|
public int LineNumber { get; init; } = 0;
|
||||||
|
public string Expression { get; init; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface TimedMeta : RecordedMeta
|
||||||
|
{
|
||||||
|
public DateTime CreatedAt { get; }
|
||||||
|
public DateTime TouchedAt { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata for version, recording, and timing.
|
||||||
|
/// </summary>
|
||||||
|
public record Metadata_Timed : Metadata_Recorded, TimedMeta
|
||||||
|
{
|
||||||
|
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
||||||
|
public DateTime TouchedAt { get; init; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Record Hierarchy ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 1: Basic versioning. Implements Obj<T>.
|
||||||
|
/// </summary>
|
||||||
|
public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
||||||
|
{
|
||||||
|
public Metadata_Versioned Meta { get; init; } = new();
|
||||||
|
|
||||||
|
[DebuggerBrowsable( DebuggerBrowsableState.Never )]
|
||||||
|
[JsonIgnore]
|
||||||
|
public ChangeDelegate<T> OnChange { get; set; } = ( o, n ) => { };
|
||||||
|
|
||||||
|
public virtual Obj? DebugOld => null;
|
||||||
|
|
||||||
|
Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
[JsonIgnore]
|
||||||
|
Obj? Obj.DebugOld => this.DebugOld;
|
||||||
|
|
||||||
|
public Versioned() { }
|
||||||
|
protected Versioned( Versioned<T> original )
|
||||||
|
{
|
||||||
|
OnChange = original.OnChange;
|
||||||
|
Meta = original.Meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "Processed",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
var newVersion = next with
|
||||||
|
{
|
||||||
|
Meta = new Metadata_Versioned { /*...*/ },
|
||||||
|
OnChange = current.OnChange
|
||||||
|
};
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Basic Record. Made virtual. Implements Obj<T>.Record.
|
||||||
|
/// </summary>
|
||||||
|
public virtual T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements Obj.Record by calling the virtual T Record.
|
||||||
|
/// </summary>
|
||||||
|
Obj Obj.Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgName, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 2: Adds history and caller info.
|
||||||
|
/// </summary>
|
||||||
|
public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
||||||
|
{
|
||||||
|
new public Metadata_Recorded Meta { get; init; } = new();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
new public T? DebugOld => Meta.OldObject as T;
|
||||||
|
|
||||||
|
//public override Obj? DebugOld => this.DebugOld;
|
||||||
|
//Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
|
||||||
|
public Recorded() { }
|
||||||
|
protected Recorded( Recorded<T> original ) : base( original ) { Meta = original.Meta; }
|
||||||
|
|
||||||
|
public override T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
var newMeta = current.Meta with
|
||||||
|
{
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgName,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = expStr,
|
||||||
|
OldObject = current,
|
||||||
|
};
|
||||||
|
|
||||||
|
var newVersion = next with { Meta = newMeta, OnChange = current.OnChange };
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
|
{
|
||||||
|
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Level 3: Adds timestamps.
|
||||||
|
/// </summary>
|
||||||
|
public record class Timed<T> : Recorded<T> where T : Timed<T>
|
||||||
|
{
|
||||||
|
new public Metadata_Timed Meta { get; init; } = new();
|
||||||
|
//Metadata_Versioned Obj.Meta => this.Meta;
|
||||||
|
public TimeSpan SinceLastTouch => Meta.TouchedAt - ( DebugOld?.Meta as Metadata_Timed )?.TouchedAt ?? TimeSpan.Zero;
|
||||||
|
public TimeSpan TotalAge => Meta.TouchedAt - Meta.CreatedAt;
|
||||||
|
|
||||||
|
public Timed() { }
|
||||||
|
protected Timed( Timed<T> original ) : base( original ) { Meta = original.Meta; }
|
||||||
|
|
||||||
|
|
||||||
|
public override T Process(
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgMethod = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string dbgExpStr = "" )
|
||||||
|
{
|
||||||
|
var current = (T)this;
|
||||||
|
var next = fn( current );
|
||||||
|
|
||||||
|
if( ReferenceEquals( current, next ) )
|
||||||
|
return current;
|
||||||
|
|
||||||
|
/*
|
||||||
|
var newMeta = new Metadata_Timed
|
||||||
|
{
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgMethod,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = dbgExpression,
|
||||||
|
OldObject = current,
|
||||||
|
CreatedAt = current.Meta.CreatedAt,
|
||||||
|
TouchedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
var newMeta = current.Meta with
|
||||||
|
{
|
||||||
|
Version = current.Meta.Version + 1,
|
||||||
|
Reason = reason,
|
||||||
|
MemberName = dbgMethod,
|
||||||
|
FilePath = dbgPath,
|
||||||
|
LineNumber = dbgLine,
|
||||||
|
Expression = dbgExpStr,
|
||||||
|
OldObject = current,
|
||||||
|
TouchedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
// Testing: Shouldnt need the OnChange
|
||||||
|
var newVersion = next with { Meta = newMeta };
|
||||||
|
newVersion.OnChange( current, newVersion );
|
||||||
|
return newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new T Record(
|
||||||
|
string reason = "Recorded",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
|
{
|
||||||
|
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static class TimedExt
|
||||||
|
{
|
||||||
|
public static T Process<T>(
|
||||||
|
ref T obj,
|
||||||
|
Func<T, T> fn,
|
||||||
|
string reason = "",
|
||||||
|
[CallerMemberName] string dbgName = "",
|
||||||
|
[CallerFilePath] string dbgPath = "",
|
||||||
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
|
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
||||||
|
) where T : io.Timed<T>
|
||||||
|
{
|
||||||
|
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
52
imm/iu.cs
52
imm/iu.cs
@ -1,52 +0,0 @@
|
|||||||
//#nullable enable
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using imm;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper static class for processing immutable objects using a 'ref' pattern.
|
|
||||||
/// Provides different levels of processing based on the type.
|
|
||||||
/// </summary>
|
|
||||||
public static class iu
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Processes a 'Versioned' object (Level 1).
|
|
||||||
/// </summary>
|
|
||||||
public static T LightProcess<T>(
|
|
||||||
ref T obj,
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "Processed" )
|
|
||||||
where T : Versioned<T>
|
|
||||||
{
|
|
||||||
obj = obj.Process( fn, reason );
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Processes a 'Recorded' object (Level 2), capturing caller info.
|
|
||||||
/// </summary>
|
|
||||||
public static T Process<T>(
|
|
||||||
ref T obj,
|
|
||||||
Func<T, T> fn,
|
|
||||||
string reason = "",
|
|
||||||
[CallerMemberName] string dbgName = "",
|
|
||||||
[CallerFilePath] string dbgPath = "",
|
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
|
||||||
where T : Recorded<T> // Catches Recorded and Timed
|
|
||||||
{
|
|
||||||
// This will call the 'Timed' override if T is Timed,
|
|
||||||
// or the 'Recorded' override if T is Recorded.
|
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string LogDiff<T>( T cur, T old, [CallerArgumentExpression( "cur" )] string dbgExpCur = "", [CallerArgumentExpression( "old" )] string dbgExpOld = "" )
|
|
||||||
{
|
|
||||||
return $"{dbgExpCur} changed from {old} to {cur}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// No specific Process needed for Timed, as it's caught by Recorded<T>
|
|
||||||
// and its Process override handles the timing.
|
|
||||||
}
|
|
||||||
@ -17,7 +17,7 @@ using System.Collections.Immutable;
|
|||||||
|
|
||||||
namespace ser;
|
namespace ser;
|
||||||
|
|
||||||
public record CodeGenConfig : imm.Recorded<CodeGenConfig>
|
public record CodeGenConfig : io.Recorded<CodeGenConfig>
|
||||||
{
|
{
|
||||||
// Whitelists (if needed, otherwise rely on attributes/defaults)
|
// Whitelists (if needed, otherwise rely on attributes/defaults)
|
||||||
public ImmutableDictionary<string, ImmutableList<string>> WLProps { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
public ImmutableDictionary<string, ImmutableList<string>> WLProps { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
||||||
@ -111,9 +111,12 @@ public class TypeStructureAnalyzer
|
|||||||
{
|
{
|
||||||
var (hasDo, hasDont, propName) = GetMemberAttributes( mi, out var actualMiForAtts );
|
var (hasDo, hasDont, propName) = GetMemberAttributes( mi, out var actualMiForAtts );
|
||||||
|
|
||||||
if (hasDont) return false;
|
if( hasDont )
|
||||||
if (mi.GetCustomAttribute<NonSerializedAttribute>(true) != null) return false;
|
return false;
|
||||||
if (mi.Name.Contains("k__BackingField") && !propName.Any()) return false; // Skip if backing but no prop found
|
if( mi.GetCustomAttribute<NonSerializedAttribute>( true ) != null )
|
||||||
|
return false;
|
||||||
|
if( mi.Name.Contains( "k__BackingField" ) && !propName.Any() )
|
||||||
|
return false; // Skip if backing but no prop found
|
||||||
|
|
||||||
string name = string.IsNullOrEmpty( propName ) ? mi.Name : propName;
|
string name = string.IsNullOrEmpty( propName ) ? mi.Name : propName;
|
||||||
|
|
||||||
@ -135,7 +138,8 @@ public class TypeStructureAnalyzer
|
|||||||
if( fi.Name.StartsWith( "<" ) && fi.Name.EndsWith( "BackingField" ) )
|
if( fi.Name.StartsWith( "<" ) && fi.Name.EndsWith( "BackingField" ) )
|
||||||
{
|
{
|
||||||
var gtIndex = fi.Name.IndexOf( '>' );
|
var gtIndex = fi.Name.IndexOf( '>' );
|
||||||
if (gtIndex > 1) {
|
if( gtIndex > 1 )
|
||||||
|
{
|
||||||
return (true, fi.Name.Substring( 1, gtIndex - 1 ));
|
return (true, fi.Name.Substring( 1, gtIndex - 1 ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,8 +154,11 @@ public class TypeStructureAnalyzer
|
|||||||
{
|
{
|
||||||
propName = IsBackingField( fi ).Item2;
|
propName = IsBackingField( fi ).Item2;
|
||||||
var propInfo = mi.DeclaringType?.GetProperty( propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
|
var propInfo = mi.DeclaringType?.GetProperty( propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
|
||||||
if (propInfo != null) actualMi = propInfo;
|
if( propInfo != null )
|
||||||
} else if (mi is PropertyInfo) {
|
actualMi = propInfo;
|
||||||
|
}
|
||||||
|
else if( mi is PropertyInfo )
|
||||||
|
{
|
||||||
propName = mi.Name;
|
propName = mi.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +171,10 @@ public class TypeStructureAnalyzer
|
|||||||
|
|
||||||
private Type GetElementType( Type collectionType )
|
private Type GetElementType( Type collectionType )
|
||||||
{
|
{
|
||||||
if (collectionType.IsArray) return collectionType.GetElementType()!;
|
if( collectionType.IsArray )
|
||||||
if (collectionType.IsGenericType) return collectionType.GetGenericArguments().Last(); // Usually last (e.g., List<T>, Dict<K,V>)
|
return collectionType.GetElementType()!;
|
||||||
|
if( collectionType.IsGenericType )
|
||||||
|
return collectionType.GetGenericArguments().Last(); // Usually last (e.g., List<T>, Dict<K,V>)
|
||||||
return typeof( object ); // Fallback
|
return typeof( object ); // Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -186,7 +186,8 @@ namespace lib
|
|||||||
|
|
||||||
doc.Load( reader );
|
doc.Load( reader );
|
||||||
|
|
||||||
if( doc.DocumentElement == null ) return null;
|
if( doc.DocumentElement == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
if( t == null )
|
if( t == null )
|
||||||
return Deserialize( doc.DocumentElement );
|
return Deserialize( doc.DocumentElement );
|
||||||
@ -256,7 +257,8 @@ namespace lib
|
|||||||
{
|
{
|
||||||
TypeCode typeCode = Type.GetTypeCode( type );
|
TypeCode typeCode = Type.GetTypeCode( type );
|
||||||
|
|
||||||
if( _cfg.VerboseLogging ) log.info( $"{type.FriendlyName()}.{name} {existing} {mi?.Name}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"{type.FriendlyName()}.{name} {existing} {mi?.Name}" );
|
||||||
|
|
||||||
if( typeCode != TypeCode.Object )
|
if( typeCode != TypeCode.Object )
|
||||||
{
|
{
|
||||||
@ -276,7 +278,8 @@ namespace lib
|
|||||||
|
|
||||||
if( obj is ser.I_Serialize iser )
|
if( obj is ser.I_Serialize iser )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
obj = iser.OnDeserialize( null );
|
obj = iser.OnDeserialize( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,7 +317,8 @@ namespace lib
|
|||||||
|
|
||||||
private object DeserializeConcrete( XmlElement elem, MemberInfo mi, string name, Type type )
|
private object DeserializeConcrete( XmlElement elem, MemberInfo mi, string name, Type type )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
|
|
||||||
string val = "";
|
string val = "";
|
||||||
|
|
||||||
@ -398,7 +402,8 @@ namespace lib
|
|||||||
|
|
||||||
private object HydrateObject( XmlElement elem, MemberInfo mi, Type finalType, object obj )
|
private object HydrateObject( XmlElement elem, MemberInfo mi, Type finalType, object obj )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
|
|
||||||
if( obj is IList )
|
if( obj is IList )
|
||||||
{
|
{
|
||||||
@ -488,9 +493,10 @@ namespace lib
|
|||||||
|
|
||||||
private object HydrateObjectOfNarrowType( XmlElement elem, MemberInfo mi, Type narrowType, object obj )
|
private object HydrateObjectOfNarrowType( XmlElement elem, MemberInfo mi, Type narrowType, object obj )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
|
|
||||||
var isImm = typeof(imm.Obj).IsAssignableFrom( narrowType );
|
var isImm = typeof( io.Obj ).IsAssignableFrom( narrowType );
|
||||||
|
|
||||||
XmlNodeList allChildren = elem.ChildNodes;
|
XmlNodeList allChildren = elem.ChildNodes;
|
||||||
|
|
||||||
@ -648,7 +654,7 @@ namespace lib
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var imm = obj as imm.Obj;
|
var imm = obj as io.Obj;
|
||||||
var newObj = imm.Record( $"From XML {fromStr}:{elem.ParentNode?.Name}{elem.Name}" );
|
var newObj = imm.Record( $"From XML {fromStr}:{elem.ParentNode?.Name}{elem.Name}" );
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
@ -686,7 +692,8 @@ namespace lib
|
|||||||
|
|
||||||
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
|
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
|
||||||
|
|
||||||
if( _cfg.VerboseLogging ) log.info( $"{finalType?.FriendlyName()}({type?.FriendlyName()}) refInt {refInt} exitingObj = {obj?.ToString()}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"{finalType?.FriendlyName()}({type?.FriendlyName()}) refInt {refInt} exitingObj = {obj?.ToString()}" );
|
||||||
|
|
||||||
obj = createObject( elem, finalType, refInt, obj );
|
obj = createObject( elem, finalType, refInt, obj );
|
||||||
|
|
||||||
@ -695,7 +702,8 @@ namespace lib
|
|||||||
|
|
||||||
private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list )
|
private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
|
|
||||||
XmlNodeList arrNodeList = elem.ChildNodes;
|
XmlNodeList arrNodeList = elem.ChildNodes;
|
||||||
|
|
||||||
@ -731,7 +739,8 @@ namespace lib
|
|||||||
typeElem = typeof( KeyValuePair<,> ).MakeGenericType( type.GenericTypeArguments );
|
typeElem = typeof( KeyValuePair<,> ).MakeGenericType( type.GenericTypeArguments );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _cfg.VerboseLogging ) log.info( $"DserCol {type.GetType().FriendlyName()} {typeElem.Name} into reflT {mi.ReflectedType.FriendlyName()} declT {mi.DeclaringType.FriendlyName()} {mi.Name}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"DserCol {type.GetType().FriendlyName()} {typeElem.Name} into reflT {mi.ReflectedType.FriendlyName()} declT {mi.DeclaringType.FriendlyName()} {mi.Name}" );
|
||||||
|
|
||||||
string refString = elem.GetAttribute( "ref" );
|
string refString = elem.GetAttribute( "ref" );
|
||||||
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
|
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
|
||||||
@ -776,7 +785,8 @@ namespace lib
|
|||||||
|
|
||||||
var typeGen = Type.MakeGenericSignatureType( type );
|
var typeGen = Type.MakeGenericSignatureType( type );
|
||||||
|
|
||||||
if( _cfg.VerboseLogging ) log.info( $"TypeGen: {typeGen.FriendlyName()}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"TypeGen: {typeGen.FriendlyName()}" );
|
||||||
|
|
||||||
if( type == typeof( ImmutableArray<> ).MakeGenericType( typeElem ) )
|
if( type == typeof( ImmutableArray<> ).MakeGenericType( typeElem ) )
|
||||||
{
|
{
|
||||||
@ -833,7 +843,8 @@ namespace lib
|
|||||||
|
|
||||||
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
|
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"" );
|
||||||
|
|
||||||
Type typeElem = type.GetElementType();
|
Type typeElem = type.GetElementType();
|
||||||
|
|
||||||
@ -883,7 +894,8 @@ namespace lib
|
|||||||
|
|
||||||
if( _cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
|
if( _cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"Reuse object" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"Reuse object" );
|
||||||
return m_alreadySerialized[refInt];
|
return m_alreadySerialized[refInt];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -893,7 +905,8 @@ namespace lib
|
|||||||
|
|
||||||
if( isProxy )
|
if( isProxy )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"use Proxy" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"use Proxy" );
|
||||||
object obj = null;
|
object obj = null;
|
||||||
|
|
||||||
var tryType = type;
|
var tryType = type;
|
||||||
@ -937,7 +950,8 @@ namespace lib
|
|||||||
|
|
||||||
if( isSubclass )
|
if( isSubclass )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"Using existing obj {existingObj?.ToString()}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"Using existing obj {existingObj?.ToString()}" );
|
||||||
return existingObj;
|
return existingObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -953,12 +967,14 @@ namespace lib
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"For {type.FriendlyName()} check for constructors" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"For {type.FriendlyName()} check for constructors" );
|
||||||
|
|
||||||
var cons = type.GetConstructor( Type.EmptyTypes );
|
var cons = type.GetConstructor( Type.EmptyTypes );
|
||||||
if( cons != null )
|
if( cons != null )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"Activator.CreateInstance" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"Activator.CreateInstance" );
|
||||||
obj = Activator.CreateInstance( type );
|
obj = Activator.CreateInstance( type );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -967,15 +983,18 @@ namespace lib
|
|||||||
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
|
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _cfg.VerboseLogging ) log.info( $"Got obj {obj?.ToString()}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"Got obj {obj?.ToString()}" );
|
||||||
}
|
}
|
||||||
catch( Exception )
|
catch( Exception )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"GetUninitializedObject" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"GetUninitializedObject" );
|
||||||
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
|
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
|
||||||
if( _cfg.VerboseLogging ) log.info( $"Got obj {obj?.ToString()}" );
|
if( _cfg.VerboseLogging )
|
||||||
|
log.info( $"Got obj {obj?.ToString()}" );
|
||||||
}
|
}
|
||||||
catch( Exception exInner )
|
catch( Exception exInner )
|
||||||
{
|
{
|
||||||
@ -1290,7 +1309,7 @@ namespace lib
|
|||||||
HashSet<string> whitelistFields, whitelistProps;
|
HashSet<string> whitelistFields, whitelistProps;
|
||||||
GetFilters( _cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
|
GetFilters( _cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
|
||||||
|
|
||||||
var isImm = typeof(imm.Obj).IsAssignableFrom( narrowType );
|
var isImm = typeof( io.Obj ).IsAssignableFrom( narrowType );
|
||||||
|
|
||||||
if( doFields || doImpls )
|
if( doFields || doImpls )
|
||||||
{
|
{
|
||||||
@ -1323,8 +1342,10 @@ namespace lib
|
|||||||
|
|
||||||
if( isImm )
|
if( isImm )
|
||||||
{
|
{
|
||||||
if( name == "MetaStorage" ) continue;
|
if( name == "MetaStorage" )
|
||||||
if( name == "Fn" ) continue;
|
continue;
|
||||||
|
if( name == "Fn" )
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !doAtt.Any() && FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) )
|
if( !doAtt.Any() && FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) )
|
||||||
@ -1365,8 +1386,10 @@ namespace lib
|
|||||||
|
|
||||||
if( isImm )
|
if( isImm )
|
||||||
{
|
{
|
||||||
if( name == "MetaStorage" ) continue;
|
if( name == "MetaStorage" )
|
||||||
if( name == "Fn" ) continue;
|
continue;
|
||||||
|
if( name == "Fn" )
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( FilterField( filterProps, doImpls, whitelistProps, childPi as MemberInfo, name ) )
|
if( FilterField( filterProps, doImpls, whitelistProps, childPi as MemberInfo, name ) )
|
||||||
@ -1413,8 +1436,10 @@ namespace lib
|
|||||||
Type type = root.GetType();
|
Type type = root.GetType();
|
||||||
|
|
||||||
Type typeOfMember = typeof( object );
|
Type typeOfMember = typeof( object );
|
||||||
if( mi is FieldInfo fi ) typeOfMember = fi.FieldType;
|
if( mi is FieldInfo fi )
|
||||||
if( mi is PropertyInfo pi ) typeOfMember = pi.PropertyType;
|
typeOfMember = fi.FieldType;
|
||||||
|
if( mi is PropertyInfo pi )
|
||||||
|
typeOfMember = pi.PropertyType;
|
||||||
if( typeOfMember != type )
|
if( typeOfMember != type )
|
||||||
{
|
{
|
||||||
log.info( $"SerArr {typeOfMember.FriendlyName()} {mi?.Name} != {type.FriendlyName()}" );
|
log.info( $"SerArr {typeOfMember.FriendlyName()} {mi?.Name} != {type.FriendlyName()}" );
|
||||||
|
|||||||
@ -37,7 +37,8 @@ public static class LogGCExt
|
|||||||
{
|
{
|
||||||
var index = te.PayloadIndex( name );
|
var index = te.PayloadIndex( name );
|
||||||
|
|
||||||
if( index <= 0 ) return default;
|
if( index <= 0 )
|
||||||
|
return default;
|
||||||
|
|
||||||
var value = te.PayloadValue( index );
|
var value = te.PayloadValue( index );
|
||||||
|
|
||||||
@ -144,28 +145,33 @@ public class LogGC
|
|||||||
{
|
{
|
||||||
var cat = catIn;
|
var cat = catIn;
|
||||||
|
|
||||||
if( blacklist.Contains( te.EventName )) return;
|
if( blacklist.Contains( te.EventName ) )
|
||||||
|
return;
|
||||||
|
|
||||||
{
|
{
|
||||||
var methodBeingCompiledNamespace = te.Get( "MethodBeingCompiledNamespace" );
|
var methodBeingCompiledNamespace = te.Get( "MethodBeingCompiledNamespace" );
|
||||||
if( ( methodBeingCompiledNamespace.StartsWith( "Microsoft" ) || methodBeingCompiledNamespace.StartsWith( "System" ) ) ) return;
|
if( ( methodBeingCompiledNamespace.StartsWith( "Microsoft" ) || methodBeingCompiledNamespace.StartsWith( "System" ) ) )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var methodNamespace = te.PayloadStringByName( "MethodNamespace" ) ?? "";
|
var methodNamespace = te.PayloadStringByName( "MethodNamespace" ) ?? "";
|
||||||
if( ( methodNamespace.StartsWith( "Microsoft" ) || methodNamespace.StartsWith( "System" ) ) ) return;
|
if( ( methodNamespace.StartsWith( "Microsoft" ) || methodNamespace.StartsWith( "System" ) ) )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var ns = te.PayloadStringByName( "TypeName" ) ?? "";
|
var ns = te.PayloadStringByName( "TypeName" ) ?? "";
|
||||||
if( ( ns.StartsWith( "Microsoft" ) || ns.StartsWith( "System" ) ) ) return;
|
if( ( ns.StartsWith( "Microsoft" ) || ns.StartsWith( "System" ) ) )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
var ns = te.PayloadStringByName( "TypeLoad/Stop" ) ?? "";
|
var ns = te.PayloadStringByName( "TypeLoad/Stop" ) ?? "";
|
||||||
if( ns.StartsWith( "Godot" ) ) return;
|
if( ns.StartsWith( "Godot" ) )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -174,7 +180,8 @@ public class LogGC
|
|||||||
if( payloadIndex > 0 )
|
if( payloadIndex > 0 )
|
||||||
{
|
{
|
||||||
var count = (int)te.PayloadValue( payloadIndex );
|
var count = (int)te.PayloadValue( payloadIndex );
|
||||||
if( count > 16 ) return;
|
if( count > 16 )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,8 +199,10 @@ public class LogGC
|
|||||||
var optTier = te.Get( "OptimizationTier" );
|
var optTier = te.Get( "OptimizationTier" );
|
||||||
var methodNamespace = te.Get( "MethodNamespace" );
|
var methodNamespace = te.Get( "MethodNamespace" );
|
||||||
|
|
||||||
if( optTier == "MinOptJitted" ) return;
|
if( optTier == "MinOptJitted" )
|
||||||
if( methodNamespace.StartsWith( "FastSerialization" )) return;
|
return;
|
||||||
|
if( methodNamespace.StartsWith( "FastSerialization" ) )
|
||||||
|
return;
|
||||||
|
|
||||||
log.info( $"{optTier} {LogGCExt.MethodInfo( te )}", cat: te.EventName );
|
log.info( $"{optTier} {LogGCExt.MethodInfo( te )}", cat: te.EventName );
|
||||||
|
|
||||||
@ -204,7 +213,8 @@ public class LogGC
|
|||||||
{
|
{
|
||||||
var methodNamespace = te.Get( "MethodBeingCompiledNamespace" );
|
var methodNamespace = te.Get( "MethodBeingCompiledNamespace" );
|
||||||
|
|
||||||
if( methodNamespace.StartsWith( "FastSerialization" )) return;
|
if( methodNamespace.StartsWith( "FastSerialization" ) )
|
||||||
|
return;
|
||||||
|
|
||||||
log.info( $"Inlining {te.Get( "FailReason" )} {te.Get( "OptimizationTier" )} {LogGCExt.MethodInfo( te, "MethodBeingCompiled" )}", cat: te.EventName );
|
log.info( $"Inlining {te.Get( "FailReason" )} {te.Get( "OptimizationTier" )} {LogGCExt.MethodInfo( te, "MethodBeingCompiled" )}", cat: te.EventName );
|
||||||
|
|
||||||
|
|||||||
@ -18,10 +18,10 @@ using System.Text;
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
T O D O :
|
T O D O :
|
||||||
x) Hook the C# prints from glue.
|
|
||||||
x) Fix
|
|
||||||
x) Refactor various logs in order to do automagic structured logging
|
x) Refactor various logs in order to do automagic structured logging
|
||||||
ref: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler
|
ref: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler
|
||||||
|
x) Add support for structured logging (e.g., JSON, XML)
|
||||||
|
x) Implement a centralized logger that can be used across the application
|
||||||
|
|
||||||
|
|
||||||
D O N E:
|
D O N E:
|
||||||
@ -32,6 +32,63 @@ N O T D O I N G :
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// L L M
|
||||||
|
# Context: Logging Infrastructure (`log` static class)
|
||||||
|
**Strict Rule:** DO NOT use `Console.WriteLine`, `Debug.WriteLine`, or standard `ILogger`. Use the static `log` class for all diagnostics.
|
||||||
|
|
||||||
|
## Core Design Pattern
|
||||||
|
The logging system uses **Caller Info Attributes** (`[CallerMemberName]`, `[CallerFilePath]`, etc.) to automatically capture context. Do not manually format method names or file paths in messages.
|
||||||
|
|
||||||
|
## API Surface
|
||||||
|
### 1. Standard Levels. Standard goto loggin
|
||||||
|
* `log.trace(msg)`, `log.debug(msg)`, `log.info(msg)`
|
||||||
|
* `log.warn(msg)`, `log.high(msg)`
|
||||||
|
* `log.error(msg)`, `log.fatal(msg)`
|
||||||
|
* **Signature:** `(string msg, string cat = "", SourceLoc? loc = null)`
|
||||||
|
* *Note:* `cat` (Category) and `loc` (SourceLocation) are optional; the system auto-resolves `cat` based on directory structure if omitted.
|
||||||
|
|
||||||
|
### 2. Functional Pass-Throughs If necessary
|
||||||
|
Use these to log data flow without breaking the statement chain.
|
||||||
|
* `T log.var<T>(T val)`: Logs `name_of_var = value` and returns `val`.
|
||||||
|
* `T log.call<T>(Func<T> func)`: Logs entry, executes `func`, logs exit + result, returns result.
|
||||||
|
* `void log.call(Action func)`: Logs entry, executes `action`, logs exit.
|
||||||
|
* `log.operations(Action/Func)`: Wraps execution with info logs.
|
||||||
|
|
||||||
|
### 3. Special Types
|
||||||
|
* `Value<T>`: A struct wrapper to capture variable expressions alongside values. Use `log.Value(var)` when you need explicit expression capturing in custom constructs.
|
||||||
|
* `log.exception(Exception ex, string msg)`: specialized dump for exceptions.
|
||||||
|
* `log.logProps(object obj, string header)`: Reflection-based dump of all properties on an object.
|
||||||
|
|
||||||
|
## Idiomatic Usage Examples
|
||||||
|
**Good:**
|
||||||
|
```csharp
|
||||||
|
// Variable inspection (Pass-through)
|
||||||
|
int x = log.var(Calculate());
|
||||||
|
|
||||||
|
// Function tracing
|
||||||
|
var result = log.call(() => ComplexCalculation(input));
|
||||||
|
|
||||||
|
// Exception handling
|
||||||
|
try { ... }
|
||||||
|
catch (Exception ex) { log.exception(ex, "Calculation failed"); }
|
||||||
|
|
||||||
|
// Object Dump
|
||||||
|
log.logProps(userConfig, "Current Config State");
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bad:**
|
||||||
|
```csharp
|
||||||
|
int x = Calculate();
|
||||||
|
Console.WriteLine("x is " + x); // Forbidden
|
||||||
|
log.info($"x = {x}"); // Redundant manual formatting
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
public record struct Value<T>( T _val, string _exp = "" )
|
public record struct Value<T>( T _val, string _exp = "" )
|
||||||
{
|
{
|
||||||
public static T Default = default!;
|
public static T Default = default!;
|
||||||
@ -190,7 +247,7 @@ static public class log
|
|||||||
All = File | Console,
|
All = File | Console,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region LogEvent
|
||||||
public struct LogEvent
|
public struct LogEvent
|
||||||
{
|
{
|
||||||
public DateTime Time;
|
public DateTime Time;
|
||||||
@ -254,6 +311,7 @@ static public class log
|
|||||||
|
|
||||||
return logEvent;
|
return logEvent;
|
||||||
}
|
}
|
||||||
|
#endregion // LogEvent
|
||||||
|
|
||||||
public delegate void Log_delegate( LogEvent evt );
|
public delegate void Log_delegate( LogEvent evt );
|
||||||
|
|
||||||
|
|||||||
@ -128,7 +128,8 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
|
return false;
|
||||||
return obj is BoundingBoxExt && Equals( (BoundingBoxExt)obj );
|
return obj is BoundingBoxExt && Equals( (BoundingBoxExt)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -192,7 +192,8 @@ namespace math
|
|||||||
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
|
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
|
||||||
public static unsafe void FromPoints( Vec3[] points, out BoundingSphere result )
|
public static unsafe void FromPoints( Vec3[] points, out BoundingSphere result )
|
||||||
{
|
{
|
||||||
if (points == null) throw new ArgumentNullException("points");
|
if( points == null )
|
||||||
|
throw new ArgumentNullException( "points" );
|
||||||
fixed( void* pointsPtr = points )
|
fixed( void* pointsPtr = points )
|
||||||
{
|
{
|
||||||
FromPoints( (IntPtr)pointsPtr, 0, points.Length, lib.Util.SizeOf<Vec3>(), out result );
|
FromPoints( (IntPtr)pointsPtr, 0, points.Length, lib.Util.SizeOf<Vec3>(), out result );
|
||||||
|
|||||||
@ -227,10 +227,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return R;
|
case 0:
|
||||||
case 1: return G;
|
return R;
|
||||||
case 2: return B;
|
case 1:
|
||||||
case 3: return A;
|
return G;
|
||||||
|
case 2:
|
||||||
|
return B;
|
||||||
|
case 3:
|
||||||
|
return A;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color run from 0 to 3, inclusive." );
|
||||||
@ -240,11 +244,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: R = value; break;
|
case 0:
|
||||||
case 1: G = value; break;
|
R = value;
|
||||||
case 2: B = value; break;
|
break;
|
||||||
case 3: A = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color run from 0 to 3, inclusive.");
|
G = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
B = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
A = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,11 +369,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
return ( max + min ) / 2;
|
return ( max + min ) / 2;
|
||||||
}
|
}
|
||||||
@ -385,11 +402,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
delta = max - min;
|
delta = max - min;
|
||||||
|
|
||||||
@ -430,11 +451,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
// if max == min, then there is no color and
|
// if max == min, then there is no color and
|
||||||
// the saturation is zero.
|
// the saturation is zero.
|
||||||
|
|||||||
@ -149,9 +149,12 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return R;
|
case 0:
|
||||||
case 1: return G;
|
return R;
|
||||||
case 2: return B;
|
case 1:
|
||||||
|
return G;
|
||||||
|
case 2:
|
||||||
|
return B;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color3 run from 0 to 2, inclusive." );
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color3 run from 0 to 2, inclusive." );
|
||||||
@ -161,10 +164,17 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: R = value; break;
|
case 0:
|
||||||
case 1: G = value; break;
|
R = value;
|
||||||
case 2: B = value; break;
|
break;
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color3 run from 0 to 2, inclusive.");
|
case 1:
|
||||||
|
G = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
B = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color3 run from 0 to 2, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -231,10 +231,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return R;
|
case 0:
|
||||||
case 1: return G;
|
return R;
|
||||||
case 2: return B;
|
case 1:
|
||||||
case 3: return A;
|
return G;
|
||||||
|
case 2:
|
||||||
|
return B;
|
||||||
|
case 3:
|
||||||
|
return A;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color4 run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color4 run from 0 to 3, inclusive." );
|
||||||
@ -244,11 +248,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: R = value; break;
|
case 0:
|
||||||
case 1: G = value; break;
|
R = value;
|
||||||
case 2: B = value; break;
|
break;
|
||||||
case 3: A = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Color4 run from 0 to 3, inclusive.");
|
G = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
B = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
A = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for Color4 run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -189,10 +189,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return B;
|
case 0:
|
||||||
case 1: return G;
|
return B;
|
||||||
case 2: return R;
|
case 1:
|
||||||
case 3: return A;
|
return G;
|
||||||
|
case 2:
|
||||||
|
return R;
|
||||||
|
case 3:
|
||||||
|
return A;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for ColorBGRA run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for ColorBGRA run from 0 to 3, inclusive." );
|
||||||
@ -202,11 +206,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: B = value; break;
|
case 0:
|
||||||
case 1: G = value; break;
|
B = value;
|
||||||
case 2: R = value; break;
|
break;
|
||||||
case 3: A = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for ColorBGRA run from 0 to 3, inclusive.");
|
G = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
R = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
A = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( nameof( index ), "Indices for ColorBGRA run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,11 +303,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
return ( max + min ) / 2;
|
return ( max + min ) / 2;
|
||||||
}
|
}
|
||||||
@ -319,11 +336,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
delta = max - min;
|
delta = max - min;
|
||||||
|
|
||||||
@ -364,11 +385,15 @@ namespace math
|
|||||||
max = r;
|
max = r;
|
||||||
min = r;
|
min = r;
|
||||||
|
|
||||||
if (g > max) max = g;
|
if( g > max )
|
||||||
if (b > max) max = b;
|
max = g;
|
||||||
|
if( b > max )
|
||||||
|
max = b;
|
||||||
|
|
||||||
if (g < min) min = g;
|
if( g < min )
|
||||||
if (b < min) min = b;
|
min = g;
|
||||||
|
if( b < min )
|
||||||
|
min = b;
|
||||||
|
|
||||||
// if max == min, then there is no color and
|
// if max == min, then there is no color and
|
||||||
// the saturation is zero.
|
// the saturation is zero.
|
||||||
|
|||||||
@ -127,8 +127,10 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(ColorHSV)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( ColorHSV ) )
|
||||||
|
return false;
|
||||||
return Equals( (ColorHSV)obj );
|
return Equals( (ColorHSV)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -124,8 +124,10 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
|
case 1:
|
||||||
|
return Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Double2 run from 0 to 1, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double2 run from 0 to 1, inclusive." );
|
||||||
@ -135,9 +137,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Double2 run from 0 to 1, inclusive.");
|
break;
|
||||||
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double2 run from 0 to 1, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -152,9 +152,12 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Double3 run from 0 to 2, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double3 run from 0 to 2, inclusive." );
|
||||||
@ -164,10 +167,17 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Double3 run from 0 to 2, inclusive.");
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double3 run from 0 to 2, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,10 +183,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
case 3: return W;
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
|
case 3:
|
||||||
|
return W;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Double4 run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double4 run from 0 to 3, inclusive." );
|
||||||
@ -196,11 +200,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
case 3: W = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Double4 run from 0 to 3, inclusive.");
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
W = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Double4 run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
math/Int2.cs
17
math/Int2.cs
@ -139,8 +139,10 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
|
case 1:
|
||||||
|
return Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Int2 run from 0 to 1, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Int2 run from 0 to 1, inclusive." );
|
||||||
@ -150,9 +152,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Int2 run from 0 to 1, inclusive.");
|
break;
|
||||||
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Int2 run from 0 to 1, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
math/Int3.cs
24
math/Int3.cs
@ -156,9 +156,12 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Int3 run from 0 to 2, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Int3 run from 0 to 2, inclusive." );
|
||||||
@ -168,10 +171,17 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Int3 run from 0 to 2, inclusive.");
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Int3 run from 0 to 2, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -453,7 +453,8 @@ namespace math
|
|||||||
/// <returns>A linear value.</returns>
|
/// <returns>A linear value.</returns>
|
||||||
public static float SRgbToLinear( float sRgbValue )
|
public static float SRgbToLinear( float sRgbValue )
|
||||||
{
|
{
|
||||||
if (sRgbValue < 0.04045f) return sRgbValue / 12.92f;
|
if( sRgbValue < 0.04045f )
|
||||||
|
return sRgbValue / 12.92f;
|
||||||
return (float)Math.Pow( ( sRgbValue + 0.055 ) / 1.055, 2.4 );
|
return (float)Math.Pow( ( sRgbValue + 0.055 ) / 1.055, 2.4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +465,8 @@ namespace math
|
|||||||
/// <returns>The encoded sRGB value.</returns>
|
/// <returns>The encoded sRGB value.</returns>
|
||||||
public static float LinearToSRgb( float linearValue )
|
public static float LinearToSRgb( float linearValue )
|
||||||
{
|
{
|
||||||
if (linearValue < 0.0031308f) return linearValue * 12.92f;
|
if( linearValue < 0.0031308f )
|
||||||
|
return linearValue * 12.92f;
|
||||||
return (float)( 1.055 * Math.Pow( linearValue, 1 / 2.4 ) - 0.055 );
|
return (float)( 1.055 * Math.Pow( linearValue, 1 / 2.4 ) - 0.055 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
218
math/Matrix.cs
218
math/Matrix.cs
@ -175,10 +175,22 @@ namespace math
|
|||||||
float M31, float M32, float M33, float M34,
|
float M31, float M32, float M33, float M34,
|
||||||
float M41, float M42, float M43, float M44 )
|
float M41, float M42, float M43, float M44 )
|
||||||
{
|
{
|
||||||
this.M11 = M11; this.M12 = M12; this.M13 = M13; this.M14 = M14;
|
this.M11 = M11;
|
||||||
this.M21 = M21; this.M22 = M22; this.M23 = M23; this.M24 = M24;
|
this.M12 = M12;
|
||||||
this.M31 = M31; this.M32 = M32; this.M33 = M33; this.M34 = M34;
|
this.M13 = M13;
|
||||||
this.M41 = M41; this.M42 = M42; this.M43 = M43; this.M44 = M44;
|
this.M14 = M14;
|
||||||
|
this.M21 = M21;
|
||||||
|
this.M22 = M22;
|
||||||
|
this.M23 = M23;
|
||||||
|
this.M24 = M24;
|
||||||
|
this.M31 = M31;
|
||||||
|
this.M32 = M32;
|
||||||
|
this.M33 = M33;
|
||||||
|
this.M34 = M34;
|
||||||
|
this.M41 = M41;
|
||||||
|
this.M42 = M42;
|
||||||
|
this.M43 = M43;
|
||||||
|
this.M44 = M44;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -400,22 +412,38 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return M11;
|
case 0:
|
||||||
case 1: return M12;
|
return M11;
|
||||||
case 2: return M13;
|
case 1:
|
||||||
case 3: return M14;
|
return M12;
|
||||||
case 4: return M21;
|
case 2:
|
||||||
case 5: return M22;
|
return M13;
|
||||||
case 6: return M23;
|
case 3:
|
||||||
case 7: return M24;
|
return M14;
|
||||||
case 8: return M31;
|
case 4:
|
||||||
case 9: return M32;
|
return M21;
|
||||||
case 10: return M33;
|
case 5:
|
||||||
case 11: return M34;
|
return M22;
|
||||||
case 12: return M41;
|
case 6:
|
||||||
case 13: return M42;
|
return M23;
|
||||||
case 14: return M43;
|
case 7:
|
||||||
case 15: return M44;
|
return M24;
|
||||||
|
case 8:
|
||||||
|
return M31;
|
||||||
|
case 9:
|
||||||
|
return M32;
|
||||||
|
case 10:
|
||||||
|
return M33;
|
||||||
|
case 11:
|
||||||
|
return M34;
|
||||||
|
case 12:
|
||||||
|
return M41;
|
||||||
|
case 13:
|
||||||
|
return M42;
|
||||||
|
case 14:
|
||||||
|
return M43;
|
||||||
|
case 15:
|
||||||
|
return M44;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Matrix run from 0 to 15, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Matrix run from 0 to 15, inclusive." );
|
||||||
@ -425,23 +453,56 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: M11 = value; break;
|
case 0:
|
||||||
case 1: M12 = value; break;
|
M11 = value;
|
||||||
case 2: M13 = value; break;
|
break;
|
||||||
case 3: M14 = value; break;
|
case 1:
|
||||||
case 4: M21 = value; break;
|
M12 = value;
|
||||||
case 5: M22 = value; break;
|
break;
|
||||||
case 6: M23 = value; break;
|
case 2:
|
||||||
case 7: M24 = value; break;
|
M13 = value;
|
||||||
case 8: M31 = value; break;
|
break;
|
||||||
case 9: M32 = value; break;
|
case 3:
|
||||||
case 10: M33 = value; break;
|
M14 = value;
|
||||||
case 11: M34 = value; break;
|
break;
|
||||||
case 12: M41 = value; break;
|
case 4:
|
||||||
case 13: M42 = value; break;
|
M21 = value;
|
||||||
case 14: M43 = value; break;
|
break;
|
||||||
case 15: M44 = value; break;
|
case 5:
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Matrix run from 0 to 15, inclusive.");
|
M22 = value;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
M23 = value;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
M24 = value;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
M31 = value;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
M32 = value;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
M33 = value;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
M34 = value;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
M41 = value;
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
M42 = value;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
M43 = value;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
M44 = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Matrix run from 0 to 15, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,14 +571,26 @@ namespace math
|
|||||||
{
|
{
|
||||||
float temp;
|
float temp;
|
||||||
|
|
||||||
temp = M21; M21 = M12; M12 = temp;
|
temp = M21;
|
||||||
temp = M31; M31 = M13; M13 = temp;
|
M21 = M12;
|
||||||
temp = M41; M41 = M14; M14 = temp;
|
M12 = temp;
|
||||||
|
temp = M31;
|
||||||
|
M31 = M13;
|
||||||
|
M13 = temp;
|
||||||
|
temp = M41;
|
||||||
|
M41 = M14;
|
||||||
|
M14 = temp;
|
||||||
|
|
||||||
temp = M32; M32 = M23; M23 = temp;
|
temp = M32;
|
||||||
temp = M42; M42 = M24; M24 = temp;
|
M32 = M23;
|
||||||
|
M23 = temp;
|
||||||
|
temp = M42;
|
||||||
|
M42 = M24;
|
||||||
|
M24 = temp;
|
||||||
|
|
||||||
temp = M43; M43 = M34; M34 = temp;
|
temp = M43;
|
||||||
|
M43 = M34;
|
||||||
|
M34 = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1405,10 +1478,22 @@ namespace math
|
|||||||
float d43 = value.M31 * -a4 + value.M32 * a2 + value.M34 * a0;
|
float d43 = value.M31 * -a4 + value.M32 * a2 + value.M34 * a0;
|
||||||
float d44 = value.M31 * a3 + value.M32 * -a1 + value.M33 * a0;
|
float d44 = value.M31 * a3 + value.M32 * -a1 + value.M33 * a0;
|
||||||
|
|
||||||
result.M11 = +d11 * det; result.M12 = -d21 * det; result.M13 = +d31 * det; result.M14 = -d41 * det;
|
result.M11 = +d11 * det;
|
||||||
result.M21 = -d12 * det; result.M22 = +d22 * det; result.M23 = -d32 * det; result.M24 = +d42 * det;
|
result.M12 = -d21 * det;
|
||||||
result.M31 = +d13 * det; result.M32 = -d23 * det; result.M33 = +d33 * det; result.M34 = -d43 * det;
|
result.M13 = +d31 * det;
|
||||||
result.M41 = -d14 * det; result.M42 = +d24 * det; result.M43 = -d34 * det; result.M44 = +d44 * det;
|
result.M14 = -d41 * det;
|
||||||
|
result.M21 = -d12 * det;
|
||||||
|
result.M22 = +d22 * det;
|
||||||
|
result.M23 = -d32 * det;
|
||||||
|
result.M24 = +d42 * det;
|
||||||
|
result.M31 = +d13 * det;
|
||||||
|
result.M32 = -d23 * det;
|
||||||
|
result.M33 = +d33 * det;
|
||||||
|
result.M34 = -d43 * det;
|
||||||
|
result.M41 = -d14 * det;
|
||||||
|
result.M42 = +d24 * det;
|
||||||
|
result.M43 = -d34 * det;
|
||||||
|
result.M44 = +d44 * det;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1883,7 +1968,8 @@ namespace math
|
|||||||
if( j != r )
|
if( j != r )
|
||||||
{
|
{
|
||||||
float sub = matrix[j, lead];
|
float sub = matrix[j, lead];
|
||||||
for (int k = 0; k < columncount; k++) matrix[j, k] -= (sub * matrix[r, k]);
|
for( int k = 0; k < columncount; k++ )
|
||||||
|
matrix[j, k] -= ( sub * matrix[r, k] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1983,14 +2069,22 @@ namespace math
|
|||||||
public static void LookAtLH( ref Vec3 eye, ref Vec3 target, ref Vec3 up, out Matrix result )
|
public static void LookAtLH( ref Vec3 eye, ref Vec3 target, ref Vec3 up, out Matrix result )
|
||||||
{
|
{
|
||||||
Vec3 xaxis, yaxis, zaxis;
|
Vec3 xaxis, yaxis, zaxis;
|
||||||
Vec3.Subtract(ref target, ref eye, out zaxis); zaxis.Normalize();
|
Vec3.Subtract( ref target, ref eye, out zaxis );
|
||||||
Vec3.Cross(ref up, ref zaxis, out xaxis); xaxis.Normalize();
|
zaxis.Normalize();
|
||||||
|
Vec3.Cross( ref up, ref zaxis, out xaxis );
|
||||||
|
xaxis.Normalize();
|
||||||
Vec3.Cross( ref zaxis, ref xaxis, out yaxis );
|
Vec3.Cross( ref zaxis, ref xaxis, out yaxis );
|
||||||
|
|
||||||
result = Matrix.Identity;
|
result = Matrix.Identity;
|
||||||
result.M11 = xaxis.X; result.M21 = xaxis.Y; result.M31 = xaxis.Z;
|
result.M11 = xaxis.X;
|
||||||
result.M12 = yaxis.X; result.M22 = yaxis.Y; result.M32 = yaxis.Z;
|
result.M21 = xaxis.Y;
|
||||||
result.M13 = zaxis.X; result.M23 = zaxis.Y; result.M33 = zaxis.Z;
|
result.M31 = xaxis.Z;
|
||||||
|
result.M12 = yaxis.X;
|
||||||
|
result.M22 = yaxis.Y;
|
||||||
|
result.M32 = yaxis.Z;
|
||||||
|
result.M13 = zaxis.X;
|
||||||
|
result.M23 = zaxis.Y;
|
||||||
|
result.M33 = zaxis.Z;
|
||||||
|
|
||||||
Vec3.Dot( ref xaxis, ref eye, out result.M41 );
|
Vec3.Dot( ref xaxis, ref eye, out result.M41 );
|
||||||
Vec3.Dot( ref yaxis, ref eye, out result.M42 );
|
Vec3.Dot( ref yaxis, ref eye, out result.M42 );
|
||||||
@ -2025,14 +2119,22 @@ namespace math
|
|||||||
public static void LookAtRH( ref Vec3 eye, ref Vec3 target, ref Vec3 up, out Matrix result )
|
public static void LookAtRH( ref Vec3 eye, ref Vec3 target, ref Vec3 up, out Matrix result )
|
||||||
{
|
{
|
||||||
Vec3 xaxis, yaxis, zaxis;
|
Vec3 xaxis, yaxis, zaxis;
|
||||||
Vec3.Subtract(ref eye, ref target, out zaxis); zaxis.Normalize();
|
Vec3.Subtract( ref eye, ref target, out zaxis );
|
||||||
Vec3.Cross(ref up, ref zaxis, out xaxis); xaxis.Normalize();
|
zaxis.Normalize();
|
||||||
|
Vec3.Cross( ref up, ref zaxis, out xaxis );
|
||||||
|
xaxis.Normalize();
|
||||||
Vec3.Cross( ref zaxis, ref xaxis, out yaxis );
|
Vec3.Cross( ref zaxis, ref xaxis, out yaxis );
|
||||||
|
|
||||||
result = Matrix.Identity;
|
result = Matrix.Identity;
|
||||||
result.M11 = xaxis.X; result.M21 = xaxis.Y; result.M31 = xaxis.Z;
|
result.M11 = xaxis.X;
|
||||||
result.M12 = yaxis.X; result.M22 = yaxis.Y; result.M32 = yaxis.Z;
|
result.M21 = xaxis.Y;
|
||||||
result.M13 = zaxis.X; result.M23 = zaxis.Y; result.M33 = zaxis.Z;
|
result.M31 = xaxis.Z;
|
||||||
|
result.M12 = yaxis.X;
|
||||||
|
result.M22 = yaxis.Y;
|
||||||
|
result.M32 = yaxis.Z;
|
||||||
|
result.M13 = zaxis.X;
|
||||||
|
result.M23 = zaxis.Y;
|
||||||
|
result.M33 = zaxis.Z;
|
||||||
|
|
||||||
Vec3.Dot( ref xaxis, ref eye, out result.M41 );
|
Vec3.Dot( ref xaxis, ref eye, out result.M41 );
|
||||||
Vec3.Dot( ref yaxis, ref eye, out result.M42 );
|
Vec3.Dot( ref yaxis, ref eye, out result.M42 );
|
||||||
|
|||||||
@ -153,10 +153,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return Normal.X;
|
case 0:
|
||||||
case 1: return Normal.Y;
|
return Normal.X;
|
||||||
case 2: return Normal.Z;
|
case 1:
|
||||||
case 3: return D;
|
return Normal.Y;
|
||||||
|
case 2:
|
||||||
|
return Normal.Z;
|
||||||
|
case 3:
|
||||||
|
return D;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Plane run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Plane run from 0 to 3, inclusive." );
|
||||||
@ -166,11 +170,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: Normal.X = value; break;
|
case 0:
|
||||||
case 1: Normal.Y = value; break;
|
Normal.X = value;
|
||||||
case 2: Normal.Z = value; break;
|
break;
|
||||||
case 3: D = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Plane run from 0 to 3, inclusive.");
|
Normal.Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Normal.Z = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
D = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Plane run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,8 +78,10 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(Point)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( Point ) )
|
||||||
|
return false;
|
||||||
return Equals( (Point)obj );
|
return Equals( (Point)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -244,10 +244,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
case 3: return W;
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
|
case 3:
|
||||||
|
return W;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Quaternion run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Quaternion run from 0 to 3, inclusive." );
|
||||||
@ -257,11 +261,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
case 3: W = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Quaternion run from 0 to 3, inclusive.");
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
W = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Quaternion run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -425,8 +425,10 @@ namespace math
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(Rectangle)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( Rectangle ) )
|
||||||
|
return false;
|
||||||
return Equals( (Rectangle)obj );
|
return Equals( (Rectangle)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -418,8 +418,10 @@ namespace math
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(RectangleF)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( RectangleF ) )
|
||||||
|
return false;
|
||||||
return Equals( (RectangleF)obj );
|
return Equals( (RectangleF)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -83,8 +83,10 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(Size2)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( Size2 ) )
|
||||||
|
return false;
|
||||||
return Equals( (Size2)obj );
|
return Equals( (Size2)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -83,8 +83,10 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
if (obj.GetType() != typeof(Size2F)) return false;
|
return false;
|
||||||
|
if( obj.GetType() != typeof( Size2F ) )
|
||||||
|
return false;
|
||||||
return Equals( (Size2F)obj );
|
return Equals( (Size2F)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,7 +101,8 @@ namespace math
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if( ReferenceEquals( null, obj ) )
|
||||||
|
return false;
|
||||||
return obj is Size3 && Equals( (Size3)obj );
|
return obj is Size3 && Equals( (Size3)obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -138,8 +138,10 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
|
case 1:
|
||||||
|
return Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Vector2 run from 0 to 1, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector2 run from 0 to 1, inclusive." );
|
||||||
@ -149,9 +151,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Vector2 run from 0 to 1, inclusive.");
|
break;
|
||||||
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector2 run from 0 to 1, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -166,9 +166,12 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Vector3 run from 0 to 2, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector3 run from 0 to 2, inclusive." );
|
||||||
@ -178,10 +181,17 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Vector3 run from 0 to 2, inclusive.");
|
case 1:
|
||||||
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector3 run from 0 to 2, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,10 +195,14 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: return X;
|
case 0:
|
||||||
case 1: return Y;
|
return X;
|
||||||
case 2: return Z;
|
case 1:
|
||||||
case 3: return W;
|
return Y;
|
||||||
|
case 2:
|
||||||
|
return Z;
|
||||||
|
case 3:
|
||||||
|
return W;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException( "index", "Indices for Vector4 run from 0 to 3, inclusive." );
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector4 run from 0 to 3, inclusive." );
|
||||||
@ -208,11 +212,20 @@ namespace math
|
|||||||
{
|
{
|
||||||
switch( index )
|
switch( index )
|
||||||
{
|
{
|
||||||
case 0: X = value; break;
|
case 0:
|
||||||
case 1: Y = value; break;
|
X = value;
|
||||||
case 2: Z = value; break;
|
break;
|
||||||
case 3: W = value; break;
|
case 1:
|
||||||
default: throw new ArgumentOutOfRangeException("index", "Indices for Vector4 run from 0 to 3, inclusive.");
|
Y = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Z = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
W = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException( "index", "Indices for Vector4 run from 0 to 3, inclusive." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|||||||
@ -15,17 +15,21 @@ namespace Tracing
|
|||||||
// No need to override GetHashCode because we don't want to use it as a key in a dictionary
|
// No need to override GetHashCode because we don't want to use it as a key in a dictionary
|
||||||
public override bool Equals( object obj )
|
public override bool Equals( object obj )
|
||||||
{
|
{
|
||||||
if (obj == null) return false;
|
if( obj == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
var stack = obj as AddressStack;
|
var stack = obj as AddressStack;
|
||||||
if (stack == null) return false;
|
if( stack == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
var frameCount = _stack.Count;
|
var frameCount = _stack.Count;
|
||||||
if (frameCount != stack._stack.Count) return false;
|
if( frameCount != stack._stack.Count )
|
||||||
|
return false;
|
||||||
|
|
||||||
for( int i = 0; i < frameCount; i++ )
|
for( int i = 0; i < frameCount; i++ )
|
||||||
{
|
{
|
||||||
if (_stack[i] != stack._stack[i]) return false;
|
if( _stack[i] != stack._stack[i] )
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -144,7 +144,8 @@ namespace ProfilerHelpers
|
|||||||
if( baseAddress == 0 )
|
if( baseAddress == 0 )
|
||||||
{
|
{
|
||||||
// should work if the same module is added more than once
|
// should work if the same module is added more than once
|
||||||
if (Marshal.GetLastWin32Error() == ERROR_SUCCESS) return;
|
if( Marshal.GetLastWin32Error() == ERROR_SUCCESS )
|
||||||
|
return;
|
||||||
|
|
||||||
Console.WriteLine( $"SymLoadModule64 failed for {filename}" );
|
Console.WriteLine( $"SymLoadModule64 failed for {filename}" );
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@ namespace Tracing
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if( _disposed )
|
||||||
|
return;
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
foreach( var methodStore in _methods.Values )
|
foreach( var methodStore in _methods.Values )
|
||||||
|
|||||||
@ -107,7 +107,8 @@ namespace Tracing
|
|||||||
for( int i = 0; i < _stacks.Count; i++ )
|
for( int i = 0; i < _stacks.Count; i++ )
|
||||||
{
|
{
|
||||||
var info = _stacks[i];
|
var info = _stacks[i];
|
||||||
if (stack.Equals(info.Stack)) return info;
|
if( stack.Equals( info.Stack ) )
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -182,7 +182,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
// Let's assume you'll add saving logic here.
|
// Let's assume you'll add saving logic here.
|
||||||
// Mgr.Save(value, path); // Example: Needs implementation
|
// Mgr.Save(value, path); // Example: Needs implementation
|
||||||
|
|
||||||
var immMeta = (value as imm.Obj)?.Meta;
|
var immMeta = ( value as io.Obj )?.Meta;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -433,10 +433,10 @@ public static class Mgr
|
|||||||
var loadedObject = loaderHolder.Load( filename, reason, dbgName, dbgPath, dbgLine );
|
var loadedObject = loaderHolder.Load( filename, reason, dbgName, dbgPath, dbgLine );
|
||||||
if( loadedObject is T value )
|
if( loadedObject is T value )
|
||||||
{
|
{
|
||||||
var meta = (value as imm.Obj)?.Meta;
|
var meta = ( value as io.Obj )?.Meta;
|
||||||
|
|
||||||
// If it's an immutable object, record its loading.
|
// If it's an immutable object, record its loading.
|
||||||
if( value is imm.Obj imm )
|
if( value is io.Obj imm )
|
||||||
{
|
{
|
||||||
return (T)imm.Record( $"Loading bcs {reason}", dbgName, dbgPath, dbgLine );
|
return (T)imm.Record( $"Loading bcs {reason}", dbgName, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,8 @@ public static class scr
|
|||||||
|
|
||||||
public static FieldInfo? GetFieldInfo( Type? t, string name )
|
public static FieldInfo? GetFieldInfo( Type? t, string name )
|
||||||
{
|
{
|
||||||
if (t == null) return null;
|
if( t == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
var fi = t.GetField( name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly );
|
var fi = t.GetField( name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly );
|
||||||
|
|
||||||
@ -82,7 +83,8 @@ public static class scr
|
|||||||
|
|
||||||
// From stack overflow
|
// From stack overflow
|
||||||
static Lazy<ISet<Type>> typeSetLazy =
|
static Lazy<ISet<Type>> typeSetLazy =
|
||||||
new Lazy<ISet<Type>>(() => {
|
new Lazy<ISet<Type>>( () =>
|
||||||
|
{
|
||||||
var types = AppDomain
|
var types = AppDomain
|
||||||
.CurrentDomain
|
.CurrentDomain
|
||||||
.GetAssemblies()
|
.GetAssemblies()
|
||||||
@ -115,14 +117,16 @@ public static class scr
|
|||||||
|
|
||||||
for( int i = 0; i < safeStr.Length; ++i )
|
for( int i = 0; i < safeStr.Length; ++i )
|
||||||
{
|
{
|
||||||
if( s_badChars.Contains(safeStr[i]) ) safeStr[i] = '_';
|
if( s_badChars.Contains( safeStr[i] ) )
|
||||||
|
safeStr[i] = '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
return safeStr.ToString();
|
return safeStr.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static public FileSystemWatcher s_watcher = new();
|
static public FileSystemWatcher s_watcher = new();
|
||||||
static public Action<Assembly> s_fnAss = (ass) => {
|
static public Action<Assembly> s_fnAss = ( ass ) =>
|
||||||
|
{
|
||||||
log.warn( $"Need to replace s_fnAss with custom function" );
|
log.warn( $"Need to replace s_fnAss with custom function" );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,52 @@ namespace ser;
|
|||||||
|
|
||||||
#region Attributes & Enums (Mostly unchanged, ensure these exist)
|
#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
|
public interface I_Serialize
|
||||||
{
|
{
|
||||||
@ -90,7 +136,7 @@ public enum BackingFieldNaming { Short, Regular }
|
|||||||
public enum POD { Attributes, Elements }
|
public enum POD { Attributes, Elements }
|
||||||
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
|
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
|
||||||
|
|
||||||
public record XmlCfg : imm.Recorded<XmlCfg>
|
public record XmlCfg : io.Recorded<XmlCfg>
|
||||||
{
|
{
|
||||||
public bool Verbose { get; init; } = false;
|
public bool Verbose { get; init; } = false;
|
||||||
public Datastructure Structure { get; init; } = Datastructure.Tree;
|
public Datastructure Structure { get; init; } = Datastructure.Tree;
|
||||||
@ -254,7 +300,7 @@ public class TypeMetaCache
|
|||||||
GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps );
|
GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps );
|
||||||
|
|
||||||
|
|
||||||
var isImm = typeof( imm.Obj ).IsAssignableFrom( type );
|
var isImm = typeof( io.Obj ).IsAssignableFrom( type );
|
||||||
|
|
||||||
var typesAtt = type.GetCustomAttribute<ser.Ser>( true );
|
var typesAtt = type.GetCustomAttribute<ser.Ser>( true );
|
||||||
var serTypes = typesAtt?.Types ?? ser.Types.None;
|
var serTypes = typesAtt?.Types ?? ser.Types.None;
|
||||||
@ -708,7 +754,8 @@ public class XmlSer // : IFormatter
|
|||||||
}
|
}
|
||||||
catch( Exception ex )
|
catch( Exception ex )
|
||||||
{
|
{
|
||||||
log.exception( ex, $"XML Load failed: {ex.Message}" ); return;
|
log.exception( ex, $"XML Load failed: {ex.Message}" );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( doc.DocumentElement == null )
|
if( doc.DocumentElement == null )
|
||||||
|
|||||||
@ -179,7 +179,7 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
// 3. Post-processing
|
// 3. Post-processing
|
||||||
if( obj is ser.I_Serialize iSer )
|
if( obj is ser.I_Serialize iSer )
|
||||||
obj = iSer.OnDeserialize( null );
|
obj = iSer.OnDeserialize( null );
|
||||||
if( ti.IsImm && obj is imm.Obj immObj )
|
if( ti.IsImm && obj is io.Obj immObj )
|
||||||
return immObj.Record( $"From XML {elem.Name}" );
|
return immObj.Record( $"From XML {elem.Name}" );
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ using System.Text;
|
|||||||
namespace ser;
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
public record class SimpleImmutable( string Name, int Age ) : io.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
static public class Test
|
static public class Test
|
||||||
{
|
{
|
||||||
|
|||||||
@ -150,7 +150,8 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
{
|
{
|
||||||
foreach( var memberMeta in ti.Members )
|
foreach( var memberMeta in ti.Members )
|
||||||
{
|
{
|
||||||
if( !memberMeta.IsPodAttribute ) continue;
|
if( !memberMeta.IsPodAttribute )
|
||||||
|
continue;
|
||||||
|
|
||||||
var value = memberMeta.GetValue( obj );
|
var value = memberMeta.GetValue( obj );
|
||||||
if( value != null )
|
if( value != null )
|
||||||
@ -169,7 +170,8 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
|
|
||||||
foreach( var memberMeta in ti.Members )
|
foreach( var memberMeta in ti.Members )
|
||||||
{
|
{
|
||||||
if( memberMeta.IsPodAttribute ) continue;
|
if( memberMeta.IsPodAttribute )
|
||||||
|
continue;
|
||||||
|
|
||||||
var value = memberMeta.GetValue( obj );
|
var value = memberMeta.GetValue( obj );
|
||||||
if( value != null )
|
if( value != null )
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.5.002.0
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
|||||||
375
srl/srl.Core.cs
Normal file
375
srl/srl.Core.cs
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace srl;
|
||||||
|
|
||||||
|
// --- INTERFACES ---
|
||||||
|
|
||||||
|
public interface Driver
|
||||||
|
{
|
||||||
|
// Structural
|
||||||
|
void BeginScope( string name, Type type, int id );
|
||||||
|
void EndScope();
|
||||||
|
void BeginCollection( string name, int count );
|
||||||
|
void EndCollection();
|
||||||
|
|
||||||
|
// Data
|
||||||
|
void WriteAttr( string name, string value ); // Primitives & Compact Strings
|
||||||
|
void WriteRef( string name, int id ); // DAG/Cycle Reference
|
||||||
|
|
||||||
|
// Metadata Hook
|
||||||
|
void OnProp( MemberInfo member, string name );
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IInput
|
||||||
|
{
|
||||||
|
string? Value { get; }
|
||||||
|
bool IsLeaf { get; }
|
||||||
|
IInput? GetAttr( string name );
|
||||||
|
IInput? GetChild( string name );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- THE MODEL (Introspection) ---
|
||||||
|
|
||||||
|
public class TypePlan
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public bool IsCollection;
|
||||||
|
public bool IsHybrid; // True if Type has a StringParser registered
|
||||||
|
public StringParser Parser; // The "Compact" converter
|
||||||
|
public List<PropPlan> Props = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PropPlan
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public string MemberName;
|
||||||
|
public Type Type;
|
||||||
|
public bool IsSmart; // Primitive/Enum/String
|
||||||
|
public MemberInfo Info;
|
||||||
|
|
||||||
|
// Fast Accessors
|
||||||
|
public Func<object, object?> Getter;
|
||||||
|
public Action<object, object?> Setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Model
|
||||||
|
{
|
||||||
|
private static Dictionary<Type, TypePlan> _cache = new();
|
||||||
|
|
||||||
|
public static TypePlan Get( Type t )
|
||||||
|
{
|
||||||
|
if( _cache.TryGetValue( t, out var plan ) )
|
||||||
|
return plan;
|
||||||
|
|
||||||
|
plan = new TypePlan { Name = t.Name };
|
||||||
|
|
||||||
|
// 1. Check for Custom Parsers (Hybrid Mode)
|
||||||
|
if( Parsers.TryGet( t, out var parser ) )
|
||||||
|
{
|
||||||
|
plan.IsHybrid = true;
|
||||||
|
plan.Parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check for Collections
|
||||||
|
if( t != typeof( string ) && typeof( IEnumerable ).IsAssignableFrom( t ) )
|
||||||
|
{
|
||||||
|
plan.IsCollection = true;
|
||||||
|
_cache[t] = plan;
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Scan Properties
|
||||||
|
foreach( var p in t.GetProperties( BindingFlags.Public | BindingFlags.Instance ) )
|
||||||
|
{
|
||||||
|
if( Attribute.IsDefined( p, typeof( IgnoreAttribute ) ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var prop = new PropPlan
|
||||||
|
{
|
||||||
|
Name = p.Name, // Default to PascalCase, Drivers can lower if needed
|
||||||
|
MemberName = p.Name,
|
||||||
|
Type = p.PropertyType,
|
||||||
|
Info = p,
|
||||||
|
Getter = ( o ) => p.GetValue( o ),
|
||||||
|
Setter = ( o, v ) => p.SetValue( o, v )
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override name
|
||||||
|
var nameAttr = p.GetCustomAttribute<NameAttribute>();
|
||||||
|
if( nameAttr != null )
|
||||||
|
prop.Name = nameAttr.Name;
|
||||||
|
|
||||||
|
// Is "Smart" (Atomic)?
|
||||||
|
prop.IsSmart = prop.Type.IsPrimitive || prop.Type.IsEnum ||
|
||||||
|
prop.Type == typeof( string ) || prop.Type == typeof( Guid );
|
||||||
|
|
||||||
|
plan.Props.Add( prop );
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache[t] = plan;
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ATTRIBUTES ---
|
||||||
|
public class IgnoreAttribute : Attribute { }
|
||||||
|
public class NameAttribute : Attribute { public string Name; public NameAttribute( string n ) => Name = n; }
|
||||||
|
|
||||||
|
// --- UTILITIES ---
|
||||||
|
|
||||||
|
public struct StringParser
|
||||||
|
{
|
||||||
|
public Func<object, string> To;
|
||||||
|
public Func<string, object> From;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Parsers
|
||||||
|
{
|
||||||
|
private static Dictionary<Type, StringParser> _registry = new();
|
||||||
|
|
||||||
|
public static void Register<T>( Func<T, string> to, Func<string, T> from )
|
||||||
|
{
|
||||||
|
_registry[typeof( T )] = new StringParser { To = o => to( (T)o ), From = s => from( s ) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGet( Type t, out StringParser p )
|
||||||
|
{
|
||||||
|
if( _registry.TryGetValue( t, out p ) )
|
||||||
|
return true;
|
||||||
|
// Auto-Discovery could go here (Static Parse methods)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Binder
|
||||||
|
{
|
||||||
|
// Handles "Cool.Rare" dot notation
|
||||||
|
public static void Apply( object root, string path, string value )
|
||||||
|
{
|
||||||
|
var current = root;
|
||||||
|
var parts = path.Split( '.' );
|
||||||
|
|
||||||
|
for( int i = 0; i < parts.Length; i++ )
|
||||||
|
{
|
||||||
|
var part = parts[i];
|
||||||
|
var isLast = i == parts.Length - 1;
|
||||||
|
var plan = Model.Get( current.GetType() );
|
||||||
|
|
||||||
|
// Case-Insensitive Match
|
||||||
|
var prop = plan.Props.Find( p => p.Name.Equals( part, StringComparison.OrdinalIgnoreCase ) );
|
||||||
|
if( prop == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( isLast )
|
||||||
|
{
|
||||||
|
var val = ParseUtils.Convert( value, prop.Type );
|
||||||
|
prop.Setter( current, val );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var next = prop.Getter( current );
|
||||||
|
if( next == null )
|
||||||
|
{
|
||||||
|
next = Activator.CreateInstance( prop.Type );
|
||||||
|
prop.Setter( current, next );
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ParseUtils
|
||||||
|
{
|
||||||
|
public static object Convert( string raw, Type t )
|
||||||
|
{
|
||||||
|
if( t == typeof( string ) )
|
||||||
|
return raw;
|
||||||
|
if( t == typeof( int ) )
|
||||||
|
return int.Parse( raw );
|
||||||
|
if( t == typeof( float ) )
|
||||||
|
return float.Parse( raw.Replace( "f", "" ) );
|
||||||
|
if( t == typeof( bool ) )
|
||||||
|
return bool.Parse( raw );
|
||||||
|
if( t.IsEnum )
|
||||||
|
return Enum.Parse( t, raw );
|
||||||
|
// Fallback to TypeConverter
|
||||||
|
var cv = TypeDescriptor.GetConverter( t );
|
||||||
|
if( cv != null && cv.CanConvertFrom( typeof( string ) ) )
|
||||||
|
return cv.ConvertFrom( raw );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CopyFields( object src, object dst )
|
||||||
|
{
|
||||||
|
foreach( var p in src.GetType().GetProperties() )
|
||||||
|
if( p.CanRead && p.CanWrite )
|
||||||
|
p.SetValue( dst, p.GetValue( src ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- THE ENGINE (Walker & Loader) ---
|
||||||
|
|
||||||
|
public static class Walker
|
||||||
|
{
|
||||||
|
public class Context
|
||||||
|
{
|
||||||
|
private Dictionary<object, int> _seen = new( ReferenceEqualityComparer.Instance );
|
||||||
|
private int _nextId = 1;
|
||||||
|
public (int, bool) GetId( object o )
|
||||||
|
{
|
||||||
|
if( _seen.TryGetValue( o, out var id ) )
|
||||||
|
return (id, false);
|
||||||
|
_seen[o] = _nextId;
|
||||||
|
return (_nextId++, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Serialize( object root, Driver d )
|
||||||
|
{
|
||||||
|
if( root == null )
|
||||||
|
return;
|
||||||
|
SerializeRecursive( root, d, new Context(), "root", null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SerializeRecursive( object obj, Driver d, Context ctx, string name, MemberInfo? member )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Type type = obj.GetType();
|
||||||
|
var plan = Model.Get( type );
|
||||||
|
|
||||||
|
// STRATEGY 1: COMPACT (Hybrid Parser)
|
||||||
|
if( plan.IsHybrid )
|
||||||
|
{
|
||||||
|
if( member != null )
|
||||||
|
d.OnProp( member, name );
|
||||||
|
d.WriteAttr( name, plan.Parser.To( obj ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STRATEGY 2: DAG CHECK
|
||||||
|
bool isRef = !type.IsValueType && type != typeof( string );
|
||||||
|
if( isRef )
|
||||||
|
{
|
||||||
|
var (id, isNew) = ctx.GetId( obj );
|
||||||
|
if( !isNew )
|
||||||
|
{ d.WriteRef( name, id ); return; }
|
||||||
|
if( member != null )
|
||||||
|
d.OnProp( member, name );
|
||||||
|
d.BeginScope( name, type, id );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( member != null )
|
||||||
|
d.OnProp( member, name );
|
||||||
|
d.BeginScope( name, type, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// STRATEGY 3: COLLECTIONS
|
||||||
|
if( plan.IsCollection )
|
||||||
|
{
|
||||||
|
var list = (IEnumerable)obj;
|
||||||
|
int count = 0; // Simple count (could optimize for ICollection)
|
||||||
|
foreach( var _ in list )
|
||||||
|
count++;
|
||||||
|
|
||||||
|
d.BeginCollection( name, count );
|
||||||
|
foreach( var item in list )
|
||||||
|
SerializeRecursive( item, d, ctx, "item", null );
|
||||||
|
d.EndCollection();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// STRATEGY 4: STANDARD OBJECT
|
||||||
|
foreach( var prop in plan.Props )
|
||||||
|
{
|
||||||
|
var val = prop.Getter( obj );
|
||||||
|
if( prop.IsSmart )
|
||||||
|
{
|
||||||
|
d.OnProp( prop.Info, prop.Name );
|
||||||
|
d.WriteAttr( prop.Name, val?.ToString() ?? "" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( val != null )
|
||||||
|
SerializeRecursive( val, d, ctx, prop.Name, prop.Info );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.EndScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Loader
|
||||||
|
{
|
||||||
|
public static void Load( object target, IInput input )
|
||||||
|
{
|
||||||
|
if( target == null || input == null )
|
||||||
|
return;
|
||||||
|
var plan = Model.Get( target.GetType() );
|
||||||
|
|
||||||
|
// 1. HYBRID PARSE (Compact String)
|
||||||
|
// If we have a parser AND input is just a value "1,1"
|
||||||
|
if( plan.IsHybrid && input.IsLeaf && !string.IsNullOrWhiteSpace( input.Value ) )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var newObj = plan.Parser.From( input.Value );
|
||||||
|
ParseUtils.CopyFields( newObj, target );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. STRUCTURAL MAP
|
||||||
|
foreach( var prop in plan.Props )
|
||||||
|
{
|
||||||
|
// Look for Attribute OR Child Element
|
||||||
|
var sub = input.GetAttr( prop.Name ) ?? input.GetChild( prop.Name );
|
||||||
|
|
||||||
|
// Look for Dot Notation (e.g. "Pos.X") in attributes
|
||||||
|
// (Note: This simple loop doesn't scan ALL attrs for dots,
|
||||||
|
// it relies on the caller or specific recursive logic.
|
||||||
|
// For full dot support on root, we need to iterate input attributes if possible.
|
||||||
|
// But 'Binder' below handles it if we pass the specific attr key).
|
||||||
|
|
||||||
|
if( sub != null )
|
||||||
|
{
|
||||||
|
if( prop.IsSmart )
|
||||||
|
{
|
||||||
|
if( sub.Value != null )
|
||||||
|
prop.Setter( target, ParseUtils.Convert( sub.Value, prop.Type ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var child = prop.Getter( target );
|
||||||
|
if( child == null )
|
||||||
|
{
|
||||||
|
child = Activator.CreateInstance( prop.Type );
|
||||||
|
prop.Setter( target, child );
|
||||||
|
}
|
||||||
|
Load( child, sub );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to scan all attributes on an element for "Cool.Rare" patterns
|
||||||
|
public static void LoadDotNotations( object target, IEnumerable<(string k, string v)> attrs )
|
||||||
|
{
|
||||||
|
foreach( var (k, v) in attrs )
|
||||||
|
{
|
||||||
|
if( k.Contains( '.' ) )
|
||||||
|
Binder.Apply( target, k, v );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
47
srl/srl.Debug.cs
Normal file
47
srl/srl.Debug.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace srl.Debug;
|
||||||
|
|
||||||
|
public class DebugDriver : Driver
|
||||||
|
{
|
||||||
|
private StringBuilder _sb = new();
|
||||||
|
private int _indent = 0;
|
||||||
|
|
||||||
|
public override string ToString() => _sb.ToString();
|
||||||
|
|
||||||
|
private void Line( string s ) => _sb.AppendLine( new string( ' ', _indent * 2 ) + s );
|
||||||
|
|
||||||
|
public void BeginScope( string name, Type type, int id )
|
||||||
|
{
|
||||||
|
var refStr = id > 0 ? $" #{id}" : "";
|
||||||
|
Line( $"[{name}] <{type.Name}>{refStr}" );
|
||||||
|
_indent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndScope() => _indent--;
|
||||||
|
|
||||||
|
public void BeginCollection( string name, int count )
|
||||||
|
{
|
||||||
|
Line( $"[{name}] (Count: {count})" );
|
||||||
|
_indent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndCollection() => _indent--;
|
||||||
|
|
||||||
|
public void WriteAttr( string name, string value )
|
||||||
|
{
|
||||||
|
Line( $"{name} = {value}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteRef( string name, int id )
|
||||||
|
{
|
||||||
|
Line( $"{name} -> See #{id}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProp( MemberInfo m, string name )
|
||||||
|
{
|
||||||
|
// Could log attributes here, e.g. [Tooltip]
|
||||||
|
}
|
||||||
|
}
|
||||||
131
srl/srl.Xml.cs
Normal file
131
srl/srl.Xml.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace srl.Xml;
|
||||||
|
|
||||||
|
// --- INPUT ADAPTER ---
|
||||||
|
public class XmlAdapter : IInput
|
||||||
|
{
|
||||||
|
private XElement _e;
|
||||||
|
private XAttribute _a;
|
||||||
|
|
||||||
|
public XmlAdapter( XElement e ) => _e = e;
|
||||||
|
public XmlAdapter( XAttribute a ) => _a = a;
|
||||||
|
|
||||||
|
public string? Value => _e?.Value ?? _a?.Value;
|
||||||
|
public bool IsLeaf => _a != null || ( _e != null && !_e.HasElements );
|
||||||
|
|
||||||
|
public IInput? GetAttr( string name )
|
||||||
|
{
|
||||||
|
if( _e == null )
|
||||||
|
return null;
|
||||||
|
// Case-Insensitive search
|
||||||
|
var a = _e.Attribute( name ) ?? _e.Attribute( name.ToLower() );
|
||||||
|
return a != null ? new XmlAdapter( a ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IInput? GetChild( string name )
|
||||||
|
{
|
||||||
|
if( _e == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var c = _e.Element( name ) ?? _e.Element( name.ToLower() );
|
||||||
|
|
||||||
|
return c != null ? new XmlAdapter( c ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra helper for Dot Notation scanning
|
||||||
|
public IEnumerable<(string, string)> GetAllAttrs()
|
||||||
|
{
|
||||||
|
if( _e == null )
|
||||||
|
yield break;
|
||||||
|
foreach( var a in _e.Attributes() )
|
||||||
|
yield return (a.Name.LocalName, a.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- OUTPUT DRIVER ---
|
||||||
|
public class XmlDriver : Driver
|
||||||
|
{
|
||||||
|
private Stack<XElement> _stack = new();
|
||||||
|
private XDocument _doc;
|
||||||
|
|
||||||
|
public XDocument Document => _doc;
|
||||||
|
|
||||||
|
public XmlDriver()
|
||||||
|
{
|
||||||
|
_doc = new XDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginScope( string name, Type type, int id )
|
||||||
|
{
|
||||||
|
var el = new XElement( name );
|
||||||
|
|
||||||
|
// Polymorphism Metadata (if needed, e.g. <Item type="Sword">)
|
||||||
|
// el.Add(new XAttribute("_type", type.Name));
|
||||||
|
|
||||||
|
if( id > 0 )
|
||||||
|
el.Add( new XAttribute( "_id", id ) ); // DAG ID
|
||||||
|
|
||||||
|
if( _stack.Count > 0 )
|
||||||
|
_stack.Peek().Add( el );
|
||||||
|
else
|
||||||
|
_doc.Add( el );
|
||||||
|
|
||||||
|
_stack.Push( el );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndScope() => _stack.Pop();
|
||||||
|
|
||||||
|
public void BeginCollection( string name, int count )
|
||||||
|
{
|
||||||
|
// XML doesn't strictly need array wrappers, but it helps structure
|
||||||
|
// We use the same BeginScope logic effectively
|
||||||
|
var el = new XElement( name, new XAttribute( "_count", count ) );
|
||||||
|
if( _stack.Count > 0 )
|
||||||
|
_stack.Peek().Add( el );
|
||||||
|
_stack.Push( el );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndCollection() => _stack.Pop();
|
||||||
|
|
||||||
|
public void WriteAttr( string name, string value )
|
||||||
|
{
|
||||||
|
if( _stack.Count == 0 )
|
||||||
|
return;
|
||||||
|
_stack.Peek().Add( new XAttribute( name, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteRef( string name, int id )
|
||||||
|
{
|
||||||
|
var el = new XElement( name, new XAttribute( "_ref", id ) );
|
||||||
|
_stack.Peek().Add( el );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProp( MemberInfo m, string name ) { /* Optional: Write tooltips/comments */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- FACADE ---
|
||||||
|
public static class XmlSerializer
|
||||||
|
{
|
||||||
|
public static string Serialize( object obj )
|
||||||
|
{
|
||||||
|
var driver = new XmlDriver();
|
||||||
|
srl.Walker.Serialize( obj, driver );
|
||||||
|
return driver.Document.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Deserialize( object root, string xml )
|
||||||
|
{
|
||||||
|
var doc = XDocument.Parse( xml );
|
||||||
|
var adapter = new XmlAdapter( doc.Root );
|
||||||
|
|
||||||
|
// 1. Standard Load
|
||||||
|
srl.Loader.Load( root, adapter );
|
||||||
|
|
||||||
|
// 2. Dot Notation Pass (MyBag Cool.Rare="Changed")
|
||||||
|
srl.Loader.LoadDotNotations( root, adapter.GetAllAttrs() );
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ using System.IO;
|
|||||||
namespace test;
|
namespace test;
|
||||||
|
|
||||||
|
|
||||||
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
public record class SimpleImmutable( string Name, int Age ) : io.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
static public class XmlFormatter2
|
static public class XmlFormatter2
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user