OLD | NEW |
1 #region Copyright notice and license | 1 #region Copyright notice and license |
2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
3 // Copyright 2015 Google Inc. All rights reserved. | 3 // Copyright 2015 Google Inc. All rights reserved. |
4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
5 // | 5 // |
6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
8 // met: | 8 // met: |
9 // | 9 // |
10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
(...skipping 12 matching lines...) Expand all Loading... |
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 #endregion | 31 #endregion |
32 | 32 |
33 using Google.Protobuf.Compatibility; | |
34 using Google.Protobuf.Reflection; | 33 using Google.Protobuf.Reflection; |
35 using System; | 34 using System; |
36 using System.Collections; | 35 using System.Collections; |
37 using System.Collections.Generic; | 36 using System.Collections.Generic; |
38 using System.IO; | 37 using System.IO; |
39 using System.Linq; | 38 using System.Linq; |
| 39 using System.Text; |
| 40 using Google.Protobuf.Compatibility; |
40 | 41 |
41 namespace Google.Protobuf.Collections | 42 namespace Google.Protobuf.Collections |
42 { | 43 { |
43 /// <summary> | 44 /// <summary> |
44 /// Representation of a map field in a Protocol Buffer message. | 45 /// Representation of a map field in a Protocol Buffer message. |
45 /// </summary> | 46 /// </summary> |
46 /// <typeparam name="TKey">Key type in the map. Must be a type supported by
Protocol Buffer map keys.</typeparam> | 47 /// <typeparam name="TKey">Key type in the map. Must be a type supported by
Protocol Buffer map keys.</typeparam> |
47 /// <typeparam name="TValue">Value type in the map. Must be a type supported
by Protocol Buffers.</typeparam> | 48 /// <typeparam name="TValue">Value type in the map. Must be a type supported
by Protocol Buffers.</typeparam> |
48 /// <remarks> | 49 /// <remarks> |
49 /// <para> | 50 /// <para> |
| 51 /// This implementation preserves insertion order for simplicity of testing |
| 52 /// code using maps fields. Overwriting an existing entry does not change th
e |
| 53 /// position of that entry within the map. Equality is not order-sensitive. |
50 /// For string keys, the equality comparison is provided by <see cref="Strin
gComparer.Ordinal" />. | 54 /// For string keys, the equality comparison is provided by <see cref="Strin
gComparer.Ordinal" />. |
51 /// </para> | 55 /// </para> |
52 /// <para> | 56 /// <para> |
53 /// Null values are not permitted in the map, either for wrapper types or re
gular messages. | 57 /// Null values are not permitted in the map, either for wrapper types or re
gular messages. |
54 /// If a map is deserialized from a data stream and the value is missing fro
m an entry, a default value | 58 /// If a map is deserialized from a data stream and the value is missing fro
m an entry, a default value |
55 /// is created instead. For primitive types, that is the regular default val
ue (0, the empty string and so | 59 /// is created instead. For primitive types, that is the regular default val
ue (0, the empty string and so |
56 /// on); for message types, an empty instance of the message is created, as
if the map entry contained a 0-length | 60 /// on); for message types, an empty instance of the message is created, as
if the map entry contained a 0-length |
57 /// encoded value for the field. | 61 /// encoded value for the field. |
58 /// </para> | 62 /// </para> |
59 /// <para> | 63 /// <para> |
60 /// This implementation does not generally prohibit the use of key/value typ
es which are not | 64 /// This implementation does not generally prohibit the use of key/value typ
es which are not |
61 /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code
>) but nor does it guarantee | 65 /// supported by Protocol Buffers (e.g. using a key type of <code>byte</code
>) but nor does it guarantee |
62 /// that all operations will work in such cases. | 66 /// that all operations will work in such cases. |
63 /// </para> | 67 /// </para> |
64 /// <para> | |
65 /// The order in which entries are returned when iterating over this object
is undefined, and may change | |
66 /// in future versions. | |
67 /// </para> | |
68 /// </remarks> | 68 /// </remarks> |
69 public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, T
Value>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDiction
ary | 69 public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, T
Value>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDiction
ary |
70 { | 70 { |
71 // TODO: Don't create the map/list until we have an entry. (Assume many
maps will be empty.) | 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, TVal
ue>>> map = | 72 private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TVal
ue>>> map = |
73 new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); | 73 new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); |
74 private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new Linke
dList<KeyValuePair<TKey, TValue>>(); | 74 private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new Linke
dList<KeyValuePair<TKey, TValue>>(); |
75 | 75 |
76 /// <summary> | 76 /// <summary> |
77 /// Creates a deep clone of this object. | 77 /// Creates a deep clone of this object. |
(...skipping 27 matching lines...) Expand all Loading... |
105 /// This operation fails if the key already exists in the map. To replac
e an existing entry, use the indexer. | 105 /// This operation fails if the key already exists in the map. To replac
e an existing entry, use the indexer. |
106 /// </remarks> | 106 /// </remarks> |
107 /// <param name="key">The key to add</param> | 107 /// <param name="key">The key to add</param> |
108 /// <param name="value">The value to add.</param> | 108 /// <param name="value">The value to add.</param> |
109 /// <exception cref="System.ArgumentException">The given key already exi
sts in map.</exception> | 109 /// <exception cref="System.ArgumentException">The given key already exi
sts in map.</exception> |
110 public void Add(TKey key, TValue value) | 110 public void Add(TKey key, TValue value) |
111 { | 111 { |
112 // Validation of arguments happens in ContainsKey and the indexer | 112 // Validation of arguments happens in ContainsKey and the indexer |
113 if (ContainsKey(key)) | 113 if (ContainsKey(key)) |
114 { | 114 { |
115 throw new ArgumentException("Key already exists in map", nameof(
key)); | 115 throw new ArgumentException("Key already exists in map", "key"); |
116 } | 116 } |
117 this[key] = value; | 117 this[key] = value; |
118 } | 118 } |
119 | 119 |
120 /// <summary> | 120 /// <summary> |
121 /// Determines whether the specified key is present in the map. | 121 /// Determines whether the specified key is present in the map. |
122 /// </summary> | 122 /// </summary> |
123 /// <param name="key">The key to check.</param> | 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> | 124 /// <returns><c>true</c> if the map contains the given key; <c>false</c>
otherwise.</returns> |
125 public bool ContainsKey(TKey key) | 125 public bool ContainsKey(TKey key) |
126 { | 126 { |
127 ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); | 127 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
128 return map.ContainsKey(key); | 128 return map.ContainsKey(key); |
129 } | 129 } |
130 | 130 |
131 private bool ContainsValue(TValue value) | 131 private bool ContainsValue(TValue value) |
132 { | 132 { |
133 var comparer = EqualityComparer<TValue>.Default; | 133 var comparer = EqualityComparer<TValue>.Default; |
134 return list.Any(pair => comparer.Equals(pair.Value, value)); | 134 return list.Any(pair => comparer.Equals(pair.Value, value)); |
135 } | 135 } |
136 | 136 |
137 /// <summary> | 137 /// <summary> |
138 /// Removes the entry identified by the given key from the map. | 138 /// Removes the entry identified by the given key from the map. |
139 /// </summary> | 139 /// </summary> |
140 /// <param name="key">The key indicating the entry to remove from the ma
p.</param> | 140 /// <param name="key">The key indicating the entry to remove from the ma
p.</param> |
141 /// <returns><c>true</c> if the map contained the given key before the e
ntry was removed; <c>false</c> otherwise.</returns> | 141 /// <returns><c>true</c> if the map contained the given key before the e
ntry was removed; <c>false</c> otherwise.</returns> |
142 public bool Remove(TKey key) | 142 public bool Remove(TKey key) |
143 { | 143 { |
144 ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); | 144 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
145 LinkedListNode<KeyValuePair<TKey, TValue>> node; | 145 LinkedListNode<KeyValuePair<TKey, TValue>> node; |
146 if (map.TryGetValue(key, out node)) | 146 if (map.TryGetValue(key, out node)) |
147 { | 147 { |
148 map.Remove(key); | 148 map.Remove(key); |
149 node.List.Remove(node); | 149 node.List.Remove(node); |
150 return true; | 150 return true; |
151 } | 151 } |
152 else | 152 else |
153 { | 153 { |
154 return false; | 154 return false; |
(...skipping 27 matching lines...) Expand all Loading... |
182 /// Gets or sets the value associated with the specified key. | 182 /// Gets or sets the value associated with the specified key. |
183 /// </summary> | 183 /// </summary> |
184 /// <param name="key">The key of the value to get or set.</param> | 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> | 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 specifi
ed key is not found, | 186 /// <returns>The value associated with the specified key. If the specifi
ed key is not found, |
187 /// a get operation throws a <see cref="KeyNotFoundException"/>, and a s
et operation creates a new element with the specified key.</returns> | 187 /// a get operation throws a <see cref="KeyNotFoundException"/>, and a s
et operation creates a new element with the specified key.</returns> |
188 public TValue this[TKey key] | 188 public TValue this[TKey key] |
189 { | 189 { |
190 get | 190 get |
191 { | 191 { |
192 ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); | 192 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
193 TValue value; | 193 TValue value; |
194 if (TryGetValue(key, out value)) | 194 if (TryGetValue(key, out value)) |
195 { | 195 { |
196 return value; | 196 return value; |
197 } | 197 } |
198 throw new KeyNotFoundException(); | 198 throw new KeyNotFoundException(); |
199 } | 199 } |
200 set | 200 set |
201 { | 201 { |
202 ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key)); | 202 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
203 // value == null check here is redundant, but avoids boxing. | 203 // value == null check here is redundant, but avoids boxing. |
204 if (value == null) | 204 if (value == null) |
205 { | 205 { |
206 ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(v
alue)); | 206 ProtoPreconditions.CheckNotNullUnconstrained(value, "value")
; |
207 } | 207 } |
208 LinkedListNode<KeyValuePair<TKey, TValue>> node; | 208 LinkedListNode<KeyValuePair<TKey, TValue>> node; |
209 var pair = new KeyValuePair<TKey, TValue>(key, value); | 209 var pair = new KeyValuePair<TKey, TValue>(key, value); |
210 if (map.TryGetValue(key, out node)) | 210 if (map.TryGetValue(key, out node)) |
211 { | 211 { |
212 node.Value = pair; | 212 node.Value = pair; |
213 } | 213 } |
214 else | 214 else |
215 { | 215 { |
216 node = list.AddLast(pair); | 216 node = list.AddLast(pair); |
(...skipping 11 matching lines...) Expand all Loading... |
228 /// Gets a collection containing the values in the map. | 228 /// Gets a collection containing the values in the map. |
229 /// </summary> | 229 /// </summary> |
230 public ICollection<TValue> Values { get { return new MapView<TValue>(thi
s, pair => pair.Value, ContainsValue); } } | 230 public ICollection<TValue> Values { get { return new MapView<TValue>(thi
s, pair => pair.Value, ContainsValue); } } |
231 | 231 |
232 /// <summary> | 232 /// <summary> |
233 /// Adds the specified entries to the map. The keys and values are not a
utomatically cloned. | 233 /// Adds the specified entries to the map. The keys and values are not a
utomatically cloned. |
234 /// </summary> | 234 /// </summary> |
235 /// <param name="entries">The entries to add to the map.</param> | 235 /// <param name="entries">The entries to add to the map.</param> |
236 public void Add(IDictionary<TKey, TValue> entries) | 236 public void Add(IDictionary<TKey, TValue> entries) |
237 { | 237 { |
238 ProtoPreconditions.CheckNotNull(entries, nameof(entries)); | 238 ProtoPreconditions.CheckNotNull(entries, "entries"); |
239 foreach (var pair in entries) | 239 foreach (var pair in entries) |
240 { | 240 { |
241 Add(pair.Key, pair.Value); | 241 Add(pair.Key, pair.Value); |
242 } | 242 } |
243 } | 243 } |
244 | 244 |
245 /// <summary> | 245 /// <summary> |
246 /// Returns an enumerator that iterates through the collection. | 246 /// Returns an enumerator that iterates through the collection. |
247 /// </summary> | 247 /// </summary> |
248 /// <returns> | 248 /// <returns> |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 /// <summary> | 307 /// <summary> |
308 /// Removes the specified key/value pair from the map. | 308 /// Removes the specified key/value pair from the map. |
309 /// </summary> | 309 /// </summary> |
310 /// <remarks>Both the key and the value must be found for the entry to b
e removed.</remarks> | 310 /// <remarks>Both the key and the value must be found for the entry to b
e removed.</remarks> |
311 /// <param name="item">The key/value pair to remove.</param> | 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> | 312 /// <returns><c>true</c> if the key/value pair was found and removed; <c
>false</c> otherwise.</returns> |
313 bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, T
Value> item) | 313 bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, T
Value> item) |
314 { | 314 { |
315 if (item.Key == null) | 315 if (item.Key == null) |
316 { | 316 { |
317 throw new ArgumentException("Key is null", nameof(item)); | 317 throw new ArgumentException("Key is null", "item"); |
318 } | 318 } |
319 LinkedListNode<KeyValuePair<TKey, TValue>> node; | 319 LinkedListNode<KeyValuePair<TKey, TValue>> node; |
320 if (map.TryGetValue(item.Key, out node) && | 320 if (map.TryGetValue(item.Key, out node) && |
321 EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.V
alue)) | 321 EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.V
alue)) |
322 { | 322 { |
323 map.Remove(item.Key); | 323 map.Remove(item.Key); |
324 node.List.Remove(node); | 324 node.List.Remove(node); |
325 return true; | 325 return true; |
326 } | 326 } |
327 else | 327 else |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 return ContainsKey((TKey)key); | 495 return ContainsKey((TKey)key); |
496 } | 496 } |
497 | 497 |
498 IDictionaryEnumerator IDictionary.GetEnumerator() | 498 IDictionaryEnumerator IDictionary.GetEnumerator() |
499 { | 499 { |
500 return new DictionaryEnumerator(GetEnumerator()); | 500 return new DictionaryEnumerator(GetEnumerator()); |
501 } | 501 } |
502 | 502 |
503 void IDictionary.Remove(object key) | 503 void IDictionary.Remove(object key) |
504 { | 504 { |
505 ProtoPreconditions.CheckNotNull(key, nameof(key)); | 505 ProtoPreconditions.CheckNotNull(key, "key"); |
506 if (!(key is TKey)) | 506 if (!(key is TKey)) |
507 { | 507 { |
508 return; | 508 return; |
509 } | 509 } |
510 Remove((TKey)key); | 510 Remove((TKey)key); |
511 } | 511 } |
512 | 512 |
513 void ICollection.CopyTo(Array array, int index) | 513 void ICollection.CopyTo(Array array, int index) |
514 { | 514 { |
515 // This is ugly and slow as heck, but with any luck it will never be
used anyway. | 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(); | 516 ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key,
pair.Value)).ToList(); |
517 temp.CopyTo(array, index); | 517 temp.CopyTo(array, index); |
518 } | 518 } |
519 | 519 |
520 bool IDictionary.IsFixedSize { get { return false; } } | 520 bool IDictionary.IsFixedSize { get { return false; } } |
521 | 521 |
522 ICollection IDictionary.Keys { get { return (ICollection)Keys; } } | 522 ICollection IDictionary.Keys { get { return (ICollection)Keys; } } |
523 | 523 |
524 ICollection IDictionary.Values { get { return (ICollection)Values; } } | 524 ICollection IDictionary.Values { get { return (ICollection)Values; } } |
525 | 525 |
526 bool ICollection.IsSynchronized { get { return false; } } | 526 bool ICollection.IsSynchronized { get { return false; } } |
527 | 527 |
528 object ICollection.SyncRoot { get { return this; } } | 528 object ICollection.SyncRoot { get { return this; } } |
529 | 529 |
530 object IDictionary.this[object key] | 530 object IDictionary.this[object key] |
531 { | 531 { |
532 get | 532 get |
533 { | 533 { |
534 ProtoPreconditions.CheckNotNull(key, nameof(key)); | 534 ProtoPreconditions.CheckNotNull(key, "key"); |
535 if (!(key is TKey)) | 535 if (!(key is TKey)) |
536 { | 536 { |
537 return null; | 537 return null; |
538 } | 538 } |
539 TValue value; | 539 TValue value; |
540 TryGetValue((TKey)key, out value); | 540 TryGetValue((TKey)key, out value); |
541 return value; | 541 return value; |
542 } | 542 } |
543 | 543 |
544 set | 544 set |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
706 | 706 |
707 public bool Contains(T item) | 707 public bool Contains(T item) |
708 { | 708 { |
709 return containsCheck(item); | 709 return containsCheck(item); |
710 } | 710 } |
711 | 711 |
712 public void CopyTo(T[] array, int arrayIndex) | 712 public void CopyTo(T[] array, int arrayIndex) |
713 { | 713 { |
714 if (arrayIndex < 0) | 714 if (arrayIndex < 0) |
715 { | 715 { |
716 throw new ArgumentOutOfRangeException(nameof(arrayIndex)); | 716 throw new ArgumentOutOfRangeException("arrayIndex"); |
717 } | 717 } |
718 if (arrayIndex + Count >= array.Length) | 718 if (arrayIndex + Count >= array.Length) |
719 { | 719 { |
720 throw new ArgumentException("Not enough space in the array",
nameof(array)); | 720 throw new ArgumentException("Not enough space in the array",
"array"); |
721 } | 721 } |
722 foreach (var item in this) | 722 foreach (var item in this) |
723 { | 723 { |
724 array[arrayIndex++] = item; | 724 array[arrayIndex++] = item; |
725 } | 725 } |
726 } | 726 } |
727 | 727 |
728 public IEnumerator<T> GetEnumerator() | 728 public IEnumerator<T> GetEnumerator() |
729 { | 729 { |
730 return parent.list.Select(projection).GetEnumerator(); | 730 return parent.list.Select(projection).GetEnumerator(); |
731 } | 731 } |
732 | 732 |
733 public bool Remove(T item) | 733 public bool Remove(T item) |
734 { | 734 { |
735 throw new NotSupportedException(); | 735 throw new NotSupportedException(); |
736 } | 736 } |
737 | 737 |
738 IEnumerator IEnumerable.GetEnumerator() | 738 IEnumerator IEnumerable.GetEnumerator() |
739 { | 739 { |
740 return GetEnumerator(); | 740 return GetEnumerator(); |
741 } | 741 } |
742 | 742 |
743 public void CopyTo(Array array, int index) | 743 public void CopyTo(Array array, int index) |
744 { | 744 { |
745 if (index < 0) | 745 if (index < 0) |
746 { | 746 { |
747 throw new ArgumentOutOfRangeException(nameof(index)); | 747 throw new ArgumentOutOfRangeException("index"); |
748 } | 748 } |
749 if (index + Count >= array.Length) | 749 if (index + Count >= array.Length) |
750 { | 750 { |
751 throw new ArgumentException("Not enough space in the array",
nameof(array)); | 751 throw new ArgumentException("Not enough space in the array",
"array"); |
752 } | 752 } |
753 foreach (var item in this) | 753 foreach (var item in this) |
754 { | 754 { |
755 array.SetValue(item, index++); | 755 array.SetValue(item, index++); |
756 } | 756 } |
757 } | 757 } |
758 } | 758 } |
759 } | 759 } |
760 } | 760 } |
OLD | NEW |