Skip to content

Commit

Permalink
Merge pull request #159 from agocke/visitor-ifaces
Browse files Browse the repository at this point in the history
Use boxed interface visitors instead of constrained generics
  • Loading branch information
agocke authored Feb 20, 2024
2 parents c432b61 + bfb0d39 commit 2432515
Show file tree
Hide file tree
Showing 59 changed files with 475 additions and 413 deletions.
5 changes: 2 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/artifacts/bin/bench/Debug/net7.0/bench.dll",
"program": "${workspaceFolder}/perf/bench/bin/Debug/net8.0/bench.dll",
"args": [],
"cwd": "${workspaceFolder}/bench",
"cwd": "${workspaceFolder}/perf/bench",
"console": "internalConsole",
"stopAtEntry": false,
"justMyCode": false,
Expand Down
147 changes: 89 additions & 58 deletions perf/bench/SampleTypes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#nullable disable

using System;
using Serde;

namespace Benchmarks
Expand Down Expand Up @@ -57,79 +58,115 @@ public partial record Location

public partial record LocationWrap : IDeserialize<Location>
{
[GenerateDeserialize]
private enum FieldNames : byte
static Benchmarks.Location Serde.IDeserialize<Benchmarks.Location>.Deserialize<D>(ref D deserializer)
{
Id = 1,
Address1 = 2,
Address2 = 3,
City = 4,
State = 5,
PostalCode = 6,
Name = 7,
PhoneNumber = 8,
Country = 9,
var fieldNames = new[]
{
"Id",
"Address1",
"Address2",
"City",
"State",
"PostalCode",
"Name",
"PhoneNumber",
"Country"
};
return deserializer.DeserializeType("Location", fieldNames, SerdeVisitor.Instance);
}

public static Location Deserialize<D>(ref D deserializer) where D : IDeserializer
{
return Deserialize<Location, D>(ref deserializer);
}
private sealed class LocationVisitor : IDeserializeVisitor<Location>
private sealed class SerdeVisitor : Serde.IDeserializeVisitor<Benchmarks.Location>
{
public string ExpectedTypeName => nameof(Location);
public static readonly SerdeVisitor Instance = new SerdeVisitor();
public string ExpectedTypeName => "Benchmarks.Location";

Location IDeserializeVisitor<Location>.VisitDictionary<D>(ref D d)
private sealed class FieldNameVisitor : Serde.IDeserialize<byte>, Serde.IDeserializeVisitor<byte>
{
int _l_id = default!;
string _l_address1 = default!;
string _l_address2 = default!;
string _l_city = default!;
string _l_state = default!;
string _l_postalCode = default!;
string _l_name = default!;
string _l_phoneNumber = default!;
string _l_country = default!;
short _r_assignedValid = 0b0;
while (d.TryGetNextKey<FieldNames, FieldNamesWrap>(out var key))
public static readonly FieldNameVisitor Instance = new FieldNameVisitor();
public static byte Deserialize<D>(ref D deserializer)
where D : IDeserializer => deserializer.DeserializeString(Instance);
public string ExpectedTypeName => "string";

byte Serde.IDeserializeVisitor<byte>.VisitString(string s) => VisitUtf8Span(System.Text.Encoding.UTF8.GetBytes(s));
public byte VisitUtf8Span(System.ReadOnlySpan<byte> s)
{
switch (s[0])
{
case (byte)'i'when s.SequenceEqual("id"u8):
return 1;
case (byte)'a'when s.SequenceEqual("address1"u8):
return 2;
case (byte)'a'when s.SequenceEqual("address2"u8):
return 3;
case (byte)'c'when s.SequenceEqual("city"u8):
return 4;
case (byte)'s'when s.SequenceEqual("state"u8):
return 5;
case (byte)'p'when s.SequenceEqual("postalCode"u8):
return 6;
case (byte)'n'when s.SequenceEqual("name"u8):
return 7;
case (byte)'p'when s.SequenceEqual("phoneNumber"u8):
return 8;
case (byte)'c'when s.SequenceEqual("country"u8):
return 9;
default:
return 0;
}
}
}

Benchmarks.Location Serde.IDeserializeVisitor<Benchmarks.Location>.VisitDictionary<D>(ref D d)
{
int _l_id = default !;
string _l_address1 = default !;
string _l_address2 = default !;
string _l_city = default !;
string _l_state = default !;
string _l_postalcode = default !;
string _l_name = default !;
string _l_phonenumber = default !;
string _l_country = default !;
ushort _r_assignedValid = 0b0;
while (d.TryGetNextKey<byte, FieldNameVisitor>(out byte key))
{
switch (key)
{
case FieldNames.Id:
case 1:
_l_id = d.GetNextValue<int, Int32Wrap>();
_r_assignedValid |= 1 << 0;
_r_assignedValid |= ((ushort)1) << 0;
break;
case FieldNames.Address1:
case 2:
_l_address1 = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 1;
_r_assignedValid |= ((ushort)1) << 1;
break;
case FieldNames.Address2:
case 3:
_l_address2 = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 2;
_r_assignedValid |= ((ushort)1) << 2;
break;
case FieldNames.City:
case 4:
_l_city = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 3;
_r_assignedValid |= ((ushort)1) << 3;
break;
case FieldNames.State:
case 5:
_l_state = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 4;
_r_assignedValid |= ((ushort)1) << 4;
break;
case FieldNames.PostalCode:
_l_postalCode = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 5;
case 6:
_l_postalcode = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= ((ushort)1) << 5;
break;
case FieldNames.Name:
case 7:
_l_name = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 6;
_r_assignedValid |= ((ushort)1) << 6;
break;
case FieldNames.PhoneNumber:
_l_phoneNumber = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 7;
case 8:
_l_phonenumber = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= ((ushort)1) << 7;
break;
case FieldNames.Country:
case 9:
_l_country = d.GetNextValue<string, StringWrap>();
_r_assignedValid |= 1 << 8;
_r_assignedValid |= ((ushort)1) << 8;
break;
}
}
Expand All @@ -139,27 +176,21 @@ Location IDeserializeVisitor<Location>.VisitDictionary<D>(ref D d)
throw new Serde.InvalidDeserializeValueException("Not all members were assigned");
}

var newType = new Location
var newType = new Benchmarks.Location()
{
Id = _l_id,
Address1 = _l_address1,
Address2 = _l_address2,
City = _l_city,
State = _l_state,
PostalCode = _l_postalCode,
PostalCode = _l_postalcode,
Name = _l_name,
PhoneNumber = _l_phoneNumber,
PhoneNumber = _l_phonenumber,
Country = _l_country,
};
return newType;
}
}

