| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using System.Numerics;
- using System.Runtime.Intrinsics.X86;
- using Internal.Runtime.CompilerServices;
- #if BIT64
- using nuint = System.UInt64;
- #else
- using nuint = System.UInt32;
- #endif
- namespace System
- {
- internal static partial class SpanHelpers // .Char
- {
- public static int IndexOf(ref char searchSpace, int searchSpaceLength, ref char value, int valueLength)
- {
- Debug.Assert(searchSpaceLength >= 0);
- Debug.Assert(valueLength >= 0);
- if (valueLength == 0)
- return 0; // A zero-length sequence is always treated as "found" at the start of the search space.
- char valueHead = value;
- ref char valueTail = ref Unsafe.Add(ref value, 1);
- int valueTailLength = valueLength - 1;
- int remainingSearchSpaceLength = searchSpaceLength - valueTailLength;
- int index = 0;
- while (remainingSearchSpaceLength > 0)
- {
- // Do a quick search for the first element of "value".
- int relativeIndex = IndexOf(ref Unsafe.Add(ref searchSpace, index), valueHead, remainingSearchSpaceLength);
- if (relativeIndex == -1)
- break;
- remainingSearchSpaceLength -= relativeIndex;
- index += relativeIndex;
- if (remainingSearchSpaceLength <= 0)
- break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there.
- // Found the first element of "value". See if the tail matches.
- if (SequenceEqual(
- ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, index + 1)),
- ref Unsafe.As<char, byte>(ref valueTail),
- (nuint)valueTailLength * 2))
- {
- return index; // The tail matched. Return a successful find.
- }
- remainingSearchSpaceLength--;
- index++;
- }
- return -1;
- }
- public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength)
- {
- Debug.Assert(firstLength >= 0);
- Debug.Assert(secondLength >= 0);
- int lengthDelta = firstLength - secondLength;
- if (Unsafe.AreSame(ref first, ref second))
- goto Equal;
- IntPtr minLength = (IntPtr)((firstLength < secondLength) ? firstLength : secondLength);
- IntPtr i = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
- if ((byte*)minLength >= (byte*)(sizeof(UIntPtr) / sizeof(char)))
- {
- if (Vector.IsHardwareAccelerated && (byte*)minLength >= (byte*)Vector<ushort>.Count)
- {
- IntPtr nLength = minLength - Vector<ushort>.Count;
- do
- {
- if (Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
- Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, i))))
- {
- break;
- }
- i += Vector<ushort>.Count;
- }
- while ((byte*)nLength >= (byte*)i);
- }
- while ((byte*)minLength >= (byte*)(i + sizeof(UIntPtr) / sizeof(char)))
- {
- if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
- Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, i))))
- {
- break;
- }
- i += sizeof(UIntPtr) / sizeof(char);
- }
- }
- if (sizeof(UIntPtr) > sizeof(int) && (byte*)minLength >= (byte*)(i + sizeof(int) / sizeof(char)))
- {
- if (Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) ==
- Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, i))))
- {
- i += sizeof(int) / sizeof(char);
- }
- }
- while ((byte*)i < (byte*)minLength)
- {
- int result = Unsafe.Add(ref first, i).CompareTo(Unsafe.Add(ref second, i));
- if (result != 0)
- return result;
- i += 1;
- }
- Equal:
- return lengthDelta;
- }
- // Adapted from IndexOf(...)
- public static unsafe bool Contains(ref char searchSpace, char value, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (value == *pCh ||
- value == *(pCh + 1) ||
- value == *(pCh + 2) ||
- value == *(pCh + 3))
- {
- goto Found;
- }
- pCh += 4;
- }
- while (length > 0)
- {
- length -= 1;
- if (value == *pCh)
- goto Found;
- pCh += 1;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> vComparison = new Vector<ushort>(value);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- goto Found;
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return false;
- Found:
- return true;
- }
- }
- public static unsafe int IndexOf(ref char searchSpace, char value, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (pCh[0] == value)
- goto Found;
- if (pCh[1] == value)
- goto Found1;
- if (pCh[2] == value)
- goto Found2;
- if (pCh[3] == value)
- goto Found3;
- pCh += 4;
- }
- while (length > 0)
- {
- length--;
- if (pCh[0] == value)
- goto Found;
- pCh++;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> vComparison = new Vector<ushort>(value);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of first match
- return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found3:
- pCh++;
- Found2:
- pCh++;
- Found1:
- pCh++;
- Found:
- return (int)(pCh - pChars);
- }
- }
- public static unsafe int IndexOfAny(ref char searchSpace, char value0, char value1, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (pCh[0] == value0 || pCh[0] == value1)
- goto Found;
- if (pCh[1] == value0 || pCh[1] == value1)
- goto Found1;
- if (pCh[2] == value0 || pCh[2] == value1)
- goto Found2;
- if (pCh[3] == value0 || pCh[3] == value1)
- goto Found3;
- pCh += 4;
- }
- while (length > 0)
- {
- length--;
- if (pCh[0] == value0 || pCh[0] == value1)
- goto Found;
- pCh++;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> values0 = new Vector<ushort>(value0);
- Vector<ushort> values1 = new Vector<ushort>(value1);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vData = Unsafe.Read<Vector<ushort>>(pCh);
- var vMatches = Vector.BitwiseOr(
- Vector.Equals(vData, values0),
- Vector.Equals(vData, values1));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of first match
- return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found3:
- pCh++;
- Found2:
- pCh++;
- Found1:
- pCh++;
- Found:
- return (int)(pCh - pChars);
- }
- }
- public static unsafe int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2)
- goto Found;
- if (pCh[1] == value0 || pCh[1] == value1 || pCh[1] == value2)
- goto Found1;
- if (pCh[2] == value0 || pCh[2] == value1 || pCh[2] == value2)
- goto Found2;
- if (pCh[3] == value0 || pCh[3] == value1 || pCh[3] == value2)
- goto Found3;
- pCh += 4;
- }
- while (length > 0)
- {
- length--;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2)
- goto Found;
- pCh++;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> values0 = new Vector<ushort>(value0);
- Vector<ushort> values1 = new Vector<ushort>(value1);
- Vector<ushort> values2 = new Vector<ushort>(value2);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vData = Unsafe.Read<Vector<ushort>>(pCh);
- var vMatches = Vector.BitwiseOr(
- Vector.BitwiseOr(
- Vector.Equals(vData, values0),
- Vector.Equals(vData, values1)),
- Vector.Equals(vData, values2));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of first match
- return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found3:
- pCh++;
- Found2:
- pCh++;
- Found1:
- pCh++;
- Found:
- return (int)(pCh - pChars);
- }
- }
- public static unsafe int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, char value3, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2 || pCh[0] == value3)
- goto Found;
- if (pCh[1] == value0 || pCh[1] == value1 || pCh[1] == value2 || pCh[1] == value3)
- goto Found1;
- if (pCh[2] == value0 || pCh[2] == value1 || pCh[2] == value2 || pCh[2] == value3)
- goto Found2;
- if (pCh[3] == value0 || pCh[3] == value1 || pCh[3] == value2 || pCh[3] == value3)
- goto Found3;
- pCh += 4;
- }
- while (length > 0)
- {
- length--;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2 || pCh[0] == value3)
- goto Found;
- pCh++;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> values0 = new Vector<ushort>(value0);
- Vector<ushort> values1 = new Vector<ushort>(value1);
- Vector<ushort> values2 = new Vector<ushort>(value2);
- Vector<ushort> values3 = new Vector<ushort>(value3);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vData = Unsafe.Read<Vector<ushort>>(pCh);
- var vMatches = Vector.BitwiseOr(
- Vector.BitwiseOr(
- Vector.BitwiseOr(Vector.Equals(vData, values0), Vector.Equals(vData, values1)),
- Vector.Equals(vData, values2)),
- Vector.Equals(vData, values3));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of first match
- return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found3:
- pCh++;
- Found2:
- pCh++;
- Found1:
- pCh++;
- Found:
- return (int)(pCh - pChars);
- }
- }
- public static unsafe int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, char value3, char value4, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars;
- char* pEndCh = pCh + length;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially until we are vector aligned
- // This is equivalent to:
- // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2 || pCh[0] == value3 || pCh[0] == value4)
- goto Found;
- if (pCh[1] == value0 || pCh[1] == value1 || pCh[1] == value2 || pCh[1] == value3 || pCh[1] == value4)
- goto Found1;
- if (pCh[2] == value0 || pCh[2] == value1 || pCh[2] == value2 || pCh[2] == value3 || pCh[2] == value4)
- goto Found2;
- if (pCh[3] == value0 || pCh[3] == value1 || pCh[3] == value2 || pCh[3] == value3 || pCh[3] == value4)
- goto Found3;
- pCh += 4;
- }
- while (length > 0)
- {
- length--;
- if (pCh[0] == value0 || pCh[0] == value1 || pCh[0] == value2 || pCh[0] == value3 || pCh[0] == value4)
- goto Found;
- pCh++;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh < pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
- length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> values0 = new Vector<ushort>(value0);
- Vector<ushort> values1 = new Vector<ushort>(value1);
- Vector<ushort> values2 = new Vector<ushort>(value2);
- Vector<ushort> values3 = new Vector<ushort>(value3);
- Vector<ushort> values4 = new Vector<ushort>(value4);
- while (length > 0)
- {
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
- Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vData = Unsafe.Read<Vector<ushort>>(pCh);
- var vMatches = Vector.BitwiseOr(
- Vector.BitwiseOr(
- Vector.BitwiseOr(
- Vector.BitwiseOr(Vector.Equals(vData, values0), Vector.Equals(vData, values1)),
- Vector.Equals(vData, values2)),
- Vector.Equals(vData, values3)),
- Vector.Equals(vData, values4));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh += Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of first match
- return (int)(pCh - pChars) + LocateFirstFoundChar(vMatches);
- }
- if (pCh < pEndCh)
- {
- length = (int)(pEndCh - pCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found3:
- pCh++;
- Found2:
- pCh++;
- Found1:
- pCh++;
- Found:
- return (int)(pCh - pChars);
- }
- }
- public static unsafe int LastIndexOf(ref char searchSpace, char value, int length)
- {
- Debug.Assert(length >= 0);
- fixed (char* pChars = &searchSpace)
- {
- char* pCh = pChars + length;
- char* pEndCh = pChars;
- if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
- {
- // Figure out how many characters to read sequentially from the end until we are vector aligned
- // This is equivalent to: length = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- length = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
- }
- SequentialScan:
- while (length >= 4)
- {
- length -= 4;
- pCh -= 4;
- if (*(pCh + 3) == value)
- goto Found3;
- if (*(pCh + 2) == value)
- goto Found2;
- if (*(pCh + 1) == value)
- goto Found1;
- if (*pCh == value)
- goto Found;
- }
- while (length > 0)
- {
- length -= 1;
- pCh -= 1;
- if (*pCh == value)
- goto Found;
- }
- // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
- // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
- if (Vector.IsHardwareAccelerated && pCh > pEndCh)
- {
- // Get the highest multiple of Vector<ushort>.Count that is within the search space.
- // That will be how many times we iterate in the loop below.
- // This is equivalent to: length = Vector<ushort>.Count * ((int)(pCh - pEndCh) / Vector<ushort>.Count)
- length = (int)((pCh - pEndCh) & ~(Vector<ushort>.Count - 1));
- // Get comparison Vector
- Vector<ushort> vComparison = new Vector<ushort>(value);
- while (length > 0)
- {
- char* pStart = pCh - Vector<ushort>.Count;
- // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh (and hence pSart) is always vector aligned
- Debug.Assert(((int)pStart & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
- Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pStart));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- pCh -= Vector<ushort>.Count;
- length -= Vector<ushort>.Count;
- continue;
- }
- // Find offset of last match
- return (int)(pStart - pEndCh) + LocateLastFoundChar(vMatches);
- }
- if (pCh > pEndCh)
- {
- length = (int)(pCh - pEndCh);
- goto SequentialScan;
- }
- }
- return -1;
- Found:
- return (int)(pCh - pEndCh);
- Found1:
- return (int)(pCh - pEndCh) + 1;
- Found2:
- return (int)(pCh - pEndCh) + 2;
- Found3:
- return (int)(pCh - pEndCh) + 3;
- }
- }
- // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int LocateFirstFoundChar(Vector<ushort> match)
- {
- var vector64 = Vector.AsVectorUInt64(match);
- ulong candidate = 0;
- int i = 0;
- // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
- for (; i < Vector<ulong>.Count; i++)
- {
- candidate = vector64[i];
- if (candidate != 0)
- {
- break;
- }
- }
- // Single LEA instruction with jitted const (using function result)
- return i * 4 + LocateFirstFoundChar(candidate);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int LocateFirstFoundChar(ulong match)
- {
- // TODO: Arm variants
- if (Bmi1.X64.IsSupported)
- {
- return (int)(Bmi1.X64.TrailingZeroCount(match) >> 4);
- }
- else
- {
- unchecked
- {
- // Flag least significant power of two bit
- var powerOfTwoFlag = match ^ (match - 1);
- // Shift all powers of two into the high byte and extract
- return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49);
- }
- }
- }
- private const ulong XorPowerOfTwoToHighChar = (0x03ul |
- 0x02ul << 16 |
- 0x01ul << 32) + 1;
- // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int LocateLastFoundChar(Vector<ushort> match)
- {
- var vector64 = Vector.AsVectorUInt64(match);
- ulong candidate = 0;
- int i = Vector<ulong>.Count - 1;
- // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
- for (; i >= 0; i--)
- {
- candidate = vector64[i];
- if (candidate != 0)
- {
- break;
- }
- }
- // Single LEA instruction with jitted const (using function result)
- return i * 4 + LocateLastFoundChar(candidate);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int LocateLastFoundChar(ulong match)
- {
- // TODO: Arm variants
- if (Lzcnt.X64.IsSupported)
- {
- return 3 - (int)(Lzcnt.X64.LeadingZeroCount(match) >> 4);
- }
- else
- {
- // Find the most significant char that has its highest bit set
- int index = 3;
- while ((long)match > 0)
- {
- match = match << 16;
- index--;
- }
- return index;
- }
- }
- }
- }
|