using System; using System.Collections; using System.Collections.Generic; namespace Google.Protobuf.Collections { public sealed class RepeatedField : IList, IEquatable> { private static readonly T[] EmptyArray = new T[0]; private const int MinArraySize = 8; private T[] array = EmptyArray; private int count = 0; /// /// Creates a deep clone of this repeated field. /// /// /// If the field type is /// a message type, each element is also cloned; otherwise, it is /// assumed that the field type is primitive (including string and /// bytes, both of which are immutable) and so a simple copy is /// equivalent to a deep clone. /// /// A deep clone of this repeated field. public RepeatedField Clone() { RepeatedField clone = new RepeatedField(); if (array != EmptyArray) { clone.array = (T[])array.Clone(); IDeepCloneable[] cloneableArray = clone.array as IDeepCloneable[]; if (cloneableArray != null) { for (int i = 0; i < count; i++) { clone.array[i] = cloneableArray[i].Clone(); } } } clone.count = count; return clone; } private void EnsureSize(int size) { size = Math.Max(size, MinArraySize); if (array.Length < size) { int newSize = Math.Max(array.Length * 2, size); var tmp = new T[newSize]; Array.Copy(array, 0, tmp, 0, array.Length); array = tmp; } } public void Add(T item) { if (item == null) { throw new ArgumentNullException("item"); } EnsureSize(count + 1); array[count++] = item; } /// /// Hack to allow us to add enums easily... will only work with int-based types. /// /// internal void AddInt32(int item) { EnsureSize(count + 1); int[] castArray = (int[]) (object) array; castArray[count++] = item; } public void Clear() { array = EmptyArray; count = 0; } public bool Contains(T item) { return IndexOf(item) != -1; } public void CopyTo(T[] array, int arrayIndex) { Array.Copy(this.array, 0, array, arrayIndex, count); } public bool Remove(T item) { int index = IndexOf(item); if (index == -1) { return false; } Array.Copy(array, index + 1, array, index, count - index - 1); count--; array[count] = default(T); return true; } public int Count { get { return count; } } // TODO(jonskeet): If we implement freezing, make this reflect it. public bool IsReadOnly { get { return false; } } public void Add(RepeatedField values) { if (values == null) { throw new ArgumentNullException("values"); } 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; } public void Add(IEnumerable values) { if (values == null) { throw new ArgumentNullException("values"); } // TODO: Check for ICollection and get the Count? foreach (T item in values) { Add(item); } } public RepeatedField.Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override bool Equals(object obj) { return Equals(obj as RepeatedField); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Returns an enumerator of the values in this list as integers. /// Used for enum types. /// internal Int32Enumerator GetInt32Enumerator() { return new Int32Enumerator((int[])(object)array, count); } public override int GetHashCode() { int hash = 23; for (int i = 0; i < count; i++) { hash = hash * 31 + array[i].GetHashCode(); } return hash; } public bool Equals(RepeatedField other) { if (ReferenceEquals(other, null)) { return false; } if (ReferenceEquals(other, this)) { return true; } if (other.Count != this.Count) { return false; } // TODO(jonskeet): Does this box for enums? EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < count; i++) { if (!comparer.Equals(array[i], other.array[i])) { return false; } } return true; } public int IndexOf(T item) { if (item == null) { throw new ArgumentNullException("item"); } // TODO(jonskeet): Does this box for enums? EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < count; i++) { if (comparer.Equals(array[i], item)) { return i; } } return -1; } public void Insert(int index, T item) { if (item == null) { throw new ArgumentNullException("item"); } if (index < 0 || index > count) { throw new ArgumentOutOfRangeException("index"); } EnsureSize(count + 1); Array.Copy(array, index, array, index + 1, count - index); count++; } public void RemoveAt(int index) { if (index < 0 || index >= count) { throw new ArgumentOutOfRangeException("index"); } Array.Copy(array, index + 1, array, index, count - index - 1); count--; array[count] = default(T); } public T this[int index] { get { if (index < 0 || index >= count) { throw new ArgumentOutOfRangeException("index"); } return array[index]; } set { if (index < 0 || index >= count) { throw new ArgumentOutOfRangeException("index"); } if (value == null) { throw new ArgumentNullException("value"); } array[index] = value; } } public struct Enumerator : IEnumerator { private int index; private readonly RepeatedField field; public Enumerator(RepeatedField field) { this.field = field; this.index = -1; } public bool MoveNext() { if (index + 1 >= field.Count) { return false; } index++; return true; } public void Reset() { index = -1; } public T Current { get { if (index == -1 || index >= field.count) { throw new InvalidOperationException(); } return field.array[index]; } } object IEnumerator.Current { get { return Current; } } public void Dispose() { } } internal struct Int32Enumerator : IEnumerator { private int index; private readonly int[] array; private readonly int count; public Int32Enumerator(int[] array, int count) { this.array = array; this.index = -1; this.count = count; } public bool MoveNext() { if (index + 1 >= count) { return false; } index++; return true; } public void Reset() { index = -1; } // No guard here, as we're only going to use this internally... public int Current { get { return array[index]; } } object IEnumerator.Current { get { return Current; } } public void Dispose() { } } } }