// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; #pragma warning disable SA1649 // File name should match first type name namespace MessagePack.Formatters { // unfortunately, can't use IDictionary because supports IReadOnlyDictionary. public abstract class DictionaryFormatterBase : IMessagePackFormatter where TDictionary : IEnumerable> where TEnumerator : IEnumerator> { public void Serialize(ref MessagePackWriter writer, TDictionary value, MessagePackSerializerOptions options) { if (value == null) { writer.WriteNil(); } else { IFormatterResolver resolver = options.Resolver; IMessagePackFormatter keyFormatter = resolver.GetFormatterWithVerify(); IMessagePackFormatter valueFormatter = resolver.GetFormatterWithVerify(); int count; { var col = value as ICollection>; if (col != null) { count = col.Count; } else { var col2 = value as IReadOnlyCollection>; if (col2 != null) { count = col2.Count; } else { throw new MessagePackSerializationException("DictionaryFormatterBase's TDictionary supports only ICollection or IReadOnlyCollection"); } } } writer.WriteMapHeader(count); TEnumerator e = this.GetSourceEnumerator(value); try { while (e.MoveNext()) { writer.CancellationToken.ThrowIfCancellationRequested(); KeyValuePair item = e.Current; keyFormatter.Serialize(ref writer, item.Key, options); valueFormatter.Serialize(ref writer, item.Value, options); } } finally { e.Dispose(); } } } public TDictionary Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { if (reader.TryReadNil()) { return default(TDictionary); } else { IFormatterResolver resolver = options.Resolver; IMessagePackFormatter keyFormatter = resolver.GetFormatterWithVerify(); IMessagePackFormatter valueFormatter = resolver.GetFormatterWithVerify(); var len = reader.ReadMapHeader(); TIntermediate dict = this.Create(len, options); options.Security.DepthStep(ref reader); try { for (int i = 0; i < len; i++) { reader.CancellationToken.ThrowIfCancellationRequested(); TKey key = keyFormatter.Deserialize(ref reader, options); TValue value = valueFormatter.Deserialize(ref reader, options); this.Add(dict, i, key, value, options); } } finally { reader.Depth--; } return this.Complete(dict); } } // abstraction for serialize // Some collections can use struct iterator, this is optimization path protected abstract TEnumerator GetSourceEnumerator(TDictionary source); // abstraction for deserialize protected abstract TIntermediate Create(int count, MessagePackSerializerOptions options); protected abstract void Add(TIntermediate collection, int index, TKey key, TValue value, MessagePackSerializerOptions options); protected abstract TDictionary Complete(TIntermediate intermediateCollection); } public abstract class DictionaryFormatterBase : DictionaryFormatterBase>, TDictionary> where TDictionary : IEnumerable> { protected override IEnumerator> GetSourceEnumerator(TDictionary source) { return source.GetEnumerator(); } } public abstract class DictionaryFormatterBase : DictionaryFormatterBase where TDictionary : IDictionary { protected override TDictionary Complete(TDictionary intermediateCollection) { return intermediateCollection; } } public sealed class DictionaryFormatter : DictionaryFormatterBase, Dictionary.Enumerator, Dictionary> { protected override void Add(Dictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override Dictionary Complete(Dictionary intermediateCollection) { return intermediateCollection; } protected override Dictionary Create(int count, MessagePackSerializerOptions options) { return new Dictionary(count, options.Security.GetEqualityComparer()); } protected override Dictionary.Enumerator GetSourceEnumerator(Dictionary source) { return source.GetEnumerator(); } } public sealed class GenericDictionaryFormatter : DictionaryFormatterBase where TDictionary : IDictionary, new() { protected override void Add(TDictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override TDictionary Create(int count, MessagePackSerializerOptions options) { return CollectionHelpers>.CreateHashCollection(count, options.Security.GetEqualityComparer()); } } public sealed class GenericReadOnlyDictionaryFormatter : DictionaryFormatterBase, TDictionary> where TDictionary : IReadOnlyDictionary { protected override void Add(Dictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override Dictionary Create(int count, MessagePackSerializerOptions options) { return new Dictionary(count, options.Security.GetEqualityComparer()); } protected override TDictionary Complete(Dictionary intermediateCollection) { return (TDictionary)Activator.CreateInstance(typeof(TDictionary), intermediateCollection); } } public sealed class InterfaceDictionaryFormatter : DictionaryFormatterBase, IDictionary> { protected override void Add(Dictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override Dictionary Create(int count, MessagePackSerializerOptions options) { return new Dictionary(count, options.Security.GetEqualityComparer()); } protected override IDictionary Complete(Dictionary intermediateCollection) { return intermediateCollection; } } public sealed class SortedListFormatter : DictionaryFormatterBase> { protected override void Add(SortedList collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override SortedList Create(int count, MessagePackSerializerOptions options) { return new SortedList(count); } } public sealed class SortedDictionaryFormatter : DictionaryFormatterBase, SortedDictionary.Enumerator, SortedDictionary> { protected override void Add(SortedDictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override SortedDictionary Complete(SortedDictionary intermediateCollection) { return intermediateCollection; } protected override SortedDictionary Create(int count, MessagePackSerializerOptions options) { return new SortedDictionary(); } protected override SortedDictionary.Enumerator GetSourceEnumerator(SortedDictionary source) { return source.GetEnumerator(); } } public sealed class ReadOnlyDictionaryFormatter : DictionaryFormatterBase, ReadOnlyDictionary> { protected override void Add(Dictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override ReadOnlyDictionary Complete(Dictionary intermediateCollection) { return new ReadOnlyDictionary(intermediateCollection); } protected override Dictionary Create(int count, MessagePackSerializerOptions options) { return new Dictionary(count, options.Security.GetEqualityComparer()); } } public sealed class InterfaceReadOnlyDictionaryFormatter : DictionaryFormatterBase, IReadOnlyDictionary> { protected override void Add(Dictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.Add(key, value); } protected override IReadOnlyDictionary Complete(Dictionary intermediateCollection) { return intermediateCollection; } protected override Dictionary Create(int count, MessagePackSerializerOptions options) { return new Dictionary(count, options.Security.GetEqualityComparer()); } } public sealed class ConcurrentDictionaryFormatter : DictionaryFormatterBase> { protected override void Add(ConcurrentDictionary collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) { collection.TryAdd(key, value); } protected override ConcurrentDictionary Create(int count, MessagePackSerializerOptions options) { // concurrent dictionary can't access defaultConcurrecyLevel so does not use count overload. return new ConcurrentDictionary(options.Security.GetEqualityComparer()); } } }