Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
171 changes: 171 additions & 0 deletions src/MessagePack/Formatters/FrozenCollectionFormatters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if NET8_0_OR_GREATER

using System.Collections.Frozen;
using System.Collections.Generic;
using MessagePack.Formatters;

#pragma warning disable SA1402 // File may only contain a single type
#pragma warning disable SA1649 // File name should match first type name

namespace MessagePack.ImmutableCollection
{
public sealed class FrozenDictionaryFormatter<TKey, TValue> : IMessagePackFormatter<FrozenDictionary<TKey, TValue>?>
where TKey : notnull
{
private readonly IEqualityComparer<TKey>? comparer;

public FrozenDictionaryFormatter()
{
}

public FrozenDictionaryFormatter(IEqualityComparer<TKey>? comparer)
{
this.comparer = comparer;
}

public void Serialize(ref MessagePackWriter writer, FrozenDictionary<TKey, TValue>? value, MessagePackSerializerOptions options)
{
if (value is null)
{
writer.WriteNil();
return;
}

IFormatterResolver resolver = options.Resolver;
IMessagePackFormatter<TKey> keyFormatter = resolver.GetFormatterWithVerify<TKey>();
IMessagePackFormatter<TValue> valueFormatter = resolver.GetFormatterWithVerify<TValue>();

// https://github.com/dotnet/runtime/blob/4c500699b938d53993b928b93543b8dbe68f69aa/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.cs#L134C2-L134C2
// FrozenDictionary.Count uses FrozenHashTable's Count property which is O(1).
var count = value.Count;
writer.WriteMapHeader(count);
if (count == 0)
{
return;
}

foreach (KeyValuePair<TKey, TValue> item in value)
{
writer.CancellationToken.ThrowIfCancellationRequested();
keyFormatter.Serialize(ref writer, item.Key, options);
valueFormatter.Serialize(ref writer, item.Value, options);
}
}

public FrozenDictionary<TKey, TValue>? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return default;
}

var count = reader.ReadMapHeader();
if (count == 0)
{
return FrozenDictionary<TKey, TValue>.Empty;
}

IFormatterResolver resolver = options.Resolver;
IMessagePackFormatter<TKey> keyFormatter = resolver.GetFormatterWithVerify<TKey>();
IMessagePackFormatter<TValue> valueFormatter = resolver.GetFormatterWithVerify<TValue>();
IEqualityComparer<TKey> comparer = this.comparer ?? options.Security.GetEqualityComparer<TKey>();

// https://github.com/dotnet/runtime/blob/4c500699b938d53993b928b93543b8dbe68f69aa/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs#L87
// FrozenDictionary.ToFrozenDictionary internally allocates Dictionary<TKey, TValue> object.
var dictionary = new Dictionary<TKey, TValue>(count, comparer);
options.Security.DepthStep(ref reader);
try
{
for (var i = 0; i < count; i++)
{
reader.CancellationToken.ThrowIfCancellationRequested();
dictionary.Add(keyFormatter.Deserialize(ref reader, options), valueFormatter.Deserialize(ref reader, options));
}
}
finally
{
reader.Depth--;
}

return dictionary.ToFrozenDictionary(comparer);
}
}

public sealed class FrozenSetFormatter<T> : IMessagePackFormatter<FrozenSet<T>?>
{
private readonly IEqualityComparer<T>? comparer;

public FrozenSetFormatter()
{
}

public FrozenSetFormatter(IEqualityComparer<T> comparer)
{
this.comparer = comparer;
}

public void Serialize(ref MessagePackWriter writer, FrozenSet<T>? value, MessagePackSerializerOptions options)
{
if (value is null)
{
writer.WriteNil();
return;
}

var count = value.Count;
writer.WriteArrayHeader(count);
if (count == 0)
{
return;
}

IMessagePackFormatter<T> formatter = options.Resolver.GetFormatterWithVerify<T>();
foreach (var item in value)
{
writer.CancellationToken.ThrowIfCancellationRequested();
formatter.Serialize(ref writer, item, options);
}
}

public FrozenSet<T>? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return default;
}

var count = reader.ReadArrayHeader();
if (count == 0)
{
return FrozenSet<T>.Empty;
}

IMessagePackFormatter<T> formatter = options.Resolver.GetFormatterWithVerify<T>();
IEqualityComparer<T> comparer = this.comparer ?? options.Security.GetEqualityComparer<T>();

// https://github.com/dotnet/runtime/blob/4c500699b938d53993b928b93543b8dbe68f69aa/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs#L41
// FrozenSet.ToFrozenSet internally allocates HashSet<T> object.
var set = new HashSet<T>(count, comparer);
options.Security.DepthStep(ref reader);
try
{
for (var i = 0; i < count; i++)
{
reader.CancellationToken.ThrowIfCancellationRequested();
set.Add(formatter.Deserialize(ref reader, options));
}
}
finally
{
reader.Depth--;
}

