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

Commit bbe425e

Browse files
authored
Temporarily removing leftover use of ReadOnlySpan indexer. (#25908)
* Temporarily removing leftover use of ReadOnlySpan indexer. * Fixing some typos and marking method as unsafe.
1 parent 2775e08 commit bbe425e

File tree

2 files changed

+84
-68
lines changed

2 files changed

+84
-68
lines changed

src/Common/src/System/Globalization/FormatProvider.Number.cs

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -547,17 +547,21 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
547547
return false;
548548
}
549549

550-
private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
550+
private static unsafe bool TrailingZeros(ReadOnlySpan<char> s, int index)
551551
{
552-
// For compatibility, we need to allow trailing zeros at the end of a number string
553-
for (int i = index; i < s.Length; i++)
552+
fixed (char* sPtr = &s.DangerousGetPinnableReference())
554553
{
555-
if (s[i] != '\0')
554+
var span = new Span<char>(sPtr, s.Length);
555+
// For compatibility, we need to allow trailing zeros at the end of a number string
556+
for (int i = index; i < s.Length; i++)
556557
{
557-
return false;
558+
if (span[i] != '\0')
559+
{
560+
return false;
561+
}
558562
}
563+
return true;
559564
}
560-
return true;
561565
}
562566

563567
internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
@@ -634,68 +638,72 @@ internal static unsafe void Int32ToDecChars(char* buffer, ref int index, uint va
634638

635639
internal static unsafe char ParseFormatSpecifier(ReadOnlySpan<char> format, out int digits)
636640
{
637-
char c = default;
638-
if (format.Length > 0)
641+
fixed (char* formatPtr = &format.DangerousGetPinnableReference())
639642
{
640-
// If the format begins with a symbol, see if it's a standard format
641-
// with or without a specified number of digits.
642-
c = format[0];
643-
if ((uint)(c - 'A') <= 'Z' - 'A' ||
644-
(uint)(c - 'a') <= 'z' - 'a')
643+
var formatSpan = new Span<char>(formatPtr, format.Length);
644+
char c = default;
645+
if (format.Length > 0)
645646
{
646-
// Fast path for sole symbol, e.g. "D"
647-
if (format.Length == 1)
648-
{
649-
digits = -1;
650-
return c;
651-
}
652-
653-
if (format.Length == 2)
647+
// If the format begins with a symbol, see if it's a standard format
648+
// with or without a specified number of digits.
649+
c = formatSpan[0];
650+
if ((uint)(c - 'A') <= 'Z' - 'A' ||
651+
(uint)(c - 'a') <= 'z' - 'a')
654652
{
655-
// Fast path for symbol and single digit, e.g. "X4"
656-
int d = format[1] - '0';
657-
if ((uint)d < 10)
653+
// Fast path for sole symbol, e.g. "D"
654+
if (format.Length == 1)
658655
{
659-
digits = d;
656+
digits = -1;
660657
return c;
661658
}
662-
}
663-
else if (format.Length == 3)
664-
{
665-
// Fast path for symbol and double digit, e.g. "F12"
666-
int d1 = format[1] - '0', d2 = format[2] - '0';
667-
if ((uint)d1 < 10 && (uint)d2 < 10)
659+
660+
if (format.Length == 2)
668661
{
669-
digits = d1 * 10 + d2;
670-
return c;
662+
// Fast path for symbol and single digit, e.g. "X4"
663+
int d = formatSpan[1] - '0';
664+
if ((uint)d < 10)
665+
{
666+
digits = d;
667+
return c;
668+
}
669+
}
670+
else if (format.Length == 3)
671+
{
672+
// Fast path for symbol and double digit, e.g. "F12"
673+
int d1 = formatSpan[1] - '0', d2 = formatSpan[2] - '0';
674+
if ((uint)d1 < 10 && (uint)d2 < 10)
675+
{
676+
digits = d1 * 10 + d2;
677+
return c;
678+
}
671679
}
672-
}
673680

674-
// Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99,
675-
// but it can begin with any number of 0s, and thus we may need to check more than two
676-
// digits. Further, for compat, we need to stop when we hit a null char.
677-
int n = 0;
678-
int i = 1;
679-
while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10)
680-
{
681-
n = (n * 10) + format[i++] - '0';
682-
}
681+
// Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99,
682+
// but it can begin with any number of 0s, and thus we may need to check more than two
683+
// digits. Further, for compat, we need to stop when we hit a null char.
684+
int n = 0;
685+
int i = 1;
686+
while (i < format.Length && (((uint)formatSpan[i] - '0') < 10) && n < 10)
687+
{
688+
n = (n * 10) + formatSpan[i++] - '0';
689+
}
683690

684-
// If we're at the end of the digits rather than having stopped because we hit something
685-
// other than a digit or overflowed, return the standard format info.
686-
if (i == format.Length || format[i] == '\0')
687-
{
688-
digits = n;
689-
return c;
691+
// If we're at the end of the digits rather than having stopped because we hit something
692+
// other than a digit or overflowed, return the standard format info.
693+
if (i == format.Length || formatSpan[i] == '\0')
694+
{
695+
digits = n;
696+
return c;
697+
}
690698
}
691699
}
692-
}
693700

694-
// Default empty format to be "G"; custom format is signified with '\0'.
695-
digits = -1;
696-
return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
697-
'G' :
698-
'\0';
701+
// Default empty format to be "G"; custom format is signified with '\0'.
702+
digits = -1;
703+
return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
704+
'G' :
705+
'\0';
706+
}
699707
}
700708

