Fix NetBitArraySerializer compatibility.

Apparently NetSerializer treats IDynamicTypeSerializer and IStaticTypeSerializer differently for sealed types??
This commit is contained in:
PJB3005
2025-12-02 00:50:30 +01:00
parent f2d74df4d7
commit 6bbeaeeba6
3 changed files with 144 additions and 39 deletions

View File

@@ -3,6 +3,7 @@ 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;
@@ -22,8 +23,11 @@ namespace Robust.Shared.Serialization;
/// This code is designed to be backportable & network compatible with the previous behavior on .NET 9.
/// </para>
/// </remarks>
internal sealed class NetBitArraySerializer : IStaticTypeSerializer
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;
@@ -41,14 +45,32 @@ internal sealed class NetBitArraySerializer : IStaticTypeSerializer
return [typeof(int[]), typeof(int)];
}
public MethodInfo GetStaticWriter(Type type)
public void GenerateWriterMethod(Serializer serializer, Type type, ILGenerator il)
{
return typeof(NetBitArraySerializer).GetMethod("Write", BindingFlags.Static | BindingFlags.NonPublic)!;
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 MethodInfo GetStaticReader(Type type)
public void GenerateReaderMethod(Serializer serializer, Type type, ILGenerator il)
{
return typeof(NetBitArraySerializer).GetMethod("Read", BindingFlags.Static | BindingFlags.NonPublic)!;
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]

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections;
using System.IO;
using System.Text;
using NetSerializer;
using NUnit.Framework;
using Robust.Shared.Serialization;
namespace Robust.UnitTesting.Shared.Serialization;
[Parallelizable(ParallelScope.All)]
[TestFixture, TestOf(typeof(NetBitArraySerializer))]
internal sealed class NetSerializerBitArrayTest
{
// Test that BitArray serialization matches the behavior before .NET 10
// This test can be removed in future RT versions.
[Test]
[TestCase("little creature, have you ever heard about gay people?",
"AgAP2KWjxw7YlYOyDOSVi8YO6smrxgXAoIvmDsqByfcN6oGp5g7KyYOCDcqFk8cMwIST9g3q0YPyDMLlg4IOyr2Dxw3K/QHgBg==")]
[TestCase("", "AgABAA==")]
public void Test(string testData, string expected)
{
var bitData = Encoding.UTF8.GetBytes(testData);
var bitArray = new BitArray(bitData);
var serializer = new Serializer([typeof(BitArray)],
new Settings
{
CustomTypeSerializers = [new NetBitArraySerializer()]
});
var stream = new MemoryStream();
serializer.Serialize(stream, bitArray);
var base64 = Convert.ToBase64String(stream.ToArray());
TestContext.Out.WriteLine(base64);
Assert.That(base64, Is.EqualTo(expected));
stream.Position = 0;
var newBitArray = (BitArray)serializer.Deserialize(stream);
Assert.That(newBitArray, Is.EquivalentTo(bitArray));
}
[Test]
public void Unset()
{
var bitArray = new BitArray(16);
var serializer = new Serializer([typeof(BitArray)],
new Settings
{
CustomTypeSerializers = [new NetBitArraySerializer()]
});
var stream = new MemoryStream();
serializer.Serialize(stream, bitArray);
var base64 = Convert.ToBase64String(stream.ToArray());
TestContext.Out.WriteLine(base64);
Assert.That(base64, Is.EqualTo("AgACACA="));
stream.Position = 0;
var newBitArray = (BitArray)serializer.Deserialize(stream);
Assert.That(newBitArray, Is.EquivalentTo(bitArray));
}
[Test]
public void TestClass()
{
var obj = new FooBar
{
Real = 0x3005,
Heck = "omg",
Wawa = new BitArray("I miss my wife"u8.ToArray())
};
var serializer = new Serializer([typeof(BitArray), typeof(FooBar)],
new Settings
{
CustomTypeSerializers = [new NetBitArraySerializer()]
});
var stream = new MemoryStream();
serializer.Serialize(stream, obj);
var base64 = Convert.ToBase64String(stream.ToArray());
TestContext.Out.WriteLine(base64);
Assert.That(base64, Is.EqualTo("AgQDb21nisABAwAFkoHplg3mzYPSDfKBuZcNzJUD4AE="));
stream.Position = 0;
var newObject = (FooBar)serializer.Deserialize(stream);
Assert.Multiple(() =>
{
Assert.That(newObject.Real, Is.EqualTo(obj.Real));
Assert.That(newObject.Heck, Is.EqualTo(obj.Heck));
Assert.That(newObject.Wawa, Is.EquivalentTo(obj.Wawa));
});
}
[Serializable]
private sealed class FooBar
{
public required int Real;
public required string Heck;
public required BitArray Wawa;
}
}

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Numerics;
using System.Text;
using NetSerializer;
using NUnit.Framework;
using Robust.Shared.Maths;
@@ -247,37 +245,5 @@ namespace Robust.UnitTesting.Shared.Serialization
Assert.That(read, NUnit.Framework.Is.EquivalentTo(x));
}
[Test, TestOf(typeof(NetBitArraySerializer))]
public void TestBitArray()
{
// Test that BitArray serialization matches the behavior before .NET 10
// This test can be removed in future RT versions.
var bitData = "little creature, have you ever heard about gay people?"u8.ToArray();
var bitArray = new BitArray(bitData);
var serializer = new Serializer([typeof(BitArray)],
new Settings
{
CustomTypeSerializers = [new NetBitArraySerializer()]
});
var stream = new MemoryStream();
serializer.Serialize(stream, bitArray);
var base64 = Convert.ToBase64String(stream.ToArray());
TestContext.Out.WriteLine(base64);
Assert.That(base64,
Is.EqualTo(
"AgAP2KWjxw7YlYOyDOSVi8YO6smrxgXAoIvmDsqByfcN6oGp5g7KyYOCDcqFk8cMwIST9g3q0YPyDMLlg4IOyr2Dxw3K/QHgBg=="));
stream.Position = 0;
var newBitArray = (BitArray)serializer.Deserialize(stream);
Assert.That(newBitArray, Is.EquivalentTo(bitArray));
}
}
}