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