Array.cs 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Collections.ObjectModel;
  7. using System.Diagnostics;
  8. using System.Diagnostics.CodeAnalysis;
  9. using System.Reflection;
  10. using System.Runtime.CompilerServices;
  11. using Internal.Runtime.CompilerServices;
  12. namespace System
  13. {
  14. [Serializable]
  15. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  16. public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable
  17. {
  18. // We impose limits on maximum array length in each dimension to allow efficient
  19. // implementation of advanced range check elimination in future.
  20. // Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength.
  21. // The constants are defined in this method: inline SIZE_T MaxArrayLength(SIZE_T componentSize) from gcscan
  22. // We have different max sizes for arrays with elements of size 1 for backwards compatibility
  23. internal const int MaxArrayLength = 0X7FEFFFFF;
  24. internal const int MaxByteArrayLength = 0x7FFFFFC7;
  25. // This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area.
  26. private protected Array() { }
  27. public static ReadOnlyCollection<T> AsReadOnly<T>(T[] array)
  28. {
  29. if (array == null)
  30. {
  31. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  32. }
  33. // T[] implements IList<T>.
  34. return new ReadOnlyCollection<T>(array);
  35. }
  36. public static void Resize<T>([NotNull] ref T[]? array, int newSize)
  37. {
  38. if (newSize < 0)
  39. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.newSize, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
  40. T[]? larray = array;
  41. if (larray == null)
  42. {
  43. array = new T[newSize];
  44. return;
  45. }
  46. if (larray.Length != newSize)
  47. {
  48. T[] newArray = new T[newSize];
  49. Copy(larray, 0, newArray, 0, larray.Length > newSize ? newSize : larray.Length);
  50. array = newArray;
  51. }
  52. }
  53. public static Array CreateInstance(Type elementType, params long[] lengths)
  54. {
  55. if (lengths == null)
  56. {
  57. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lengths);
  58. }
  59. if (lengths.Length == 0)
  60. ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank);
  61. int[] intLengths = new int[lengths.Length];
  62. for (int i = 0; i < lengths.Length; ++i)
  63. {
  64. long len = lengths[i];
  65. int ilen = (int)len;
  66. if (len != ilen)
  67. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.len, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  68. intLengths[i] = ilen;
  69. }
  70. return Array.CreateInstance(elementType, intLengths);
  71. }
  72. public static void Copy(Array sourceArray, Array destinationArray, long length)
  73. {
  74. int ilength = (int)length;
  75. if (length != ilength)
  76. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  77. Copy(sourceArray, destinationArray, ilength);
  78. }
  79. public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length)
  80. {
  81. int isourceIndex = (int)sourceIndex;
  82. int idestinationIndex = (int)destinationIndex;
  83. int ilength = (int)length;
  84. if (sourceIndex != isourceIndex)
  85. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  86. if (destinationIndex != idestinationIndex)
  87. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.destinationIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  88. if (length != ilength)
  89. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  90. Copy(sourceArray, isourceIndex, destinationArray, idestinationIndex, ilength);
  91. }
  92. public object? GetValue(long index)
  93. {
  94. int iindex = (int)index;
  95. if (index != iindex)
  96. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  97. return this.GetValue(iindex);
  98. }
  99. public object? GetValue(long index1, long index2)
  100. {
  101. int iindex1 = (int)index1;
  102. int iindex2 = (int)index2;
  103. if (index1 != iindex1)
  104. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  105. if (index2 != iindex2)
  106. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  107. return this.GetValue(iindex1, iindex2);
  108. }
  109. public object? GetValue(long index1, long index2, long index3)
  110. {
  111. int iindex1 = (int)index1;
  112. int iindex2 = (int)index2;
  113. int iindex3 = (int)index3;
  114. if (index1 != iindex1)
  115. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  116. if (index2 != iindex2)
  117. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  118. if (index3 != iindex3)
  119. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index3, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  120. return this.GetValue(iindex1, iindex2, iindex3);
  121. }
  122. public object? GetValue(params long[] indices)
  123. {
  124. if (indices == null)
  125. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
  126. if (Rank != indices.Length)
  127. ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
  128. int[] intIndices = new int[indices.Length];
  129. for (int i = 0; i < indices.Length; ++i)
  130. {
  131. long index = indices[i];
  132. int iindex = (int)index;
  133. if (index != iindex)
  134. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  135. intIndices[i] = iindex;
  136. }
  137. return this.GetValue(intIndices);
  138. }
  139. public void SetValue(object? value, long index)
  140. {
  141. int iindex = (int)index;
  142. if (index != iindex)
  143. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  144. this.SetValue(value, iindex);
  145. }
  146. public void SetValue(object? value, long index1, long index2)
  147. {
  148. int iindex1 = (int)index1;
  149. int iindex2 = (int)index2;
  150. if (index1 != iindex1)
  151. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  152. if (index2 != iindex2)
  153. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  154. this.SetValue(value, iindex1, iindex2);
  155. }
  156. public void SetValue(object? value, long index1, long index2, long index3)
  157. {
  158. int iindex1 = (int)index1;
  159. int iindex2 = (int)index2;
  160. int iindex3 = (int)index3;
  161. if (index1 != iindex1)
  162. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  163. if (index2 != iindex2)
  164. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  165. if (index3 != iindex3)
  166. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index3, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  167. this.SetValue(value, iindex1, iindex2, iindex3);
  168. }
  169. public void SetValue(object? value, params long[] indices)
  170. {
  171. if (indices == null)
  172. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
  173. if (Rank != indices.Length)
  174. ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
  175. int[] intIndices = new int[indices.Length];
  176. for (int i = 0; i < indices.Length; ++i)
  177. {
  178. long index = indices[i];
  179. int iindex = (int)index;
  180. if (index != iindex)
  181. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  182. intIndices[i] = iindex;
  183. }
  184. this.SetValue(value, intIndices);
  185. }
  186. private static int GetMedian(int low, int hi)
  187. {
  188. // Note both may be negative, if we are dealing with arrays w/ negative lower bounds.
  189. Debug.Assert(low <= hi);
  190. Debug.Assert(hi - low >= 0, "Length overflow!");
  191. return low + ((hi - low) >> 1);
  192. }
  193. public long GetLongLength(int dimension)
  194. {
  195. // This method should throw an IndexOufOfRangeException for compat if dimension < 0 or >= Rank
  196. return GetLength(dimension);
  197. }
  198. // Number of elements in the Array.
  199. int ICollection.Count => Length;
  200. // Returns an object appropriate for synchronizing access to this
  201. // Array.
  202. public object SyncRoot => this;
  203. // Is this Array read-only?
  204. public bool IsReadOnly => false;
  205. public bool IsFixedSize => true;
  206. // Is this Array synchronized (i.e., thread-safe)? If you want a synchronized
  207. // collection, you can use SyncRoot as an object to synchronize your
  208. // collection with. You could also call GetSynchronized()
  209. // to get a synchronized wrapper around the Array.
  210. public bool IsSynchronized => false;
  211. object? IList.this[int index]
  212. {
  213. get => GetValue(index);
  214. set => SetValue(value, index);
  215. }
  216. int IList.Add(object? value)
  217. {
  218. ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
  219. return default;
  220. }
  221. bool IList.Contains(object? value)
  222. {
  223. return Array.IndexOf(this, value) >= this.GetLowerBound(0);
  224. }
  225. void IList.Clear()
  226. {
  227. Array.Clear(this, this.GetLowerBound(0), this.Length);
  228. }
  229. int IList.IndexOf(object? value)
  230. {
  231. return Array.IndexOf(this, value);
  232. }
  233. void IList.Insert(int index, object? value)
  234. {
  235. ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
  236. }
  237. void IList.Remove(object? value)
  238. {
  239. ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
  240. }
  241. void IList.RemoveAt(int index)
  242. {
  243. ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
  244. }
  245. // Make a new array which is a shallow copy of the original array.
  246. //
  247. public object Clone()
  248. {
  249. return MemberwiseClone();
  250. }
  251. int IStructuralComparable.CompareTo(object? other, IComparer comparer)
  252. {
  253. if (other == null)
  254. {
  255. return 1;
  256. }
  257. Array? o = other as Array;
  258. if (o == null || this.Length != o.Length)
  259. {
  260. ThrowHelper.ThrowArgumentException(ExceptionResource.ArgumentException_OtherNotArrayOfCorrectLength, ExceptionArgument.other);
  261. }
  262. int i = 0;
  263. int c = 0;
  264. while (i < o.Length && c == 0)
  265. {
  266. object? left = GetValue(i);
  267. object? right = o.GetValue(i);
  268. c = comparer.Compare(left, right);
  269. i++;
  270. }
  271. return c;
  272. }
  273. bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer)
  274. {
  275. if (other == null)
  276. {
  277. return false;
  278. }
  279. if (object.ReferenceEquals(this, other))
  280. {
  281. return true;
  282. }
  283. if (!(other is Array o) || o.Length != this.Length)
  284. {
  285. return false;
  286. }
  287. int i = 0;
  288. while (i < o.Length)
  289. {
  290. object? left = GetValue(i);
  291. object? right = o.GetValue(i);
  292. if (!comparer.Equals(left, right))
  293. {
  294. return false;
  295. }
  296. i++;
  297. }
  298. return true;
  299. }
  300. int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
  301. {
  302. if (comparer == null)
  303. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
  304. int ret = 0;
  305. for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++)
  306. {
  307. ret = HashCode.Combine(ret, comparer.GetHashCode(GetValue(i)!));
  308. }
  309. return ret;
  310. }
  311. // Searches an array for a given element using a binary search algorithm.
  312. // Elements of the array are compared to the search value using the
  313. // IComparable interface, which must be implemented by all elements
  314. // of the array and the given search value. This method assumes that the
  315. // array is already sorted according to the IComparable interface;
  316. // if this is not the case, the result will be incorrect.
  317. //
  318. // The method returns the index of the given value in the array. If the
  319. // array does not contain the given value, the method returns a negative
  320. // integer. The bitwise complement operator (~) can be applied to a
  321. // negative result to produce the index of the first element (if any) that
  322. // is larger than the given search value.
  323. //
  324. public static int BinarySearch(Array array, object? value)
  325. {
  326. if (array == null)
  327. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  328. return BinarySearch(array, array.GetLowerBound(0), array.Length, value, null);
  329. }
  330. // Searches a section of an array for a given element using a binary search
  331. // algorithm. Elements of the array are compared to the search value using
  332. // the IComparable interface, which must be implemented by all
  333. // elements of the array and the given search value. This method assumes
  334. // that the array is already sorted according to the IComparable
  335. // interface; if this is not the case, the result will be incorrect.
  336. //
  337. // The method returns the index of the given value in the array. If the
  338. // array does not contain the given value, the method returns a negative
  339. // integer. The bitwise complement operator (~) can be applied to a
  340. // negative result to produce the index of the first element (if any) that
  341. // is larger than the given search value.
  342. //
  343. public static int BinarySearch(Array array, int index, int length, object? value)
  344. {
  345. return BinarySearch(array, index, length, value, null);
  346. }
  347. // Searches an array for a given element using a binary search algorithm.
  348. // Elements of the array are compared to the search value using the given
  349. // IComparer interface. If comparer is null, elements of the
  350. // array are compared to the search value using the IComparable
  351. // interface, which in that case must be implemented by all elements of the
  352. // array and the given search value. This method assumes that the array is
  353. // already sorted; if this is not the case, the result will be incorrect.
  354. //
  355. // The method returns the index of the given value in the array. If the
  356. // array does not contain the given value, the method returns a negative
  357. // integer. The bitwise complement operator (~) can be applied to a
  358. // negative result to produce the index of the first element (if any) that
  359. // is larger than the given search value.
  360. //
  361. public static int BinarySearch(Array array, object? value, IComparer? comparer)
  362. {
  363. if (array == null)
  364. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  365. return BinarySearch(array, array.GetLowerBound(0), array.Length, value, comparer);
  366. }
  367. // Searches a section of an array for a given element using a binary search
  368. // algorithm. Elements of the array are compared to the search value using
  369. // the given IComparer interface. If comparer is null,
  370. // elements of the array are compared to the search value using the
  371. // IComparable interface, which in that case must be implemented by
  372. // all elements of the array and the given search value. This method
  373. // assumes that the array is already sorted; if this is not the case, the
  374. // result will be incorrect.
  375. //
  376. // The method returns the index of the given value in the array. If the
  377. // array does not contain the given value, the method returns a negative
  378. // integer. The bitwise complement operator (~) can be applied to a
  379. // negative result to produce the index of the first element (if any) that
  380. // is larger than the given search value.
  381. //
  382. public static int BinarySearch(Array array, int index, int length, object? value, IComparer? comparer)
  383. {
  384. if (array == null)
  385. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  386. int lb = array.GetLowerBound(0);
  387. if (index < lb)
  388. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  389. if (length < 0)
  390. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  391. if (array.Length - (index - lb) < length)
  392. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  393. if (array.Rank != 1)
  394. ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
  395. comparer ??= Comparer.Default;
  396. int lo = index;
  397. int hi = index + length - 1;
  398. if (array is object[] objArray)
  399. {
  400. while (lo <= hi)
  401. {
  402. // i might overflow if lo and hi are both large positive numbers.
  403. int i = GetMedian(lo, hi);
  404. int c;
  405. try
  406. {
  407. c = comparer.Compare(objArray[i], value);
  408. }
  409. catch (Exception e)
  410. {
  411. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
  412. return default;
  413. }
  414. if (c == 0) return i;
  415. if (c < 0)
  416. {
  417. lo = i + 1;
  418. }
  419. else
  420. {
  421. hi = i - 1;
  422. }
  423. }
  424. return ~lo;
  425. }
  426. if (comparer == Comparer.Default)
  427. {
  428. CorElementType et = array.GetCorElementTypeOfElementType();
  429. if (et.IsPrimitiveType()
  430. // IntPtr/UIntPtr does not implement IComparable
  431. && (et != CorElementType.ELEMENT_TYPE_I) && (et != CorElementType.ELEMENT_TYPE_U))
  432. {
  433. if (value == null)
  434. return ~index;
  435. if (array.IsValueOfElementType(value))
  436. {
  437. int adjustedIndex = index - lb;
  438. int result = -1;
  439. switch (et)
  440. {
  441. case CorElementType.ELEMENT_TYPE_I1:
  442. result = GenericBinarySearch<sbyte>(array, adjustedIndex, length, value);
  443. break;
  444. case CorElementType.ELEMENT_TYPE_U1:
  445. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  446. result = GenericBinarySearch<byte>(array, adjustedIndex, length, value);
  447. break;
  448. case CorElementType.ELEMENT_TYPE_I2:
  449. result = GenericBinarySearch<short>(array, adjustedIndex, length, value);
  450. break;
  451. case CorElementType.ELEMENT_TYPE_U2:
  452. case CorElementType.ELEMENT_TYPE_CHAR:
  453. result = GenericBinarySearch<ushort>(array, adjustedIndex, length, value);
  454. break;
  455. case CorElementType.ELEMENT_TYPE_I4:
  456. result = GenericBinarySearch<int>(array, adjustedIndex, length, value);
  457. break;
  458. case CorElementType.ELEMENT_TYPE_U4:
  459. result = GenericBinarySearch<uint>(array, adjustedIndex, length, value);
  460. break;
  461. case CorElementType.ELEMENT_TYPE_I8:
  462. result = GenericBinarySearch<long>(array, adjustedIndex, length, value);
  463. break;
  464. case CorElementType.ELEMENT_TYPE_U8:
  465. result = GenericBinarySearch<ulong>(array, adjustedIndex, length, value);
  466. break;
  467. case CorElementType.ELEMENT_TYPE_R4:
  468. result = GenericBinarySearch<float>(array, adjustedIndex, length, value);
  469. break;
  470. case CorElementType.ELEMENT_TYPE_R8:
  471. result = GenericBinarySearch<double>(array, adjustedIndex, length, value);
  472. break;
  473. default:
  474. Debug.Fail("All primitive types should be handled above");
  475. break;
  476. }
  477. return (result >= 0) ? (index + result) : ~(index + ~result);
  478. static int GenericBinarySearch<T>(Array array, int adjustedIndex, int length, object value) where T: struct, IComparable<T>
  479. => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).BinarySearch(Unsafe.As<byte, T>(ref value.GetRawData()));
  480. }
  481. }
  482. }
  483. while (lo <= hi)
  484. {
  485. int i = GetMedian(lo, hi);
  486. int c;
  487. try
  488. {
  489. c = comparer.Compare(array.GetValue(i), value);
  490. }
  491. catch (Exception e)
  492. {
  493. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
  494. return default;
  495. }
  496. if (c == 0) return i;
  497. if (c < 0)
  498. {
  499. lo = i + 1;
  500. }
  501. else
  502. {
  503. hi = i - 1;
  504. }
  505. }
  506. return ~lo;
  507. }
  508. public static int BinarySearch<T>(T[] array, T value)
  509. {
  510. if (array == null)
  511. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  512. return BinarySearch<T>(array, 0, array.Length, value, null);
  513. }
  514. public static int BinarySearch<T>(T[] array, T value, System.Collections.Generic.IComparer<T>? comparer)
  515. {
  516. if (array == null)
  517. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  518. return BinarySearch<T>(array, 0, array.Length, value, comparer);
  519. }
  520. public static int BinarySearch<T>(T[] array, int index, int length, T value)
  521. {
  522. return BinarySearch<T>(array, index, length, value, null);
  523. }
  524. public static int BinarySearch<T>(T[] array, int index, int length, T value, System.Collections.Generic.IComparer<T>? comparer)
  525. {
  526. if (array == null)
  527. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  528. if (index < 0)
  529. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  530. if (length < 0)
  531. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  532. if (array.Length - index < length)
  533. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  534. return ArraySortHelper<T>.Default.BinarySearch(array, index, length, value, comparer);
  535. }
  536. public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter)
  537. {
  538. if (array == null)
  539. {
  540. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  541. }
  542. if (converter == null)
  543. {
  544. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
  545. }
  546. TOutput[] newArray = new TOutput[array.Length];
  547. for (int i = 0; i < array.Length; i++)
  548. {
  549. newArray[i] = converter(array[i]);
  550. }
  551. return newArray;
  552. }
  553. // CopyTo copies a collection into an Array, starting at a particular
  554. // index into the array.
  555. //
  556. // This method is to support the ICollection interface, and calls
  557. // Array.Copy internally. If you aren't using ICollection explicitly,
  558. // call Array.Copy to avoid an extra indirection.
  559. //
  560. public void CopyTo(Array array, int index)
  561. {
  562. if (array != null && array.Rank != 1)
  563. ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
  564. // Note: Array.Copy throws a RankException and we want a consistent ArgumentException for all the IList CopyTo methods.
  565. Array.Copy(this, GetLowerBound(0), array!, index, Length);
  566. }
  567. public void CopyTo(Array array, long index)
  568. {
  569. int iindex = (int)index;
  570. if (index != iindex)
  571. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
  572. this.CopyTo(array, iindex);
  573. }
  574. private static class EmptyArray<T>
  575. {
  576. #pragma warning disable CA1825 // this is the implementation of Array.Empty<T>()
  577. internal static readonly T[] Value = new T[0];
  578. #pragma warning restore CA1825
  579. }
  580. public static T[] Empty<T>()
  581. {
  582. return EmptyArray<T>.Value;
  583. }
  584. public static bool Exists<T>(T[] array, Predicate<T> match)
  585. {
  586. return Array.FindIndex(array, match) != -1;
  587. }
  588. public static void Fill<T>(T[] array, T value)
  589. {
  590. if (array == null)
  591. {
  592. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  593. }
  594. for (int i = 0; i < array.Length; i++)
  595. {
  596. array[i] = value;
  597. }
  598. }
  599. public static void Fill<T>(T[] array, T value, int startIndex, int count)
  600. {
  601. if (array == null)
  602. {
  603. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  604. }
  605. if (startIndex < 0 || startIndex > array.Length)
  606. {
  607. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  608. }
  609. if (count < 0 || startIndex > array.Length - count)
  610. {
  611. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  612. }
  613. for (int i = startIndex; i < startIndex + count; i++)
  614. {
  615. array[i] = value;
  616. }
  617. }
  618. [return: MaybeNull]
  619. public static T Find<T>(T[] array, Predicate<T> match)
  620. {
  621. if (array == null)
  622. {
  623. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  624. }
  625. if (match == null)
  626. {
  627. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  628. }
  629. for (int i = 0; i < array.Length; i++)
  630. {
  631. if (match(array[i]))
  632. {
  633. return array[i];
  634. }
  635. }
  636. return default!;
  637. }
  638. public static T[] FindAll<T>(T[] array, Predicate<T> match)
  639. {
  640. if (array == null)
  641. {
  642. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  643. }
  644. if (match == null)
  645. {
  646. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  647. }
  648. List<T> list = new List<T>();
  649. for (int i = 0; i < array.Length; i++)
  650. {
  651. if (match(array[i]))
  652. {
  653. list.Add(array[i]);
  654. }
  655. }
  656. return list.ToArray();
  657. }
  658. public static int FindIndex<T>(T[] array, Predicate<T> match)
  659. {
  660. if (array == null)
  661. {
  662. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  663. }
  664. return FindIndex(array, 0, array.Length, match);
  665. }
  666. public static int FindIndex<T>(T[] array, int startIndex, Predicate<T> match)
  667. {
  668. if (array == null)
  669. {
  670. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  671. }
  672. return FindIndex(array, startIndex, array.Length - startIndex, match);
  673. }
  674. public static int FindIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
  675. {
  676. if (array == null)
  677. {
  678. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  679. }
  680. if (startIndex < 0 || startIndex > array.Length)
  681. {
  682. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  683. }
  684. if (count < 0 || startIndex > array.Length - count)
  685. {
  686. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  687. }
  688. if (match == null)
  689. {
  690. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  691. }
  692. int endIndex = startIndex + count;
  693. for (int i = startIndex; i < endIndex; i++)
  694. {
  695. if (match(array[i]))
  696. return i;
  697. }
  698. return -1;
  699. }
  700. [return: MaybeNull]
  701. public static T FindLast<T>(T[] array, Predicate<T> match)
  702. {
  703. if (array == null)
  704. {
  705. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  706. }
  707. if (match == null)
  708. {
  709. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  710. }
  711. for (int i = array.Length - 1; i >= 0; i--)
  712. {
  713. if (match(array[i]))
  714. {
  715. return array[i];
  716. }
  717. }
  718. return default!;
  719. }
  720. public static int FindLastIndex<T>(T[] array, Predicate<T> match)
  721. {
  722. if (array == null)
  723. {
  724. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  725. }
  726. return FindLastIndex(array, array.Length - 1, array.Length, match);
  727. }
  728. public static int FindLastIndex<T>(T[] array, int startIndex, Predicate<T> match)
  729. {
  730. if (array == null)
  731. {
  732. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  733. }
  734. return FindLastIndex(array, startIndex, startIndex + 1, match);
  735. }
  736. public static int FindLastIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
  737. {
  738. if (array == null)
  739. {
  740. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  741. }
  742. if (match == null)
  743. {
  744. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  745. }
  746. if (array.Length == 0)
  747. {
  748. // Special case for 0 length List
  749. if (startIndex != -1)
  750. {
  751. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  752. }
  753. }
  754. else
  755. {
  756. // Make sure we're not out of range
  757. if (startIndex < 0 || startIndex >= array.Length)
  758. {
  759. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  760. }
  761. }
  762. // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
  763. if (count < 0 || startIndex - count + 1 < 0)
  764. {
  765. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  766. }
  767. int endIndex = startIndex - count;
  768. for (int i = startIndex; i > endIndex; i--)
  769. {
  770. if (match(array[i]))
  771. {
  772. return i;
  773. }
  774. }
  775. return -1;
  776. }
  777. public static void ForEach<T>(T[] array, Action<T> action)
  778. {
  779. if (array == null)
  780. {
  781. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  782. }
  783. if (action == null)
  784. {
  785. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
  786. }
  787. for (int i = 0; i < array.Length; i++)
  788. {
  789. action(array[i]);
  790. }
  791. }
  792. // Returns the index of the first occurrence of a given value in an array.
  793. // The array is searched forwards, and the elements of the array are
  794. // compared to the given value using the Object.Equals method.
  795. //
  796. public static int IndexOf(Array array, object? value)
  797. {
  798. if (array == null)
  799. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  800. return IndexOf(array, value, array.GetLowerBound(0), array.Length);
  801. }
  802. // Returns the index of the first occurrence of a given value in a range of
  803. // an array. The array is searched forwards, starting at index
  804. // startIndex and ending at the last element of the array. The
  805. // elements of the array are compared to the given value using the
  806. // Object.Equals method.
  807. //
  808. public static int IndexOf(Array array, object? value, int startIndex)
  809. {
  810. if (array == null)
  811. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  812. int lb = array.GetLowerBound(0);
  813. return IndexOf(array, value, startIndex, array.Length - startIndex + lb);
  814. }
  815. // Returns the index of the first occurrence of a given value in a range of
  816. // an array. The array is searched forwards, starting at index
  817. // startIndex and upto count elements. The
  818. // elements of the array are compared to the given value using the
  819. // Object.Equals method.
  820. //
  821. public static int IndexOf(Array array, object? value, int startIndex, int count)
  822. {
  823. if (array == null)
  824. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  825. if (array.Rank != 1)
  826. ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
  827. int lb = array.GetLowerBound(0);
  828. if (startIndex < lb || startIndex > array.Length + lb)
  829. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  830. if (count < 0 || count > array.Length - startIndex + lb)
  831. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  832. int endIndex = startIndex + count;
  833. if (array is object[] objArray)
  834. {
  835. if (value == null)
  836. {
  837. for (int i = startIndex; i < endIndex; i++)
  838. {
  839. if (objArray[i] == null)
  840. return i;
  841. }
  842. }
  843. else
  844. {
  845. for (int i = startIndex; i < endIndex; i++)
  846. {
  847. object obj = objArray[i];
  848. if (obj != null && obj.Equals(value))
  849. return i;
  850. }
  851. }
  852. return -1;
  853. }
  854. CorElementType et = array.GetCorElementTypeOfElementType();
  855. if (et.IsPrimitiveType())
  856. {
  857. if (value == null)
  858. return lb - 1;
  859. if (array.IsValueOfElementType(value))
  860. {
  861. int adjustedIndex = startIndex - lb;
  862. int result = -1;
  863. switch (et)
  864. {
  865. case CorElementType.ELEMENT_TYPE_I1:
  866. case CorElementType.ELEMENT_TYPE_U1:
  867. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  868. result = GenericIndexOf<byte>(array, value, adjustedIndex, count);
  869. break;
  870. case CorElementType.ELEMENT_TYPE_I2:
  871. case CorElementType.ELEMENT_TYPE_U2:
  872. case CorElementType.ELEMENT_TYPE_CHAR:
  873. result = GenericIndexOf<char>(array, value, adjustedIndex, count);
  874. break;
  875. case CorElementType.ELEMENT_TYPE_I4:
  876. case CorElementType.ELEMENT_TYPE_U4:
  877. #if !BIT64
  878. case CorElementType.ELEMENT_TYPE_I:
  879. case CorElementType.ELEMENT_TYPE_U:
  880. #endif
  881. result = GenericIndexOf<int>(array, value, adjustedIndex, count);
  882. break;
  883. case CorElementType.ELEMENT_TYPE_I8:
  884. case CorElementType.ELEMENT_TYPE_U8:
  885. #if BIT64
  886. case CorElementType.ELEMENT_TYPE_I:
  887. case CorElementType.ELEMENT_TYPE_U:
  888. #endif
  889. result = GenericIndexOf<long>(array, value, adjustedIndex, count);
  890. break;
  891. case CorElementType.ELEMENT_TYPE_R4:
  892. result = GenericIndexOf<float>(array, value, adjustedIndex, count);
  893. break;
  894. case CorElementType.ELEMENT_TYPE_R8:
  895. result = GenericIndexOf<double>(array, value, adjustedIndex, count);
  896. break;
  897. default:
  898. Debug.Fail("All primitive types should be handled above");
  899. break;
  900. }
  901. return (result >= 0 ? startIndex : lb) + result;
  902. static int GenericIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
  903. => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).IndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
  904. }
  905. }
  906. for (int i = startIndex; i < endIndex; i++)
  907. {
  908. object? obj = array.GetValue(i);
  909. if (obj == null)
  910. {
  911. if (value == null)
  912. return i;
  913. }
  914. else
  915. {
  916. if (obj.Equals(value))
  917. return i;
  918. }
  919. }
  920. // Return one less than the lower bound of the array. This way,
  921. // for arrays with a lower bound of -1 we will not return -1 when the
  922. // item was not found. And for SZArrays (the vast majority), -1 still
  923. // works for them.
  924. return lb - 1;
  925. }
  926. public static int IndexOf<T>(T[] array, T value)
  927. {
  928. if (array == null)
  929. {
  930. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  931. }
  932. return IndexOf(array, value, 0, array.Length);
  933. }
  934. public static int IndexOf<T>(T[] array, T value, int startIndex)
  935. {
  936. if (array == null)
  937. {
  938. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  939. }
  940. return IndexOf(array, value, startIndex, array.Length - startIndex);
  941. }
  942. public static int IndexOf<T>(T[] array, T value, int startIndex, int count)
  943. {
  944. if (array == null)
  945. {
  946. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  947. }
  948. if ((uint)startIndex > (uint)array.Length)
  949. {
  950. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  951. }
  952. if ((uint)count > (uint)(array.Length - startIndex))
  953. {
  954. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  955. }
  956. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  957. {
  958. if (Unsafe.SizeOf<T>() == sizeof(byte))
  959. {
  960. int result = SpanHelpers.IndexOf(
  961. ref Unsafe.Add(ref array.GetRawSzArrayData(), startIndex),
  962. Unsafe.As<T, byte>(ref value),
  963. count);
  964. return (result >= 0 ? startIndex : 0) + result;
  965. }
  966. else if (Unsafe.SizeOf<T>() == sizeof(char))
  967. {
  968. int result = SpanHelpers.IndexOf(
  969. ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), startIndex),
  970. Unsafe.As<T, char>(ref value),
  971. count);
  972. return (result >= 0 ? startIndex : 0) + result;
  973. }
  974. else if (Unsafe.SizeOf<T>() == sizeof(int))
  975. {
  976. int result = SpanHelpers.IndexOf(
  977. ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), startIndex),
  978. Unsafe.As<T, int>(ref value),
  979. count);
  980. return (result >= 0 ? startIndex : 0) + result;
  981. }
  982. else if (Unsafe.SizeOf<T>() == sizeof(long))
  983. {
  984. int result = SpanHelpers.IndexOf(
  985. ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), startIndex),
  986. Unsafe.As<T, long>(ref value),
  987. count);
  988. return (result >= 0 ? startIndex : 0) + result;
  989. }
  990. }
  991. #if !CORERT
  992. return EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);
  993. #else
  994. return IndexOfImpl(array, value, startIndex, count);
  995. #endif
  996. }
  997. // Returns the index of the last occurrence of a given value in an array.
  998. // The array is searched backwards, and the elements of the array are
  999. // compared to the given value using the Object.Equals method.
  1000. //
  1001. public static int LastIndexOf(Array array, object? value)
  1002. {
  1003. if (array == null)
  1004. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1005. int lb = array.GetLowerBound(0);
  1006. return LastIndexOf(array, value, array.Length - 1 + lb, array.Length);
  1007. }
  1008. // Returns the index of the last occurrence of a given value in a range of
  1009. // an array. The array is searched backwards, starting at index
  1010. // startIndex and ending at index 0. The elements of the array are
  1011. // compared to the given value using the Object.Equals method.
  1012. //
  1013. public static int LastIndexOf(Array array, object? value, int startIndex)
  1014. {
  1015. if (array == null)
  1016. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1017. int lb = array.GetLowerBound(0);
  1018. return LastIndexOf(array, value, startIndex, startIndex + 1 - lb);
  1019. }
  1020. // Returns the index of the last occurrence of a given value in a range of
  1021. // an array. The array is searched backwards, starting at index
  1022. // startIndex and counting uptocount elements. The elements of
  1023. // the array are compared to the given value using the Object.Equals
  1024. // method.
  1025. //
  1026. public static int LastIndexOf(Array array, object? value, int startIndex, int count)
  1027. {
  1028. if (array == null)
  1029. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1030. int lb = array.GetLowerBound(0);
  1031. if (array.Length == 0)
  1032. {
  1033. return lb - 1;
  1034. }
  1035. if (startIndex < lb || startIndex >= array.Length + lb)
  1036. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  1037. if (count < 0)
  1038. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  1039. if (count > startIndex - lb + 1)
  1040. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.endIndex, ExceptionResource.ArgumentOutOfRange_EndIndexStartIndex);
  1041. if (array.Rank != 1)
  1042. ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
  1043. int endIndex = startIndex - count + 1;
  1044. if (array is object[] objArray)
  1045. {
  1046. if (value == null)
  1047. {
  1048. for (int i = startIndex; i >= endIndex; i--)
  1049. {
  1050. if (objArray[i] == null)
  1051. return i;
  1052. }
  1053. }
  1054. else
  1055. {
  1056. for (int i = startIndex; i >= endIndex; i--)
  1057. {
  1058. object obj = objArray[i];
  1059. if (obj != null && obj.Equals(value))
  1060. return i;
  1061. }
  1062. }
  1063. return -1;
  1064. }
  1065. CorElementType et = array.GetCorElementTypeOfElementType();
  1066. if (et.IsPrimitiveType())
  1067. {
  1068. if (value == null)
  1069. return lb - 1;
  1070. if (array.IsValueOfElementType(value))
  1071. {
  1072. int adjustedIndex = endIndex - lb;
  1073. int result = -1;
  1074. switch (et)
  1075. {
  1076. case CorElementType.ELEMENT_TYPE_I1:
  1077. case CorElementType.ELEMENT_TYPE_U1:
  1078. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  1079. result = GenericLastIndexOf<byte>(array, value, adjustedIndex, count);
  1080. break;
  1081. case CorElementType.ELEMENT_TYPE_I2:
  1082. case CorElementType.ELEMENT_TYPE_U2:
  1083. case CorElementType.ELEMENT_TYPE_CHAR:
  1084. result = GenericLastIndexOf<char>(array, value, adjustedIndex, count);
  1085. break;
  1086. case CorElementType.ELEMENT_TYPE_I4:
  1087. case CorElementType.ELEMENT_TYPE_U4:
  1088. #if !BIT64
  1089. case CorElementType.ELEMENT_TYPE_I:
  1090. case CorElementType.ELEMENT_TYPE_U:
  1091. #endif
  1092. result = GenericLastIndexOf<int>(array, value, adjustedIndex, count);
  1093. break;
  1094. case CorElementType.ELEMENT_TYPE_I8:
  1095. case CorElementType.ELEMENT_TYPE_U8:
  1096. #if BIT64
  1097. case CorElementType.ELEMENT_TYPE_I:
  1098. case CorElementType.ELEMENT_TYPE_U:
  1099. #endif
  1100. result = GenericLastIndexOf<long>(array, value, adjustedIndex, count);
  1101. break;
  1102. case CorElementType.ELEMENT_TYPE_R4:
  1103. result = GenericLastIndexOf<float>(array, value, adjustedIndex, count);
  1104. break;
  1105. case CorElementType.ELEMENT_TYPE_R8:
  1106. result = GenericLastIndexOf<double>(array, value, adjustedIndex, count);
  1107. break;
  1108. default:
  1109. Debug.Fail("All primitive types should be handled above");
  1110. break;
  1111. }
  1112. return (result >= 0 ? endIndex : lb) + result;
  1113. static int GenericLastIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
  1114. => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).LastIndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
  1115. }
  1116. }
  1117. for (int i = startIndex; i >= endIndex; i--)
  1118. {
  1119. object? obj = array.GetValue(i);
  1120. if (obj == null)
  1121. {
  1122. if (value == null)
  1123. return i;
  1124. }
  1125. else
  1126. {
  1127. if (obj.Equals(value))
  1128. return i;
  1129. }
  1130. }
  1131. return lb - 1; // Return lb-1 for arrays with negative lower bounds.
  1132. }
  1133. public static int LastIndexOf<T>(T[] array, T value)
  1134. {
  1135. if (array == null)
  1136. {
  1137. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1138. }
  1139. return LastIndexOf(array, value, array.Length - 1, array.Length);
  1140. }
  1141. public static int LastIndexOf<T>(T[] array, T value, int startIndex)
  1142. {
  1143. if (array == null)
  1144. {
  1145. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1146. }
  1147. // if array is empty and startIndex is 0, we need to pass 0 as count
  1148. return LastIndexOf(array, value, startIndex, (array.Length == 0) ? 0 : (startIndex + 1));
  1149. }
  1150. public static int LastIndexOf<T>(T[] array, T value, int startIndex, int count)
  1151. {
  1152. if (array == null)
  1153. {
  1154. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1155. }
  1156. if (array.Length == 0)
  1157. {
  1158. //
  1159. // Special case for 0 length List
  1160. // accept -1 and 0 as valid startIndex for compablility reason.
  1161. //
  1162. if (startIndex != -1 && startIndex != 0)
  1163. {
  1164. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  1165. }
  1166. // only 0 is a valid value for count if array is empty
  1167. if (count != 0)
  1168. {
  1169. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  1170. }
  1171. return -1;
  1172. }
  1173. // Make sure we're not out of range
  1174. if ((uint)startIndex >= (uint)array.Length)
  1175. {
  1176. ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
  1177. }
  1178. // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
  1179. if (count < 0 || startIndex - count + 1 < 0)
  1180. {
  1181. ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
  1182. }
  1183. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  1184. {
  1185. if (Unsafe.SizeOf<T>() == sizeof(byte))
  1186. {
  1187. int endIndex = startIndex - count + 1;
  1188. int result = SpanHelpers.LastIndexOf(
  1189. ref Unsafe.Add(ref array.GetRawSzArrayData(), endIndex),
  1190. Unsafe.As<T, byte>(ref value),
  1191. count);
  1192. return (result >= 0 ? endIndex : 0) + result;
  1193. }
  1194. else if (Unsafe.SizeOf<T>() == sizeof(char))
  1195. {
  1196. int endIndex = startIndex - count + 1;
  1197. int result = SpanHelpers.LastIndexOf(
  1198. ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), endIndex),
  1199. Unsafe.As<T, char>(ref value),
  1200. count);
  1201. return (result >= 0 ? endIndex : 0) + result;
  1202. }
  1203. else if (Unsafe.SizeOf<T>() == sizeof(int))
  1204. {
  1205. int endIndex = startIndex - count + 1;
  1206. int result = SpanHelpers.LastIndexOf(
  1207. ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), endIndex),
  1208. Unsafe.As<T, int>(ref value),
  1209. count);
  1210. return (result >= 0 ? endIndex : 0) + result;
  1211. }
  1212. else if (Unsafe.SizeOf<T>() == sizeof(long))
  1213. {
  1214. int endIndex = startIndex - count + 1;
  1215. int result = SpanHelpers.LastIndexOf(
  1216. ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), endIndex),
  1217. Unsafe.As<T, long>(ref value),
  1218. count);
  1219. return (result >= 0 ? endIndex : 0) + result;
  1220. }
  1221. }
  1222. #if !CORERT
  1223. return EqualityComparer<T>.Default.LastIndexOf(array, value, startIndex, count);
  1224. #else
  1225. return LastIndexOfImpl(array, value, startIndex, count);
  1226. #endif
  1227. }
  1228. // Reverses all elements of the given array. Following a call to this
  1229. // method, an element previously located at index i will now be
  1230. // located at index length - i - 1, where length is the
  1231. // length of the array.
  1232. //
  1233. public static void Reverse(Array array)
  1234. {
  1235. if (array == null)
  1236. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1237. Reverse(array, array.GetLowerBound(0), array.Length);
  1238. }
  1239. // Reverses the elements in a range of an array. Following a call to this
  1240. // method, an element in the range given by index and count
  1241. // which was previously located at index i will now be located at
  1242. // index index + (index + count - i - 1).
  1243. // Reliability note: This may fail because it may have to box objects.
  1244. //
  1245. public static void Reverse(Array array, int index, int length)
  1246. {
  1247. if (array == null)
  1248. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1249. int lowerBound = array.GetLowerBound(0);
  1250. if (index < lowerBound)
  1251. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  1252. if (length < 0)
  1253. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  1254. if (array.Length - (index - lowerBound) < length)
  1255. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  1256. if (array.Rank != 1)
  1257. ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
  1258. if (length <= 1)
  1259. return;
  1260. int adjustedIndex = index - lowerBound;
  1261. switch (array.GetCorElementTypeOfElementType())
  1262. {
  1263. case CorElementType.ELEMENT_TYPE_I1:
  1264. case CorElementType.ELEMENT_TYPE_U1:
  1265. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  1266. UnsafeArrayAsSpan<byte>(array, adjustedIndex, length).Reverse();
  1267. return;
  1268. case CorElementType.ELEMENT_TYPE_I2:
  1269. case CorElementType.ELEMENT_TYPE_U2:
  1270. case CorElementType.ELEMENT_TYPE_CHAR:
  1271. UnsafeArrayAsSpan<short>(array, adjustedIndex, length).Reverse();
  1272. return;
  1273. case CorElementType.ELEMENT_TYPE_I4:
  1274. case CorElementType.ELEMENT_TYPE_U4:
  1275. #if !BIT64
  1276. case CorElementType.ELEMENT_TYPE_I:
  1277. case CorElementType.ELEMENT_TYPE_U:
  1278. #endif
  1279. case CorElementType.ELEMENT_TYPE_R4:
  1280. UnsafeArrayAsSpan<int>(array, adjustedIndex, length).Reverse();
  1281. return;
  1282. case CorElementType.ELEMENT_TYPE_I8:
  1283. case CorElementType.ELEMENT_TYPE_U8:
  1284. #if BIT64
  1285. case CorElementType.ELEMENT_TYPE_I:
  1286. case CorElementType.ELEMENT_TYPE_U:
  1287. #endif
  1288. case CorElementType.ELEMENT_TYPE_R8:
  1289. UnsafeArrayAsSpan<long>(array, adjustedIndex, length).Reverse();
  1290. return;
  1291. case CorElementType.ELEMENT_TYPE_OBJECT:
  1292. case CorElementType.ELEMENT_TYPE_ARRAY:
  1293. case CorElementType.ELEMENT_TYPE_SZARRAY:
  1294. UnsafeArrayAsSpan<object>(array, adjustedIndex, length).Reverse();
  1295. return;
  1296. }
  1297. int i = index;
  1298. int j = index + length - 1;
  1299. while (i < j)
  1300. {
  1301. object? temp = array.GetValue(i);
  1302. array.SetValue(array.GetValue(j), i);
  1303. array.SetValue(temp, j);
  1304. i++;
  1305. j--;
  1306. }
  1307. }
  1308. public static void Reverse<T>(T[] array)
  1309. {
  1310. if (array == null)
  1311. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1312. Reverse(array, 0, array.Length);
  1313. }
  1314. public static void Reverse<T>(T[] array, int index, int length)
  1315. {
  1316. if (array == null)
  1317. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1318. if (index < 0)
  1319. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  1320. if (length < 0)
  1321. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  1322. if (array.Length - index < length)
  1323. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  1324. if (length <= 1)
  1325. return;
  1326. ref T first = ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), index);
  1327. ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, length), -1);
  1328. do
  1329. {
  1330. T temp = first;
  1331. first = last;
  1332. last = temp;
  1333. first = ref Unsafe.Add(ref first, 1);
  1334. last = ref Unsafe.Add(ref last, -1);
  1335. } while (Unsafe.IsAddressLessThan(ref first, ref last));
  1336. }
  1337. // Sorts the elements of an array. The sort compares the elements to each
  1338. // other using the IComparable interface, which must be implemented
  1339. // by all elements of the array.
  1340. //
  1341. public static void Sort(Array array)
  1342. {
  1343. if (array == null)
  1344. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1345. Sort(array, null, array.GetLowerBound(0), array.Length, null);
  1346. }
  1347. // Sorts the elements of two arrays based on the keys in the first array.
  1348. // Elements in the keys array specify the sort keys for
  1349. // corresponding elements in the items array. The sort compares the
  1350. // keys to each other using the IComparable interface, which must be
  1351. // implemented by all elements of the keys array.
  1352. //
  1353. public static void Sort(Array keys, Array? items)
  1354. {
  1355. if (keys == null)
  1356. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1357. Sort(keys, items, keys.GetLowerBound(0), keys.Length, null);
  1358. }
  1359. // Sorts the elements in a section of an array. The sort compares the
  1360. // elements to each other using the IComparable interface, which
  1361. // must be implemented by all elements in the given section of the array.
  1362. //
  1363. public static void Sort(Array array, int index, int length)
  1364. {
  1365. Sort(array, null, index, length, null);
  1366. }
  1367. // Sorts the elements in a section of two arrays based on the keys in the
  1368. // first array. Elements in the keys array specify the sort keys for
  1369. // corresponding elements in the items array. The sort compares the
  1370. // keys to each other using the IComparable interface, which must be
  1371. // implemented by all elements of the keys array.
  1372. //
  1373. public static void Sort(Array keys, Array? items, int index, int length)
  1374. {
  1375. Sort(keys, items, index, length, null);
  1376. }
  1377. // Sorts the elements of an array. The sort compares the elements to each
  1378. // other using the given IComparer interface. If comparer is
  1379. // null, the elements are compared to each other using the
  1380. // IComparable interface, which in that case must be implemented by
  1381. // all elements of the array.
  1382. //
  1383. public static void Sort(Array array, IComparer? comparer)
  1384. {
  1385. if (array == null)
  1386. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1387. Sort(array, null, array.GetLowerBound(0), array.Length, comparer);
  1388. }
  1389. // Sorts the elements of two arrays based on the keys in the first array.
  1390. // Elements in the keys array specify the sort keys for
  1391. // corresponding elements in the items array. The sort compares the
  1392. // keys to each other using the given IComparer interface. If
  1393. // comparer is null, the elements are compared to each other using
  1394. // the IComparable interface, which in that case must be implemented
  1395. // by all elements of the keys array.
  1396. //
  1397. public static void Sort(Array keys, Array? items, IComparer? comparer)
  1398. {
  1399. if (keys == null)
  1400. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1401. Sort(keys, items, keys.GetLowerBound(0), keys.Length, comparer);
  1402. }
  1403. // Sorts the elements in a section of an array. The sort compares the
  1404. // elements to each other using the given IComparer interface. If
  1405. // comparer is null, the elements are compared to each other using
  1406. // the IComparable interface, which in that case must be implemented
  1407. // by all elements in the given section of the array.
  1408. //
  1409. public static void Sort(Array array, int index, int length, IComparer? comparer)
  1410. {
  1411. Sort(array, null, index, length, comparer);
  1412. }
  1413. // Sorts the elements in a section of two arrays based on the keys in the
  1414. // first array. Elements in the keys array specify the sort keys for
  1415. // corresponding elements in the items array. The sort compares the
  1416. // keys to each other using the given IComparer interface. If
  1417. // comparer is null, the elements are compared to each other using
  1418. // the IComparable interface, which in that case must be implemented
  1419. // by all elements of the given section of the keys array.
  1420. //
  1421. public static void Sort(Array keys, Array? items, int index, int length, IComparer? comparer)
  1422. {
  1423. if (keys == null)
  1424. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1425. if (keys.Rank != 1 || (items != null && items.Rank != 1))
  1426. ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
  1427. int keysLowerBound = keys.GetLowerBound(0);
  1428. if (items != null && keysLowerBound != items.GetLowerBound(0))
  1429. ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_LowerBoundsMustMatch);
  1430. if (index < keysLowerBound)
  1431. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  1432. if (length < 0)
  1433. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  1434. if (keys.Length - (index - keysLowerBound) < length || (items != null && (index - keysLowerBound) > items.Length - length))
  1435. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  1436. if (length <= 1)
  1437. return;
  1438. comparer ??= Comparer.Default;
  1439. if (keys is object[] objKeys)
  1440. {
  1441. object[]? objItems = items as object[];
  1442. if (items == null || objItems != null)
  1443. {
  1444. new SorterObjectArray(objKeys, objItems, comparer).Sort(index, length);
  1445. return;
  1446. }
  1447. }
  1448. if (comparer == Comparer.Default)
  1449. {
  1450. CorElementType et = keys.GetCorElementTypeOfElementType();
  1451. if (items == null || items.GetCorElementTypeOfElementType() == et)
  1452. {
  1453. int adjustedIndex = index - keysLowerBound;
  1454. switch (et)
  1455. {
  1456. case CorElementType.ELEMENT_TYPE_I1:
  1457. GenericSort<sbyte>(keys, items, adjustedIndex, length);
  1458. return;
  1459. case CorElementType.ELEMENT_TYPE_U1:
  1460. case CorElementType.ELEMENT_TYPE_BOOLEAN:
  1461. GenericSort<byte>(keys, items, adjustedIndex, length);
  1462. return;
  1463. case CorElementType.ELEMENT_TYPE_I2:
  1464. GenericSort<short>(keys, items, adjustedIndex, length);
  1465. return;
  1466. case CorElementType.ELEMENT_TYPE_U2:
  1467. case CorElementType.ELEMENT_TYPE_CHAR:
  1468. GenericSort<ushort>(keys, items, adjustedIndex, length);
  1469. return;
  1470. case CorElementType.ELEMENT_TYPE_I4:
  1471. GenericSort<int>(keys, items, adjustedIndex, length);
  1472. return;
  1473. case CorElementType.ELEMENT_TYPE_U4:
  1474. GenericSort<uint>(keys, items, adjustedIndex, length);
  1475. return;
  1476. case CorElementType.ELEMENT_TYPE_I8:
  1477. GenericSort<long>(keys, items, adjustedIndex, length);
  1478. return;
  1479. case CorElementType.ELEMENT_TYPE_U8:
  1480. GenericSort<ulong>(keys, items, adjustedIndex, length);
  1481. return;
  1482. case CorElementType.ELEMENT_TYPE_R4:
  1483. GenericSort<float>(keys, items, adjustedIndex, length);
  1484. return;
  1485. case CorElementType.ELEMENT_TYPE_R8:
  1486. GenericSort<double>(keys, items, adjustedIndex, length);
  1487. return;
  1488. case CorElementType.ELEMENT_TYPE_I:
  1489. case CorElementType.ELEMENT_TYPE_U:
  1490. // IntPtr/UIntPtr does not implement IComparable
  1491. break;
  1492. }
  1493. static void GenericSort<T>(Array keys, Array? items, int adjustedIndex, int length) where T: struct
  1494. {
  1495. Span<T> keysSpan = UnsafeArrayAsSpan<T>(keys, adjustedIndex, length);
  1496. if (items != null)
  1497. {
  1498. keysSpan.Sort<T, T>(UnsafeArrayAsSpan<T>(items, adjustedIndex, length));
  1499. }
  1500. else
  1501. {
  1502. keysSpan.Sort<T>();
  1503. }
  1504. }
  1505. }
  1506. }
  1507. new SorterGenericArray(keys, items, comparer).Sort(index, length);
  1508. }
  1509. public static void Sort<T>(T[] array)
  1510. {
  1511. if (array == null)
  1512. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1513. if (array.Length > 1)
  1514. {
  1515. var span = new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), array.Length);
  1516. ArraySortHelper<T>.Default.Sort(span, null);
  1517. }
  1518. }
  1519. public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items)
  1520. {
  1521. if (keys == null)
  1522. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1523. Sort<TKey, TValue>(keys, items, 0, keys.Length, null);
  1524. }
  1525. public static void Sort<T>(T[] array, int index, int length)
  1526. {
  1527. Sort<T>(array, index, length, null);
  1528. }
  1529. public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, int index, int length)
  1530. {
  1531. Sort<TKey, TValue>(keys, items, index, length, null);
  1532. }
  1533. public static void Sort<T>(T[] array, System.Collections.Generic.IComparer<T>? comparer)
  1534. {
  1535. if (array == null)
  1536. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1537. Sort<T>(array, 0, array.Length, comparer);
  1538. }
  1539. public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, System.Collections.Generic.IComparer<TKey>? comparer)
  1540. {
  1541. if (keys == null)
  1542. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1543. Sort<TKey, TValue>(keys, items, 0, keys.Length, comparer);
  1544. }
  1545. public static void Sort<T>(T[] array, int index, int length, System.Collections.Generic.IComparer<T>? comparer)
  1546. {
  1547. if (array == null)
  1548. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1549. if (index < 0)
  1550. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  1551. if (length < 0)
  1552. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  1553. if (array.Length - index < length)
  1554. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  1555. if (length > 1)
  1556. {
  1557. var span = new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), index), length);
  1558. ArraySortHelper<T>.Default.Sort(span, comparer);
  1559. }
  1560. }
  1561. public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, int index, int length, System.Collections.Generic.IComparer<TKey>? comparer)
  1562. {
  1563. if (keys == null)
  1564. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
  1565. if (index < 0)
  1566. ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
  1567. if (length < 0)
  1568. ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
  1569. if (keys.Length - index < length || (items != null && index > items.Length - length))
  1570. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
  1571. if (length > 1)
  1572. {
  1573. if (items == null)
  1574. {
  1575. Sort<TKey>(keys, index, length, comparer);
  1576. return;
  1577. }
  1578. var spanKeys = new Span<TKey>(ref Unsafe.Add(ref Unsafe.As<byte, TKey>(ref keys.GetRawSzArrayData()), index), length);
  1579. var spanItems = new Span<TValue>(ref Unsafe.Add(ref Unsafe.As<byte, TValue>(ref items.GetRawSzArrayData()), index), length);
  1580. ArraySortHelper<TKey, TValue>.Default.Sort(spanKeys, spanItems, comparer);
  1581. }
  1582. }
  1583. public static void Sort<T>(T[] array, Comparison<T> comparison)
  1584. {
  1585. if (array == null)
  1586. {
  1587. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1588. }
  1589. if (comparison == null)
  1590. {
  1591. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
  1592. }
  1593. var span = new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), array.Length);
  1594. ArraySortHelper<T>.Sort(span, comparison);
  1595. }
  1596. public static bool TrueForAll<T>(T[] array, Predicate<T> match)
  1597. {
  1598. if (array == null)
  1599. {
  1600. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1601. }
  1602. if (match == null)
  1603. {
  1604. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  1605. }
  1606. for (int i = 0; i < array.Length; i++)
  1607. {
  1608. if (!match(array[i]))
  1609. {
  1610. return false;
  1611. }
  1612. }
  1613. return true;
  1614. }
  1615. // Private value type used by the Sort methods.
  1616. private readonly struct SorterObjectArray
  1617. {
  1618. private readonly object[] keys;
  1619. private readonly object?[]? items;
  1620. private readonly IComparer comparer;
  1621. internal SorterObjectArray(object[] keys, object?[]? items, IComparer comparer)
  1622. {
  1623. this.keys = keys;
  1624. this.items = items;
  1625. this.comparer = comparer;
  1626. }
  1627. internal void SwapIfGreater(int a, int b)
  1628. {
  1629. if (a != b)
  1630. {
  1631. if (comparer.Compare(keys[a], keys[b]) > 0)
  1632. {
  1633. object temp = keys[a];
  1634. keys[a] = keys[b];
  1635. keys[b] = temp;
  1636. if (items != null)
  1637. {
  1638. object? item = items[a];
  1639. items[a] = items[b];
  1640. items[b] = item;
  1641. }
  1642. }
  1643. }
  1644. }
  1645. private void Swap(int i, int j)
  1646. {
  1647. object t = keys[i];
  1648. keys[i] = keys[j];
  1649. keys[j] = t;
  1650. if (items != null)
  1651. {
  1652. object? item = items[i];
  1653. items[i] = items[j];
  1654. items[j] = item;
  1655. }
  1656. }
  1657. internal void Sort(int left, int length)
  1658. {
  1659. IntrospectiveSort(left, length);
  1660. }
  1661. private void IntrospectiveSort(int left, int length)
  1662. {
  1663. if (length < 2)
  1664. return;
  1665. try
  1666. {
  1667. IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
  1668. }
  1669. catch (IndexOutOfRangeException)
  1670. {
  1671. IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
  1672. }
  1673. catch (Exception e)
  1674. {
  1675. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
  1676. }
  1677. }
  1678. private void IntroSort(int lo, int hi, int depthLimit)
  1679. {
  1680. while (hi > lo)
  1681. {
  1682. int partitionSize = hi - lo + 1;
  1683. if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
  1684. {
  1685. if (partitionSize == 1)
  1686. {
  1687. return;
  1688. }
  1689. if (partitionSize == 2)
  1690. {
  1691. SwapIfGreater(lo, hi);
  1692. return;
  1693. }
  1694. if (partitionSize == 3)
  1695. {
  1696. SwapIfGreater(lo, hi - 1);
  1697. SwapIfGreater(lo, hi);
  1698. SwapIfGreater(hi - 1, hi);
  1699. return;
  1700. }
  1701. InsertionSort(lo, hi);
  1702. return;
  1703. }
  1704. if (depthLimit == 0)
  1705. {
  1706. Heapsort(lo, hi);
  1707. return;
  1708. }
  1709. depthLimit--;
  1710. int p = PickPivotAndPartition(lo, hi);
  1711. IntroSort(p + 1, hi, depthLimit);
  1712. hi = p - 1;
  1713. }
  1714. }
  1715. private int PickPivotAndPartition(int lo, int hi)
  1716. {
  1717. // Compute median-of-three. But also partition them, since we've done the comparison.
  1718. int mid = lo + (hi - lo) / 2;
  1719. // Sort lo, mid and hi appropriately, then pick mid as the pivot.
  1720. SwapIfGreater(lo, mid);
  1721. SwapIfGreater(lo, hi);
  1722. SwapIfGreater(mid, hi);
  1723. object pivot = keys[mid];
  1724. Swap(mid, hi - 1);
  1725. int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
  1726. while (left < right)
  1727. {
  1728. while (comparer.Compare(keys[++left], pivot) < 0) ;
  1729. while (comparer.Compare(pivot, keys[--right]) < 0) ;
  1730. if (left >= right)
  1731. break;
  1732. Swap(left, right);
  1733. }
  1734. // Put pivot in the right location.
  1735. Swap(left, hi - 1);
  1736. return left;
  1737. }
  1738. private void Heapsort(int lo, int hi)
  1739. {
  1740. int n = hi - lo + 1;
  1741. for (int i = n / 2; i >= 1; i--)
  1742. {
  1743. DownHeap(i, n, lo);
  1744. }
  1745. for (int i = n; i > 1; i--)
  1746. {
  1747. Swap(lo, lo + i - 1);
  1748. DownHeap(1, i - 1, lo);
  1749. }
  1750. }
  1751. private void DownHeap(int i, int n, int lo)
  1752. {
  1753. object d = keys[lo + i - 1];
  1754. object? dt = items?[lo + i - 1];
  1755. int child;
  1756. while (i <= n / 2)
  1757. {
  1758. child = 2 * i;
  1759. if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
  1760. {
  1761. child++;
  1762. }
  1763. if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
  1764. break;
  1765. keys[lo + i - 1] = keys[lo + child - 1];
  1766. if (items != null)
  1767. items[lo + i - 1] = items[lo + child - 1];
  1768. i = child;
  1769. }
  1770. keys[lo + i - 1] = d;
  1771. if (items != null)
  1772. items[lo + i - 1] = dt;
  1773. }
  1774. private void InsertionSort(int lo, int hi)
  1775. {
  1776. int i, j;
  1777. object t;
  1778. object? ti;
  1779. for (i = lo; i < hi; i++)
  1780. {
  1781. j = i;
  1782. t = keys[i + 1];
  1783. ti = items?[i + 1];
  1784. while (j >= lo && comparer.Compare(t, keys[j]) < 0)
  1785. {
  1786. keys[j + 1] = keys[j];
  1787. if (items != null)
  1788. items[j + 1] = items[j];
  1789. j--;
  1790. }
  1791. keys[j + 1] = t;
  1792. if (items != null)
  1793. items[j + 1] = ti;
  1794. }
  1795. }
  1796. }
  1797. // Private value used by the Sort methods for instances of Array.
  1798. // This is slower than the one for Object[], since we can't use the JIT helpers
  1799. // to access the elements. We must use GetValue & SetValue.
  1800. private readonly struct SorterGenericArray
  1801. {
  1802. private readonly Array keys;
  1803. private readonly Array? items;
  1804. private readonly IComparer comparer;
  1805. internal SorterGenericArray(Array keys, Array? items, IComparer comparer)
  1806. {
  1807. this.keys = keys;
  1808. this.items = items;
  1809. this.comparer = comparer;
  1810. }
  1811. internal void SwapIfGreater(int a, int b)
  1812. {
  1813. if (a != b)
  1814. {
  1815. if (comparer.Compare(keys.GetValue(a), keys.GetValue(b)) > 0)
  1816. {
  1817. object? key = keys.GetValue(a);
  1818. keys.SetValue(keys.GetValue(b), a);
  1819. keys.SetValue(key, b);
  1820. if (items != null)
  1821. {
  1822. object? item = items.GetValue(a);
  1823. items.SetValue(items.GetValue(b), a);
  1824. items.SetValue(item, b);
  1825. }
  1826. }
  1827. }
  1828. }
  1829. private void Swap(int i, int j)
  1830. {
  1831. object? t1 = keys.GetValue(i);
  1832. keys.SetValue(keys.GetValue(j), i);
  1833. keys.SetValue(t1, j);
  1834. if (items != null)
  1835. {
  1836. object? t2 = items.GetValue(i);
  1837. items.SetValue(items.GetValue(j), i);
  1838. items.SetValue(t2, j);
  1839. }
  1840. }
  1841. internal void Sort(int left, int length)
  1842. {
  1843. IntrospectiveSort(left, length);
  1844. }
  1845. private void IntrospectiveSort(int left, int length)
  1846. {
  1847. if (length < 2)
  1848. return;
  1849. try
  1850. {
  1851. IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
  1852. }
  1853. catch (IndexOutOfRangeException)
  1854. {
  1855. IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
  1856. }
  1857. catch (Exception e)
  1858. {
  1859. ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
  1860. }
  1861. }
  1862. private void IntroSort(int lo, int hi, int depthLimit)
  1863. {
  1864. while (hi > lo)
  1865. {
  1866. int partitionSize = hi - lo + 1;
  1867. if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
  1868. {
  1869. if (partitionSize == 1)
  1870. {
  1871. return;
  1872. }
  1873. if (partitionSize == 2)
  1874. {
  1875. SwapIfGreater(lo, hi);
  1876. return;
  1877. }
  1878. if (partitionSize == 3)
  1879. {
  1880. SwapIfGreater(lo, hi - 1);
  1881. SwapIfGreater(lo, hi);
  1882. SwapIfGreater(hi - 1, hi);
  1883. return;
  1884. }
  1885. InsertionSort(lo, hi);
  1886. return;
  1887. }
  1888. if (depthLimit == 0)
  1889. {
  1890. Heapsort(lo, hi);
  1891. return;
  1892. }
  1893. depthLimit--;
  1894. int p = PickPivotAndPartition(lo, hi);
  1895. IntroSort(p + 1, hi, depthLimit);
  1896. hi = p - 1;
  1897. }
  1898. }
  1899. private int PickPivotAndPartition(int lo, int hi)
  1900. {
  1901. // Compute median-of-three. But also partition them, since we've done the comparison.
  1902. int mid = lo + (hi - lo) / 2;
  1903. SwapIfGreater(lo, mid);
  1904. SwapIfGreater(lo, hi);
  1905. SwapIfGreater(mid, hi);
  1906. object? pivot = keys.GetValue(mid);
  1907. Swap(mid, hi - 1);
  1908. int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
  1909. while (left < right)
  1910. {
  1911. while (comparer.Compare(keys.GetValue(++left), pivot) < 0) ;
  1912. while (comparer.Compare(pivot, keys.GetValue(--right)) < 0) ;
  1913. if (left >= right)
  1914. break;
  1915. Swap(left, right);
  1916. }
  1917. // Put pivot in the right location.
  1918. Swap(left, hi - 1);
  1919. return left;
  1920. }
  1921. private void Heapsort(int lo, int hi)
  1922. {
  1923. int n = hi - lo + 1;
  1924. for (int i = n / 2; i >= 1; i--)
  1925. {
  1926. DownHeap(i, n, lo);
  1927. }
  1928. for (int i = n; i > 1; i--)
  1929. {
  1930. Swap(lo, lo + i - 1);
  1931. DownHeap(1, i - 1, lo);
  1932. }
  1933. }
  1934. private void DownHeap(int i, int n, int lo)
  1935. {
  1936. object? d = keys.GetValue(lo + i - 1);
  1937. object? dt = items?.GetValue(lo + i - 1);
  1938. int child;
  1939. while (i <= n / 2)
  1940. {
  1941. child = 2 * i;
  1942. if (child < n && comparer.Compare(keys.GetValue(lo + child - 1), keys.GetValue(lo + child)) < 0)
  1943. {
  1944. child++;
  1945. }
  1946. if (!(comparer.Compare(d, keys.GetValue(lo + child - 1)) < 0))
  1947. break;
  1948. keys.SetValue(keys.GetValue(lo + child - 1), lo + i - 1);
  1949. if (items != null)
  1950. items.SetValue(items.GetValue(lo + child - 1), lo + i - 1);
  1951. i = child;
  1952. }
  1953. keys.SetValue(d, lo + i - 1);
  1954. if (items != null)
  1955. items.SetValue(dt, lo + i - 1);
  1956. }
  1957. private void InsertionSort(int lo, int hi)
  1958. {
  1959. int i, j;
  1960. object? t;
  1961. object? dt;
  1962. for (i = lo; i < hi; i++)
  1963. {
  1964. j = i;
  1965. t = keys.GetValue(i + 1);
  1966. dt = items?.GetValue(i + 1);
  1967. while (j >= lo && comparer.Compare(t, keys.GetValue(j)) < 0)
  1968. {
  1969. keys.SetValue(keys.GetValue(j), j + 1);
  1970. if (items != null)
  1971. items.SetValue(items.GetValue(j), j + 1);
  1972. j--;
  1973. }
  1974. keys.SetValue(t, j + 1);
  1975. if (items != null)
  1976. items.SetValue(dt, j + 1);
  1977. }
  1978. }
  1979. }
  1980. private static Span<T> UnsafeArrayAsSpan<T>(Array array, int adjustedIndex, int length) =>
  1981. new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length);
  1982. #if !CORERT
  1983. public IEnumerator GetEnumerator()
  1984. {
  1985. int lowerBound = GetLowerBound(0);
  1986. if (Rank == 1 && lowerBound == 0)
  1987. return new SZArrayEnumerator(this);
  1988. else
  1989. return new ArrayEnumerator(this, lowerBound, Length);
  1990. }
  1991. #endif // !CORERT
  1992. }
  1993. }