MemoryExtensions.cs 83 KB

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