Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b526aff

Browse files
benaadamsjkotas
authored andcommitted
Vectorize string.IndexOf(..., StringComparison.Ordinal) (#21076)
* Vectorize string.IndexOf(..., StringComparison.Ordinal) * Merge two IndexOf(..., int* matchLengthPtr) * fix 2 callers expect value.Length == 0 to be tested first 3rd caller ReplaceCore doesn't allow a value.Length == 0 so moving it first is ok
1 parent ba7d5ce commit b526aff

File tree

8 files changed

+172
-148
lines changed

8 files changed

+172
-148
lines changed

src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Unix.cs

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using System.Runtime.InteropServices;
99
using System.Security;
1010

11+
using Internal.Runtime.CompilerServices;
12+
1113
namespace System.Globalization
1214
{
1315
public partial class CompareInfo
@@ -240,31 +242,12 @@ internal unsafe int IndexOfCore(string source, string target, int startIndex, in
240242
Debug.Assert(!string.IsNullOrEmpty(source));
241243
Debug.Assert(target != null);
242244
Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0);
243-
244-
int index;
245-
246-
if (target.Length == 0)
247-
{
248-
if (matchLengthPtr != null)
249-
*matchLengthPtr = 0;
250-
return startIndex;
251-
}
252-
253-
if (options == CompareOptions.Ordinal)
254-
{
255-
index = IndexOfOrdinal(source, target, startIndex, count, ignoreCase: false);
256-
if (index != -1)
257-
{
258-
if (matchLengthPtr != null)
259-
*matchLengthPtr = target.Length;
260-
}
261-
return index;
262-
}
245+
Debug.Assert((options & CompareOptions.Ordinal) == 0);
263246

264247
#if CORECLR
265248
if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options) && source.IsFastSort() && target.IsFastSort())
266249
{
267-
index = IndexOf(source, target, startIndex, count, GetOrdinalCompareOptions(options));
250+
int index = IndexOf(source, target, startIndex, count, GetOrdinalCompareOptions(options));
268251
if (index != -1)
269252
{
270253
if (matchLengthPtr != null)
@@ -277,7 +260,7 @@ internal unsafe int IndexOfCore(string source, string target, int startIndex, in
277260
fixed (char* pSource = source)
278261
fixed (char* pTarget = target)
279262
{
280-
index = Interop.Globalization.IndexOf(_sortHandle, pTarget, target.Length, pSource + startIndex, count, options, matchLengthPtr);
263+
int index = Interop.Globalization.IndexOf(_sortHandle, pTarget, target.Length, pSource + startIndex, count, options, matchLengthPtr);
281264

282265
return index != -1 ? index + startIndex : -1;
283266
}

src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Windows.cs

Lines changed: 29 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
using System.Buffers;
66
using System.Diagnostics;
7-
using System.Security;
8-
using System.Runtime.CompilerServices;
97
using System.Runtime.InteropServices;
108

9+
using Internal.Runtime.CompilerServices;
10+
1111
namespace System.Globalization
1212
{
1313
public partial class CompareInfo
@@ -327,37 +327,13 @@ internal unsafe int IndexOfCore(string source, string target, int startIndex, in
327327
Debug.Assert(source != null);
328328
Debug.Assert(target != null);
329329
Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0);
330+
Debug.Assert((options & CompareOptions.Ordinal) == 0);
330331

331-
if (target.Length == 0)
332-
{
333-
if (matchLengthPtr != null)
334-
*matchLengthPtr = 0;
335-
return startIndex;
336-
}
337-
338-
if (source.Length == 0)
339-
{
340-
return -1;
341-
}
342-
343-
if ((options & CompareOptions.Ordinal) != 0)
344-
{
345-
int retValue = FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: false);
346-
if (retValue >= 0)
347-
{
348-
if (matchLengthPtr != null)
349-
*matchLengthPtr = target.Length;
350-
}
351-
return retValue;
352-
}
353-
else
332+
int retValue = FindString(FIND_FROMSTART | (uint)GetNativeCompareFlags(options), source, startIndex, count,
333+
target, 0, target.Length, matchLengthPtr);
334+
if (retValue >= 0)
354335
{
355-
int retValue = FindString(FIND_FROMSTART | (uint)GetNativeCompareFlags(options), source, startIndex, count,
356-
target, 0, target.Length, matchLengthPtr);
357-
if (retValue >= 0)
358-
{
359-
return retValue + startIndex;
360-
}
336+
return retValue + startIndex;
361337
}
362338

363339
return -1;
@@ -388,7 +364,7 @@ private unsafe int LastIndexOfCore(string source, string target, int startIndex,
388364

389365
if ((options & CompareOptions.Ordinal) != 0)
390366
{
391-
return FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: true);
367+
return FastLastIndexOfString(source, target, startIndex, count, target.Length);
392368
}
393369
else
394370
{
@@ -462,75 +438,44 @@ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffi
462438
private const int FIND_FROMSTART = 0x00400000;
463439
private const int FIND_FROMEND = 0x00800000;
464440

465-
// TODO: Instead of this method could we just have upstack code call IndexOfOrdinal with ignoreCase = false?
466-
private static unsafe int FastIndexOfString(string source, string target, int startIndex, int sourceCount, int targetCount, bool findLastIndex)
441+
// TODO: Instead of this method could we just have upstack code call LastIndexOfOrdinal with ignoreCase = false?
442+
private static unsafe int FastLastIndexOfString(string source, string target, int startIndex, int sourceCount, int targetCount)
467443
{
468444
int retValue = -1;
469445

470-
int sourceStartIndex = findLastIndex ? startIndex - sourceCount + 1 : startIndex;
446+
int sourceStartIndex = startIndex - sourceCount + 1;
471447

472448
fixed (char* pSource = source, spTarget = target)
473449
{
474450
char* spSubSource = pSource + sourceStartIndex;
475451

476-
if (findLastIndex)
452+
int endPattern = sourceCount - targetCount;
453+
if (endPattern < 0)
454+
return -1;
455+
456+
Debug.Assert(target.Length >= 1);
457+
char patternChar0 = spTarget[0];
458+
for (int ctrSrc = endPattern; ctrSrc >= 0; ctrSrc--)
477459
{
478-
int startPattern = (sourceCount - 1) - targetCount + 1;
479-
if (startPattern < 0)
480-
return -1;
460+
if (spSubSource[ctrSrc] != patternChar0)
461+
continue;
481462

482-
char patternChar0 = spTarget[0];
483-
for (int ctrSrc = startPattern; ctrSrc >= 0; ctrSrc--)
463+
int ctrPat;
464+
for (ctrPat = 1; ctrPat < targetCount; ctrPat++)
484465
{
485-
if (spSubSource[ctrSrc] != patternChar0)
486-
continue;
487-
488-
int ctrPat;
489-
for (ctrPat = 1; ctrPat < targetCount; ctrPat++)
490-
{
491-
if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat])
492-
break;
493-
}
494-
if (ctrPat == targetCount)
495-
{
496-
retValue = ctrSrc;
466+
if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat])
497467
break;
498-
}
499468
}
500-
501-
if (retValue >= 0)
469+
if (ctrPat == targetCount)
502470
{
503-
retValue += startIndex - sourceCount + 1;
471+
retValue = ctrSrc;
472+
break;
504473
}
505474
}
506-
else
507-
{
508-
int endPattern = (sourceCount - 1) - targetCount + 1;
509-
if (endPattern < 0)
510-
return -1;
511-
512-
char patternChar0 = spTarget[0];
513-
for (int ctrSrc = 0; ctrSrc <= endPattern; ctrSrc++)
514-
{
515-
if (spSubSource[ctrSrc] != patternChar0)
516-
continue;
517-
int ctrPat;
518-
for (ctrPat = 1; ctrPat < targetCount; ctrPat++)
519-
{
520-
if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat])
521-
break;
522-
}
523-
if (ctrPat == targetCount)
524-
{
525-
retValue = ctrSrc;
526-
break;
527-
}
528-
}
529475

530-
if (retValue >= 0)
531-
{
532-
retValue += startIndex;
533-
}
476+
if (retValue >= 0)
477+
{
478+
retValue += startIndex - sourceCount + 1;
534479
}
535480
}
536481

