Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStephen Toub <stoub@microsoft.com>2018-04-04 13:54:30 +0300
committerJan Kotas <jkotas@microsoft.com>2018-04-05 18:25:06 +0300
commit4596d662c7598f67ccbb808561aa1e35a013ddb2 (patch)
tree1b3b38cb13275248e9f09f0e3efb74ea261a9ecc /src
parent1c8cdc1acff22077fd536ca691208e139ea98de0 (diff)
Consolidate and optimize TextInfo.ChangeCase (dotnet/coreclr#17391)
- Move most of the implementation to the platform-agnostic file, rather than having completely different implementations for Windows and Unix. Now the only logic in each platform-specific file is the logic around invoking the associated P/Invoke. - Optimize that implementation to take a fast path that doesn't allocate when no case change is needed, and to avoid the native call when the whole string is ASCII. Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
Diffstat (limited to 'src')
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs111
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs120
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs218
3 files changed, 236 insertions, 213 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
index d13d3e8ce..c431e462b 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs
@@ -13,116 +13,7 @@ namespace System.Globalization
{
private Tristate _needsTurkishCasing = Tristate.NotInitialized;
- private void FinishInitialization()
- {
- }
-
- private unsafe string ChangeCase(string s, bool toUpper)
- {
- Debug.Assert(!_invariantMode);
-
- Debug.Assert(s != null);
-
- if (s.Length == 0)
- {
- return string.Empty;
- }
-
- string result = string.FastAllocateString(s.Length);
-
- fixed (char* pSource = s)
- {
- fixed (char* pResult = result)
- {
-#if CORECLR
- if (IsAsciiCasingSameAsInvariant && s.IsAscii())
- {
- int length = s.Length;
- char* a = pSource, b = pResult;
- if (toUpper)
- {
- while (length-- != 0)
- {
- *b++ = ToUpperAsciiInvariant(*a++);
- }
- }
- else
- {
- while (length-- != 0)
- {
- *b++ = ToLowerAsciiInvariant(*a++);
- }
- }
- }
- else
-#endif
- {
- ChangeCase(pSource, s.Length, pResult, result.Length, toUpper);
- }
- }
- }
-
- 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 = 0;
- char* a = pSource, b = pResult;
- if (toUpper)
- {
- while (length < source.Length && *a < 0x80)
- {
- *b++ = ToUpperAsciiInvariant(*a++);
- length++;
- }
- }
- else
- {
- while (length < source.Length && *a < 0x80)
- {
- *b++ = ToLowerAsciiInvariant(*a++);
- length++;
- }
- }
-
- if (length != source.Length)
- {
- 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);
-
- char dst = default(char);
-
- ChangeCase(&c, 1, &dst, 1, toUpper);
-
- return dst;
- }
+ private void FinishInitialization() { }
// -----------------------------
// ---- PAL layer ends here ----
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
index 015b37fcc..6e5e32100 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
-using System.Runtime.InteropServices;
namespace System.Globalization
{
@@ -24,115 +23,33 @@ namespace System.Globalization
_sortHandle = ret > 0 ? handle : IntPtr.Zero;
}
- private unsafe string ChangeCase(string s, bool toUpper)
+ private unsafe void ChangeCase(char* pSource, int pSourceLen, char* pResult, int pResultLen, bool toUpper)
{
Debug.Assert(!_invariantMode);
-
- Debug.Assert(s != null);
-
- //
- // Get the length of the string.
- //
- int nLengthInput = s.Length;
-
- //
- // Check if we have the empty string.
- //
- if (nLengthInput == 0)
- {
- return s;
- }
-
- int ret;
+ Debug.Assert(pSource != null);
+ Debug.Assert(pResult != null);
+ Debug.Assert(pSourceLen >= 0);
+ Debug.Assert(pResultLen >= 0);
+ Debug.Assert(pSourceLen <= pResultLen);
// Check for Invariant to avoid A/V in LCMapStringEx
uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING;
- //
- // Create the result string.
- //
- string result = string.FastAllocateString(nLengthInput);
-
- fixed (char* pSource = s)
- fixed (char* pResult = result)
- {
- ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName,
- linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE),
- pSource,
- nLengthInput,
- pResult,
- nLengthInput,
- null,
- null,
- _sortHandle);
- }
-
+ int ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName,
+ linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE),
+ pSource,
+ pSourceLen,
+ pResult,
+ pSourceLen,
+ null,
+ null,
+ _sortHandle);
if (ret == 0)
{
throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
}
- Debug.Assert(ret == nLengthInput, "Expected getting the same length of the original string");
- 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);
-
- char retVal = '\0';
-
- // Check for Invariant to avoid A/V in LCMapStringEx
- uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING;
-
- Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName,
- toUpper ? LCMAP_UPPERCASE | linguisticCasing : LCMAP_LOWERCASE | linguisticCasing,
- &c,
- 1,
- &retVal,
- 1,
- null,
- null,
- _sortHandle);
-
- return retVal;
+ Debug.Assert(ret == pSourceLen, "Expected getting the same length of the original string");
}
// PAL Ends here
@@ -143,9 +60,6 @@ namespace System.Globalization
private const uint LCMAP_LOWERCASE = 0x00000100;
private const uint LCMAP_UPPERCASE = 0x00000200;
- private static bool IsInvariantLocale(string localeName)
- {
- return localeName == "";
- }
+ private static bool IsInvariantLocale(string localeName) => localeName == "";
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
index 431458bc7..e875e179f 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs
@@ -13,6 +13,7 @@
////////////////////////////////////////////////////////////////////////////
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
@@ -201,6 +202,223 @@ namespace System.Globalization
return ChangeCase(str, toUpper: false);
}
+ private unsafe char ChangeCase(char c, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+
+ char dst = default;
+ ChangeCase(&c, 1, &dst, 1, toUpper);
+ return dst;
+ }
+
+ private unsafe string ChangeCase(string source, bool toUpper)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(source != null);
+
+ // If the string is empty, we're done.
+ if (source.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ int sourcePos = 0;
+ string result = null;
+
+ // 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)
+ {
+ if (toUpper)
+ {
+ // Loop through each character.
+ for (sourcePos = 0; sourcePos < source.Length; sourcePos++)
+ {
+ // If the character is lower-case, we're going to need to allocate a string.
+ char c = source[sourcePos];
+ if ((uint)(c - 'a') <= 'z' - 'a')
+ {
+ // Allocate the result string.
+ result = string.FastAllocateString(source.Length);
+ fixed (char* pResult = result)
+ {
+ // Store all of characters examined thus far.
+ if (sourcePos > 0)
+ {
+ source.AsSpan(0, sourcePos).CopyTo(new Span<char>(pResult, sourcePos));
+ }
+
+ // And store the current character, upper-cased.
+ char* d = pResult + sourcePos;
+ *d++ = (char)(c & ~0x20);
+ sourcePos++;
+
+ // Then continue looping through the remainder of the characters. If we hit
+ // a non-ASCII character, bail to fall back to culture-aware casing.
+ for (; sourcePos < source.Length; sourcePos++)
+ {
+ c = source[sourcePos];
+ if ((uint)(c - 'a') <= 'z' - 'a')
+ {
+ *d++ = (char)(c & ~0x20);
+ }
+ else if (!IsAscii(c))
+ {
+ break;
+ }
+ else
+ {
+ *d++ = c;
+ }
+ }
+ }
+
+ break;
+ }
+ else if (!IsAscii(c))
+ {
+ // The character isn't ASCII; bail to fall back to a culture-aware casing.
+ break;
+ }
+ }
+ }
+ else // toUpper == false
+ {
+ // Loop through each character.
+ for (sourcePos = 0; sourcePos < source.Length; sourcePos++)
+ {
+ // If the character is upper-case, we're going to need to allocate a string.
+ char c = source[sourcePos];
+ if ((uint)(c - 'A') <= 'Z' - 'A')
+ {
+ // Allocate the result string.
+ result = string.FastAllocateString(source.Length);
+ fixed (char* pResult = result)
+ {
+ // Store all of characters examined thus far.
+ if (sourcePos > 0)
+ {
+ source.AsSpan(0, sourcePos).CopyTo(new Span<char>(pResult, sourcePos));
+ }
+
+ // And store the current character, upper-cased.
+ char* d = pResult + sourcePos;
+ *d++ = (char)(c | 0x20);
+ sourcePos++;
+
+ // Then continue looping through the remainder of the characters. If we hit
+ // a non-ASCII character, bail to fall back to culture-aware casing.
+ for (; sourcePos < source.Length; sourcePos++)
+ {
+ c = source[sourcePos];
+ if ((uint)(c - 'A') <= 'Z' - 'A')
+ {
+ *d++ = (char)(c | 0x20);
+ }
+ else if (!IsAscii(c))
+ {
+ break;
+ }
+ else
+ {
+ *d++ = c;
+ }
+ }
+ }
+
+ break;
+ }
+ else if (!IsAscii(c))
+ {
+ // The character isn't ASCII; bail to fall back to a culture-aware casing.
+ break;
+ }
+ }
+ }
+
+ // If we successfully iterated through all of the characters, we didn't need to fall back
+ // to culture-aware casing. In that case, if we allocated a result string, use it, otherwise
+ // just return the original string, as no modifications were necessary.
+ if (sourcePos == source.Length)
+ {
+ return result ?? source;
+ }
+ }
+
+ // Falling back to culture-aware casing. Make sure we have a result string to write into.
+ // If we need to allocate the result string, we'll also need to copy over to it any
+ // characters already examined.
+ if (result == null)
+ {
+ result = string.FastAllocateString(source.Length);
+ if (sourcePos > 0)
+ {
+ fixed (char* pResult = result)
+ {
+ source.AsSpan(0, sourcePos).CopyTo(new Span<char>(pResult, sourcePos));
+ }
+ }
+ }
+
+ // Do the casing operation on everything after what we already processed.
+ fixed (char* pSource = source)
+ {
+ fixed (char* pResult = result)
+ {
+ ChangeCase(pSource + sourcePos, source.Length - sourcePos, pResult + sourcePos, result.Length - sourcePos, toUpper);
+ }
+ }
+
+ 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 = 0;
+ char* a = pSource, b = pResult;
+ if (toUpper)
+ {
+ while (length < source.Length && *a < 0x80)
+ {
+ *b++ = ToUpperAsciiInvariant(*a++);
+ length++;
+ }
+ }
+ else
+ {
+ while (length < source.Length && *a < 0x80)
+ {
+ *b++ = ToLowerAsciiInvariant(*a++);
+ length++;
+ }
+ }
+
+ if (length != source.Length)
+ {
+ ChangeCase(a, source.Length - length, b, destination.Length - length, toUpper);
+ }
+ }
+ else
+ {
+ ChangeCase(pSource, source.Length, pResult, destination.Length, toUpper);
+ }
+ }
+ }
+
private unsafe string ToLowerAsciiInvariant(string s)
{
if (s.Length == 0)