MemoryExtensions.cs 83 KB

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