From 574f075256fbadd2dadb769c5aff5ebfd6dedd56 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Thu, 5 Mar 2020 01:22:04 +0200 Subject: Switched generic messages to Protobuf-net. --- .../Visual_Studio/Tango.Transport/AutoProtobuf.cs | 138 +++++++++++++++++++++ .../Tango.Transport/ProtoSerializer.cs | 76 ++++++++++++ .../Tango.Transport/Tango.Transport.csproj | 7 +- .../Tango.Transport/TransporterBase.cs | 18 +-- 4 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 Software/Visual_Studio/Tango.Transport/AutoProtobuf.cs create mode 100644 Software/Visual_Studio/Tango.Transport/ProtoSerializer.cs (limited to 'Software/Visual_Studio/Tango.Transport') diff --git a/Software/Visual_Studio/Tango.Transport/AutoProtobuf.cs b/Software/Visual_Studio/Tango.Transport/AutoProtobuf.cs new file mode 100644 index 000000000..d028860b8 --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/AutoProtobuf.cs @@ -0,0 +1,138 @@ +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); + } + } + } + } +} \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Transport/ProtoSerializer.cs b/Software/Visual_Studio/Tango.Transport/ProtoSerializer.cs new file mode 100644 index 000000000..927300224 --- /dev/null +++ b/Software/Visual_Studio/Tango.Transport/ProtoSerializer.cs @@ -0,0 +1,76 @@ +using Google.Protobuf; +using ProtoBuf; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.Transport +{ + public static class ProtoSerializer + { + static ProtoSerializer() + { + ProtoBuf.Meta.RuntimeTypeModel.Default.AutoAddMissingTypes = true; + ProtoBuf.Meta.RuntimeTypeModel.Default.AutoAddProtoContractTypesOnly = false; + ProtoBuf.Meta.RuntimeTypeModel.Default.InferTagFromNameDefault = true; + ProtoBuf.Meta.RuntimeTypeModel.Default.UseImplicitZeroDefaults = true; + } + + public static object Deserialize(Type type, byte[] array) + { + AutoProtobuf.Build(type); + + using (MemoryStream ms = new MemoryStream(array)) + { + return Serializer.Deserialize(type, ms); + } + } + + public static object DeserializeFromByteString(Type type, ByteString byteString) + { + AutoProtobuf.Build(type); + + return Deserialize(type, byteString.ToByteArray()); + } + + //--------------------------------------------------------------------- + + public static byte[] Serialize(T message) + { + AutoProtobuf.Build(); + + using (MemoryStream ms = new MemoryStream()) + { + Serializer.Serialize(ms, message); + return ms.ToArray(); + } + } + + public static T Deserialize(byte[] array) + { + AutoProtobuf.Build(); + + using (MemoryStream ms = new MemoryStream(array)) + { + return Serializer.Deserialize(ms); + } + } + + public static ByteString SerializeToByteString(T message) + { + AutoProtobuf.Build(); + + return ByteString.CopyFrom(Serialize(message)); + } + + public static T DeserializeFromByteString(ByteString byteString) + { + AutoProtobuf.Build(); + + return Deserialize(byteString.ToByteArray()); + } + } +} diff --git a/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj b/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj index 260a1e4f7..8c71f80e7 100644 --- a/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj +++ b/Software/Visual_Studio/Tango.Transport/Tango.Transport.csproj @@ -41,6 +41,9 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + ..\Referenced Assemblies\Protobuf-net\protobuf-net.dll + @@ -83,6 +86,7 @@ + @@ -121,6 +125,7 @@ + @@ -181,7 +186,7 @@ - + \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs index 45b89f9ff..339473929 100644 --- a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs +++ b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs @@ -866,10 +866,11 @@ namespace Tango.Transport public async Task SendGenericRequest(Request request, TransportRequestConfig config = null) where Request : class where Response : class { GenericRequest genericRequest = new GenericRequest(); - genericRequest.Type = request.GetType().Name; - genericRequest.Data = ByteString.CopyFromUtf8(JsonConvert.SerializeObject(request, _genericMessageSettings)); + genericRequest.Type = request.GetType().AssemblyQualifiedName; + genericRequest.Data = ProtoSerializer.SerializeToByteString(request); + var response = await SendRequest(genericRequest, config); - var responseObject = JsonConvert.DeserializeObject(response.Message.Data.ToStringUtf8()); + var responseObject = ProtoSerializer.DeserializeFromByteString(response.Message.Data); return responseObject; } @@ -884,7 +885,9 @@ namespace Tango.Transport public async Task SendGenericResponse(Response response, String token, TransportResponseConfig config = null) where Response : class { GenericResponse genericResponse = new GenericResponse(); - genericResponse.Data = ByteString.CopyFromUtf8(JsonConvert.SerializeObject(response, _genericMessageSettings)); + + genericResponse.Data = ProtoSerializer.SerializeToByteString(response); + await SendResponse(genericResponse, token, config); } @@ -898,8 +901,9 @@ namespace Tango.Transport public IObservable SendGenericContinuousRequest(Request request, TransportContinuousRequestConfig config = null) where Request : class where Response : class { GenericRequest genericRequest = new GenericRequest(); - genericRequest.Type = request.GetType().Name; - genericRequest.Data = ByteString.CopyFromUtf8(JsonConvert.SerializeObject(request, _genericMessageSettings)); + genericRequest.Type = request.GetType().AssemblyQualifiedName; + + genericRequest.Data = ProtoSerializer.SerializeToByteString(request); Subject subject = new Subject(); @@ -908,7 +912,7 @@ namespace Tango.Transport try { - var responseObject = JsonConvert.DeserializeObject(response.Message.Data.ToStringUtf8()); + var responseObject = ProtoSerializer.DeserializeFromByteString(response.Message.Data); subject.OnNext(responseObject); } catch (Exception ex) -- cgit v1.3.1