AyCode.Core/AyCode.Core/Serializers/AcSerializerContextBase.cs

137 lines
4.8 KiB
C#

using AyCode.Core.Serializers.Jsons;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace AyCode.Core.Serializers;
/// <summary>
/// Base class for all serializer contexts.
/// Provides GetWrapper for type metadata access with per-context tracking state.
/// GlobalMetadataCache stores metadata (thread-safe), wrappers store per-context tracking.
/// </summary>
/// <typeparam name="TMetadata">The concrete metadata type.</typeparam>
public abstract class AcSerializerContextBase<TMetadata> where TMetadata : TypeMetadataBase
{
public byte MaxDepth { get; private set; }
public ReferenceHandlingMode ReferenceHandling { get; internal set; }
/// <summary>
/// Global shared cache for metadata (thread-safe, shared across all contexts).
/// Generic specialization ensures separate cache per TMetadata type.
/// </summary>
private static readonly ConcurrentDictionary<Type, TMetadata> GlobalMetadataCache = new();
/// <summary>
/// Per-context wrappers containing metadata + tracking state.
/// </summary>
private readonly Dictionary<Type, TypeMetadataWrapper<TMetadata>> _wrappers = new();
/// <summary>
/// Factory function to create metadata. Implemented by derived class.
/// </summary>
protected abstract Func<Type, TMetadata> MetadataFactory { get; }
#region Wrapper Access
/// <summary>
/// Gets or creates a wrapper for the specified type.
/// The wrapper contains metadata (from GlobalMetadataCache) + per-context tracking state.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TypeMetadataWrapper<TMetadata> GetWrapper(Type type)
{
if (_wrappers.TryGetValue(type, out var wrapper))
return wrapper;
return GetWrapperSlow(type);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private TypeMetadataWrapper<TMetadata> GetWrapperSlow(Type type)
{
// Get metadata from global cache (thread-safe)
var metadata = GlobalMetadataCache.GetOrAdd(type, MetadataFactory);
// Create wrapper with metadata + tracking state (per-context)
var wrapper = new TypeMetadataWrapper<TMetadata>(metadata);
_wrappers[type] = wrapper;
return wrapper;
}
#endregion
#region Tracking API - int
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TypeMetadataWrapper<TMetadata> wrapper, int refId, out object? instance)
{
return wrapper.TryGetValue(refId, out instance);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TypeMetadataWrapper<TMetadata> wrapper, long refId, out object? instance)
{
return wrapper.TryGetValue(refId, out instance);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TypeMetadataWrapper<TMetadata> wrapper, Guid refId, out object? instance)
{
return wrapper.TryGetValue(refId, out instance);
}
/// <summary>
/// For deserialization: checks if an object with this Id was already seen.
/// If yes, returns the existing object. If no, stores this object and returns it.
/// Uses IdentityMap.TryGetOrAddValue - single Dictionary operation!
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object TryGetOrStoreInt32(TypeMetadataWrapper<TMetadata> wrapper, object newObj, int id)
{
return id == 0 ? newObj : wrapper.TryGetOrStoreId(id, newObj);
}
/// <summary>
/// For deserialization: checks if an object with this Id was already seen (long version).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object TryGetOrStoreLong(TypeMetadataWrapper<TMetadata> wrapper, object newObj, long id)
{
return id == 0 ? newObj : wrapper.TryGetOrStoreId(id, newObj);
}
/// <summary>
/// For deserialization: checks if an object with this Id was already seen (Guid version).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object TryGetOrStoreGuid(TypeMetadataWrapper<TMetadata> wrapper, object newObj, Guid id)
{
return id == Guid.Empty ? newObj : wrapper.TryGetOrStoreId(id, newObj);
}
#endregion
#region Reset
/// <summary>
/// Resets all wrapper tracking states for reuse.
/// Does not remove wrappers - keeps them for next operation.
/// </summary>
public virtual void Reset(in AcSerializerOptions? options)
{
foreach (var wrapper in _wrappers.Values)
{
wrapper.ResetTracking();
}
if (options == null) return;
MaxDepth = options.MaxDepth;
ReferenceHandling = options.ReferenceHandling;
}
#endregion
}