Array.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using Internal.Runtime.CompilerServices;
  5. #if BIT64
  6. using nuint = System.UInt64;
  7. #else
  8. using nuint = System.UInt32;
  9. #endif
  10. namespace System
  11. {
  12. public class Array
  13. {
  14. // We impose limits on maximum array lenght in each dimension to allow efficient
  15. // implementation of advanced range check elimination in future.
  16. // Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength.
  17. // The constants are defined in this method: inline SIZE_T MaxArrayLength(SIZE_T componentSize) from gcscan
  18. // We have different max sizes for arrays with elements of size 1 for backwards compatibility
  19. internal const int MaxArrayLength = 0X7FEFFFFF;
  20. internal const int MaxByteArrayLength = 0x7FFFFFC7;
  21. public extern long LongLength
  22. {
  23. [MethodImpl(MethodImplOptions.InternalCall)]
  24. get;
  25. }
  26. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  27. public extern int GetLength(int dimension);
  28. public long GetLongLength(int dimension)
  29. {
  30. //This method should throw an IndexOufOfRangeException for compat if dimension < 0 or >= Rank
  31. return GetLength(dimension);
  32. }
  33. public extern int Rank
  34. {
  35. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  36. get;
  37. }
  38. public extern int Length
  39. {
  40. [MethodImpl(MethodImplOptions.InternalCall)]
  41. get;
  42. }
  43. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  44. public extern int GetUpperBound(int dimension);
  45. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  46. public extern int GetLowerBound(int dimension);
  47. private static class EmptyArray<T>
  48. {
  49. internal static readonly T[] Value = new T[0];
  50. }
  51. public static T[] Empty<T>()
  52. {
  53. return EmptyArray<T>.Value;
  54. }
  55. // Copies length elements from sourceArray, starting at index 0, to
  56. // destinationArray, starting at index 0.
  57. //
  58. public static void Copy(Array sourceArray, Array destinationArray, int length)
  59. {
  60. if (sourceArray == null)
  61. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
  62. if (destinationArray == null)
  63. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
  64. Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, false);
  65. }
  66. // Copies length elements from sourceArray, starting at sourceIndex, to
  67. // destinationArray, starting at destinationIndex.
  68. //
  69. public static void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
  70. {
  71. Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, false);
  72. }
  73. // Reliability-wise, this method will either possibly corrupt your
  74. // instance & might fail when called from within a CER, or if the
  75. // reliable flag is true, it will either always succeed or always
  76. // throw an exception with no side effects.
  77. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  78. internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);
  79. // Provides a strong exception guarantee - either it succeeds, or
  80. // it throws an exception with no side effects. The arrays must be
  81. // compatible array types based on the array element type - this
  82. // method does not support casting, boxing, or primitive widening.
  83. // It will up-cast, assuming the array types are correct.
  84. public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
  85. {
  86. Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, true);
  87. }
  88. public static void Copy(Array sourceArray, Array destinationArray, long length)
  89. {
  90. if (length > int.MaxValue || length < int.MinValue)
  91. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  92. Array.Copy(sourceArray, destinationArray, (int)length);
  93. }
  94. public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length)
  95. {
  96. if (sourceIndex > int.MaxValue || sourceIndex < int.MinValue)
  97. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  98. if (destinationIndex > int.MaxValue || destinationIndex < int.MinValue)
  99. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.destinationIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  100. if (length > int.MaxValue || length < int.MinValue)
  101. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  102. Array.Copy(sourceArray, (int)sourceIndex, destinationArray, (int)destinationIndex, (int)length);
  103. }
  104. public static void Resize<T>(ref T[] array, int newSize)
  105. {
  106. if (newSize < 0)
  107. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.newSize, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
  108. T[] larray = array;
  109. if (larray == null)
  110. {
  111. array = new T[newSize];
  112. return;
  113. }
  114. if (larray.Length != newSize)
  115. {
  116. T[] newArray = new T[newSize];
  117. Array.Copy(larray, 0, newArray, 0, larray.Length > newSize ? newSize : larray.Length);
  118. array = newArray;
  119. }
  120. }
  121. // Make a new array which is a shallow copy of the original array.
  122. //
  123. public object Clone()
  124. {
  125. return MemberwiseClone();
  126. }
  127. public static int BinarySearch<T>(T[] array, T value)
  128. {
  129. if (array == null)
  130. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  131. return BinarySearch<T>(array, 0, array.Length, value, null);
  132. }
  133. public static int BinarySearch<T>(T[] array, T value, System.Collections.Generic.IComparer<T>? comparer)
  134. {
  135. if (array == null)
  136. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  137. return BinarySearch<T>(array, 0, array.Length, value, comparer);
  138. }
  139. public static int BinarySearch<T>(T[] array, int index, int length, T value)
  140. {
  141. return BinarySearch<T>(array, index, length, value, null);
  142. }
  143. public static int BinarySearch<T>(T[] array, int index, int length, T value, System.Collections.Generic.IComparer<T>? comparer)
  144. {
  145. if (array == null)
  146. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  147. if (index < 0)
  148. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  149. if (length < 0)
  150. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  151. if (array.Length - index < length)
  152. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  153. return ArraySortHelper<T>.Default.BinarySearch(array, index, length, value, comparer);
  154. }
  155. public static int IndexOf<T>(T[] array, T value)
  156. {
  157. if (array == null)
  158. {
  159. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  160. }
  161. return IndexOf(array, value, 0, array.Length);
  162. }
  163. public static int IndexOf<T>(T[] array, T value, int startIndex)
  164. {
  165. if (array == null)
  166. {
  167. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  168. }
  169. return IndexOf(array, value, startIndex, array.Length - startIndex);
  170. }
  171. public static int IndexOf<T>(T[] array, T value, int startIndex, int count)
  172. {
  173. if (array == null)
  174. {
  175. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  176. }
  177. if ((uint)startIndex > (uint)array.Length)
  178. {
  179. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  180. }
  181. if ((uint)count > (uint)(array.Length - startIndex))
  182. {
  183. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  184. }
  185. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  186. {
  187. if (Unsafe.SizeOf<T>() == sizeof(byte))
  188. {
  189. int result = SpanHelpers.IndexOf(
  190. ref Unsafe.Add(ref array.GetRawSzArrayData(), startIndex),
  191. Unsafe.As<T, byte>(ref value),
  192. count);
  193. return (result >= 0 ? startIndex : 0) + result;
  194. }
  195. else if (Unsafe.SizeOf<T>() == sizeof(char))
  196. {
  197. int result = SpanHelpers.IndexOf(
  198. ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), startIndex),
  199. Unsafe.As<T, char>(ref value),
  200. count);
  201. return (result >= 0 ? startIndex : 0) + result;
  202. }
  203. else if (Unsafe.SizeOf<T>() == sizeof(int))
  204. {
  205. int result = SpanHelpers.IndexOf(
  206. ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), startIndex),
  207. Unsafe.As<T, int>(ref value),
  208. count);
  209. return (result >= 0 ? startIndex : 0) + result;
  210. }
  211. else if (Unsafe.SizeOf<T>() == sizeof(long))
  212. {
  213. int result = SpanHelpers.IndexOf(
  214. ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), startIndex),
  215. Unsafe.As<T, long>(ref value),
  216. count);
  217. return (result >= 0 ? startIndex : 0) + result;
  218. }
  219. }
  220. return EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);
  221. }
  222. public static void Sort<T>(T[] array)
  223. {
  224. if (array == null)
  225. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  226. Sort<T>(array, 0, array.Length, null);
  227. }
  228. public static void Sort<T>(T[] array, System.Collections.Generic.IComparer<T>? comparer)
  229. {
  230. if (array == null)
  231. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  232. Sort<T>(array, 0, array.Length, comparer);
  233. }
  234. public static void Sort<T>(T[] array, int index, int length, System.Collections.Generic.IComparer<T>? comparer)
  235. {
  236. if (array == null)
  237. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  238. if (index < 0)
  239. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  240. if (length < 0)
  241. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  242. if (array.Length - index < length)
  243. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  244. if (length > 1)
  245. {
  246. #if CORECLR
  247. if (comparer == null || comparer == Comparer<T>.Default)
  248. {
  249. if (TrySZSort(array, null, index, index + length - 1))
  250. {
  251. return;
  252. }
  253. }
  254. #endif
  255. ArraySortHelper<T>.Default.Sort(array, index, length, comparer);
  256. }
  257. }
  258. // Sets length elements in array to 0 (or null for Object arrays), starting
  259. // at index.
  260. //
  261. public static unsafe void Clear(Array array, int index, int length)
  262. {
  263. if (array == null)
  264. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  265. ref byte p = ref GetRawArrayGeometry(array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers);
  266. int offset = index - lowerBound;
  267. if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > numComponents)
  268. ThrowHelper.ThrowIndexOutOfRangeException();
  269. ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * (nuint)elementSize);
  270. nuint byteLength = (uint)length * (nuint)elementSize;
  271. if (containsGCPointers)
  272. SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));
  273. else
  274. SpanHelpers.ClearWithoutReferences(ref ptr, byteLength);
  275. }
  276. [MethodImpl(MethodImplOptions.InternalCall)]
  277. private static extern ref byte GetRawArrayGeometry(Array array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers);
  278. public static int LastIndexOf<T>(T[] array, T value)
  279. {
  280. if (array == null)
  281. {
  282. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  283. }
  284. return LastIndexOf(array, value, array.Length - 1, array.Length);
  285. }
  286. public static int LastIndexOf<T>(T[] array, T value, int startIndex)
  287. {
  288. if (array == null)
  289. {
  290. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  291. }
  292. // if array is empty and startIndex is 0, we need to pass 0 as count
  293. return LastIndexOf(array, value, startIndex, (array.Length == 0) ? 0 : (startIndex + 1));
  294. }
  295. public static int LastIndexOf<T>(T[] array, T value, int startIndex, int count)
  296. {
  297. if (array == null)
  298. {
  299. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  300. }
  301. if (array.Length == 0)
  302. {
  303. //
  304. // Special case for 0 length List
  305. // accept -1 and 0 as valid startIndex for compablility reason.
  306. //
  307. if (startIndex != -1 && startIndex != 0)
  308. {
  309. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  310. }
  311. // only 0 is a valid value for count if array is empty
  312. if (count != 0)
  313. {
  314. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  315. }
  316. return -1;
  317. }
  318. // Make sure we're not out of range
  319. if ((uint)startIndex >= (uint)array.Length)
  320. {
  321. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  322. }
  323. // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
  324. if (count < 0 || startIndex - count + 1 < 0)
  325. {
  326. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  327. }
  328. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  329. {
  330. if (Unsafe.SizeOf<T>() == sizeof(byte))
  331. {
  332. int endIndex = startIndex - count + 1;
  333. int result = SpanHelpers.LastIndexOf(
  334. ref Unsafe.Add(ref array.GetRawSzArrayData(), endIndex),
  335. Unsafe.As<T, byte>(ref value),
  336. count);
  337. return (result >= 0 ? endIndex : 0) + result;
  338. }
  339. else if (Unsafe.SizeOf<T>() == sizeof(char))
  340. {
  341. int endIndex = startIndex - count + 1;
  342. int result = SpanHelpers.LastIndexOf(
  343. ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), endIndex),
  344. Unsafe.As<T, char>(ref value),
  345. count);
  346. return (result >= 0 ? endIndex : 0) + result;
  347. }
  348. else if (Unsafe.SizeOf<T>() == sizeof(int))
  349. {
  350. int endIndex = startIndex - count + 1;
  351. int result = SpanHelpers.LastIndexOf(
  352. ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), endIndex),
  353. Unsafe.As<T, int>(ref value),
  354. count);
  355. return (result >= 0 ? endIndex : 0) + result;
  356. }
  357. else if (Unsafe.SizeOf<T>() == sizeof(long))
  358. {
  359. int endIndex = startIndex - count + 1;
  360. int result = SpanHelpers.LastIndexOf(
  361. ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), endIndex),
  362. Unsafe.As<T, long>(ref value),
  363. count);
  364. return (result >= 0 ? endIndex : 0) + result;
  365. }
  366. }
  367. return EqualityComparer<T>.Default.LastIndexOf(array, value, startIndex, count);
  368. }
  369. public static void Reverse<T>(T[] array)
  370. {
  371. if (array == null)
  372. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  373. Reverse(array, 0, array.Length);
  374. }
  375. public static void Reverse<T>(T[] array, int index, int length)
  376. {
  377. if (array == null)
  378. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  379. if (index < 0)
  380. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  381. if (length < 0)
  382. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  383. if (array.Length - index < length)
  384. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  385. if (length <= 1)
  386. return;
  387. ref T first = ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), index);
  388. ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, length), -1);
  389. do
  390. {
  391. T temp = first;
  392. first = last;
  393. last = temp;
  394. first = ref Unsafe.Add(ref first, 1);
  395. last = ref Unsafe.Add(ref last, -1);
  396. } while (Unsafe.IsAddressLessThan(ref first, ref last));
  397. }
  398. }
  399. }