|
@@ -34,7 +34,6 @@ using System;
|
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
|
using System.IO;
|
|
|
-using System.Text;
|
|
|
|
|
|
namespace Google.Protobuf.Collections
|
|
|
{
|
|
@@ -227,10 +226,7 @@ namespace Google.Protobuf.Collections
|
|
|
/// <param name="item">The item to add.</param>
|
|
|
public void Add(T item)
|
|
|
{
|
|
|
- if (item == null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException("item");
|
|
|
- }
|
|
|
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
|
|
|
EnsureSize(count + 1);
|
|
|
array[count++] = item;
|
|
|
}
|
|
@@ -285,46 +281,82 @@ namespace Google.Protobuf.Collections
|
|
|
/// <summary>
|
|
|
/// Gets the number of elements contained in the collection.
|
|
|
/// </summary>
|
|
|
- public int Count { get { return count; } }
|
|
|
+ public int Count => count;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets a value indicating whether the collection is read-only.
|
|
|
/// </summary>
|
|
|
- public bool IsReadOnly { get { return false; } }
|
|
|
-
|
|
|
- // TODO: Remove this overload and just handle it in the one below, at execution time?
|
|
|
+ public bool IsReadOnly => false;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Adds all of the specified values into this collection.
|
|
|
/// </summary>
|
|
|
/// <param name="values">The values to add to this collection.</param>
|
|
|
- public void Add(RepeatedField<T> values)
|
|
|
+ public void AddRange(IEnumerable<T> values)
|
|
|
{
|
|
|
- if (values == null)
|
|
|
+ ProtoPreconditions.CheckNotNull(values, nameof(values));
|
|
|
+
|
|
|
+ // Optimization 1: If the collection we're adding is already a RepeatedField<T>,
|
|
|
+ // we know the values are valid.
|
|
|
+ var otherRepeatedField = values as RepeatedField<T>;
|
|
|
+ if (otherRepeatedField != null)
|
|
|
{
|
|
|
- throw new ArgumentNullException("values");
|
|
|
+ EnsureSize(count + otherRepeatedField.count);
|
|
|
+ Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
|
|
|
+ count += otherRepeatedField.count;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Optimization 2: The collection is an ICollection, so we can expand
|
|
|
+ // just once and ask the collection to copy itself into the array.
|
|
|
+ var collection = values as ICollection;
|
|
|
+ if (collection != null)
|
|
|
+ {
|
|
|
+ var extraCount = collection.Count;
|
|
|
+ // For reference types and nullable value types, we need to check that there are no nulls
|
|
|
+ // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
|
|
|
+ // We expect the JITter to optimize this test to true/false, so it's effectively conditional
|
|
|
+ // specialization.
|
|
|
+ if (default(T) == null)
|
|
|
+ {
|
|
|
+ // TODO: Measure whether iterating once to check and then letting the collection copy
|
|
|
+ // itself is faster or slower than iterating and adding as we go. For large
|
|
|
+ // collections this will not be great in terms of cache usage... but the optimized
|
|
|
+ // copy may be significantly faster than doing it one at a time.
|
|
|
+ foreach (var item in collection)
|
|
|
+ {
|
|
|
+ if (item == null)
|
|
|
+ {
|
|
|
+ throw new ArgumentException("Sequence contained null element", nameof(values));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ EnsureSize(count + extraCount);
|
|
|
+ collection.CopyTo(array, count);
|
|
|
+ count += extraCount;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // We *could* check for ICollection<T> as well, but very very few collections implement
|
|
|
+ // ICollection<T> but not ICollection. (HashSet<T> does, for one...)
|
|
|
+
|
|
|
+ // Fall back to a slower path of adding items one at a time.
|
|
|
+ foreach (T item in values)
|
|
|
+ {
|
|
|
+ Add(item);
|
|
|
}
|
|
|
- EnsureSize(count + values.count);
|
|
|
- // We know that all the values will be valid, because it's a RepeatedField.
|
|
|
- Array.Copy(values.array, 0, array, count, values.count);
|
|
|
- count += values.count;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Adds all of the specified values into this collection.
|
|
|
+ /// Adds all of the specified values into this collection. This method is present to
|
|
|
+ /// allow repeated fields to be constructed from queries within collection initializers.
|
|
|
+ /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
|
|
|
+ /// method instead for clarity.
|
|
|
/// </summary>
|
|
|
/// <param name="values">The values to add to this collection.</param>
|
|
|
public void Add(IEnumerable<T> values)
|
|
|
{
|
|
|
- if (values == null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException("values");
|
|
|
- }
|
|
|
- // TODO: Check for ICollection and get the Count, to optimize?
|
|
|
- foreach (T item in values)
|
|
|
- {
|
|
|
- Add(item);
|
|
|
- }
|
|
|
+ AddRange(values);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
@@ -418,10 +450,7 @@ namespace Google.Protobuf.Collections
|
|
|
/// <returns>The zero-based index of the item, or -1 if it is not found.</returns>
|
|
|
public int IndexOf(T item)
|
|
|
{
|
|
|
- if (item == null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException("item");
|
|
|
- }
|
|
|
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
|
|
|
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
|
|
for (int i = 0; i < count; i++)
|
|
|
{
|
|
@@ -440,13 +469,10 @@ namespace Google.Protobuf.Collections
|
|
|
/// <param name="item">The item to insert.</param>
|
|
|
public void Insert(int index, T item)
|
|
|
{
|
|
|
- if (item == null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException("item");
|
|
|
- }
|
|
|
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
|
|
|
if (index < 0 || index > count)
|
|
|
{
|
|
|
- throw new ArgumentOutOfRangeException("index");
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
}
|
|
|
EnsureSize(count + 1);
|
|
|
Array.Copy(array, index, array, index + 1, count - index);
|
|
@@ -462,7 +488,7 @@ namespace Google.Protobuf.Collections
|
|
|
{
|
|
|
if (index < 0 || index >= count)
|
|
|
{
|
|
|
- throw new ArgumentOutOfRangeException("index");
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
}
|
|
|
Array.Copy(array, index + 1, array, index, count - index - 1);
|
|
|
count--;
|
|
@@ -494,7 +520,7 @@ namespace Google.Protobuf.Collections
|
|
|
{
|
|
|
if (index < 0 || index >= count)
|
|
|
{
|
|
|
- throw new ArgumentOutOfRangeException("index");
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
}
|
|
|
return array[index];
|
|
|
}
|
|
@@ -502,27 +528,24 @@ namespace Google.Protobuf.Collections
|
|
|
{
|
|
|
if (index < 0 || index >= count)
|
|
|
{
|
|
|
- throw new ArgumentOutOfRangeException("index");
|
|
|
- }
|
|
|
- if (value == null)
|
|
|
- {
|
|
|
- throw new ArgumentNullException("value");
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
}
|
|
|
+ ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
|
|
|
array[index] = value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#region Explicit interface implementation for IList and ICollection.
|
|
|
- bool IList.IsFixedSize { get { return false; } }
|
|
|
+ bool IList.IsFixedSize => false;
|
|
|
|
|
|
void ICollection.CopyTo(Array array, int index)
|
|
|
{
|
|
|
Array.Copy(this.array, 0, array, index, count);
|
|
|
}
|
|
|
|
|
|
- bool ICollection.IsSynchronized { get { return false; } }
|
|
|
+ bool ICollection.IsSynchronized => false;
|
|
|
|
|
|
- object ICollection.SyncRoot { get { return this; } }
|
|
|
+ object ICollection.SyncRoot => this;
|
|
|
|
|
|
object IList.this[int index]
|
|
|
{
|