Skip to content

Commit

Permalink
Temporarily removing leftover use of ReadOnlySpan indexer. (dotnet#25908
Browse files Browse the repository at this point in the history
)

* Temporarily removing leftover use of ReadOnlySpan indexer.

* Fixing some typos and marking method as unsafe.
  • Loading branch information
ahsonkhan authored Dec 13, 2017
1 parent 2775e08 commit bbe425e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 68 deletions.
118 changes: 63 additions & 55 deletions src/Common/src/System/Globalization/FormatProvider.Number.cs
Original file line number Diff line number Diff line change
Expand Up @@ -547,17 +547,21 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
return false;
}

private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
private static unsafe bool TrailingZeros(ReadOnlySpan<char> s, int index)
{
// For compatibility, we need to allow trailing zeros at the end of a number string
for (int i = index; i < s.Length; i++)
fixed (char* sPtr = &s.DangerousGetPinnableReference())
{
if (s[i] != '\0')
var span = new Span<char>(sPtr, s.Length);
// For compatibility, we need to allow trailing zeros at the end of a number string
for (int i = index; i < s.Length; i++)
{
return false;
if (span[i] != '\0')
{
return false;
}
}
return true;
}
return true;
}

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

internal static unsafe char ParseFormatSpecifier(ReadOnlySpan<char> format, out int digits)
{
char c = default;
if (format.Length > 0)
fixed (char* formatPtr = &format.DangerousGetPinnableReference())
{
// If the format begins with a symbol, see if it's a standard format
// with or without a specified number of digits.
c = format[0];
if ((uint)(c - 'A') <= 'Z' - 'A' ||
(uint)(c - 'a') <= 'z' - 'a')
var formatSpan = new Span<char>(formatPtr, format.Length);
char c = default;
if (format.Length > 0)
{
// Fast path for sole symbol, e.g. "D"
if (format.Length == 1)
{
digits = -1;
return c;
}

if (format.Length == 2)
// If the format begins with a symbol, see if it's a standard format
// with or without a specified number of digits.
c = formatSpan[0];
if ((uint)(c - 'A') <= 'Z' - 'A' ||
(uint)(c - 'a') <= 'z' - 'a')
{
// Fast path for symbol and single digit, e.g. "X4"
int d = format[1] - '0';
if ((uint)d < 10)
// Fast path for sole symbol, e.g. "D"
if (format.Length == 1)
{
digits = d;
digits = -1;
return c;
}
}
else if (format.Length == 3)
{
// Fast path for symbol and double digit, e.g. "F12"
int d1 = format[1] - '0', d2 = format[2] - '0';
if ((uint)d1 < 10 && (uint)d2 < 10)

if (format.Length == 2)
{
digits = d1 * 10 + d2;
return c;
// Fast path for symbol and single digit, e.g. "X4"
int d = formatSpan[1] - '0';
if ((uint)d < 10)
{
digits = d;
return c;
}
}
else if (format.Length == 3)
{
// Fast path for symbol and double digit, e.g. "F12"
int d1 = formatSpan[1] - '0', d2 = formatSpan[2] - '0';
if ((uint)d1 < 10 && (uint)d2 < 10)
{
digits = d1 * 10 + d2;
return c;
}
}
}

// Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99,
// but it can begin with any number of 0s, and thus we may need to check more than two
// digits. Further, for compat, we need to stop when we hit a null char.
int n = 0;
int i = 1;
while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10)
{
n = (n * 10) + format[i++] - '0';
}
// Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99,
// but it can begin with any number of 0s, and thus we may need to check more than two
// digits. Further, for compat, we need to stop when we hit a null char.
int n = 0;
int i = 1;
while (i < format.Length && (((uint)formatSpan[i] - '0') < 10) && n < 10)
{
n = (n * 10) + formatSpan[i++] - '0';
}

// If we're at the end of the digits rather than having stopped because we hit something
// other than a digit or overflowed, return the standard format info.
if (i == format.Length || format[i] == '\0')
{
digits = n;
return c;
// If we're at the end of the digits rather than having stopped because we hit something
// other than a digit or overflowed, return the standard format info.
if (i == format.Length || formatSpan[i] == '\0')
{
digits = n;
return c;
}
}
}
}

// Default empty format to be "G"; custom format is signified with '\0'.
digits = -1;
return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
'G' :
'\0';
// Default empty format to be "G"; custom format is signified with '\0'.
digits = -1;
return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
'G' :
'\0';
}
}

internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal)
Expand Down
34 changes: 21 additions & 13 deletions src/System.IO.FileSystem/src/System/IO/PathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,13 @@ internal unsafe static string CombineNoChecks(string first, string second)
private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
{
Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");

bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
|| PathInternal.IsDirectorySeparator(second[0]);

fixed (char* f = &first.DangerousGetPinnableReference(), s = &second.DangerousGetPinnableReference())
{
var firstSpan = new Span<char>(f, first.Length);
var secondSpan = new Span<char>(s, second.Length);
bool hasSeparator = PathInternal.IsDirectorySeparator(firstSpan[first.Length - 1])
|| PathInternal.IsDirectorySeparator(secondSpan[0]);

return string.Create(
first.Length + second.Length + (hasSeparator ? 0 : 1),
(First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
Expand All @@ -136,14 +137,17 @@ private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, R
private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third)
{
Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths");

bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
|| PathInternal.IsDirectorySeparator(second[0]);
bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1])
|| PathInternal.IsDirectorySeparator(third[0]);

fixed (char* f = &first.DangerousGetPinnableReference(), s = &second.DangerousGetPinnableReference(), t = &third.DangerousGetPinnableReference())
{
var firstSpan = new Span<char>(f, first.Length);
var secondSpan = new Span<char>(s, second.Length);
var thirdSpan = new Span<char>(t, third.Length);

bool firstHasSeparator = PathInternal.IsDirectorySeparator(firstSpan[first.Length - 1])
|| PathInternal.IsDirectorySeparator(secondSpan[0]);
bool thirdHasSeparator = PathInternal.IsDirectorySeparator(secondSpan[second.Length - 1])
|| PathInternal.IsDirectorySeparator(thirdSpan[0]);

return string.Create(
first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1),
(First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length,
Expand All @@ -167,9 +171,13 @@ private unsafe static string CombineNoChecksInternal(ReadOnlySpan<char> first, R
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsDotOrDotDot(ReadOnlySpan<char> fileName)
{
return !(fileName.Length > 2
|| fileName[0] != '.'
|| (fileName.Length == 2 && fileName[1] != '.'));
fixed (char* fileNamePtr = &fileName.DangerousGetPinnableReference())
{
var fileNameSpan = new Span<char>(fileNamePtr, fileName.Length);
return !(fileName.Length > 2
|| fileNameSpan[0] != '.'
|| (fileName.Length == 2 && fileNameSpan[1] != '.'));
}
}

public static unsafe ReadOnlySpan<char> GetDirectoryNameNoChecks(ReadOnlySpan<char> path)
Expand Down

0 comments on commit bbe425e

Please sign in to comment.