RepeatedField.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace Google.Protobuf.Collections
  5. {
  6. public sealed class RepeatedField<T> : IList<T>, IEquatable<RepeatedField<T>>
  7. {
  8. private static readonly T[] EmptyArray = new T[0];
  9. private const int MinArraySize = 8;
  10. private T[] array = EmptyArray;
  11. private int count = 0;
  12. /// <summary>
  13. /// Creates a deep clone of this repeated field.
  14. /// </summary>
  15. /// <remarks>
  16. /// If the field type is
  17. /// a message type, each element is also cloned; otherwise, it is
  18. /// assumed that the field type is primitive (including string and
  19. /// bytes, both of which are immutable) and so a simple copy is
  20. /// equivalent to a deep clone.
  21. /// </remarks>
  22. /// <returns>A deep clone of this repeated field.</returns>
  23. public RepeatedField<T> Clone()
  24. {
  25. RepeatedField<T> clone = new RepeatedField<T>();
  26. if (array != EmptyArray)
  27. {
  28. clone.array = (T[])array.Clone();
  29. IDeepCloneable<T>[] cloneableArray = clone.array as IDeepCloneable<T>[];
  30. if (cloneableArray != null)
  31. {
  32. for (int i = 0; i < count; i++)
  33. {
  34. clone.array[i] = cloneableArray[i].Clone();
  35. }
  36. }
  37. }
  38. clone.count = count;
  39. return clone;
  40. }
  41. private void EnsureSize(int size)
  42. {
  43. size = Math.Max(size, MinArraySize);
  44. if (array.Length < size)
  45. {
  46. int newSize = Math.Max(array.Length * 2, size);
  47. var tmp = new T[newSize];
  48. Array.Copy(array, 0, tmp, 0, array.Length);
  49. array = tmp;
  50. }
  51. }
  52. public void Add(T item)
  53. {
  54. if (item == null)
  55. {
  56. throw new ArgumentNullException("item");
  57. }
  58. EnsureSize(count + 1);
  59. array[count++] = item;
  60. }
  61. /// <summary>
  62. /// Hack to allow us to add enums easily... will only work with int-based types.
  63. /// </summary>
  64. /// <param name="readEnum"></param>
  65. internal void AddInt32(int item)
  66. {
  67. EnsureSize(count + 1);
  68. int[] castArray = (int[]) (object) array;
  69. castArray[count++] = item;
  70. }
  71. public void Clear()
  72. {
  73. array = EmptyArray;
  74. count = 0;
  75. }
  76. public bool Contains(T item)
  77. {
  78. return IndexOf(item) != -1;
  79. }
  80. public void CopyTo(T[] array, int arrayIndex)
  81. {
  82. Array.Copy(this.array, 0, array, arrayIndex, count);
  83. }
  84. public bool Remove(T item)
  85. {
  86. int index = IndexOf(item);
  87. if (index == -1)
  88. {
  89. return false;
  90. }
  91. Array.Copy(array, index + 1, array, index, count - index - 1);
  92. count--;
  93. array[count] = default(T);
  94. return true;
  95. }
  96. public int Count { get { return count; } }
  97. // TODO(jonskeet): If we implement freezing, make this reflect it.
  98. public bool IsReadOnly { get { return false; } }
  99. public void Add(RepeatedField<T> values)
  100. {
  101. if (values == null)
  102. {
  103. throw new ArgumentNullException("values");
  104. }
  105. EnsureSize(count + values.count);
  106. // We know that all the values will be valid, because it's a RepeatedField.
  107. Array.Copy(values.array, 0, array, count, values.count);
  108. count += values.count;
  109. }
  110. public void Add(IEnumerable<T> values)
  111. {
  112. if (values == null)
  113. {
  114. throw new ArgumentNullException("values");
  115. }
  116. // TODO: Check for ICollection and get the Count?
  117. foreach (T item in values)
  118. {
  119. Add(item);
  120. }
  121. }
  122. public RepeatedField<T>.Enumerator GetEnumerator()
  123. {
  124. return new Enumerator(this);
  125. }
  126. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  127. {
  128. return GetEnumerator();
  129. }
  130. public override bool Equals(object obj)
  131. {
  132. return Equals(obj as RepeatedField<T>);
  133. }
  134. IEnumerator IEnumerable.GetEnumerator()
  135. {
  136. return GetEnumerator();
  137. }
  138. /// <summary>
  139. /// Returns an enumerator of the values in this list as integers.
  140. /// Used for enum types.
  141. /// </summary>
  142. internal Int32Enumerator GetInt32Enumerator()
  143. {
  144. return new Int32Enumerator((int[])(object)array, count);
  145. }
  146. public override int GetHashCode()
  147. {
  148. int hash = 23;
  149. for (int i = 0; i < count; i++)
  150. {
  151. hash = hash * 31 + array[i].GetHashCode();
  152. }
  153. return hash;
  154. }
  155. public bool Equals(RepeatedField<T> other)
  156. {
  157. if (ReferenceEquals(other, null))
  158. {
  159. return false;
  160. }
  161. if (ReferenceEquals(other, this))
  162. {
  163. return true;
  164. }
  165. if (other.Count != this.Count)
  166. {
  167. return false;
  168. }
  169. // TODO(jonskeet): Does this box for enums?
  170. EqualityComparer<T> comparer = EqualityComparer<T>.Default;
  171. for (int i = 0; i < count; i++)
  172. {
  173. if (!comparer.Equals(array[i], other.array[i]))
  174. {
  175. return false;
  176. }
  177. }
  178. return true;
  179. }
  180. public int IndexOf(T item)
  181. {
  182. if (item == null)
  183. {
  184. throw new ArgumentNullException("item");
  185. }
  186. // TODO(jonskeet): Does this box for enums?
  187. EqualityComparer<T> comparer = EqualityComparer<T>.Default;
  188. for (int i = 0; i < count; i++)
  189. {
  190. if (comparer.Equals(array[i], item))
  191. {
  192. return i;
  193. }
  194. }
  195. return -1;
  196. }
  197. public void Insert(int index, T item)
  198. {
  199. if (item == null)
  200. {
  201. throw new ArgumentNullException("item");
  202. }
  203. if (index < 0 || index > count)
  204. {
  205. throw new ArgumentOutOfRangeException("index");
  206. }
  207. EnsureSize(count + 1);
  208. Array.Copy(array, index, array, index + 1, count - index);
  209. count++;
  210. }
  211. public void RemoveAt(int index)
  212. {
  213. if (index < 0 || index >= count)
  214. {
  215. throw new ArgumentOutOfRangeException("index");
  216. }
  217. Array.Copy(array, index + 1, array, index, count - index - 1);
  218. count--;
  219. array[count] = default(T);
  220. }
  221. public T this[int index]
  222. {
  223. get
  224. {
  225. if (index < 0 || index >= count)
  226. {
  227. throw new ArgumentOutOfRangeException("index");
  228. }
  229. return array[index];
  230. }
  231. set
  232. {
  233. if (index < 0 || index >= count)
  234. {
  235. throw new ArgumentOutOfRangeException("index");
  236. }
  237. if (value == null)
  238. {
  239. throw new ArgumentNullException("value");
  240. }
  241. array[index] = value;
  242. }
  243. }
  244. public struct Enumerator : IEnumerator<T>
  245. {
  246. private int index;
  247. private readonly RepeatedField<T> field;
  248. public Enumerator(RepeatedField<T> field)
  249. {
  250. this.field = field;
  251. this.index = -1;
  252. }
  253. public bool MoveNext()
  254. {
  255. if (index + 1 >= field.Count)
  256. {
  257. return false;
  258. }
  259. index++;
  260. return true;
  261. }
  262. public void Reset()
  263. {
  264. index = -1;
  265. }
  266. public T Current
  267. {
  268. get
  269. {
  270. if (index == -1 || index >= field.count)
  271. {
  272. throw new InvalidOperationException();
  273. }
  274. return field.array[index];
  275. }
  276. }
  277. object IEnumerator.Current
  278. {
  279. get { return Current; }
  280. }
  281. public void Dispose()
  282. {
  283. }
  284. }
  285. internal struct Int32Enumerator : IEnumerator<int>
  286. {
  287. private int index;
  288. private readonly int[] array;
  289. private readonly int count;
  290. public Int32Enumerator(int[] array, int count)
  291. {
  292. this.array = array;
  293. this.index = -1;
  294. this.count = count;
  295. }
  296. public bool MoveNext()
  297. {
  298. if (index + 1 >= count)
  299. {
  300. return false;
  301. }
  302. index++;
  303. return true;
  304. }
  305. public void Reset()
  306. {
  307. index = -1;
  308. }
  309. // No guard here, as we're only going to use this internally...
  310. public int Current { get { return array[index]; } }
  311. object IEnumerator.Current
  312. {
  313. get { return Current; }
  314. }
  315. public void Dispose()
  316. {
  317. }
  318. }
  319. }
  320. }