| 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 |