Skip to content

Commit ad5dce5

Browse files
authored
Merge pull request #238 from Cysharp/hadashiA/net8-collections
Add FrozenCollectionFormatter
2 parents fb16a8b + cdcc877 commit ad5dce5

5 files changed

Lines changed: 191 additions & 5 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#if NET8_0_OR_GREATER
2+
using MemoryPack.Formatters;
3+
using MemoryPack.Internal;
4+
using System.Collections.Frozen;
5+
6+
// Frozen Collections formatters
7+
8+
namespace MemoryPack
9+
{
10+
public static partial class MemoryPackFormatterProvider
11+
{
12+
static readonly Dictionary<Type, Type> FrozenCollectionFormatters = new Dictionary<Type, Type>()
13+
{
14+
{ typeof(FrozenDictionary<,>), typeof(FrozenDictionaryFormatter<,>) },
15+
{ typeof(FrozenSet<>), typeof(FrozenSetFormatter<>) },
16+
};
17+
}
18+
}
19+
20+
namespace MemoryPack.Formatters
21+
{
22+
[Preserve]
23+
public sealed class FrozenDictionaryFormatter<TKey, TValue> : MemoryPackFormatter<FrozenDictionary<TKey, TValue?>>
24+
where TKey : notnull
25+
{
26+
readonly IEqualityComparer<TKey>? equalityComparer;
27+
28+
public FrozenDictionaryFormatter() : this(null)
29+
{
30+
31+
}
32+
33+
public FrozenDictionaryFormatter(IEqualityComparer<TKey>? equalityComparer)
34+
{
35+
this.equalityComparer = equalityComparer;
36+
}
37+
38+
[Preserve]
39+
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref FrozenDictionary<TKey, TValue?>? value)
40+
{
41+
if (value == null)
42+
{
43+
writer.WriteNullCollectionHeader();
44+
return;
45+
}
46+
47+
var keyFormatter = writer.GetFormatter<TKey>();
48+
var valueFormatter = writer.GetFormatter<TValue>();
49+
50+
var count = value.Count;
51+
writer.WriteCollectionHeader(count);
52+
var i = 0;
53+
foreach (var item in value)
54+
{
55+
i++;
56+
KeyValuePairFormatter.Serialize(keyFormatter, valueFormatter, ref writer, item!);
57+
}
58+
59+
if (i != count) MemoryPackSerializationException.ThrowInvalidConcurrrentCollectionOperation();
60+
}
61+
62+
[Preserve]
63+
public override void Deserialize(ref MemoryPackReader reader, scoped ref FrozenDictionary<TKey, TValue?>? value)
64+
{
65+
if (!reader.TryReadCollectionHeader(out var length))
66+
{
67+
value = null;
68+
return;
69+
}
70+
71+
var dict = new Dictionary<TKey, TValue?>(length, equalityComparer);
72+
73+
var keyFormatter = reader.GetFormatter<TKey>();
74+
var valueFormatter = reader.GetFormatter<TValue>();
75+
for (var i = 0; i < length; i++)
76+
{
77+
KeyValuePairFormatter.Deserialize(keyFormatter, valueFormatter, ref reader, out var k, out var v);
78+
dict.Add(k!, v);
79+
}
80+
value = dict.ToFrozenDictionary(equalityComparer);
81+
}
82+
}
83+
84+
public sealed class FrozenSetFormatter<T> : MemoryPackFormatter<FrozenSet<T?>>
85+
{
86+
readonly IEqualityComparer<T?>? equalityComparer;
87+
88+
public FrozenSetFormatter() : this(null)
89+
{
90+
}
91+
92+
public FrozenSetFormatter(IEqualityComparer<T?>? equalityComparer)
93+
{
94+
this.equalityComparer = equalityComparer;
95+
}
96+
97+
[Preserve]
98+
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref FrozenSet<T?>? value)
99+
{
100+
if (value == null)
101+
{
102+
writer.WriteNullCollectionHeader();
103+
return;
104+
}
105+
106+
var formatter = writer.GetFormatter<T?>();
107+
writer.WriteCollectionHeader(value.Count);
108+
foreach (var item in value)
109+
{
110+
var v = item;
111+
formatter.Serialize(ref writer, ref v);
112+
}
113+
}
114+
115+
[Preserve]
116+
public override void Deserialize(ref MemoryPackReader reader, scoped ref FrozenSet<T?>? value)
117+
{
118+
if (!reader.TryReadCollectionHeader(out var length))
119+
{
120+
value = null;
121+
return;
122+
}
123+
124+
var set = new HashSet<T>(length, equalityComparer);
125+
126+
var formatter = reader.GetFormatter<T?>();
127+
for (int i = 0; i < length; i++)
128+
{
129+
T? v = default;
130+
formatter.Deserialize(ref reader, ref v);
131+
set.Add(v!);
132+
}
133+
134+
value = set.ToFrozenSet(equalityComparer)!;
135+
}
136+
}
137+
}
138+
#endif

