Skip to content

Commit

Permalink
Lots of serializer improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
halgari committed Jan 23, 2024
1 parent 5c5574c commit 483ffe7
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 72 deletions.
31 changes: 31 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/AFixedSizeSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Serialization;

/// <summary>
/// A abstract fixed size serializer.
/// </summary>
/// <param name="size"></param>
/// <typeparam name="TType"></typeparam>
public abstract class AFixedSizeSerializer<TType>(int Size) : IFixedSizeSerializer<TType>
{
/// <inheritdoc />
public bool CanSerialize(Type valueType)
{
return valueType == typeof(TType);
}

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = Size;
return true;
}

/// <inheritdoc />
public abstract void Serialize(TType value, Span<byte> output);

/// <inheritdoc />
public abstract TType Deserialize(ReadOnlySpan<byte> from);
}
19 changes: 3 additions & 16 deletions src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,16 @@ namespace NexusMods.EventSourcing.Serialization;
/// <summary>
/// Serializer for bools.
/// </summary>
public class BoolSerializer : IFixedSizeSerializer<bool>
internal sealed class BoolSerializer() : AFixedSizeSerializer<bool>(sizeof(bool))
{
/// <inheritdoc />
public bool CanSerialize(Type valueType)
{
return valueType == typeof(bool);
}

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = sizeof(bool);
return valueType == typeof(bool);
}

/// <inheritdoc />
public void Serialize(bool value, Span<byte> output)
public override void Serialize(bool value, Span<byte> output)
{
output[0] = value ? (byte)1 : (byte)0;
}

/// <inheritdoc />
public bool Deserialize(ReadOnlySpan<byte> from)
public override bool Deserialize(ReadOnlySpan<byte> from)
{
return from[0] == 1;
}
Expand Down
17 changes: 17 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/DoubleSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Buffers.Binary;

namespace NexusMods.EventSourcing.Serialization;

internal sealed class DoubleSerializer() : AFixedSizeSerializer<double>(sizeof(double))
{
public override void Serialize(double value, Span<byte> output)
{
BinaryPrimitives.WriteDoubleBigEndian(output, value);
}

public override double Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadDoubleBigEndian(from);
}
}
23 changes: 23 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/FloatSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Buffers.Binary;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Serialization;

/// <summary>
/// Serializer for floats.
/// </summary>
internal sealed class FloatSerializer() : AFixedSizeSerializer<float>(sizeof(float))
{
/// <inheritdoc />
public override void Serialize(float value, Span<byte> output)
{
BinaryPrimitives.WriteSingleBigEndian(output, value);
}

/// <inheritdoc />
public override float Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadSingleBigEndian(from);
}
}
19 changes: 3 additions & 16 deletions src/NexusMods.EventSourcing/Serialization/GuidSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,16 @@ namespace NexusMods.EventSourcing.Serialization;
/// <summary>
/// Serializer for Guids.
/// </summary>
public sealed class GuidSerializer : IFixedSizeSerializer<Guid>
internal sealed class GuidSerializer() : AFixedSizeSerializer<Guid>(16)
{
/// <inheritdoc />
public bool CanSerialize(Type valueType)
{
return valueType == typeof(Guid);
}

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 16;
return true;
}

/// <inheritdoc />
public void Serialize(Guid value, Span<byte> output)
public override void Serialize(Guid value, Span<byte> output)
{
value.TryWriteBytes(output);
}

/// <inheritdoc />
public Guid Deserialize(ReadOnlySpan<byte> from)
public override Guid Deserialize(ReadOnlySpan<byte> from)
{
return new(from);
}
Expand Down
17 changes: 17 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/Int16Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Buffers.Binary;

namespace NexusMods.EventSourcing.Serialization;

internal class Int16Serializer() : AFixedSizeSerializer<short>(sizeof(short))
{
public override void Serialize(short value, Span<byte> output)
{
BinaryPrimitives.WriteInt16BigEndian(output, value);
}

public override short Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadInt16BigEndian(from);
}
}
16 changes: 16 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/Int32Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Buffers.Binary;

