Skip to content

Commit faf08f7

Browse files
Prep CompareInfo for spanification (#32385)
1 parent 02aa1d9 commit faf08f7

File tree

6 files changed

+158
-191
lines changed

6 files changed

+158
-191
lines changed

src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ internal static partial class Interop
99
{
1010
internal static unsafe partial class Kernel32
1111
{
12+
// Under debug mode only, we'll want to check the error codes
13+
// of some of the p/invokes we make.
14+
15+
#if DEBUG
16+
private const bool SetLastErrorForDebug = true;
17+
#else
18+
private const bool SetLastErrorForDebug = false;
19+
#endif
20+
1221
internal const uint LOCALE_ALLOW_NEUTRAL_NAMES = 0x08000000; // Flag to allow returning neutral names/lcids for name conversion
1322
internal const uint LOCALE_ILANGUAGE = 0x00000001;
1423
internal const uint LOCALE_SUPPLEMENTAL = 0x00000002;
@@ -52,7 +61,7 @@ internal static extern int LCMapStringEx(
5261
void* lpReserved,
5362
IntPtr sortHandle);
5463

55-
[DllImport("kernel32.dll", EntryPoint = "FindNLSStringEx")]
64+
[DllImport("kernel32.dll", EntryPoint = "FindNLSStringEx", SetLastError = SetLastErrorForDebug)]
5665
internal static extern int FindNLSStringEx(
5766
char* lpLocaleName,
5867
uint dwFindNLSStringFlags,
@@ -85,14 +94,14 @@ internal static extern int CompareStringOrdinal(
8594
int cchCount2,
8695
bool bIgnoreCase);
8796

88-
[DllImport("kernel32.dll", EntryPoint = "FindStringOrdinal")]
97+
[DllImport("kernel32.dll", EntryPoint = "FindStringOrdinal", SetLastError = SetLastErrorForDebug)]
8998
internal static extern int FindStringOrdinal(
9099
uint dwFindStringOrdinalFlags,
91100
char* lpStringSource,
92101
int cchSource,
93102
char* lpStringValue,
94103
int cchValue,
95-
int bIgnoreCase);
104+
BOOL bIgnoreCase);
96105

97106
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
98107
internal static extern bool IsNLSDefinedString(

src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ private SortKey InvariantCreateSortKey(string source, CompareOptions options)
243243
}
244244
}
245245

246-
return new SortKey(Name, source, options, keyData);
246+
return new SortKey(this, source, options, keyData);
247247
}
248248

249249
private static void InvariantCreateSortKeyOrdinal(ReadOnlySpan<char> source, Span<byte> sortKey)

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

Lines changed: 24 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -43,64 +43,22 @@ private void InitSort(CultureInfo culture)
4343
}
4444
}
4545

