using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using ProtoBuf.Meta; namespace Tango.Transport { public static class AutoProtobuf { private const BindingFlags Flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; private static readonly Dictionary> SubTypes = new Dictionary>(); private static readonly ConcurrentBag BuiltTypes = new ConcurrentBag(); private static readonly Type ObjectType = typeof(object); /// /// Build the ProtoBuf serializer from the generic type. /// /// The type of build the serializer for. public static void Build() { var type = typeof(T); Build(type); } /// /// Build the ProtoBuf serializer from the data's type. /// /// The type of build the serializer for. /// The data who's type a serializer will be made. // ReSharper disable once UnusedParameter.Global public static void Build(T data) { Build(); } /// /// Build the ProtoBuf serializer for the type. /// /// The type of build the serializer for. public static void Build(Type type) { if (BuiltTypes.Contains(type)) { return; } lock (type) { if (RuntimeTypeModel.Default.CanSerialize(type)) { if (type.IsGenericType) { BuildGenerics(type); } return; } var meta = RuntimeTypeModel.Default.Add(type, false); var fields = GetFields(type); meta.Add(fields.Select(m => m.Name).ToArray()); meta.UseConstructor = false; BuildBaseClasses(type); BuildGenerics(type); foreach (var memberType in fields.Select(f => f.FieldType).Where(t => !t.IsPrimitive)) { Build(memberType); } BuiltTypes.Add(type); } } /// /// Gets the fields for a type. /// /// The type. /// private static FieldInfo[] GetFields(Type type) { return type.GetFields(Flags); } /// /// Builds the base class serializers for a type. /// /// The type. private static void BuildBaseClasses(Type type) { var baseType = type.BaseType; var inheritingType = type; while (baseType != null && baseType != ObjectType) { HashSet baseTypeEntry; if (!SubTypes.TryGetValue(baseType, out baseTypeEntry)) { baseTypeEntry = new HashSet(); SubTypes.Add(baseType, baseTypeEntry); } if (!baseTypeEntry.Contains(inheritingType)) { Build(baseType); RuntimeTypeModel.Default[baseType].AddSubType(baseTypeEntry.Count + 500, inheritingType); baseTypeEntry.Add(inheritingType); } inheritingType = baseType; baseType = baseType.BaseType; } } /// /// Builds the serializers for the generic parameters for a given type. /// /// The type. private static void BuildGenerics(Type type) { if (type.IsGenericType || (type.BaseType != null && type.BaseType.IsGenericType)) { var generics = type.IsGenericType ? type.GetGenericArguments() : type.BaseType.GetGenericArguments(); foreach (var generic in generics) { Build(generic); } } } } }