Fix pragma warning restore (dotnet/coreclr#26389)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / SpanHelpers.T.cs
blobdccc8fb4603b3fb6c8cf96b92430b8feaf78475d
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.
5 using System.Diagnostics;
6 #if !netstandard
7 using Internal.Runtime.CompilerServices;
8 #endif
10 namespace System
12 internal static partial class SpanHelpers // .T
14 public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
15 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
16 where T : IEquatable<T>
17 #nullable restore
19 Debug.Assert(searchSpaceLength >= 0);
20 Debug.Assert(valueLength >= 0);
22 if (valueLength == 0)
23 return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
25 T valueHead = value;
26 ref T valueTail = ref Unsafe.Add(ref value, 1);
27 int valueTailLength = valueLength - 1;
29 int index = 0;
30 while (true)
32 Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
33 int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
34 if (remainingSearchSpaceLength <= 0)
35 break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
37 // Do a quick search for the first element of "value".
38 int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
39 if (relativeIndex == -1)
40 break;
41 index += relativeIndex;
43 // Found the first element of "value". See if the tail matches.
44 if (SequenceEqual(ref Unsafe.Add(ref searchSpace, index + 1), ref valueTail, valueTailLength))
45 return index; // The tail matched. Return a successful find.
47 index++;
49 return -1;
52 // Adapted from IndexOf(...)
53 public static unsafe bool Contains<T>(ref T searchSpace, T value, int length)
54 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
55 where T : IEquatable<T>
56 #nullable restore
58 Debug.Assert(length >= 0);
60 IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
62 if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
64 while (length >= 8)
66 length -= 8;
68 if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
69 value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
70 value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
71 value.Equals(Unsafe.Add(ref searchSpace, index + 3)) ||
72 value.Equals(Unsafe.Add(ref searchSpace, index + 4)) ||
73 value.Equals(Unsafe.Add(ref searchSpace, index + 5)) ||
74 value.Equals(Unsafe.Add(ref searchSpace, index + 6)) ||
75 value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
77 goto Found;
80 index += 8;
83 if (length >= 4)
85 length -= 4;
87 if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
88 value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
89 value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
90 value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
92 goto Found;
95 index += 4;
98 while (length > 0)
100 length -= 1;
102 if (value.Equals(Unsafe.Add(ref searchSpace, index)))
103 goto Found;
105 index += 1;
108 else
110 byte* len = (byte*)length;
111 for (index = (IntPtr)0; index.ToPointer() < len; index += 1)
113 if ((object)Unsafe.Add(ref searchSpace, index) is null)
115 goto Found;
120 return false;
122 Found:
123 return true;
126 public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length)
127 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
128 where T : IEquatable<T>
129 #nullable restore
131 Debug.Assert(length >= 0);
133 IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
134 if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
136 while (length >= 8)
138 length -= 8;
140 if (value.Equals(Unsafe.Add(ref searchSpace, index)))
141 goto Found;
142 if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
143 goto Found1;
144 if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
145 goto Found2;
146 if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
147 goto Found3;
148 if (value.Equals(Unsafe.Add(ref searchSpace, index + 4)))
149 goto Found4;
150 if (value.Equals(Unsafe.Add(ref searchSpace, index + 5)))
151 goto Found5;
152 if (value.Equals(Unsafe.Add(ref searchSpace, index + 6)))
153 goto Found6;
154 if (value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
155 goto Found7;
157 index += 8;
160 if (length >= 4)
162 length -= 4;
164 if (value.Equals(Unsafe.Add(ref searchSpace, index)))
165 goto Found;
166 if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
167 goto Found1;
168 if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
169 goto Found2;
170 if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
171 goto Found3;
173 index += 4;
176 while (length > 0)
178 if (value.Equals(Unsafe.Add(ref searchSpace, index)))
179 goto Found;
181 index += 1;
182 length--;
185 else
187 byte* len = (byte*)length;
188 for (index = (IntPtr)0; index.ToPointer() < len; index += 1)
190 if ((object)Unsafe.Add(ref searchSpace, index) is null)
192 goto Found;
196 return -1;
198 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
199 return (int)(byte*)index;
200 Found1:
201 return (int)(byte*)(index + 1);
202 Found2:
203 return (int)(byte*)(index + 2);
204 Found3:
205 return (int)(byte*)(index + 3);
206 Found4:
207 return (int)(byte*)(index + 4);
208 Found5:
209 return (int)(byte*)(index + 5);
210 Found6:
211 return (int)(byte*)(index + 6);
212 Found7:
213 return (int)(byte*)(index + 7);
216 public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
217 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
218 where T : IEquatable<T>
219 #nullable restore
221 Debug.Assert(length >= 0);
223 T lookUp;
224 int index = 0;
225 if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
227 while ((length - index) >= 8)
229 lookUp = Unsafe.Add(ref searchSpace, index);
230 if (value0.Equals(lookUp) || value1.Equals(lookUp))
231 goto Found;
232 lookUp = Unsafe.Add(ref searchSpace, index + 1);
233 if (value0.Equals(lookUp) || value1.Equals(lookUp))
234 goto Found1;
235 lookUp = Unsafe.Add(ref searchSpace, index + 2);
236 if (value0.Equals(lookUp) || value1.Equals(lookUp))
237 goto Found2;
238 lookUp = Unsafe.Add(ref searchSpace, index + 3);
239 if (value0.Equals(lookUp) || value1.Equals(lookUp))
240 goto Found3;
241 lookUp = Unsafe.Add(ref searchSpace, index + 4);
242 if (value0.Equals(lookUp) || value1.Equals(lookUp))
243 goto Found4;
244 lookUp = Unsafe.Add(ref searchSpace, index + 5);
245 if (value0.Equals(lookUp) || value1.Equals(lookUp))
246 goto Found5;
247 lookUp = Unsafe.Add(ref searchSpace, index + 6);
248 if (value0.Equals(lookUp) || value1.Equals(lookUp))
249 goto Found6;
250 lookUp = Unsafe.Add(ref searchSpace, index + 7);
251 if (value0.Equals(lookUp) || value1.Equals(lookUp))
252 goto Found7;
254 index += 8;
257 if ((length - index) >= 4)
259 lookUp = Unsafe.Add(ref searchSpace, index);
260 if (value0.Equals(lookUp) || value1.Equals(lookUp))
261 goto Found;
262 lookUp = Unsafe.Add(ref searchSpace, index + 1);
263 if (value0.Equals(lookUp) || value1.Equals(lookUp))
264 goto Found1;
265 lookUp = Unsafe.Add(ref searchSpace, index + 2);
266 if (value0.Equals(lookUp) || value1.Equals(lookUp))
267 goto Found2;
268 lookUp = Unsafe.Add(ref searchSpace, index + 3);
269 if (value0.Equals(lookUp) || value1.Equals(lookUp))
270 goto Found3;
272 index += 4;
275 while (index < length)
277 lookUp = Unsafe.Add(ref searchSpace, index);
278 if (value0.Equals(lookUp) || value1.Equals(lookUp))
279 goto Found;
281 index++;
284 else
286 for (index = 0; index < length; index++)
288 lookUp = Unsafe.Add(ref searchSpace, index);
289 if ((object?)lookUp is null)
291 if ((object?)value0 is null || (object?)value1 is null)
293 goto Found;
296 else if (lookUp.Equals(value0) || lookUp.Equals(value1))
298 goto Found;
303 return -1;
305 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
306 return index;
307 Found1:
308 return index + 1;
309 Found2:
310 return index + 2;
311 Found3:
312 return index + 3;
313 Found4:
314 return index + 4;
315 Found5:
316 return index + 5;
317 Found6:
318 return index + 6;
319 Found7:
320 return index + 7;
323 public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
324 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
325 where T : IEquatable<T>
326 #nullable restore
328 Debug.Assert(length >= 0);
330 T lookUp;
331 int index = 0;
332 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)
334 while ((length - index) >= 8)
336 lookUp = Unsafe.Add(ref searchSpace, index);
337 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
338 goto Found;
339 lookUp = Unsafe.Add(ref searchSpace, index + 1);
340 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
341 goto Found1;
342 lookUp = Unsafe.Add(ref searchSpace, index + 2);
343 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
344 goto Found2;
345 lookUp = Unsafe.Add(ref searchSpace, index + 3);
346 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
347 goto Found3;
348 lookUp = Unsafe.Add(ref searchSpace, index + 4);
349 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
350 goto Found4;
351 lookUp = Unsafe.Add(ref searchSpace, index + 5);
352 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
353 goto Found5;
354 lookUp = Unsafe.Add(ref searchSpace, index + 6);
355 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
356 goto Found6;
357 lookUp = Unsafe.Add(ref searchSpace, index + 7);
358 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
359 goto Found7;
361 index += 8;
364 if ((length - index) >= 4)
366 lookUp = Unsafe.Add(ref searchSpace, index);
367 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
368 goto Found;
369 lookUp = Unsafe.Add(ref searchSpace, index + 1);
370 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
371 goto Found1;
372 lookUp = Unsafe.Add(ref searchSpace, index + 2);
373 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
374 goto Found2;
375 lookUp = Unsafe.Add(ref searchSpace, index + 3);
376 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
377 goto Found3;
379 index += 4;
382 while (index < length)
384 lookUp = Unsafe.Add(ref searchSpace, index);
385 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
386 goto Found;
388 index++;
391 else
393 for (index = 0; index < length; index++)
395 lookUp = Unsafe.Add(ref searchSpace, index);
396 if ((object?)lookUp is null)
398 if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
400 goto Found;
403 else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
405 goto Found;
409 return -1;
411 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
412 return index;
413 Found1:
414 return index + 1;
415 Found2:
416 return index + 2;
417 Found3:
418 return index + 3;
419 Found4:
420 return index + 4;
421 Found5:
422 return index + 5;
423 Found6:
424 return index + 6;
425 Found7:
426 return index + 7;
429 public static int IndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
430 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
431 where T : IEquatable<T>
432 #nullable restore
434 Debug.Assert(searchSpaceLength >= 0);
435 Debug.Assert(valueLength >= 0);
437 if (valueLength == 0)
438 return -1; // A zero-length set of values is always treated as "not found".
440 int index = -1;
441 for (int i = 0; i < valueLength; i++)
443 int tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
444 if ((uint)tempIndex < (uint)index)
446 index = tempIndex;
447 // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value
448 searchSpaceLength = tempIndex;
450 if (index == 0)
451 break;
454 return index;
457 public static int LastIndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
458 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
459 where T : IEquatable<T>
460 #nullable restore
462 Debug.Assert(searchSpaceLength >= 0);
463 Debug.Assert(valueLength >= 0);
465 if (valueLength == 0)
466 return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
468 T valueHead = value;
469 ref T valueTail = ref Unsafe.Add(ref value, 1);
470 int valueTailLength = valueLength - 1;
472 int index = 0;
473 while (true)
475 Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength".
476 int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength;
477 if (remainingSearchSpaceLength <= 0)
478 break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
480 // Do a quick search for the first element of "value".
481 int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength);
482 if (relativeIndex == -1)
483 break;
485 // Found the first element of "value". See if the tail matches.
486 if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength))
487 return relativeIndex; // The tail matched. Return a successful find.
489 index += remainingSearchSpaceLength - relativeIndex;
491 return -1;
494 public static int LastIndexOf<T>(ref T searchSpace, T value, int length)
495 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
496 where T : IEquatable<T>
497 #nullable restore
499 Debug.Assert(length >= 0);
501 if (default(T)! != null || (object)value != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
503 while (length >= 8)
505 length -= 8;
507 if (value.Equals(Unsafe.Add(ref searchSpace, length + 7)))
508 goto Found7;
509 if (value.Equals(Unsafe.Add(ref searchSpace, length + 6)))
510 goto Found6;
511 if (value.Equals(Unsafe.Add(ref searchSpace, length + 5)))
512 goto Found5;
513 if (value.Equals(Unsafe.Add(ref searchSpace, length + 4)))
514 goto Found4;
515 if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
516 goto Found3;
517 if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
518 goto Found2;
519 if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
520 goto Found1;
521 if (value.Equals(Unsafe.Add(ref searchSpace, length)))
522 goto Found;
525 if (length >= 4)
527 length -= 4;
529 if (value.Equals(Unsafe.Add(ref searchSpace, length + 3)))
530 goto Found3;
531 if (value.Equals(Unsafe.Add(ref searchSpace, length + 2)))
532 goto Found2;
533 if (value.Equals(Unsafe.Add(ref searchSpace, length + 1)))
534 goto Found1;
535 if (value.Equals(Unsafe.Add(ref searchSpace, length)))
536 goto Found;
539 while (length > 0)
541 length--;
543 if (value.Equals(Unsafe.Add(ref searchSpace, length)))
544 goto Found;
547 else
549 for (length--; length >= 0; length--)
551 if ((object)Unsafe.Add(ref searchSpace, length) is null)
553 goto Found;
558 return -1;
560 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
561 return length;
562 Found1:
563 return length + 1;
564 Found2:
565 return length + 2;
566 Found3:
567 return length + 3;
568 Found4:
569 return length + 4;
570 Found5:
571 return length + 5;
572 Found6:
573 return length + 6;
574 Found7:
575 return length + 7;
578 public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
579 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
580 where T : IEquatable<T>
581 #nullable restore
583 Debug.Assert(length >= 0);
585 T lookUp;
586 if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
588 while (length >= 8)
590 length -= 8;
592 lookUp = Unsafe.Add(ref searchSpace, length + 7);
593 if (value0.Equals(lookUp) || value1.Equals(lookUp))
594 goto Found7;
595 lookUp = Unsafe.Add(ref searchSpace, length + 6);
596 if (value0.Equals(lookUp) || value1.Equals(lookUp))
597 goto Found6;
598 lookUp = Unsafe.Add(ref searchSpace, length + 5);
599 if (value0.Equals(lookUp) || value1.Equals(lookUp))
600 goto Found5;
601 lookUp = Unsafe.Add(ref searchSpace, length + 4);
602 if (value0.Equals(lookUp) || value1.Equals(lookUp))
603 goto Found4;
604 lookUp = Unsafe.Add(ref searchSpace, length + 3);
605 if (value0.Equals(lookUp) || value1.Equals(lookUp))
606 goto Found3;
607 lookUp = Unsafe.Add(ref searchSpace, length + 2);
608 if (value0.Equals(lookUp) || value1.Equals(lookUp))
609 goto Found2;
610 lookUp = Unsafe.Add(ref searchSpace, length + 1);
611 if (value0.Equals(lookUp) || value1.Equals(lookUp))
612 goto Found1;
613 lookUp = Unsafe.Add(ref searchSpace, length);
614 if (value0.Equals(lookUp) || value1.Equals(lookUp))
615 goto Found;
618 if (length >= 4)
620 length -= 4;
622 lookUp = Unsafe.Add(ref searchSpace, length + 3);
623 if (value0.Equals(lookUp) || value1.Equals(lookUp))
624 goto Found3;
625 lookUp = Unsafe.Add(ref searchSpace, length + 2);
626 if (value0.Equals(lookUp) || value1.Equals(lookUp))
627 goto Found2;
628 lookUp = Unsafe.Add(ref searchSpace, length + 1);
629 if (value0.Equals(lookUp) || value1.Equals(lookUp))
630 goto Found1;
631 lookUp = Unsafe.Add(ref searchSpace, length);
632 if (value0.Equals(lookUp) || value1.Equals(lookUp))
633 goto Found;
636 while (length > 0)
638 length--;
640 lookUp = Unsafe.Add(ref searchSpace, length);
641 if (value0.Equals(lookUp) || value1.Equals(lookUp))
642 goto Found;
645 else
647 for (length--; length >= 0; length--)
649 lookUp = Unsafe.Add(ref searchSpace, length);
650 if ((object?)lookUp is null)
652 if ((object?)value0 is null || (object?)value1 is null)
654 goto Found;
657 else if (lookUp.Equals(value0) || lookUp.Equals(value1))
659 goto Found;
664 return -1;
666 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
667 return length;
668 Found1:
669 return length + 1;
670 Found2:
671 return length + 2;
672 Found3:
673 return length + 3;
674 Found4:
675 return length + 4;
676 Found5:
677 return length + 5;
678 Found6:
679 return length + 6;
680 Found7:
681 return length + 7;
684 public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
685 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
686 where T : IEquatable<T>
687 #nullable restore
689 Debug.Assert(length >= 0);
691 T lookUp;
692 if (default(T)! != null || ((object)value0 != null && (object)value1 != null)) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
694 while (length >= 8)
696 length -= 8;
698 lookUp = Unsafe.Add(ref searchSpace, length + 7);
699 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
700 goto Found7;
701 lookUp = Unsafe.Add(ref searchSpace, length + 6);
702 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
703 goto Found6;
704 lookUp = Unsafe.Add(ref searchSpace, length + 5);
705 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
706 goto Found5;
707 lookUp = Unsafe.Add(ref searchSpace, length + 4);
708 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
709 goto Found4;
710 lookUp = Unsafe.Add(ref searchSpace, length + 3);
711 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
712 goto Found3;
713 lookUp = Unsafe.Add(ref searchSpace, length + 2);
714 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
715 goto Found2;
716 lookUp = Unsafe.Add(ref searchSpace, length + 1);
717 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
718 goto Found1;
719 lookUp = Unsafe.Add(ref searchSpace, length);
720 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
721 goto Found;
724 if (length >= 4)
726 length -= 4;
728 lookUp = Unsafe.Add(ref searchSpace, length + 3);
729 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
730 goto Found3;
731 lookUp = Unsafe.Add(ref searchSpace, length + 2);
732 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
733 goto Found2;
734 lookUp = Unsafe.Add(ref searchSpace, length + 1);
735 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
736 goto Found1;
737 lookUp = Unsafe.Add(ref searchSpace, length);
738 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
739 goto Found;
742 while (length > 0)
744 length--;
746 lookUp = Unsafe.Add(ref searchSpace, length);
747 if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
748 goto Found;
751 else
753 for (length--; length >= 0; length--)
755 lookUp = Unsafe.Add(ref searchSpace, length);
756 if ((object?)lookUp is null)
758 if ((object?)value0 is null || (object?)value1 is null || (object?)value2 is null)
760 goto Found;
763 else if (lookUp.Equals(value0) || lookUp.Equals(value1) || lookUp.Equals(value2))
765 goto Found;
770 return -1;
772 Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
773 return length;
774 Found1:
775 return length + 1;
776 Found2:
777 return length + 2;
778 Found3:
779 return length + 3;
780 Found4:
781 return length + 4;
782 Found5:
783 return length + 5;
784 Found6:
785 return length + 6;
786 Found7:
787 return length + 7;
790 public static int LastIndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
791 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
792 where T : IEquatable<T>
793 #nullable restore
795 Debug.Assert(searchSpaceLength >= 0);
796 Debug.Assert(valueLength >= 0);
798 if (valueLength == 0)
799 return -1; // A zero-length set of values is always treated as "not found".
801 int index = -1;
802 for (int i = 0; i < valueLength; i++)
804 int tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
805 if (tempIndex > index)
806 index = tempIndex;
808 return index;
811 public static bool SequenceEqual<T>(ref T first, ref T second, int length)
812 #nullable disable // to enable use with both T and T? for reference types due to IEquatable<T> being invariant
813 where T : IEquatable<T>
814 #nullable restore
816 Debug.Assert(length >= 0);
818 if (Unsafe.AreSame(ref first, ref second))
819 goto Equal;
821 IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
822 T lookUp0;
823 T lookUp1;
824 while (length >= 8)
826 length -= 8;
828 lookUp0 = Unsafe.Add(ref first, index);
829 lookUp1 = Unsafe.Add(ref second, index);
830 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
831 goto NotEqual;
832 lookUp0 = Unsafe.Add(ref first, index + 1);
833 lookUp1 = Unsafe.Add(ref second, index + 1);
834 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
835 goto NotEqual;
836 lookUp0 = Unsafe.Add(ref first, index + 2);
837 lookUp1 = Unsafe.Add(ref second, index + 2);
838 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
839 goto NotEqual;
840 lookUp0 = Unsafe.Add(ref first, index + 3);
841 lookUp1 = Unsafe.Add(ref second, index + 3);
842 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
843 goto NotEqual;
844 lookUp0 = Unsafe.Add(ref first, index + 4);
845 lookUp1 = Unsafe.Add(ref second, index + 4);
846 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
847 goto NotEqual;
848 lookUp0 = Unsafe.Add(ref first, index + 5);
849 lookUp1 = Unsafe.Add(ref second, index + 5);
850 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
851 goto NotEqual;
852 lookUp0 = Unsafe.Add(ref first, index + 6);
853 lookUp1 = Unsafe.Add(ref second, index + 6);
854 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
855 goto NotEqual;
856 lookUp0 = Unsafe.Add(ref first, index + 7);
857 lookUp1 = Unsafe.Add(ref second, index + 7);
858 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
859 goto NotEqual;
861 index += 8;
864 if (length >= 4)
866 length -= 4;
868 lookUp0 = Unsafe.Add(ref first, index);
869 lookUp1 = Unsafe.Add(ref second, index);
870 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
871 goto NotEqual;
872 lookUp0 = Unsafe.Add(ref first, index + 1);
873 lookUp1 = Unsafe.Add(ref second, index + 1);
874 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
875 goto NotEqual;
876 lookUp0 = Unsafe.Add(ref first, index + 2);
877 lookUp1 = Unsafe.Add(ref second, index + 2);
878 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
879 goto NotEqual;
880 lookUp0 = Unsafe.Add(ref first, index + 3);
881 lookUp1 = Unsafe.Add(ref second, index + 3);
882 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
883 goto NotEqual;
885 index += 4;
888 while (length > 0)
890 lookUp0 = Unsafe.Add(ref first, index);
891 lookUp1 = Unsafe.Add(ref second, index);
892 if (!(lookUp0?.Equals(lookUp1) ?? (object?)lookUp1 is null))
893 goto NotEqual;
894 index += 1;
895 length--;
898 Equal:
899 return true;
901 NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549
902 return false;
905 public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T second, int secondLength)
906 where T : IComparable<T>
908 Debug.Assert(firstLength >= 0);
909 Debug.Assert(secondLength >= 0);
911 int minLength = firstLength;
912 if (minLength > secondLength)
913 minLength = secondLength;
914 for (int i = 0; i < minLength; i++)
916 T lookUp = Unsafe.Add(ref second, i);
917 int result = (Unsafe.Add(ref first, i)?.CompareTo(lookUp) ?? (((object?)lookUp is null) ? 0 : -1));
918 if (result != 0)
919 return result;
921 return firstLength.CompareTo(secondLength);