Skip to content

Commit

Permalink
Added variable sized serializers
Browse files Browse the repository at this point in the history
  • Loading branch information
halgari committed Jan 9, 2024
1 parent 739b332 commit 5595144
Show file tree
Hide file tree
Showing 16 changed files with 496 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -23,7 +24,12 @@ public static IServiceCollection AddEvent<T>(this IServiceCollection collection)
{
throw new ArgumentException($"Event type {type.Name} does not have an EventIdAttribute.");
}
collection.AddSingleton(s => new EventDefinition(attribute.Guid, type));

Span<byte> span = stackalloc byte[16];
attribute.Guid.TryWriteBytes(span);
var id = BinaryPrimitives.ReadUInt128BigEndian(span);

collection.AddSingleton(s => new EventDefinition(id, type));
return collection;
}

Expand Down
25 changes: 21 additions & 4 deletions src/NexusMods.EventSourcing.Abstractions/EntityId.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
using System;
using System.Buffers.Binary;
using System.Globalization;
using TransparentValueObjects;

namespace NexusMods.EventSourcing.Abstractions;

[ValueObject<Guid>]
[ValueObject<UInt128>]
public readonly partial struct EntityId
{
public EntityId<T> Cast<T>() where T : IEntity => new(this);

public static EntityId NewId()
{
var guid = Guid.NewGuid();
Span<byte> bytes = stackalloc byte[16];
guid.TryWriteBytes(bytes);
var value = BinaryPrimitives.ReadUInt128BigEndian(bytes);
return From(value);
}

public static EntityId From(ReadOnlySpan<byte> data) => new(BinaryPrimitives.ReadUInt128BigEndian(data));

public void TryWriteBytes(Span<byte> span)
{
BinaryPrimitives.WriteUInt128BigEndian(span, Value);
}
}


Expand All @@ -29,7 +46,7 @@ public readonly partial struct EntityId
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static EntityId<T> From(Guid id) => new(EntityId.From(id));
public static EntityId<T> From(UInt128 id) => new(EntityId.From(id));



Expand All @@ -38,7 +55,7 @@ public readonly partial struct EntityId
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static EntityId<T> From(string id) => From(Guid.Parse(id));
public static EntityId<T> From(string id) => From(UInt128.Parse(id, NumberStyles.HexNumber));


/// <summary>
Expand All @@ -55,7 +72,7 @@ public readonly partial struct EntityId
/// <inheritdoc />
public override string ToString()
{
return typeof(T).Name + "<" + Value.Value + ">";
return typeof(T).Name + "<" + Value.Value.ToString("X") + ">";
}


Expand Down
4 changes: 2 additions & 2 deletions src/NexusMods.EventSourcing.Abstractions/EventDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace NexusMods.EventSourcing.Abstractions;
/// <summary>
/// A record that defines an event and the unique GUID that identifies it.
/// </summary>
/// <param name="Guid"></param>
/// <param name="Id"></param>
/// <param name="Type"></param>
public record EventDefinition(Guid Guid, Type Type);
public record EventDefinition(UInt128 Id, Type Type);
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.IO;

namespace NexusMods.EventSourcing.Abstractions.Serialization;
Expand All @@ -13,5 +15,20 @@ public interface ISerializer
public interface IFixedSizeSerializer<T> : ISerializer
{
public void Serialize(T value, Span<byte> output);
public T Deserialize(Span<byte> from);
public T Deserialize(ReadOnlySpan<byte> from);
}

public interface IVariableSizeSerializer<T> : ISerializer
{
public void Serialize<TWriter>(T value, TWriter output) where TWriter : IBufferWriter<byte>;
public int Deserialize(ReadOnlySpan<byte> from, out T value);
}


