// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using EpicGames.Serialization.Converters;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace EpicGames.Serialization
{
///
/// Attribute used to mark a property that should be serialized to compact binary
///
[AttributeUsage(AttributeTargets.Property)]
public class CbFieldAttribute : Attribute
{
///
/// Name of the serialized field
///
public string? Name { get; set; }
///
/// Default constructor
///
public CbFieldAttribute()
{
}
///
/// Constructor
///
///
public CbFieldAttribute(string Name)
{
this.Name = Name;
}
}
///
/// Exception thrown when serializing cb objects
///
public class CbException : Exception
{
///
public CbException(string Message) : base(Message)
{
}
///
public CbException(string Message, Exception Inner) : base(Message, Inner)
{
}
}
///
/// Attribute-driven compact binary serializer
///
public static class CbSerializer
{
///
/// Object used for locking access to shared objects
///
static object LockObject = new object();
///
/// Cache of type to converter
///
public static Dictionary TypeToConverter = new Dictionary()
{
[typeof(CbField)] = new CbFieldConverter(),
[typeof(CbObject)] = new CbObjectConverter()
};
///
/// List of converter factories. Must be
///
public static List ConverterFactories = new List
{
new CbDefaultConverterFactory(),
new CbListConverterFactory(),
new CbDictionaryConverterFactory(),
new CbNullableConverterFactory()
};
///
/// Class used to cache values returned by CreateConverterInfo without having to do a dictionary lookup.
///
/// The type to be converted
class CbConverterCache
{
///
/// The converter instance
///
public static CbConverter Instance { get; } = CreateConverter();
///
/// Create a typed converter
///
///
static CbConverter CreateConverter()
{
CbConverter Converter = GetConverter(typeof(T));
return (Converter as CbConverter) ?? new CbConverterWrapper(Converter);
}
///
/// Wrapper class to convert an untyped converter into a typed one
///
class CbConverterWrapper : CbConverter
{
CbConverter Inner;
public CbConverterWrapper(CbConverter Inner) => this.Inner = Inner;
///
public override T Read(CbField Field) => (T)Inner.ReadObject(Field)!;
///
public override void Write(CbWriter Writer, T Value) => Inner.WriteObject(Writer, Value);
///
public override void WriteNamed(CbWriter Writer, Utf8String Name, T Value) => Inner.WriteNamedObject(Writer, Name, Value);
}
}
///
/// Gets the converter for a particular type
///
///
///
public static CbConverter GetConverter(Type Type)
{
CbConverter? Converter;
lock (LockObject)
{
if (!TypeToConverter.TryGetValue(Type, out Converter))
{
CbConverterAttribute? ConverterAttribute = Type.GetCustomAttribute();
if (ConverterAttribute != null)
{
Type ConverterType = ConverterAttribute.ConverterType;
if (Type.IsGenericType && ConverterType.IsGenericTypeDefinition)
{
ConverterType = ConverterType.MakeGenericType(Type.GetGenericArguments());
}
Converter = (CbConverter?)Activator.CreateInstance(ConverterType)!;
}
else
{
for (int Idx = ConverterFactories.Count - 1; Idx >= 0 && Converter == null; Idx--)
{
Converter = ConverterFactories[Idx].CreateConverter(Type);
}
if (Converter == null)
{
throw new CbException($"Unable to create converter for {Type.Name}");
}
}
TypeToConverter.Add(Type, Converter!);
}
}
return Converter;
}
///
/// Gets the converter for a given type
///
/// Type to retrieve the converter for
///
public static CbConverter GetConverter()
{
return CbConverterCache.Instance;
}
///
/// Serialize an object
///
/// Type of the object to serialize
///
///
public static CbObject Serialize(Type Type, object Value)
{
CbWriter Writer = new CbWriter();
GetConverter(Type).WriteObject(Writer, Value);
return Writer.ToObject();
}
///
/// Serialize an object
///
///
///
///
public static CbObject Serialize(T Value)
{
CbWriter Writer = new CbWriter();
GetConverter().Write(Writer, Value);
return Writer.ToObject();
}
///
/// Serialize a property to a given writer
///
///
///
///
public static void Serialize(CbWriter Writer, T Value)
{
GetConverter().Write(Writer, Value);
}
///
/// Serialize a named property to the given writer
///
///
///
///
///
public static void Serialize(CbWriter Writer, Utf8String Name, T Value)
{
GetConverter().WriteNamed(Writer, Name, Value);
}
///
/// Deserialize an object from a
///
///
/// Type of the object to read
///
public static object? Deserialize(CbField Field, Type Type)
{
return GetConverter(Type).ReadObject(Field);
}
///
/// Deserialize an object from a
///
///
///
///
public static T Deserialize(CbField Field)
{
return GetConverter().Read(Field);
}
///
/// Deserialize an object from a
///
///
///
///
public static T Deserialize(CbObject Object) => Deserialize(Object.AsField());
///
/// Deserialize an object from a block of memory
///
///
///
///
public static T Deserialize(ReadOnlyMemory Data) => Deserialize(new CbField(Data));
}
}