src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -972,20 +972,12 @@ public unsafe virtual int IndexOf(string source, char value, int startIndex, int
972972
return -1;
973973
}
974974

975-
if (options == CompareOptions.OrdinalIgnoreCase)
976-
{
977-
return source.IndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
978-
}
979-
980975
// Validate CompareOptions
981976
// Ordinal can't be selected with other flags
982-
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
977+
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal && options != CompareOptions.OrdinalIgnoreCase))
983978
throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
984979

985-
if (GlobalizationMode.Invariant)
986-
return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
987-
988-
return IndexOfCore(source, new string(value, 1), startIndex, count, options, null);
980+
return IndexOf(source, new string(value, 1), startIndex, count, options, null);
989981
}
990982

991983
public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options)
@@ -1020,28 +1012,21 @@ public unsafe virtual int IndexOf(string source, string value, int startIndex, i
10201012
if (count < 0 || startIndex > source.Length - count)
10211013
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
10221014

1023-
if (options == CompareOptions.OrdinalIgnoreCase)
1024-
{
1025-
return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
1026-
}
1027-
10281015
// Validate CompareOptions
10291016
// Ordinal can't be selected with other flags
1030-
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
1017+
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal && options != CompareOptions.OrdinalIgnoreCase))
10311018
throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
10321019

