1ffe3c632Sopenharmony_ci#region Copyright notice and license
2ffe3c632Sopenharmony_ci// Protocol Buffers - Google's data interchange format
3ffe3c632Sopenharmony_ci// Copyright 2015 Google Inc.  All rights reserved.
4ffe3c632Sopenharmony_ci// https://developers.google.com/protocol-buffers/
5ffe3c632Sopenharmony_ci//
6ffe3c632Sopenharmony_ci// Redistribution and use in source and binary forms, with or without
7ffe3c632Sopenharmony_ci// modification, are permitted provided that the following conditions are
8ffe3c632Sopenharmony_ci// met:
9ffe3c632Sopenharmony_ci//
10ffe3c632Sopenharmony_ci//     * Redistributions of source code must retain the above copyright
11ffe3c632Sopenharmony_ci// notice, this list of conditions and the following disclaimer.
12ffe3c632Sopenharmony_ci//     * Redistributions in binary form must reproduce the above
13ffe3c632Sopenharmony_ci// copyright notice, this list of conditions and the following disclaimer
14ffe3c632Sopenharmony_ci// in the documentation and/or other materials provided with the
15ffe3c632Sopenharmony_ci// distribution.
16ffe3c632Sopenharmony_ci//     * Neither the name of Google Inc. nor the names of its
17ffe3c632Sopenharmony_ci// contributors may be used to endorse or promote products derived from
18ffe3c632Sopenharmony_ci// this software without specific prior written permission.
19ffe3c632Sopenharmony_ci//
20ffe3c632Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21ffe3c632Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22ffe3c632Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23ffe3c632Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24ffe3c632Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25ffe3c632Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26ffe3c632Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27ffe3c632Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28ffe3c632Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29ffe3c632Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30ffe3c632Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31ffe3c632Sopenharmony_ci#endregion
32ffe3c632Sopenharmony_ci
33ffe3c632Sopenharmony_ciusing Google.Protobuf.Compatibility;
34ffe3c632Sopenharmony_ciusing Google.Protobuf.Reflection;
35ffe3c632Sopenharmony_ciusing System;
36ffe3c632Sopenharmony_ciusing System.Buffers;
37ffe3c632Sopenharmony_ciusing System.Collections;
38ffe3c632Sopenharmony_ciusing System.Collections.Generic;
39ffe3c632Sopenharmony_ciusing System.IO;
40ffe3c632Sopenharmony_ciusing System.Linq;
41ffe3c632Sopenharmony_ciusing System.Security;
42ffe3c632Sopenharmony_ci
43ffe3c632Sopenharmony_cinamespace Google.Protobuf.Collections
44ffe3c632Sopenharmony_ci{
45ffe3c632Sopenharmony_ci    /// <summary>
46ffe3c632Sopenharmony_ci    /// Representation of a map field in a Protocol Buffer message.
47ffe3c632Sopenharmony_ci    /// </summary>
48ffe3c632Sopenharmony_ci    /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
49ffe3c632Sopenharmony_ci    /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
50ffe3c632Sopenharmony_ci    /// <remarks>
51ffe3c632Sopenharmony_ci    /// <para>
52ffe3c632Sopenharmony_ci    /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.
53ffe3c632Sopenharmony_ci    /// </para>
54ffe3c632Sopenharmony_ci    /// <para>
55ffe3c632Sopenharmony_ci    /// Null values are not permitted in the map, either for wrapper types or regular messages.
56ffe3c632Sopenharmony_ci    /// If a map is deserialized from a data stream and the value is missing from an entry, a default value
57ffe3c632Sopenharmony_ci    /// is created instead. For primitive types, that is the regular default value (0, the empty string and so
58ffe3c632Sopenharmony_ci    /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
59ffe3c632Sopenharmony_ci    /// encoded value for the field.
60ffe3c632Sopenharmony_ci    /// </para>
61ffe3c632Sopenharmony_ci    /// <para>
62ffe3c632Sopenharmony_ci    /// This implementation does not generally prohibit the use of key/value types which are not
63ffe3c632Sopenharmony_ci    /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee
64ffe3c632Sopenharmony_ci    /// that all operations will work in such cases.
65ffe3c632Sopenharmony_ci    /// </para>
66ffe3c632Sopenharmony_ci    /// <para>
67ffe3c632Sopenharmony_ci    /// The order in which entries are returned when iterating over this object is undefined, and may change
68ffe3c632Sopenharmony_ci    /// in future versions.
69ffe3c632Sopenharmony_ci    /// </para>
70ffe3c632Sopenharmony_ci    /// </remarks>
71ffe3c632Sopenharmony_ci    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
72ffe3c632Sopenharmony_ci#if !NET35
73ffe3c632Sopenharmony_ci        , IReadOnlyDictionary<TKey, TValue>
74ffe3c632Sopenharmony_ci#endif
75ffe3c632Sopenharmony_ci    {
76ffe3c632Sopenharmony_ci        private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();
77ffe3c632Sopenharmony_ci        private static readonly EqualityComparer<TKey> KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TKey>();
78ffe3c632Sopenharmony_ci
79ffe3c632Sopenharmony_ci        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
80ffe3c632Sopenharmony_ci        private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
81ffe3c632Sopenharmony_ci            new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(KeyEqualityComparer);
82ffe3c632Sopenharmony_ci        private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
83ffe3c632Sopenharmony_ci
84ffe3c632Sopenharmony_ci        /// <summary>
85ffe3c632Sopenharmony_ci        /// Creates a deep clone of this object.
86ffe3c632Sopenharmony_ci        /// </summary>
87ffe3c632Sopenharmony_ci        /// <returns>
88ffe3c632Sopenharmony_ci        /// A deep clone of this object.
89ffe3c632Sopenharmony_ci        /// </returns>
90ffe3c632Sopenharmony_ci        public MapField<TKey, TValue> Clone()
91ffe3c632Sopenharmony_ci        {
92ffe3c632Sopenharmony_ci            var clone = new MapField<TKey, TValue>();
93ffe3c632Sopenharmony_ci            // Keys are never cloneable. Values might be.
94ffe3c632Sopenharmony_ci            if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
95ffe3c632Sopenharmony_ci            {
96ffe3c632Sopenharmony_ci                foreach (var pair in list)
97ffe3c632Sopenharmony_ci                {
98ffe3c632Sopenharmony_ci                    clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());
99ffe3c632Sopenharmony_ci                }
100ffe3c632Sopenharmony_ci            }
101ffe3c632Sopenharmony_ci            else
102ffe3c632Sopenharmony_ci            {
103ffe3c632Sopenharmony_ci                // Nothing is cloneable, so we don't need to worry.
104ffe3c632Sopenharmony_ci                clone.Add(this);
105ffe3c632Sopenharmony_ci            }
106ffe3c632Sopenharmony_ci            return clone;
107ffe3c632Sopenharmony_ci        }
108ffe3c632Sopenharmony_ci
109ffe3c632Sopenharmony_ci        /// <summary>
110ffe3c632Sopenharmony_ci        /// Adds the specified key/value pair to the map.
111ffe3c632Sopenharmony_ci        /// </summary>
112ffe3c632Sopenharmony_ci        /// <remarks>
113ffe3c632Sopenharmony_ci        /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.
114ffe3c632Sopenharmony_ci        /// </remarks>
115ffe3c632Sopenharmony_ci        /// <param name="key">The key to add</param>
116ffe3c632Sopenharmony_ci        /// <param name="value">The value to add.</param>
117ffe3c632Sopenharmony_ci        /// <exception cref="System.ArgumentException">The given key already exists in map.</exception>
118ffe3c632Sopenharmony_ci        public void Add(TKey key, TValue value)
119ffe3c632Sopenharmony_ci        {
120ffe3c632Sopenharmony_ci            // Validation of arguments happens in ContainsKey and the indexer
121ffe3c632Sopenharmony_ci            if (ContainsKey(key))
122ffe3c632Sopenharmony_ci            {
123ffe3c632Sopenharmony_ci                throw new ArgumentException("Key already exists in map", nameof(key));
124ffe3c632Sopenharmony_ci            }
125ffe3c632Sopenharmony_ci            this[key] = value;
126ffe3c632Sopenharmony_ci        }
127ffe3c632Sopenharmony_ci
128ffe3c632Sopenharmony_ci        /// <summary>
129ffe3c632Sopenharmony_ci        /// Determines whether the specified key is present in the map.
130ffe3c632Sopenharmony_ci        /// </summary>
131ffe3c632Sopenharmony_ci        /// <param name="key">The key to check.</param>
132ffe3c632Sopenharmony_ci        /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>
133ffe3c632Sopenharmony_ci        public bool ContainsKey(TKey key)
134ffe3c632Sopenharmony_ci        {
135ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
136ffe3c632Sopenharmony_ci            return map.ContainsKey(key);
137ffe3c632Sopenharmony_ci        }
138ffe3c632Sopenharmony_ci
139ffe3c632Sopenharmony_ci        private bool ContainsValue(TValue value) =>
140ffe3c632Sopenharmony_ci            list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));
141ffe3c632Sopenharmony_ci
142ffe3c632Sopenharmony_ci        /// <summary>
143ffe3c632Sopenharmony_ci        /// Removes the entry identified by the given key from the map.
144ffe3c632Sopenharmony_ci        /// </summary>
145ffe3c632Sopenharmony_ci        /// <param name="key">The key indicating the entry to remove from the map.</param>
146ffe3c632Sopenharmony_ci        /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>
147ffe3c632Sopenharmony_ci        public bool Remove(TKey key)
148ffe3c632Sopenharmony_ci        {
149ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
150ffe3c632Sopenharmony_ci            LinkedListNode<KeyValuePair<TKey, TValue>> node;
151ffe3c632Sopenharmony_ci            if (map.TryGetValue(key, out node))
152ffe3c632Sopenharmony_ci            {
153ffe3c632Sopenharmony_ci                map.Remove(key);
154ffe3c632Sopenharmony_ci                node.List.Remove(node);
155ffe3c632Sopenharmony_ci                return true;
156ffe3c632Sopenharmony_ci            }
157ffe3c632Sopenharmony_ci            else
158ffe3c632Sopenharmony_ci            {
159ffe3c632Sopenharmony_ci                return false;
160ffe3c632Sopenharmony_ci            }
161ffe3c632Sopenharmony_ci        }
162ffe3c632Sopenharmony_ci
163ffe3c632Sopenharmony_ci        /// <summary>
164ffe3c632Sopenharmony_ci        /// Gets the value associated with the specified key.
165ffe3c632Sopenharmony_ci        /// </summary>
166ffe3c632Sopenharmony_ci        /// <param name="key">The key whose value to get.</param>
167ffe3c632Sopenharmony_ci        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found;
168ffe3c632Sopenharmony_ci        /// otherwise, the default value for the type of the <paramref name="value"/> parameter.
169ffe3c632Sopenharmony_ci        /// This parameter is passed uninitialized.</param>
170ffe3c632Sopenharmony_ci        /// <returns><c>true</c> if the map contains an element with the specified key; otherwise, <c>false</c>.</returns>
171ffe3c632Sopenharmony_ci        public bool TryGetValue(TKey key, out TValue value)
172ffe3c632Sopenharmony_ci        {
173ffe3c632Sopenharmony_ci            LinkedListNode<KeyValuePair<TKey, TValue>> node;
174ffe3c632Sopenharmony_ci            if (map.TryGetValue(key, out node))
175ffe3c632Sopenharmony_ci            {
176ffe3c632Sopenharmony_ci                value = node.Value.Value;
177ffe3c632Sopenharmony_ci                return true;
178ffe3c632Sopenharmony_ci            }
179ffe3c632Sopenharmony_ci            else
180ffe3c632Sopenharmony_ci            {
181ffe3c632Sopenharmony_ci                value = default(TValue);
182ffe3c632Sopenharmony_ci                return false;
183ffe3c632Sopenharmony_ci            }
184ffe3c632Sopenharmony_ci        }
185ffe3c632Sopenharmony_ci
186ffe3c632Sopenharmony_ci        /// <summary>
187ffe3c632Sopenharmony_ci        /// Gets or sets the value associated with the specified key.
188ffe3c632Sopenharmony_ci        /// </summary>
189ffe3c632Sopenharmony_ci        /// <param name="key">The key of the value to get or set.</param>
190ffe3c632Sopenharmony_ci        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>
191ffe3c632Sopenharmony_ci        /// <returns>The value associated with the specified key. If the specified key is not found,
192ffe3c632Sopenharmony_ci        /// a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>
193ffe3c632Sopenharmony_ci        public TValue this[TKey key]
194ffe3c632Sopenharmony_ci        {
195ffe3c632Sopenharmony_ci            get
196ffe3c632Sopenharmony_ci            {
197ffe3c632Sopenharmony_ci                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
198ffe3c632Sopenharmony_ci                TValue value;
199ffe3c632Sopenharmony_ci                if (TryGetValue(key, out value))
200ffe3c632Sopenharmony_ci                {
201ffe3c632Sopenharmony_ci                    return value;
202ffe3c632Sopenharmony_ci                }
203ffe3c632Sopenharmony_ci                throw new KeyNotFoundException();
204ffe3c632Sopenharmony_ci            }
205ffe3c632Sopenharmony_ci            set
206ffe3c632Sopenharmony_ci            {
207ffe3c632Sopenharmony_ci                ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
208ffe3c632Sopenharmony_ci                // value == null check here is redundant, but avoids boxing.
209ffe3c632Sopenharmony_ci                if (value == null)
210ffe3c632Sopenharmony_ci                {
211ffe3c632Sopenharmony_ci                    ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
212ffe3c632Sopenharmony_ci                }
213ffe3c632Sopenharmony_ci                LinkedListNode<KeyValuePair<TKey, TValue>> node;
214ffe3c632Sopenharmony_ci                var pair = new KeyValuePair<TKey, TValue>(key, value);
215ffe3c632Sopenharmony_ci                if (map.TryGetValue(key, out node))
216ffe3c632Sopenharmony_ci                {
217ffe3c632Sopenharmony_ci                    node.Value = pair;
218ffe3c632Sopenharmony_ci                }
219ffe3c632Sopenharmony_ci                else
220ffe3c632Sopenharmony_ci                {
221ffe3c632Sopenharmony_ci                    node = list.AddLast(pair);
222ffe3c632Sopenharmony_ci                    map[key] = node;
223ffe3c632Sopenharmony_ci                }
224ffe3c632Sopenharmony_ci            }
225ffe3c632Sopenharmony_ci        }
226ffe3c632Sopenharmony_ci
227ffe3c632Sopenharmony_ci        /// <summary>
228ffe3c632Sopenharmony_ci        /// Gets a collection containing the keys in the map.
229ffe3c632Sopenharmony_ci        /// </summary>
230ffe3c632Sopenharmony_ci        public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pair => pair.Key, ContainsKey); } }
231ffe3c632Sopenharmony_ci
232ffe3c632Sopenharmony_ci        /// <summary>
233ffe3c632Sopenharmony_ci        /// Gets a collection containing the values in the map.
234ffe3c632Sopenharmony_ci        /// </summary>
235ffe3c632Sopenharmony_ci        public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
236ffe3c632Sopenharmony_ci
237ffe3c632Sopenharmony_ci        /// <summary>
238ffe3c632Sopenharmony_ci        /// Adds the specified entries to the map. The keys and values are not automatically cloned.
239ffe3c632Sopenharmony_ci        /// </summary>
240ffe3c632Sopenharmony_ci        /// <param name="entries">The entries to add to the map.</param>
241ffe3c632Sopenharmony_ci        public void Add(IDictionary<TKey, TValue> entries)
242ffe3c632Sopenharmony_ci        {
243ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNull(entries, nameof(entries));
244ffe3c632Sopenharmony_ci            foreach (var pair in entries)
245ffe3c632Sopenharmony_ci            {
246ffe3c632Sopenharmony_ci                Add(pair.Key, pair.Value);
247ffe3c632Sopenharmony_ci            }
248ffe3c632Sopenharmony_ci        }
249ffe3c632Sopenharmony_ci
250ffe3c632Sopenharmony_ci        /// <summary>
251ffe3c632Sopenharmony_ci        /// Returns an enumerator that iterates through the collection.
252ffe3c632Sopenharmony_ci        /// </summary>
253ffe3c632Sopenharmony_ci        /// <returns>
254ffe3c632Sopenharmony_ci        /// An enumerator that can be used to iterate through the collection.
255ffe3c632Sopenharmony_ci        /// </returns>
256ffe3c632Sopenharmony_ci        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
257ffe3c632Sopenharmony_ci        {
258ffe3c632Sopenharmony_ci            return list.GetEnumerator();
259ffe3c632Sopenharmony_ci        }
260ffe3c632Sopenharmony_ci
261ffe3c632Sopenharmony_ci        /// <summary>
262ffe3c632Sopenharmony_ci        /// Returns an enumerator that iterates through a collection.
263ffe3c632Sopenharmony_ci        /// </summary>
264ffe3c632Sopenharmony_ci        /// <returns>
265ffe3c632Sopenharmony_ci        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
266ffe3c632Sopenharmony_ci        /// </returns>
267ffe3c632Sopenharmony_ci        IEnumerator IEnumerable.GetEnumerator()
268ffe3c632Sopenharmony_ci        {
269ffe3c632Sopenharmony_ci            return GetEnumerator();
270ffe3c632Sopenharmony_ci        }
271ffe3c632Sopenharmony_ci
272ffe3c632Sopenharmony_ci        /// <summary>
273ffe3c632Sopenharmony_ci        /// Adds the specified item to the map.
274ffe3c632Sopenharmony_ci        /// </summary>
275ffe3c632Sopenharmony_ci        /// <param name="item">The item to add to the map.</param>
276ffe3c632Sopenharmony_ci        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
277ffe3c632Sopenharmony_ci        {
278ffe3c632Sopenharmony_ci            Add(item.Key, item.Value);
279ffe3c632Sopenharmony_ci        }
280ffe3c632Sopenharmony_ci
281ffe3c632Sopenharmony_ci        /// <summary>
282ffe3c632Sopenharmony_ci        /// Removes all items from the map.
283ffe3c632Sopenharmony_ci        /// </summary>
284ffe3c632Sopenharmony_ci        public void Clear()
285ffe3c632Sopenharmony_ci        {
286ffe3c632Sopenharmony_ci            list.Clear();
287ffe3c632Sopenharmony_ci            map.Clear();
288ffe3c632Sopenharmony_ci        }
289ffe3c632Sopenharmony_ci
290ffe3c632Sopenharmony_ci        /// <summary>
291ffe3c632Sopenharmony_ci        /// Determines whether map contains an entry equivalent to the given key/value pair.
292ffe3c632Sopenharmony_ci        /// </summary>
293ffe3c632Sopenharmony_ci        /// <param name="item">The key/value pair to find.</param>
294ffe3c632Sopenharmony_ci        /// <returns></returns>
295ffe3c632Sopenharmony_ci        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
296ffe3c632Sopenharmony_ci        {
297ffe3c632Sopenharmony_ci            TValue value;
298ffe3c632Sopenharmony_ci            return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);
299ffe3c632Sopenharmony_ci        }
300ffe3c632Sopenharmony_ci
301ffe3c632Sopenharmony_ci        /// <summary>
302ffe3c632Sopenharmony_ci        /// Copies the key/value pairs in this map to an array.
303ffe3c632Sopenharmony_ci        /// </summary>
304ffe3c632Sopenharmony_ci        /// <param name="array">The array to copy the entries into.</param>
305ffe3c632Sopenharmony_ci        /// <param name="arrayIndex">The index of the array at which to start copying values.</param>
306ffe3c632Sopenharmony_ci        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
307ffe3c632Sopenharmony_ci        {
308ffe3c632Sopenharmony_ci            list.CopyTo(array, arrayIndex);
309ffe3c632Sopenharmony_ci        }
310ffe3c632Sopenharmony_ci
311ffe3c632Sopenharmony_ci        /// <summary>
312ffe3c632Sopenharmony_ci        /// Removes the specified key/value pair from the map.
313ffe3c632Sopenharmony_ci        /// </summary>
314ffe3c632Sopenharmony_ci        /// <remarks>Both the key and the value must be found for the entry to be removed.</remarks>
315ffe3c632Sopenharmony_ci        /// <param name="item">The key/value pair to remove.</param>
316ffe3c632Sopenharmony_ci        /// <returns><c>true</c> if the key/value pair was found and removed; <c>false</c> otherwise.</returns>
317ffe3c632Sopenharmony_ci        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
318ffe3c632Sopenharmony_ci        {
319ffe3c632Sopenharmony_ci            if (item.Key == null)
320ffe3c632Sopenharmony_ci            {
321ffe3c632Sopenharmony_ci                throw new ArgumentException("Key is null", nameof(item));
322ffe3c632Sopenharmony_ci            }
323ffe3c632Sopenharmony_ci            LinkedListNode<KeyValuePair<TKey, TValue>> node;
324ffe3c632Sopenharmony_ci            if (map.TryGetValue(item.Key, out node) &&
325ffe3c632Sopenharmony_ci                EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))
326ffe3c632Sopenharmony_ci            {
327ffe3c632Sopenharmony_ci                map.Remove(item.Key);
328ffe3c632Sopenharmony_ci                node.List.Remove(node);
329ffe3c632Sopenharmony_ci                return true;
330ffe3c632Sopenharmony_ci            }
331ffe3c632Sopenharmony_ci            else
332ffe3c632Sopenharmony_ci            {
333ffe3c632Sopenharmony_ci                return false;
334ffe3c632Sopenharmony_ci            }
335ffe3c632Sopenharmony_ci        }
336ffe3c632Sopenharmony_ci
337ffe3c632Sopenharmony_ci        /// <summary>
338ffe3c632Sopenharmony_ci        /// Gets the number of elements contained in the map.
339ffe3c632Sopenharmony_ci        /// </summary>
340ffe3c632Sopenharmony_ci        public int Count { get { return list.Count; } }
341ffe3c632Sopenharmony_ci
342ffe3c632Sopenharmony_ci        /// <summary>
343ffe3c632Sopenharmony_ci        /// Gets a value indicating whether the map is read-only.
344ffe3c632Sopenharmony_ci        /// </summary>
345ffe3c632Sopenharmony_ci        public bool IsReadOnly { get { return false; } }
346ffe3c632Sopenharmony_ci
347ffe3c632Sopenharmony_ci        /// <summary>
348ffe3c632Sopenharmony_ci        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
349ffe3c632Sopenharmony_ci        /// </summary>
350ffe3c632Sopenharmony_ci        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.</param>
351ffe3c632Sopenharmony_ci        /// <returns>
352ffe3c632Sopenharmony_ci        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
353ffe3c632Sopenharmony_ci        /// </returns>
354ffe3c632Sopenharmony_ci        public override bool Equals(object other)
355ffe3c632Sopenharmony_ci        {
356ffe3c632Sopenharmony_ci            return Equals(other as MapField<TKey, TValue>);
357ffe3c632Sopenharmony_ci        }
358ffe3c632Sopenharmony_ci
359ffe3c632Sopenharmony_ci        /// <summary>
360ffe3c632Sopenharmony_ci        /// Returns a hash code for this instance.
361ffe3c632Sopenharmony_ci        /// </summary>
362ffe3c632Sopenharmony_ci        /// <returns>
363ffe3c632Sopenharmony_ci        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
364ffe3c632Sopenharmony_ci        /// </returns>
365ffe3c632Sopenharmony_ci        public override int GetHashCode()
366ffe3c632Sopenharmony_ci        {
367ffe3c632Sopenharmony_ci            var keyComparer = KeyEqualityComparer;
368ffe3c632Sopenharmony_ci            var valueComparer = ValueEqualityComparer;
369ffe3c632Sopenharmony_ci            int hash = 0;
370ffe3c632Sopenharmony_ci            foreach (var pair in list)
371ffe3c632Sopenharmony_ci            {
372ffe3c632Sopenharmony_ci                hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);
373ffe3c632Sopenharmony_ci            }
374ffe3c632Sopenharmony_ci            return hash;
375ffe3c632Sopenharmony_ci        }
376ffe3c632Sopenharmony_ci
377ffe3c632Sopenharmony_ci        /// <summary>
378ffe3c632Sopenharmony_ci        /// Compares this map with another for equality.
379ffe3c632Sopenharmony_ci        /// </summary>
380ffe3c632Sopenharmony_ci        /// <remarks>
381ffe3c632Sopenharmony_ci        /// The order of the key/value pairs in the maps is not deemed significant in this comparison.
382ffe3c632Sopenharmony_ci        /// </remarks>
383ffe3c632Sopenharmony_ci        /// <param name="other">The map to compare this with.</param>
384ffe3c632Sopenharmony_ci        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal map; <c>false</c> otherwise.</returns>
385ffe3c632Sopenharmony_ci        public bool Equals(MapField<TKey, TValue> other)
386ffe3c632Sopenharmony_ci        {
387ffe3c632Sopenharmony_ci            if (other == null)
388ffe3c632Sopenharmony_ci            {
389ffe3c632Sopenharmony_ci                return false;
390ffe3c632Sopenharmony_ci            }
391ffe3c632Sopenharmony_ci            if (other == this)
392ffe3c632Sopenharmony_ci            {
393ffe3c632Sopenharmony_ci                return true;
394ffe3c632Sopenharmony_ci            }
395ffe3c632Sopenharmony_ci            if (other.Count != this.Count)
396ffe3c632Sopenharmony_ci            {
397ffe3c632Sopenharmony_ci                return false;
398ffe3c632Sopenharmony_ci            }
399ffe3c632Sopenharmony_ci            var valueComparer = ValueEqualityComparer;
400ffe3c632Sopenharmony_ci            foreach (var pair in this)
401ffe3c632Sopenharmony_ci            {
402ffe3c632Sopenharmony_ci                TValue value;
403ffe3c632Sopenharmony_ci                if (!other.TryGetValue(pair.Key, out value))
404ffe3c632Sopenharmony_ci                {
405ffe3c632Sopenharmony_ci                    return false;
406ffe3c632Sopenharmony_ci                }
407ffe3c632Sopenharmony_ci                if (!valueComparer.Equals(value, pair.Value))
408ffe3c632Sopenharmony_ci                {
409ffe3c632Sopenharmony_ci                    return false;
410ffe3c632Sopenharmony_ci                }
411ffe3c632Sopenharmony_ci            }
412ffe3c632Sopenharmony_ci            return true;
413ffe3c632Sopenharmony_ci        }
414ffe3c632Sopenharmony_ci
415ffe3c632Sopenharmony_ci        /// <summary>
416ffe3c632Sopenharmony_ci        /// Adds entries to the map from the given stream.
417ffe3c632Sopenharmony_ci        /// </summary>
418ffe3c632Sopenharmony_ci        /// <remarks>
419ffe3c632Sopenharmony_ci        /// It is assumed that the stream is initially positioned after the tag specified by the codec.
420ffe3c632Sopenharmony_ci        /// This method will continue reading entries from the stream until the end is reached, or
421ffe3c632Sopenharmony_ci        /// a different tag is encountered.
422ffe3c632Sopenharmony_ci        /// </remarks>
423ffe3c632Sopenharmony_ci        /// <param name="input">Stream to read from</param>
424ffe3c632Sopenharmony_ci        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
425ffe3c632Sopenharmony_ci        public void AddEntriesFrom(CodedInputStream input, Codec codec)
426ffe3c632Sopenharmony_ci        {
427ffe3c632Sopenharmony_ci            ParseContext.Initialize(input, out ParseContext ctx);
428ffe3c632Sopenharmony_ci            try
429ffe3c632Sopenharmony_ci            {
430ffe3c632Sopenharmony_ci                AddEntriesFrom(ref ctx, codec);
431ffe3c632Sopenharmony_ci            }
432ffe3c632Sopenharmony_ci            finally
433ffe3c632Sopenharmony_ci            {
434ffe3c632Sopenharmony_ci                ctx.CopyStateTo(input);
435ffe3c632Sopenharmony_ci            }
436ffe3c632Sopenharmony_ci        }
437ffe3c632Sopenharmony_ci
438ffe3c632Sopenharmony_ci        /// <summary>
439ffe3c632Sopenharmony_ci        /// Adds entries to the map from the given parse context.
440ffe3c632Sopenharmony_ci        /// </summary>
441ffe3c632Sopenharmony_ci        /// <remarks>
442ffe3c632Sopenharmony_ci        /// It is assumed that the input is initially positioned after the tag specified by the codec.
443ffe3c632Sopenharmony_ci        /// This method will continue reading entries from the input until the end is reached, or
444ffe3c632Sopenharmony_ci        /// a different tag is encountered.
445ffe3c632Sopenharmony_ci        /// </remarks>
446ffe3c632Sopenharmony_ci        /// <param name="ctx">Input to read from</param>
447ffe3c632Sopenharmony_ci        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
448ffe3c632Sopenharmony_ci        [SecuritySafeCritical]
449ffe3c632Sopenharmony_ci        public void AddEntriesFrom(ref ParseContext ctx, Codec codec)
450ffe3c632Sopenharmony_ci        {
451ffe3c632Sopenharmony_ci            var adapter = new Codec.MessageAdapter(codec);
452ffe3c632Sopenharmony_ci            do
453ffe3c632Sopenharmony_ci            {
454ffe3c632Sopenharmony_ci                adapter.Reset();
455ffe3c632Sopenharmony_ci                ctx.ReadMessage(adapter);
456ffe3c632Sopenharmony_ci                this[adapter.Key] = adapter.Value;
457ffe3c632Sopenharmony_ci            } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));
458ffe3c632Sopenharmony_ci        }
459ffe3c632Sopenharmony_ci
460ffe3c632Sopenharmony_ci        /// <summary>
461ffe3c632Sopenharmony_ci        /// Writes the contents of this map to the given coded output stream, using the specified codec
462ffe3c632Sopenharmony_ci        /// to encode each entry.
463ffe3c632Sopenharmony_ci        /// </summary>
464ffe3c632Sopenharmony_ci        /// <param name="output">The output stream to write to.</param>
465ffe3c632Sopenharmony_ci        /// <param name="codec">The codec to use for each entry.</param>
466ffe3c632Sopenharmony_ci        public void WriteTo(CodedOutputStream output, Codec codec)
467ffe3c632Sopenharmony_ci        {
468ffe3c632Sopenharmony_ci            WriteContext.Initialize(output, out WriteContext ctx);
469ffe3c632Sopenharmony_ci            try
470ffe3c632Sopenharmony_ci            {
471ffe3c632Sopenharmony_ci                WriteTo(ref ctx, codec);
472ffe3c632Sopenharmony_ci            }
473ffe3c632Sopenharmony_ci            finally
474ffe3c632Sopenharmony_ci            {
475ffe3c632Sopenharmony_ci                ctx.CopyStateTo(output);
476ffe3c632Sopenharmony_ci            }
477ffe3c632Sopenharmony_ci        }
478ffe3c632Sopenharmony_ci
479ffe3c632Sopenharmony_ci        /// <summary>
480ffe3c632Sopenharmony_ci        /// Writes the contents of this map to the given write context, using the specified codec
481ffe3c632Sopenharmony_ci        /// to encode each entry.
482ffe3c632Sopenharmony_ci        /// </summary>
483ffe3c632Sopenharmony_ci        /// <param name="ctx">The write context to write to.</param>
484ffe3c632Sopenharmony_ci        /// <param name="codec">The codec to use for each entry.</param>
485ffe3c632Sopenharmony_ci        [SecuritySafeCritical]
486ffe3c632Sopenharmony_ci        public void WriteTo(ref WriteContext ctx, Codec codec)
487ffe3c632Sopenharmony_ci        {
488ffe3c632Sopenharmony_ci            var message = new Codec.MessageAdapter(codec);
489ffe3c632Sopenharmony_ci            foreach (var entry in list)
490ffe3c632Sopenharmony_ci            {
491ffe3c632Sopenharmony_ci                message.Key = entry.Key;
492ffe3c632Sopenharmony_ci                message.Value = entry.Value;
493ffe3c632Sopenharmony_ci                ctx.WriteTag(codec.MapTag);
494ffe3c632Sopenharmony_ci                ctx.WriteMessage(message);
495ffe3c632Sopenharmony_ci            }
496ffe3c632Sopenharmony_ci        }
497ffe3c632Sopenharmony_ci
498ffe3c632Sopenharmony_ci        /// <summary>
499ffe3c632Sopenharmony_ci        /// Calculates the size of this map based on the given entry codec.
500ffe3c632Sopenharmony_ci        /// </summary>
501ffe3c632Sopenharmony_ci        /// <param name="codec">The codec to use to encode each entry.</param>
502ffe3c632Sopenharmony_ci        /// <returns></returns>
503ffe3c632Sopenharmony_ci        public int CalculateSize(Codec codec)
504ffe3c632Sopenharmony_ci        {
505ffe3c632Sopenharmony_ci            if (Count == 0)
506ffe3c632Sopenharmony_ci            {
507ffe3c632Sopenharmony_ci                return 0;
508ffe3c632Sopenharmony_ci            }
509ffe3c632Sopenharmony_ci            var message = new Codec.MessageAdapter(codec);
510ffe3c632Sopenharmony_ci            int size = 0;
511ffe3c632Sopenharmony_ci            foreach (var entry in list)
512ffe3c632Sopenharmony_ci            {
513ffe3c632Sopenharmony_ci                message.Key = entry.Key;
514ffe3c632Sopenharmony_ci                message.Value = entry.Value;
515ffe3c632Sopenharmony_ci                size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
516ffe3c632Sopenharmony_ci                size += CodedOutputStream.ComputeMessageSize(message);
517ffe3c632Sopenharmony_ci            }
518ffe3c632Sopenharmony_ci            return size;
519ffe3c632Sopenharmony_ci        }
520ffe3c632Sopenharmony_ci
521ffe3c632Sopenharmony_ci        /// <summary>
522ffe3c632Sopenharmony_ci        /// Returns a string representation of this repeated field, in the same
523ffe3c632Sopenharmony_ci        /// way as it would be represented by the default JSON formatter.
524ffe3c632Sopenharmony_ci        /// </summary>
525ffe3c632Sopenharmony_ci        public override string ToString()
526ffe3c632Sopenharmony_ci        {
527ffe3c632Sopenharmony_ci            var writer = new StringWriter();
528ffe3c632Sopenharmony_ci            JsonFormatter.Default.WriteDictionary(writer, this);
529ffe3c632Sopenharmony_ci            return writer.ToString();
530ffe3c632Sopenharmony_ci        }
531ffe3c632Sopenharmony_ci
532ffe3c632Sopenharmony_ci        #region IDictionary explicit interface implementation
533ffe3c632Sopenharmony_ci        void IDictionary.Add(object key, object value)
534ffe3c632Sopenharmony_ci        {
535ffe3c632Sopenharmony_ci            Add((TKey)key, (TValue)value);
536ffe3c632Sopenharmony_ci        }
537ffe3c632Sopenharmony_ci
538ffe3c632Sopenharmony_ci        bool IDictionary.Contains(object key)
539ffe3c632Sopenharmony_ci        {
540ffe3c632Sopenharmony_ci            if (!(key is TKey))
541ffe3c632Sopenharmony_ci            {
542ffe3c632Sopenharmony_ci                return false;
543ffe3c632Sopenharmony_ci            }
544ffe3c632Sopenharmony_ci            return ContainsKey((TKey)key);
545ffe3c632Sopenharmony_ci        }
546ffe3c632Sopenharmony_ci
547ffe3c632Sopenharmony_ci        IDictionaryEnumerator IDictionary.GetEnumerator()
548ffe3c632Sopenharmony_ci        {
549ffe3c632Sopenharmony_ci            return new DictionaryEnumerator(GetEnumerator());
550ffe3c632Sopenharmony_ci        }
551ffe3c632Sopenharmony_ci
552ffe3c632Sopenharmony_ci        void IDictionary.Remove(object key)
553ffe3c632Sopenharmony_ci        {
554ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNull(key, nameof(key));
555ffe3c632Sopenharmony_ci            if (!(key is TKey))
556ffe3c632Sopenharmony_ci            {
557ffe3c632Sopenharmony_ci                return;
558ffe3c632Sopenharmony_ci            }
559ffe3c632Sopenharmony_ci            Remove((TKey)key);
560ffe3c632Sopenharmony_ci        }
561ffe3c632Sopenharmony_ci
562ffe3c632Sopenharmony_ci        void ICollection.CopyTo(Array array, int index)
563ffe3c632Sopenharmony_ci        {
564ffe3c632Sopenharmony_ci            // This is ugly and slow as heck, but with any luck it will never be used anyway.
565ffe3c632Sopenharmony_ci            ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
566ffe3c632Sopenharmony_ci            temp.CopyTo(array, index);
567ffe3c632Sopenharmony_ci        }
568ffe3c632Sopenharmony_ci
569ffe3c632Sopenharmony_ci        bool IDictionary.IsFixedSize { get { return false; } }
570ffe3c632Sopenharmony_ci
571ffe3c632Sopenharmony_ci        ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
572ffe3c632Sopenharmony_ci
573ffe3c632Sopenharmony_ci        ICollection IDictionary.Values { get { return (ICollection)Values; } }
574ffe3c632Sopenharmony_ci
575ffe3c632Sopenharmony_ci        bool ICollection.IsSynchronized { get { return false; } }
576ffe3c632Sopenharmony_ci
577ffe3c632Sopenharmony_ci        object ICollection.SyncRoot { get { return this; } }
578ffe3c632Sopenharmony_ci
579ffe3c632Sopenharmony_ci        object IDictionary.this[object key]
580ffe3c632Sopenharmony_ci        {
581ffe3c632Sopenharmony_ci            get
582ffe3c632Sopenharmony_ci            {
583ffe3c632Sopenharmony_ci                ProtoPreconditions.CheckNotNull(key, nameof(key));
584ffe3c632Sopenharmony_ci                if (!(key is TKey))
585ffe3c632Sopenharmony_ci                {
586ffe3c632Sopenharmony_ci                    return null;
587ffe3c632Sopenharmony_ci                }
588ffe3c632Sopenharmony_ci                TValue value;
589ffe3c632Sopenharmony_ci                TryGetValue((TKey)key, out value);
590ffe3c632Sopenharmony_ci                return value;
591ffe3c632Sopenharmony_ci            }
592ffe3c632Sopenharmony_ci
593ffe3c632Sopenharmony_ci            set
594ffe3c632Sopenharmony_ci            {
595ffe3c632Sopenharmony_ci                this[(TKey)key] = (TValue)value;
596ffe3c632Sopenharmony_ci            }
597ffe3c632Sopenharmony_ci        }
598ffe3c632Sopenharmony_ci        #endregion
599ffe3c632Sopenharmony_ci
600ffe3c632Sopenharmony_ci        #region IReadOnlyDictionary explicit interface implementation
601ffe3c632Sopenharmony_ci#if !NET35
602ffe3c632Sopenharmony_ci        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
603ffe3c632Sopenharmony_ci
604ffe3c632Sopenharmony_ci        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
605ffe3c632Sopenharmony_ci#endif
606ffe3c632Sopenharmony_ci        #endregion
607ffe3c632Sopenharmony_ci
608ffe3c632Sopenharmony_ci        private class DictionaryEnumerator : IDictionaryEnumerator
609ffe3c632Sopenharmony_ci        {
610ffe3c632Sopenharmony_ci            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
611ffe3c632Sopenharmony_ci
612ffe3c632Sopenharmony_ci            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
613ffe3c632Sopenharmony_ci            {
614ffe3c632Sopenharmony_ci                this.enumerator = enumerator;
615ffe3c632Sopenharmony_ci            }
616ffe3c632Sopenharmony_ci
617ffe3c632Sopenharmony_ci            public bool MoveNext()
618ffe3c632Sopenharmony_ci            {
619ffe3c632Sopenharmony_ci                return enumerator.MoveNext();
620ffe3c632Sopenharmony_ci            }
621ffe3c632Sopenharmony_ci
622ffe3c632Sopenharmony_ci            public void Reset()
623ffe3c632Sopenharmony_ci            {
624ffe3c632Sopenharmony_ci                enumerator.Reset();
625ffe3c632Sopenharmony_ci            }
626ffe3c632Sopenharmony_ci
627ffe3c632Sopenharmony_ci            public object Current { get { return Entry; } }
628ffe3c632Sopenharmony_ci            public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
629ffe3c632Sopenharmony_ci            public object Key { get { return enumerator.Current.Key; } }
630ffe3c632Sopenharmony_ci            public object Value { get { return enumerator.Current.Value; } }
631ffe3c632Sopenharmony_ci        }
632ffe3c632Sopenharmony_ci
633ffe3c632Sopenharmony_ci        /// <summary>
634ffe3c632Sopenharmony_ci        /// A codec for a specific map field. This contains all the information required to encode and
635ffe3c632Sopenharmony_ci        /// decode the nested messages.
636ffe3c632Sopenharmony_ci        /// </summary>
637ffe3c632Sopenharmony_ci        public sealed class Codec
638ffe3c632Sopenharmony_ci        {
639ffe3c632Sopenharmony_ci            private readonly FieldCodec<TKey> keyCodec;
640ffe3c632Sopenharmony_ci            private readonly FieldCodec<TValue> valueCodec;
641ffe3c632Sopenharmony_ci            private readonly uint mapTag;
642ffe3c632Sopenharmony_ci
643ffe3c632Sopenharmony_ci            /// <summary>
644ffe3c632Sopenharmony_ci            /// Creates a new entry codec based on a separate key codec and value codec,
645ffe3c632Sopenharmony_ci            /// and the tag to use for each map entry.
646ffe3c632Sopenharmony_ci            /// </summary>
647ffe3c632Sopenharmony_ci            /// <param name="keyCodec">The key codec.</param>
648ffe3c632Sopenharmony_ci            /// <param name="valueCodec">The value codec.</param>
649ffe3c632Sopenharmony_ci            /// <param name="mapTag">The map tag to use to introduce each map entry.</param>
650ffe3c632Sopenharmony_ci            public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)
651ffe3c632Sopenharmony_ci            {
652ffe3c632Sopenharmony_ci                this.keyCodec = keyCodec;
653ffe3c632Sopenharmony_ci                this.valueCodec = valueCodec;
654ffe3c632Sopenharmony_ci                this.mapTag = mapTag;
655ffe3c632Sopenharmony_ci            }
656ffe3c632Sopenharmony_ci
657ffe3c632Sopenharmony_ci            /// <summary>
658ffe3c632Sopenharmony_ci            /// The tag used in the enclosing message to indicate map entries.
659ffe3c632Sopenharmony_ci            /// </summary>
660ffe3c632Sopenharmony_ci            internal uint MapTag { get { return mapTag; } }
661ffe3c632Sopenharmony_ci
662ffe3c632Sopenharmony_ci            /// <summary>
663ffe3c632Sopenharmony_ci            /// A mutable message class, used for parsing and serializing. This
664ffe3c632Sopenharmony_ci            /// delegates the work to a codec, but implements the <see cref="IMessage"/> interface
665ffe3c632Sopenharmony_ci            /// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.
666ffe3c632Sopenharmony_ci            /// This is nested inside Codec as it's tightly coupled to the associated codec,
667ffe3c632Sopenharmony_ci            /// and it's simpler if it has direct access to all its fields.
668ffe3c632Sopenharmony_ci            /// </summary>
669ffe3c632Sopenharmony_ci            internal class MessageAdapter : IMessage, IBufferMessage
670ffe3c632Sopenharmony_ci            {
671ffe3c632Sopenharmony_ci                private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
672ffe3c632Sopenharmony_ci
673ffe3c632Sopenharmony_ci                private readonly Codec codec;
674ffe3c632Sopenharmony_ci                internal TKey Key { get; set; }
675ffe3c632Sopenharmony_ci                internal TValue Value { get; set; }
676ffe3c632Sopenharmony_ci
677ffe3c632Sopenharmony_ci                internal MessageAdapter(Codec codec)
678ffe3c632Sopenharmony_ci                {
679ffe3c632Sopenharmony_ci                    this.codec = codec;
680ffe3c632Sopenharmony_ci                }
681ffe3c632Sopenharmony_ci
682ffe3c632Sopenharmony_ci                internal void Reset()
683ffe3c632Sopenharmony_ci                {
684ffe3c632Sopenharmony_ci                    Key = codec.keyCodec.DefaultValue;
685ffe3c632Sopenharmony_ci                    Value = codec.valueCodec.DefaultValue;
686ffe3c632Sopenharmony_ci                }
687ffe3c632Sopenharmony_ci
688ffe3c632Sopenharmony_ci                public void MergeFrom(CodedInputStream input)
689ffe3c632Sopenharmony_ci                {
690ffe3c632Sopenharmony_ci                    // Message adapter is an internal class and we know that all the parsing will happen via InternalMergeFrom.
691ffe3c632Sopenharmony_ci                    throw new NotImplementedException();
692ffe3c632Sopenharmony_ci                }
693ffe3c632Sopenharmony_ci
694ffe3c632Sopenharmony_ci                [SecuritySafeCritical]
695ffe3c632Sopenharmony_ci                public void InternalMergeFrom(ref ParseContext ctx)
696ffe3c632Sopenharmony_ci                {
697ffe3c632Sopenharmony_ci                    uint tag;
698ffe3c632Sopenharmony_ci                    while ((tag = ctx.ReadTag()) != 0)
699ffe3c632Sopenharmony_ci                    {
700ffe3c632Sopenharmony_ci                        if (tag == codec.keyCodec.Tag)
701ffe3c632Sopenharmony_ci                        {
702ffe3c632Sopenharmony_ci                            Key = codec.keyCodec.Read(ref ctx);
703ffe3c632Sopenharmony_ci                        }
704ffe3c632Sopenharmony_ci                        else if (tag == codec.valueCodec.Tag)
705ffe3c632Sopenharmony_ci                        {
706ffe3c632Sopenharmony_ci                            Value = codec.valueCodec.Read(ref ctx);
707ffe3c632Sopenharmony_ci                        }
708ffe3c632Sopenharmony_ci                        else
709ffe3c632Sopenharmony_ci                        {
710ffe3c632Sopenharmony_ci                            ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
711ffe3c632Sopenharmony_ci                        }
712ffe3c632Sopenharmony_ci                    }
713ffe3c632Sopenharmony_ci
714ffe3c632Sopenharmony_ci                    // Corner case: a map entry with a key but no value, where the value type is a message.
715ffe3c632Sopenharmony_ci                    // Read it as if we'd seen input with no data (i.e. create a "default" message).
716ffe3c632Sopenharmony_ci                    if (Value == null)
717ffe3c632Sopenharmony_ci                    {
718ffe3c632Sopenharmony_ci                        if (ctx.state.CodedInputStream != null)
719ffe3c632Sopenharmony_ci                        {
720ffe3c632Sopenharmony_ci                            // the decoded message might not support parsing from ParseContext, so
721ffe3c632Sopenharmony_ci                            // we need to allow fallback to the legacy MergeFrom(CodedInputStream) parsing.
722ffe3c632Sopenharmony_ci                            Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
723ffe3c632Sopenharmony_ci                        }
724ffe3c632Sopenharmony_ci                        else
725ffe3c632Sopenharmony_ci                        {
726ffe3c632Sopenharmony_ci                            ParseContext.Initialize(new ReadOnlySequence<byte>(ZeroLengthMessageStreamData), out ParseContext zeroLengthCtx);
727ffe3c632Sopenharmony_ci                            Value = codec.valueCodec.Read(ref zeroLengthCtx);
728ffe3c632Sopenharmony_ci                        }
729ffe3c632Sopenharmony_ci                    }
730ffe3c632Sopenharmony_ci                }
731ffe3c632Sopenharmony_ci
732ffe3c632Sopenharmony_ci                public void WriteTo(CodedOutputStream output)
733ffe3c632Sopenharmony_ci                {
734ffe3c632Sopenharmony_ci                    // Message adapter is an internal class and we know that all the writing will happen via InternalWriteTo.
735ffe3c632Sopenharmony_ci                    throw new NotImplementedException();
736ffe3c632Sopenharmony_ci                }
737ffe3c632Sopenharmony_ci
738ffe3c632Sopenharmony_ci                [SecuritySafeCritical]
739ffe3c632Sopenharmony_ci                public void InternalWriteTo(ref WriteContext ctx)
740ffe3c632Sopenharmony_ci                {
741ffe3c632Sopenharmony_ci                    codec.keyCodec.WriteTagAndValue(ref ctx, Key);
742ffe3c632Sopenharmony_ci                    codec.valueCodec.WriteTagAndValue(ref ctx, Value);
743ffe3c632Sopenharmony_ci                }
744ffe3c632Sopenharmony_ci
745ffe3c632Sopenharmony_ci                public int CalculateSize()
746ffe3c632Sopenharmony_ci                {
747ffe3c632Sopenharmony_ci                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
748ffe3c632Sopenharmony_ci                }
749ffe3c632Sopenharmony_ci
750ffe3c632Sopenharmony_ci                MessageDescriptor IMessage.Descriptor { get { return null; } }
751ffe3c632Sopenharmony_ci            }
752ffe3c632Sopenharmony_ci        }
753ffe3c632Sopenharmony_ci
754ffe3c632Sopenharmony_ci        private class MapView<T> : ICollection<T>, ICollection
755ffe3c632Sopenharmony_ci        {
756ffe3c632Sopenharmony_ci            private readonly MapField<TKey, TValue> parent;
757ffe3c632Sopenharmony_ci            private readonly Func<KeyValuePair<TKey, TValue>, T> projection;
758ffe3c632Sopenharmony_ci            private readonly Func<T, bool> containsCheck;
759ffe3c632Sopenharmony_ci
760ffe3c632Sopenharmony_ci            internal MapView(
761ffe3c632Sopenharmony_ci                MapField<TKey, TValue> parent,
762ffe3c632Sopenharmony_ci                Func<KeyValuePair<TKey, TValue>, T> projection,
763ffe3c632Sopenharmony_ci                Func<T, bool> containsCheck)
764ffe3c632Sopenharmony_ci            {
765ffe3c632Sopenharmony_ci                this.parent = parent;
766ffe3c632Sopenharmony_ci                this.projection = projection;
767ffe3c632Sopenharmony_ci                this.containsCheck = containsCheck;
768ffe3c632Sopenharmony_ci            }
769ffe3c632Sopenharmony_ci
770ffe3c632Sopenharmony_ci            public int Count { get { return parent.Count; } }
771ffe3c632Sopenharmony_ci
772ffe3c632Sopenharmony_ci            public bool IsReadOnly { get { return true; } }
773ffe3c632Sopenharmony_ci
774ffe3c632Sopenharmony_ci            public bool IsSynchronized { get { return false; } }
775ffe3c632Sopenharmony_ci
776ffe3c632Sopenharmony_ci            public object SyncRoot { get { return parent; } }
777ffe3c632Sopenharmony_ci
778ffe3c632Sopenharmony_ci            public void Add(T item)
779ffe3c632Sopenharmony_ci            {
780ffe3c632Sopenharmony_ci                throw new NotSupportedException();
781ffe3c632Sopenharmony_ci            }
782ffe3c632Sopenharmony_ci
783ffe3c632Sopenharmony_ci            public void Clear()
784ffe3c632Sopenharmony_ci            {
785ffe3c632Sopenharmony_ci                throw new NotSupportedException();
786ffe3c632Sopenharmony_ci            }
787ffe3c632Sopenharmony_ci
788ffe3c632Sopenharmony_ci            public bool Contains(T item)
789ffe3c632Sopenharmony_ci            {
790ffe3c632Sopenharmony_ci                return containsCheck(item);
791ffe3c632Sopenharmony_ci            }
792ffe3c632Sopenharmony_ci
793ffe3c632Sopenharmony_ci            public void CopyTo(T[] array, int arrayIndex)
794ffe3c632Sopenharmony_ci            {
795ffe3c632Sopenharmony_ci                if (arrayIndex < 0)
796ffe3c632Sopenharmony_ci                {
797ffe3c632Sopenharmony_ci                    throw new ArgumentOutOfRangeException(nameof(arrayIndex));
798ffe3c632Sopenharmony_ci                }
799ffe3c632Sopenharmony_ci                if (arrayIndex + Count > array.Length)
800ffe3c632Sopenharmony_ci                {
801ffe3c632Sopenharmony_ci                    throw new ArgumentException("Not enough space in the array", nameof(array));
802ffe3c632Sopenharmony_ci                }
803ffe3c632Sopenharmony_ci                foreach (var item in this)
804ffe3c632Sopenharmony_ci                {
805ffe3c632Sopenharmony_ci                    array[arrayIndex++] = item;
806ffe3c632Sopenharmony_ci                }
807ffe3c632Sopenharmony_ci            }
808ffe3c632Sopenharmony_ci
809ffe3c632Sopenharmony_ci            public IEnumerator<T> GetEnumerator()
810ffe3c632Sopenharmony_ci            {
811ffe3c632Sopenharmony_ci                return parent.list.Select(projection).GetEnumerator();
812ffe3c632Sopenharmony_ci            }
813ffe3c632Sopenharmony_ci
814ffe3c632Sopenharmony_ci            public bool Remove(T item)
815ffe3c632Sopenharmony_ci            {
816ffe3c632Sopenharmony_ci                throw new NotSupportedException();
817ffe3c632Sopenharmony_ci            }
818ffe3c632Sopenharmony_ci
819ffe3c632Sopenharmony_ci            IEnumerator IEnumerable.GetEnumerator()
820ffe3c632Sopenharmony_ci            {
821ffe3c632Sopenharmony_ci                return GetEnumerator();
822ffe3c632Sopenharmony_ci            }
823ffe3c632Sopenharmony_ci
824ffe3c632Sopenharmony_ci            public void CopyTo(Array array, int index)
825ffe3c632Sopenharmony_ci            {
826ffe3c632Sopenharmony_ci                if (index < 0)
827ffe3c632Sopenharmony_ci                {
828ffe3c632Sopenharmony_ci                    throw new ArgumentOutOfRangeException(nameof(index));
829ffe3c632Sopenharmony_ci                }
830ffe3c632Sopenharmony_ci                if (index + Count > array.Length)
831ffe3c632Sopenharmony_ci                {
832ffe3c632Sopenharmony_ci                    throw new ArgumentException("Not enough space in the array", nameof(array));
833ffe3c632Sopenharmony_ci                }
834ffe3c632Sopenharmony_ci                foreach (var item in this)
835ffe3c632Sopenharmony_ci                {
836ffe3c632Sopenharmony_ci                    array.SetValue(item, index++);
837ffe3c632Sopenharmony_ci                }
838ffe3c632Sopenharmony_ci            }
839ffe3c632Sopenharmony_ci        }
840ffe3c632Sopenharmony_ci    }
841ffe3c632Sopenharmony_ci}
842