return set.ToFrozenSet(comparer);
}
}
}

#endif
7 changes: 7 additions & 0 deletions src/MessagePack/Resolvers/ImmutableCollectionResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
#if NET8_0_OR_GREATER
using System.Collections.Frozen;
#endif
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
Expand Down Expand Up @@ -52,6 +55,10 @@ internal static class ImmutableCollectionGetFormatterHelper
{ typeof(IImmutableQueue<>), typeof(InterfaceImmutableQueueFormatter<>) },
{ typeof(IImmutableSet<>), typeof(InterfaceImmutableSetFormatter<>) },
{ typeof(IImmutableStack<>), typeof(InterfaceImmutableStackFormatter<>) },
#if NET8_0_OR_GREATER
{ typeof(FrozenDictionary<,>), typeof(FrozenDictionaryFormatter<,>) },
{ typeof(FrozenSet<>), typeof(FrozenSetFormatter<>) },
#endif
};

internal static object? GetFormatter(Type t)
Expand Down
10 changes: 10 additions & 0 deletions src/MessagePack/net8.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ MessagePack.Formatters.Vector3Formatter.Serialize(ref MessagePack.MessagePackWri
MessagePack.Formatters.Vector4Formatter
MessagePack.Formatters.Vector4Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Vector4
MessagePack.Formatters.Vector4Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Vector4 value, MessagePack.MessagePackSerializerOptions! options) -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter<TKey, TValue>
MessagePack.ImmutableCollection.FrozenDictionaryFormatter<TKey, TValue>.FrozenDictionaryFormatter() -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter<TKey, TValue>.FrozenDictionaryFormatter(System.Collections.Generic.IEqualityComparer<TKey>? comparer) -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter<TKey, TValue>.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Frozen.FrozenDictionary<TKey, TValue>? value, MessagePack.MessagePackSerializerOptions! options) -> void
MessagePack.ImmutableCollection.FrozenDictionaryFormatter<TKey, TValue>.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Frozen.FrozenDictionary<TKey, TValue>?
MessagePack.ImmutableCollection.FrozenSetFormatter<T>
MessagePack.ImmutableCollection.FrozenSetFormatter<T>.FrozenSetFormatter() -> void
MessagePack.ImmutableCollection.FrozenSetFormatter<T>.FrozenSetFormatter(System.Collections.Generic.IEqualityComparer<T>! comparer) -> void
MessagePack.ImmutableCollection.FrozenSetFormatter<T>.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Frozen.FrozenSet<T>? value, MessagePack.MessagePackSerializerOptions! options) -> void
MessagePack.ImmutableCollection.FrozenSetFormatter<T>.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Frozen.FrozenSet<T>?
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions!
Expand Down
116 changes: 116 additions & 0 deletions tests/MessagePack.Tests/ExtensionTests/FrozenCollectionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if NET8_0_OR_GREATER
#nullable enable

using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Xunit;

namespace MessagePack.Tests.ExtensionTests
{
public class FrozenCollectionTest
{
private T Convert<T>(T value)
{
MessagePackSerializerOptions options = MessagePackSerializerOptions.Standard;
return MessagePackSerializer.Deserialize<T>(MessagePackSerializer.Serialize(value, options), options);
}

[Fact]
public void EmptySet()
{
{
var empty = FrozenSet<int>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenSet<string>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenSet<ulong>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenSet<DateTime>.Empty;
Convert(empty).IsStructuralEqual(empty);
}
}

[Fact]
public void EmptyDictionary()
{
{
var empty = FrozenDictionary<string, int>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<string, string?>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<string, ulong>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<string, DateTime>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<int, int>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<int, string?>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<int, ulong>.Empty;
Convert(empty).IsStructuralEqual(empty);
}

{
var empty = FrozenDictionary<int, DateTime>.Empty;
Convert(empty).IsStructuralEqual(empty);
}
}

[Fact]
public void IntSet()
{
for (var i = 1; i < 11; i++)
{
var array = new int[1 << i];
Random.Shared.NextBytes(MemoryMarshal.AsBytes<int>(array));
var set = array.ToFrozenSet();
Convert(set).IsStructuralEqualIgnoreCollectionOrder(set);
}
}

[Fact]
public void IntDictionary()
{
for (var i = 1; i < 11; i++)
{
var array = new KeyValuePair<int, int>[1 << i];
Random.Shared.NextBytes(MemoryMarshal.AsBytes<KeyValuePair<int, int>>(array));
var dictionary = array.ToFrozenDictionary();
Convert(dictionary).IsStructuralEqualIgnoreCollectionOrder(dictionary);
}
}
}
}
#endif