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