MemoryExtensions.cs 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677
  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 'start' index and ending at 'end' index (exclusive).
  1043. /// </summary>
  1044. /// <param name="segment">The target array.</param>
  1045. /// <param name="start">The index at which to begin the Span.</param>
  1046. /// <param name="length">The number of items in the Span.</param>
  1047. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1048. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1049. /// <exception cref="System.ArgumentOutOfRangeException">
  1050. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=segment.Count).
  1051. /// </exception>
  1052. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1053. public static Span<T> AsSpan<T>(this ArraySegment<T> segment, int start, int length)
  1054. {
  1055. if (((uint)start) > (uint)segment.Count)
  1056. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1057. if (((uint)length) > (uint)(segment.Count - start))
  1058. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
  1059. return new Span<T>(segment.Array, segment.Offset + start, length);
  1060. }
  1061. /// <summary>
  1062. /// Creates a new memory over the target array.
  1063. /// </summary>
  1064. public static Memory<T> AsMemory<T>(this T[] array) => new Memory<T>(array);
  1065. /// <summary>
  1066. /// Creates a new memory over the portion of the target array beginning
  1067. /// at 'start' index and ending at 'end' index (exclusive).
  1068. /// </summary>
  1069. /// <param name="array">The target array.</param>
  1070. /// <param name="start">The index at which to begin the memory.</param>
  1071. /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
  1072. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
  1073. /// <exception cref="System.ArgumentOutOfRangeException">
  1074. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=array.Length).
  1075. /// </exception>
  1076. public static Memory<T> AsMemory<T>(this T[] array, int start) => new Memory<T>(array, start);
  1077. /// <summary>
  1078. /// Creates a new memory over the portion of the target array beginning
  1079. /// at 'start' index and ending at 'end' index (exclusive).
  1080. /// </summary>
  1081. /// <param name="array">The target array.</param>
  1082. /// <param name="start">The index at which to begin the memory.</param>
  1083. /// <param name="length">The number of items in the memory.</param>
  1084. /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
  1085. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
  1086. /// <exception cref="System.ArgumentOutOfRangeException">
  1087. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
  1088. /// </exception>
  1089. public static Memory<T> AsMemory<T>(this T[] array, int start, int length) => new Memory<T>(array, start, length);
  1090. /// <summary>
  1091. /// Creates a new memory over the portion of the target array.
  1092. /// </summary>
  1093. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment) => new Memory<T>(segment.Array, segment.Offset, segment.Count);
  1094. /// <summary>
  1095. /// Creates a new memory over the portion of the target array beginning
  1096. /// at 'start' index and ending at 'end' index (exclusive).
  1097. /// </summary>
  1098. /// <param name="segment">The target array.</param>
  1099. /// <param name="start">The index at which to begin the memory.</param>
  1100. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1101. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1102. /// <exception cref="System.ArgumentOutOfRangeException">
  1103. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=segment.Count).
  1104. /// </exception>
  1105. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start)
  1106. {
  1107. if (((uint)start) > (uint)segment.Count)
  1108. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1109. return new Memory<T>(segment.Array, segment.Offset + start, segment.Count - start);
  1110. }
  1111. /// <summary>
  1112. /// Creates a new memory over the portion of the target array beginning
  1113. /// at 'start' index and ending at 'end' index (exclusive).
  1114. /// </summary>
  1115. /// <param name="segment">The target array.</param>
  1116. /// <param name="start">The index at which to begin the memory.</param>
  1117. /// <param name="length">The number of items in the memory.</param>
  1118. /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks>
  1119. /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception>
  1120. /// <exception cref="System.ArgumentOutOfRangeException">
  1121. /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=segment.Count).
  1122. /// </exception>
  1123. public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start, int length)
  1124. {
  1125. if (((uint)start) > (uint)segment.Count)
  1126. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
  1127. if (((uint)length) > (uint)(segment.Count - start))
  1128. ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
  1129. return new Memory<T>(segment.Array, segment.Offset + start, length);
  1130. }
  1131. /// <summary>
  1132. /// Copies the contents of the array into the span. If the source
  1133. /// and destinations overlap, this method behaves as if the original values in
  1134. /// a temporary location before the destination is overwritten.
  1135. ///
  1136. ///<param name="source">The array to copy items from.</param>
  1137. /// <param name="destination">The span to copy items into.</param>
  1138. /// <exception cref="System.ArgumentException">
  1139. /// Thrown when the destination Span is shorter than the source array.
  1140. /// </exception>
  1141. /// </summary>
  1142. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1143. public static void CopyTo<T>(this T[] source, Span<T> destination)
  1144. {
  1145. new ReadOnlySpan<T>(source).CopyTo(destination);
  1146. }
  1147. /// <summary>
  1148. /// Copies the contents of the array into the memory. If the source
  1149. /// and destinations overlap, this method behaves as if the original values are in
  1150. /// a temporary location before the destination is overwritten.
  1151. ///
  1152. ///<param name="source">The array to copy items from.</param>
  1153. /// <param name="destination">The memory to copy items into.</param>
  1154. /// <exception cref="System.ArgumentException">
  1155. /// Thrown when the destination is shorter than the source array.
  1156. /// </exception>
  1157. /// </summary>
  1158. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1159. public static void CopyTo<T>(this T[] source, Memory<T> destination)
  1160. {
  1161. source.CopyTo(destination.Span);
  1162. }
  1163. //
  1164. // Overlaps
  1165. // ========
  1166. //
  1167. // The following methods can be used to determine if two sequences
  1168. // overlap in memory.
  1169. //
  1170. // Two sequences overlap if they have positions in common and neither
  1171. // is empty. Empty sequences do not overlap with any other sequence.
  1172. //
  1173. // If two sequences overlap, the element offset is the number of
  1174. // elements by which the second sequence is offset from the first
  1175. // sequence (i.e., second minus first). An exception is thrown if the
  1176. // number is not a whole number, which can happen when a sequence of a
  1177. // smaller type is cast to a sequence of a larger type with unsafe code
  1178. // or NonPortableCast. If the sequences do not overlap, the offset is
  1179. // meaningless and arbitrarily set to zero.
  1180. //
  1181. // Implementation
  1182. // --------------
  1183. //
  1184. // Implementing this correctly is quite tricky due of two problems:
  1185. //
  1186. // * If the sequences refer to two different objects on the managed
  1187. // heap, the garbage collector can move them freely around or change
  1188. // their relative order in memory.
  1189. //
  1190. // * The distance between two sequences can be greater than
  1191. // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit
  1192. // system).
  1193. //
  1194. // (For simplicity, the following text assumes a 32-bit system, but
  1195. // everything also applies to a 64-bit system if every 32 is replaced a
  1196. // 64.)
  1197. //
  1198. // The first problem is solved by calculating the distance with exactly
  1199. // one atomic operation. If the garbage collector happens to move the
  1200. // sequences afterwards and the sequences overlapped before, they will
  1201. // still overlap after the move and their distance hasn't changed. If
  1202. // the sequences did not overlap, the distance can change but the
  1203. // sequences still won't overlap.
  1204. //
  1205. // The second problem is solved by making all addresses relative to the
  1206. // start of the first sequence and performing all operations in
  1207. // unsigned integer arithmetic modulo 2³².
  1208. //
  1209. // Example
  1210. // -------
  1211. //
  1212. // Let's say there are two sequences, x and y. Let
  1213. //
  1214. // ref T xRef = MemoryMarshal.GetReference(x)
  1215. // uint xLength = x.Length * Unsafe.SizeOf<T>()
  1216. // ref T yRef = MemoryMarshal.GetReference(y)
  1217. // uint yLength = y.Length * Unsafe.SizeOf<T>()
  1218. //
  1219. // Visually, the two sequences are located somewhere in the 32-bit
  1220. // address space as follows:
  1221. //
  1222. // [----------------------------------------------) normal address space
  1223. // 0 2³²
  1224. // [------------------) first sequence
  1225. // xRef xRef + xLength
  1226. // [--------------------------) . second sequence
  1227. // yRef . yRef + yLength
  1228. // : . . .
  1229. // : . . .
  1230. // . . .
  1231. // . . .
  1232. // . . .
  1233. // [----------------------------------------------) relative address space
  1234. // 0 . . 2³²
  1235. // [------------------) : first sequence
  1236. // x1 . x2 :
  1237. // -------------) [------------- second sequence
  1238. // y2 y1
  1239. //
  1240. // The idea is to make all addresses relative to xRef: Let x1 be the
  1241. // start address of x in this relative address space, x2 the end
  1242. // address of x, y1 the start address of y, and y2 the end address of
  1243. // y:
  1244. //
  1245. // nuint x1 = 0
  1246. // nuint x2 = xLength
  1247. // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef)
  1248. // nuint y2 = y1 + yLength
  1249. //
  1250. // xRef relative to xRef is 0.
  1251. //
  1252. // x2 is simply x1 + xLength. This cannot overflow.
  1253. //
  1254. // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is
  1255. // negative, casting it to an unsigned 32-bit integer turns it into
  1256. // (yRef - xRef + 2³²). So, in the example above, y1 moves to the right
  1257. // of x2.
  1258. //
  1259. // y2 is simply y1 + yLength. Note that this can overflow, as in the
  1260. // example above, which must be avoided.
  1261. //
  1262. // The two sequences do *not* overlap if y is entirely in the space
  1263. // right of x in the relative address space. (It can't be left of it!)
  1264. //
  1265. // (y1 >= x2) && (y2 <= 2³²)
  1266. //
  1267. // Inversely, they do overlap if
  1268. //
  1269. // (y1 < x2) || (y2 > 2³²)
  1270. //
  1271. // After substituting x2 and y2 with their respective definition:
  1272. //
  1273. // == (y1 < xLength) || (y1 + yLength > 2³²)
  1274. //
  1275. // Since yLength can't be greater than the size of the address space,
  1276. // the overflow can be avoided as follows:
  1277. //
  1278. // == (y1 < xLength) || (y1 > 2³² - yLength)
  1279. //
  1280. // However, 2³² cannot be stored in an unsigned 32-bit integer, so one
  1281. // more change is needed to keep doing everything with unsigned 32-bit
  1282. // integers:
  1283. //
  1284. // == (y1 < xLength) || (y1 > -yLength)
  1285. //
  1286. // Due to modulo arithmetic, this gives exactly same result *except* if
  1287. // yLength is zero, since 2³² - 0 is 0 and not 2³². So the case
  1288. // y.IsEmpty must be handled separately first.
  1289. //
  1290. /// <summary>
  1291. /// Determines whether two sequences overlap in memory.
  1292. /// </summary>
  1293. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1294. public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other)
  1295. {
  1296. return Overlaps((ReadOnlySpan<T>)span, other);
  1297. }
  1298. /// <summary>
  1299. /// Determines whether two sequences overlap in memory and outputs the element offset.
  1300. /// </summary>
  1301. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1302. public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other, out int elementOffset)
  1303. {
  1304. return Overlaps((ReadOnlySpan<T>)span, other, out elementOffset);
  1305. }
  1306. /// <summary>
  1307. /// Determines whether two sequences overlap in memory.
  1308. /// </summary>
  1309. public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
  1310. {
  1311. if (span.IsEmpty || other.IsEmpty)
  1312. {
  1313. return false;
  1314. }
  1315. IntPtr byteOffset = Unsafe.ByteOffset(
  1316. ref MemoryMarshal.GetReference(span),
  1317. ref MemoryMarshal.GetReference(other));
  1318. if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
  1319. {
  1320. return (uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) ||
  1321. (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>());
  1322. }
  1323. else
  1324. {
  1325. return (ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) ||
  1326. (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>());
  1327. }
  1328. }
  1329. /// <summary>
  1330. /// Determines whether two sequences overlap in memory and outputs the element offset.
  1331. /// </summary>
  1332. public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other, out int elementOffset)
  1333. {
  1334. if (span.IsEmpty || other.IsEmpty)
  1335. {
  1336. elementOffset = 0;
  1337. return false;
  1338. }
  1339. IntPtr byteOffset = Unsafe.ByteOffset(
  1340. ref MemoryMarshal.GetReference(span),
  1341. ref MemoryMarshal.GetReference(other));
  1342. if (Unsafe.SizeOf<IntPtr>() == sizeof(int))
  1343. {
  1344. if ((uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) ||
  1345. (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>()))
  1346. {
  1347. if ((int)byteOffset % Unsafe.SizeOf<T>() != 0)
  1348. ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
  1349. elementOffset = (int)byteOffset / Unsafe.SizeOf<T>();
  1350. return true;
  1351. }
  1352. else
  1353. {
  1354. elementOffset = 0;
  1355. return false;
  1356. }
  1357. }
  1358. else
  1359. {
  1360. if ((ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) ||
  1361. (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>()))
  1362. {
  1363. if ((long)byteOffset % Unsafe.SizeOf<T>() != 0)
  1364. ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch();
  1365. elementOffset = (int)((long)byteOffset / Unsafe.SizeOf<T>());
  1366. return true;
  1367. }
  1368. else
  1369. {
  1370. elementOffset = 0;
  1371. return false;
  1372. }
  1373. }
  1374. }
  1375. /// <summary>
  1376. /// Searches an entire sorted <see cref="Span{T}"/> for a value
  1377. /// using the specified <see cref="IComparable{T}"/> generic interface.
  1378. /// </summary>
  1379. /// <typeparam name="T">The element type of the span.</typeparam>
  1380. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1381. /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
  1382. /// <returns>
  1383. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1384. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1385. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1386. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1387. /// </returns>
  1388. /// <exception cref="T:System.ArgumentNullException">
  1389. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1390. /// </exception>
  1391. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1392. public static int BinarySearch<T>(
  1393. this Span<T> span, IComparable<T> comparable)
  1394. {
  1395. return BinarySearch<T, IComparable<T>>(span, comparable);
  1396. }
  1397. /// <summary>
  1398. /// Searches an entire sorted <see cref="Span{T}"/> for a value
  1399. /// using the specified <typeparamref name="TComparable"/> generic type.
  1400. /// </summary>
  1401. /// <typeparam name="T">The element type of the span.</typeparam>
  1402. /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
  1403. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1404. /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
  1405. /// <returns>
  1406. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1407. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1408. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1409. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1410. /// </returns>
  1411. /// <exception cref="T:System.ArgumentNullException">
  1412. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1413. /// </exception>
  1414. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1415. public static int BinarySearch<T, TComparable>(
  1416. this Span<T> span, TComparable comparable)
  1417. where TComparable : IComparable<T>
  1418. {
  1419. return BinarySearch((ReadOnlySpan<T>)span, comparable);
  1420. }
  1421. /// <summary>
  1422. /// Searches an entire sorted <see cref="Span{T}"/> for the specified <paramref name="value"/>
  1423. /// using the specified <typeparamref name="TComparer"/> generic type.
  1424. /// </summary>
  1425. /// <typeparam name="T">The element type of the span.</typeparam>
  1426. /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
  1427. /// <param name="span">The sorted <see cref="Span{T}"/> to search.</param>
  1428. /// <param name="value">The object to locate. The value can be null for reference types.</param>
  1429. /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
  1430. /// /// <returns>
  1431. /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
  1432. /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
  1433. /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
  1434. /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>.
  1435. /// </returns>
  1436. /// <exception cref="T:System.ArgumentNullException">
  1437. /// <paramref name = "comparer" /> is <see langword="null"/> .
  1438. /// </exception>
  1439. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1440. public static int BinarySearch<T, TComparer>(
  1441. this Span<T> span, T value, TComparer comparer)
  1442. where TComparer : IComparer<T>
  1443. {
  1444. return BinarySearch((ReadOnlySpan<T>)span, value, comparer);
  1445. }
  1446. /// <summary>
  1447. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
  1448. /// using the specified <see cref="IComparable{T}"/> generic interface.
  1449. /// </summary>
  1450. /// <typeparam name="T">The element type of the span.</typeparam>
  1451. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1452. /// <param name="comparable">The <see cref="IComparable{T}"/> to use when comparing.</param>
  1453. /// <returns>
  1454. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1455. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1456. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1457. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1458. /// </returns>
  1459. /// <exception cref="T:System.ArgumentNullException">
  1460. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1461. /// </exception>
  1462. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1463. public static int BinarySearch<T>(
  1464. this ReadOnlySpan<T> span, IComparable<T> comparable)
  1465. {
  1466. return BinarySearch<T, IComparable<T>>(span, comparable);
  1467. }
  1468. /// <summary>
  1469. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for a value
  1470. /// using the specified <typeparamref name="TComparable"/> generic type.
  1471. /// </summary>
  1472. /// <typeparam name="T">The element type of the span.</typeparam>
  1473. /// <typeparam name="TComparable">The specific type of <see cref="IComparable{T}"/>.</typeparam>
  1474. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1475. /// <param name="comparable">The <typeparamref name="TComparable"/> to use when comparing.</param>
  1476. /// <returns>
  1477. /// The zero-based index of <paramref name="comparable"/> in the sorted <paramref name="span"/>,
  1478. /// if <paramref name="comparable"/> is found; otherwise, a negative number that is the bitwise complement
  1479. /// of the index of the next element that is larger than <paramref name="comparable"/> or, if there is
  1480. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1481. /// </returns>
  1482. /// <exception cref="T:System.ArgumentNullException">
  1483. /// <paramref name = "comparable" /> is <see langword="null"/> .
  1484. /// </exception>
  1485. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1486. public static int BinarySearch<T, TComparable>(
  1487. this ReadOnlySpan<T> span, TComparable comparable)
  1488. where TComparable : IComparable<T>
  1489. {
  1490. return SpanHelpers.BinarySearch(span, comparable);
  1491. }
  1492. /// <summary>
  1493. /// Searches an entire sorted <see cref="ReadOnlySpan{T}"/> for the specified <paramref name="value"/>
  1494. /// using the specified <typeparamref name="TComparer"/> generic type.
  1495. /// </summary>
  1496. /// <typeparam name="T">The element type of the span.</typeparam>
  1497. /// <typeparam name="TComparer">The specific type of <see cref="IComparer{T}"/>.</typeparam>
  1498. /// <param name="span">The sorted <see cref="ReadOnlySpan{T}"/> to search.</param>
  1499. /// <param name="value">The object to locate. The value can be null for reference types.</param>
  1500. /// <param name="comparer">The <typeparamref name="TComparer"/> to use when comparing.</param>
  1501. /// /// <returns>
  1502. /// The zero-based index of <paramref name="value"/> in the sorted <paramref name="span"/>,
  1503. /// if <paramref name="value"/> is found; otherwise, a negative number that is the bitwise complement
  1504. /// of the index of the next element that is larger than <paramref name="value"/> or, if there is
  1505. /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>.
  1506. /// </returns>
  1507. /// <exception cref="T:System.ArgumentNullException">
  1508. /// <paramref name = "comparer" /> is <see langword="null"/> .
  1509. /// </exception>
  1510. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1511. public static int BinarySearch<T, TComparer>(
  1512. this ReadOnlySpan<T> span, T value, TComparer comparer)
  1513. where TComparer : IComparer<T>
  1514. {
  1515. if (comparer == null)
  1516. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
  1517. var comparable = new SpanHelpers.ComparerComparable<T, TComparer>(
  1518. value, comparer);
  1519. return BinarySearch(span, comparable);
  1520. }
  1521. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1522. private static bool IsTypeComparableAsBytes<T>(out nuint size)
  1523. {
  1524. if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte))
  1525. {
  1526. size = sizeof(byte);
  1527. return true;
  1528. }
  1529. if (typeof(T) == typeof(char) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
  1530. {
  1531. size = sizeof(char);
  1532. return true;
  1533. }
  1534. if (typeof(T) == typeof(int) || typeof(T) == typeof(uint))
  1535. {
  1536. size = sizeof(int);
  1537. return true;
  1538. }
  1539. if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong))
  1540. {
  1541. size = sizeof(long);
  1542. return true;
  1543. }
  1544. size = default;
  1545. return false;
  1546. }
  1547. }
  1548. }