namespace NexusMods.EventSourcing.Serialization;

internal sealed class Int32Serializer() : AFixedSizeSerializer<int>(sizeof(int)) {
public override void Serialize(int value, Span<byte> output)
{
BinaryPrimitives.WriteInt32BigEndian(output, value);
}

public override int Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadInt32BigEndian(from);
}
}
18 changes: 18 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/Int64Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Buffers.Binary;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Serialization;

internal sealed class Int64Serializer() : AFixedSizeSerializer<long>(sizeof(long))
{
public override void Serialize(long value, Span<byte> output)
{
BinaryPrimitives.WriteInt64BigEndian(output, value);
}

public override long Deserialize(ReadOnlySpan<byte> from)
{
return (long) BinaryPrimitives.ReadInt64BigEndian(from);
}
}
23 changes: 23 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/UInt16Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Buffers.Binary;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Serialization;

/// <summary>
/// Serializer for unsigned 16 bit integers.
/// </summary>
internal sealed class UInt16Serializer() : AFixedSizeSerializer<ushort>(sizeof(ushort))
{
/// <inheritdoc />
public override void Serialize(ushort value, Span<byte> output)
{
BinaryPrimitives.WriteUInt16BigEndian(output, value);
}

/// <inheritdoc />
public override ushort Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadUInt16BigEndian(from);
}
}
16 changes: 3 additions & 13 deletions src/NexusMods.EventSourcing/Serialization/UInt32Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,16 @@ namespace NexusMods.EventSourcing.Serialization;
/// <summary>
/// Serializer for writing UInt32.
/// </summary>
public sealed class UInt32Serializer : IFixedSizeSerializer<uint>
internal sealed class UInt32Serializer() : AFixedSizeSerializer<uint>(sizeof(uint))
{
/// <inheritdoc />
public bool CanSerialize(Type valueType) => valueType == typeof(uint);

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = sizeof(uint);
return valueType == typeof(uint);
}

/// <inheritdoc />
public void Serialize(uint value, Span<byte> output)
public override void Serialize(uint value, Span<byte> output)
{
BinaryPrimitives.WriteUInt32BigEndian(output, value);
}

/// <inheritdoc />
public uint Deserialize(ReadOnlySpan<byte> from)
public override uint Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadUInt32BigEndian(from);
}
Expand Down
16 changes: 3 additions & 13 deletions src/NexusMods.EventSourcing/Serialization/UInt64Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,16 @@ namespace NexusMods.EventSourcing.Serialization;
/// <summary>
/// Serializer for writing UInt64.
/// </summary>
public sealed class UInt64Serializer : IFixedSizeSerializer<ulong>
internal sealed class UInt64Serializer() : AFixedSizeSerializer<ulong>(sizeof(ulong))
{
/// <inheritdoc />
public bool CanSerialize(Type valueType) => valueType == typeof(ulong);

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = sizeof(ulong);
return valueType == typeof(ulong);
}

/// <inheritdoc />
public void Serialize(ulong value, Span<byte> output)
public override void Serialize(ulong value, Span<byte> output)
{
BinaryPrimitives.WriteUInt64BigEndian(output, value);
}