46-
internal static unsafe int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase)
47-
{
48-
Debug.Assert(!GlobalizationMode.Invariant);
49-
50-
Debug.Assert(source != null);
51-
Debug.Assert(value != null);
52-
53-
if (value.Length == 0)
54-
{
55-
return startIndex;
56-
}
57-
58-
if (count < value.Length)
59-
{
60-
return -1;
61-
}
62-
63-
if (ignoreCase)
64-
{
65-
fixed (char* pSource = source)
66-
{
67-
int index = Interop.Globalization.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + startIndex, count, findLast: false);
68-
return index != -1 ?
69-
startIndex + index :
70-
-1;
71-
}
72-
}
73-
74-
int endIndex = startIndex + (count - value.Length);
75-
for (int i = startIndex; i <= endIndex; i++)
76-
{
77-
int valueIndex, sourceIndex;
78-
79-
for (valueIndex = 0, sourceIndex = i;
80-
valueIndex < value.Length && source[sourceIndex] == value[valueIndex];
81-
valueIndex++, sourceIndex++) ;
82-
83-
if (valueIndex == value.Length)
84-
{
85-
return i;
86-
}
87-
}
88-
89-
return -1;
90-
}
91-
9246
internal static unsafe int IndexOfOrdinalCore(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase, bool fromBeginning)
9347
{
9448
Debug.Assert(!GlobalizationMode.Invariant);
49+
Debug.Assert(!value.IsEmpty);
9550

96-
Debug.Assert(source.Length != 0);
97-
Debug.Assert(value.Length != 0);
51+
// Ordinal (non-linguistic) comparisons require the length of the target string to be no greater
52+
// than the length of the search space. Since our caller already checked for empty target strings,
53+
// the below check also handles the case of empty search space strings.
9854

9955
if (source.Length < value.Length)
10056
{
10157
return -1;
10258
}
10359

60+
Debug.Assert(!source.IsEmpty);
61+
10462
if (ignoreCase)
10563
{
10664
fixed (char* pSource = &MemoryMarshal.GetReference(source))
@@ -199,9 +157,14 @@ private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int c
199157
{
200158
Debug.Assert(!GlobalizationMode.Invariant);
201159

160+
Debug.Assert(count1 > 0);
161+
Debug.Assert(count2 > 0);
162+
202163
fixed (char* char1 = &string1)
203164
fixed (char* char2 = &string2)
204165
{
166+
Debug.Assert(char1 != null);
167+
Debug.Assert(char2 != null);
205168
return Interop.Globalization.CompareStringOrdinalIgnoreCase(char1, count1, char2, count2);
206169
}
207170
}
@@ -215,6 +178,9 @@ private unsafe int CompareString(ReadOnlySpan<char> string1, string string2, Com
215178
Debug.Assert(string2 != null);
216179
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
217180

181+
// Unlike NLS, ICU (ucol_getSortKey) allows passing nullptr for either of the source arguments
182+
// as long as the corresponding length parameter is 0.
183+
218184
fixed (char* pString1 = &MemoryMarshal.GetReference(string1))
219185
fixed (char* pString2 = &string2.GetRawStringData())
220186
{
@@ -227,6 +193,9 @@ private unsafe int CompareString(ReadOnlySpan<char> string1, ReadOnlySpan<char>
227193
Debug.Assert(!GlobalizationMode.Invariant);
228194
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
229195

196+
// Unlike NLS, ICU (ucol_getSortKey) allows passing nullptr for either of the source arguments
197+
// as long as the corresponding length parameter is 0.
198+
230199
fixed (char* pString1 = &MemoryMarshal.GetReference(string1))
231200
fixed (char* pString2 = &MemoryMarshal.GetReference(string2))
232201
{
@@ -540,7 +509,6 @@ private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> pre
540509
{
541510
Debug.Assert(!GlobalizationMode.Invariant);
542511

543-
Debug.Assert(!source.IsEmpty);
544512
Debug.Assert(!prefix.IsEmpty);
545513
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
546514

@@ -553,7 +521,7 @@ private unsafe bool StartsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> pre
553521
}
554522
else
555523
{
556-
fixed (char* pSource = &MemoryMarshal.GetReference(source))
524+
fixed (char* pSource = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
557525
fixed (char* pPrefix = &MemoryMarshal.GetReference(prefix))
558526
{
559527
return Interop.Globalization.StartsWith(_sortHandle, pPrefix, prefix.Length, pSource, source.Length, options);
@@ -565,13 +533,12 @@ private unsafe bool StartsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> source,
565533
{
566534
Debug.Assert(!GlobalizationMode.Invariant);
567535

568-
Debug.Assert(!source.IsEmpty);
569536
Debug.Assert(!prefix.IsEmpty);
570537
Debug.Assert(_isAsciiEqualityOrdinal);
571538

572539
int length = Math.Min(source.Length, prefix.Length);
573540

574-
fixed (char* ap = &MemoryMarshal.GetReference(source))
541+
fixed (char* ap = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
575542
fixed (char* bp = &MemoryMarshal.GetReference(prefix))
576543
{
577544
char* a = ap;
@@ -636,13 +603,12 @@ private unsafe bool StartsWithOrdinalHelper(ReadOnlySpan<char> source, ReadOnlyS
636603
{
637604
Debug.Assert(!GlobalizationMode.Invariant);
638605

639-
Debug.Assert(!source.IsEmpty);
640606
Debug.Assert(!prefix.IsEmpty);
641607
Debug.Assert(_isAsciiEqualityOrdinal);
642608

643609
int length = Math.Min(source.Length, prefix.Length);
644610

645-
fixed (char* ap = &MemoryMarshal.GetReference(source))
611+
fixed (char* ap = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
646612
fixed (char* bp = &MemoryMarshal.GetReference(prefix))
647613
{
648614
char* a = ap;
@@ -696,7 +662,6 @@ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffi
696662
{
697663
Debug.Assert(!GlobalizationMode.Invariant);
698664

699-
Debug.Assert(!source.IsEmpty);
700665
Debug.Assert(!suffix.IsEmpty);
701666
Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
702667

@@ -709,7 +674,7 @@ private unsafe bool EndsWith(ReadOnlySpan<char> source, ReadOnlySpan<char> suffi
709674
}
710675
else
711676
{
712-
fixed (char* pSource = &MemoryMarshal.GetReference(source))
677+
fixed (char* pSource = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
713678
fixed (char* pSuffix = &MemoryMarshal.GetReference(suffix))
714679
{
715680
return Interop.Globalization.EndsWith(_sortHandle, pSuffix, suffix.Length, pSource, source.Length, options);
@@ -721,13 +686,12 @@ private unsafe bool EndsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> source, R
721686
{
722687
Debug.Assert(!GlobalizationMode.Invariant);
723688

724-
Debug.Assert(!source.IsEmpty);
725689
Debug.Assert(!suffix.IsEmpty);
726690
Debug.Assert(_isAsciiEqualityOrdinal);
727691

728692
int length = Math.Min(source.Length, suffix.Length);
729693

730-
fixed (char* ap = &MemoryMarshal.GetReference(source))
694+
fixed (char* ap = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
731695
fixed (char* bp = &MemoryMarshal.GetReference(suffix))
732696
{
733697
char* a = ap + source.Length - 1;
@@ -773,13 +737,12 @@ private unsafe bool EndsWithOrdinalHelper(ReadOnlySpan<char> source, ReadOnlySpa
773737
{
774738
Debug.Assert(!GlobalizationMode.Invariant);
775739

776-
Debug.Assert(!source.IsEmpty);
777740
Debug.Assert(!suffix.IsEmpty);
778741
Debug.Assert(_isAsciiEqualityOrdinal);
779742

780743
int length = Math.Min(source.Length, suffix.Length);
781744

782-
fixed (char* ap = &MemoryMarshal.GetReference(source))
745+
fixed (char* ap = &MemoryMarshal.GetReference(source)) // could be null (or otherwise unable to be dereferenced)
783746
fixed (char* bp = &MemoryMarshal.GetReference(suffix))
784747
{
785748
char* a = ap + source.Length - 1;
@@ -836,7 +799,7 @@ private unsafe SortKey CreateSortKey(string source, CompareOptions options)
836799
}
837800
}
838801

839-
return new SortKey(Name, source, options, keyData);
802+
return new SortKey(this, source, options, keyData);
840803
}
841804

842805
private static unsafe bool IsSortable(char *text, int length)

0 commit comments

Comments
 (0)