diff --git a/AyCode.Core/Serializers/AcSerializerContextBase.cs b/AyCode.Core/Serializers/AcSerializerContextBase.cs index 7117dee..e60fddc 100644 --- a/AyCode.Core/Serializers/AcSerializerContextBase.cs +++ b/AyCode.Core/Serializers/AcSerializerContextBase.cs @@ -13,8 +13,16 @@ namespace AyCode.Core.Serializers; /// GlobalMetadataCache stores metadata (thread-safe), wrappers store per-context tracking. /// /// The concrete metadata type. -public abstract class AcSerializerContextBase where TMetadata : TypeMetadataBase +/// The concrete options type. +public abstract class AcSerializerContextBase + where TMetadata : TypeMetadataBase + where TOptions : AcSerializerOptions { + /// + /// The options used for this context. Set during Reset. + /// + public TOptions Options { get; private set; } = null!; + public byte MaxDepth { get; private set; } public ReferenceHandlingMode ReferenceHandling { get; internal set; } /// @@ -33,6 +41,19 @@ public abstract class AcSerializerContextBase where TMetadata : TypeM /// protected abstract Func MetadataFactory { get; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool UseTypeReferenceHandling(Type type) + { + var wrapper = GetWrapper(type); + return UseTypeReferenceHandling(wrapper.Metadata); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool UseTypeReferenceHandling(TMetadata metaData) + { + return ReferenceHandling != ReferenceHandlingMode.None && (metaData.IsIId || ReferenceHandling == ReferenceHandlingMode.All); + } + #region Wrapper Access /// @@ -119,15 +140,14 @@ public abstract class AcSerializerContextBase where TMetadata : TypeM /// Resets all wrapper tracking states for reuse. /// Does not remove wrappers - keeps them for next operation. /// - public virtual void Reset(in AcSerializerOptions? options) + public virtual void Reset(TOptions options) { foreach (var wrapper in _wrappers.Values) { wrapper.ResetTracking(); } - if (options == null) return; - + Options = options; MaxDepth = options.MaxDepth; ReferenceHandling = options.ReferenceHandling; } diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContextClass.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContextClass.cs index 0798333..acf654d 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContextClass.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContextClass.cs @@ -11,7 +11,7 @@ public static partial class AcBinaryDeserializer /// Inherits from AcSerializerContextBase for unified metadata caching and IId-based reference tracking. /// Used in composition with the ref struct BinaryDeserializationContext. /// - internal sealed class BinaryDeserializationContextClass : AcSerializerContextBase + internal sealed class BinaryDeserializationContextClass : AcSerializerContextBase { /// /// Factory for creating BinaryDeserializeTypeMetadata instances. @@ -19,6 +19,12 @@ public static partial class AcBinaryDeserializer protected override Func MetadataFactory => static t => new BinaryDeserializeTypeMetadata(t); + public BinaryDeserializationContextClass() + { + // Initialize with default options - will be reset with actual options before use + Reset(AcBinarySerializerOptions.Default); + } + /// /// After PopulateObject, checks if we should reuse an existing IId object. /// Uses the unified tracking from AcSerializerContextBase (IdentityMap). diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs index 04e34f3..5171450 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs @@ -49,7 +49,7 @@ public static partial class AcBinarySerializer /// /// Binary serialization context. Public for generated serializers. /// - internal sealed class BinarySerializationContext : SerializationContextBase, IDisposable + internal sealed class BinarySerializationContext : SerializationContextBase, IDisposable { private const int MinBufferSize = 256; private const int PropertyIndexBufferMaxCache = 512; @@ -72,11 +72,12 @@ public static partial class AcBinarySerializer private int[]? _propertyIndexBuffer; private byte[]? _propertyStateBuffer; - public bool UseStringInterning { get; private set; } - public bool UseMetadata { get; private set; } - public byte MinStringInternLength { get; private set; } - public byte MaxStringInternLength { get; private set; } - public BinaryPropertyFilter? PropertyFilter { get; private set; } + // These properties delegate to Options for convenience + public bool UseStringInterning => Options.UseStringInterning; + public bool UseMetadata => Options.UseMetadata; + public byte MinStringInternLength => Options.MinStringInternLength; + public byte MaxStringInternLength => Options.MaxStringInternLength; + public BinaryPropertyFilter? PropertyFilter => Options.PropertyFilter; public int Position => _position; @@ -93,17 +94,12 @@ public static partial class AcBinarySerializer protected override Func MetadataFactory => static t => new BinarySerializeTypeMetadata(t, HasJsonIgnoreAttribute); - public void Reset(AcBinarySerializerOptions options) + public override void Reset(AcBinarySerializerOptions options) { // Reset wrapper tracking state from base class (IId tracking) base.Reset(options); _position = 0; - UseStringInterning = options.UseStringInterning; - UseMetadata = options.UseMetadata; - MinStringInternLength = options.MinStringInternLength; - MaxStringInternLength = options.MaxStringInternLength; - PropertyFilter = options.PropertyFilter; _initialBufferSize = Math.Max(options.InitialBufferCapacity, MinBufferSize); if (_buffer.Length < _initialBufferSize) diff --git a/AyCode.Core/Serializers/DeserializationContextBase.cs b/AyCode.Core/Serializers/DeserializationContextBase.cs index 4f37ceb..67b3440 100644 --- a/AyCode.Core/Serializers/DeserializationContextBase.cs +++ b/AyCode.Core/Serializers/DeserializationContextBase.cs @@ -9,8 +9,10 @@ namespace AyCode.Core.Serializers; /// Derived classes are sealed for JIT devirtualization (direct call speed). /// /// The concrete metadata type for deserialization. -public abstract class DeserializationContextBase : AcSerializerContextBase +/// The concrete options type. +public abstract class DeserializationContextBase : AcSerializerContextBase where TMetadata : TypeMetadataBase + where TOptions : AcSerializerOptions { #region Object Lookup by Id (to be implemented by derived sealed classes) @@ -41,7 +43,7 @@ public abstract class DeserializationContextBase : AcSerializerContex /// /// Resets deserialization-specific state. Called by derived classes. /// - public override void Reset(in AcSerializerOptions options) + public override void Reset(TOptions options) { base.Reset(options); // Future: Reset deserialization-specific state diff --git a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonDeserializationContext.cs b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonDeserializationContext.cs index 3c38f6c..b15339e 100644 --- a/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonDeserializationContext.cs +++ b/AyCode.Core/Serializers/Jsons/AcJsonDeserializer.JsonDeserializationContext.cs @@ -22,11 +22,11 @@ public static partial class AcJsonDeserializer } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Return(DeserializationContext context, in AcSerializerOptions options) + public static void Return(DeserializationContext context, in AcJsonSerializerOptions options) { if (Pool.Count < MaxPoolSize) { - context.Clear(null); + context.Clear(options); Pool.Enqueue(context); } } @@ -45,7 +45,7 @@ public static partial class AcJsonDeserializer public readonly int RefId = refId; } - private sealed class DeserializationContext : AcSerializerContextBase + private sealed class DeserializationContext : AcSerializerContextBase { // Use shared reference tracker from AcSerializerCommon private readonly AcSerializerCommon.DeserializationReferenceTracker _refTracker = new(); @@ -75,7 +75,7 @@ public static partial class AcJsonDeserializer Reset(options); } - public override void Reset(in AcSerializerOptions? options) + public override void Reset(AcJsonSerializerOptions options) { IsMergeMode = false; ChainTracker = null; @@ -84,7 +84,7 @@ public static partial class AcJsonDeserializer base.Reset(options); } - public void Clear(in AcSerializerOptions? options) + public void Clear(AcJsonSerializerOptions options) { _refTracker.Reset(); _propertiesToResolve?.Clear(); diff --git a/AyCode.Core/Serializers/Jsons/AcJsonSerializer.JsonSerializationContext.cs b/AyCode.Core/Serializers/Jsons/AcJsonSerializer.JsonSerializationContext.cs index 3f2bbfb..3374d60 100644 --- a/AyCode.Core/Serializers/Jsons/AcJsonSerializer.JsonSerializationContext.cs +++ b/AyCode.Core/Serializers/Jsons/AcJsonSerializer.JsonSerializationContext.cs @@ -35,7 +35,7 @@ public static partial class AcJsonSerializer } } - private sealed class JsonSerializationContext : SerializationContextBase, IDisposable + private sealed class JsonSerializationContext : SerializationContextBase, IDisposable { private readonly ArrayBufferWriter _buffer; public Utf8JsonWriter Writer { get; private set; } @@ -62,11 +62,11 @@ public static partial class AcJsonSerializer protected override Func MetadataFactory => static t => new JsonSerializeTypeMetadata(t); - public override void Reset(in AcSerializerOptions options) + public override void Reset(AcJsonSerializerOptions options) { _refTracker.Reset(); - if (ReferenceHandling != ReferenceHandlingMode.None) + if (options.ReferenceHandling != ReferenceHandlingMode.None) { _refTracker.EnsureInitialized(); } diff --git a/AyCode.Core/Serializers/SerializationContextBase.cs b/AyCode.Core/Serializers/SerializationContextBase.cs index e7d2d7b..21d08a5 100644 --- a/AyCode.Core/Serializers/SerializationContextBase.cs +++ b/AyCode.Core/Serializers/SerializationContextBase.cs @@ -11,8 +11,10 @@ namespace AyCode.Core.Serializers; /// Derived classes are sealed for JIT devirtualization (direct call speed). /// /// The concrete metadata type for serialization. -public abstract class SerializationContextBase : AcSerializerContextBase +/// The concrete options type. +public abstract class SerializationContextBase : AcSerializerContextBase where TMetadata : TypeMetadataBase + where TOptions : AcSerializerOptions { private const int BitArraySize = 1024; private const int MaxSmallId = BitArraySize * 64; @@ -103,7 +105,7 @@ public abstract class SerializationContextBase : AcSerializerContextB /// /// Resets serialization-specific state. Called by derived classes. /// - public override void Reset(in AcSerializerOptions options) + public override void Reset(TOptions options) { base.Reset(options); // Future: Reset serialization-specific state diff --git a/AyCode.Core/Serializers/Toons/AcToonSerializer.ToonSerializationContext.cs b/AyCode.Core/Serializers/Toons/AcToonSerializer.ToonSerializationContext.cs index 00c23cc..9636dff 100644 --- a/AyCode.Core/Serializers/Toons/AcToonSerializer.ToonSerializationContext.cs +++ b/AyCode.Core/Serializers/Toons/AcToonSerializer.ToonSerializationContext.cs @@ -49,7 +49,7 @@ public static partial class AcToonSerializer /// Pooled context for Toon serialization. /// Handles output building, indentation, and reference tracking. /// - private sealed class ToonSerializationContext : SerializationContextBase + private sealed class ToonSerializationContext : SerializationContextBase { private readonly StringBuilder _builder; private Dictionary? _scanOccurrences; @@ -58,13 +58,11 @@ public static partial class AcToonSerializer private HashSet? _registeredTypes; private int _nextRefId; - public AcToonSerializerOptions Options { get; private set; } public int CurrentIndentLevel { get; set; } public ToonSerializationContext(AcToonSerializerOptions options) { _builder = new StringBuilder(4096); - Options = options; Reset(options); } @@ -74,13 +72,12 @@ public static partial class AcToonSerializer protected override Func MetadataFactory => static t => new ToonSerializeTypeMetadata(t); - public void Reset(AcToonSerializerOptions options) + public override void Reset(AcToonSerializerOptions options) { - Options = options; CurrentIndentLevel = 0; _nextRefId = 1; - if (ReferenceHandling != ReferenceHandlingMode.None) + if (options.ReferenceHandling != ReferenceHandlingMode.None) { _scanOccurrences ??= new Dictionary(64, ReferenceEqualityComparer.Instance); _writtenRefs ??= new Dictionary(32, ReferenceEqualityComparer.Instance); diff --git a/AyCode.Core/Serializers/TypeMetadataBase.cs b/AyCode.Core/Serializers/TypeMetadataBase.cs index a44d35b..d77f2a5 100644 --- a/AyCode.Core/Serializers/TypeMetadataBase.cs +++ b/AyCode.Core/Serializers/TypeMetadataBase.cs @@ -1,10 +1,11 @@ +using AyCode.Core.Helpers; +using AyCode.Core.Serializers.Jsons; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using AyCode.Core.Helpers; using static AyCode.Core.Helpers.JsonUtilities; namespace AyCode.Core.Serializers; @@ -199,6 +200,12 @@ public abstract class TypeMetadataBase } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool UseReferenceHandling(ReferenceHandlingMode referenceHandling) + { + return referenceHandling != ReferenceHandlingMode.None && (IsIId || referenceHandling == ReferenceHandlingMode.All); + } + /// /// Gets serializable properties with stable property index ordering. /// Properties are ordered hierarchy-aware (base class first, then derived) diff --git a/AyCode.Core/Serializers/TypeMetadataWrapper.cs b/AyCode.Core/Serializers/TypeMetadataWrapper.cs index cb259de..8354620 100644 --- a/AyCode.Core/Serializers/TypeMetadataWrapper.cs +++ b/AyCode.Core/Serializers/TypeMetadataWrapper.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using AyCode.Core.Helpers; +using AyCode.Core.Serializers.Jsons; namespace AyCode.Core.Serializers;