mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Apparently NetSerializer treats IDynamicTypeSerializer and IStaticTypeSerializer differently for sealed types??
(cherry-picked from 6bbeaeeba6, without the test changes)
(cherry picked from commit d187018834dfa1cdead9ce5a7b72c38b07f8c81f)
101 lines
3.2 KiB
C#
101 lines
3.2 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Custom serializer implementation for <see cref="BitArray"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This type is necessary as, since .NET 10, the internal layout of <see cref="BitArray"/> was changed.
|
|
/// The type now (internally) implements <see cref="ISerializable"/> for backwards compatibility with existing
|
|
/// <c>BinaryFormatter</c> code, but NetSerializer does not support <see cref="ISerializable"/>.
|
|
/// </para>
|
|
/// <para>
|
|
/// This code is designed to be backportable & network compatible with the previous behavior on .NET 9.
|
|
/// </para>
|
|
/// </remarks>
|
|
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<Type> 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<int>(stream, out _); // _version
|
|
serializer.DeserializeDirect<int[]>(stream, out var array); // m_array
|
|
serializer.DeserializeDirect<int>(stream, out var length); // m_length
|
|
|
|
value = new BitArray(array)
|
|
{
|
|
Length = length
|
|
};
|
|
}
|
|
}
|