Skip to content

Commit 1dc76db

Browse files
committed
Implement basic support for MessagePack
1 parent 3496b18 commit 1dc76db

21 files changed

+1284
-97
lines changed

bench/DataGenerator.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
using System;
3+
4+
namespace Benchmarks
5+
{
6+
internal static class DataGenerator
7+
{
8+
public static T GenerateSerialize<T>() where T : Serde.ISerializeProvider<T>
9+
{
10+
if (typeof(T) == typeof(LoginViewModel))
11+
return (T)(object)CreateLoginViewModel();
12+
if (typeof(T) == typeof(Location))
13+
return (T)(object)Location.Sample;
14+
15+
throw new InvalidOperationException();
16+
17+
static LoginViewModel CreateLoginViewModel() => new LoginViewModel
18+
{
19+
Email = "name.familyname@not.com",
20+
// [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy credentials for perf testing.")]
21+
Password = "abcdefgh123456!@",
22+
RememberMe = true
23+
};
24+
25+
}
26+
27+
public static Location CreateLocation() => new Location
28+
{
29+
Id = 1234,
30+
Address1 = "The Street Name",
31+
Address2 = "20/11",
32+
City = "The City",
33+
State = "The State",
34+
PostalCode = "abc-12",
35+
Name = "Nonexisting",
36+
PhoneNumber = "+0 11 222 333 44",
37+
Country = "The Greatest"
38+
};
39+
40+
public static byte[] GenerateDeserialize<T>()
41+
{
42+
if (typeof(T) == typeof(LoginViewModel))
43+
return MessagePack.MessagePackSerializer.Serialize(LoginViewSample);
44+
if (typeof(T) == typeof(Location))
45+
return MessagePack.MessagePackSerializer.Serialize(Location.Sample);
46+
47+
throw new InvalidOperationException("Unexpected type");
48+
}
49+
50+
public const string LoginViewSample = """
51+
{
52+
"email": "name.familyname@not.com",
53+
"password": "abcdefgh123456!@",
54+
"rememberMe": true
55+
}
56+
""";
57+
58+
}
59+
}

bench/DeserializeFromString.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Attributes;
6+
using MessagePack;
7+
using Serde;
8+
9+
namespace Benchmarks
10+
{
11+
[GenericTypeArguments(typeof(Location), typeof(LocationWrap))]
12+
public class DeserializeFromString<T, U>
13+
where T : Serde.IDeserializeProvider<T>
14+
where U : Serde.IDeserializeProvider<T>
15+
{
16+
private byte[] value = null!;
17+
18+
private readonly IDeserialize<T> _proxy = T.DeserializeInstance;
19+
private readonly IDeserialize<T> _manualProxy = U.DeserializeInstance;
20+
21+
[GlobalSetup]
22+
public void Setup()
23+
{
24+
value = DataGenerator.GenerateDeserialize<T>();
25+
}
26+
27+
[Benchmark]
28+
public T? MessagePack()
29+
{
30+
return MessagePackSerializer.Deserialize<T>(value);
31+
}
32+
33+
[Benchmark]
34+
public T SerdeMsgPack() => Serde.MsgPack.MsgPackSerializer.Deserialize<T, IDeserialize<T>>(value, _proxy);
35+
36+
[Benchmark]
37+
public T SerdeMsgPackManual() => Serde.MsgPack.MsgPackSerializer.Deserialize<T, IDeserialize<T>>(value, _manualProxy);
38+
39+
// DataContractJsonSerializer does not provide an API to serialize to string
40+
// so it's not included here (apples vs apples thing)
41+
}
42+
}

bench/Program.cs

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
1-
// See https://aka.ms/new-console-template for more information
2-
using System;
3-
using System.Security.Cryptography;
4-
using BenchmarkDotNet.Attributes;
1+
using BenchmarkDotNet.Configs;
2+
using BenchmarkDotNet.Diagnosers;
53
using BenchmarkDotNet.Running;
4+
using Benchmarks;
5+
using MessagePack;
66

7-
namespace MsgPack.Benchmarks;
7+
var msg1 = MessagePackSerializer.Serialize(Location.Sample);
8+
var msg2 = Serde.MsgPack.MsgPackSerializer.Serialize(Location.Sample);
89

9-
public class MessagePackUnit
10+
if (!msg1.SequenceEqual(msg2))
1011
{
11-
private const int N = 10000;
12-
private readonly byte[] data;
13-
14-
private readonly SHA256 sha256 = SHA256.Create();
15-
private readonly MD5 md5 = MD5.Create();
16-
17-
public MessagePackUnit()
18-
{
19-
data = new byte[N];
20-
new Random(42).NextBytes(data);
21-
}
22-
23-
[Benchmark]
24-
public byte[] Sha256() => sha256.ComputeHash(data);
25-
26-
[Benchmark]
27-
public byte[] Md5() => md5.ComputeHash(data);
12+
Console.WriteLine(string.Join(", ", msg1));
13+
Console.WriteLine(string.Join(", ", msg2));
14+
throw new InvalidOperationException("bytes do not match");
2815
}
2916

