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