SpanHelpers.T.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  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.Diagnostics;
  5. using System.Runtime.CompilerServices; // Do not remove. This is necessary for netstandard, since this file is mirrored into corefx
  6. #if !netstandard
  7. using Internal.Runtime.CompilerServices;
  8. #endif
  9. namespace System
  10. {
  11. internal static partial class SpanHelpers // .T
  12. {
  13. public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
  14. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  15. where T : IEquatable<T>
  16. #nullable restore
  17. {
  18. Debug.Assert(searchSpaceLength >= 0);
  19. Debug.Assert(valueLength >= 0);
  20. if (valueLength == 0)
  21. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  22. T valueHead = value;
  23. ref T valueTail = ref Unsafe.Add(ref value, 1);
  24. int valueTailLength = valueLength - 1;
  25. int index = 0;
  26. for (; ; )
  27. {
  28. Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
  29. int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
  30. if (remainingSearchSpaceLength <= 0)
  31. break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
  32. // Do a quick search for the first element of "value".
  33. int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
  34. if (relativeIndex == -1)
  35. break;
  36. index += relativeIndex;
  37. // Found the first element of "value". See if the tail matches.
  38. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
  39. return index; // The tail matched. Return a successful find.
  40. index++;
  41. }
  42. return -1;
  43. }
  44. // Adapted from IndexOf(...)
  45. public static unsafe bool Contains<T>(ref T searchSpace, T value, int length)
  46. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  47. where T : IEquatable<T>
  48. #nullable restore
  49. {
  50. Debug.Assert(length >= 0);
  51. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  52. if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  53. {
  54. while (length >= 8)
  55. {
  56. length -= 8;
  57. if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
  58. value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
  59. value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
  60. value.Equals(Unsafe.Add(ref searchSpace, index + 3)) ||
  61. value.Equals(Unsafe.Add(ref searchSpace, index + 4)) ||
  62. value.Equals(Unsafe.Add(ref searchSpace, index + 5)) ||
  63. value.Equals(Unsafe.Add(ref searchSpace, index + 6)) ||
  64. value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
  65. {
  66. goto Found;
  67. }
  68. index += 8;
  69. }
  70. if (length >= 4)
  71. {
  72. length -= 4;
  73. if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
  74. value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
  75. value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
  76. value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
  77. {
  78. goto Found;
  79. }
  80. index += 4;
  81. }
  82. while (length > 0)
  83. {
  84. length -= 1;
  85. if (value.Equals(Unsafe.Add(ref searchSpace, index)))
  86. goto Found;
  87. index += 1;
  88. }
  89. }
  90. else
  91. {
  92. byte* len = (byte*)length;
  93. for (index = (IntPtr)0; index.ToPointer() < len; index += 1)
  94. {
  95. if ((object)Unsafe.Add(ref searchSpace, index) is null)
  96. {
  97. goto Found;
  98. }
  99. }
  100. }
  101. return false;
  102. Found:
  103. return true;
  104. }
  105. public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length)
  106. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  107. where T : IEquatable<T>
  108. #nullable restore
  109. {
  110. Debug.Assert(length >= 0);
  111. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  112. if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  113. {
  114. while (length >= 8)
  115. {
  116. length -= 8;
  117. if (value.Equals(Unsafe.Add(ref searchSpace, index)))
  118. goto Found;
  119. if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
  120. goto Found1;
  121. if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
  122. goto Found2;
  123. if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
  124. goto Found3;
  125. if (value.Equals(Unsafe.Add(ref searchSpace, index + 4)))
  126. goto Found4;
  127. if (value.Equals(Unsafe.Add(ref searchSpace, index + 5)))
  128. goto Found5;
  129. if (value.Equals(Unsafe.Add(ref searchSpace, index + 6)))
  130. goto Found6;
  131. if (value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
  132. goto Found7;
  133. index += 8;
  134. }
  135. if (length >= 4)
  136. {
  137. length -= 4;
  138. if (value.Equals(Unsafe.Add(ref searchSpace, index)))
  139. goto Found;
  140. if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
  141. goto Found1;
  142. if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
  143. goto Found2;
  144. if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
  145. goto Found3;
  146. index += 4;
  147. }
  148. while (length > 0)
  149. {
  150. if (value.Equals(Unsafe.Add(ref searchSpace, index)))
  151. goto Found;
  152. index += 1;
  153. length--;
  154. }
  155. }
  156. else
  157. {
  158. byte* len = (byte*)length;
  159. for (index = (IntPtr)0; index.ToPointer() < len; index += 1)
  160. {
  161. if ((object)Unsafe.Add(ref searchSpace, index) is null)
  162. {
  163. goto Found;
  164. }
  165. }
  166. }
  167. return -1;
  168. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  169. return (int)(byte*)index;
  170. Found1:
  171. return (int)(byte*)(index + 1);
  172. Found2:
  173. return (int)(byte*)(index + 2);
  174. Found3:
  175. return (int)(byte*)(index + 3);
  176. Found4:
  177. return (int)(byte*)(index + 4);
  178. Found5:
  179. return (int)(byte*)(index + 5);
  180. Found6:
  181. return (int)(byte*)(index + 6);
  182. Found7:
  183. return (int)(byte*)(index + 7);
  184. }
  185. public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
  186. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  187. where T : IEquatable<T>
  188. #nullable restore
  189. {
  190. Debug.Assert(length >= 0);
  191. T lookUp;
  192. int index = 0;
  193. if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  194. {
  195. while ((length - index) >= 8)
  196. {
  197. lookUp = Unsafe.Add(ref searchSpace, index);
  198. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  199. goto Found;
  200. lookUp = Unsafe.Add(ref searchSpace, index + 1);
  201. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  202. goto Found1;
  203. lookUp = Unsafe.Add(ref searchSpace, index + 2);
  204. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  205. goto Found2;
  206. lookUp = Unsafe.Add(ref searchSpace, index + 3);
  207. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  208. goto Found3;
  209. lookUp = Unsafe.Add(ref searchSpace, index + 4);
  210. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  211. goto Found4;
  212. lookUp = Unsafe.Add(ref searchSpace, index + 5);
  213. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  214. goto Found5;
  215. lookUp = Unsafe.Add(ref searchSpace, index + 6);
  216. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  217. goto Found6;
  218. lookUp = Unsafe.Add(ref searchSpace, index + 7);
  219. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  220. goto Found7;
  221. index += 8;
  222. }
  223. if ((length - index) >= 4)
  224. {
  225. lookUp = Unsafe.Add(ref searchSpace, index);
  226. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  227. goto Found;
  228. lookUp = Unsafe.Add(ref searchSpace, index + 1);
  229. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  230. goto Found1;
  231. lookUp = Unsafe.Add(ref searchSpace, index + 2);
  232. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  233. goto Found2;
  234. lookUp = Unsafe.Add(ref searchSpace, index + 3);
  235. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  236. goto Found3;
  237. index += 4;
  238. }
  239. while (index < length)
  240. {
  241. lookUp = Unsafe.Add(ref searchSpace, index);
  242. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  243. goto Found;
  244. index++;
  245. }
  246. }
  247. else
  248. {
  249. for (index = 0; index < length; index++)
  250. {
  251. lookUp = Unsafe.Add(ref searchSpace, index);
  252. if ((object?)lookUp is null)
  253. {
  254. if ((object?)value0 is null || (object?)value1 is null)
  255. {
  256. goto Found;
  257. }
  258. }
  259. else if (lookUp.Equals(value0) || lookUp.Equals(value1))
  260. {
  261. goto Found;
  262. }
  263. }
  264. }
  265. return -1;
  266. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  267. return index;
  268. Found1:
  269. return index + 1;
  270. Found2:
  271. return index + 2;
  272. Found3:
  273. return index + 3;
  274. Found4:
  275. return index + 4;
  276. Found5:
  277. return index + 5;
  278. Found6:
  279. return index + 6;
  280. Found7:
  281. return index + 7;
  282. }
  283. public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
  284. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  285. where T : IEquatable<T>
  286. #nullable restore
  287. {
  288. Debug.Assert(length >= 0);
  289. T lookUp;
  290. int index = 0;
  291. if (default(T)! != null || ((object)value0 != null && (object)value1 != null && (object)value2 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  292. {
  293. while ((length - index) >= 8)
  294. {
  295. lookUp = Unsafe.Add(ref searchSpace, index);
  296. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  297. goto Found;
  298. lookUp = Unsafe.Add(ref searchSpace, index + 1);
  299. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  300. goto Found1;
  301. lookUp = Unsafe.Add(ref searchSpace, index + 2);
  302. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  303. goto Found2;
  304. lookUp = Unsafe.Add(ref searchSpace, index + 3);
  305. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  306. goto Found3;
  307. lookUp = Unsafe.Add(ref searchSpace, index + 4);
  308. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  309. goto Found4;
  310. lookUp = Unsafe.Add(ref searchSpace, index + 5);
  311. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  312. goto Found5;
  313. lookUp = Unsafe.Add(ref searchSpace, index + 6);
  314. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  315. goto Found6;
  316. lookUp = Unsafe.Add(ref searchSpace, index + 7);
  317. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  318. goto Found7;
  319. index += 8;
  320. }
  321. if ((length - index) >= 4)
  322. {
  323. lookUp = Unsafe.Add(ref searchSpace, index);
  324. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  325. goto Found;
  326. lookUp = Unsafe.Add(ref searchSpace, index + 1);
  327. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  328. goto Found1;
  329. lookUp = Unsafe.Add(ref searchSpace, index + 2);
  330. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  331. goto Found2;
  332. lookUp = Unsafe.Add(ref searchSpace, index + 3);
  333. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  334. goto Found3;
  335. index += 4;
  336. }
  337. while (index < length)
  338. {
  339. lookUp = Unsafe.Add(ref searchSpace, index);
  340. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  341. goto Found;
  342. index++;
  343. }
  344. }
  345. else
  346. {
  347. for (index = 0; index < length; index++)
  348. {
  349. lookUp = Unsafe.Add(ref searchSpace, index);
  350. if ((object?)lookUp is null)
  351. {
  352. if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
  353. {
  354. goto Found;
  355. }
  356. }
  357. else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
  358. {
  359. goto Found;
  360. }
  361. }
  362. }
  363. return -1;
  364. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  365. return index;
  366. Found1:
  367. return index + 1;
  368. Found2:
  369. return index + 2;
  370. Found3:
  371. return index + 3;
  372. Found4:
  373. return index + 4;
  374. Found5:
  375. return index + 5;
  376. Found6:
  377. return index + 6;
  378. Found7:
  379. return index + 7;
  380. }
  381. public static int IndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
  382. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  383. where T : IEquatable<T>
  384. #nullable restore
  385. {
  386. Debug.Assert(searchSpaceLength >= 0);
  387. Debug.Assert(valueLength >= 0);
  388. if (valueLength == 0)
  389. return -1; // A zero-length set of values is always treated as "not found".
  390. int index = -1;
  391. for (int i = 0; i < valueLength; i++)
  392. {
  393. var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
  394. if ((uint)tempIndex < (uint)index)
  395. {
  396. index = tempIndex;
  397. // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value
  398. searchSpaceLength = tempIndex;
  399. if (index == 0)
  400. break;
  401. }
  402. }
  403. return index;
  404. }
  405. public static int LastIndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
  406. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  407. where T : IEquatable<T>
  408. #nullable restore
  409. {
  410. Debug.Assert(searchSpaceLength >= 0);
  411. Debug.Assert(valueLength >= 0);
  412. if (valueLength == 0)
  413. return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
  414. T valueHead = value;
  415. ref T valueTail = ref Unsafe.Add(ref value, 1);
  416. int valueTailLength = valueLength - 1;
  417. int index = 0;
  418. for (; ; )
  419. {
  420. Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
  421. int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
  422. if (remainingSearchSpaceLength <= 0)
  423. break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
  424. // Do a quick search for the first element of "value".
  425. int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
  426. if (relativeIndex == -1)
  427. break;
  428. // Found the first element of "value". See if the tail matches.
  429. if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
  430. return relativeIndex; // The tail matched. Return a successful find.
  431. index += remainingSearchSpaceLength - relativeIndex;
  432. }
  433. return -1;
  434. }
  435. public static int LastIndexOf<T>(ref T searchSpace, T value, int length)
  436. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  437. where T : IEquatable<T>
  438. #nullable restore
  439. {
  440. Debug.Assert(length >= 0);
  441. if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  442. {
  443. while (length >= 8)
  444. {
  445. length -= 8;
  446. if (value.Equals(Unsafe.Add(ref searchSpace, length + 7)))
  447. goto Found7;
  448. if (value.Equals(Unsafe.Add(ref searchSpace, length + 6)))
  449. goto Found6;
  450. if (value.Equals(Unsafe.Add(ref searchSpace, length + 5)))
  451. goto Found5;
  452. if (value.Equals(Unsafe.Add(ref searchSpace, length + 4)))
  453. goto Found4;
  454. if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
  455. goto Found3;
  456. if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
  457. goto Found2;
  458. if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
  459. goto Found1;
  460. if (value.Equals(Unsafe.Add(ref searchSpace, length)))
  461. goto Found;
  462. }
  463. if (length >= 4)
  464. {
  465. length -= 4;
  466. if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
  467. goto Found3;
  468. if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
  469. goto Found2;
  470. if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
  471. goto Found1;
  472. if (value.Equals(Unsafe.Add(ref searchSpace, length)))
  473. goto Found;
  474. }
  475. while (length > 0)
  476. {
  477. length--;
  478. if (value.Equals(Unsafe.Add(ref searchSpace, length)))
  479. goto Found;
  480. }
  481. }
  482. else
  483. {
  484. for (length--; length >= 0; length--)
  485. {
  486. if ((object)Unsafe.Add(ref searchSpace, length) is null)
  487. {
  488. goto Found;
  489. }
  490. }
  491. }
  492. return -1;
  493. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  494. return length;
  495. Found1:
  496. return length + 1;
  497. Found2:
  498. return length + 2;
  499. Found3:
  500. return length + 3;
  501. Found4:
  502. return length + 4;
  503. Found5:
  504. return length + 5;
  505. Found6:
  506. return length + 6;
  507. Found7:
  508. return length + 7;
  509. }
  510. public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
  511. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  512. where T : IEquatable<T>
  513. #nullable restore
  514. {
  515. Debug.Assert(length >= 0);
  516. T lookUp;
  517. if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  518. {
  519. while (length >= 8)
  520. {
  521. length -= 8;
  522. lookUp = Unsafe.Add(ref searchSpace, length + 7);
  523. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  524. goto Found7;
  525. lookUp = Unsafe.Add(ref searchSpace, length + 6);
  526. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  527. goto Found6;
  528. lookUp = Unsafe.Add(ref searchSpace, length + 5);
  529. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  530. goto Found5;
  531. lookUp = Unsafe.Add(ref searchSpace, length + 4);
  532. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  533. goto Found4;
  534. lookUp = Unsafe.Add(ref searchSpace, length + 3);
  535. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  536. goto Found3;
  537. lookUp = Unsafe.Add(ref searchSpace, length + 2);
  538. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  539. goto Found2;
  540. lookUp = Unsafe.Add(ref searchSpace, length + 1);
  541. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  542. goto Found1;
  543. lookUp = Unsafe.Add(ref searchSpace, length);
  544. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  545. goto Found;
  546. }
  547. if (length >= 4)
  548. {
  549. length -= 4;
  550. lookUp = Unsafe.Add(ref searchSpace, length + 3);
  551. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  552. goto Found3;
  553. lookUp = Unsafe.Add(ref searchSpace, length + 2);
  554. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  555. goto Found2;
  556. lookUp = Unsafe.Add(ref searchSpace, length + 1);
  557. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  558. goto Found1;
  559. lookUp = Unsafe.Add(ref searchSpace, length);
  560. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  561. goto Found;
  562. }
  563. while (length > 0)
  564. {
  565. length--;
  566. lookUp = Unsafe.Add(ref searchSpace, length);
  567. if (value0.Equals(lookUp) || value1.Equals(lookUp))
  568. goto Found;
  569. }
  570. }
  571. else
  572. {
  573. for (length--; length >= 0; length--)
  574. {
  575. lookUp = Unsafe.Add(ref searchSpace, length);
  576. if ((object?)lookUp is null)
  577. {
  578. if ((object?)value0 is null || (object?)value1 is null)
  579. {
  580. goto Found;
  581. }
  582. }
  583. else if (lookUp.Equals(value0) || lookUp.Equals(value1))
  584. {
  585. goto Found;
  586. }
  587. }
  588. }
  589. return -1;
  590. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  591. return length;
  592. Found1:
  593. return length + 1;
  594. Found2:
  595. return length + 2;
  596. Found3:
  597. return length + 3;
  598. Found4:
  599. return length + 4;
  600. Found5:
  601. return length + 5;
  602. Found6:
  603. return length + 6;
  604. Found7:
  605. return length + 7;
  606. }
  607. public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
  608. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  609. where T : IEquatable<T>
  610. #nullable restore
  611. {
  612. Debug.Assert(length >= 0);
  613. T lookUp;
  614. if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
  615. {
  616. while (length >= 8)
  617. {
  618. length -= 8;
  619. lookUp = Unsafe.Add(ref searchSpace, length + 7);
  620. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  621. goto Found7;
  622. lookUp = Unsafe.Add(ref searchSpace, length + 6);
  623. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  624. goto Found6;
  625. lookUp = Unsafe.Add(ref searchSpace, length + 5);
  626. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  627. goto Found5;
  628. lookUp = Unsafe.Add(ref searchSpace, length + 4);
  629. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  630. goto Found4;
  631. lookUp = Unsafe.Add(ref searchSpace, length + 3);
  632. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  633. goto Found3;
  634. lookUp = Unsafe.Add(ref searchSpace, length + 2);
  635. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  636. goto Found2;
  637. lookUp = Unsafe.Add(ref searchSpace, length + 1);
  638. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  639. goto Found1;
  640. lookUp = Unsafe.Add(ref searchSpace, length);
  641. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  642. goto Found;
  643. }
  644. if (length >= 4)
  645. {
  646. length -= 4;
  647. lookUp = Unsafe.Add(ref searchSpace, length + 3);
  648. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  649. goto Found3;
  650. lookUp = Unsafe.Add(ref searchSpace, length + 2);
  651. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  652. goto Found2;
  653. lookUp = Unsafe.Add(ref searchSpace, length + 1);
  654. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  655. goto Found1;
  656. lookUp = Unsafe.Add(ref searchSpace, length);
  657. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  658. goto Found;
  659. }
  660. while (length > 0)
  661. {
  662. length--;
  663. lookUp = Unsafe.Add(ref searchSpace, length);
  664. if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
  665. goto Found;
  666. }
  667. }
  668. else
  669. {
  670. for (length--; length >= 0; length--)
  671. {
  672. lookUp = Unsafe.Add(ref searchSpace, length);
  673. if ((object?)lookUp is null)
  674. {
  675. if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
  676. {
  677. goto Found;
  678. }
  679. }
  680. else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
  681. {
  682. goto Found;
  683. }
  684. }
  685. }
  686. return -1;
  687. Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  688. return length;
  689. Found1:
  690. return length + 1;
  691. Found2:
  692. return length + 2;
  693. Found3:
  694. return length + 3;
  695. Found4:
  696. return length + 4;
  697. Found5:
  698. return length + 5;
  699. Found6:
  700. return length + 6;
  701. Found7:
  702. return length + 7;
  703. }
  704. public static int LastIndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
  705. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  706. where T : IEquatable<T>
  707. #nullable restore
  708. {
  709. Debug.Assert(searchSpaceLength >= 0);
  710. Debug.Assert(valueLength >= 0);
  711. if (valueLength == 0)
  712. return -1; // A zero-length set of values is always treated as "not found".
  713. int index = -1;
  714. for (int i = 0; i < valueLength; i++)
  715. {
  716. var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
  717. if (tempIndex > index)
  718. index = tempIndex;
  719. }
  720. return index;
  721. }
  722. public static bool SequenceEqual<T>(ref T first, ref T second, int length)
  723. #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
  724. where T : IEquatable<T>
  725. #nullable restore
  726. {
  727. Debug.Assert(length >= 0);
  728. if (Unsafe.AreSame(ref first, ref second))
  729. goto Equal;
  730. IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
  731. T lookUp0;
  732. T lookUp1;
  733. while (length >= 8)
  734. {
  735. length -= 8;
  736. lookUp0 = Unsafe.Add(ref first, index);
  737. lookUp1 = Unsafe.Add(ref second, index);
  738. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  739. goto NotEqual;
  740. lookUp0 = Unsafe.Add(ref first, index + 1);
  741. lookUp1 = Unsafe.Add(ref second, index + 1);
  742. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  743. goto NotEqual;
  744. lookUp0 = Unsafe.Add(ref first, index + 2);
  745. lookUp1 = Unsafe.Add(ref second, index + 2);
  746. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  747. goto NotEqual;
  748. lookUp0 = Unsafe.Add(ref first, index + 3);
  749. lookUp1 = Unsafe.Add(ref second, index + 3);
  750. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  751. goto NotEqual;
  752. lookUp0 = Unsafe.Add(ref first, index + 4);
  753. lookUp1 = Unsafe.Add(ref second, index + 4);
  754. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  755. goto NotEqual;
  756. lookUp0 = Unsafe.Add(ref first, index + 5);
  757. lookUp1 = Unsafe.Add(ref second, index + 5);
  758. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  759. goto NotEqual;
  760. lookUp0 = Unsafe.Add(ref first, index + 6);
  761. lookUp1 = Unsafe.Add(ref second, index + 6);
  762. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  763. goto NotEqual;
  764. lookUp0 = Unsafe.Add(ref first, index + 7);
  765. lookUp1 = Unsafe.Add(ref second, index + 7);
  766. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  767. goto NotEqual;
  768. index += 8;
  769. }
  770. if (length >= 4)
  771. {
  772. length -= 4;
  773. lookUp0 = Unsafe.Add(ref first, index);
  774. lookUp1 = Unsafe.Add(ref second, index);
  775. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  776. goto NotEqual;
  777. lookUp0 = Unsafe.Add(ref first, index + 1);
  778. lookUp1 = Unsafe.Add(ref second, index + 1);
  779. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  780. goto NotEqual;
  781. lookUp0 = Unsafe.Add(ref first, index + 2);
  782. lookUp1 = Unsafe.Add(ref second, index + 2);
  783. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  784. goto NotEqual;
  785. lookUp0 = Unsafe.Add(ref first, index + 3);
  786. lookUp1 = Unsafe.Add(ref second, index + 3);
  787. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  788. goto NotEqual;
  789. index += 4;
  790. }
  791. while (length > 0)
  792. {
  793. lookUp0 = Unsafe.Add(ref first, index);
  794. lookUp1 = Unsafe.Add(ref second, index);
  795. if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
  796. goto NotEqual;
  797. index += 1;
  798. length--;
  799. }
  800. Equal:
  801. return true;
  802. NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
  803. return false;
  804. }
  805. public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T second, int secondLength)
  806. where T : IComparable<T>
  807. {
  808. Debug.Assert(firstLength >= 0);
  809. Debug.Assert(secondLength >= 0);
  810. var minLength = firstLength;
  811. if (minLength > secondLength)
  812. minLength = secondLength;
  813. for (int i = 0; i < minLength; i++)
  814. {
  815. T lookUp = Unsafe.Add(ref second, i);
  816. int result = (Unsafe.Add(ref first, i)?.CompareTo(lookUp) ?? (((object?)lookUp is null) ? 0 : -1));
  817. if (result != 0)
  818. return result;
  819. }
  820. return firstLength.CompareTo(secondLength);
  821. }
  822. }
  823. }