mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-01 17:47:24 +02:00
666 lines
26 KiB
C#
666 lines
26 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
using Robust.Shared.Serialization.Manager.Exceptions;
|
|
using Robust.Shared.Serialization.Markdown;
|
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
|
using Robust.Shared.Serialization.Markdown.Sequence;
|
|
using Robust.Shared.Serialization.Markdown.Value;
|
|
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Shared.Serialization.Manager;
|
|
|
|
public sealed partial class SerializationManager
|
|
{
|
|
/// <summary>
|
|
/// <see cref="CopyCreatorIndex"/>
|
|
/// <see cref="CopierIndex"/>
|
|
/// </summary>
|
|
private static readonly ImmutableArray<Type> SerializerInterfaces = new[]
|
|
{
|
|
typeof(ITypeReader<,>),
|
|
typeof(ITypeInheritanceHandler<,>),
|
|
typeof(ITypeValidator<,>),
|
|
typeof(ITypeCopyCreator<>),
|
|
typeof(ITypeCopier<>),
|
|
typeof(ITypeWriter<>)
|
|
}.ToImmutableArray();
|
|
|
|
private static readonly ImmutableArray<Type> Nodes = new[]
|
|
{
|
|
typeof(MappingDataNode),
|
|
typeof(SequenceDataNode),
|
|
typeof(ValueDataNode),
|
|
}.ToImmutableArray();
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int ReaderIndex = 0;
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int InheritanceHandlerIndex = 1;
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int ValidatorIndex = 2;
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int CopyCreatorIndex = 3;
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int CopierIndex = 4;
|
|
|
|
/// <summary>
|
|
/// <see cref="SerializerInterfaces"/>
|
|
/// </summary>
|
|
private const int WriterIndex = 5;
|
|
|
|
/// <summary>
|
|
/// <see cref="Nodes"/>
|
|
/// </summary>
|
|
private const int MappingIndex = 0;
|
|
|
|
/// <summary>
|
|
/// <see cref="Nodes"/>
|
|
/// </summary>
|
|
private const int SequenceIndex = 1;
|
|
|
|
/// <summary>
|
|
/// <see cref="Nodes"/>
|
|
/// </summary>
|
|
private const int ValueIndex = 2;
|
|
|
|
private SerializerProvider _regularSerializerProvider = default!;
|
|
|
|
private ISawmill _serializerSawmill = default!;
|
|
|
|
private void InitializeTypeSerializers(IEnumerable<Type> typeSerializers)
|
|
{
|
|
DebugTools.AssertEqual(ReaderIndex, SerializerInterfaces.IndexOf(typeof(ITypeReader<,>)));
|
|
DebugTools.AssertEqual(InheritanceHandlerIndex, SerializerInterfaces.IndexOf(typeof(ITypeInheritanceHandler<,>)));
|
|
DebugTools.AssertEqual(ValidatorIndex, SerializerInterfaces.IndexOf(typeof(ITypeValidator<,>)));
|
|
DebugTools.AssertEqual(CopyCreatorIndex, SerializerInterfaces.IndexOf(typeof(ITypeCopyCreator<>)));
|
|
DebugTools.AssertEqual(CopierIndex, SerializerInterfaces.IndexOf(typeof(ITypeCopier<>)));
|
|
|
|
DebugTools.AssertEqual(MappingIndex, Nodes.IndexOf(typeof(MappingDataNode)));
|
|
DebugTools.AssertEqual(SequenceIndex, Nodes.IndexOf(typeof(SequenceDataNode)));
|
|
DebugTools.AssertEqual(ValueIndex, Nodes.IndexOf(typeof(ValueDataNode)));
|
|
|
|
_regularSerializerProvider = new(this, typeSerializers);
|
|
}
|
|
|
|
private object CreateSerializer(Type type)
|
|
{
|
|
DebugTools.Assert(!type.IsGenericTypeDefinition);
|
|
DebugTools.Assert(!type.IsAbstract);
|
|
|
|
var result = Activator.CreateInstance(type)!;
|
|
DependencyCollection.InjectDependencies(result);
|
|
if (result is BaseTypeSerializer ser)
|
|
{
|
|
ser.SerMan = this;
|
|
ser.Log = _serializerSawmill;
|
|
}
|
|
|
|
if (result is IPostInjectInit postInject)
|
|
postInject.PostInject();
|
|
|
|
return result;
|
|
}
|
|
|
|
[Obsolete]
|
|
public bool TryGetCopierOrCreator<TType>(out ITypeCopier<TType>? copier, out ITypeCopyCreator<TType>? copyCreator, ISerializationContext? context = null)
|
|
{
|
|
if (context != null)
|
|
{
|
|
context.SerializerProvider.TryGetCopierOrCreator(out copier, out copyCreator);
|
|
if (copier != null || copyCreator != null)
|
|
return true;
|
|
}
|
|
|
|
_regularSerializerProvider.TryGetCopierOrCreator(out copier, out copyCreator);
|
|
return copier != null || copyCreator != null;
|
|
}
|
|
|
|
[Obsolete]
|
|
public bool TryCustomCopy<T>(T source, ref T target, SerializationHookContext hookCtx, bool hasHooks, ISerializationContext? context = null)
|
|
{
|
|
if (target != null && source is ISerializationGenerated)
|
|
return false;
|
|
|
|
if (TryGetCopierOrCreator<T>(out var copier, out var copyCreator, context))
|
|
{
|
|
if (copier != null)
|
|
{
|
|
CopyTo(copier, source, ref target, hookCtx, context);
|
|
return true;
|
|
}
|
|
|
|
target = CreateCopy(copyCreator!, source, hookCtx, context);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public sealed class SerializerProvider
|
|
{
|
|
private readonly SerializationManager _ser;
|
|
|
|
public SerializerProvider(ISerializationManager ser, IEnumerable<Type> typeSerializers) : this(ser)
|
|
{
|
|
foreach (var typeSerializer in typeSerializers)
|
|
{
|
|
RegisterSerializer(typeSerializer);
|
|
}
|
|
}
|
|
|
|
public SerializerProvider(ISerializationManager ser)
|
|
{
|
|
// cast it here so every user of this can just directly pass it from a [Dependency] without casting it themselves
|
|
_ser = (SerializationManager) ser;
|
|
foreach (var serializerInterface in SerializerInterfaces)
|
|
{
|
|
RegisterSerializerInterface(serializerInterface);
|
|
}
|
|
}
|
|
|
|
private (object? Regular, object? Generic, bool Init)[] _typeNodeSerializersArray = [];
|
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<(Type ObjectType, Type NodeType), object>> _typeNodeSerializers = new();
|
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, object>> _typeSerializers = new();
|
|
|
|
// TODO make this a 1d array containing the 6 interfaces
|
|
/// <summary>
|
|
/// Type serializers indexed by their type serializer and type
|
|
/// that they serialize.
|
|
/// <see cref="SerializationManager.SerializerInterfaces"/> for the first index.
|
|
/// </summary>
|
|
private (object? Regular, object? Generic)[]?[] _typeSerializersArray = [];
|
|
|
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<(Type ObjectType, Type NodeType), Type>> _genericTypeNodeSerializers = new();
|
|
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, Type>> _genericTypeSerializers = new();
|
|
|
|
private readonly List<Type> _typeNodeInterfaces = new();
|
|
private readonly List<Type> _typeInterfaces = new();
|
|
|
|
private readonly Lock _lock = new();
|
|
|
|
#region GetSerializerMethods
|
|
|
|
public bool TryGetTypeNodeSerializer<TInterface, TType, TNode>([NotNullWhen(true)] out TInterface? serializer)
|
|
where TInterface : BaseSerializerInterfaces.ITypeNodeInterface<TType, TNode>
|
|
where TNode : DataNode
|
|
{
|
|
serializer = default;
|
|
object? rawSerializer;
|
|
var index = TypeSerializerType<TInterface, TType, TNode>.Index;
|
|
if (index < _typeNodeSerializersArray.Length)
|
|
{
|
|
ref var serializers = ref _typeNodeSerializersArray[index];
|
|
if (serializers.Init)
|
|
{
|
|
if (serializers.Regular != null)
|
|
{
|
|
serializer = (TInterface) serializers.Regular;
|
|
return true;
|
|
}
|
|
|
|
if (serializers.Generic != null)
|
|
{
|
|
serializer = (TInterface) serializers.Generic;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(),
|
|
typeof(TType),
|
|
typeof(TNode),
|
|
out rawSerializer))
|
|
{
|
|
serializer = (TInterface) rawSerializer;
|
|
return true;
|
|
}
|
|
|
|
serializers.Init = true;
|
|
return false;
|
|
}
|
|
|
|
if (TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(),
|
|
typeof(TType),
|
|
typeof(TNode),
|
|
out rawSerializer))
|
|
{
|
|
serializer = (TInterface) rawSerializer;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal bool TryGetTypeNodeSerializerArray<TInterface, TType, TNode>([NotNullWhen(true)] out TInterface? serializer)
|
|
where TInterface : BaseSerializerInterfaces.ITypeNodeInterface<TType[], TNode>
|
|
where TNode : DataNode
|
|
{
|
|
serializer = default;
|
|
if (!TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(), typeof(TType[]), typeof(TNode), out var rawSerializer))
|
|
return false;
|
|
|
|
serializer = (TInterface)rawSerializer;
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetTypeNodeSerializer(Type interfaceType, Type objectType, Type nodeType, [NotNullWhen(true)] out object? serializer)
|
|
{
|
|
if (_typeNodeSerializers.TryGetValue(interfaceType, out var typeNodeSerializers) &&
|
|
typeNodeSerializers.TryGetValue((objectType, nodeType), out serializer))
|
|
return true;
|
|
|
|
if (_genericTypeNodeSerializers.TryGetValue(interfaceType, out var genericTypeNodeSerializers) &&
|
|
objectType.IsGenericType)
|
|
{
|
|
var typeDef = objectType.GetGenericTypeDefinition();
|
|
foreach (var (key, val) in genericTypeNodeSerializers)
|
|
{
|
|
if (!typeDef.HasSameMetadataDefinitionAs(key.ObjectType) || nodeType != key.NodeType)
|
|
continue;
|
|
|
|
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
|
|
serializer = RegisterSerializer(serializerType)!;
|
|
RegisterIndexedNodeSerializer(interfaceType, objectType, key.NodeType, serializer, false);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
serializer = null;
|
|
return false;
|
|
}
|
|
|
|
public TInterface GetTypeNodeSerializer<TInterface, TType, TNode>()
|
|
where TInterface : BaseSerializerInterfaces.ITypeNodeInterface<TType, TNode>
|
|
where TNode : DataNode
|
|
{
|
|
if (!TryGetTypeNodeSerializer<TInterface, TType, TNode>(out var serializer))
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
return serializer;
|
|
}
|
|
|
|
public object GetTypeNodeSerializer(Type interfaceType, Type objectType, Type nodeType)
|
|
{
|
|
if (!TryGetTypeNodeSerializer(interfaceType, objectType, nodeType, out var serializer))
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
return serializer;
|
|
}
|
|
|
|
public bool TryGetTypeSerializer<TInterface, TType>([NotNullWhen(true)] out TInterface? serializer)
|
|
where TInterface : BaseSerializerInterfaces.ITypeInterface<TType>
|
|
{
|
|
serializer = default;
|
|
if (!TryGetTypeSerializer(typeof(TInterface).GetGenericTypeDefinition(), typeof(TType), out var rawSerializer))
|
|
return false;
|
|
|
|
serializer = (TInterface)rawSerializer;
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetTypeSerializer(Type interfaceType, Type objectType, [NotNullWhen(true)] out object? serializer)
|
|
{
|
|
if (_typeSerializers.TryGetValue(interfaceType, out var typeSerializers) &&
|
|
typeSerializers.TryGetValue(objectType, out serializer))
|
|
return true;
|
|
|
|
if (_genericTypeSerializers.TryGetValue(interfaceType, out var genericTypeSerializers) &&
|
|
objectType.IsGenericType)
|
|
{
|
|
var typeDef = objectType.GetGenericTypeDefinition();
|
|
foreach (var (key, val) in genericTypeSerializers)
|
|
{
|
|
if (!typeDef.HasSameMetadataDefinitionAs(key))
|
|
continue;
|
|
|
|
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
|
|
serializer = RegisterSerializer(serializerType)!;
|
|
RegisterIndexedSerializer(
|
|
objectType,
|
|
SerializerInterfaces.IndexOf(interfaceType),
|
|
serializer,
|
|
false
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
serializer = null;
|
|
return false;
|
|
}
|
|
|
|
internal bool TryGetCopierOrCreator<TType>(out ITypeCopier<TType>? copier, out ITypeCopyCreator<TType>? copyCreator)
|
|
{
|
|
copier = null;
|
|
copyCreator = null;
|
|
|
|
var information = SerializedType<TType>.Information;
|
|
if (information.Id < _typeSerializersArray.Length &&
|
|
_typeSerializersArray[information.Id] is { } serializerArray)
|
|
{
|
|
var copiers = serializerArray[CopierIndex];
|
|
var copyCreators = serializerArray[CopyCreatorIndex];
|
|
copier = Unsafe.As<ITypeCopier<TType>?>(copiers.Regular);
|
|
copyCreator = Unsafe.As<ITypeCopyCreator<TType>?>(copyCreators.Regular);
|
|
|
|
if (copier != null || copyCreator != null)
|
|
return true;
|
|
|
|
copier = Unsafe.As<ITypeCopier<TType>?>(copiers.Generic);
|
|
copyCreator = Unsafe.As<ITypeCopyCreator<TType>?>(copyCreators.Generic);
|
|
|
|
if (copier != null || copyCreator != null)
|
|
return true;
|
|
}
|
|
|
|
if (TryGetTypeSerializer(typeof(ITypeCopier<>), typeof(TType), out var rawCopier))
|
|
copier = (ITypeCopier<TType>) rawCopier;
|
|
|
|
if (TryGetTypeSerializer(typeof(ITypeCopyCreator<>), typeof(TType), out var rawCopyCreator))
|
|
copyCreator = (ITypeCopyCreator<TType>) rawCopyCreator;
|
|
|
|
return copier != null || copyCreator != null;
|
|
}
|
|
|
|
public TInterface GetTypeSerializer<TInterface, TType>()
|
|
where TInterface : BaseSerializerInterfaces.ITypeInterface<TType>
|
|
{
|
|
if (!TryGetTypeSerializer<TInterface, TType>(out var serializer))
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
return serializer;
|
|
}
|
|
|
|
public object GetTypeSerializer(Type interfaceType, Type objectType)
|
|
{
|
|
if (!TryGetTypeSerializer(interfaceType, objectType, out var serializer))
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
return serializer;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RegisterMethods
|
|
|
|
public object RegisterSerializer(object obj) => RegisterSerializer(obj.GetType(), obj);
|
|
|
|
private object RegisterSerializer(Type type, object obj)
|
|
{
|
|
foreach (var @interface in type.GetInterfaces())
|
|
{
|
|
if (!@interface.IsGenericType) continue;
|
|
|
|
foreach (var typeInterface in _typeInterfaces)
|
|
{
|
|
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
|
|
continue;
|
|
|
|
var arguments = @interface.GetGenericArguments();
|
|
if (arguments.Length != 1)
|
|
throw new InvalidGenericParameterCountException();
|
|
|
|
_typeSerializers.GetOrNew(typeInterface).TryAdd(arguments[0], obj);
|
|
RegisterIndexedSerializer(
|
|
arguments[0],
|
|
SerializerInterfaces.IndexOf(typeInterface),
|
|
obj,
|
|
true
|
|
);
|
|
}
|
|
|
|
foreach (var typeInterface in _typeNodeInterfaces)
|
|
{
|
|
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
|
|
continue;
|
|
|
|
var arguments = @interface.GetGenericArguments();
|
|
if (arguments.Length != 2)
|
|
throw new InvalidGenericParameterCountException();
|
|
|
|
_typeNodeSerializers.GetOrAdd(typeInterface, _ => new())
|
|
.TryAdd((arguments[0], arguments[1]), obj);
|
|
RegisterIndexedNodeSerializer(
|
|
typeInterface,
|
|
arguments[0],
|
|
arguments[1],
|
|
obj,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
public T? RegisterSerializer<T>() => (T?)RegisterSerializer(typeof(T));
|
|
|
|
public object? RegisterSerializer(Type type)
|
|
{
|
|
if (!type.IsGenericTypeDefinition)
|
|
return RegisterSerializer(type, _ser.CreateSerializer(type));
|
|
|
|
var typeArguments = type.GetGenericArguments();
|
|
foreach (var @interface in type.GetInterfaces())
|
|
{
|
|
foreach (var typeInterface in _typeInterfaces)
|
|
{
|
|
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
|
|
continue;
|
|
|
|
var arguments = @interface.GetGenericArguments();
|
|
if (arguments.Length != 1)
|
|
throw new InvalidGenericParameterCountException();
|
|
var objArguments = arguments[0].GetGenericArguments();
|
|
for (var i = 0; i < typeArguments.Length; i++)
|
|
{
|
|
if (typeArguments[i] != objArguments[i])
|
|
throw new GenericParameterMismatchException();
|
|
}
|
|
|
|
_genericTypeSerializers.GetOrNew(typeInterface).TryAdd(arguments[0], type);
|
|
}
|
|
|
|
foreach (var typeInterface in _typeNodeInterfaces)
|
|
{
|
|
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
|
|
continue;
|
|
|
|
var arguments = @interface.GetGenericArguments();
|
|
if (arguments.Length != 2)
|
|
throw new InvalidGenericParameterCountException();
|
|
var objArguments = arguments[0].GetGenericArguments();
|
|
for (var i = 0; i < typeArguments.Length; i++)
|
|
{
|
|
if (typeArguments[i] != objArguments[i])
|
|
throw new GenericParameterMismatchException();
|
|
}
|
|
|
|
_genericTypeNodeSerializers.GetOrNew(typeInterface).TryAdd((arguments[0], arguments[1]), type);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
//todo paul serv3 is there a better way than comparing names here?
|
|
private void RegisterSerializerInterface(Type type)
|
|
{
|
|
if (!type.IsGenericTypeDefinition)
|
|
throw new ArgumentException("Only generic type definitions can be signed up as interfaces", nameof(type));
|
|
|
|
// Note: lock is entered recursively.
|
|
lock (_lock)
|
|
{
|
|
var genericTypeNode = typeof(BaseSerializerInterfaces.ITypeNodeInterface<,>);
|
|
var genericType = typeof(BaseSerializerInterfaces.ITypeInterface<>);
|
|
var genericParams = type.GetGenericArguments();
|
|
foreach (var @interface in type.GetInterfaces())
|
|
{
|
|
var genericInterface = @interface.GetGenericTypeDefinition();
|
|
if (genericInterface.HasSameMetadataDefinitionAs(genericTypeNode))
|
|
{
|
|
var genericInterfaceParams = genericInterface.GetGenericArguments();
|
|
for (var i = 0; i < genericParams.Length; i++)
|
|
{
|
|
if (genericParams[i].Name != genericInterfaceParams[i].Name)
|
|
throw new GenericParameterMismatchException();
|
|
}
|
|
|
|
_typeNodeInterfaces.Add(type);
|
|
}
|
|
else if (genericInterface.HasSameMetadataDefinitionAs(genericType))
|
|
{
|
|
var genericInterfaceParams = genericInterface.GetGenericArguments();
|
|
for (var i = 0; i < genericParams.Length; i++)
|
|
{
|
|
if (genericParams[i].Name != genericInterfaceParams[i].Name)
|
|
throw new GenericParameterMismatchException();
|
|
}
|
|
|
|
_typeInterfaces.Add(type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RegisterIndexedSerializer(Type elementType, int interfaceIndex, object serializer, bool regular)
|
|
{
|
|
var id = SerializedType.GetId(elementType);
|
|
if (id >= _typeSerializersArray.Length)
|
|
Array.Resize(ref _typeSerializersArray, (id + 1) * 2);
|
|
|
|
var array = _typeSerializersArray[id];
|
|
if (array == null)
|
|
{
|
|
array = new (object? Regular, object? Generic)[SerializerInterfaces.Length];
|
|
_typeSerializersArray[id] = array;
|
|
}
|
|
|
|
if (regular)
|
|
array[interfaceIndex].Regular = serializer;
|
|
else
|
|
array[interfaceIndex].Generic = serializer;
|
|
}
|
|
|
|
private void RegisterIndexedNodeSerializer(Type interfaceIndex, Type elementType, Type nodeType, object serializer, bool regular)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
var id = TypeSerializerType.GetId(interfaceIndex, elementType, nodeType);
|
|
if (id >= _typeNodeSerializersArray.Length)
|
|
Array.Resize(ref _typeNodeSerializersArray, (id + 1) * 2);
|
|
|
|
ref var tuple = ref _typeNodeSerializersArray[id];
|
|
if (regular)
|
|
tuple.Regular = serializer;
|
|
else
|
|
tuple.Generic = serializer;
|
|
|
|
tuple.Init = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
private static class SerializedType
|
|
{
|
|
internal static int Id;
|
|
private static readonly Lock Lock = new();
|
|
|
|
internal static int GetId(Type type)
|
|
{
|
|
lock (Lock)
|
|
{
|
|
var serializedType = typeof(SerializedType<>).MakeGenericType(type);
|
|
var field = serializedType.GetField("Information", BindingFlags.Static | BindingFlags.NonPublic);
|
|
var information = (TypeInformation) field!.GetValue(null)!;
|
|
return information.Id;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static class SerializedType<T>
|
|
{
|
|
// ReSharper disable once StaticMemberInGenericType
|
|
internal static readonly TypeInformation Information;
|
|
|
|
static SerializedType()
|
|
{
|
|
var type = typeof(T);
|
|
var returnSource = type.IsPrimitive || type.IsEnum || type == typeof(string) || type == typeof(Type) || type.IsDefined(typeof(CopyByRefAttribute), true);
|
|
var serializationGenerated = type.IsAssignableTo(typeof(ISerializationGenerated<T>));
|
|
Information = new TypeInformation(Interlocked.Increment(ref SerializedType.Id), returnSource, serializationGenerated);
|
|
}
|
|
}
|
|
|
|
internal readonly struct TypeInformation
|
|
{
|
|
internal readonly int Id;
|
|
internal readonly bool ReturnSource;
|
|
internal readonly bool SerializationGenerated;
|
|
|
|
public TypeInformation(int id, bool returnSource, bool serializationGenerated)
|
|
{
|
|
Id = id;
|
|
ReturnSource = returnSource;
|
|
SerializationGenerated = serializationGenerated;
|
|
}
|
|
}
|
|
|
|
internal static class TypeSerializerType
|
|
{
|
|
internal static int GetId(Type typeInterface, Type type, Type typeNode)
|
|
{
|
|
var interfaceIndex = SerializerInterfaces.IndexOf(typeInterface.GetGenericTypeDefinition());
|
|
if (interfaceIndex == -1)
|
|
throw new ArgumentException($"Invalid type interface: {typeInterface}");
|
|
|
|
var nodeIndex = Nodes.IndexOf(typeNode);
|
|
if (nodeIndex == -1)
|
|
throw new ArgumentException($"Invalid node type: {typeInterface}");
|
|
|
|
return SerializedType.GetId(type) *
|
|
(SerializerInterfaces.Length + Nodes.Length) +
|
|
interfaceIndex +
|
|
nodeIndex;
|
|
}
|
|
}
|
|
|
|
internal static class TypeSerializerType<TInterface, TType, TNode>
|
|
{
|
|
// ReSharper disable once StaticMemberInGenericType
|
|
internal static readonly int Index = SerializedType<TType>.Information.Id *
|
|
(SerializerInterfaces.Length + Nodes.Length) +
|
|
SerializerInterfaces.IndexOf(typeof(TInterface).GetGenericTypeDefinition()) +
|
|
Nodes.IndexOf(typeof(TNode));
|
|
}
|
|
}
|