Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

InlinedArray.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. ////REVIEW: what about ignoring 'firstValue' entirely in case length > 1 and putting everything into an array in that case
  6. namespace UnityEngine.InputSystem.Utilities
  7. {
  8. /// <summary>
  9. /// Helper to avoid array allocations if there's only a single value in the array.
  10. /// </summary>
  11. /// <remarks>
  12. /// Also, once more than one entry is necessary, allows treating the extra array as having capacity.
  13. /// This means that, for example, 5 or 10 entries can be allocated in batch rather than growing an
  14. /// array one by one.
  15. /// </remarks>
  16. /// <typeparam name="TValue">Element type for the array.</typeparam>
  17. internal struct InlinedArray<TValue> : IEnumerable<TValue>
  18. {
  19. // We inline the first value so if there's only one, there's
  20. // no additional allocation. If more are added, we allocate an array.
  21. public int length;
  22. public TValue firstValue;
  23. public TValue[] additionalValues;
  24. public int Capacity => additionalValues?.Length + 1 ?? 1;
  25. public InlinedArray(TValue value)
  26. {
  27. length = 1;
  28. firstValue = value;
  29. additionalValues = null;
  30. }
  31. public InlinedArray(TValue firstValue, params TValue[] additionalValues)
  32. {
  33. length = 1 + additionalValues.Length;
  34. this.firstValue = firstValue;
  35. this.additionalValues = additionalValues;
  36. }
  37. public InlinedArray(IEnumerable<TValue> values)
  38. : this()
  39. {
  40. length = values.Count();
  41. if (length > 1)
  42. additionalValues = new TValue[length - 1];
  43. else
  44. additionalValues = null;
  45. var index = 0;
  46. foreach (var value in values)
  47. {
  48. if (index == 0)
  49. firstValue = value;
  50. else
  51. additionalValues[index - 1] = value;
  52. ++index;
  53. }
  54. }
  55. public TValue this[int index]
  56. {
  57. get
  58. {
  59. if (index < 0 || index >= length)
  60. throw new ArgumentOutOfRangeException(nameof(index));
  61. if (index == 0)
  62. return firstValue;
  63. return additionalValues[index - 1];
  64. }
  65. set
  66. {
  67. if (index < 0 || index >= length)
  68. throw new ArgumentOutOfRangeException(nameof(index));
  69. if (index == 0)
  70. firstValue = value;
  71. else
  72. additionalValues[index - 1] = value;
  73. }
  74. }
  75. public void Clear()
  76. {
  77. length = 0;
  78. firstValue = default;
  79. additionalValues = null;
  80. }
  81. public void ClearWithCapacity()
  82. {
  83. firstValue = default;
  84. for (var i = 0; i < length - 1; ++i)
  85. additionalValues[i] = default;
  86. length = 0;
  87. }
  88. ////REVIEW: This is inconsistent with ArrayHelpers.Clone() which also clones elements
  89. public InlinedArray<TValue> Clone()
  90. {
  91. return new InlinedArray<TValue>
  92. {
  93. length = length,
  94. firstValue = firstValue,
  95. additionalValues = additionalValues != null ? ArrayHelpers.Copy(additionalValues) : null
  96. };
  97. }
  98. public void SetLength(int size)
  99. {
  100. // Null out everything we're cutting off.
  101. if (size < length)
  102. {
  103. for (var i = size; i < length; ++i)
  104. this[i] = default;
  105. }
  106. length = size;
  107. if (size > 1 && (additionalValues == null || additionalValues.Length < size - 1))
  108. Array.Resize(ref additionalValues, size - 1);
  109. }
  110. public TValue[] ToArray()
  111. {
  112. return ArrayHelpers.Join(firstValue, additionalValues);
  113. }
  114. public TOther[] ToArray<TOther>(Func<TValue, TOther> mapFunction)
  115. {
  116. if (length == 0)
  117. return null;
  118. var result = new TOther[length];
  119. for (var i = 0; i < length; ++i)
  120. result[i] = mapFunction(this[i]);
  121. return result;
  122. }
  123. public int IndexOf(TValue value)
  124. {
  125. var comparer = EqualityComparer<TValue>.Default;
  126. if (length > 0)
  127. {
  128. if (comparer.Equals(firstValue, value))
  129. return 0;
  130. if (additionalValues != null)
  131. {
  132. for (var i = 0; i < length - 1; ++i)
  133. if (comparer.Equals(additionalValues[i], value))
  134. return i + 1;
  135. }
  136. }
  137. return -1;
  138. }
  139. public int Append(TValue value)
  140. {
  141. if (length == 0)
  142. {
  143. firstValue = value;
  144. }
  145. else if (additionalValues == null)
  146. {
  147. additionalValues = new TValue[1];
  148. additionalValues[0] = value;
  149. }
  150. else
  151. {
  152. Array.Resize(ref additionalValues, length);
  153. additionalValues[length - 1] = value;
  154. }
  155. var index = length;
  156. ++length;
  157. return index;
  158. }
  159. public int AppendWithCapacity(TValue value, int capacityIncrement = 10)
  160. {
  161. if (length == 0)
  162. {
  163. firstValue = value;
  164. }
  165. else
  166. {
  167. var numAdditionalValues = length - 1;
  168. ArrayHelpers.AppendWithCapacity(ref additionalValues, ref numAdditionalValues, value, capacityIncrement: capacityIncrement);
  169. }
  170. var index = length;
  171. ++length;
  172. return index;
  173. }
  174. public void AssignWithCapacity(InlinedArray<TValue> values)
  175. {
  176. if (Capacity < values.length && values.length > 1)
  177. additionalValues = new TValue[values.length - 1];
  178. length = values.length;
  179. if (length > 0)
  180. firstValue = values.firstValue;
  181. if (length > 1)
  182. Array.Copy(values.additionalValues, additionalValues, length - 1);
  183. }
  184. public void Append(IEnumerable<TValue> values)
  185. {
  186. foreach (var value in values)
  187. Append(value);
  188. }
  189. public void Remove(TValue value)
  190. {
  191. if (length < 1)
  192. return;
  193. if (EqualityComparer<TValue>.Default.Equals(firstValue, value))
  194. {
  195. RemoveAt(0);
  196. }
  197. else if (additionalValues != null)
  198. {
  199. for (var i = 0; i < length - 1; ++i)
  200. {
  201. if (EqualityComparer<TValue>.Default.Equals(additionalValues[i], value))
  202. {
  203. RemoveAt(i + 1);
  204. break;
  205. }
  206. }
  207. }
  208. }
  209. public void RemoveAtWithCapacity(int index)
  210. {
  211. if (index < 0 || index >= length)
  212. throw new ArgumentOutOfRangeException(nameof(index));
  213. if (index == 0)
  214. {
  215. if (length == 1)
  216. {
  217. firstValue = default;
  218. }
  219. else if (length == 2)
  220. {
  221. firstValue = additionalValues[0];
  222. additionalValues[0] = default;
  223. }
  224. else
  225. {
  226. Debug.Assert(length > 2);
  227. firstValue = additionalValues[0];
  228. var numAdditional = length - 1;
  229. ArrayHelpers.EraseAtWithCapacity(additionalValues, ref numAdditional, 0);
  230. }
  231. }
  232. else
  233. {
  234. var numAdditional = length - 1;
  235. ArrayHelpers.EraseAtWithCapacity(additionalValues, ref numAdditional, index - 1);
  236. }
  237. --length;
  238. }
  239. public void RemoveAt(int index)
  240. {
  241. if (index < 0 || index >= length)
  242. throw new ArgumentOutOfRangeException(nameof(index));
  243. if (index == 0)
  244. {
  245. if (additionalValues != null)
  246. {
  247. firstValue = additionalValues[0];
  248. if (additionalValues.Length == 1)
  249. additionalValues = null;
  250. else
  251. {
  252. Array.Copy(additionalValues, 1, additionalValues, 0, additionalValues.Length - 1);
  253. Array.Resize(ref additionalValues, additionalValues.Length - 1);
  254. }
  255. }
  256. else
  257. {
  258. firstValue = default;
  259. }
  260. }
  261. else
  262. {
  263. Debug.Assert(additionalValues != null);
  264. var numAdditionalValues = length - 1;
  265. if (numAdditionalValues == 1)
  266. {
  267. // Remove only entry in array.
  268. additionalValues = null;
  269. }
  270. else if (index == length - 1)
  271. {
  272. // Remove entry at end.
  273. Array.Resize(ref additionalValues, numAdditionalValues - 1);
  274. }
  275. else
  276. {
  277. // Remove entry at beginning or in middle by pasting together
  278. // into a new array.
  279. var newAdditionalValues = new TValue[numAdditionalValues - 1];
  280. if (index >= 2)
  281. {
  282. // Copy elements before entry.
  283. Array.Copy(additionalValues, 0, newAdditionalValues, 0, index - 1);
  284. }
  285. // Copy elements after entry. We already know that we're not removing
  286. // the last entry so there have to be entries.
  287. Array.Copy(additionalValues, index + 1 - 1, newAdditionalValues, index - 1,
  288. length - index - 1);
  289. additionalValues = newAdditionalValues;
  290. }
  291. }
  292. --length;
  293. }
  294. public void RemoveAtByMovingTailWithCapacity(int index)
  295. {
  296. if (index < 0 || index >= length)
  297. throw new ArgumentOutOfRangeException(nameof(index));
  298. var numAdditionalValues = length - 1;
  299. if (index == 0)
  300. {
  301. if (length > 1)
  302. {
  303. firstValue = additionalValues[numAdditionalValues - 1];
  304. additionalValues[numAdditionalValues - 1] = default;
  305. }
  306. else
  307. {
  308. firstValue = default;
  309. }
  310. }
  311. else
  312. {
  313. Debug.Assert(additionalValues != null);
  314. ArrayHelpers.EraseAtByMovingTail(additionalValues, ref numAdditionalValues, index - 1);
  315. }
  316. --length;
  317. }
  318. public bool RemoveByMovingTailWithCapacity(TValue value)
  319. {
  320. var index = IndexOf(value);
  321. if (index == -1)
  322. return false;
  323. RemoveAtByMovingTailWithCapacity(index);
  324. return true;
  325. }
  326. public bool Contains(TValue value, IEqualityComparer<TValue> comparer)
  327. {
  328. for (var n = 0; n < length; ++n)
  329. if (comparer.Equals(this[n], value))
  330. return true;
  331. return false;
  332. }
  333. public void Merge(InlinedArray<TValue> other)
  334. {
  335. var comparer = EqualityComparer<TValue>.Default;
  336. for (var i = 0; i < other.length; ++i)
  337. {
  338. var value = other[i];
  339. if (Contains(value, comparer))
  340. continue;
  341. ////FIXME: this is ugly as it repeatedly copies
  342. Append(value);
  343. }
  344. }
  345. public IEnumerator<TValue> GetEnumerator()
  346. {
  347. return new Enumerator { array = this, index = -1 };
  348. }
  349. IEnumerator IEnumerable.GetEnumerator()
  350. {
  351. return GetEnumerator();
  352. }
  353. private struct Enumerator : IEnumerator<TValue>
  354. {
  355. public InlinedArray<TValue> array;
  356. public int index;
  357. public bool MoveNext()
  358. {
  359. if (index >= array.length)
  360. return false;
  361. ++index;
  362. return index < array.length;
  363. }
  364. public void Reset()
  365. {
  366. index = -1;
  367. }
  368. public TValue Current => array[index];
  369. object IEnumerator.Current => Current;
  370. public void Dispose()
  371. {
  372. }
  373. }
  374. }
  375. internal static class InputArrayExtensions
  376. {
  377. public static int IndexOfReference<TValue>(this InlinedArray<TValue> array, TValue value)
  378. where TValue : class
  379. {
  380. for (var i = 0; i < array.length; ++i)
  381. if (ReferenceEquals(array[i], value))
  382. return i;
  383. return -1;
  384. }
  385. public static bool Contains<TValue>(this InlinedArray<TValue> array, TValue value)
  386. {
  387. for (var i = 0; i < array.length; ++i)
  388. if (array[i].Equals(value))
  389. return true;
  390. return false;
  391. }
  392. public static bool ContainsReference<TValue>(this InlinedArray<TValue> array, TValue value)
  393. where TValue : class
  394. {
  395. return IndexOfReference(array, value) != -1;
  396. }
  397. }
  398. }