#nullable enable using System; using System.Linq; using System.Reflection; namespace ShrinkNetwork { public sealed class ShrinkMessagePackNetworkSerializer : IShrinkNetworkSerializer { private readonly MethodInfo _serializeMethod; private readonly MethodInfo _deserializeMethod; private readonly object? _serializerOptions; public ShrinkMessagePackNetworkSerializer() { var serializerType = Type.GetType("MessagePack.MessagePackSerializer, MessagePack"); if (serializerType == null) { throw new InvalidOperationException( "MessagePack assembly was not found. Please install MessagePack-CSharp before using ShrinkMessagePackNetworkSerializer."); } _serializerOptions = ResolveSerializerOptions(serializerType.Assembly); var serializeMethod = serializerType .GetMethods(BindingFlags.Public | BindingFlags.Static) .FirstOrDefault(m => { if (m.Name != "Serialize") return false; var parameters = m.GetParameters(); return parameters.Length >= 2 && parameters[0].ParameterType == typeof(Type) && parameters[1].ParameterType == typeof(object); }); var deserializeMethod = serializerType .GetMethods(BindingFlags.Public | BindingFlags.Static) .FirstOrDefault(m => { if (m.Name != "Deserialize") return false; var parameters = m.GetParameters(); return parameters.Length >= 2 && parameters[0].ParameterType == typeof(Type) && (parameters[1].ParameterType == typeof(byte[]) || parameters[1].ParameterType == typeof(ReadOnlyMemory)); }); if (serializeMethod == null || deserializeMethod == null) throw new MissingMethodException("MessagePack serialize/deserialize API not found."); _serializeMethod = serializeMethod; _deserializeMethod = deserializeMethod; } public byte[] Serialize(object value) { if (value == null) return Array.Empty(); var parameters = BuildParameters(_serializeMethod, value.GetType(), value, _serializerOptions); return (byte[])_serializeMethod.Invoke(null, parameters)!; } public object Deserialize(byte[] payload, Type type) { payload ??= Array.Empty(); var parameters = BuildParameters(_deserializeMethod, type, payload, _serializerOptions); return _deserializeMethod.Invoke(null, parameters) ?? throw new InvalidOperationException($"MessagePack returned null for type {type.FullName}."); } public T Deserialize(byte[] payload) { return (T)Deserialize(payload, typeof(T)); } private static object?[] BuildParameters(MethodInfo method, Type type, object valueOrBytes, object? serializerOptions) { var parameters = method.GetParameters(); var args = new object?[parameters.Length]; if (parameters.Length > 0) args[0] = type; if (parameters.Length > 1) args[1] = ConvertPrimaryArgument(parameters[1].ParameterType, valueOrBytes); for (var i = 2; i < parameters.Length; i++) { args[i] = ResolveAdditionalArgument(parameters[i], serializerOptions); } return args; } private static object? ResolveAdditionalArgument(ParameterInfo parameter, object? serializerOptions) { if (serializerOptions != null && parameter.ParameterType.IsInstanceOfType(serializerOptions)) return serializerOptions; return parameter.HasDefaultValue ? parameter.DefaultValue : GetDefault(parameter.ParameterType); } private static object ConvertPrimaryArgument(Type parameterType, object value) { if (parameterType == typeof(ReadOnlyMemory) && value is byte[] bytes) return new ReadOnlyMemory(bytes); return value; } private static object? ResolveSerializerOptions(Assembly serializerAssembly) { var contractlessResolverType = serializerAssembly.GetType("MessagePack.Resolvers.ContractlessStandardResolver"); if (contractlessResolverType != null) { var optionsField = contractlessResolverType.GetField("Options", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); var options = optionsField?.GetValue(null); if (options != null) return options; var instanceField = contractlessResolverType.GetField("Instance", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); var instance = instanceField?.GetValue(null); if (instance != null) { var optionsType = serializerAssembly.GetType("MessagePack.MessagePackSerializerOptions"); var standardProperty = optionsType?.GetProperty("Standard", BindingFlags.Public | BindingFlags.Static); var standardOptions = standardProperty?.GetValue(null); var withResolverMethod = optionsType?.GetMethod("WithResolver", BindingFlags.Public | BindingFlags.Instance); var resolvedOptions = withResolverMethod?.Invoke(standardOptions, new[] { instance }); if (resolvedOptions != null) return resolvedOptions; } } return null; } private static object? GetDefault(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } }