mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Optimize serialization reading, create benchmarks (#1679)
* Add Robust.Benchmarks and read string benchmark
* Separate serialization manager methods, use compiled lambdas to call manager read
4 us > 200 ns
* Add int and data definition with string benchmarks
* Make serialization population use expressions to create definitions
* Make benchmark classes internal and create seed data definition
* Add complex data definition read benchmark
* Create primitive serializers, remove primitive special case
| Method | Mean | Error | StdDev |
|----------------------- |------------:|----------:|----------:|
| ReadString | 227.1 ns | 4.47 ns | 5.65 ns |
| ReadInteger | 245.4 ns | 4.82 ns | 6.26 ns |
| ReadDataDefWithString | 804.7 ns | 15.27 ns | 16.34 ns |
| ReadSeedDataDefinition | 15,846.8 ns | 312.89 ns | 773.39 ns |
* Remove testing code
* Setup delegates during initialize
* Revert "Setup delegates during initialize"
This reverts commit 7ff4d4eaaa.
* Store delegates in a concurrent dictionary because I really cannot be arsed to generate them on initialize at this point
This commit is contained in:
12
Robust.Benchmarks/Program.cs
Normal file
12
Robust.Benchmarks/Program.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace Robust.Benchmarks
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Robust.Benchmarks/Robust.Benchmarks.csproj
Normal file
18
Robust.Benchmarks/Robust.Benchmarks.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
</Project>
|
||||
11
Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
Normal file
11
Robust.Benchmarks/Serialization/DataDefinitionWithString.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
[DataDefinition]
|
||||
public class DataDefinitionWithString
|
||||
{
|
||||
[field: DataField("string")]
|
||||
private string StringField { get; } = default!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Server;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization.Read
|
||||
{
|
||||
public class SerializationReadBenchmark
|
||||
{
|
||||
public SerializationReadBenchmark()
|
||||
{
|
||||
IoCManager.InitThread();
|
||||
ServerIoC.RegisterIoC();
|
||||
IoCManager.BuildGraph();
|
||||
|
||||
var assemblies = new[]
|
||||
{
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
|
||||
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Benchmarks")
|
||||
};
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
IoCManager.Resolve<IConfigurationManagerInternal>().LoadCVarsFromAssembly(assembly);
|
||||
}
|
||||
|
||||
IoCManager.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
|
||||
|
||||
SerializationManager = IoCManager.Resolve<ISerializationManager>();
|
||||
SerializationManager.Initialize();
|
||||
|
||||
StringDataDefNode = new MappingDataNode();
|
||||
StringDataDefNode.AddNode(new ValueDataNode("string"), new ValueDataNode("ABC"));
|
||||
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(new StringReader(SeedDataDefinition.Prototype));
|
||||
|
||||
SeedNode = yamlStream.Documents[0].RootNode.ToDataNodeCast<SequenceDataNode>().Cast<MappingDataNode>(0);
|
||||
}
|
||||
|
||||
private ISerializationManager SerializationManager { get; }
|
||||
|
||||
private ValueDataNode StringNode { get; } = new("ABC");
|
||||
|
||||
private ValueDataNode IntNode { get; } = new("1");
|
||||
|
||||
private MappingDataNode StringDataDefNode { get; }
|
||||
|
||||
private MappingDataNode SeedNode { get; }
|
||||
|
||||
[Benchmark]
|
||||
public string? ReadString()
|
||||
{
|
||||
return SerializationManager.ReadValue<string>(StringNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int? ReadInteger()
|
||||
{
|
||||
return SerializationManager.ReadValue<int>(IntNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public DataDefinitionWithString? ReadDataDefinitionWithString()
|
||||
{
|
||||
return SerializationManager.ReadValue<DataDefinitionWithString>(StringDataDefNode);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public SeedDataDefinition? ReadSeedDataDefinition()
|
||||
{
|
||||
return SerializationManager.ReadValue<SeedDataDefinition>(SeedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
184
Robust.Benchmarks/Serialization/SeedDataDefinition.cs
Normal file
184
Robust.Benchmarks/Serialization/SeedDataDefinition.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Benchmarks.Serialization
|
||||
{
|
||||
[Prototype("seed")]
|
||||
public class SeedDataDefinition : IPrototype
|
||||
{
|
||||
public const string Prototype = @"
|
||||
- type: seed
|
||||
id: tobacco
|
||||
name: tobacco
|
||||
seedName: tobacco
|
||||
displayName: tobacco plant
|
||||
productPrototypes:
|
||||
- LeavesTobacco
|
||||
harvestRepeat: Repeat
|
||||
lifespan: 75
|
||||
maturation: 5
|
||||
production: 5
|
||||
yield: 2
|
||||
potency: 20
|
||||
growthStages: 3
|
||||
idealLight: 9
|
||||
idealHeat: 298
|
||||
chemicals:
|
||||
chem.Nicotine:
|
||||
Min: 1
|
||||
Max: 10
|
||||
PotencyDivisor: 10";
|
||||
|
||||
private const string SeedPrototype = "SeedBase";
|
||||
|
||||
[ViewVariables]
|
||||
[field: DataField("id", required: true)]
|
||||
public string ID { get; private init; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier of this seed. Do NOT set this.
|
||||
/// </summary>
|
||||
public int Uid { get; internal set; } = -1;
|
||||
|
||||
#region Tracking
|
||||
|
||||
[ViewVariables] [DataField("name")] public string Name { get; set; } = string.Empty;
|
||||
[ViewVariables] [DataField("seedName")] public string SeedName { get; set; } = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("seedNoun")]
|
||||
public string SeedNoun { get; set; } = "seeds";
|
||||
[ViewVariables] [DataField("displayName")] public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("roundStart")]
|
||||
public bool RoundStart { get; private set; } = true;
|
||||
[ViewVariables] [DataField("mysterious")] public bool Mysterious { get; set; }
|
||||
[ViewVariables] [DataField("immutable")] public bool Immutable { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Output
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("productPrototypes")]
|
||||
public List<string> ProductPrototypes { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("chemicals")]
|
||||
public Dictionary<string, SeedChemQuantity> Chemicals { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("consumeGasses")]
|
||||
public Dictionary<Gas, float> ConsumeGasses { get; set; } = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("exudeGasses")]
|
||||
public Dictionary<Gas, float> ExudeGasses { get; set; } = new();
|
||||
#endregion
|
||||
|
||||
#region Tolerances
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("nutrientConsumption")]
|
||||
public float NutrientConsumption { get; set; } = 0.25f;
|
||||
|
||||
[ViewVariables] [DataField("waterConsumption")] public float WaterConsumption { get; set; } = 3f;
|
||||
[ViewVariables] [DataField("idealHeat")] public float IdealHeat { get; set; } = 293f;
|
||||
[ViewVariables] [DataField("heatTolerance")] public float HeatTolerance { get; set; } = 20f;
|
||||
[ViewVariables] [DataField("idealLight")] public float IdealLight { get; set; } = 7f;
|
||||
[ViewVariables] [DataField("lightTolerance")] public float LightTolerance { get; set; } = 5f;
|
||||
[ViewVariables] [DataField("toxinsTolerance")] public float ToxinsTolerance { get; set; } = 4f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("lowPressureTolerance")]
|
||||
public float LowPressureTolerance { get; set; } = 25f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("highPressureTolerance")]
|
||||
public float HighPressureTolerance { get; set; } = 200f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("pestTolerance")]
|
||||
public float PestTolerance { get; set; } = 5f;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("weedTolerance")]
|
||||
public float WeedTolerance { get; set; } = 5f;
|
||||
#endregion
|
||||
|
||||
#region General traits
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("endurance")]
|
||||
public float Endurance { get; set; } = 100f;
|
||||
[ViewVariables] [DataField("yield")] public int Yield { get; set; }
|
||||
[ViewVariables] [DataField("lifespan")] public float Lifespan { get; set; }
|
||||
[ViewVariables] [DataField("maturation")] public float Maturation { get; set; }
|
||||
[ViewVariables] [DataField("production")] public float Production { get; set; }
|
||||
[ViewVariables] [DataField("growthStages")] public int GrowthStages { get; set; } = 6;
|
||||
[ViewVariables] [DataField("harvestRepeat")] public HarvestType HarvestRepeat { get; set; } = HarvestType.NoRepeat;
|
||||
|
||||
[ViewVariables] [DataField("potency")] public float Potency { get; set; } = 1f;
|
||||
// No, I'm not removing these.
|
||||
//public PlantSpread Spread { get; set; }
|
||||
//public PlantMutation Mutation { get; set; }
|
||||
//public float AlterTemperature { get; set; }
|
||||
//public PlantCarnivorous Carnivorous { get; set; }
|
||||
//public bool Parasite { get; set; }
|
||||
//public bool Hematophage { get; set; }
|
||||
//public bool Thorny { get; set; }
|
||||
//public bool Stinging { get; set; }
|
||||
[DataField("ligneous")]
|
||||
public bool Ligneous { get; set; }
|
||||
// public bool Teleporting { get; set; }
|
||||
// public PlantJuicy Juicy { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Cosmetics
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("plantRsi", required: true)]
|
||||
public ResourcePath PlantRsi { get; set; } = default!;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("plantIconState")]
|
||||
public string PlantIconState { get; set; } = "produce";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("bioluminescent")]
|
||||
public bool Bioluminescent { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("bioluminescentColor")]
|
||||
public Color BioluminescentColor { get; set; } = Color.White;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("splatPrototype")]
|
||||
public string? SplatPrototype { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum HarvestType
|
||||
{
|
||||
NoRepeat,
|
||||
Repeat
|
||||
}
|
||||
|
||||
public enum Gas {}
|
||||
|
||||
[DataDefinition]
|
||||
public struct SeedChemQuantity
|
||||
{
|
||||
[DataField("Min")]
|
||||
public int Min;
|
||||
[DataField("Max")]
|
||||
public int Max;
|
||||
[DataField("PotencyDivisor")]
|
||||
public int PotencyDivisor;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("Robust.Lite")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
|
||||
#if NET5_0
|
||||
[module: SkipLocalsInit]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
|
||||
#if NET5_0
|
||||
[module: SkipLocalsInit]
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq)
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
|
||||
#if NET5_0
|
||||
[module: SkipLocalsInit]
|
||||
|
||||
@@ -90,7 +90,6 @@ namespace Robust.Shared.Serialization.Manager
|
||||
#endregion
|
||||
|
||||
#region Read
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a node into an object, populating it.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,17 +18,6 @@ namespace Robust.Shared.Serialization.Manager.Result
|
||||
return (DeserializationResult) Activator.CreateInstance(type, value)!;
|
||||
}
|
||||
|
||||
public static DeserializationResult Definition(object value, DeserializedFieldEntry[] mappings)
|
||||
{
|
||||
//if (!IoCManager.Resolve<ISerializationManager>().HasDataDefinition(value.GetType()))
|
||||
// throw new ArgumentException("Provided value was not a data definition", nameof(value));
|
||||
|
||||
//todo validate mappings array count
|
||||
//unless...
|
||||
var type = typeof(DeserializedDefinition<>).MakeGenericType(value.GetType());
|
||||
return (DeserializationResult) Activator.CreateInstance(type, value, mappings)!;
|
||||
}
|
||||
|
||||
public T Cast<T>() where T : DeserializationResult
|
||||
{
|
||||
if (this is T value) return value;
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
namespace Robust.Shared.Serialization.Manager.Result
|
||||
{
|
||||
public class DeserializedValue : DeserializationResult
|
||||
{
|
||||
public DeserializedValue(object? value)
|
||||
{
|
||||
RawValue = value;
|
||||
}
|
||||
|
||||
public override object? RawValue { get; }
|
||||
|
||||
public override DeserializationResult PushInheritanceFrom(DeserializationResult source)
|
||||
{
|
||||
return source.Copy().Cast<DeserializedValue>();
|
||||
}
|
||||
|
||||
public override DeserializationResult Copy()
|
||||
{
|
||||
return new DeserializedValue(RawValue);
|
||||
}
|
||||
|
||||
public override void CallAfterDeserializationHook()
|
||||
{
|
||||
if (RawValue is ISerializationHooks hooks) hooks.AfterDeserialization();
|
||||
}
|
||||
}
|
||||
|
||||
public class DeserializedValue<T> : DeserializationResult<T>
|
||||
{
|
||||
public DeserializedValue(T value)
|
||||
@@ -23,7 +48,7 @@
|
||||
|
||||
public override void CallAfterDeserializationHook()
|
||||
{
|
||||
if(Value is ISerializationHooks hooks) hooks.AfterDeserialization();
|
||||
if (Value is ISerializationHooks hooks) hooks.AfterDeserialization();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
@@ -15,16 +16,33 @@ namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public class SerializationDataDefinition
|
||||
{
|
||||
private delegate DeserializedFieldEntry[] DeserializeDelegate(MappingDataNode mappingDataNode,
|
||||
ISerializationManager serializationManager, ISerializationContext? context, bool skipHook);
|
||||
private delegate DeserializedFieldEntry[] DeserializeDelegate(
|
||||
MappingDataNode mappingDataNode,
|
||||
ISerializationManager serializationManager,
|
||||
ISerializationContext? context,
|
||||
bool skipHook);
|
||||
|
||||
private delegate DeserializationResult PopulateDelegateSignature(object target, DeserializedFieldEntry[] deserializationResults, object?[] defaultValues);
|
||||
private delegate DeserializationResult PopulateDelegateSignature(
|
||||
object target,
|
||||
DeserializedFieldEntry[] deserializationResults,
|
||||
object?[] defaultValues);
|
||||
|
||||
private delegate MappingDataNode SerializeDelegateSignature(object obj, ISerializationManager serializationManager,
|
||||
ISerializationContext? context, bool alwaysWrite, object?[] defaultValues);
|
||||
private delegate MappingDataNode SerializeDelegateSignature(
|
||||
object obj,
|
||||
ISerializationManager serializationManager,
|
||||
ISerializationContext? context,
|
||||
bool alwaysWrite,
|
||||
object?[] defaultValues);
|
||||
|
||||
private delegate object CopyDelegateSignature(object source, object target,
|
||||
ISerializationManager serializationManager, ISerializationContext? context);
|
||||
private delegate object CopyDelegateSignature(
|
||||
object source,
|
||||
object target,
|
||||
ISerializationManager serializationManager,
|
||||
ISerializationContext? context);
|
||||
|
||||
private delegate DeserializationResult CreateDefinitionDelegate(
|
||||
object value,
|
||||
DeserializedFieldEntry[] mappings);
|
||||
|
||||
public readonly Type Type;
|
||||
|
||||
@@ -212,6 +230,19 @@ namespace Robust.Shared.Serialization.Manager
|
||||
// TODO PAUL SERV3: Turn this back into IL once it is fixed
|
||||
private PopulateDelegateSignature EmitPopulateDelegate()
|
||||
{
|
||||
//todo validate mappings array count
|
||||
var constructor =
|
||||
typeof(DeserializedDefinition<>).MakeGenericType(Type).GetConstructor(new[] {Type, typeof(DeserializedFieldEntry[])}) ??
|
||||
throw new NullReferenceException();
|
||||
|
||||
var valueParam = Expression.Parameter(typeof(object), "value");
|
||||
var valueParamCast = Expression.Convert(valueParam, Type);
|
||||
|
||||
var mappingParam = Expression.Parameter(typeof(DeserializedFieldEntry[]), "mapping");
|
||||
|
||||
var newExp = Expression.New(constructor, valueParamCast, mappingParam);
|
||||
var createDefinitionDelegate = Expression.Lambda<CreateDefinitionDelegate>(newExp, valueParam, mappingParam).Compile();
|
||||
|
||||
DeserializationResult PopulateDelegate(
|
||||
object target,
|
||||
DeserializedFieldEntry[] deserializedFields,
|
||||
@@ -234,7 +265,7 @@ namespace Robust.Shared.Serialization.Manager
|
||||
fieldDefinition.FieldInfo.SetValue(target, res.Result?.RawValue);
|
||||
}
|
||||
|
||||
return DeserializationResult.Definition(target, deserializedFields);
|
||||
return createDefinitionDelegate(target, deserializedFields);
|
||||
}
|
||||
|
||||
return PopulateDelegate;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private DeserializationResult ReadWithCustomTypeSerializer<T, TNode, TSerializer>(TNode node, ISerializationContext? context = null, bool skipHook = false)
|
||||
where TSerializer : ITypeReader<T, TNode> where T : notnull where TNode : DataNode
|
||||
{
|
||||
var serializer = (ITypeReader<T, TNode>)GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Read(this, node, DependencyCollection, skipHook, context);
|
||||
}
|
||||
|
||||
private DataNode WriteWithCustomTypeSerializer<T, TSerializer>(T value,
|
||||
ISerializationContext? context = null, bool alwaysWrite = false)
|
||||
where TSerializer : ITypeWriter<T> where T : notnull
|
||||
{
|
||||
var serializer = (ITypeWriter<T>)GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Write(this, value, alwaysWrite, context);
|
||||
}
|
||||
|
||||
private TCommon CopyWithCustomTypeSerializer<TCommon, TSource, TTarget, TSerializer>(
|
||||
TSource source,
|
||||
TTarget target,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where TSource : TCommon
|
||||
where TTarget : TCommon
|
||||
where TCommon : notnull
|
||||
where TSerializer : ITypeCopier<TCommon>
|
||||
{
|
||||
var serializer = (ITypeCopier<TCommon>) GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Copy(this, source, target, skipHook, context);
|
||||
}
|
||||
|
||||
private ValidationNode ValidateWithCustomTypeSerializer<T, TNode, TSerializer>(
|
||||
TNode node,
|
||||
ISerializationContext? context)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
where TSerializer : ITypeValidator<T, TNode>
|
||||
{
|
||||
var serializer = (ITypeValidator<T, TNode>) GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Validate(this, node, DependencyCollection, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private bool TryCopyWithTypeCopier(Type type, object source, ref object target, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryCopyWithTypeCopier) && m.GetParameters().Length == 4).MakeGenericMethod(type, source.GetType(), target.GetType());
|
||||
|
||||
var arr = new[] {source, target, skipHook, context};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
target = arr[1]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCopyWithTypeCopier<TCommon, TSource, TTarget>(
|
||||
TSource source,
|
||||
ref TTarget target,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where TSource : TCommon
|
||||
where TTarget : TCommon
|
||||
where TCommon : notnull
|
||||
{
|
||||
object? rawTypeCopier;
|
||||
|
||||
if (context != null &&
|
||||
context.TypeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier) ||
|
||||
_typeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier))
|
||||
{
|
||||
var ser = (ITypeCopier<TCommon>) rawTypeCopier;
|
||||
target = (TTarget) ser.Copy(this, source, target, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryGetGenericCopier(out ITypeCopier<TCommon>? genericTypeWriter))
|
||||
{
|
||||
target = (TTarget) genericTypeWriter.Copy(this, source, target, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetGenericCopier<T>([NotNullWhen(true)] out ITypeCopier<T>? rawCopier) where T : notnull
|
||||
{
|
||||
rawCopier = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericCopierTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawCopier = (ITypeCopier<T>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
public delegate bool ReadDelegate(
|
||||
Type type,
|
||||
DataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
[NotNullWhen(true)] out DeserializationResult? obj,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null);
|
||||
|
||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, ReadDelegate>> _readDelegates = new();
|
||||
|
||||
private ReadDelegate GetOrCreateDelegate(Type type, Type nodeType)
|
||||
{
|
||||
return _readDelegates
|
||||
.GetOrAdd(type, _ => new ConcurrentDictionary<Type, ReadDelegate>())
|
||||
.GetOrAdd(nodeType, (nT, t) =>
|
||||
{
|
||||
var instanceParam = Expression.Constant(this);
|
||||
var typeParam = Expression.Parameter(typeof(Type), "type");
|
||||
var nodeParam = Expression.Parameter(typeof(DataNode), "node");
|
||||
var dependenciesParam = Expression.Parameter(typeof(IDependencyCollection), "dependencies");
|
||||
var objParam = Expression.Parameter(typeof(DeserializationResult).MakeByRefType(), "obj");
|
||||
var skipHookParam = Expression.Parameter(typeof(bool), "skipHook");
|
||||
var contextParam = Expression.Parameter(typeof(ISerializationContext), "context");
|
||||
|
||||
var call = Expression.Call(
|
||||
instanceParam,
|
||||
nameof(TryRead),
|
||||
new[] {t, nT},
|
||||
typeParam,
|
||||
Expression.Convert(nodeParam, nT),
|
||||
dependenciesParam,
|
||||
objParam,
|
||||
skipHookParam,
|
||||
contextParam);
|
||||
|
||||
return Expression.Lambda<ReadDelegate>(
|
||||
call,
|
||||
typeParam,
|
||||
nodeParam,
|
||||
dependenciesParam,
|
||||
objParam,
|
||||
skipHookParam,
|
||||
contextParam).Compile();
|
||||
}, type);
|
||||
}
|
||||
|
||||
private bool TryGetReader<T, TNode>(
|
||||
Type type,
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ITypeReader<T, TNode>? reader)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
var nodeType = typeof(TNode);
|
||||
|
||||
if (context != null &&
|
||||
context.TypeReaders.TryGetValue((type, nodeType), out var rawTypeReader) ||
|
||||
_typeReaders.TryGetValue((type, nodeType), out rawTypeReader))
|
||||
{
|
||||
reader = (ITypeReader<T, TNode>) rawTypeReader;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericReader(out reader);
|
||||
}
|
||||
|
||||
private bool TryRead<T, TNode>(
|
||||
Type type,
|
||||
TNode node,
|
||||
IDependencyCollection dependencies,
|
||||
[NotNullWhen(true)] out DeserializationResult? obj,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (TryGetReader<T, TNode>(type, context, out var reader))
|
||||
{
|
||||
obj = reader.Read(this, node, dependencies, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
obj = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryReadRaw(
|
||||
Type type,
|
||||
DataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
[NotNullWhen(true)] out DeserializationResult? obj,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return GetOrCreateDelegate(type, node.GetType())(type, node, dependencies, out obj, skipHook, context);
|
||||
}
|
||||
|
||||
private bool TryGetGenericReader<T, TNode>(
|
||||
[NotNullWhen(true)] out ITypeReader<T, TNode>? reader)
|
||||
where TNode : DataNode
|
||||
where T : notnull
|
||||
{
|
||||
var type = typeof(T);
|
||||
var nodeType = typeof(TNode);
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var typeDef = type.GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericReaderTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(nodeType))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null)
|
||||
{
|
||||
reader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(type.GetGenericArguments());
|
||||
|
||||
reader = (ITypeReader<T, TNode>) (RegisterSerializer(serializerType) ??
|
||||
throw new NullReferenceException());
|
||||
return true;
|
||||
}
|
||||
|
||||
reader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeReaders = new();
|
||||
private readonly Dictionary<Type, object> _typeWriters = new();
|
||||
private readonly Dictionary<Type, object> _typeCopiers = new();
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeValidators = new();
|
||||
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericReaderTypes = new();
|
||||
private readonly Dictionary<Type, Type> _genericWriterTypes = new();
|
||||
private readonly Dictionary<Type, Type> _genericCopierTypes = new();
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
|
||||
|
||||
private readonly Dictionary<Type, object> _customTypeSerializers = new();
|
||||
|
||||
private void InitializeTypeSerializers()
|
||||
{
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute<TypeSerializerAttribute>())
|
||||
{
|
||||
RegisterSerializer(type);
|
||||
}
|
||||
}
|
||||
|
||||
private object GetTypeSerializer(Type type)
|
||||
{
|
||||
if (type.IsGenericTypeDefinition)
|
||||
throw new ArgumentException("TypeSerializer cannot be TypeDefinition!", nameof(type));
|
||||
|
||||
if (_customTypeSerializers.TryGetValue(type, out var obj)) return obj;
|
||||
|
||||
obj = Activator.CreateInstance(type)!;
|
||||
_customTypeSerializers[type] = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
private object? RegisterSerializer(Type type)
|
||||
{
|
||||
var writerInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeWriter<>)).ToArray();
|
||||
var readerInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeReader<,>)).ToArray();
|
||||
var copierInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeCopier<>)).ToArray();
|
||||
var validatorInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeValidator<,>)).ToArray();
|
||||
|
||||
if (readerInterfaces.Length == 0 &&
|
||||
writerInterfaces.Length == 0 &&
|
||||
copierInterfaces.Length == 0 &&
|
||||
validatorInterfaces.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Tried to register TypeReader/Writer/Copier that had none of the interfaces inherited.");
|
||||
}
|
||||
|
||||
if (type.IsGenericTypeDefinition)
|
||||
{
|
||||
foreach (var writerInterface in writerInterfaces)
|
||||
{
|
||||
if (!_genericWriterTypes.TryAdd(writerInterface.GetGenericArguments()[0], type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic writer for type {writerInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var readerInterface in readerInterfaces)
|
||||
{
|
||||
var genericArguments = readerInterface.GetGenericArguments();
|
||||
var readType = genericArguments[0];
|
||||
var nodeType = genericArguments[1];
|
||||
|
||||
if (!_genericReaderTypes.TryAdd((readType, nodeType), type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {readType} and node {nodeType} twice");
|
||||
}
|
||||
|
||||
foreach (var copierInterface in copierInterfaces)
|
||||
{
|
||||
if (!_genericCopierTypes.TryAdd(copierInterface.GetGenericArguments()[0], type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic copier for type {copierInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var validatorInterface in validatorInterfaces)
|
||||
{
|
||||
if (!_genericValidatorTypes.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {validatorInterface.GetGenericArguments()[0]} and node {validatorInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var serializer = GetTypeSerializer(type);
|
||||
|
||||
foreach (var writerInterface in writerInterfaces)
|
||||
{
|
||||
if (!_typeWriters.TryAdd(writerInterface.GetGenericArguments()[0], serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering writer for type {writerInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var readerInterface in readerInterfaces)
|
||||
{
|
||||
if (!_typeReaders.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering reader for type {readerInterface.GetGenericArguments()[0]} and node {readerInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
foreach (var copierInterface in copierInterfaces)
|
||||
{
|
||||
if (!_typeCopiers.TryAdd(copierInterface.GetGenericArguments()[0], serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering copier for type {copierInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var validatorInterface in validatorInterfaces)
|
||||
{
|
||||
if (!_typeValidators.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering reader for type {validatorInterface.GetGenericArguments()[0]} and node {validatorInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private bool TryValidateWithTypeValidator(
|
||||
Type type,
|
||||
DataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ValidationNode? valid)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryValidateWithTypeValidator) && m.GetParameters().Length == 4).MakeGenericMethod(type, node.GetType());
|
||||
|
||||
var arr = new object?[] {node, dependencies, context, null};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
valid = (ValidationNode) arr[3]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
valid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryValidateWithTypeValidator<T, TNode>(
|
||||
TNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ValidationNode? valid)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (TryGetValidator<T, TNode>(null, out var reader))
|
||||
{
|
||||
valid = reader.Validate(this, node, dependencies, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
valid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetValidator<T, TNode>(
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ITypeValidator<T, TNode>? reader)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (context != null && context.TypeValidators.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeValidator) ||
|
||||
_typeValidators.TryGetValue((typeof(T), typeof(TNode)), out rawTypeValidator))
|
||||
{
|
||||
reader = (ITypeReader<T, TNode>) rawTypeValidator;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericValidator(out reader);
|
||||
}
|
||||
|
||||
private bool TryGetGenericValidator<T, TNode>([NotNullWhen(true)] out ITypeValidator<T, TNode>? rawReader)
|
||||
where TNode : DataNode where T : notnull
|
||||
{
|
||||
rawReader = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericValidatorTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawReader = (ITypeValidator<T, TNode>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private bool TryWriteWithTypeSerializers(
|
||||
Type type,
|
||||
object obj,
|
||||
[NotNullWhen(true)] out DataNode? node,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryWriteWithTypeSerializers) && m.GetParameters().Length == 4).MakeGenericMethod(type);
|
||||
|
||||
node = null;
|
||||
|
||||
var arr = new[] {obj, node, alwaysWrite, context};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
node = (DataNode) arr[1]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetWriter<T>(ISerializationContext? context, [NotNullWhen(true)] out ITypeWriter<T>? writer) where T : notnull
|
||||
{
|
||||
if (context != null && context.TypeWriters.TryGetValue(typeof(T), out var rawTypeWriter) ||
|
||||
_typeWriters.TryGetValue(typeof(T), out rawTypeWriter))
|
||||
{
|
||||
writer = (ITypeWriter<T>) rawTypeWriter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericWriter(out writer);
|
||||
}
|
||||
|
||||
private bool TryWriteWithTypeSerializers<T>(
|
||||
T obj,
|
||||
[NotNullWhen(true)] out DataNode? node,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null) where T : notnull
|
||||
{
|
||||
node = default;
|
||||
if (TryGetWriter<T>(context, out var writer))
|
||||
{
|
||||
node = writer.Write(this, obj, alwaysWrite, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetGenericWriter<T>([NotNullWhen(true)] out ITypeWriter<T>? rawWriter) where T : notnull
|
||||
{
|
||||
rawWriter = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericWriterTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawWriter = (ITypeWriter<T>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -125,9 +124,6 @@ namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
var underlyingType = type.EnsureNotNullableType();
|
||||
|
||||
if (underlyingType.IsPrimitive || underlyingType == typeof(decimal))
|
||||
return node is ValueDataNode valueDataNode ? new ValidatedValueNode(valueDataNode) : new ErrorNode(node, "Invalid nodetype for primitive/decimal.", true);
|
||||
|
||||
if (underlyingType.IsArray)
|
||||
{
|
||||
if (node is not SequenceDataNode sequenceDataNode) return new ErrorNode(node, "Invalid nodetype for array.", true);
|
||||
@@ -268,14 +264,6 @@ namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
var underlyingType = type.EnsureNotNullableType();
|
||||
|
||||
// val primitives
|
||||
if (underlyingType.IsPrimitive || underlyingType == typeof(decimal))
|
||||
{
|
||||
if (node is not ValueDataNode valueDataNode) throw new InvalidNodeTypeException();
|
||||
var foo = TypeDescriptor.GetConverter(type);
|
||||
return DeserializationResult.Value(foo.ConvertFromInvariantString(valueDataNode.Value));
|
||||
}
|
||||
|
||||
// array
|
||||
if (underlyingType.IsArray)
|
||||
{
|
||||
@@ -296,7 +284,7 @@ namespace Robust.Shared.Serialization.Manager
|
||||
|
||||
if (underlyingType.IsEnum)
|
||||
{
|
||||
return DeserializationResult.Value(node switch
|
||||
return new DeserializedValue(node switch
|
||||
{
|
||||
ValueDataNode valueNode => Enum.Parse(underlyingType, valueNode.Value, true),
|
||||
SequenceDataNode sequenceNode => Enum.Parse(underlyingType, string.Join(", ", sequenceNode.Sequence), true),
|
||||
@@ -310,7 +298,7 @@ namespace Robust.Shared.Serialization.Manager
|
||||
underlyingType = ResolveConcreteType(underlyingType, typeString);
|
||||
}
|
||||
|
||||
if (TryReadWithTypeSerializers(underlyingType, node, DependencyCollection, out var serializedObj, skipHook, context))
|
||||
if (TryReadRaw(underlyingType, node, DependencyCollection, out var serializedObj, skipHook, context))
|
||||
{
|
||||
return serializedObj;
|
||||
}
|
||||
@@ -322,7 +310,7 @@ namespace Robust.Shared.Serialization.Manager
|
||||
var selfSerObj = (ISelfSerialize) Activator.CreateInstance(underlyingType)!;
|
||||
selfSerObj.Deserialize(valueDataNode.Value);
|
||||
|
||||
return DeserializationResult.Value(selfSerObj);
|
||||
return new DeserializedValue(selfSerObj);
|
||||
}
|
||||
|
||||
//if (node is not MappingDataNode mappingDataNode) throw new InvalidNodeTypeException();
|
||||
@@ -404,11 +392,9 @@ namespace Robust.Shared.Serialization.Manager
|
||||
|
||||
if (value == null) return new MappingDataNode();
|
||||
|
||||
if (underlyingType.IsPrimitive ||
|
||||
underlyingType.IsEnum ||
|
||||
underlyingType == typeof(decimal))
|
||||
if (underlyingType.IsEnum)
|
||||
{
|
||||
// All primitives and enums implement IConvertible.
|
||||
// Enums implement IConvertible.
|
||||
// Need it for the culture overload.
|
||||
var convertible = (IConvertible) value;
|
||||
return new ValueDataNode(convertible.ToString(CultureInfo.InvariantCulture));
|
||||
@@ -487,20 +473,6 @@ namespace Robust.Shared.Serialization.Manager
|
||||
var sourceType = source.GetType();
|
||||
var targetType = target.GetType();
|
||||
|
||||
if (sourceType.IsPrimitive && targetType.IsPrimitive)
|
||||
{
|
||||
//todo does this work
|
||||
//i think it does
|
||||
//todo validate we can assign source
|
||||
return source;
|
||||
}
|
||||
|
||||
if (source.GetType().IsPrimitive != target.GetType().IsPrimitive)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Source and target do not match. Source ({sourceType}) is primitive type? {sourceType.IsPrimitive}. Target ({targetType}) is primitive type? {targetType.IsPrimitive}");
|
||||
}
|
||||
|
||||
if (sourceType.IsValueType && targetType.IsValueType)
|
||||
{
|
||||
return source;
|
||||
@@ -611,7 +583,11 @@ namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
if (source == null) return source;
|
||||
|
||||
if (type.IsPrimitive || type.IsEnum || source is string || _copyByRefRegistrations.Contains(type))
|
||||
|
||||
if (type.IsPrimitive ||
|
||||
type.IsEnum ||
|
||||
source is string ||
|
||||
_copyByRefRegistrations.Contains(type))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager
|
||||
{
|
||||
public partial class SerializationManager
|
||||
{
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeReaders = new();
|
||||
private readonly Dictionary<Type, object> _typeWriters = new();
|
||||
private readonly Dictionary<Type, object> _typeCopiers = new();
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), object> _typeValidators = new();
|
||||
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericReaderTypes = new();
|
||||
private readonly Dictionary<Type, Type> _genericWriterTypes = new();
|
||||
private readonly Dictionary<Type, Type> _genericCopierTypes = new();
|
||||
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
|
||||
|
||||
private readonly Dictionary<Type, object> _customTypeSerializers = new();
|
||||
|
||||
private void InitializeTypeSerializers()
|
||||
{
|
||||
foreach (var type in _reflectionManager.FindTypesWithAttribute<TypeSerializerAttribute>())
|
||||
{
|
||||
RegisterSerializer(type);
|
||||
}
|
||||
}
|
||||
|
||||
private object GetTypeSerializer(Type type)
|
||||
{
|
||||
if (type.IsGenericTypeDefinition)
|
||||
throw new ArgumentException("TypeSerializer cannot be TypeDefinition!", nameof(type));
|
||||
|
||||
if (_customTypeSerializers.TryGetValue(type, out var obj)) return obj;
|
||||
|
||||
obj = Activator.CreateInstance(type)!;
|
||||
_customTypeSerializers[type] = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
private object? RegisterSerializer(Type type)
|
||||
{
|
||||
var writerInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeWriter<>)).ToArray();
|
||||
var readerInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeReader<,>)).ToArray();
|
||||
var copierInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeCopier<>)).ToArray();
|
||||
var validatorInterfaces = type.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeValidator<,>)).ToArray();
|
||||
|
||||
if (readerInterfaces.Length == 0 && writerInterfaces.Length == 0 && copierInterfaces.Length == 0 && validatorInterfaces.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Tried to register TypeReader/Writer/Copier that had none of the interfaces inherited.");
|
||||
}
|
||||
|
||||
if (type.IsGenericTypeDefinition)
|
||||
{
|
||||
foreach (var writerInterface in writerInterfaces)
|
||||
{
|
||||
if (!_genericWriterTypes.TryAdd(writerInterface.GetGenericArguments()[0], type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic writer for type {writerInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var readerInterface in readerInterfaces)
|
||||
{
|
||||
if (!_genericReaderTypes.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {readerInterface.GetGenericArguments()[0]} and datanode {readerInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
foreach (var copierInterface in copierInterfaces)
|
||||
{
|
||||
if (!_genericCopierTypes.TryAdd(copierInterface.GetGenericArguments()[0], type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic copier for type {copierInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var validatorInterface in validatorInterfaces)
|
||||
{
|
||||
if (!_genericValidatorTypes.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), type))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering generic reader for type {validatorInterface.GetGenericArguments()[0]} and datanode {validatorInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var serializer = GetTypeSerializer(type);
|
||||
|
||||
foreach (var writerInterface in writerInterfaces)
|
||||
{
|
||||
if (!_typeWriters.TryAdd(writerInterface.GetGenericArguments()[0], serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering writer for type {writerInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var readerInterface in readerInterfaces)
|
||||
{
|
||||
if (!_typeReaders.TryAdd((readerInterface.GetGenericArguments()[0], readerInterface.GetGenericArguments()[1]), serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering reader for type {readerInterface.GetGenericArguments()[0]} and datanode {readerInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
foreach (var copierInterface in copierInterfaces)
|
||||
{
|
||||
if (!_typeCopiers.TryAdd(copierInterface.GetGenericArguments()[0], serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering copier for type {copierInterface.GetGenericArguments()[0]} twice");
|
||||
}
|
||||
|
||||
foreach (var validatorInterface in validatorInterfaces)
|
||||
{
|
||||
if (!_typeValidators.TryAdd((validatorInterface.GetGenericArguments()[0], validatorInterface.GetGenericArguments()[1]), serializer))
|
||||
Logger.ErrorS(LogCategory, $"Tried registering reader for type {validatorInterface.GetGenericArguments()[0]} and datanode {validatorInterface.GetGenericArguments()[1]} twice");
|
||||
}
|
||||
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
|
||||
#region TryGetGeneric
|
||||
private bool TryGetGenericReader<T, TNode>([NotNullWhen(true)] out ITypeReader<T, TNode>? rawReader)
|
||||
where TNode : DataNode where T : notnull
|
||||
{
|
||||
rawReader = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericReaderTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawReader = (ITypeReader<T, TNode>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetGenericWriter<T>([NotNullWhen(true)] out ITypeWriter<T>? rawWriter) where T : notnull
|
||||
{
|
||||
rawWriter = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericWriterTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawWriter = (ITypeWriter<T>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetGenericCopier<T>([NotNullWhen(true)] out ITypeCopier<T>? rawCopier) where T : notnull
|
||||
{
|
||||
rawCopier = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericCopierTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawCopier = (ITypeCopier<T>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetGenericValidator<T, TNode>([NotNullWhen(true)] out ITypeValidator<T, TNode>? rawReader)
|
||||
where TNode : DataNode where T : notnull
|
||||
{
|
||||
rawReader = null;
|
||||
|
||||
if (typeof(T).IsGenericType)
|
||||
{
|
||||
var typeDef = typeof(T).GetGenericTypeDefinition();
|
||||
|
||||
Type? serializerTypeDef = null;
|
||||
|
||||
foreach (var (key, val) in _genericValidatorTypes)
|
||||
{
|
||||
if (typeDef.HasSameMetadataDefinitionAs(key.Type) && key.DataNodeType.IsAssignableFrom(typeof(TNode)))
|
||||
{
|
||||
serializerTypeDef = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializerTypeDef == null) return false;
|
||||
|
||||
var serializerType = serializerTypeDef.MakeGenericType(typeof(T).GetGenericArguments());
|
||||
rawReader = (ITypeValidator<T, TNode>) RegisterSerializer(serializerType)!;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryValidate
|
||||
private bool TryValidateWithTypeValidator(
|
||||
Type type,
|
||||
DataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ValidationNode? valid)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryValidateWithTypeValidator) && m.GetParameters().Length == 4).MakeGenericMethod(type, node.GetType());
|
||||
|
||||
var arr = new object?[] {node, dependencies, context, null};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
valid = (ValidationNode) arr[3]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
valid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryValidateWithTypeValidator<T, TNode>(
|
||||
TNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ValidationNode? valid)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (TryGetValidator<T, TNode>(null, out var reader))
|
||||
{
|
||||
valid = reader.Validate(this, node, dependencies, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
valid = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetValidator<T, TNode>(
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ITypeValidator<T, TNode>? reader)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (context != null && context.TypeValidators.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeValidator) ||
|
||||
_typeValidators.TryGetValue((typeof(T), typeof(TNode)), out rawTypeValidator))
|
||||
{
|
||||
reader = (ITypeReader<T, TNode>) rawTypeValidator;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericValidator(out reader);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryRead
|
||||
private bool TryGetReader<T, TNode>(
|
||||
ISerializationContext? context,
|
||||
[NotNullWhen(true)] out ITypeReader<T, TNode>? reader)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (context != null && context.TypeReaders.TryGetValue((typeof(T), typeof(TNode)), out var rawTypeReader) ||
|
||||
_typeReaders.TryGetValue((typeof(T), typeof(TNode)), out rawTypeReader))
|
||||
{
|
||||
reader = (ITypeReader<T, TNode>) rawTypeReader;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericReader(out reader);
|
||||
}
|
||||
|
||||
private bool TryReadWithTypeSerializers(
|
||||
Type type,
|
||||
DataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
[NotNullWhen(true)] out DeserializationResult? obj,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods()
|
||||
.First(m => m.Name == nameof(TryReadWithTypeSerializers) && m.GetParameters().Length == 5)
|
||||
.MakeGenericMethod(type, node.GetType());
|
||||
|
||||
obj = default;
|
||||
|
||||
var arr = new object?[] {node, dependencies, obj, skipHook, context};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
obj = (DeserializationResult) arr[2]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryReadWithTypeSerializers<T, TNode>(
|
||||
TNode node,
|
||||
IDependencyCollection dependencies,
|
||||
[NotNullWhen(true)] out DeserializationResult? obj,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
{
|
||||
if (TryGetReader<T, TNode>(context, out var reader))
|
||||
{
|
||||
obj = reader.Read(this, node, dependencies, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
obj = null;
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryWrite
|
||||
private bool TryWriteWithTypeSerializers(
|
||||
Type type,
|
||||
object obj,
|
||||
[NotNullWhen(true)] out DataNode? node,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryWriteWithTypeSerializers) && m.GetParameters().Length == 4).MakeGenericMethod(type);
|
||||
|
||||
node = null;
|
||||
|
||||
var arr = new[] {obj, node, alwaysWrite, context};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
node = (DataNode) arr[1]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetWriter<T>(ISerializationContext? context, [NotNullWhen(true)] out ITypeWriter<T>? writer) where T : notnull
|
||||
{
|
||||
if (context != null && context.TypeWriters.TryGetValue(typeof(T), out var rawTypeWriter) ||
|
||||
_typeWriters.TryGetValue(typeof(T), out rawTypeWriter))
|
||||
{
|
||||
writer = (ITypeWriter<T>) rawTypeWriter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetGenericWriter(out writer);
|
||||
}
|
||||
|
||||
private bool TryWriteWithTypeSerializers<T>(
|
||||
T obj,
|
||||
[NotNullWhen(true)] out DataNode? node,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null) where T : notnull
|
||||
{
|
||||
node = default;
|
||||
if (TryGetWriter<T>(context, out var writer))
|
||||
{
|
||||
node = writer.Write(this, obj, alwaysWrite, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryCopy
|
||||
private bool TryCopyWithTypeCopier(Type type, object source, ref object target, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
//TODO Paul: do this shit w/ delegates
|
||||
var method = typeof(SerializationManager).GetRuntimeMethods().First(m =>
|
||||
m.Name == nameof(TryCopyWithTypeCopier) && m.GetParameters().Length == 4).MakeGenericMethod(type, source.GetType(), target.GetType());
|
||||
|
||||
var arr = new[] {source, target, skipHook, context};
|
||||
var res = method.Invoke(this, arr);
|
||||
|
||||
if (res as bool? ?? false)
|
||||
{
|
||||
target = arr[1]!;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCopyWithTypeCopier<TCommon, TSource, TTarget>(
|
||||
TSource source,
|
||||
ref TTarget target,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where TSource : TCommon
|
||||
where TTarget : TCommon
|
||||
where TCommon : notnull
|
||||
{
|
||||
object? rawTypeCopier;
|
||||
|
||||
if (context != null &&
|
||||
context.TypeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier) ||
|
||||
_typeCopiers.TryGetValue(typeof(TCommon), out rawTypeCopier))
|
||||
{
|
||||
var ser = (ITypeCopier<TCommon>) rawTypeCopier;
|
||||
target = (TTarget) ser.Copy(this, source, target, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryGetGenericCopier(out ITypeCopier<TCommon>? genericTypeWriter))
|
||||
{
|
||||
target = (TTarget) genericTypeWriter.Copy(this, source, target, skipHook, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Custom
|
||||
|
||||
private DeserializationResult ReadWithCustomTypeSerializer<T, TNode, TSerializer>(TNode node, ISerializationContext? context = null, bool skipHook = false)
|
||||
where TSerializer : ITypeReader<T, TNode> where T : notnull where TNode : DataNode
|
||||
{
|
||||
var serializer = (ITypeReader<T, TNode>)GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Read(this, node, DependencyCollection, skipHook, context);
|
||||
}
|
||||
|
||||
private DataNode WriteWithCustomTypeSerializer<T, TSerializer>(T value,
|
||||
ISerializationContext? context = null, bool alwaysWrite = false)
|
||||
where TSerializer : ITypeWriter<T> where T : notnull
|
||||
{
|
||||
var serializer = (ITypeWriter<T>)GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Write(this, value, alwaysWrite, context);
|
||||
}
|
||||
|
||||
private TCommon CopyWithCustomTypeSerializer<TCommon, TSource, TTarget, TSerializer>(
|
||||
TSource source,
|
||||
TTarget target,
|
||||
bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
where TSource : TCommon
|
||||
where TTarget : TCommon
|
||||
where TCommon : notnull
|
||||
where TSerializer : ITypeCopier<TCommon>
|
||||
{
|
||||
var serializer = (ITypeCopier<TCommon>) GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Copy(this, source, target, skipHook, context);
|
||||
}
|
||||
|
||||
private ValidationNode ValidateWithCustomTypeSerializer<T, TNode, TSerializer>(
|
||||
TNode node,
|
||||
ISerializationContext? context)
|
||||
where T : notnull
|
||||
where TNode : DataNode
|
||||
where TSerializer : ITypeValidator<T, TNode>
|
||||
{
|
||||
var serializer = (ITypeValidator<T, TNode>) GetTypeSerializer(typeof(TSerializer));
|
||||
return serializer.Validate(this, node, DependencyCollection, context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
var constType = serializationManager.GetConstantTypeFromTag(typeof(TTag));
|
||||
return DeserializationResult.Value((int) Enum.Parse(constType, node.Value));
|
||||
return new DeserializedValue((int) Enum.Parse(constType, node.Value));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
var flagType = serializationManager.GetFlagTypeFromTag(typeof(TTag));
|
||||
return DeserializationResult.Value((int)Enum.Parse(flagType, node.Value));
|
||||
return new DeserializedValue((int)Enum.Parse(flagType, node.Value));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
|
||||
@@ -89,7 +89,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom
|
||||
flags |= (int) Enum.Parse(flagType, valueDataNode.Value);
|
||||
}
|
||||
|
||||
return DeserializationResult.Value(flags);
|
||||
return new DeserializedValue(flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class BooleanSerializer : ITypeSerializer<bool, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return bool.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing boolean value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<bool>(bool.Parse(node.Value));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, bool value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public bool Copy(ISerializationManager serializationManager, bool source, bool target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class ByteSerializer : ITypeSerializer<byte, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return byte.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing byte value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<byte>(byte.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, byte value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public byte Copy(ISerializationManager serializationManager, byte source, byte target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class CharSerializer : ITypeSerializer<char, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return char.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing char value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<char>(char.Parse(node.Value));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, char value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public char Copy(ISerializationManager serializationManager, char source, char target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class DecimalSerializer : ITypeSerializer<decimal, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return decimal.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing decimal value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<decimal>(decimal.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, decimal value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public decimal Copy(ISerializationManager serializationManager, decimal source, decimal target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class DoubleSerializer : ITypeSerializer<double, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return bool.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing double value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<double>(double.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, double value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public double Copy(ISerializationManager serializationManager, double source, double target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class FloatSerializer : ITypeSerializer<float, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return float.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing float value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<float>(float.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, float value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public float Copy(ISerializationManager serializationManager, float source, float target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class IntSerializer : ITypeSerializer<int, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return int.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing int value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<int>(int.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, int value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public int Copy(ISerializationManager serializationManager, int source, int target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class LongSerializer : ITypeSerializer<long, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return long.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing long value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<long>(long.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, long value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public long Copy(ISerializationManager serializationManager, long source, long target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class SByteSerializer : ITypeSerializer<sbyte, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return sbyte.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing signed byte value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<sbyte>(sbyte.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, sbyte value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public sbyte Copy(ISerializationManager serializationManager, sbyte source, sbyte target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class ShortSerializer : ITypeSerializer<short, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return short.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing short value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<short>(short.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, short value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public short Copy(ISerializationManager serializationManager, short source, short target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class UIntSerializer : ITypeSerializer<uint, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return uint.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing unsigned int value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<uint>(uint.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, uint value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public uint Copy(ISerializationManager serializationManager, uint source, uint target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class ULongSerializer : ITypeSerializer<ulong, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return ulong.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing unsigned long value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<ulong>(ulong.Parse(node.ToString(), CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, ulong value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public ulong Copy(ISerializationManager serializationManager, ulong source, ulong target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Primitive
|
||||
{
|
||||
[TypeSerializer]
|
||||
public class UShortSerializer : ITypeSerializer<ushort, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
return ushort.TryParse(node.Value, out _)
|
||||
? new ValidatedValueNode(node)
|
||||
: new ErrorNode(node, $"Failed parsing unsigned short value: {node.Value}");
|
||||
}
|
||||
|
||||
public DeserializationResult Read(ISerializationManager serializationManager, ValueDataNode node,
|
||||
IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null)
|
||||
{
|
||||
return new DeserializedValue<ushort>(ushort.Parse(node.Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, ushort value, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new ValueDataNode(value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public ushort Copy(ISerializationManager serializationManager, ushort source, ushort target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Result;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Interfaces
|
||||
{
|
||||
public interface ITypeReader<[UsedImplicitly]TType, TNode> : ITypeValidator<TType, TNode> where TType : notnull where TNode : DataNode
|
||||
public interface ITypeReader<TType, TNode> : ITypeValidator<TType, TNode> where TType : notnull where TNode : DataNode
|
||||
{
|
||||
DeserializationResult Read(ISerializationManager serializationManager,
|
||||
DeserializationResult Read(
|
||||
ISerializationManager serializationManager,
|
||||
TNode node,
|
||||
IDependencyCollection dependencies,
|
||||
bool skipHook,
|
||||
|
||||
Reference in New Issue
Block a user