/// <inheritdoc />
public ulong Deserialize(ReadOnlySpan<byte> from)
public override ulong Deserialize(ReadOnlySpan<byte> from)
{
return BinaryPrimitives.ReadUInt64BigEndian(from);
}
Expand Down
16 changes: 3 additions & 13 deletions src/NexusMods.EventSourcing/Serialization/UInt8Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,16 @@ namespace NexusMods.EventSourcing.Serialization;
/// <summary>
/// Serializer for writing UInt8.
/// </summary>
public sealed class UInt8Serializer : IFixedSizeSerializer<byte>
internal sealed class UInt8Serializer() : AFixedSizeSerializer<byte>(sizeof(byte))
{
/// <inheritdoc />
public bool CanSerialize(Type valueType) => valueType == typeof(byte);

/// <inheritdoc />
public bool TryGetFixedSize(Type valueType, out int size)
{
size = sizeof(byte);
return valueType == typeof(byte);
}

/// <inheritdoc />
public void Serialize(byte value, Span<byte> output)
public override void Serialize(byte value, Span<byte> output)
{
output[0] = value;
}

/// <inheritdoc />
public byte Deserialize(ReadOnlySpan<byte> from)
public override byte Deserialize(ReadOnlySpan<byte> from)
{
return from[0];
}
Expand Down
9 changes: 8 additions & 1 deletion src/NexusMods.EventSourcing/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ public static IServiceCollection AddEventSourcing(this IServiceCollection servic
.AddSingleton<ISerializer, GenericEntityIdSerializer>()
.AddSingleton<ISerializer, StringSerializer>()
.AddSingleton<ISerializer, BoolSerializer>()
.AddSingleton<BinaryEventSerializer>()
.AddSingleton<ISerializer, GuidSerializer>()
.AddSingleton<ISerializer, Int16Serializer>()
.AddSingleton<ISerializer, Int32Serializer>()
.AddSingleton<ISerializer, Int64Serializer>()
.AddSingleton<ISerializer, UInt8Serializer>()
.AddSingleton<ISerializer, UInt16Serializer>()
.AddSingleton<ISerializer, UInt32Serializer>()
.AddSingleton<ISerializer, UInt64Serializer>()
.AddSingleton<ISerializer, FloatSerializer>()
.AddSingleton<ISerializer, DoubleSerializer>()
.AddSingleton<ISerializer, EntityIdSerializer>()
.AddSingleton<BinaryEventSerializer>()
.AddEvent<TransactionEvent>()
.AddSingleton<IEntityContext, EntityContext>();
}
Expand Down
70 changes: 70 additions & 0 deletions tests/NexusMods.EventSourcing.Tests/ValueSerializerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Buffers;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Tests;

public class ValueSerializerTests
{
private readonly IEnumerable<ISerializer> _serializers;

public ValueSerializerTests(IEnumerable<ISerializer> serializers)
{
_serializers = serializers;
}

[Theory]
[MemberData(nameof(Data))]
public void SerializeValue(string typeName, object value)
{
var serializer = _serializers.FirstOrDefault(s => s.CanSerialize(value.GetType()));
serializer.Should().NotBeNull();
if (serializer == default) return;

serializer.CanSerialize(value.GetType()).Should().BeTrue();

var methodInfo = typeof(ValueSerializerTests).GetMethod(nameof(TestSerializer), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
methodInfo = methodInfo!.MakeGenericMethod(value.GetType());
methodInfo.Invoke(this, [serializer, value]);
}

private void TestSerializer<T>(ISerializer serializer, object t)
{
if(serializer is IFixedSizeSerializer<T> fixedSizeSerializer)
{
fixedSizeSerializer.TryGetFixedSize(typeof(T), out var size).Should().BeTrue();
var span = new byte[size];
fixedSizeSerializer.Serialize((T)t, span);
var deserialized = fixedSizeSerializer.Deserialize(span);
deserialized.Should().Be(t);
}
else if (serializer is IVariableSizeSerializer<T> variableSizeSerializer)
{
var writer = new ArrayBufferWriter<byte>();
variableSizeSerializer.Serialize((T)t, writer);
var deserializedSize = variableSizeSerializer.Deserialize(writer.WrittenSpan, out var read);
deserializedSize.Should().Be(writer.WrittenCount);
read.Should().Be(t);
}
else
{
throw new Exception("Unknown serializer type");
}
}


public static IEnumerable<object[]> Data = new object[]
{
"Test",
(byte)1,
(short)1,
(int)1,
(long)1,
(float)1,
(double)1,
(ushort)1,
(uint)1,
(ulong)1,
Guid.Parse("154C9597-9E14-41A8-BFB9-2AEA27CA534B"),
}.Select(v => new[] {v.GetType().Name, v }).ToList();

}

0 comments on commit 483ffe7

Please sign in to comment.