30-
public class Program
17+
var loc1 = MessagePackSerializer.Deserialize<Location>(msg1);
18+
var loc2 = Serde.MsgPack.MsgPackSerializer.Deserialize<Location, LocationWrap>(msg1, LocationWrap.Instance);
19+
20+
Console.WriteLine("Checking correctness of serialization: " + (loc1 == loc2));
21+
if (loc1 != loc2)
3122
{
32-
public static void Main(string[] args)
33-
{
34-
var summary = BenchmarkRunner.Run<MessagePackUnit>();
35-
}
23+
throw new InvalidOperationException($"""
24+
Serialization is not correct
25+
STJ:
26+
{loc1}
27+
28+
Serde:
29+
{loc2}
30+
""");
3631
}
3732

33+
var config = DefaultConfig.Instance.AddDiagnoser(MemoryDiagnoser.Default);
34+
var summary = BenchmarkSwitcher.FromAssembly(typeof(DeserializeFromString<,>).Assembly)
35+
.Run(args, config);

bench/SampleTypes.cs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
2+
#nullable disable
3+
4+
using System;
5+
using MessagePack;
6+
using Serde;
7+
8+
namespace Benchmarks
9+
{
10+
// the view models come from a real world app called "AllReady"
11+
[GenerateSerialize, GenerateDeserialize]
12+
public partial class LoginViewModel
13+
{
14+
public string Email { get; set; }
15+
public string Password { get; set; }
16+
public bool RememberMe { get; set; }
17+
}
18+
19+
[GenerateSerialize, GenerateDeserialize]
20+
[MessagePackObject]
21+
public partial record Location
22+
{
23+
[Key(0)]
24+
public int Id { get; set; }
25+
[Key(1)]
26+
public string Address1 { get; set; }
27+
[Key(2)]
28+
public string Address2 { get; set; }
29+
[Key(3)]
30+
public string City { get; set; }
31+
[Key(4)]
32+
public string State { get; set; }
33+
[Key(5)]
34+
public string PostalCode { get; set; }
35+
[Key(6)]
36+
public string Name { get; set; }
37+
[Key(7)]
38+
public string PhoneNumber { get; set; }
39+
[Key(8)]
40+
public string Country { get; set; }
41+
42+
public static Location Sample => new Location
43+
{
44+
Id = 1234,
45+
Address1 = "The Street Name",
46+
Address2 = "20/11",
47+
City = "The City",
48+
State = "The State",
49+
PostalCode = "abc-12",
50+
Name = "Nonexisting",
51+
PhoneNumber = "+0 11 222 333 44",
52+
Country = "The Greatest"
53+
};
54+
}
55+
56+
public sealed partial class LocationWrap : IDeserialize<Location>, IDeserializeProvider<Location>
57+
{
58+
public static LocationWrap Instance { get; } = new();
59+
static IDeserialize<Location> IDeserializeProvider<Location>.DeserializeInstance => Instance;
60+
private LocationWrap() { }
61+
62+
public static ISerdeInfo SerdeInfo { get; } = Serde.SerdeInfo.MakeCustom(
63+
"Location",
64+
typeof(Location).GetCustomAttributesData(),
65+
[
66+
("id", Int32Proxy.SerdeInfo, typeof(Location).GetProperty("Id")!),
67+
("address1", StringProxy.SerdeInfo, typeof(Location).GetProperty("Address1")!),
68+
("address2", StringProxy.SerdeInfo, typeof(Location).GetProperty("Address2")!),
69+
("city", StringProxy.SerdeInfo, typeof(Location).GetProperty("City")!),
70+
("state", StringProxy.SerdeInfo, typeof(Location).GetProperty("State")!),
71+
("postalCode", StringProxy.SerdeInfo, typeof(Location).GetProperty("PostalCode")!),
72+
("name", StringProxy.SerdeInfo, typeof(Location).GetProperty("Name")!),
73+
("phoneNumber", StringProxy.SerdeInfo, typeof(Location).GetProperty("PhoneNumber")!),
74+
("country", StringProxy.SerdeInfo, typeof(Location).GetProperty("Country")!)
75+
]);
76+
77+
Benchmarks.Location Serde.IDeserialize<Benchmarks.Location>.Deserialize(IDeserializer deserializer)
78+
{
79+
int _l_id = default !;
80+
string _l_address1 = default !;
81+
string _l_address2 = default !;
82+
string _l_city = default !;
83+
string _l_state = default !;
84+
string _l_postalcode = default !;
85+
string _l_name = default !;
86+
string _l_phonenumber = default !;
87+
string _l_country = default !;
88+
ushort _r_assignedValid = 0b0;
89+
90+
var _l_serdeInfo = SerdeInfo;
91+
var typeDeserialize = deserializer.ReadType(_l_serdeInfo);
92+
int index;
93+
while ((index = typeDeserialize.TryReadIndex(_l_serdeInfo, out _)) != IDeserializeType.EndOfType)
94+
{
95+
switch (index)
96+
{
97+
case 0:
98+
_l_id = typeDeserialize.ReadI32(index);
99+
_r_assignedValid |= ((ushort)1) << 0;
100+
break;
101+
case 1:
102+
_l_address1 = typeDeserialize.ReadString(index);
103+
_r_assignedValid |= ((ushort)1) << 1;
104+
break;
105+
case 2:
106+
_l_address2 = typeDeserialize.ReadString(index);
107+
_r_assignedValid |= ((ushort)1) << 2;
108+
break;
109+
case 3:
110+
_l_city = typeDeserialize.ReadString(index);
111+
_r_assignedValid |= ((ushort)1) << 3;
112+
break;
113+
case 4:
114+
_l_state = typeDeserialize.ReadString(index);
115+
_r_assignedValid |= ((ushort)1) << 4;
116+
break;
117+
case 5:
118+
_l_postalcode = typeDeserialize.ReadString(index);
119+
_r_assignedValid |= ((ushort)1) << 5;
120+
break;
121+
case 6:
122+
_l_name = typeDeserialize.ReadString(index);
123+
_r_assignedValid |= ((ushort)1) << 6;
124+
break;
125+
case 7:
126+
_l_phonenumber = typeDeserialize.ReadString(index);
127+
_r_assignedValid |= ((ushort)1) << 7;
128+
break;
129+
case 8:
130+
_l_country = typeDeserialize.ReadString(index);
131+
_r_assignedValid |= ((ushort)1) << 8;
132+
break;
133+
case Serde.IDeserializeType.IndexNotFound:
134+
typeDeserialize.SkipValue();
135+
break;
136+
default:
137+
throw new InvalidOperationException("Unexpected index: " + index);
138+
}
139+
}
140+
141+
if (_r_assignedValid != 0b111111111)
142+
{
143+
throw Serde.DeserializeException.UnassignedMember();
144+
}
145+
146+
var newType = new Benchmarks.Location()
147+
{
148+
Id = _l_id,
149+
Address1 = _l_address1,
150+
Address2 = _l_address2,
151+
City = _l_city,
152+
State = _l_state,
153+
PostalCode = _l_postalcode,
154+
Name = _l_name,
155+
PhoneNumber = _l_phonenumber,
156+
Country = _l_country,
157+
};
158+
return newType;
159+
}
160+
}
161+
}

