// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Security;
using System.Text;
return result;
}
+ internal unsafe void ChangeCase(ReadOnlySpan<char> source, Span<char> destination, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(destination.Length >= source.Length);
+
+ if (source.IsEmpty)
+ {
+ return;
+ }
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ {
+ fixed (char* pResult = &MemoryMarshal.GetReference(destination))
+ {
+ if (IsAsciiCasingSameAsInvariant)
+ {
+ int length = source.Length;
+ char* a = pSource, b = pResult;
+ if (toUpper)
+ {
+ while (length-- != 0 && *a < 0x80)
+ {
+ *b++ = ToUpperAsciiInvariant(*a++);
+ }
+ }
+ else
+ {
+ while (length-- != 0 && *a < 0x80)
+ {
+ *b++ = ToLowerAsciiInvariant(*a++);
+ }
+ }
+
+ if (length != 0)
+ {
+ ChangeCase(a, source.Length - length, b, destination.Length - length, toUpper);
+ }
+ }
+ else
+ {
+ ChangeCase(pSource, source.Length, pResult, destination.Length, toUpper);
+ }
+ }
+ }
+ }
+
private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!_invariantMode);
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.InteropServices;
namespace System.Globalization
{
return result;
}
+ internal unsafe void ChangeCase(ReadOnlySpan<char> source, Span<char> destination, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(destination.Length >= source.Length);
+
+ if (source.IsEmpty)
+ {
+ return;
+ }
+
+ int ret;
+
+ // Check for Invariant to avoid A/V in LCMapStringEx
+ uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING;
+
+ fixed (char* pSource = &MemoryMarshal.GetReference(source))
+ fixed (char* pResult = &MemoryMarshal.GetReference(destination))
+ {
+ ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName,
+ linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE),
+ pSource,
+ source.Length,
+ pResult,
+ source.Length,
+ null,
+ null,
+ _sortHandle);
+ }
+
+ if (ret == 0)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ }
+
+ Debug.Assert(ret == source.Length, "Expected getting the same length of the original span");
+ }
+
private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!_invariantMode);
}
}
+ internal void ToLowerAsciiInvariant(ReadOnlySpan<char> source, Span<char> destination)
+ {
+ Debug.Assert(destination.Length >= source.Length);
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = ToLowerAsciiInvariant(source[i]);
+ }
+ }
+
private unsafe string ToUpperAsciiInvariant(string s)
{
if (s.Length == 0)
}
}
+ internal void ToUpperAsciiInvariant(ReadOnlySpan<char> source, Span<char> destination)
+ {
+ Debug.Assert(destination.Length >= source.Length);
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = ToUpperAsciiInvariant(source[i]);
+ }
+ }
+
private static char ToLowerAsciiInvariant(char c)
{
if ((uint)(c - 'A') <= (uint)('Z' - 'A'))
/// </summary>
public static class Span
{
- // s_invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant
- private static readonly bool s_invariantMode = GlobalizationMode.Invariant;
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to lowercase.
+ /// </summary>
+ public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
+ {
+ if (culture == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
+
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ culture.TextInfo.ToLowerAsciiInvariant(source, destination);
+ else
+ culture.TextInfo.ChangeCase(source, destination, toUpper: false);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to lowercase
+ /// using the casing rules of the invariant culture.
+ /// </summary>
+ public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
+ {
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ CultureInfo.InvariantCulture.TextInfo.ToLowerAsciiInvariant(source, destination);
+ else
+ CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: false);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to uppercase.
+ /// </summary>
+ public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
+ {
+ if (culture == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
+
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ culture.TextInfo.ToUpperAsciiInvariant(source, destination);
+ else
+ culture.TextInfo.ChangeCase(source, destination, toUpper: true);
+ return source.Length;
+ }
+
+ /// <summary>
+ /// Copies the characters from the source span into the destination, converting each character to uppercase
+ /// using the casing rules of the invariant culture.
+ /// </summary>
+ public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
+ {
+ // Assuming that changing case does not affect length
+ if (destination.Length < source.Length)
+ return -1;
+
+ if (GlobalizationMode.Invariant)
+ CultureInfo.InvariantCulture.TextInfo.ToUpperAsciiInvariant(source, destination);
+ else
+ CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: true);
+ return source.Length;
+ }
/// <summary>
/// Determines whether the beginning of the span matches the specified value when compared using the specified comparison option.
{
Debug.Assert(value.Length != 0);
- if (s_invariantMode)
+ if (GlobalizationMode.Invariant)
{
return StartsWithOrdinalHelper(span, value);
}
{
Debug.Assert(value.Length != 0);
- if (s_invariantMode)
+ if (GlobalizationMode.Invariant)
{
return StartsWithOrdinalIgnoreCaseHelper(span, value);
}
{
Debug.Assert(value.Length != 0);
- if (s_invariantMode)
+ if (GlobalizationMode.Invariant)
{
return EndsWithOrdinalHelper(span, value);
}
{
Debug.Assert(value.Length != 0);
- if (s_invariantMode)
+ if (GlobalizationMode.Invariant)
{
return EndsWithOrdinalIgnoreCaseHelper(span, value);
}