MemoryExtensions.cs 101 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004
  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.Generic;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7. using Internal.Runtime.CompilerServices;
  8. #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
  9. #if BIT64
  10. using nuint = System.UInt64;
  11. #else
  12. using nuint = System.UInt32;
  13. #endif // BIT64
  14. namespace System
  15. {
  16. /// <summary>
  17. /// Extension methods for Span{T}, Memory{T}, and friends.
  18. /// </summary>
  19. public static partial class MemoryExtensions
  20. {
  21. /// <summary>
  22. /// Creates a new span over the portion of the target array.
  23. /// </summary>
  24. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  25. public static Span<T> AsSpan<T>(this T[]? array, int start)
  26. {
  27. if (array == null)
  28. {
  29. if (start != 0)
  30. ThrowHelper.ThrowArgumentOutOfRangeException();
  31. return default;
  32. }
  33. if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  34. ThrowHelper.ThrowArrayTypeMismatchException();
  35. if ((uint)start > (uint)array.Length)
  36. ThrowHelper.ThrowArgumentOutOfRangeException();
  37. return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), array.Length - start);
  38. }
  39. /// <summary>
  40. /// Creates a new span over the portion of the target array.
  41. /// </summary>
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. public static Span<T> AsSpan<T>(this T[]? array, Index startIndex)
  44. {
  45. if (array == null)
  46. {
  47. if (!startIndex.Equals(Index.Start))
  48. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  49. return default;
  50. }
  51. if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  52. ThrowHelper.ThrowArrayTypeMismatchException();
  53. int actualIndex = startIndex.GetOffset(array.Length);
  54. if ((uint)actualIndex > (uint)array.Length)
  55. ThrowHelper.ThrowArgumentOutOfRangeException();
  56. return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), actualIndex), array.Length - actualIndex);
  57. }
  58. /// <summary>
  59. /// Creates a new span over the portion of the target array.
  60. /// </summary>
  61. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  62. public static Span<T> AsSpan<T>(this T[]? array, Range range)
  63. {
  64. if (array == null)
  65. {
  66. Index startIndex = range.Start;
  67. Index endIndex = range.End;
  68. if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
  69. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  70. return default;
  71. }
  72. if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  73. ThrowHelper.ThrowArrayTypeMismatchException();
  74. (int start, int length) = range.GetOffsetAndLength(array.Length);
  75. return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), length);
  76. }
  77. /// <summary>
  78. /// Creates a new readonly span over the portion of the target string.
  79. /// </summary>
  80. /// <param name="text">The target string.</param>
  81. /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
  82. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  83. public static ReadOnlySpan<char> AsSpan(this string? text)
  84. {
  85. if (text == null)
  86. return default;
  87. return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
  88. }
  89. /// <summary>
  90. /// Creates a new readonly span over the portion of the target string.
  91. /// </summary>
  92. /// <param name="text">The target string.</param>
  93. /// <param name="start">The index at which to begin this slice.</param>
  94. /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is null.</exception>
  95. /// <exception cref="System.ArgumentOutOfRangeException">
  96. /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
  97. /// </exception>
  98. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  99. public static ReadOnlySpan<char> AsSpan(this string? text, int start)
  100. {
  101. if (text == null)
  102. {
  103. if (start != 0)
  104. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  105. return default;
  106. }
  107. if ((uint)start > (uint)text.Length)
  108. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  109. return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
  110. }
  111. /// <summary>
  112. /// Creates a new readonly span over the portion of the target string.
  113. /// </summary>
  114. /// <param name="text">The target string.</param>
  115. /// <param name="start">The index at which to begin this slice.</param>
  116. /// <param name="length">The desired length for the slice (exclusive).</param>
  117. /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
  118. /// <exception cref="System.ArgumentOutOfRangeException">
  119. /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
  120. /// </exception>
  121. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  122. public static ReadOnlySpan<char> AsSpan(this string? text, int start, int length)
  123. {
  124. if (text == null)
  125. {
  126. if (start != 0 || length != 0)
  127. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  128. return default;
  129. }
  130. #if BIT64
  131. // See comment in Span<T>.Slice for how this works.
  132. if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
  133. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  134. #else
  135. if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
  136. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  137. #endif
  138. return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
  139. }
  140. /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
  141. /// <param name="text">The target string.</param>
  142. /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
  143. public static ReadOnlyMemory<char> AsMemory(this string? text)
  144. {
  145. if (text == null)
  146. return default;
  147. return new ReadOnlyMemory<char>(text, 0, text.Length);
  148. }
  149. /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
  150. /// <param name="text">The target string.</param>
  151. /// <param name="start">The index at which to begin this slice.</param>
  152. /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
  153. /// <exception cref="System.ArgumentOutOfRangeException">
  154. /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
  155. /// </exception>
  156. public static ReadOnlyMemory<char> AsMemory(this string? text, int start)
  157. {
  158. if (text == null)
  159. {
  160. if (start != 0)
  161. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  162. return default;
  163. }
  164. if ((uint)start > (uint)text.Length)
  165. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  166. return new ReadOnlyMemory<char>(text, start, text.Length - start);
  167. }
  168. /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
  169. /// <param name="text">The target string.</param>
  170. /// <param name="startIndex">The index at which to begin this slice.</param>
  171. public static ReadOnlyMemory<char> AsMemory(this string? text, Index startIndex)
  172. {
  173. if (text == null)
  174. {
  175. if (!startIndex.Equals(Index.Start))
  176. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
  177. return default;
  178. }
  179. int actualIndex = startIndex.GetOffset(text.Length);
  180. if ((uint)actualIndex > (uint)text.Length)
  181. ThrowHelper.ThrowArgumentOutOfRangeException();
  182. return new ReadOnlyMemory<char>(text, actualIndex, text.Length - actualIndex);
  183. }
  184. /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
  185. /// <param name="text">The target string.</param>
  186. /// <param name="start">The index at which to begin this slice.</param>
  187. /// <param name="length">The desired length for the slice (exclusive).</param>
  188. /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
  189. /// <exception cref="System.ArgumentOutOfRangeException">
  190. /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
  191. /// </exception>
  192. public static ReadOnlyMemory<char> AsMemory(this string? text, int start, int length)
  193. {
  194. if (text == null)
  195. {
  196. if (start != 0 || length != 0)
  197. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  198. return default;
  199. }
  200. #if BIT64
  201. // See comment in Span<T>.Slice for how this works.
  202. if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
  203. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  204. #else
  205. if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
  206. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  207. #endif
  208. return new ReadOnlyMemory<char>(text, start, length);
  209. }
  210. /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
  211. /// <param name="text">The target string.</param>
  212. /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
  213. public static ReadOnlyMemory<char> AsMemory(this string? text, Range range)
  214. {
  215. if (text == null)
  216. {
  217. Index startIndex = range.Start;
  218. Index endIndex = range.End;
  219. if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
  220. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
  221. return default;
  222. }
  223. (int start, int length) = range.GetOffsetAndLength(text.Length);
  224. return new ReadOnlyMemory<char>(text, start, length);
  225. }
  226. /// <summary>
  227. /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
  228. /// </summary>
  229. /// <typeparam name="T"></typeparam>
  230. /// <param name="span">The span to search.</param>
  231. /// <param name="value">The value to search for.</param>
  232. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  233. public static bool Contains<T>(this Span<T> span, T value)
  234. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  235. where T : IEquatable<T>
  236. #nullable restore
  237. {
  238. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  239. {
  240. if (Unsafe.SizeOf<T>() == sizeof(byte))
  241. return SpanHelpers.Contains(
  242. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  243. Unsafe.As<T, byte>(ref value),
  244. span.Length);
  245. if (Unsafe.SizeOf<T>() == sizeof(char))
  246. return SpanHelpers.Contains(
  247. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  248. Unsafe.As<T, char>(ref value),
  249. span.Length);
  250. }
  251. return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
  252. }
  253. /// <summary>
  254. /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
  255. /// </summary>
  256. /// <typeparam name="T"></typeparam>
  257. /// <param name="span">The span to search.</param>
  258. /// <param name="value">The value to search for.</param>
  259. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  260. public static bool Contains<T>(this ReadOnlySpan<T> span, T value)
  261. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  262. where T : IEquatable<T>
  263. #nullable restore
  264. {
  265. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  266. {
  267. if (Unsafe.SizeOf<T>() == sizeof(byte))
  268. return SpanHelpers.Contains(
  269. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  270. Unsafe.As<T, byte>(ref value),
  271. span.Length);
  272. if (Unsafe.SizeOf<T>() == sizeof(char))
  273. return SpanHelpers.Contains(
  274. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  275. Unsafe.As<T, char>(ref value),
  276. span.Length);
  277. }
  278. return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
  279. }
  280. /// <summary>
  281. /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  282. /// </summary>
  283. /// <param name="span">The span to search.</param>
  284. /// <param name="value">The value to search for.</param>
  285. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  286. public static int IndexOf<T>(this Span<T> span, T value)
  287. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  288. where T : IEquatable<T>
  289. #nullable restore
  290. {
  291. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  292. {
  293. if (Unsafe.SizeOf<T>() == sizeof(byte))
  294. return SpanHelpers.IndexOf(
  295. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  296. Unsafe.As<T, byte>(ref value),
  297. span.Length);
  298. if (Unsafe.SizeOf<T>() == sizeof(char))
  299. return SpanHelpers.IndexOf(
  300. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  301. Unsafe.As<T, char>(ref value),
  302. span.Length);
  303. }
  304. return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
  305. }
  306. /// <summary>
  307. /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  308. /// </summary>
  309. /// <param name="span">The span to search.</param>
  310. /// <param name="value">The sequence to search for.</param>
  311. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  312. public static int IndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
  313. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  314. where T : IEquatable<T>
  315. #nullable restore
  316. {
  317. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  318. {
  319. if (Unsafe.SizeOf<T>() == sizeof(byte))
  320. return SpanHelpers.IndexOf(
  321. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  322. span.Length,
  323. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  324. value.Length);
  325. if (Unsafe.SizeOf<T>() == sizeof(char))
  326. return SpanHelpers.IndexOf(
  327. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  328. span.Length,
  329. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
  330. value.Length);
  331. }
  332. return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
  333. }
  334. /// <summary>
  335. /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  336. /// </summary>
  337. /// <param name="span">The span to search.</param>
  338. /// <param name="value">The value to search for.</param>
  339. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  340. public static int LastIndexOf<T>(this Span<T> span, T value)
  341. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  342. where T : IEquatable<T>
  343. #nullable restore
  344. {
  345. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  346. {
  347. if (Unsafe.SizeOf<T>() == sizeof(byte))
  348. return SpanHelpers.LastIndexOf(
  349. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  350. Unsafe.As<T, byte>(ref value),
  351. span.Length);
  352. if (Unsafe.SizeOf<T>() == sizeof(char))
  353. return SpanHelpers.LastIndexOf(
  354. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  355. Unsafe.As<T, char>(ref value),
  356. span.Length);
  357. }
  358. return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
  359. }
  360. /// <summary>
  361. /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  362. /// </summary>
  363. /// <param name="span">The span to search.</param>
  364. /// <param name="value">The sequence to search for.</param>
  365. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  366. public static int LastIndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
  367. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  368. where T : IEquatable<T>
  369. #nullable restore
  370. {
  371. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  372. return SpanHelpers.LastIndexOf(
  373. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  374. span.Length,
  375. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  376. value.Length);
  377. return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
  378. }
  379. /// <summary>
  380. /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
  381. /// </summary>
  382. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  383. public static bool SequenceEqual<T>(this Span<T> span, ReadOnlySpan<T> other)
  384. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  385. where T : IEquatable<T>
  386. #nullable restore
  387. {
  388. int length = span.Length;
  389. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  390. {
  391. nuint size = (nuint)Unsafe.SizeOf<T>();
  392. return length == other.Length &&
  393. SpanHelpers.SequenceEqual(
  394. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  395. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
  396. ((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  397. }
  398. return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
  399. }
  400. /// <summary>
  401. /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
  402. /// </summary>
  403. public static int SequenceCompareTo<T>(this Span<T> span, ReadOnlySpan<T> other)
  404. where T : IComparable<T>
  405. {
  406. // Can't use IsBitwiseEquatable<T>() below because that only tells us about
  407. // equality checks, not about CompareTo checks.
  408. if (typeof(T) == typeof(byte))
  409. return SpanHelpers.SequenceCompareTo(
  410. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  411. span.Length,
  412. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
  413. other.Length);
  414. if (typeof(T) == typeof(char))
  415. return SpanHelpers.SequenceCompareTo(
  416. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  417. span.Length,
  418. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(other)),
  419. other.Length);
  420. return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length);
  421. }
  422. /// <summary>
  423. /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  424. /// </summary>
  425. /// <param name="span">The span to search.</param>
  426. /// <param name="value">The value to search for.</param>
  427. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  428. public static int IndexOf<T>(this ReadOnlySpan<T> span, T value)
  429. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  430. where T : IEquatable<T>
  431. #nullable restore
  432. {
  433. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  434. {
  435. if (Unsafe.SizeOf<T>() == sizeof(byte))
  436. return SpanHelpers.IndexOf(
  437. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  438. Unsafe.As<T, byte>(ref value),
  439. span.Length);
  440. if (Unsafe.SizeOf<T>() == sizeof(char))
  441. return SpanHelpers.IndexOf(
  442. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  443. Unsafe.As<T, char>(ref value),
  444. span.Length);
  445. }
  446. return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
  447. }
  448. /// <summary>
  449. /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  450. /// </summary>
  451. /// <param name="span">The span to search.</param>
  452. /// <param name="value">The sequence to search for.</param>
  453. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  454. public static int IndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
  455. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  456. where T : IEquatable<T>
  457. #nullable restore
  458. {
  459. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  460. {
  461. if (Unsafe.SizeOf<T>() == sizeof(byte))
  462. return SpanHelpers.IndexOf(
  463. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  464. span.Length,
  465. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  466. value.Length);
  467. if (Unsafe.SizeOf<T>() == sizeof(char))
  468. return SpanHelpers.IndexOf(
  469. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  470. span.Length,
  471. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
  472. value.Length);
  473. }
  474. return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
  475. }
  476. /// <summary>
  477. /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  478. /// </summary>
  479. /// <param name="span">The span to search.</param>
  480. /// <param name="value">The value to search for.</param>
  481. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  482. public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value)
  483. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  484. where T : IEquatable<T>
  485. #nullable restore
  486. {
  487. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  488. {
  489. if (Unsafe.SizeOf<T>() == sizeof(byte))
  490. return SpanHelpers.LastIndexOf(
  491. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  492. Unsafe.As<T, byte>(ref value),
  493. span.Length);
  494. if (Unsafe.SizeOf<T>() == sizeof(char))
  495. return SpanHelpers.LastIndexOf(
  496. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  497. Unsafe.As<T, char>(ref value),
  498. span.Length);
  499. }
  500. return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
  501. }
  502. /// <summary>
  503. /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
  504. /// </summary>
  505. /// <param name="span">The span to search.</param>
  506. /// <param name="value">The sequence to search for.</param>
  507. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  508. public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
  509. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  510. where T : IEquatable<T>
  511. #nullable restore
  512. {
  513. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  514. return SpanHelpers.LastIndexOf(
  515. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  516. span.Length,
  517. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  518. value.Length);
  519. return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
  520. }
  521. /// <summary>
  522. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  523. /// </summary>
  524. /// <param name="span">The span to search.</param>
  525. /// <param name="value0">One of the values to search for.</param>
  526. /// <param name="value1">One of the values to search for.</param>
  527. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  528. public static int IndexOfAny<T>(this Span<T> span, T value0, T value1)
  529. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  530. where T : IEquatable<T>
  531. #nullable restore
  532. {
  533. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  534. {
  535. if (Unsafe.SizeOf<T>() == sizeof(byte))
  536. return SpanHelpers.IndexOfAny(
  537. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  538. Unsafe.As<T, byte>(ref value0),
  539. Unsafe.As<T, byte>(ref value1),
  540. span.Length);
  541. if (Unsafe.SizeOf<T>() == sizeof(char))
  542. return SpanHelpers.IndexOfAny(
  543. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  544. Unsafe.As<T, char>(ref value0),
  545. Unsafe.As<T, char>(ref value1),
  546. span.Length);
  547. }
  548. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
  549. }
  550. /// <summary>
  551. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  552. /// </summary>
  553. /// <param name="span">The span to search.</param>
  554. /// <param name="value0">One of the values to search for.</param>
  555. /// <param name="value1">One of the values to search for.</param>
  556. /// <param name="value2">One of the values to search for.</param>
  557. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  558. public static int IndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
  559. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  560. where T : IEquatable<T>
  561. #nullable restore
  562. {
  563. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  564. {
  565. if (Unsafe.SizeOf<T>() == sizeof(byte))
  566. return SpanHelpers.IndexOfAny(
  567. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  568. Unsafe.As<T, byte>(ref value0),
  569. Unsafe.As<T, byte>(ref value1),
  570. Unsafe.As<T, byte>(ref value2),
  571. span.Length);
  572. if (Unsafe.SizeOf<T>() == sizeof(char))
  573. return SpanHelpers.IndexOfAny(
  574. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  575. Unsafe.As<T, char>(ref value0),
  576. Unsafe.As<T, char>(ref value1),
  577. Unsafe.As<T, char>(ref value2),
  578. span.Length);
  579. }
  580. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
  581. }
  582. /// <summary>
  583. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  584. /// </summary>
  585. /// <param name="span">The span to search.</param>
  586. /// <param name="values">The set of values to search for.</param>
  587. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  588. public static int IndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
  589. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  590. where T : IEquatable<T>
  591. #nullable restore
  592. {
  593. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  594. {
  595. if (Unsafe.SizeOf<T>() == sizeof(byte))
  596. {
  597. ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
  598. if (values.Length == 2)
  599. {
  600. return SpanHelpers.IndexOfAny(
  601. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  602. valueRef,
  603. Unsafe.Add(ref valueRef, 1),
  604. span.Length);
  605. }
  606. else if (values.Length == 3)
  607. {
  608. return SpanHelpers.IndexOfAny(
  609. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  610. valueRef,
  611. Unsafe.Add(ref valueRef, 1),
  612. Unsafe.Add(ref valueRef, 2),
  613. span.Length);
  614. }
  615. else
  616. {
  617. return SpanHelpers.IndexOfAny(
  618. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  619. span.Length,
  620. ref valueRef,
  621. values.Length);
  622. }
  623. }
  624. if (Unsafe.SizeOf<T>() == sizeof(char))
  625. {
  626. ref char valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
  627. if (values.Length == 5)
  628. {
  629. // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
  630. return SpanHelpers.IndexOfAny(
  631. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  632. valueRef,
  633. Unsafe.Add(ref valueRef, 1),
  634. Unsafe.Add(ref valueRef, 2),
  635. Unsafe.Add(ref valueRef, 3),
  636. Unsafe.Add(ref valueRef, 4),
  637. span.Length);
  638. }
  639. else if (values.Length == 2)
  640. {
  641. // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
  642. return SpanHelpers.IndexOfAny(
  643. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  644. valueRef,
  645. Unsafe.Add(ref valueRef, 1),
  646. span.Length);
  647. }
  648. else if (values.Length == 4)
  649. {
  650. // Length 4 before 3 as 3 has an explicit overload
  651. return SpanHelpers.IndexOfAny(
  652. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  653. valueRef,
  654. Unsafe.Add(ref valueRef, 1),
  655. Unsafe.Add(ref valueRef, 2),
  656. Unsafe.Add(ref valueRef, 3),
  657. span.Length);
  658. }
  659. else if (values.Length == 3)
  660. {
  661. return SpanHelpers.IndexOfAny(
  662. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  663. valueRef,
  664. Unsafe.Add(ref valueRef, 1),
  665. Unsafe.Add(ref valueRef, 2),
  666. span.Length);
  667. }
  668. else if (values.Length == 1)
  669. {
  670. // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
  671. // is already throwing away a bunch of performance vs just calling IndexOf
  672. return SpanHelpers.IndexOf(
  673. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  674. valueRef,
  675. span.Length);
  676. }
  677. }
  678. }
  679. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
  680. }
  681. /// <summary>
  682. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  683. /// </summary>
  684. /// <param name="span">The span to search.</param>
  685. /// <param name="value0">One of the values to search for.</param>
  686. /// <param name="value1">One of the values to search for.</param>
  687. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  688. public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
  689. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  690. where T : IEquatable<T>
  691. #nullable restore
  692. {
  693. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  694. {
  695. if (Unsafe.SizeOf<T>() == sizeof(byte))
  696. return SpanHelpers.IndexOfAny(
  697. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  698. Unsafe.As<T, byte>(ref value0),
  699. Unsafe.As<T, byte>(ref value1),
  700. span.Length);
  701. if (Unsafe.SizeOf<T>() == sizeof(char))
  702. return SpanHelpers.IndexOfAny(
  703. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  704. Unsafe.As<T, char>(ref value0),
  705. Unsafe.As<T, char>(ref value1),
  706. span.Length);
  707. }
  708. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
  709. }
  710. /// <summary>
  711. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  712. /// </summary>
  713. /// <param name="span">The span to search.</param>
  714. /// <param name="value0">One of the values to search for.</param>
  715. /// <param name="value1">One of the values to search for.</param>
  716. /// <param name="value2">One of the values to search for.</param>
  717. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  718. public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
  719. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  720. where T : IEquatable<T>
  721. #nullable restore
  722. {
  723. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  724. {
  725. if (Unsafe.SizeOf<T>() == sizeof(byte))
  726. return SpanHelpers.IndexOfAny(
  727. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  728. Unsafe.As<T, byte>(ref value0),
  729. Unsafe.As<T, byte>(ref value1),
  730. Unsafe.As<T, byte>(ref value2),
  731. span.Length);
  732. if (Unsafe.SizeOf<T>() == sizeof(char))
  733. return SpanHelpers.IndexOfAny(
  734. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  735. Unsafe.As<T, char>(ref value0),
  736. Unsafe.As<T, char>(ref value1),
  737. Unsafe.As<T, char>(ref value2),
  738. span.Length);
  739. }
  740. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
  741. }
  742. /// <summary>
  743. /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
  744. /// </summary>
  745. /// <param name="span">The span to search.</param>
  746. /// <param name="values">The set of values to search for.</param>
  747. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  748. public static int IndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
  749. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  750. where T : IEquatable<T>
  751. #nullable restore
  752. {
  753. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  754. {
  755. if (Unsafe.SizeOf<T>() == sizeof(byte))
  756. {
  757. ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
  758. if (values.Length == 2)
  759. {
  760. return SpanHelpers.IndexOfAny(
  761. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  762. valueRef,
  763. Unsafe.Add(ref valueRef, 1),
  764. span.Length);
  765. }
  766. else if (values.Length == 3)
  767. {
  768. return SpanHelpers.IndexOfAny(
  769. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  770. valueRef,
  771. Unsafe.Add(ref valueRef, 1),
  772. Unsafe.Add(ref valueRef, 2),
  773. span.Length);
  774. }
  775. else
  776. {
  777. return SpanHelpers.IndexOfAny(
  778. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  779. span.Length,
  780. ref valueRef,
  781. values.Length);
  782. }
  783. }
  784. if (Unsafe.SizeOf<T>() == sizeof(char))
  785. {
  786. ref char valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
  787. if (values.Length == 5)
  788. {
  789. // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
  790. return SpanHelpers.IndexOfAny(
  791. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  792. valueRef,
  793. Unsafe.Add(ref valueRef, 1),
  794. Unsafe.Add(ref valueRef, 2),
  795. Unsafe.Add(ref valueRef, 3),
  796. Unsafe.Add(ref valueRef, 4),
  797. span.Length);
  798. }
  799. else if (values.Length == 2)
  800. {
  801. // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
  802. return SpanHelpers.IndexOfAny(
  803. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  804. valueRef,
  805. Unsafe.Add(ref valueRef, 1),
  806. span.Length);
  807. }
  808. else if (values.Length == 4)
  809. {
  810. // Length 4 before 3 as 3 has an explicit overload
  811. return SpanHelpers.IndexOfAny(
  812. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  813. valueRef,
  814. Unsafe.Add(ref valueRef, 1),
  815. Unsafe.Add(ref valueRef, 2),
  816. Unsafe.Add(ref valueRef, 3),
  817. span.Length);
  818. }
  819. else if (values.Length == 3)
  820. {
  821. return SpanHelpers.IndexOfAny(
  822. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  823. valueRef,
  824. Unsafe.Add(ref valueRef, 1),
  825. Unsafe.Add(ref valueRef, 2),
  826. span.Length);
  827. }
  828. else if (values.Length == 1)
  829. {
  830. // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
  831. // is already throwing away a bunch of performance vs just calling IndexOf
  832. return SpanHelpers.IndexOf(
  833. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  834. valueRef,
  835. span.Length);
  836. }
  837. }
  838. }
  839. return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
  840. }
  841. /// <summary>
  842. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  843. /// </summary>
  844. /// <param name="span">The span to search.</param>
  845. /// <param name="value0">One of the values to search for.</param>
  846. /// <param name="value1">One of the values to search for.</param>
  847. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  848. public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1)
  849. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  850. where T : IEquatable<T>
  851. #nullable restore
  852. {
  853. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  854. return SpanHelpers.LastIndexOfAny(
  855. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  856. Unsafe.As<T, byte>(ref value0),
  857. Unsafe.As<T, byte>(ref value1),
  858. span.Length);
  859. return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
  860. }
  861. /// <summary>
  862. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  863. /// </summary>
  864. /// <param name="span">The span to search.</param>
  865. /// <param name="value0">One of the values to search for.</param>
  866. /// <param name="value1">One of the values to search for.</param>
  867. /// <param name="value2">One of the values to search for.</param>
  868. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  869. public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
  870. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  871. where T : IEquatable<T>
  872. #nullable restore
  873. {
  874. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  875. return SpanHelpers.LastIndexOfAny(
  876. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  877. Unsafe.As<T, byte>(ref value0),
  878. Unsafe.As<T, byte>(ref value1),
  879. Unsafe.As<T, byte>(ref value2),
  880. span.Length);
  881. return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
  882. }
  883. /// <summary>
  884. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  885. /// </summary>
  886. /// <param name="span">The span to search.</param>
  887. /// <param name="values">The set of values to search for.</param>
  888. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  889. public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
  890. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  891. where T : IEquatable<T>
  892. #nullable restore
  893. {
  894. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  895. return SpanHelpers.LastIndexOfAny(
  896. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  897. span.Length,
  898. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
  899. values.Length);
  900. return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
  901. }
  902. /// <summary>
  903. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  904. /// </summary>
  905. /// <param name="span">The span to search.</param>
  906. /// <param name="value0">One of the values to search for.</param>
  907. /// <param name="value1">One of the values to search for.</param>
  908. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  909. public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
  910. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  911. where T : IEquatable<T>
  912. #nullable restore
  913. {
  914. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  915. return SpanHelpers.LastIndexOfAny(
  916. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  917. Unsafe.As<T, byte>(ref value0),
  918. Unsafe.As<T, byte>(ref value1),
  919. span.Length);
  920. return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
  921. }
  922. /// <summary>
  923. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  924. /// </summary>
  925. /// <param name="span">The span to search.</param>
  926. /// <param name="value0">One of the values to search for.</param>
  927. /// <param name="value1">One of the values to search for.</param>
  928. /// <param name="value2">One of the values to search for.</param>
  929. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  930. public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
  931. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  932. where T : IEquatable<T>
  933. #nullable restore
  934. {
  935. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  936. return SpanHelpers.LastIndexOfAny(
  937. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  938. Unsafe.As<T, byte>(ref value0),
  939. Unsafe.As<T, byte>(ref value1),
  940. Unsafe.As<T, byte>(ref value2),
  941. span.Length);
  942. return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
  943. }
  944. /// <summary>
  945. /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
  946. /// </summary>
  947. /// <param name="span">The span to search.</param>
  948. /// <param name="values">The set of values to search for.</param>
  949. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  950. public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
  951. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  952. where T : IEquatable<T>
  953. #nullable restore
  954. {
  955. if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
  956. return SpanHelpers.LastIndexOfAny(
  957. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  958. span.Length,
  959. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)),
  960. values.Length);
  961. return SpanHelpers.LastIndexOfAny<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
  962. }
  963. /// <summary>
  964. /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
  965. /// </summary>
  966. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  967. public static bool SequenceEqual<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
  968. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  969. where T : IEquatable<T>
  970. #nullable restore
  971. {
  972. int length = span.Length;
  973. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  974. {
  975. nuint size = (nuint)Unsafe.SizeOf<T>();
  976. return length == other.Length &&
  977. SpanHelpers.SequenceEqual(
  978. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  979. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
  980. ((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  981. }
  982. return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
  983. }
  984. /// <summary>
  985. /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
  986. /// </summary>
  987. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  988. public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
  989. where T : IComparable<T>
  990. {
  991. // Can't use IsBitwiseEquatable<T>() below because that only tells us about
  992. // equality checks, not about CompareTo checks.
  993. if (typeof(T) == typeof(byte))
  994. return SpanHelpers.SequenceCompareTo(
  995. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  996. span.Length,
  997. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
  998. other.Length);
  999. if (typeof(T) == typeof(char))
  1000. return SpanHelpers.SequenceCompareTo(
  1001. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
  1002. span.Length,
  1003. ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(other)),
  1004. other.Length);
  1005. return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length);
  1006. }
  1007. /// <summary>
  1008. /// Determines whether the specified sequence appears at the start of the span.
  1009. /// </summary>
  1010. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1011. public static bool StartsWith<T>(this Span<T> span, ReadOnlySpan<T> value)
  1012. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  1013. where T : IEquatable<T>
  1014. #nullable restore
  1015. {
  1016. int valueLength = value.Length;
  1017. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  1018. {
  1019. nuint size = (nuint)Unsafe.SizeOf<T>();
  1020. return valueLength <= span.Length &&
  1021. SpanHelpers.SequenceEqual(
  1022. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  1023. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  1024. ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  1025. }
  1026. return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
  1027. }
  1028. /// <summary>
  1029. /// Determines whether the specified sequence appears at the start of the span.
  1030. /// </summary>
  1031. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1032. public static bool StartsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
  1033. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  1034. where T : IEquatable<T>
  1035. #nullable restore
  1036. {
  1037. int valueLength = value.Length;
  1038. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  1039. {
  1040. nuint size = (nuint)Unsafe.SizeOf<T>();
  1041. return valueLength <= span.Length &&
  1042. SpanHelpers.SequenceEqual(
  1043. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
  1044. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  1045. ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  1046. }
  1047. return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
  1048. }
  1049. /// <summary>
  1050. /// Determines whether the specified sequence appears at the end of the span.
  1051. /// </summary>
  1052. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1053. public static bool EndsWith<T>(this Span<T> span, ReadOnlySpan<T> value)
  1054. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  1055. where T : IEquatable<T>
  1056. #nullable restore
  1057. {
  1058. int spanLength = span.Length;
  1059. int valueLength = value.Length;
  1060. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  1061. {
  1062. nuint size = (nuint)Unsafe.SizeOf<T>();
  1063. return valueLength <= spanLength &&
  1064. SpanHelpers.SequenceEqual(
  1065. ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
  1066. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  1067. ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  1068. }
  1069. return valueLength <= spanLength &&
  1070. SpanHelpers.SequenceEqual(
  1071. ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength),
  1072. ref MemoryMarshal.GetReference(value),
  1073. valueLength);
  1074. }
  1075. /// <summary>
  1076. /// Determines whether the specified sequence appears at the end of the span.
  1077. /// </summary>
  1078. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1079. public static bool EndsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
  1080. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  1081. where T : IEquatable<T>
  1082. #nullable restore
  1083. {
  1084. int spanLength = span.Length;
  1085. int valueLength = value.Length;
  1086. if (RuntimeHelpers.IsBitwiseEquatable<T>())
  1087. {
  1088. nuint size = (nuint)Unsafe.SizeOf<T>();
  1089. return valueLength <= spanLength &&
  1090. SpanHelpers.SequenceEqual(
  1091. ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
  1092. ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
  1093. ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
  1094. }
  1095. return valueLength <= spanLength &&
  1096. SpanHelpers.SequenceEqual(
  1097. ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength),
  1098. ref MemoryMarshal.GetReference(value),
  1099. valueLength);
  1100. }
  1101. /// <summary>
  1102. /// Reverses the sequence of the elements in the entire span.
  1103. /// </summary>
  1104. public static void Reverse<T>(this Span<T> span)
  1105. {
  1106. if (span.Length <= 1)
  1107. {
  1108. return;
  1109. }
  1110. ref T first = ref MemoryMarshal.GetReference(span);
  1111. ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, span.Length), -1);
  1112. do
  1113. {
  1114. T temp = first;
  1115. first = last;
  1116. last = temp;
  1117. first = ref Unsafe.Add(ref first, 1);
  1118. last = ref Unsafe.Add(ref last, -1);
  1119. } while (Unsafe.IsAddressLessThan(ref first, ref last));
  1120. }
  1121. /// <summary>
  1122. /// Creates a new span over the target array.
  1123. /// </summary>
  1124. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1125. public static Span<T> AsSpan<T>(this T[]? array)
  1126. {
  1127. return new Span<T>(array);
  1128. }
  1129. /// <summary>
  1130. /// Creates a new Span over the portion of the target array beginning
  1131. /// at 'start' index and ending at 'end' index (exclusive).
  1132. /// </summary>
  1133. /// <param name="array">The target array.</param>
  1134. /// <param name="start">The index at which to begin the Span.</param>
  1135. /// <param name="length">The number of items in the Span.</param>
  1136. /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
  1137. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
  1138. /// <exception cref="System.ArgumentOutOfRangeException">
  1139. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;Length).
  1140. /// </exception>
  1141. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1142. public static Span<T> AsSpan<T>(this T[]? array, int start, int length)
  1143. {
  1144. return new Span<T>(array, start, length);
  1145. }
  1146. /// <summary>
  1147. /// Creates a new span over the portion of the target array segment.
  1148. /// </summary>
  1149. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1150. public static Span<T> AsSpan<T>(this ArraySegment<T> segment)
  1151. {
  1152. return new Span<T>(segment.Array, segment.Offset, segment.Count);
  1153. }
  1154. /// <summary>
  1155. /// Creates a new Span over the portion of the target array beginning
  1156. /// at 'start' index and ending at 'end' index (exclusive).
  1157. /// </summary>
  1158. /// <param name="segment">The target array.</param>
  1159. /// <param name="start">The index at which to begin the Span.</param>
  1160. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1161. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1162. /// <exception cref="System.ArgumentOutOfRangeException">
  1163. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;segment.Count).
  1164. /// </exception>
  1165. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1166. public static Span<T> AsSpan<T>(this ArraySegment<T> segment, int start)
  1167. {
  1168. if (((uint)start) > (uint)segment.Count)
  1169. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1170. return new Span<T>(segment.Array, segment.Offset + start, segment.Count - start);
  1171. }
  1172. /// <summary>
  1173. /// Creates a new Span over the portion of the target array beginning
  1174. /// at 'startIndex' and ending at the end of the segment.
  1175. /// </summary>
  1176. /// <param name="segment">The target array.</param>
  1177. /// <param name="startIndex">The index at which to begin the Span.</param>
  1178. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1179. public static Span<T> AsSpan<T>(this ArraySegment<T> segment, Index startIndex)
  1180. {
  1181. int actualIndex = startIndex.GetOffset(segment.Count);
  1182. return AsSpan(segment, actualIndex);
  1183. }
  1184. /// <summary>
  1185. /// Creates a new Span over the portion of the target array beginning
  1186. /// at 'start' index and ending at 'end' index (exclusive).
  1187. /// </summary>
  1188. /// <param name="segment">The target array.</param>
  1189. /// <param name="start">The index at which to begin the Span.</param>
  1190. /// <param name="length">The number of items in the Span.</param>
  1191. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1192. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1193. /// <exception cref="System.ArgumentOutOfRangeException">
  1194. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;segment.Count).
  1195. /// </exception>
  1196. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1197. public static Span<T> AsSpan<T>(this ArraySegment<T> segment, int start, int length)
  1198. {
  1199. if (((uint)start) > (uint)segment.Count)
  1200. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1201. if (((uint)length) > (uint)(segment.Count - start))
  1202. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
  1203. return new Span<T>(segment.Array, segment.Offset + start, length);
  1204. }
  1205. /// <summary>
  1206. /// Creates a new Span over the portion of the target array using the range start and end indexes
  1207. /// </summary>
  1208. /// <param name="segment">The target array.</param>
  1209. /// <param name="range">The range which has start and end indexes to use for slicing the array.</param>
  1210. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1211. public static Span<T> AsSpan<T>(this ArraySegment<T> segment, Range range)
  1212. {
  1213. (int start, int length) = range.GetOffsetAndLength(segment.Count);
  1214. return new Span<T>(segment.Array, segment.Offset + start, length);
  1215. }
  1216. /// <summary>
  1217. /// Creates a new memory over the target array.
  1218. /// </summary>
  1219. public static Memory<T> AsMemory<T>(this T[]? array) => new Memory<T>(array);
  1220. /// <summary>
  1221. /// Creates a new memory over the portion of the target array beginning
  1222. /// at 'start' index and ending at 'end' index (exclusive).
  1223. /// </summary>
  1224. /// <param name="array">The target array.</param>
  1225. /// <param name="start">The index at which to begin the memory.</param>
  1226. /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
  1227. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
  1228. /// <exception cref="System.ArgumentOutOfRangeException">
  1229. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;array.Length).
  1230. /// </exception>
  1231. public static Memory<T> AsMemory<T>(this T[]? array, int start) => new Memory<T>(array, start);
  1232. /// <summary>
  1233. /// Creates a new memory over the portion of the target array starting from
  1234. /// 'startIndex' to the end of the array.
  1235. /// </summary>
  1236. public static Memory<T> AsMemory<T>(this T[]? array, Index startIndex)
  1237. {
  1238. if (array == null)
  1239. {
  1240. if (!startIndex.Equals(Index.Start))
  1241. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1242. return default;
  1243. }
  1244. int actualIndex = startIndex.GetOffset(array.Length);
  1245. return new Memory<T>(array, actualIndex);
  1246. }
  1247. /// <summary>
  1248. /// Creates a new memory over the portion of the target array beginning
  1249. /// at 'start' index and ending at 'end' index (exclusive).
  1250. /// </summary>
  1251. /// <param name="array">The target array.</param>
  1252. /// <param name="start">The index at which to begin the memory.</param>
  1253. /// <param name="length">The number of items in the memory.</param>
  1254. /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
  1255. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
  1256. /// <exception cref="System.ArgumentOutOfRangeException">
  1257. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;Length).
  1258. /// </exception>
  1259. public static Memory<T> AsMemory<T>(this T[]? array, int start, int length) => new Memory<T>(array, start, length);
  1260. /// <summary>
  1261. /// Creates a new memory over the portion of the target array beginning at inclusive start index of the range
  1262. /// and ending at the exclusive end index of the range.
  1263. /// </summary>
  1264. public static Memory<T> AsMemory<T>(this T[]? array, Range range)
  1265. {
  1266. if (array == null)
  1267. {
  1268. Index startIndex = range.Start;
  1269. Index endIndex = range.End;
  1270. if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
  1271. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
  1272. return default;
  1273. }
  1274. (int start, int length) = range.GetOffsetAndLength(array.Length);
  1275. return new Memory<T>(array, start, length);
  1276. }
  1277. /// <summary>
  1278. /// Creates a new memory over the portion of the target array.
  1279. /// </summary>
  1280. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment) => new Memory<T>(segment.Array, segment.Offset, segment.Count);
  1281. /// <summary>
  1282. /// Creates a new memory over the portion of the target array beginning
  1283. /// at 'start' index and ending at 'end' index (exclusive).
  1284. /// </summary>
  1285. /// <param name="segment">The target array.</param>
  1286. /// <param name="start">The index at which to begin the memory.</param>
  1287. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1288. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1289. /// <exception cref="System.ArgumentOutOfRangeException">
  1290. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;segment.Count).
  1291. /// </exception>
  1292. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start)
  1293. {
  1294. if (((uint)start) > (uint)segment.Count)
  1295. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1296. return new Memory<T>(segment.Array, segment.Offset + start, segment.Count - start);
  1297. }
  1298. /// <summary>
  1299. /// Creates a new memory over the portion of the target array beginning
  1300. /// at 'start' index and ending at 'end' index (exclusive).
  1301. /// </summary>
  1302. /// <param name="segment">The target array.</param>
  1303. /// <param name="start">The index at which to begin the memory.</param>
  1304. /// <param name="length">The number of items in the memory.</param>
  1305. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1306. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1307. /// <exception cref="System.ArgumentOutOfRangeException">
  1308. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;segment.Count).
  1309. /// </exception>
  1310. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start, int length)
  1311. {
  1312. if (((uint)start) > (uint)segment.Count)
  1313. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1314. if (((uint)length) > (uint)(segment.Count - start))
  1315. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
  1316. return new Memory<T>(segment.Array, segment.Offset + start, length);
  1317. }
  1318. /// <summary>
  1319. /// Copies the contents of the array into the span. If the source
  1320. /// and destinations overlap, this method behaves as if the original values in
  1321. /// a temporary location before the destination is overwritten.
  1322. ///
  1323. ///<param name="source">The array to copy items from.</param>
  1324. /// <param name="destination">The span to copy items into.</param>
  1325. /// <exception cref="System.ArgumentException">
  1326. /// Thrown when the destination Span is shorter than the source array.
  1327. /// </exception>
  1328. /// </summary>
  1329. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1330. public static void CopyTo<T>(this T[]? source, Span<T> destination)
  1331. {
  1332. new ReadOnlySpan<T>(source).CopyTo(destination);
  1333. }
  1334. /// <summary>
  1335. /// Copies the contents of the array into the memory. If the source
  1336. /// and destinations overlap, this method behaves as if the original values are in
  1337. /// a temporary location before the destination is overwritten.
  1338. ///
  1339. ///<param name="source">The array to copy items from.</param>
  1340. /// <param name="destination">The memory to copy items into.</param>
  1341. /// <exception cref="System.ArgumentException">
  1342. /// Thrown when the destination is shorter than the source array.
  1343. /// </exception>
  1344. /// </summary>
  1345. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1346. public static void CopyTo<T>(this T[]? source, Memory<T> destination)
  1347. {
  1348. source.CopyTo(destination.Span);
  1349. }
  1350. //
  1351. // Overlaps
  1352. // ========
  1353. //
  1354. // The following methods can be used to determine if two sequences
  1355. // overlap in memory.
  1356. //
  1357. // Two sequences overlap if they have positions in common and neither
  1358. // is empty. Empty sequences do not overlap with any other sequence.
  1359. //
  1360. // If two sequences overlap, the element offset is the number of
  1361. // elements by which the second sequence is offset from the first
  1362. // sequence (i.e., second minus first). An exception is thrown if the
  1363. // number is not a whole number, which can happen when a sequence of a
  1364. // smaller type is cast to a sequence of a larger type with unsafe code
  1365. // or NonPortableCast. If the sequences do not overlap, the offset is
  1366. // meaningless and arbitrarily set to zero.
  1367. //
  1368. // Implementation
  1369. // --------------
  1370. //
  1371. // Implementing this correctly is quite tricky due of two problems:
  1372. //
  1373. // * If the sequences refer to two different objects on the managed
  1374. // heap, the garbage collector can move them freely around or change
  1375. // their relative order in memory.
  1376. //
  1377. // * The distance between two sequences can be greater than
  1378. // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit
  1379. // system).
  1380. //
  1381. // (For simplicity, the following text assumes a 32-bit system, but
  1382. // everything also applies to a 64-bit system if every 32 is replaced a
  1383. // 64.)
  1384. //
  1385. // The first problem is solved by calculating the distance with exactly
  1386. // one atomic operation. If the garbage collector happens to move the
  1387. // sequences afterwards and the sequences overlapped before, they will
  1388. // still overlap after the move and their distance hasn't changed. If
  1389. // the sequences did not overlap, the distance can change but the
  1390. // sequences still won't overlap.
  1391. //
  1392. // The second problem is solved by making all addresses relative to the
  1393. // start of the first sequence and performing all operations in
  1394. // unsigned integer arithmetic modulo 2^32.
  1395. //
  1396. // Example
  1397. // -------
  1398. //
  1399. // Let's say there are two sequences, x and y. Let
  1400. //
  1401. // ref T xRef = MemoryMarshal.GetReference(x)
  1402. // uint xLength = x.Length * Unsafe.SizeOf<T>()
  1403. // ref T yRef = MemoryMarshal.GetReference(y)
  1404. // uint yLength = y.Length * Unsafe.SizeOf<T>()
  1405. //
  1406. // Visually, the two sequences are located somewhere in the 32-bit
  1407. // address space as follows:
  1408. //
  1409. // [----------------------------------------------) normal address space
  1410. // 0 2^32
  1411. // [------------------) first sequence
  1412. // xRef xRef + xLength
  1413. // [--------------------------) . second sequence
  1414. // yRef . yRef + yLength
  1415. // : . . .
  1416. // : . . .
  1417. // . . .
  1418. // . . .
  1419. // . . .
  1420. // [----------------------------------------------) relative address space
  1421. // 0 . . 2^32
  1422. // [------------------) : first sequence
  1423. // x1 . x2 :
  1424. // -------------) [------------- second sequence
  1425. // y2 y1
  1426. //
  1427. // The idea is to make all addresses relative to xRef: Let x1 be the
  1428. // start address of x in this relative address space, x2 the end
  1429. // address of x, y1 the start address of y, and y2 the end address of
  1430. // y:
  1431. //
  1432. // nuint x1 = 0
  1433. // nuint x2 = xLength
  1434. // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef)
  1435. // nuint y2 = y1 + yLength
  1436. //
  1437. // xRef relative to xRef is 0.
  1438. //
  1439. // x2 is simply x1 + xLength. This cannot overflow.
  1440. //
  1441. // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is
  1442. // negative, casting it to an unsigned 32-bit integer turns it into
  1443. // (yRef - xRef + 2^32). So, in the example above, y1 moves to the right
  1444. // of x2.
  1445. //
  1446. // y2 is simply y1 + yLength. Note that this can overflow, as in the
  1447. // example above, which must be avoided.
  1448. //
  1449. // The two sequences do *not* overlap if y is entirely in the space
  1450. // right of x in the relative address space. (It can't be left of it!)
  1451. //
  1452. // (y1 >= x2) && (y2 <= 2^32)
  1453. //
  1454. // Inversely, they do overlap if
  1455. //
  1456. // (y1 < x2) || (y2 > 2^32)
  1457. //
  1458. // After substituting x2 and y2 with their respective definition:
  1459. //
  1460. // == (y1 < xLength) || (y1 + yLength > 2^32)
  1461. //
  1462. // Since yLength can't be greater than the size of the address space,
  1463. // the overflow can be avoided as follows:
  1464. //
  1465. // == (y1 < xLength) || (y1 > 2^32 - yLength)
  1466. //
  1467. // However, 2^32 cannot be stored in an unsigned 32-bit integer, so one
  1468. // more change is needed to keep doing everything with unsigned 32-bit
  1469. // integers:
  1470. //
  1471. // == (y1 < xLength) || (y1 > -yLength)
  1472. //
  1473. // Due to modulo arithmetic, this gives exactly same result *except* if
  1474. // yLength is zero, since 2^32 - 0 is 0 and not 2^32. So the case
  1475. // y.IsEmpty must be handled separately first.
  1476. //
  1477. /// <summary>
  1478. /// Determines whether two sequences overlap in memory.
  1479. /// </summary>
  1480. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1481. public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other)
  1482. {
  1483. return Overlaps((ReadOnlySpan<T>)span, other);
  1484. }
  1485. /// <summary>
  1486. /// Determines whether two sequences overlap in memory and outputs the element offset.
  1487. /// </summary>
  1488. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1489. public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other, out int elementOffset)
  1490. {
  1491. return Overlaps((ReadOnlySpan<T>)span, other, out elementOffset);
  1492. }
  1493. /// <summary>
  1494. /// Determines whether two sequences overlap in memory.
  1495. /// </summary>
  1496. public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
  1497. {
  1498. if (span.IsEmpty || other.IsEmpty)
  1499. {
  1500. return false;
  1501. }
  1502. IntPtr byteOffset = Unsafe.ByteOffset(
  1503. ref MemoryMarshal.GetReference(span),
  1504. ref MemoryMarshal.GetReference(other));
  1505. if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
  1506. {
  1507. return (uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) ||
  1508. (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>());
  1509. }
  1510. else
  1511. {
  1512. return (ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) ||
  1513. (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>());
  1514. }
  1515. }
  1516. /// <summary>
  1517. /// Determines whether two sequences overlap in memory and outputs the element offset.
  1518. /// </summary>
  1519. public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other, out int elementOffset)
  1520. {
  1521. if (span.IsEmpty || other.IsEmpty)
  1522. {
  1523. elementOffset = 0;
  1524. return false;
  1525. }
  1526. IntPtr byteOffset = Unsafe.ByteOffset(
  1527. ref MemoryMarshal.GetReference(span),
  1528. ref MemoryMarshal.GetReference(other));
  1529. if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
  1530. {
  1531. if ((uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) ||
  1532. (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>()))
  1533. {
  1534. if ((int)byteOffset % Unsafe.SizeOf<T>() != 0)
  1535. ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
  1536. elementOffset = (int)byteOffset / Unsafe.SizeOf<T>();
  1537. return true;
  1538. }
  1539. else
  1540. {
  1541. elementOffset = 0;
  1542. return false;
  1543. }
  1544. }
  1545. else
  1546. {
  1547. if ((ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) ||
  1548. (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>()))
  1549. {
  1550. if ((long)byteOffset % Unsafe.SizeOf<T>() != 0)
  1551. ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
  1552. elementOffset = (int)((long)byteOffset / Unsafe.SizeOf<T>());
  1553. return true;
  1554. }
  1555. else
  1556. {
  1557. elementOffset = 0;
  1558. return false;
  1559. }
  1560. }
  1561. }
  1562. /// <summary>
  1563. /// Searches an entire sorted <see cref="Span{T}"/> for a value
  1564. /// using the specified <see cref="IComparable{T}"/> generic interface.
  1565. /// </summary>
  1566. /// <typeparam name="T">The element type of the span.</typeparam>
  1567. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1568. /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
  1569. /// <returns>
  1570. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1571. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1572. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1573. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1574. /// </returns>
  1575. /// <exception cref="System.ArgumentNullException">
  1576. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1577. /// </exception>
  1578. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1579. public static int BinarySearch<T>(
  1580. this Span<T> span, IComparable<T> comparable)
  1581. {
  1582. return BinarySearch<T, IComparable<T>>(span, comparable);
  1583. }
  1584. /// <summary>
  1585. /// Searches an entire sorted <see cref="Span{T}"/> for a value
  1586. /// using the specified <typeparamref name="TComparable"/> generic type.
  1587. /// </summary>
  1588. /// <typeparam name="T">The element type of the span.</typeparam>
  1589. /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
  1590. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1591. /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
  1592. /// <returns>
  1593. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1594. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1595. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1596. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1597. /// </returns>
  1598. /// <exception cref="System.ArgumentNullException">
  1599. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1600. /// </exception>
  1601. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1602. public static int BinarySearch<T, TComparable>(
  1603. this Span<T> span, TComparable comparable)
  1604. where TComparable : IComparable<T>
  1605. {
  1606. return BinarySearch((ReadOnlySpan<T>)span, comparable);
  1607. }
  1608. /// <summary>
  1609. /// Searches an entire sorted <see cref="Span{T}"/> for the specified <paramref name="value"/>
  1610. /// using the specified <typeparamref name="TComparer"/> generic type.
  1611. /// </summary>
  1612. /// <typeparam name="T">The element type of the span.</typeparam>
  1613. /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
  1614. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1615. /// <param name="value">The object to locate. The value can be null for reference types.</param>
  1616. /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
  1617. /// /// <returns>
  1618. /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
  1619. /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
  1620. /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
  1621. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1622. /// </returns>
  1623. /// <exception cref="System.ArgumentNullException">
  1624. /// <paramref name = "comparer" /> is <see langword="null"/> .
  1625. /// </exception>
  1626. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1627. public static int BinarySearch<T, TComparer>(
  1628. this Span<T> span, T value, TComparer comparer)
  1629. where TComparer : IComparer<T>
  1630. {
  1631. return BinarySearch((ReadOnlySpan<T>)span, value, comparer);
  1632. }
  1633. /// <summary>
  1634. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
  1635. /// using the specified <see cref="IComparable{T}"/> generic interface.
  1636. /// </summary>
  1637. /// <typeparam name="T">The element type of the span.</typeparam>
  1638. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1639. /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
  1640. /// <returns>
  1641. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1642. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1643. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1644. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1645. /// </returns>
  1646. /// <exception cref="System.ArgumentNullException">
  1647. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1648. /// </exception>
  1649. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1650. public static int BinarySearch<T>(
  1651. this ReadOnlySpan<T> span, IComparable<T> comparable)
  1652. {
  1653. return BinarySearch<T, IComparable<T>>(span, comparable);
  1654. }
  1655. /// <summary>
  1656. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
  1657. /// using the specified <typeparamref name="TComparable"/> generic type.
  1658. /// </summary>
  1659. /// <typeparam name="T">The element type of the span.</typeparam>
  1660. /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
  1661. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1662. /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
  1663. /// <returns>
  1664. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1665. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1666. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1667. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1668. /// </returns>
  1669. /// <exception cref="System.ArgumentNullException">
  1670. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1671. /// </exception>
  1672. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1673. public static int BinarySearch<T, TComparable>(
  1674. this ReadOnlySpan<T> span, TComparable comparable)
  1675. where TComparable : IComparable<T>
  1676. {
  1677. return SpanHelpers.BinarySearch(span, comparable);
  1678. }
  1679. /// <summary>
  1680. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for the specified <paramref name="value"/>
  1681. /// using the specified <typeparamref name="TComparer"/> generic type.
  1682. /// </summary>
  1683. /// <typeparam name="T">The element type of the span.</typeparam>
  1684. /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
  1685. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1686. /// <param name="value">The object to locate. The value can be null for reference types.</param>
  1687. /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
  1688. /// /// <returns>
  1689. /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
  1690. /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
  1691. /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
  1692. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1693. /// </returns>
  1694. /// <exception cref="System.ArgumentNullException">
  1695. /// <paramref name = "comparer" /> is <see langword="null"/> .
  1696. /// </exception>
  1697. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1698. public static int BinarySearch<T, TComparer>(
  1699. this ReadOnlySpan<T> span, T value, TComparer comparer)
  1700. where TComparer : IComparer<T>
  1701. {
  1702. if (comparer == null)
  1703. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
  1704. var comparable = new SpanHelpers.ComparerComparable<T, TComparer>(
  1705. value, comparer);
  1706. return BinarySearch(span, comparable);
  1707. }
  1708. /// <summary>
  1709. /// Sorts the elements in the entire <see cref="Span{T}" /> using the <see cref="IComparable{T}" /> implementation
  1710. /// of each element of the <see cref= "Span{T}" />
  1711. /// </summary>
  1712. /// <typeparam name="T">The type of the elements of the span.</typeparam>
  1713. /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
  1714. /// <exception cref="InvalidOperationException">
  1715. /// One or more elements in <paramref name="span"/> do not implement the <see cref="IComparable{T}" /> interface.
  1716. /// </exception>
  1717. public static void Sort<T>(this Span<T> span) =>
  1718. Sort(span, (IComparer<T>?)null);
  1719. /// <summary>
  1720. /// Sorts the elements in the entire <see cref="Span{T}" /> using the <typeparamref name="TComparer" />.
  1721. /// </summary>
  1722. /// <typeparam name="T">The type of the elements of the span.</typeparam>
  1723. /// <typeparam name="TComparer">The type of the comparer to use to compare elements.</typeparam>
  1724. /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
  1725. /// <param name="comparer">
  1726. /// The <see cref="IComparer{T}"/> implementation to use when comparing elements, or null to
  1727. /// use the <see cref="IComparable{T}"/> interface implementation of each element.
  1728. /// </param>
  1729. /// <exception cref="InvalidOperationException">
  1730. /// <paramref name="comparer"/> is null, and one or more elements in <paramref name="span"/> do not
  1731. /// implement the <see cref="IComparable{T}" /> interface.
  1732. /// </exception>
  1733. /// <exception cref="ArgumentException">
  1734. /// The implementation of <paramref name="comparer"/> caused an error during the sort.
  1735. /// </exception>
  1736. public static void Sort<T, TComparer>(this Span<T> span, TComparer comparer)
  1737. where TComparer : IComparer<T>?
  1738. {
  1739. if (span.Length > 1)
  1740. {
  1741. ArraySortHelper<T>.Default.Sort(span, comparer); // value-type comparer will be boxed
  1742. }
  1743. }
  1744. /// <summary>
  1745. /// Sorts the elements in the entire <see cref="Span{T}" /> using the specified <see cref="Comparison{T}" />.
  1746. /// </summary>
  1747. /// <typeparam name="T">The type of the elements of the span.</typeparam>
  1748. /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
  1749. /// <param name="comparison">The <see cref="Comparison{T}"/> to use when comparing elements.</param>
  1750. /// <exception cref="ArgumentNullException"><paramref name="comparison"/> is null.</exception>
  1751. public static void Sort<T>(this Span<T> span, Comparison<T> comparison)
  1752. {
  1753. if (comparison == null)
  1754. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
  1755. if (span.Length > 1)
  1756. {
  1757. ArraySortHelper<T>.Sort(span, comparison);
  1758. }
  1759. }
  1760. /// <summary>
  1761. /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
  1762. /// based on the keys in the first <see cref="Span{TKey}" /> using the <see cref="IComparable{T}" />
  1763. /// implementation of each key.
  1764. /// </summary>
  1765. /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
  1766. /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
  1767. /// <param name="keys">The span that contains the keys to sort.</param>
  1768. /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
  1769. /// <exception cref="ArgumentException">
  1770. /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
  1771. /// </exception>
  1772. /// <exception cref="InvalidOperationException">
  1773. /// One or more elements in <paramref name="keys"/> do not implement the <see cref="IComparable{T}" /> interface.
  1774. /// </exception>
  1775. public static void Sort<TKey, TValue>(this Span<TKey> keys, Span<TValue> items) =>
  1776. Sort(keys, items, (IComparer<TKey>?)null);
  1777. /// <summary>
  1778. /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
  1779. /// based on the keys in the first <see cref="Span{TKey}" /> using the specified comparer.
  1780. /// </summary>
  1781. /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
  1782. /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
  1783. /// <typeparam name="TComparer">The type of the comparer to use to compare elements.</typeparam>
  1784. /// <param name="keys">The span that contains the keys to sort.</param>
  1785. /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
  1786. /// <param name="comparer">
  1787. /// The <see cref="IComparer{T}"/> implementation to use when comparing elements, or null to
  1788. /// use the <see cref="IComparable{T}"/> interface implementation of each element.
  1789. /// </param>
  1790. /// <exception cref="ArgumentException">
  1791. /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
  1792. /// </exception>
  1793. /// <exception cref="InvalidOperationException">
  1794. /// <paramref name="comparer"/> is null, and one or more elements in <paramref name="keys"/> do not
  1795. /// implement the <see cref="IComparable{T}" /> interface.
  1796. /// </exception>
  1797. public static void Sort<TKey, TValue, TComparer>(this Span<TKey> keys, Span<TValue> items, TComparer comparer)
  1798. where TComparer : IComparer<TKey>?
  1799. {
  1800. if (keys.Length != items.Length)
  1801. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength);
  1802. if (keys.Length > 1)
  1803. {
  1804. ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, comparer); // value-type comparer will be boxed
  1805. }
  1806. }
  1807. /// <summary>
  1808. /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
  1809. /// based on the keys in the first <see cref="Span{TKey}" /> using the specified comparison.
  1810. /// </summary>
  1811. /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
  1812. /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
  1813. /// <param name="keys">The span that contains the keys to sort.</param>
  1814. /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
  1815. /// <param name="comparison">The <see cref="Comparison{T}"/> to use when comparing elements.</param>
  1816. /// <exception cref="ArgumentNullException"><paramref name="comparison"/> is null.</exception>
  1817. /// <exception cref="ArgumentException">
  1818. /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
  1819. /// </exception>
  1820. public static void Sort<TKey, TValue>(this Span<TKey> keys, Span<TValue> items, Comparison<TKey> comparison)
  1821. {
  1822. if (comparison == null)
  1823. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
  1824. if (keys.Length != items.Length)
  1825. ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength);
  1826. if (keys.Length > 1)
  1827. {
  1828. ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, new ComparisonComparer<TKey>(comparison));
  1829. }
  1830. }
  1831. }
  1832. }