1033-
if (GlobalizationMode.Invariant)
1034-
return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
1035-
1036-
return IndexOfCore(source, value, startIndex, count, options, null);
1020+
return IndexOf(source, value, startIndex, count, options, null);
10371021
}
10381022

1039-
internal int IndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
1023+
internal int IndexOfOrdinalIgnoreCase(ReadOnlySpan<char> source, ReadOnlySpan<char> value)
10401024
{
10411025
Debug.Assert(!GlobalizationMode.Invariant);
10421026
Debug.Assert(!source.IsEmpty);
10431027
Debug.Assert(!value.IsEmpty);
1044-
return IndexOfOrdinalCore(source, value, ignoreCase, fromBeginning: true);
1028+
1029+
return IndexOfOrdinalCore(source, value, ignoreCase: true, fromBeginning: true);
10451030
}
10461031

10471032
internal int LastIndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
@@ -1075,16 +1060,15 @@ internal unsafe int IndexOf(string source, string value, int startIndex, int cou
10751060
Debug.Assert(source != null);
10761061
Debug.Assert(value != null);
10771062
Debug.Assert(startIndex >= 0);
1078-
Debug.Assert(matchLengthPtr != null);
1079-
*matchLengthPtr = 0;
10801063

1081-
if (source.Length == 0)
1064+
if (matchLengthPtr != null)
10821065
{
1083-
if (value.Length == 0)
1084-
{
1085-
return 0;
1086-
}
1087-
return -1;
1066+
*matchLengthPtr = 0;
1067+
}
1068+
1069+
if (value.Length == 0)
1070+
{
1071+
return startIndex;
10881072
}
10891073

10901074
if (startIndex >= source.Length)
@@ -1095,7 +1079,7 @@ internal unsafe int IndexOf(string source, string value, int startIndex, int cou
10951079
if (options == CompareOptions.OrdinalIgnoreCase)
10961080
{
10971081
int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
1098-
if (res >= 0)
1082+
if (res >= 0 && matchLengthPtr != null)
10991083
{
11001084
*matchLengthPtr = value.Length;
11011085
}
@@ -1105,18 +1089,49 @@ internal unsafe int IndexOf(string source, string value, int startIndex, int cou
11051089
if (GlobalizationMode.Invariant)
11061090
{
11071091
int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
1108-
if (res >= 0)
1092+
if (res >= 0 && matchLengthPtr != null)
11091093
{
11101094
*matchLengthPtr = value.Length;
11111095
}
11121096
return res;
11131097
}
11141098

1115-
return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr);
1099+
if (options == CompareOptions.Ordinal)
1100+
{
1101+
int retValue = SpanHelpers.IndexOf(
1102+
ref Unsafe.Add(ref source.GetRawStringData(), startIndex),
1103+
count,
1104+
ref value.GetRawStringData(),
1105+
value.Length);
1106+
1107+
if (retValue >= 0)
1108+
{
1109+
retValue += startIndex;
1110+
if (matchLengthPtr != null)
1111+
*matchLengthPtr = value.Length;
1112+
}
1113+
1114+
return retValue;
1115+
}
1116+
else
1117+
{
1118+
return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr);
1119+
}
11161120
}
11171121

11181122
internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
11191123
{
1124+
if (!ignoreCase)
1125+
{
1126+
int result = SpanHelpers.IndexOf(
1127+
ref Unsafe.Add(ref source.GetRawStringData(), startIndex),
1128+
count,
1129+
ref value.GetRawStringData(),
1130+
value.Length);
1131+
1132+
return (result >= 0 ? startIndex : 0) + result;
1133+
}
1134+
11201135
if (GlobalizationMode.Invariant)
11211136
{
11221137
return InvariantIndexOf(source, value, startIndex, count, ignoreCase);

src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value
141141
return -1;
142142
}
143143

144+
if (comparisonType == StringComparison.Ordinal)
145+
{
146+
return SpanHelpers.IndexOf(
147+
ref MemoryMarshal.GetReference(span),
148+
span.Length,
149+
ref MemoryMarshal.GetReference(value),
150+
value.Length);
151+
}
152+
144153
if (GlobalizationMode.Invariant)
145154
{
146155
return CompareInfo.InvariantIndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType) != CompareOptions.None);
@@ -157,8 +166,8 @@ public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value
157166
return CompareInfo.Invariant.IndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
158167

159168
default:
160-
Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);
161-
return CompareInfo.Invariant.IndexOfOrdinal(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType) != CompareOptions.None);
169+
Debug.Assert(comparisonType == StringComparison.OrdinalIgnoreCase);
170+
return CompareInfo.Invariant.IndexOfOrdinalIgnoreCase(span, value);
162171
}
163172
}
164173

0 commit comments

Comments
 (0)