137 lines
4.8 KiB
C#
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
|
|
}
|