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 16 matching lines...) Expand all Loading... |
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.Reflection; | 33 using Google.Protobuf.Reflection; |
34 using System; | 34 using System; |
35 using System.Collections; | 35 using System.Collections; |
36 using System.Collections.Generic; | 36 using System.Collections.Generic; |
| 37 using System.IO; |
37 using System.Linq; | 38 using System.Linq; |
38 using System.Text; | 39 using System.Text; |
39 using Google.Protobuf.Compatibility; | 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> |
50 /// This implementation preserves insertion order for simplicity of testing | 51 /// This implementation preserves insertion order for simplicity of testing |
51 /// code using maps fields. Overwriting an existing entry does not change th
e | 52 /// code using maps fields. Overwriting an existing entry does not change th
e |
52 /// position of that entry within the map. Equality is not order-sensitive. | 53 /// position of that entry within the map. Equality is not order-sensitive. |
53 /// 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" />. |
54 /// </para> | 55 /// </para> |
55 /// <para> | 56 /// <para> |
| 57 /// Null values are not permitted in the map, either for wrapper types or re
gular messages. |
| 58 /// If a map is deserialized from a data stream and the value is missing fro
m an entry, a default value |
| 59 /// is created instead. For primitive types, that is the regular default val
ue (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> |
56 /// 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 |
57 /// 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 |
58 /// that all operations will work in such cases. | 66 /// that all operations will work in such cases. |
59 /// </para> | 67 /// </para> |
60 /// </remarks> | 68 /// </remarks> |
61 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 |
62 { | 70 { |
63 // 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.) |
64 private readonly bool allowNullValues; | |
65 private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TVal
ue>>> map = | 72 private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TVal
ue>>> map = |
66 new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); | 73 new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); |
67 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>>(); |
68 | 75 |
69 /// <summary> | 76 /// <summary> |
70 /// Constructs a new map field, defaulting the value nullability to only
allow null values for message types | |
71 /// and non-nullable value types. | |
72 /// </summary> | |
73 public MapField() : this(typeof(IMessage).IsAssignableFrom(typeof(TValue
)) || Nullable.GetUnderlyingType(typeof(TValue)) != null) | |
74 { | |
75 } | |
76 | |
77 /// <summary> | |
78 /// Constructs a new map field, overriding the choice of whether null va
lues are permitted in the map. | |
79 /// This is used by wrapper types, where maps with string and bytes wrap
pers as the value types | |
80 /// support null values. | |
81 /// </summary> | |
82 /// <param name="allowNullValues">Whether null values are permitted in t
he map or not.</param> | |
83 public MapField(bool allowNullValues) | |
84 { | |
85 if (allowNullValues && typeof(TValue).IsValueType() && Nullable.GetU
nderlyingType(typeof(TValue)) == null) | |
86 { | |
87 throw new ArgumentException("allowNullValues", "Non-nullable val
ue types do not support null values"); | |
88 } | |
89 this.allowNullValues = allowNullValues; | |
90 } | |
91 | |
92 /// <summary> | |
93 /// Creates a deep clone of this object. | 77 /// Creates a deep clone of this object. |
94 /// </summary> | 78 /// </summary> |
95 /// <returns> | 79 /// <returns> |
96 /// A deep clone of this object. | 80 /// A deep clone of this object. |
97 /// </returns> | 81 /// </returns> |
98 public MapField<TKey, TValue> Clone() | 82 public MapField<TKey, TValue> Clone() |
99 { | 83 { |
100 var clone = new MapField<TKey, TValue>(allowNullValues); | 84 var clone = new MapField<TKey, TValue>(); |
101 // Keys are never cloneable. Values might be. | 85 // Keys are never cloneable. Values might be. |
102 if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue))) | 86 if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue))) |
103 { | 87 { |
104 foreach (var pair in list) | 88 foreach (var pair in list) |
105 { | 89 { |
106 clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDee
pCloneable<TValue>)pair.Value).Clone()); | 90 clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clo
ne()); |
107 } | 91 } |
108 } | 92 } |
109 else | 93 else |
110 { | 94 { |
111 // Nothing is cloneable, so we don't need to worry. | 95 // Nothing is cloneable, so we don't need to worry. |
112 clone.Add(this); | 96 clone.Add(this); |
113 } | 97 } |
114 return clone; | 98 return clone; |
115 } | 99 } |
116 | 100 |
(...skipping 16 matching lines...) Expand all Loading... |
133 this[key] = value; | 117 this[key] = value; |
134 } | 118 } |
135 | 119 |
136 /// <summary> | 120 /// <summary> |
137 /// Determines whether the specified key is present in the map. | 121 /// Determines whether the specified key is present in the map. |
138 /// </summary> | 122 /// </summary> |
139 /// <param name="key">The key to check.</param> | 123 /// <param name="key">The key to check.</param> |
140 /// <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> |
141 public bool ContainsKey(TKey key) | 125 public bool ContainsKey(TKey key) |
142 { | 126 { |
143 Preconditions.CheckNotNullUnconstrained(key, "key"); | 127 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
144 return map.ContainsKey(key); | 128 return map.ContainsKey(key); |
145 } | 129 } |
146 | 130 |
147 private bool ContainsValue(TValue value) | 131 private bool ContainsValue(TValue value) |
148 { | 132 { |
149 var comparer = EqualityComparer<TValue>.Default; | 133 var comparer = EqualityComparer<TValue>.Default; |
150 return list.Any(pair => comparer.Equals(pair.Value, value)); | 134 return list.Any(pair => comparer.Equals(pair.Value, value)); |
151 } | 135 } |
152 | 136 |
153 /// <summary> | 137 /// <summary> |
154 /// Removes the entry identified by the given key from the map. | 138 /// Removes the entry identified by the given key from the map. |
155 /// </summary> | 139 /// </summary> |
156 /// <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> |
157 /// <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> |
158 public bool Remove(TKey key) | 142 public bool Remove(TKey key) |
159 { | 143 { |
160 Preconditions.CheckNotNullUnconstrained(key, "key"); | 144 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
161 LinkedListNode<KeyValuePair<TKey, TValue>> node; | 145 LinkedListNode<KeyValuePair<TKey, TValue>> node; |
162 if (map.TryGetValue(key, out node)) | 146 if (map.TryGetValue(key, out node)) |
163 { | 147 { |
164 map.Remove(key); | 148 map.Remove(key); |
165 node.List.Remove(node); | 149 node.List.Remove(node); |
166 return true; | 150 return true; |
167 } | 151 } |
168 else | 152 else |
169 { | 153 { |
170 return false; | 154 return false; |
(...skipping 27 matching lines...) Expand all Loading... |
198 /// Gets or sets the value associated with the specified key. | 182 /// Gets or sets the value associated with the specified key. |
199 /// </summary> | 183 /// </summary> |
200 /// <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> |
201 /// <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> |
202 /// <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, |
203 /// 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> |
204 public TValue this[TKey key] | 188 public TValue this[TKey key] |
205 { | 189 { |
206 get | 190 get |
207 { | 191 { |
208 Preconditions.CheckNotNullUnconstrained(key, "key"); | 192 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
209 TValue value; | 193 TValue value; |
210 if (TryGetValue(key, out value)) | 194 if (TryGetValue(key, out value)) |
211 { | 195 { |
212 return value; | 196 return value; |
213 } | 197 } |
214 throw new KeyNotFoundException(); | 198 throw new KeyNotFoundException(); |
215 } | 199 } |
216 set | 200 set |
217 { | 201 { |
218 Preconditions.CheckNotNullUnconstrained(key, "key"); | 202 ProtoPreconditions.CheckNotNullUnconstrained(key, "key"); |
219 // value == null check here is redundant, but avoids boxing. | 203 // value == null check here is redundant, but avoids boxing. |
220 if (value == null && !allowNullValues) | 204 if (value == null) |
221 { | 205 { |
222 Preconditions.CheckNotNullUnconstrained(value, "value"); | 206 ProtoPreconditions.CheckNotNullUnconstrained(value, "value")
; |
223 } | 207 } |
224 LinkedListNode<KeyValuePair<TKey, TValue>> node; | 208 LinkedListNode<KeyValuePair<TKey, TValue>> node; |
225 var pair = new KeyValuePair<TKey, TValue>(key, value); | 209 var pair = new KeyValuePair<TKey, TValue>(key, value); |
226 if (map.TryGetValue(key, out node)) | 210 if (map.TryGetValue(key, out node)) |
227 { | 211 { |
228 node.Value = pair; | 212 node.Value = pair; |
229 } | 213 } |
230 else | 214 else |
231 { | 215 { |
232 node = list.AddLast(pair); | 216 node = list.AddLast(pair); |
233 map[key] = node; | 217 map[key] = node; |
234 } | 218 } |
235 } | 219 } |
236 } | 220 } |
237 | 221 |
238 /// <summary> | 222 /// <summary> |
239 /// Gets a collection containing the keys in the map. | 223 /// Gets a collection containing the keys in the map. |
240 /// </summary> | 224 /// </summary> |
241 public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pai
r => pair.Key, ContainsKey); } } | 225 public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pai
r => pair.Key, ContainsKey); } } |
242 | 226 |
243 /// <summary> | 227 /// <summary> |
244 /// Gets a collection containing the values in the map. | 228 /// Gets a collection containing the values in the map. |
245 /// </summary> | 229 /// </summary> |
246 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); } } |
247 | 231 |
248 /// <summary> | 232 /// <summary> |
249 /// Adds the specified entries to the map. | 233 /// Adds the specified entries to the map. The keys and values are not a
utomatically cloned. |
250 /// </summary> | 234 /// </summary> |
251 /// <param name="entries">The entries to add to the map.</param> | 235 /// <param name="entries">The entries to add to the map.</param> |
252 public void Add(IDictionary<TKey, TValue> entries) | 236 public void Add(IDictionary<TKey, TValue> entries) |
253 { | 237 { |
254 Preconditions.CheckNotNull(entries, "entries"); | 238 ProtoPreconditions.CheckNotNull(entries, "entries"); |
255 foreach (var pair in entries) | 239 foreach (var pair in entries) |
256 { | 240 { |
257 Add(pair.Key, pair.Value); | 241 Add(pair.Key, pair.Value); |
258 } | 242 } |
259 } | 243 } |
260 | 244 |
261 /// <summary> | 245 /// <summary> |
262 /// Returns an enumerator that iterates through the collection. | 246 /// Returns an enumerator that iterates through the collection. |
263 /// </summary> | 247 /// </summary> |
264 /// <returns> | 248 /// <returns> |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 node.List.Remove(node); | 324 node.List.Remove(node); |
341 return true; | 325 return true; |
342 } | 326 } |
343 else | 327 else |
344 { | 328 { |
345 return false; | 329 return false; |
346 } | 330 } |
347 } | 331 } |
348 | 332 |
349 /// <summary> | 333 /// <summary> |
350 /// Returns whether or not this map allows values to be null. | |
351 /// </summary> | |
352 public bool AllowsNullValues { get { return allowNullValues; } } | |
353 | |
354 /// <summary> | |
355 /// Gets the number of elements contained in the map. | 334 /// Gets the number of elements contained in the map. |
356 /// </summary> | 335 /// </summary> |
357 public int Count { get { return list.Count; } } | 336 public int Count { get { return list.Count; } } |
358 | 337 |
359 /// <summary> | 338 /// <summary> |
360 /// Gets a value indicating whether the map is read-only. | 339 /// Gets a value indicating whether the map is read-only. |
361 /// </summary> | 340 /// </summary> |
362 public bool IsReadOnly { get { return false; } } | 341 public bool IsReadOnly { get { return false; } } |
363 | 342 |
364 /// <summary> | 343 /// <summary> |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 } | 468 } |
490 return size; | 469 return size; |
491 } | 470 } |
492 | 471 |
493 /// <summary> | 472 /// <summary> |
494 /// Returns a string representation of this repeated field, in the same | 473 /// Returns a string representation of this repeated field, in the same |
495 /// way as it would be represented by the default JSON formatter. | 474 /// way as it would be represented by the default JSON formatter. |
496 /// </summary> | 475 /// </summary> |
497 public override string ToString() | 476 public override string ToString() |
498 { | 477 { |
499 var builder = new StringBuilder(); | 478 var writer = new StringWriter(); |
500 JsonFormatter.Default.WriteDictionary(builder, this); | 479 JsonFormatter.Default.WriteDictionary(writer, this); |
501 return builder.ToString(); | 480 return writer.ToString(); |
502 } | 481 } |
503 | 482 |
504 #region IDictionary explicit interface implementation | 483 #region IDictionary explicit interface implementation |
505 void IDictionary.Add(object key, object value) | 484 void IDictionary.Add(object key, object value) |
506 { | 485 { |
507 Add((TKey)key, (TValue)value); | 486 Add((TKey)key, (TValue)value); |
508 } | 487 } |
509 | 488 |
510 bool IDictionary.Contains(object key) | 489 bool IDictionary.Contains(object key) |
511 { | 490 { |
512 if (!(key is TKey)) | 491 if (!(key is TKey)) |
513 { | 492 { |
514 return false; | 493 return false; |
515 } | 494 } |
516 return ContainsKey((TKey)key); | 495 return ContainsKey((TKey)key); |
517 } | 496 } |
518 | 497 |
519 IDictionaryEnumerator IDictionary.GetEnumerator() | 498 IDictionaryEnumerator IDictionary.GetEnumerator() |
520 { | 499 { |
521 return new DictionaryEnumerator(GetEnumerator()); | 500 return new DictionaryEnumerator(GetEnumerator()); |
522 } | 501 } |
523 | 502 |
524 void IDictionary.Remove(object key) | 503 void IDictionary.Remove(object key) |
525 { | 504 { |
526 Preconditions.CheckNotNull(key, "key"); | 505 ProtoPreconditions.CheckNotNull(key, "key"); |
527 if (!(key is TKey)) | 506 if (!(key is TKey)) |
528 { | 507 { |
529 return; | 508 return; |
530 } | 509 } |
531 Remove((TKey)key); | 510 Remove((TKey)key); |
532 } | 511 } |
533 | 512 |
534 void ICollection.CopyTo(Array array, int index) | 513 void ICollection.CopyTo(Array array, int index) |
535 { | 514 { |
536 // 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. |
537 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(); |
538 temp.CopyTo(array, index); | 517 temp.CopyTo(array, index); |
539 } | 518 } |
540 | 519 |
541 bool IDictionary.IsFixedSize { get { return false; } } | 520 bool IDictionary.IsFixedSize { get { return false; } } |
542 | 521 |
543 ICollection IDictionary.Keys { get { return (ICollection)Keys; } } | 522 ICollection IDictionary.Keys { get { return (ICollection)Keys; } } |
544 | 523 |
545 ICollection IDictionary.Values { get { return (ICollection)Values; } } | 524 ICollection IDictionary.Values { get { return (ICollection)Values; } } |
546 | 525 |
547 bool ICollection.IsSynchronized { get { return false; } } | 526 bool ICollection.IsSynchronized { get { return false; } } |
548 | 527 |
549 object ICollection.SyncRoot { get { return this; } } | 528 object ICollection.SyncRoot { get { return this; } } |
550 | 529 |
551 object IDictionary.this[object key] | 530 object IDictionary.this[object key] |
552 { | 531 { |
553 get | 532 get |
554 { | 533 { |
555 Preconditions.CheckNotNull(key, "key"); | 534 ProtoPreconditions.CheckNotNull(key, "key"); |
556 if (!(key is TKey)) | 535 if (!(key is TKey)) |
557 { | 536 { |
558 return null; | 537 return null; |
559 } | 538 } |
560 TValue value; | 539 TValue value; |
561 TryGetValue((TKey)key, out value); | 540 TryGetValue((TKey)key, out value); |
562 return value; | 541 return value; |
563 } | 542 } |
564 | 543 |
565 set | 544 set |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
625 | 604 |
626 /// <summary> | 605 /// <summary> |
627 /// A mutable message class, used for parsing and serializing. This | 606 /// A mutable message class, used for parsing and serializing. This |
628 /// delegates the work to a codec, but implements the <see cref="IMe
ssage"/> interface | 607 /// delegates the work to a codec, but implements the <see cref="IMe
ssage"/> interface |
629 /// for interop with <see cref="CodedInputStream"/> and <see cref="C
odedOutputStream"/>. | 608 /// for interop with <see cref="CodedInputStream"/> and <see cref="C
odedOutputStream"/>. |
630 /// This is nested inside Codec as it's tightly coupled to the assoc
iated codec, | 609 /// This is nested inside Codec as it's tightly coupled to the assoc
iated codec, |
631 /// and it's simpler if it has direct access to all its fields. | 610 /// and it's simpler if it has direct access to all its fields. |
632 /// </summary> | 611 /// </summary> |
633 internal class MessageAdapter : IMessage | 612 internal class MessageAdapter : IMessage |
634 { | 613 { |
| 614 private static readonly byte[] ZeroLengthMessageStreamData = new
byte[] { 0 }; |
| 615 |
635 private readonly Codec codec; | 616 private readonly Codec codec; |
636 internal TKey Key { get; set; } | 617 internal TKey Key { get; set; } |
637 internal TValue Value { get; set; } | 618 internal TValue Value { get; set; } |
638 | 619 |
639 internal MessageAdapter(Codec codec) | 620 internal MessageAdapter(Codec codec) |
640 { | 621 { |
641 this.codec = codec; | 622 this.codec = codec; |
642 } | 623 } |
643 | 624 |
644 internal void Reset() | 625 internal void Reset() |
(...skipping 13 matching lines...) Expand all Loading... |
658 } | 639 } |
659 else if (tag == codec.valueCodec.Tag) | 640 else if (tag == codec.valueCodec.Tag) |
660 { | 641 { |
661 Value = codec.valueCodec.Read(input); | 642 Value = codec.valueCodec.Read(input); |
662 } | 643 } |
663 else | 644 else |
664 { | 645 { |
665 input.SkipLastField(); | 646 input.SkipLastField(); |
666 } | 647 } |
667 } | 648 } |
| 649 |
| 650 // Corner case: a map entry with a key but no value, where t
he 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(ZeroL
engthMessageStreamData)); |
| 655 } |
668 } | 656 } |
669 | 657 |
670 public void WriteTo(CodedOutputStream output) | 658 public void WriteTo(CodedOutputStream output) |
671 { | 659 { |
672 codec.keyCodec.WriteTagAndValue(output, Key); | 660 codec.keyCodec.WriteTagAndValue(output, Key); |
673 codec.valueCodec.WriteTagAndValue(output, Value); | 661 codec.valueCodec.WriteTagAndValue(output, Value); |
674 } | 662 } |
675 | 663 |
676 public int CalculateSize() | 664 public int CalculateSize() |
677 { | 665 { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
763 throw new ArgumentException("Not enough space in the array",
"array"); | 751 throw new ArgumentException("Not enough space in the array",
"array"); |
764 } | 752 } |
765 foreach (var item in this) | 753 foreach (var item in this) |
766 { | 754 { |
767 array.SetValue(item, index++); | 755 array.SetValue(item, index++); |
768 } | 756 } |
769 } | 757 } |
770 } | 758 } |
771 } | 759 } |
772 } | 760 } |
OLD | NEW |