701709
internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal)

src/System.IO.FileSystem/src/System/IO/PathHelpers.cs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,13 @@ internal unsafe static string CombineNoChecks(string first, string second)
113113
private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
114114
{
115115
Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");
116-
117-
bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
118-
|| PathInternal.IsDirectorySeparator(second[0]);
119-
120116
fixed (char* f = &first.DangerousGetPinnableReference(), s = &second.DangerousGetPinnableReference())
121117
{
118+
var firstSpan = new Span<char>(f, first.Length);
119+
var secondSpan = new Span<char>(s, second.Length);
120+
bool hasSeparator = PathInternal.IsDirectorySeparator(firstSpan[first.Length - 1])
121+
|| PathInternal.IsDirectorySeparator(secondSpan[0]);
122+
122123
return string.Create(
123124
first.Length + second.Length + (hasSeparator ? 0 : 1),
124125
(First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
@@ -136,14 +137,17 @@ private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, R
136137
private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third)
137138
{
138139
Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths");
139-
140-
bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
141-
|| PathInternal.IsDirectorySeparator(second[0]);
142-
bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
143-
|| PathInternal.IsDirectorySeparator(third[0]);
144-
145140
fixed (char* f = &first.DangerousGetPinnableReference(), s = &second.DangerousGetPinnableReference(), t = &third.DangerousGetPinnableReference())
146141
{
142+
var firstSpan = new Span<char>(f, first.Length);
143+
var secondSpan = new Span<char>(s, second.Length);
144+
var thirdSpan = new Span<char>(t, third.Length);
145+
146+
bool firstHasSeparator = PathInternal.IsDirectorySeparator(firstSpan[first.Length - 1])
147+
|| PathInternal.IsDirectorySeparator(secondSpan[0]);
148+
bool thirdHasSeparator = PathInternal.IsDirectorySeparator(secondSpan[second.Length - 1])
149+
|| PathInternal.IsDirectorySeparator(thirdSpan[0]);
150+
147151
return string.Create(
148152
first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1),
149153
(First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
@@ -167,9 +171,13 @@ private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, R
167171
[MethodImpl(MethodImplOptions.AggressiveInlining)]
168172
public static unsafe bool IsDotOrDotDot(ReadOnlySpan<char> fileName)
169173
{
170-
return !(fileName.Length > 2
171-
|| fileName[0] != '.'
172-
|| (fileName.Length == 2 && fileName[1] != '.'));
174+
fixed (char* fileNamePtr = &fileName.DangerousGetPinnableReference())
175+
{
176+
var fileNameSpan = new Span<char>(fileNamePtr, fileName.Length);
177+
return !(fileName.Length > 2
178+
|| fileNameSpan[0] != '.'
179+
|| (fileName.Length == 2 && fileNameSpan[1] != '.'));
180+
}
173181
}
174182

175183
public static unsafe ReadOnlySpan<char> GetDirectoryNameNoChecks(ReadOnlySpan<char> path)

0 commit comments

Comments
 (0)