Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel codec visiting #74

Merged
merged 3 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/EdgeDB.Net.Driver/Binary/Builders/ObjectBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using EdgeDB.Binary.Codecs;
using EdgeDB.DataTypes;
using EdgeDB.Utils;
using Microsoft.Extensions.Logging;
using System.Collections;
using System.Collections.Concurrent;
Expand All @@ -12,7 +13,8 @@ internal sealed class ObjectBuilder
private static readonly ConcurrentDictionary<Type, (int Version, ICodec Codec)> _codecVisitorStateTable = new();
private static readonly object _visitorLock = new();

public static PreheatedCodec PreheatCodec<T>(EdgeDBBinaryClient client, ICodec codec)
public static async Task<PreheatedCodec> PreheatCodecAsync<T>(EdgeDBBinaryClient client, ICodec codec,
CancellationToken token)
{
// if the codec has been visited before and we have the most up-to-date version, return it.
if (
Expand All @@ -28,17 +30,18 @@ public static PreheatedCodec PreheatCodec<T>(EdgeDBBinaryClient client, ICodec c

var visitor = new TypeVisitor(client);
visitor.SetTargetType(typeof(T));
visitor.Visit(ref codec);
var reference = new Ref<ICodec>(codec);
await visitor.VisitAsync(reference, token);

if (typeof(T) != typeof(object))
_codecVisitorStateTable[typeof(T)] = (version, codec);
_codecVisitorStateTable[typeof(T)] = (version, reference.Value);

if (client.Logger.IsEnabled(LogLevel.Debug))
{
client.Logger.ObjectDeserializationPrep(CodecFormatter.Format(codec).ToString());
client.Logger.ObjectDeserializationPrep(CodecFormatter.Format(reference.Value).ToString());
}

return new PreheatedCodec(codec);
return new PreheatedCodec(reference.Value);
}

public static T? BuildResult<T>(EdgeDBBinaryClient client, in PreheatedCodec preheated,
Expand All @@ -54,8 +57,9 @@ public static PreheatedCodec PreheatCodec<T>(EdgeDBBinaryClient client, ICodec c
return (T?)ConvertTo(typeof(T), value);
}

public static T? BuildResult<T>(EdgeDBBinaryClient client, ICodec codec, in ReadOnlyMemory<byte> data)
=> BuildResult<T>(client, PreheatCodec<T>(client, codec), data);
public static async Task<T?> BuildResultAsync<T>(
EdgeDBBinaryClient client, ICodec codec, ReadOnlyMemory<byte> data, CancellationToken token)
=> BuildResult<T>(client, await PreheatCodecAsync<T>(client, codec, token), data);

public static object? ConvertTo(Type type, object? value)
{
Expand Down
12 changes: 12 additions & 0 deletions src/EdgeDB.Net.Driver/Binary/Codecs/ArgumentCodecContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace EdgeDB.Binary.Codecs;

internal sealed class ArgumentCodecContext : CodecContext
{
public readonly ICodec[] Codecs;

public ArgumentCodecContext(ICodec[] codecs, EdgeDBBinaryClient client)
: base(client)
{
Codecs = codecs;
}
}
14 changes: 12 additions & 2 deletions src/EdgeDB.Net.Driver/Binary/Codecs/ArrayCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@
namespace EdgeDB.Binary.Codecs;

internal sealed class ArrayCodec<T>
: BaseCodec<T?[]>, IWrappingCodec, ICacheableCodec
: BaseCodec<T?[]>, IWrappingCodec, ICacheableCodec, ICompiledCodec
{
public static readonly byte[] EMPTY_ARRAY = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};

public Type CompiledFrom { get; }
public CompilableWrappingCodec Template { get; }

internal ICodec<T> InnerCodec;

public ArrayCodec(in Guid id, ICodec<T> innerCodec, CodecMetadata? metadata = null)
public ArrayCodec(
in Guid id,
Type compiledFrom,
CompilableWrappingCodec template,
ICodec<T> innerCodec,
CodecMetadata? metadata = null)
: base(in id, metadata)
{
CompiledFrom = compiledFrom;
Template = template;
InnerCodec = innerCodec;
}

Expand Down
2 changes: 1 addition & 1 deletion src/EdgeDB.Net.Driver/Binary/Codecs/CodecContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace EdgeDB.Binary.Codecs;

internal sealed class CodecContext
internal class CodecContext
{
public CodecContext(EdgeDBBinaryClient client)
{
Expand Down
39 changes: 39 additions & 0 deletions src/EdgeDB.Net.Driver/Binary/Codecs/CodecFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ namespace EdgeDB.Binary.Codecs;

internal static class CodecFormatter
{
public static StringBuilder FormatCodecAsTree(ICodec codec)
{
var sb = new StringBuilder();

AppendCodecToTree(sb, codec, 0);

return sb;
}


private const int CODEC_TREE_SPACING = 2;
private static void AppendCodecToTree(StringBuilder tree, ICodec codec, int depth, string? prefix = null)
{
tree.AppendLine("".PadLeft(depth) + $"{prefix} {codec}");

if (codec is IMultiWrappingCodec multiwrap)
{
for (int i = 0; i != multiwrap.InnerCodecs.Length; i++)
{
var innerCodec = multiwrap.InnerCodecs[i];
AppendCodecToTree(
tree,
innerCodec,
depth + CODEC_TREE_SPACING,
i == multiwrap.InnerCodecs.Length - 1 ? "\u2514" : "\u251c"
);
}
}
else if (codec is IWrappingCodec wrapping)
{
AppendCodecToTree(
tree,
wrapping.InnerCodec,
depth + CODEC_TREE_SPACING,
"\u2514"
);
}
}

public static StringBuilder Format(ICodec codec, int spacing = 2)
{
StringBuilder sb = new();
Expand Down
12 changes: 8 additions & 4 deletions src/EdgeDB.Net.Driver/Binary/Codecs/CompilableCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void ICodec.Serialize(ref PacketWriter writer, object? value, CodecContext conte
object? ICodec.Deserialize(ref PacketReader reader, CodecContext context) => throw new NotSupportedException();

// to avoid state changes to this compilable, pass in the inner codec post-walk.
public ICodec Compile(IProtocolProvider provider, Type type, ICodec? innerCodec = null)
public ICompiledCodec Compile(IProtocolProvider provider, Type type, ICodec? innerCodec = null)
{
innerCodec ??= InnerCodec;

Expand All @@ -41,15 +41,19 @@ public ICodec Compile(IProtocolProvider provider, Type type, ICodec? innerCodec

return CodecBuilder.GetProviderCache(provider).CompiledCodecCache.GetOrAdd(cacheKey, k =>
{
var codec = (ICodec)Activator.CreateInstance(genType, Id, innerCodec, Metadata)!;
var codec = (ICompiledCodec)Activator.CreateInstance(
genType,
Id, type, this, innerCodec, Metadata
)!;

if (codec is IComplexCodec complex)
{
complex.BuildRuntimeCodecs(provider);
}

return codec;
});
}) as ICompiledCodec
?? throw new InvalidOperationException("Codec that was returned from the cache was not a compiled codec, this shouldn't happen");
}

public Type GetInnerType()
Expand All @@ -58,5 +62,5 @@ public Type GetInnerType()
: InnerCodec.ConverterType;

public override string ToString()
=> $"compilable({_rootCodecType.Name})";
=> $"compilable({InnerCodec})";
}
4 changes: 2 additions & 2 deletions src/EdgeDB.Net.Driver/Binary/Codecs/Impl/BaseArgumentCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ protected BaseArgumentCodec(in Guid id, CodecMetadata? metadata)
{
}

public abstract void SerializeArguments(ref PacketWriter writer, T? value, CodecContext context);
public abstract void SerializeArguments(ref PacketWriter writer, T? value, ArgumentCodecContext context);

void IArgumentCodec.SerializeArguments(ref PacketWriter writer, object? value, CodecContext context)
void IArgumentCodec.SerializeArguments(ref PacketWriter writer, object? value, ArgumentCodecContext context)
=> SerializeArguments(ref writer, (T?)value, context);
}
4 changes: 2 additions & 2 deletions src/EdgeDB.Net.Driver/Binary/Codecs/Impl/IArgumentCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ namespace EdgeDB.Binary.Codecs;

internal interface IArgumentCodec<T> : IArgumentCodec, ICodec<T>
{
void SerializeArguments(ref PacketWriter writer, T? value, CodecContext context);
void SerializeArguments(ref PacketWriter writer, T? value, ArgumentCodecContext context);
}

internal interface IArgumentCodec
{
void SerializeArguments(ref PacketWriter writer, object? value, CodecContext context);
void SerializeArguments(ref PacketWriter writer, object? value, ArgumentCodecContext context);
}
7 changes: 7 additions & 0 deletions src/EdgeDB.Net.Driver/Binary/Codecs/Impl/ICompiledCodec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace EdgeDB.Binary.Codecs;

internal interface ICompiledCodec : ICodec, IWrappingCodec
{
Type CompiledFrom { get; }
CompilableWrappingCodec Template { get; }
}
24 changes: 21 additions & 3 deletions src/EdgeDB.Net.Driver/Binary/Codecs/MultiRangeCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,32 @@

namespace EdgeDB.Binary.Codecs;

internal sealed class MultiRangeCodec<T> : BaseCodec<MultiRange<T>>, IWrappingCodec, ICacheableCodec
internal sealed class MultiRangeCodec<T>
: BaseCodec<MultiRange<T>>, IWrappingCodec, ICacheableCodec, ICompiledCodec
where T : struct
{
public Type CompiledFrom { get; }
public CompilableWrappingCodec Template { get; }

private RangeCodec<T> _rangeCodec;

public MultiRangeCodec(in Guid id, ICodec<T> rangeInnerCodec, CodecMetadata? metadata) : base(in id, metadata)
public MultiRangeCodec(
in Guid id,
Type compiledFrom,
CompilableWrappingCodec template,
ICodec<T> rangeInnerCodec,
CodecMetadata? metadata
) : base(in id, metadata)
{
_rangeCodec = new RangeCodec<T>(in id, rangeInnerCodec, metadata);
CompiledFrom = compiledFrom;
Template = template;
_rangeCodec = new RangeCodec<T>(
in id,
compiledFrom,
template,
rangeInnerCodec,
metadata
);
}

public override void Serialize(ref PacketWriter writer, MultiRange<T> value, CodecContext context)
Expand Down
2 changes: 1 addition & 1 deletion src/EdgeDB.Net.Driver/Binary/Codecs/NullCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public NullCodec() { }

public NullCodec(CodecMetadata? metadata = null) { } // used in generic codec construction

public void SerializeArguments(ref PacketWriter writer, object? value, CodecContext context) { }
public void SerializeArguments(ref PacketWriter writer, object? value, ArgumentCodecContext context) { }

public Guid Id
=> Guid.Empty;
Expand Down
21 changes: 9 additions & 12 deletions src/EdgeDB.Net.Driver/Binary/Codecs/ObjectCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public EdgeDBTypeDeserializeInfo Deserializer
reader.Position = enumerator.Reader.Position;
}
}

public override string ToString()
=> $"object ({TargetType.Name})";
}

internal class ObjectCodec
Expand Down Expand Up @@ -109,10 +112,13 @@ public TypeInitializedObjectCodec GetOrCreateTypeCodec(Type type)
}
}

public override void SerializeArguments(ref PacketWriter writer, object? value, CodecContext context)
=> Serialize(ref writer, value, context);
public override void SerializeArguments(ref PacketWriter writer, object? value, ArgumentCodecContext context)
=> Serialize(ref writer, value, context.Codecs, context);

public override void Serialize(ref PacketWriter writer, object? value, CodecContext context)
=> Serialize(ref writer, value, InnerCodecs, context);

private void Serialize(ref PacketWriter writer, object? value, ICodec[] codecs, CodecContext context)
{
object?[]? values = null;

Expand All @@ -128,9 +134,6 @@ public override void Serialize(ref PacketWriter writer, object? value, CodecCont

writer.Write(values.Length);

// TODO: maybe cache the visited codecs based on the 'value'.
var visitor = context.CreateTypeVisitor();

for (var i = 0; i != values.Length; i++)
{
var element = values[i];
Expand All @@ -156,17 +159,11 @@ public override void Serialize(ref PacketWriter writer, object? value, CodecCont
}
else
{
var innerCodec = InnerCodecs[i];
var innerCodec = codecs[i];

// special case for enums
if (element.GetType().IsEnum && innerCodec is TextCodec)
element = element.ToString();
else
{
visitor.SetTargetType(element.GetType());
visitor.Visit(ref innerCodec);
visitor.Reset();
}

writer.WriteToWithInt32Length((ref PacketWriter innerWriter) =>
innerCodec.Serialize(ref innerWriter, element, context));
Expand Down
23 changes: 17 additions & 6 deletions src/EdgeDB.Net.Driver/Binary/Codecs/RangeCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,37 @@
namespace EdgeDB.Binary.Codecs;

internal sealed class RangeCodec<T>
: BaseComplexCodec<Range<T>>, IWrappingCodec, ICacheableCodec
: BaseComplexCodec<Range<T>>, IWrappingCodec, ICacheableCodec, ICompiledCodec
where T : struct
{
[Flags]
public enum RangeFlags : byte
{
Empty = 1 << 0,
IncudeLowerBound = 1 << 1,
IncludeLowerBound = 1 << 1,
IncludeUpperBound = 1 << 2,
InfiniteLowerBound = 1 << 3,
InfiniteUpperBound = 1 << 4
}

public ICodec<T> _innerCodec;
public Type CompiledFrom { get; }
public CompilableWrappingCodec Template { get; }

public RangeCodec(in Guid id, ICodec<T> innerCodec, CodecMetadata? metadata = null)
private ICodec<T> _innerCodec;

public RangeCodec(
in Guid id,
Type compiledFrom,
CompilableWrappingCodec template,
ICodec<T> innerCodec,
CodecMetadata? metadata = null)
: base(in id, metadata)
{
_innerCodec = innerCodec;

CompiledFrom = compiledFrom;
Template = template;

AddConverter(From, To);
}

Expand Down Expand Up @@ -108,15 +119,15 @@ public override Range<T> Deserialize(ref PacketReader reader, CodecContext conte
upperBound = _innerCodec.Deserialize(ref reader, context);
}

return new Range<T>(lowerBound, upperBound, (flags & RangeFlags.IncudeLowerBound) != 0,
return new Range<T>(lowerBound, upperBound, (flags & RangeFlags.IncludeLowerBound) != 0,
(flags & RangeFlags.IncludeUpperBound) != 0);
}

public override void Serialize(ref PacketWriter writer, Range<T> value, CodecContext context)
{
var flags = value.IsEmpty
? RangeFlags.Empty
: (value.IncludeLower ? RangeFlags.IncudeLowerBound : 0) |
: (value.IncludeLower ? RangeFlags.IncludeLowerBound : 0) |
(value.IncludeUpper ? RangeFlags.IncludeUpperBound : 0) |
(!value.Lower.HasValue ? RangeFlags.InfiniteLowerBound : 0) |
(!value.Upper.HasValue ? RangeFlags.InfiniteUpperBound : 0);
Expand Down
Loading
Loading