private static T Deserialize<T, D>(ref D deserialize)
where T : IDeserialize<T>
where D : IDeserializer
{
return T.Deserialize(ref deserialize);
}
}
}
1 change: 1 addition & 0 deletions perf/bench/bench.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<Deterministic>true</Deterministic>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
Expand Down
33 changes: 8 additions & 25 deletions src/generator/Generator.Deserialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private static MethodDeclarationSyntax GenerateDeserializeMethod(
// 3. Custom type
//
// var fieldNames = new[] { 'field1', 'field2', 'field3' ... };
// return deserializer.DeserializeType<'TypeName', 'GeneratedVisitorName'>('TypeName', fieldNames, visitor);
// return deserializer.DeserializeType('TypeName', fieldNames, visitor);

var serdeName = SerdeBuiltInName(typeSymbol.SpecialType);
var typeSyntax = ParseTypeName(typeSymbol.ToString());
Expand All @@ -80,13 +80,7 @@ private static MethodDeclarationSyntax GenerateDeserializeMethod(
stmts.Add(ReturnStatement(InvocationExpression(
QualifiedName(
IdentifierName("deserializer"),
GenericName(
Identifier("Deserialize" + serdeName),
TypeArgumentList(SeparatedList(new TypeSyntax[] {
typeSyntax,
IdentifierName(GeneratedVisitorName)
}))
)
IdentifierName("Deserialize" + serdeName)
),
ArgumentList(SeparatedList(new[] {
Argument(IdentifierName("visitor"))
Expand All @@ -99,13 +93,7 @@ private static MethodDeclarationSyntax GenerateDeserializeMethod(
stmts.Add(ReturnStatement(InvocationExpression(
QualifiedName(
IdentifierName("deserializer"),
GenericName(
Identifier("DeserializeString"),
TypeArgumentList(SeparatedList(new TypeSyntax[] {
typeSyntax,
IdentifierName(GeneratedVisitorName)
}))
)
IdentifierName("DeserializeString")
),
ArgumentList(SeparatedList(new[] {
Argument(IdentifierName("visitor"))
Expand All @@ -131,17 +119,11 @@ private static MethodDeclarationSyntax GenerateDeserializeMethod(
)
));

// return deserializer.DeserializeType<'TypeName', 'GeneratedVisitorName'>('TypeName', fieldNames, visitor);
// return deserializer.DeserializeType('TypeName', fieldNames, visitor);
stmts.Add(ReturnStatement(InvocationExpression(
QualifiedName(
IdentifierName("deserializer"),
GenericName(
Identifier("DeserializeType"),
TypeArgumentList(SeparatedList(new TypeSyntax[] {
typeSyntax,
IdentifierName(GeneratedVisitorName)
})))
),
IdentifierName("DeserializeType")),
ArgumentList(SeparatedList(new[] {
Argument(StringLiteral(typeSymbol.Name)),
Argument(IdentifierName("fieldNames")),
Expand Down Expand Up @@ -262,10 +244,11 @@ _ when System.MemoryExtensions.SequenceEqual(s, "{{formatted}}"u8) => {{typeName
private static MemberDeclarationSyntax GenerateFieldNameVisitor(ITypeSymbol type, string typeName, List<DataMemberSymbol> members)
{
var text = $$"""
private struct FieldNameVisitor : Serde.IDeserialize<byte>, Serde.IDeserializeVisitor<byte>
private sealed class FieldNameVisitor : Serde.IDeserialize<byte>, Serde.IDeserializeVisitor<byte>
{
public static readonly FieldNameVisitor Instance = new FieldNameVisitor();
public static byte Deserialize<D>(ref D deserializer) where D : IDeserializer
=> deserializer.DeserializeString<byte, FieldNameVisitor>(new FieldNameVisitor());
=> deserializer.DeserializeString(Instance);
public string ExpectedTypeName => "string";
Expand Down
40 changes: 20 additions & 20 deletions src/serde/IDeserialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,25 @@ bool TryGetNextEntry<K, DK, V, DV>([MaybeNullWhen(false)] out (K, V) next)

public interface IDeserializer
{
T DeserializeAny<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeBool<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeChar<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeByte<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeU16<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeU32<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeU64<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeSByte<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeI16<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeI32<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeI64<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeFloat<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeDouble<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeDecimal<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeString<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeIdentifier<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeType<T, V>(string typeName, ReadOnlySpan<string> fieldNames, V v) where V : IDeserializeVisitor<T>;
T DeserializeEnumerable<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeDictionary<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeNullableRef<T, V>(V v) where V : IDeserializeVisitor<T>;
T DeserializeAny<T>(IDeserializeVisitor<T> v);
T DeserializeBool<T>(IDeserializeVisitor<T> v);
T DeserializeChar<T>(IDeserializeVisitor<T> v);
T DeserializeByte<T>(IDeserializeVisitor<T> v);
T DeserializeU16<T>(IDeserializeVisitor<T> v);
T DeserializeU32<T>(IDeserializeVisitor<T> v);
T DeserializeU64<T>(IDeserializeVisitor<T> v);
T DeserializeSByte<T>(IDeserializeVisitor<T> v);
T DeserializeI16<T>(IDeserializeVisitor<T> v);
T DeserializeI32<T>(IDeserializeVisitor<T> v);
T DeserializeI64<T>(IDeserializeVisitor<T> v);
T DeserializeFloat<T>(IDeserializeVisitor<T> v);
T DeserializeDouble<T>(IDeserializeVisitor<T> v);
T DeserializeDecimal<T>(IDeserializeVisitor<T> v);
T DeserializeString<T>(IDeserializeVisitor<T> v);
T DeserializeIdentifier<T>(IDeserializeVisitor<T> v);
T DeserializeType<T>(string typeName, ReadOnlySpan<string> fieldNames, IDeserializeVisitor<T> v);
T DeserializeEnumerable<T>(IDeserializeVisitor<T> v);
T DeserializeDictionary<T>(IDeserializeVisitor<T> v);
T DeserializeNullableRef<T>(IDeserializeVisitor<T> v);
}
}
2 changes: 1 addition & 1 deletion src/serde/Wrappers.Dictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void ISerialize<Dictionary<TKey, TValue>>.Serialize(Dictionary<TKey, TValue> val
{
static Dictionary<TKey, TValue> IDeserialize<Dictionary<TKey, TValue>>.Deserialize<D>(ref D deserializer)
{
return deserializer.DeserializeDictionary<Dictionary<TKey, TValue>, Visitor>(new Visitor());
return deserializer.DeserializeDictionary(new Visitor());
}
private struct Visitor : IDeserializeVisitor<Dictionary<TKey, TValue>>
{
Expand Down
8 changes: 3 additions & 5 deletions src/serde/Wrappers.List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void ISerialize.Serialize(ISerializer serializer)
{
static T[] IDeserialize<T[]>.Deserialize<D>(ref D deserializer)
{
return deserializer.DeserializeEnumerable<T[], SerdeVisitor>(new SerdeVisitor());
return deserializer.DeserializeEnumerable(new SerdeVisitor());
}
private struct SerdeVisitor : IDeserializeVisitor<T[]>
{
Expand Down Expand Up @@ -132,7 +132,7 @@ void ISerialize<List<T>>.Serialize(List<T> value, ISerializer serializer)
{
static List<T> IDeserialize<List<T>>.Deserialize<D>(ref D deserializer)
{
return deserializer.DeserializeEnumerable<List<T>, SerdeVisitor>(new SerdeVisitor());
return deserializer.DeserializeEnumerable(new SerdeVisitor());
}
private struct SerdeVisitor : IDeserializeVisitor<List<T>>
{
Expand Down Expand Up @@ -184,9 +184,7 @@ void ISerialize<ImmutableArray<T>>.Serialize(ImmutableArray<T> value, ISerialize
{
static ImmutableArray<T> IDeserialize<ImmutableArray<T>>.Deserialize<D>(ref D deserializer)
{
return deserializer.DeserializeEnumerable<
ImmutableArray<T>,
Visitor>(new Visitor());
return deserializer.DeserializeEnumerable(new Visitor());
}

private struct Visitor : IDeserializeVisitor<ImmutableArray<T>>
Expand Down
Loading

0 comments on commit 2432515

Please sign in to comment.