Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,18 +177,19 @@ public string ToLower(string str)
return InvariantModeCasing.ToLower(str);
}

return ChangeCaseCommon<ToLowerConversion>(str);
return ChangeCaseCommon<ToLowerConversion>(this, str);
}

internal void ToLower(ReadOnlySpan<char> source, Span<char> destination)
internal static string ToLowerInvariant(string str)
{
if (GlobalizationMode.Invariant)
{
InvariantModeCasing.ToLower(source, destination);
return;
}
ArgumentNullException.ThrowIfNull(str);

return ChangeCaseCommon<ToLowerConversion>(null, str);
}

ChangeCaseCommon<ToLowerConversion>(source, destination);
internal void ToLower(ReadOnlySpan<char> source, Span<char> destination)
{
ChangeCaseCommon<ToLowerConversion>(this, source, destination);
}

private unsafe char ChangeCase(char c, bool toUpper)
Expand Down Expand Up @@ -221,20 +222,19 @@ internal static char ToUpperOrdinal(char c)
internal void ChangeCaseToLower(ReadOnlySpan<char> source, Span<char> destination)
{
Debug.Assert(destination.Length >= source.Length);
ChangeCaseCommon<ToLowerConversion>(source, destination);
ChangeCaseCommon<ToLowerConversion>(this, source, destination);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ChangeCaseToUpper(ReadOnlySpan<char> source, Span<char> destination)
{
Debug.Assert(destination.Length >= source.Length);
ChangeCaseCommon<ToUpperConversion>(source, destination);
ChangeCaseCommon<ToUpperConversion>(this, source, destination);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void ChangeCaseCommon<TConversion>(ReadOnlySpan<char> source, Span<char> destination) where TConversion : struct
private static unsafe void ChangeCaseCommon<TConversion>(TextInfo? instance, ReadOnlySpan<char> source, Span<char> destination) where TConversion : struct
{
Debug.Assert(!GlobalizationMode.Invariant);
Debug.Assert(typeof(TConversion) == typeof(ToUpperConversion) || typeof(TConversion) == typeof(ToLowerConversion));

if (source.IsEmpty)
Expand All @@ -245,7 +245,8 @@ private unsafe void ChangeCaseCommon<TConversion>(ReadOnlySpan<char> source, Spa
bool toUpper = typeof(TConversion) == typeof(ToUpperConversion); // JIT will treat this as a constant in release builds
int charsConsumed = 0;

if (IsAsciiCasingSameAsInvariant)
// instance being null indicates the invariant culture where IsAsciiCasingSameAsInvariant is always true.
if (instance == null || instance.IsAsciiCasingSameAsInvariant)
{
OperationStatus operationStatus = toUpper
? Ascii.ToUpper(source, destination, out charsConsumed)
Expand All @@ -258,19 +259,34 @@ private unsafe void ChangeCaseCommon<TConversion>(ReadOnlySpan<char> source, Spa
}
}

if (GlobalizationMode.Invariant)
{
if (toUpper)
{
InvariantModeCasing.ToUpper(source, destination);
}
else
{
InvariantModeCasing.ToLower(source, destination);
}
}

// instance being null means it's Invariant
instance ??= Invariant;

fixed (char* pSource = &MemoryMarshal.GetReference(source))
fixed (char* pDestination = &MemoryMarshal.GetReference(destination))
{
ChangeCaseCore(pSource + charsConsumed, source.Length - charsConsumed, pDestination + charsConsumed, destination.Length - charsConsumed, toUpper);
instance!.ChangeCaseCore(pSource + charsConsumed, source.Length - charsConsumed,
pDestination + charsConsumed, destination.Length - charsConsumed, toUpper);
}
}

private unsafe string ChangeCaseCommon<TConversion>(string source) where TConversion : struct
private static unsafe string ChangeCaseCommon<TConversion>(TextInfo? instance, string source) where TConversion : struct
{
Debug.Assert(typeof(TConversion) == typeof(ToUpperConversion) || typeof(TConversion) == typeof(ToLowerConversion));
bool toUpper = typeof(TConversion) == typeof(ToUpperConversion); // JIT will treat this as a constant in release builds

Debug.Assert(!GlobalizationMode.Invariant);
Debug.Assert(source != null);

// If the string is empty, we're done.
Expand All @@ -286,7 +302,9 @@ private unsafe string ChangeCaseCommon<TConversion>(string source) where TConver
// If this culture's casing for ASCII is the same as invariant, try to take
// a fast path that'll work in managed code and ASCII rather than calling out
// to the OS for culture-aware casing.
if (IsAsciiCasingSameAsInvariant)
//
// instance being null indicates the invariant culture where IsAsciiCasingSameAsInvariant is always true.
if (instance == null || instance.IsAsciiCasingSameAsInvariant)
{
// Read 2 chars (one 32-bit integer) at a time

Expand Down Expand Up @@ -341,13 +359,18 @@ private unsafe string ChangeCaseCommon<TConversion>(string source) where TConver
source.AsSpan(0, (int)currIdx).CopyTo(resultSpan);

// and re-run the fast span-based logic over the remainder of the data
ChangeCaseCommon<TConversion>(source.AsSpan((int)currIdx), resultSpan.Slice((int)currIdx));
ChangeCaseCommon<TConversion>(instance, source.AsSpan((int)currIdx), resultSpan.Slice((int)currIdx));
return result;
}
}

NotAscii:
{
if (GlobalizationMode.Invariant)
{
return toUpper ? InvariantModeCasing.ToUpper(source) : InvariantModeCasing.ToLower(source);
}

// We reached non-ASCII data *or* the requested culture doesn't map ASCII data the same way as the invariant culture.
// In either case we need to fall back to the localization tables.

Expand All @@ -360,10 +383,13 @@ private unsafe string ChangeCaseCommon<TConversion>(string source) where TConver
source.AsSpan(0, (int)currIdx).CopyTo(resultSpan);
}

// instance being null means it's Invariant
instance ??= Invariant;

// and run the culture-aware logic over the remainder of the data
fixed (char* pResult = result)
{
ChangeCaseCore(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper);
instance!.ChangeCaseCore(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper);
}
return result;
}
Expand Down Expand Up @@ -454,23 +480,19 @@ public string ToUpper(string str)
{
ArgumentNullException.ThrowIfNull(str);

if (GlobalizationMode.Invariant)
{
return InvariantModeCasing.ToUpper(str);
}
return ChangeCaseCommon<ToUpperConversion>(this, str);
}

return ChangeCaseCommon<ToUpperConversion>(str);
internal static string ToUpperInvariant(string str)
{
ArgumentNullException.ThrowIfNull(str);

return ChangeCaseCommon<ToUpperConversion>(null, str);
}

internal void ToUpper(ReadOnlySpan<char> source, Span<char> destination)
{
if (GlobalizationMode.Invariant)
{
InvariantModeCasing.ToUpper(source, destination);
return;
}

ChangeCaseCommon<ToUpperConversion>(source, destination);
ChangeCaseCommon<ToUpperConversion>(this, source, destination);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2365,7 +2365,7 @@ public string ToLower(CultureInfo? culture)
// Creates a copy of this string in lower case based on invariant culture.
public string ToLowerInvariant()
{
return TextInfo.Invariant.ToLower(this);
return TextInfo.ToLowerInvariant(this);
}

public string ToUpper() => ToUpper(null);
Expand All @@ -2380,7 +2380,7 @@ public string ToUpper(CultureInfo? culture)
// Creates a copy of this string in upper case based on invariant culture.
public string ToUpperInvariant()
{
return TextInfo.Invariant.ToUpper(this);
return TextInfo.ToUpperInvariant(this);
}

// Trims the whitespace from both ends of the string. Whitespace is defined by
Expand Down
Loading