src/MemoryPack.Core/Formatters/ImmutableCollectionFormatters.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Buffers;
44
using System.Collections.Immutable;
55
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
67

78
// Immutable Collections formatters
89

@@ -63,10 +64,14 @@ public override void Deserialize(ref MemoryPackReader reader, scoped ref Immutab
6364
return;
6465
}
6566

67+
#if NET8_0_OR_GREATER
68+
value = ImmutableCollectionsMarshal.AsImmutableArray(array);
69+
#else
6670
// create Empty and replace inner T[] field(avoid defensive copy of Create)
6771
value = ImmutableArray.Create<T?>();
6872
ref var view = ref Unsafe.As<ImmutableArray<T?>, ImmutableArrayView<T?>>(ref value);
6973
view.array = array;
74+
#endif
7075
}
7176
}
7277

@@ -348,7 +353,7 @@ public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter
348353

349354
var keyFormatter = writer.GetFormatter<TKey>();
350355
var valueFormatter = writer.GetFormatter<TValue>();
351-
356+
352357
writer.WriteCollectionHeader(value.Count);
353358
foreach (var item in value)
354359
{
@@ -879,7 +884,7 @@ public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter
879884

880885
var keyFormatter = writer.GetFormatter<TKey>();
881886
var valueFormatter = writer.GetFormatter<TValue>();
882-
887+
883888
writer.WriteCollectionHeader(value.Count);
884889
foreach (var item in value)
885890
{

src/MemoryPack.Core/MemoryPackFormatterProvider.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,11 @@ static Cache()
301301
if (formatterType != null) goto CREATE;
302302
#endif
303303

304+
#if NET8_0_OR_GREATER
305+
formatterType = TryCreateGenericFormatterType(type, FrozenCollectionFormatters);
306+
if (formatterType != null) goto CREATE;
307+
#endif
308+
304309
formatterType = TryCreateGenericFormatterType(type, InterfaceCollectionFormatters);
305310
if (formatterType != null) goto CREATE;
306311

@@ -371,7 +376,7 @@ public void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer,
371376
where TBufferWriter : IBufferWriter<byte>
372377
#else
373378
where TBufferWriter : class, IBufferWriter<byte>
374-
#endif
379+
#endif
375380
{
376381
Throw();
377382
}

src/MemoryPack/MemoryPack.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net7.0;netstandard2.1</TargetFrameworks>
4+
<TargetFrameworks>net7.0;net8.0;netstandard2.1</TargetFrameworks>
55
<!-- This project is meta package -->
66
<IncludeBuildOutput>false</IncludeBuildOutput>
77
<IncludeContentInPack>true</IncludeContentInPack>
@@ -16,4 +16,4 @@
1616
<ProjectReference Include="..\MemoryPack.Generator\MemoryPack.Generator.csproj" />
1717
</ItemGroup>
1818

19-
</Project>
19+
</Project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#if NET8_0_OR_GREATER
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Collections.Frozen;
5+
6+
namespace MemoryPack.Tests;
7+
8+
public class FrozenCollectionFormatterTest
9+
{
10+
[Fact]
11+
public void FrozenSet()
12+
{
13+
var set = new HashSet<int>();
14+
set.Add(1);
15+
set.Add(2);
16+
set.Add(3);
17+
set.Add(4);
18+
set.Add(5);
19+
20+
var value = set.ToFrozenSet();
21+
var bin = MemoryPackSerializer.Serialize(value);
22+
var deserializedValue = MemoryPackSerializer.Deserialize<FrozenSet<int>>(bin);
23+
deserializedValue.Should().Equal(value);
24+
}
25+
26+
[Fact]
27+
public void FrozenDictionary()
28+
{
29+
var dict = new Dictionary<int, int>()
30+
{
31+
{ 1, 2 }, { 3, 4 }, { 4, 5 }, { 6, 7 }, { 8, 9 }
32+
};
33+
var value = dict.ToFrozenDictionary();
34+
var bin = MemoryPackSerializer.Serialize(value);
35+
MemoryPackSerializer.Deserialize<FrozenDictionary<int, int>>(bin).Should().BeEquivalentTo(value);
36+
}
37+
}
38+
#endif

0 commit comments

Comments
 (0)