using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; using JetBrains.Annotations; using NetSerializer; namespace Robust.Shared.Serialization; /// /// Custom serializer implementation for . /// /// /// /// This type is necessary as, since .NET 10, the internal layout of was changed. /// The type now (internally) implements for backwards compatibility with existing /// BinaryFormatter code, but NetSerializer does not support . /// /// /// This code is designed to be backportable & network compatible with the previous behavior on .NET 9. /// /// internal sealed class NetBitArraySerializer : IDynamicTypeSerializer { // NOTE: MUST be a IDynamicTypeSerializer for compatibility! // Can be changed in the future. // For reference, the layout of BitArray before .NET 10 was: // private int[] m_array; // private int m_length; // private int _version; // NetSerializer serialized these in the following order (sorted by name): // _version, m_array, m_length public bool Handles(Type type) { return type == typeof(BitArray); } public IEnumerable GetSubtypes(Type type) { return [typeof(int[]), typeof(int)]; } public void GenerateWriterMethod(Serializer serializer, Type type, ILGenerator il) { var method = typeof(NetBitArraySerializer).GetMethod("Write", BindingFlags.Static | BindingFlags.NonPublic)!; // arg0: Serializer, arg1: Stream, arg2: value il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.EmitCall(OpCodes.Call, method, null); il.Emit(OpCodes.Ret); } public void GenerateReaderMethod(Serializer serializer, Type type, ILGenerator il) { var method = typeof(NetBitArraySerializer).GetMethod("Read", BindingFlags.Static | BindingFlags.NonPublic)!; // arg0: Serializer, arg1: stream, arg2: out value il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.EmitCall(OpCodes.Call, method, null); il.Emit(OpCodes.Ret); } [UsedImplicitly] private static void Write(Serializer serializer, Stream stream, BitArray value) { var intCount = (31 + value.Length) >> 5; var ints = new int[intCount]; value.CopyTo(ints, 0); serializer.SerializeDirect(stream, 0); // _version serializer.SerializeDirect(stream, ints); // m_array serializer.SerializeDirect(stream, value.Length); // m_length } [UsedImplicitly] private static void Read(Serializer serializer, Stream stream, out BitArray value) { serializer.DeserializeDirect(stream, out _); // _version serializer.DeserializeDirect(stream, out var array); // m_array serializer.DeserializeDirect(stream, out var length); // m_length value = new BitArray(array) { Length = length }; } }