bench/bench.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
<ItemGroup>
1111
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
12+
<PackageReference Include="MessagePack" Version="3.1.0" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\src\Serde.MsgPack.csproj" />
1217
</ItemGroup>
1318

1419
</Project>

bench/dotnet

Whitespace-only changes.

serde.msgpack.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serde.MsgPack.Tests", "test
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serde.MsgPack", "src\Serde.MsgPack.csproj", "{C765AC6A-ACF0-415E-BA83-E05D8CFDF5D9}"
99
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bench", "bench\bench.csproj", "{CBB8ED1D-9DA7-451E-9D61-6ADC829AE664}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -24,5 +26,9 @@ Global
2426
{C765AC6A-ACF0-415E-BA83-E05D8CFDF5D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
2527
{C765AC6A-ACF0-415E-BA83-E05D8CFDF5D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
2628
{C765AC6A-ACF0-415E-BA83-E05D8CFDF5D9}.Release|Any CPU.Build.0 = Release|Any CPU
29+
{CBB8ED1D-9DA7-451E-9D61-6ADC829AE664}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30+
{CBB8ED1D-9DA7-451E-9D61-6ADC829AE664}.Debug|Any CPU.Build.0 = Debug|Any CPU
31+
{CBB8ED1D-9DA7-451E-9D61-6ADC829AE664}.Release|Any CPU.ActiveCfg = Release|Any CPU
32+
{CBB8ED1D-9DA7-451E-9D61-6ADC829AE664}.Release|Any CPU.Build.0 = Release|Any CPU
2733
EndGlobalSection
2834
EndGlobal

src/IByteReader.cs

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)