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:
DrSmugleaf
2021-04-05 14:50:33 +02:00
committed by GitHub
parent 47ad07b3d2
commit 2a349eb023
37 changed files with 1542 additions and 588 deletions

View 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();
}
}
}

View 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>

View 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!;
}
}

View File

@@ -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);
}
}
}

View 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;
}
}

View File

@@ -3,6 +3,7 @@
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
[assembly: InternalsVisibleTo("Robust.Lite")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
#if NET5_0
[module: SkipLocalsInit]

View File

@@ -2,6 +2,7 @@
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
#if NET5_0
[module: SkipLocalsInit]

View File

@@ -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]

View File

@@ -90,7 +90,6 @@ namespace Robust.Shared.Serialization.Manager
#endregion
#region Read
/// <summary>
/// Deserializes a node into an object, populating it.
/// </summary>

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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
{

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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,