/// <summary>
/// If the serializer can specialize (e.g. for a generic type), it should implement this interface.
/// </summary>
public interface IGenericSerializer : ISerializer
{
public bool TrySpecialze(Type baseType, Type[] argTypes, [NotNullWhen(true)] out ISerializer? serializer);
}
6 changes: 3 additions & 3 deletions src/NexusMods.EventSourcing.RocksDB/RocksDBEventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public TransactionId Add<T>(T eventValue) where T : IEvent
BinaryPrimitives.WriteUInt64BigEndian(keySpan[16..], _tx.Value);
foreach (var entityId in ingester.Entities)
{
entityId.Value.TryWriteBytes(keySpan);
entityId.TryWriteBytes(keySpan);
_db.Put(keySpan, keySpan, _entityIndexColumn);
}
}
Expand All @@ -68,9 +68,9 @@ public TransactionId Add<T>(T eventValue) where T : IEvent
public void EventsForEntity<TIngester>(EntityId entityId, TIngester ingester) where TIngester : IEventIngester
{
Span<byte> startKey = stackalloc byte[24];
entityId.Value.TryWriteBytes(startKey);
entityId.TryWriteBytes(startKey);
Span<byte> endKey = stackalloc byte[24];
entityId.Value.TryWriteBytes(endKey);
entityId.TryWriteBytes(endKey);
BinaryPrimitives.WriteUInt64BigEndian(endKey[16..], ulong.MaxValue);

var options = new ReadOptions();
Expand Down
13 changes: 8 additions & 5 deletions src/NexusMods.EventSourcing/EventFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ namespace NexusMods.EventSourcing;
internal class EventFormatter : MemoryPackFormatter<IEvent>
{
private static Guid _zeroGuid = Guid.Empty;
private readonly Dictionary<Guid,Type> _eventByGuid;
private readonly Dictionary<Type,Guid> _eventsByType;
private readonly Dictionary<UInt128,Type> _eventByGuid;
private readonly Dictionary<Type,UInt128> _eventsByType;

public EventFormatter(IEnumerable<EventDefinition> events)
{
var eventsArray = events.ToArray();
_eventByGuid = eventsArray.ToDictionary(e => e.Guid, e => e.Type);
_eventsByType = eventsArray.ToDictionary(e => e.Type, e => e.Guid);
_eventByGuid = eventsArray.ToDictionary(e => e.Id, e => e.Type);
_eventsByType = eventsArray.ToDictionary(e => e.Type, e => e.Id);
}

public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref IEvent? value)
Expand All @@ -35,13 +35,16 @@ public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter

public override void Deserialize(ref MemoryPackReader reader, scoped ref IEvent? value)
{
var readValue = reader.ReadValue<Guid>();
throw new NotImplementedException();
/*
var readValue = reader.ReadValue<UInt128>();
if (readValue == _zeroGuid)
{
value = null;
return;
}
var mappedType = _eventByGuid[readValue];
value = (IEvent)reader.ReadValue(mappedType)!;
*/
}
}
28 changes: 28 additions & 0 deletions src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using NexusMods.EventSourcing.Abstractions.Serialization;

namespace NexusMods.EventSourcing.Serialization;

public class BoolSerializer : IFixedSizeSerializer<bool>
{
public bool CanSerialize(Type valueType)
{
return valueType == typeof(bool);
}

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

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

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

namespace NexusMods.EventSourcing.Serialization;

public class EntityIdSerializer : IFixedSizeSerializer<EntityId>
{
public bool CanSerialize(Type valueType)
{
return valueType == typeof(EntityId);
}

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

public void Serialize(EntityId value, Span<byte> output)
{
value.TryWriteBytes(output);
}

public EntityId Deserialize(ReadOnlySpan<byte> from)
{
return EntityId.From(from);
}
}

public class GenericEntityIdSerializer : IGenericSerializer
{
public bool CanSerialize(Type valueType)
{
return false;
}

public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}

public bool TrySpecialze(Type baseType, Type[] argTypes, [NotNullWhen(true)] out ISerializer? serializer)
{
if (baseType != typeof(EntityId<>) || argTypes.Length != 1)
{
serializer = null;
return false;
}

var type = typeof(EntityIdSerializer<>).MakeGenericType(argTypes[0]);
serializer = (ISerializer) Activator.CreateInstance(type)!;
return true;
}
}

internal class EntityIdSerializer<T> : IFixedSizeSerializer<EntityId<T>> where T : IEntity
{
public bool CanSerialize(Type valueType)
{
return valueType == typeof(EntityId<T>);
}

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

public void Serialize(EntityId<T> value, Span<byte> output)
{
value.Value.TryWriteBytes(output);
}

public EntityId<T> Deserialize(ReadOnlySpan<byte> from)
{
return EntityId<T>.From(BinaryPrimitives.ReadUInt64BigEndian(from));
}
}
Loading

0 comments on commit 5595144

Please sign in to comment.